pax_global_header00006660000000000000000000000064143200521120014500gustar00rootroot0000000000000052 comment=50ba3629bf114d5615c17c56c03a04a9cbeb6d6f opentyrian-2.1.20221123/000077500000000000000000000000001432005211200144255ustar00rootroot00000000000000opentyrian-2.1.20221123/.editorconfig000066400000000000000000000006141432005211200171030ustar00rootroot00000000000000root = true [*] charset = utf-8 trim_trailing_whitespace = true end_of_line = lf insert_final_newline = true [Makefile] indent_style = tab [*.sln] charset = utf-8-bom indent_style = tab end_of_line = crlf insert_final_newline = false [*.{vcxproj,props.template}] indent_style = space indent_size = 2 end_of_line = crlf insert_final_newline = false [*.rc] charset = latin1 end_of_line = crlf opentyrian-2.1.20221123/.gitignore000066400000000000000000000006761432005211200164260ustar00rootroot00000000000000/data/ opentyrian.cfg tyrian.cfg tyrian.sav # Text editor detritus *~ *.swp # Make build output /obj/ /src/*.gch /opentyrian # Windows build output /opentyrian.exe /SDL2.dll /SDL2_net.dll # Windows runtime output /stderr.txt /stdout.txt # MSVC build output and project /opentyrian-*.exe /opentyrian-*.ilk /opentyrian-*.pdb /opentyrian-*.iobj /opentyrian-*.ipdb /visualc/.vs/ /visualc/*.props /visualc/*.user # Doxygen output /doc/doxygen/ opentyrian-2.1.20221123/COPYING000066400000000000000000000431031432005211200154610ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. opentyrian-2.1.20221123/Doxyfile000066400000000000000000002234221432005211200161400ustar00rootroot00000000000000# Doxyfile 1.7.6.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a 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. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = "OpenTyrian" # 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 an logo or 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) # base path where the generated documentation will be put. # 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 = doc/doxygen/ # 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 cause performance problems for the file system. CREATE_SUBDIRS = 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. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) 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. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) 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. 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" "the" ABBREVIATE_BRIEF = # 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. 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. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then 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. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then 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. 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 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 if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. 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 # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # 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 comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) 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 behaviour. # 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 behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. 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. 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. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # 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. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. 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. 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, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. 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 that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # 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); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip 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. 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 (the default) # will make doxygen 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. 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. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) 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. 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). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data 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 (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT 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. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE 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 appear 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. 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 and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. 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. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When 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 (the default) only methods in the interface are included. 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 namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) 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. HIDE_UNDOC_MEMBERS = NO # 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 (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. 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 (the default) these blocks will be appended to the # function's detailed documentation block. 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 (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. 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. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # 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. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) 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. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. 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 default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to 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 default) # the group names will appear in their defined order. 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 default), 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. 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. 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. 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. 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. 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. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of 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 initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. 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. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # 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 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 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 , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. 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. The 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. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # 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. 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 # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED 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. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR 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. WARN_IF_DOC_ERROR = YES # The 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 (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = 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) 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 stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be 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. INPUT = src/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. 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. 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 pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. 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. 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 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. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are 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. 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 # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). 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 option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # 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 also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. 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. 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. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # 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. REFERENCES_LINK_SOURCE = 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. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) 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. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # 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. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) 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 one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. 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. If left blank `html' will be used as the default path. 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). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. 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 the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # style sheet in the HTML output directory as well, or it will be erased! HTML_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. 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. # The allowed range is 0 to 359. 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. 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. 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 NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # 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. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # 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, 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. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # 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. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, 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. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_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. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, 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. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, 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. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, 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). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, 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. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. 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. 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. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace 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 # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. 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. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, 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. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, 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. 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. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW 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. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) 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. ENUM_VALUES_PER_LINE = 4 # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # 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. TREEVIEW_WIDTH = 250 # When 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. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # 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. 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 before the changes have effect. 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 prerendered 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 also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # 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.org site, so you can quickly see the result without installing # MathJax, but it is strongly recommended to install a local copy of MathJax # before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # 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. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES opentyrian-2.1.20221123/Makefile000066400000000000000000000110751432005211200160710ustar00rootroot00000000000000# BUILD SETTINGS ############################################################### ifneq ($(filter Msys Cygwin, $(shell uname -o)), ) PLATFORM := WIN32 TYRIAN_DIR = C:\\TYRIAN else PLATFORM := UNIX TYRIAN_DIR = $(gamesdir)/tyrian endif WITH_NETWORK := true ################################################################################ # see https://www.gnu.org/prep/standards/html_node/Makefile-Conventions.html SHELL = /bin/sh CC ?= gcc INSTALL ?= install PKG_CONFIG ?= pkg-config VCS_IDREV ?= (git describe --tags || git rev-parse --short HEAD) INSTALL_PROGRAM ?= $(INSTALL) INSTALL_DATA ?= $(INSTALL) -m 644 prefix ?= /usr/local exec_prefix ?= $(prefix) bindir ?= $(exec_prefix)/bin datarootdir ?= $(prefix)/share datadir ?= $(datarootdir) docdir ?= $(datarootdir)/doc/opentyrian mandir ?= $(datarootdir)/man man6dir ?= $(mandir)/man6 man6ext ?= .6 desktopdir ?= $(datarootdir)/applications icondir ?= $(datarootdir)/icons # see https://www.pathname.com/fhs/pub/fhs-2.3.html gamesdir ?= $(datadir)/games ### TARGET := opentyrian SRCS := $(wildcard src/*.c) OBJS := $(SRCS:src/%.c=obj/%.o) DEPS := $(SRCS:src/%.c=obj/%.d) ### ifeq ($(WITH_NETWORK), true) EXTRA_CPPFLAGS += -DWITH_NETWORK endif OPENTYRIAN_VERSION := $(shell $(VCS_IDREV) 2>/dev/null && \ touch src/opentyrian_version.h) ifneq ($(OPENTYRIAN_VERSION), ) EXTRA_CPPFLAGS += -DOPENTYRIAN_VERSION='"$(OPENTYRIAN_VERSION)"' endif CPPFLAGS ?= -MMD CPPFLAGS += -DNDEBUG CFLAGS ?= -pedantic \ -Wall \ -Wextra \ -Wno-format-truncation \ -Wno-missing-field-initializers \ -O2 LDFLAGS ?= LDLIBS ?= ifeq ($(WITH_NETWORK), true) SDL_CPPFLAGS := $(shell $(PKG_CONFIG) sdl2 SDL2_net --cflags) SDL_LDFLAGS := $(shell $(PKG_CONFIG) sdl2 SDL2_net --libs-only-L --libs-only-other) SDL_LDLIBS := $(shell $(PKG_CONFIG) sdl2 SDL2_net --libs-only-l) else SDL_CPPFLAGS := $(shell $(PKG_CONFIG) sdl2 --cflags) SDL_LDFLAGS := $(shell $(PKG_CONFIG) sdl2 --libs-only-L --libs-only-other) SDL_LDLIBS := $(shell $(PKG_CONFIG) sdl2 --libs-only-l) endif ALL_CPPFLAGS = -DTARGET_$(PLATFORM) \ -DTYRIAN_DIR='"$(TYRIAN_DIR)"' \ $(EXTRA_CPPFLAGS) \ $(SDL_CPPFLAGS) \ $(CPPFLAGS) ALL_CFLAGS = -std=iso9899:1999 \ $(CFLAGS) ALL_LDFLAGS = $(SDL_LDFLAGS) \ $(LDFLAGS) ALL_LDLIBS = -lm \ $(SDL_LDLIBS) \ $(LDLIBS) ### .PHONY : all all : $(TARGET) .PHONY : debug debug : CPPFLAGS += -UNDEBUG debug : CFLAGS += -Werror debug : CFLAGS += -O0 debug : CFLAGS += -g3 debug : all .PHONY : installdirs installdirs : mkdir -p $(DESTDIR)$(bindir) mkdir -p $(DESTDIR)$(docdir) mkdir -p $(DESTDIR)$(man6dir) mkdir -p $(DESTDIR)$(desktopdir) mkdir -p $(DESTDIR)$(icondir)/hicolor/22x22/apps mkdir -p $(DESTDIR)$(icondir)/hicolor/24x24/apps mkdir -p $(DESTDIR)$(icondir)/hicolor/32x32/apps mkdir -p $(DESTDIR)$(icondir)/hicolor/48x48/apps mkdir -p $(DESTDIR)$(icondir)/hicolor/128x128/apps .PHONY : install install : $(TARGET) installdirs $(INSTALL_PROGRAM) $(TARGET) $(DESTDIR)$(bindir)/ $(INSTALL_DATA) NEWS README $(DESTDIR)$(docdir)/ $(INSTALL_DATA) linux/man/opentyrian.6 $(DESTDIR)$(man6dir)/opentyrian$(man6ext) $(INSTALL_DATA) linux/opentyrian.desktop $(DESTDIR)$(desktopdir)/ $(INSTALL_DATA) linux/icons/tyrian-22.png $(DESTDIR)$(icondir)/hicolor/22x22/apps/opentyrian.png $(INSTALL_DATA) linux/icons/tyrian-24.png $(DESTDIR)$(icondir)/hicolor/24x24/apps/opentyrian.png $(INSTALL_DATA) linux/icons/tyrian-32.png $(DESTDIR)$(icondir)/hicolor/32x32/apps/opentyrian.png $(INSTALL_DATA) linux/icons/tyrian-48.png $(DESTDIR)$(icondir)/hicolor/48x48/apps/opentyrian.png $(INSTALL_DATA) linux/icons/tyrian-128.png $(DESTDIR)$(icondir)/hicolor/128x128/apps/opentyrian.png .PHONY : uninstall uninstall : rm -f $(DESTDIR)$(bindir)/$(TARGET) rm -f $(DESTDIR)$(docdir)/NEWS $(DESTDIR)$(docdir)/README rm -f $(DESTDIR)$(man6dir)/opentyrian$(man6ext) rm -f $(DESTDIR)$(desktopdir)/opentyrian.desktop rm -f $(DESTDIR)$(icondir)/hicolor/22x22/apps/opentyrian.png rm -f $(DESTDIR)$(icondir)/hicolor/24x24/apps/opentyrian.png rm -f $(DESTDIR)$(icondir)/hicolor/32x32/apps/opentyrian.png rm -f $(DESTDIR)$(icondir)/hicolor/48x48/apps/opentyrian.png rm -f $(DESTDIR)$(icondir)/hicolor/128x128/apps/opentyrian.png .PHONY : clean clean : rm -f $(OBJS) rm -f $(DEPS) rm -f $(TARGET) $(TARGET) : $(OBJS) $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $^ $(ALL_LDLIBS) -include $(DEPS) obj/%.o : src/%.c @mkdir -p "$(dir $@)" $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c -o $@ $< opentyrian-2.1.20221123/NEWS000066400000000000000000000012771432005211200151330ustar00rootroot0000000000000006/02/2007 HOLOVID RELEASE ALPHA 01 Breaking News Authorities report that a Gencore special task force, working from a secret lab at Deliani, are engaged in a project to reimplement the historically significant "videogame" entitled ~Tyrian~, a work regarded by historians as a cultural artifact of utmost importance, and by the Church of Zinglon as a holy prophecy. News of this effort have caused uproar through the galaxy, but Gencore remains silent on the matter and officially-released details are sparse. When approached for comment, Gencore Chancellor Transon Lohk replied, "Who are you and what are you doing in my house? Get out!" Stay tuned for updates to this galaxy-shaking event! opentyrian-2.1.20221123/README000066400000000000000000000033001432005211200153010ustar00rootroot00000000000000OpenTyrian ================================================================================ OpenTyrian is an open-source port of the DOS game Tyrian. Tyrian is an arcade-style vertical scrolling shooter. The story is set in 20,031 where you play as Trent Hawkins, a skilled fighter-pilot employed to fight MicroSol and save the galaxy. Tyrian features a story mode, one- and two-player arcade modes, and networked multiplayer. == Additional Necessary Files ================================================== Tyrian 2.1 data files which have been released as freeware: https://camanis.net/tyrian/tyrian21.zip == Keyboard Controls =========================================================== alt-enter -- toggle full-screen arrow keys -- ship movement space -- fire weapons enter -- toggle rear weapon mode ctrl/alt -- fire left/right sidekick == Network Multiplayer ========================================================= Currently OpenTyrian does not have an arena; as such, networked games must be initiated manually via the command line simultaneously by both players. syntax: opentyrian --net HOSTNAME --net-player-name NAME --net-player-number NUMBER where HOSTNAME is the IP address of your opponent, NUMBER is either 1 or 2 depending on which ship you intend to pilot, and NAME is your alias OpenTyrian uses UDP port 1333 for multiplayer, but in most cases players will not need to open any ports because OpenTyrian makes use of UDP hole punching. == Links ======================================================================= project: https://github.com/opentyrian/opentyrian irc: ircs://irc.oftc.net/#opentyrian forums: https://tyrian2k.proboards.com/board/5 opentyrian-2.1.20221123/doc/000077500000000000000000000000001432005211200151725ustar00rootroot00000000000000opentyrian-2.1.20221123/doc/files.txt000066400000000000000000000025231432005211200170370ustar00rootroot00000000000000Files used by Tyrian and what they contain: cubetxt?.dat: Contains all the datacube text for each episode. demo.?: Recorded player input used for demo playback. levels?.dat: Episode script. This is interpreted by the game and determines the flow of each episode, including everything that happens between levels: - Shop contents - Planets visited - Datacubes shown - Text intermissions - Levels played - etc. music.mus: All the sequenced music tracks and instrument data. newsh?.shp: Enemy graphics. palette.dat: Palettes for tyrian.pic shapes?.dat: Level tileset graphics. tyrend.anm: FMV at the end of episode 3. tyrian.cdt: Credits text. tyrian.hdt: Contains game text, mostly interface strings, as well as the definitions for items, weapons and enemies used in episode 1-3. tyrian.pic: Fullscreen interface backgrounds. Uses palettes from palette.dat tyrian.shp: Interface sprites, fonts, powerups and player shots and ships. tyrian.snd: All sound effects excluding voice samples. tyrian?.lvl: Contains all level tilemaps and level scripts/event data for each episode. tyrian4.lvl also contains item definitions used for that episode instead of the ones in tyrian.hdt. tyrianc.shp: Christmas version of tyrian.shp voices.snd: Voice samples. voicesc.snd: Christmas version of voices.sndopentyrian-2.1.20221123/doc/tools/000077500000000000000000000000001432005211200163325ustar00rootroot00000000000000opentyrian-2.1.20221123/doc/tools/weapon.py000077500000000000000000000113641432005211200202050ustar00rootroot00000000000000#!/bin/env python import xml.dom.minidom as dom import sys import struct WEAP_NUM = 780 struct_fmt = "=250: Attacking power is attack-250 and goes thru enemies) 8x del:8 // Shot life (how long before it disappears) 8x sx:s8 // Horizontal speed 8x sy:s8 // Vertical speed 8x bx:s8 // Horizontal offset 8x by:s8 // Vertical offset 8x sg:16 // Shape number (graphics) acceleration:s8 // Vertical acceleration accelerationx:s8 // Horizontal acceleration circleSize:8 // Perturbs the weapons travel: // 0-19: Circle (radius) // Other produce varying results, experiment (pattern changes roughly ever 20 steps) sound:8 // Sound the effect makes (0 == no sound) trail:8 // Shot trail effect (0xFF == none, 0x62 == smoke, others == various or garbage, experiment) shipBlastFilter:8 // TODO: Not sure, but probably has something to do with the color enemies get after being hit } WEAP_NUM+1 (780+1) // Weapons: struct weaponPort { str_len:8 = 0x1E // Pascal string length, leave alone name:str30 // Weapon name opnum:8 // How many firing modes weapon has. (1 or 2, only really works for rear guns) 2x { // Weapon patterns (What it does, it's an index into the weapons structure). One for each firing mode. 11x op:16 // Each entry corresponds to a power level } cost:16 // How much does the weapon cost. Upgrades are calculated off this. itemGraphic:16 // Weapon graphic in the shop. powerUse:16 // Power drain when firing } PORT_NUM+1 (42+1) // Special moves, those things that show up in the upper left corner with a fake charging bar struct special { str_len:8 = 0x1E // Pascal string length, leave alone name:str30 // Name itemGraphic:16 // The graphic that's displayed during gameplay pwr:8 // Time needed between recharges, may be overridden sType:8 // Determines the type of the special: TODO wpn:16 // Argument used for some kinds of sType } SPECIAL_NUM+1 (46+1) // Generators struct powerSys { str_len:8 = 0x1E // Pascal string length name:str30 // Name itemGraphic:16 // Graphic that's displayed in the shop power:s8 // Amount of power provided by generator speed:8 // Unused cost:16 // Cost in shop } POWER_NUM+1 (6+1) // Ships struct ships { str_len:8 = 0x1E // String length name:str30 // Name shipGraphic:16 // Graphic used in-game itemGraphic:16 // Graphic in shop ani:8 // Sets the threshold for when to change to the turning frames spd:s8 // Unused dmg:8 // amount of armor ship has cost:16 // Cost in shop bigShipGraphic:8 // Big graphic displayed on the left while in shop } SHIP_NUM+1 (13+1) // Sidekicks struct options { str_len:8 = 0x1E // String length name:str30 // Name pwr:8 // Number of charge stages itemGraphic:16 // Graphic in shop cost:16 // Cost in shop tr:8 // Position and movement of sidekick // 0: Attached to side of ship // 1: Follower ship with turning graphics // 2: Attached to front of ship (player can "launch" if right sidekick) // 3: Follower ship with normal graphic // 4: Orbits around ship option:8 // Determines animation for ship // 0: Invisible // 1: Constantly animating // 2: Animates when firing opspd:s8 // Unused ani:8 // Number of frames in animation 20x gr:16 // In-game sprite for each animation frame wport:8 // Weapon used for power usage, etc (index into weaponPort array) wpnum:16 // Bullet pattern (index into weapon array) ammo:8 // Amount of ammo stop:8 // Unused iconGr:8 // Icon in ammo indicators } OPTION_NUM+1 (30+1) struct shields { str_len:8 = 0x1E // String length name:str30 // Name tpwr:8 // Generator power needed to charge mpwr:8 // Amount of protection itemGraphic:16 // Item icon in shop cost:16 // Cost in shop } SHIELD_NUM+1 (10+1) // Enemy definitions (warning: some of this may be wrong) struct enemyDat { ani:8 // Number of frames in animation 3x tur:8 // Type of shot to fire (index into weapon) (1st: downwards, 2nd: right, 3rd: left) 3x freq:8 // Wait between shots xmove:s8 // Horizontal speed ymove:s8 // Vertical speed xaccel:s8 yaccel:s8 xcaccel:s8 // Horizontal acceleration, more is less ycaccel:s8 // Vertical acceleration startx:s16 starty:s16 startxc:s8 startyc:s8 armor:8 esize:8 eGraphic:16 explosionType:8 animate:8 shapeBank:8 xRev:s8 yRev:s8 dgr:16 dLevel:s8 dAni:s8 eLaunchFreq:8 eLaunchType:16 value:s16 eEnemyDie:16 } ENEMY_NUM+1 (850+1) opentyrian-2.1.20221123/linux/000077500000000000000000000000001432005211200155645ustar00rootroot00000000000000opentyrian-2.1.20221123/linux/icons/000077500000000000000000000000001432005211200166775ustar00rootroot00000000000000opentyrian-2.1.20221123/linux/icons/tyrian-128.png000066400000000000000000000121151432005211200212230ustar00rootroot00000000000000PNG  IHDR>asRGBIDATx/EƟ@ "@*'G6?ܻwo]Tg}6I+駟 x ?\$LgYEu+{3M(Rægxೠ(/r"R*.>n[Q{ *Ru;CeZ  }F Tȸ>@ÂA_*}L$_YkA7GI~׃BT1>CJz] ٵKO? ?n`[KK EB%|]EK@8;-Py@"]_Jɣ'; @x(`2@Kp0>|ttVԙy[ݳ{@sSYN(sH`$ @])B UєίؽP7-zo`Ro ]\;W O/ ܙ5$課](K軮Ag`Z8(6=/S]6>{/ʶu*+@(GψQ6 Y UvA n:ػ I-t8$ p@¢:3(8"߹c]dC]4vBK)C%@/ r<|E>T. /X\9o8^NU`2VK KPEz(Qgİ0s*230MKs=ҍA JbQ/qAqA.!s{2WG?W@ `g+2|2&-=p@HԇSD[W֠xW>9[fe1_` !T7Ǖ 7Y Tkq=+ac?K8N19T?}f,5 0¡Et-qC(V!4*ϥ3qH/8o8z*ü3]^%9Mx Auew@ @!TI`EMOxKţf4ZRrt@Il yYPE}/9n?(`XDO,ȹ] ]_ 9nY#xwPpD) e-KƁ#qUfhi FJ$ZwwL=ρU;_YZK̥#"A+W|*rAاdhY9ʿddnh!0 %m+rZPRuRclZ @x-ޕ?8g&U仿=9I *t > =0xlq zIɍ8tN|һPP@4Cs`5K5(T)~Wd!Xwd:E.@u8tr 5 xՇgS޻o/ @9™ @B %Up JcX=/O,cB"OvU=2sj.a.<#ul c:3sQ9 WT݁,h ӆ D1tRa QX"NHq]Q@&}X4Hn@@` \9cGOROWxƶ]@ VmzTQdw6_uyzlw_ang2@`j/;TY)[aI~GI,ĬUϴ|W-ߓS}l_oo^淟>~~[=xCvNO^+* nW>-[)əQůK~|0a r/+1f*l+nYlKt -~%?3 [|a 5Z`J769ri1 )^E]~Кmms ܻFY?~_z恟,>Oo w _*[}L R}ZW>]%K|xde_e'Uge)DW$,Ib1!`$pH w绷O>^k[\l B4P- !vkM 5L(J) =w}lعeZRwX*.!`|ܽ`,g;Ol|OA +2 W!C|seq/|^7U OA"QȓP< 'X/6\]Q}ed:|ZV6Qe19CU8$^rsBYc "Q>pkw=VϹ<7wCnGo@w S`Qwx/@&hGEW&RϢER(&F%,/-@| e\xC:Z<-KB՗9=WPs.9 do&-o9M$/ɍ|ȣ`X[o9r/`TzJێ'%tRYo@Pl@X 1Ɣϳ8sV/d n)MX>\+8| ]EH<_ؐO3|Y|2%t,Az [(/h)v AI)|]Jf5ջ\sWj}Y j<߀9GA44opJvI|)7q7/ntѳ6y(REUWCߧ{r+ z7LhSrZׇ>Y<9C.5I-Z-loZޤ%\YMGrh*P[LmeVГ\+kE/J^G=0+Vm̓m_+r}npN?AE#3|ݴr4(s=V)KElC? y 1[*ʿmO<фTl៻TiGo{=XTIrEQD|ӳ #b@n!ؼ&Eѹ?pBP}ϙe(Y!(7iO :vI9!,1L̀%}VP=7޳dKGdIKʊ~ {وV!a1IfLRQߛ0gܫH xa9oPu[?{h@w/mrKT"m }o+ThH?gt}~/w"zu[+|}98ea|x~:Tm -nG Ë{HKh_ocS r^%}:PYFӠmt/Cecڱ044 R\'/z=}xܩNsZrGEZ<hf.l֮>.1nj]*PZ"pw-)tia\_AQ#,W/b\6:kn+_mݾMQwȇB`38\wJJW 8 o>: ֟V ؄UVW!xLبC0U|N_\o:QOZ#SFB~Z%W]8e &7u{f2>>OX3V~G?я~G?я~G?&1J IENDB`opentyrian-2.1.20221123/linux/icons/tyrian-22.png000066400000000000000000000023231432005211200211340ustar00rootroot00000000000000PNG  IHDRĴl;sRGBIDAT[lS!׳]oN/vNsN-mGY۵m㲱0.BICFHHLG&}Pã1hDј5{ԑ(na|cf`2]Y>eQfa+Gt>(հIm)jnUǔ/(T[XXf?L*|w%l7A2\\V_ʉmx8 J(d:MK6$p1Tȷ O2xj*/!^ǽ{dBH֖T&`GF$yAj Iֵ2/Z l7갗S!"]sVt4s`ub_<^ƅam55\uRsQ/vw?jF8y`_;9cKQiE>չ##89~JٟwAAߊBFb$n"|~pq3}f9M.)I9OK{IENDB`opentyrian-2.1.20221123/linux/icons/tyrian-24.png000066400000000000000000000026221432005211200211400ustar00rootroot00000000000000PNG  IHDRw=sRGBLIDAToS_ela^i{z?zvg[m]+mL@" HĈO/> ?WQ@bHyH }xA",[RPA5Jp5v6S }7)xK5A15hz>(0>b|ՙtf3x8͖t^}(%f ! |D'.U:lvv;]aӞz}} J۬ύBԇ9$bR6*H跪R/Bd ՇblN۰}FhIF`\Gs<֜8?2ءh–zYȇJ$^P-V ᰝ8y ܰ: XlWDeˌ | .0`Qikǁ,kǝc4w,-ÆQ"9w}6^<{PJq'\\9"Hp0F4;{̿TcZ_Qk++bdP%.&%5ѥk<F Z$jЭo un̨A;#懂w9p(I㩙.18L܌ Ntø0 #Ŏƪ[)6xO񾰨5xH6P=C*L>\]n;pu_'FyP,G2hkoAsSh+{ wđ\Z+GzprKxXԆ!TmgKyVBrmD_Jy&UND"'h*H͡\}ڌ:=7?/=^D,\[DTE"d,X=@Df+eKc9LLNaqJ]}ΤsPv86>ƤBqua(ݍ稬8%ܽ ŔGM*[\ZzggiӅ$&vazrx{[I'ւ>$N;E_<$D^D<{JMZ Kv:ׂT"0=glTQd˄GUGLEɕX#ꅝ}f;8!C w{`շ f 6UPۄ‘h5ړa5h,Z-" B9b2{8t$q , rѰIZ *I0v)aъߧVf2³^L`t[DTEebx8ꅗ3Tۗؾq7S3{moD}ND$r~Fv?i܂=69"\u8Dڨzܤj:PagB-8]sUMaax'+xz!#=p?lA&C +MURtDSD ,n^ʄwcXRxv)d^LmhnClF_0wߜE"AoĹf\&@[k13b: '߉X<LD,hZWH'I qlq\QewxЇ0\ъ@+rn1 DMMo)cڌ.qx@mRI{Z}gi5t1\$Q&566lV3RAiGW۝%%F,~Ye0XK]j >[ ,YZgYRӱbw>aH ?jV񙄃SW4)_$L_MWJQhж$$Q)gQ#""bsM# 'mÖ1|;pqI­pO-w4҉N m~tGIpao3! M_3硫6/No`* f즲뾘O05=X,X髒vO7<<~rr MQ;m϶Z@C}K̺[L={wڍdkBf;QADe}?ЏR&ي}}e<<< UiT'7 7 7O{qcH04GJbyVqn箝H$S}\G\H|>t|vMd0@ZvmjͨẠCe  ՎVMI]a=rfk #F!A.Aܣ' .X B宰#Y;d7ENzxd32̶kKN,~z`ԻnVè|>6}½UWX\B.G-r{))-f35H-AyˆBJ) tF TH\.T&ČM=N6Mt* /\b׮f]Q+AhM}~H!1OPS|b"[ NےqY I!3gϐ[Ⱦnܽ{$[dz8r9`K$bqle׮n;):yznzۊGN?E/Yޯ[Nw"S%QQkql*0;Mio lAM=4&A!$j &UDY/LXEߎۅQ:6%>]vO1`FSL-H%Hy^e,lK O©哔C9+-<5 *nAsR6&o$u7EZtuWH϶$.(U^\~0DgOY7‰ }\ykQEtdaȁ6 tAweW/E)4K;($Fmx[cMQ|(پm'Ɖ4F"]_n qiQY^)GSw2="|Dв/-//>C%4^MF%r,:qlTc(*"#q1YMy=2Mw ֿ5'Y Z^z"lƣ|..xo }݈0A@r&LlՓ01kA1$UE@b! h`s0HF.c$ɽ[ŘL07ոi̍'phPRtdsQKGl2yvVp-dMtR&|.1i\Lb&|b`"`LFUew .[$nb' yFz[rlʨ'9F!=B9IŤSƃ$M|1 "]g爅z$tJ"fxdZɦªը" L¬!JZx5,&dQާ!Ve*29#{84"w-ddх lHg*\2B2>-;DŮ"zZ$Me1 _NRc76‘Sio#LR=>ÝO*iꉄA> #'%:$Rx:l[ؔdWCX~}CC+ɖf95%\*dLJH-ph7f񛃽K {6#uo_F&š\|{-W*(6Ʋ:]ڸq33h(9})}Vë6\M=%Ճ<"JIÆFl`c2в]<5!h~@ܿHnE z|E02i5kŢ`ZvMsKu}##Je%|[5^ VceKxn ~ V ӝ :[쿬t`0"Ԯ \=Qc^^_5@)F Tбg~ Έ ؊si|jPB Wm ]3^[UT3xW>SMsSaϳ;6#ᵂlZuOMuf Ė$&+msQm.7_ S] l*FS)񣛝Jm]ILh,^i5]9xͺkKH ^kʓF!mg+orxŀߎT basing on AshTR's README file. opentyrian-2.1.20221123/linux/opentyrian.desktop000066400000000000000000000002651432005211200213520ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=OpenTyrian Comment=An arcade-style shoot 'em up Icon=opentyrian Categories=Game;ArcadeGame; TryExec=opentyrian Exec=opentyrian Terminal=false opentyrian-2.1.20221123/lower-script.sh000077500000000000000000000005051432005211200174160ustar00rootroot00000000000000#!/bin/bash if [[ $1 ]]; then target=$1 else target="./data" fi read -p "Lowercase all files in $target directory? [y/N] " confirm if [[ `echo $confirm | tr '[:upper:]' '[:lower:]'` == 'y' ]]; then for f in $target/*; do g=`basename "$f" | tr '[:upper:]' '[:lower:]'` mv -fv "$f" "$target/$g" 2> /dev/null done fi opentyrian-2.1.20221123/src/000077500000000000000000000000001432005211200152145ustar00rootroot00000000000000opentyrian-2.1.20221123/src/animlib.c000066400000000000000000000266661432005211200170130ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "animlib.h" #include "file.h" #include "keyboard.h" #include "network.h" #include "nortsong.h" #include "palette.h" #include "sizebuf.h" #include "video.h" #include #include /*** Structs ***/ /* The actual header has a lot of fields that are basically useless to us since * we both set our own framerate and the format itself only allows for * 320x200x8. Should a (nonexistent) ani be played that doesn't have the same * assumed values we are going to use, TOO BAD. It'll just be treated as * corrupt in playback. */ #define PALETTE_OFFSET 0x100 // 128 + sizeof(header) #define PAGEHEADER_OFFSET 0x500 // PALETTE_OFFSET + sizeof(palette) #define ANIM_OFFSET 0x0B00 // PAGEHEADER_OFFSET + sizeof(largepageheader) * 256 #define ANI_PAGE_SIZE 0x10000 // 65536. typedef struct anim_FileHeader_s { Uint16 nlps; /* Number of 'pages', max 256. */ Uint32 nRecords; /* Number of 'records', max 65535 */ } anim_FileHeader_t; typedef struct anim_LargePageHeader_s { Uint16 baseRecord; /* The first record's number */ Uint16 nRecords; /* Number of records. Supposedly there are bit flags but I saw no such code */ Uint16 nBytes; /* Number of bytes used, excluding headers */ } anim_LargePageHeader_t; /*** Globals ***/ Uint8 CurrentPageBuffer[65536]; anim_LargePageHeader_t PageHeader[256]; Uint16 CurrentPageRecordSizes[256]; anim_LargePageHeader_t CurrentPageHeader; anim_FileHeader_t FileHeader; unsigned int Curlpnum; FILE * InFile; /*** Function decs ***/ int JE_playRunSkipDump(Uint8 *, unsigned int); void JE_closeAnim(void); int JE_loadAnim(const char *); int JE_renderFrame(unsigned int); int JE_findPage (unsigned int); int JE_loadPage(unsigned int); /*** Implementation ***/ /* Loads the given page into memory. * * Returns 0 on success or nonzero on failure (bad data) */ int JE_loadPage(unsigned int pagenumber) { unsigned int i, pageSize; if (Curlpnum == pagenumber) return 0; /* Already loaded */ Curlpnum = pagenumber; /* We need to seek to the page and load it into our buffer. * Pages have a fixed size of 0x10000; any left over space is padded * unless it's the end of the file. * * Pages repeat their headers for some reason. They then have two bytes of * padding followed by a word for every record. THEN the data starts. */ fseek(InFile, ANIM_OFFSET + (pagenumber * ANI_PAGE_SIZE), SEEK_SET); fread_u16_die(&CurrentPageHeader.baseRecord, 1, InFile); fread_u16_die(&CurrentPageHeader.nRecords, 1, InFile); fread_u16_die(&CurrentPageHeader.nBytes, 1, InFile); fseek(InFile, 2, SEEK_CUR); fread_u16_die(CurrentPageRecordSizes, CurrentPageHeader.nRecords, InFile); /* What remains is the 'compressed' data */ fread_die(CurrentPageBuffer, 1, CurrentPageHeader.nBytes, InFile); /* Okay, we've succeeded in all our IO checks. Now, make sure the * headers aren't lying or damaged or something. */ pageSize = 0; for (i = 0; i < CurrentPageHeader.nRecords; i++) pageSize += CurrentPageRecordSizes[i]; if (pageSize != CurrentPageHeader.nBytes) return -1; /* So far, so good */ return 0; } int JE_findPage(unsigned int framenumber) { unsigned int i; for (i = 0; i < FileHeader.nlps; i++) { if (PageHeader[i].baseRecord <= framenumber && PageHeader[i].baseRecord + PageHeader[i].nRecords > framenumber) { return i; } } return -1; /* Did not find */ } int JE_renderFrame(unsigned int framenumber) { unsigned int i, offset, destframe; destframe = framenumber - CurrentPageHeader.baseRecord; offset = 0; for (i = 0; i < destframe; i++) offset += CurrentPageRecordSizes[i]; return (JE_playRunSkipDump(CurrentPageBuffer + offset + 4, CurrentPageRecordSizes[destframe] - 4)); } void JE_playAnim(const char *animfile, JE_byte startingframe, JE_byte speed) { unsigned int i; int pageNum; if (JE_loadAnim(animfile) != 0) return; /* Failed to open or process file */ /* Blank screen */ JE_clr256(VGAScreen); JE_showVGA(); /* re FileHeader.nRecords-1: It's -1 in the pascal too. * The final frame is a delta of the first, and we don't need that. * We could also, if we ever ended up needing to loop anis, check * the bools in the header to see if we should render the last * frame. But that's never going to be necessary :) */ for (i = startingframe; i < FileHeader.nRecords-1; i++) { /* Handle boring crap */ setDelay(speed); /* Load required frame. The loading function is smart enough to not re-load an already loaded frame */ pageNum = JE_findPage(i); if (pageNum == -1) break; if (JE_loadPage(pageNum) != 0) break; /* render frame. */ if (JE_renderFrame(i) != 0) break; JE_showVGA(); /* Return early if user presses a key */ service_SDL_events(true); if (newkey) break; /* Wait until we need the next frame */ NETWORK_KEEP_ALIVE(); wait_delay(); } JE_closeAnim(); } /* loadAnim opens the file and loads data from it into the header structs. * It should take care to clean up after itself should an error occur. */ int JE_loadAnim(const char *filename) { unsigned int i; long fileSize; char temp[4]; Curlpnum = -1; InFile = dir_fopen(data_dir(), filename, "rb"); if (InFile == NULL) return -1; fileSize = ftell_eof(InFile); if (fileSize < ANIM_OFFSET) { /* We don't know the exact size our file should be yet, * but we do know it should be way more than this */ fclose(InFile); return -1; } /* Read in the header. The header is 256 bytes long or so, * but that includes a lot of padding as well as several * vars we really don't care about. We shall check the ID and extract * the handful of vars we care about. Every value in the header that * is constant will be ignored. */ fread_die(&temp, 1, 4, InFile); /* The ID, should equal "LPF " */ fseek(InFile, 2, SEEK_CUR); /* skip over this word */ fread_u16_die(&FileHeader.nlps, 1, InFile); /* Number of pages */ fread_u32_die(&FileHeader.nRecords, 1, InFile); /* Number of records */ if (memcmp(temp, "LPF ", 4) != 0 || FileHeader.nlps == 0 || FileHeader.nRecords == 0 || FileHeader.nlps > 256 || FileHeader.nRecords > 65535) { fclose(InFile); return -1; } /* Read in headers */ fseek(InFile, PAGEHEADER_OFFSET, SEEK_SET); for (i = 0; i < FileHeader.nlps; i++) { fread_u16_die(&PageHeader[i].baseRecord, 1, InFile); fread_u16_die(&PageHeader[i].nRecords, 1, InFile); fread_u16_die(&PageHeader[i].nBytes, 1, InFile); } /* Now we have enough information to calculate the 'expected' file size. * Our calculation SHOULD be equal to fileSize, but we won't begrudge * padding */ if (fileSize < (FileHeader.nlps-1) * ANI_PAGE_SIZE + ANIM_OFFSET + PageHeader[FileHeader.nlps-1].nBytes + PageHeader[FileHeader.nlps-1].nRecords * 2 + 8) { fclose(InFile); return -1; } /* Now read in the palette. */ fseek(InFile, PALETTE_OFFSET, SEEK_SET); for (i = 0; i < 256; i++) { Uint8 bgru[4]; fread_u8_die(bgru, 4, InFile); colors[i].b = bgru[0]; colors[i].g = bgru[1]; colors[i].r = bgru[2]; } set_palette(colors, 0, 255); /* Whew! That was hard. Let's go grab some beers! */ return 0; } void JE_closeAnim(void) { fclose(InFile); } /* RunSkipDump decompresses the video. There are three operations, run, skip, * and dump. They can be used in either byte or word variations, making six * possible actions, and there's a seventh 'stop' action, which looks * like 0x80 0x00 0x00. * * Run is a memset. * Dump is a memcpy. * Skip leaves the old data intact and simply increments the pointers. * * returns 0 on success or 1 if decompressing failed. Failure to decompress * indicates a broken or malicious file; playback should terminate. */ int JE_playRunSkipDump(Uint8 *incomingBuffer, unsigned int IncomingBufferLength) { sizebuf_t Buffer_IN, Buffer_OUT; sizebuf_t * pBuffer_IN = &Buffer_IN, * pBuffer_OUT = &Buffer_OUT; #define ANI_SHORT_RLE 0x00 #define ANI_SHORT_SKIP 0x80 #define ANI_LONG_OP 0x80 #define ANI_LONG_COPY_OR_RLE 0x8000 #define ANI_LONG_RLE 0x4000 #define ANI_STOP 0x0000 SZ_Init(pBuffer_IN, incomingBuffer, IncomingBufferLength); SZ_Init(pBuffer_OUT, VGAScreen->pixels, VGAScreen->h * VGAScreen->pitch); /* 320x200 is the only supported format. * Assert is here as a hint should our screen size ever changes. * As for how to decompress to the wrong screen size... */ assert(VGAScreen->h * VGAScreen->pitch == 320 * 200); while (true) { /* Get one byte. This byte may have flags that tell us more */ unsigned int opcode = MSG_ReadByte(pBuffer_IN); /* Before we continue, check the error states/ * We should *probably* check these after every read and write, but * I've rigged it so that the buffers will never go out of bounds. * So we can afford to be lazy; if the buffer overflows below it will * silently fail its writes and we'll catch the failure on our next * run through the loop. A failure means we should be * leaving ANYWAY. The contents of our buffers doesn't matter. */ if (SZ_Error(pBuffer_IN) || SZ_Error(pBuffer_OUT)) return -1; /* Divide into 'short' and 'long' */ if (opcode == ANI_LONG_OP) /* long ops */ { opcode = MSG_ReadWord(pBuffer_IN); if (opcode == ANI_STOP) /* We are done decompressing. Leave */ { break; } else if (!(opcode & ANI_LONG_COPY_OR_RLE)) /* If it's not those two, it's a skip */ { unsigned int count = opcode; SZ_Seek(pBuffer_OUT, count, SEEK_CUR); } else /* Now things get a bit more interesting... */ { opcode &= ~ANI_LONG_COPY_OR_RLE; /* Clear that flag */ if (opcode & ANI_LONG_RLE) /* RLE */ { unsigned int count = opcode & ~ANI_LONG_RLE; /* Clear flag */ /* Extract another byte */ unsigned int value = MSG_ReadByte(pBuffer_IN); /* The actual run */ SZ_Memset(pBuffer_OUT, value, count); } else { /* Long copy */ unsigned int count = opcode; /* Copy */ SZ_Memcpy2(pBuffer_OUT, pBuffer_IN, count); } } } /* End of long ops */ else /* short ops */ { if (opcode & ANI_SHORT_SKIP) /* Short skip, move pointer only */ { unsigned int count = opcode & ~ANI_SHORT_SKIP; /* clear flag to get count */ SZ_Seek(pBuffer_OUT, count, SEEK_CUR); } else if (opcode == ANI_SHORT_RLE) /* Short RLE, memset the destination */ { /* Extract a few more bytes */ unsigned int count = MSG_ReadByte(pBuffer_IN); unsigned int value = MSG_ReadByte(pBuffer_IN); /* Run */ SZ_Memset(pBuffer_OUT, value, count); } else /* Short copy, memcpy from src to dest. */ { unsigned int count = opcode; /* Dump */ SZ_Memcpy2(pBuffer_OUT, pBuffer_IN, count); } } /* End of short ops */ } /* And that's that */ return 0; } opentyrian-2.1.20221123/src/animlib.h000066400000000000000000000017541432005211200170070ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ANIMLIB_H #define ANIMLIB_H #include "opentyr.h" void JE_playAnim(const char *animfile, JE_byte startingframe, JE_byte speed); #endif /* ANIMLIB_H */ opentyrian-2.1.20221123/src/arg_parse.c000066400000000000000000000150631432005211200173300ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "arg_parse.h" #include #include #include static void permute(const char *argv[], int *first_nonopt, int *first_opt, int after_opt); static int parse_short_opt(int argc, const char *const argv[], const Options *options, Option *option); static int parse_long_opt(int argc, const char *const argv[], const Options *options, Option *option); /*! * \brief Locate a character in a a string. * * \param[in] s the string * \param[in] c the character * \return the pointer to the first occurrence of \p c in \p s if there is an occurrences; * otherwise the pointer to the terminating NUL character of \p s */ static char *ot_strchrnul(const char *s, int c); Option parse_args(int argc, const char *argv[], const Options *options) { static int argn = 1; static bool no_more_options = false; static int first_nonopt = 1; Option option = { NOT_OPTION, NULL, 0 }; option.argn = first_nonopt; while (argn < argc) { size_t arg_len = strlen(argv[argn]); if (!no_more_options && argv[argn][0] == '-' && // first char is '-' arg_len > 1) // option is not "-" { option.argn = argn; if (argv[argn][1] == '-') // string begins with "--" { if (arg_len == 2) // "--" alone indicates end of options { ++argn; no_more_options = true; } else { argn = parse_long_opt(argc, argv, options, &option); } } else { argn = parse_short_opt(argc, argv, options, &option); } // shift option in front of non-options permute(argv, &first_nonopt, &option.argn, argn); // don't include "--" in non-options if (no_more_options) ++option.argn; break; } else { // skip non-options, permute later when option encountered ++argn; } } return option; } static void permute(const char *argv[], int *first_nonopt, int *first_opt, int after_opt) { const int nonopts = *first_opt - *first_nonopt; // slide each of the options in front of the non-options for (int i = *first_opt; i < after_opt; ++i) { for (int j = i; j > *first_nonopt; --j) { // swap argv[j] and argv[j - 1] const char *temp = argv[j]; argv[j] = argv[j - 1]; argv[j - 1] = temp; } // position of first non-option shifts right once for each option ++(*first_nonopt); } // position of first option is initial position of first non-option *first_opt -= nonopts; } static int parse_short_opt(int argc, const char *const argv[], const Options *options, Option *option) { static size_t offset = 1; // ignore the "-" int argn = option->argn; const char *arg = argv[argn]; const size_t arg_len = strlen(arg); const bool arg_attached = (offset + 1 < arg_len), // possible argument attached? last_in_argv = (argn == argc - 1); option->value = INVALID_OPTION; for (; !(options->short_opt == 0 && options->long_opt == NULL); ++options) { if (options->short_opt != 0 && options->short_opt == arg[offset]) { option->value = options->value; if (options->has_arg) { if (arg_attached) // arg directly follows option { option->arg = arg + offset + 1; offset = arg_len; } else if (!last_in_argv) // arg is next in argv { option->arg = argv[++argn]; offset = arg_len; } else { option->value = OPTION_MISSING_ARG; break; } } break; } } switch (option->value) { case INVALID_OPTION: fprintf(stderr, "%s: invalid option -- '%c'\n", argv[0], argv[option->argn][offset]); break; case OPTION_MISSING_ARG: fprintf(stderr, "%s: option requires an argument -- '%c'\n", argv[0], argv[option->argn][offset]); break; } if (++offset >= arg_len) { ++argn; offset = 1; } return argn; // which arg in argv that parse_args() should examine when called again } static int parse_long_opt(int argc, const char *const argv[], const Options *options, Option *option) { int argn = option->argn; const char *arg = argv[argn] + 2; // ignore the "--" const size_t arg_len = strlen(arg), arg_opt_len = ot_strchrnul(arg, '=') - arg; // length before "=" const bool arg_attached = (arg_opt_len < arg_len), // argument attached using "="? last_in_argv = (argn == argc - 1); option->value = INVALID_OPTION; for (; !(options->short_opt == 0 && options->long_opt == NULL); ++options) { if (options->long_opt != NULL && strncmp(options->long_opt, arg, arg_opt_len) == 0) // matches (partially, at least) { if (option->value != INVALID_OPTION) // other match already found { option->value = AMBIGUOUS_OPTION; break; } option->value = options->value; if (options->has_arg) { if (arg_attached) // arg is after "=" { option->arg = arg + arg_opt_len + 1; } else if (!last_in_argv) // arg is next in argv { option->arg = argv[++argn]; } else // arg is missing { option->value = OPTION_MISSING_ARG; // can't break, gotta check for ambiguity } } if (arg_opt_len == strlen(options->long_opt)) // exact match break; // can't break for partial match, gotta check for ambiguity } } switch (option->value) { case INVALID_OPTION: fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[option->argn]); break; case AMBIGUOUS_OPTION: fprintf(stderr, "%s: option '%s' is ambiguous\n", argv[0], argv[option->argn]); break; case OPTION_MISSING_ARG: fprintf(stderr, "%s: option '%s' requires an argument\n", argv[0], argv[option->argn]); break; } ++argn; return argn; // which arg in argv that parse_args() should examine when called again } static char *ot_strchrnul(const char *s, int c) { for (; *s != c && *s != '\0'; ++s) ; return (char *)s; } opentyrian-2.1.20221123/src/arg_parse.h000066400000000000000000000027151432005211200173350ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ARG_PARSE_H #define ARG_PARSE_H #include // this is essentially a reimplementation of getopt_long() typedef struct { int value; char short_opt; const char *long_opt; bool has_arg; } Options; enum { // indicates that argv[argn..argc) are not options NOT_OPTION = 0, /* behavior of parse_args() is undefined after it has returned any of the following values */ INVALID_OPTION = -1, AMBIGUOUS_OPTION = -2, OPTION_MISSING_ARG = -3 }; typedef struct { int value; const char *arg; int argn; } Option; Option parse_args(int argc, const char *argv[], const Options *options); #endif /* ARG_PARSE_H */ opentyrian-2.1.20221123/src/backgrnd.c000066400000000000000000000303751432005211200171430ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "backgrnd.h" #include "config.h" #include "mtrand.h" #include "opentyr.h" #include "varz.h" #include "video.h" #include /*Special Background 2 and Background 3*/ /*Back Pos 3*/ JE_word backPos, backPos2, backPos3; JE_word backMove, backMove2, backMove3; /*Main Maps*/ JE_word mapX, mapY, mapX2, mapX3, mapY2, mapY3; JE_byte **mapYPos, **mapY2Pos, **mapY3Pos; JE_word mapXPos, oldMapXOfs, mapXOfs, mapX2Ofs, mapX2Pos, mapX3Pos, oldMapX3Ofs, mapX3Ofs, tempMapXOfs; intptr_t mapXbpPos, mapX2bpPos, mapX3bpPos; JE_byte map1YDelay, map1YDelayMax, map2YDelay, map2YDelayMax; JE_boolean anySmoothies; JE_byte smoothie_data[9]; /* [1..9] */ void JE_darkenBackground(JE_word neat) /* wild detail level */ { Uint8 *s = VGAScreen->pixels; /* screen pointer, 8-bit specific */ int x, y; s += 24; for (y = 184; y; y--) { for (x = 264; x; x--) { *s = ((((*s & 0x0f) << 4) - (*s & 0x0f) + ((((x - neat - y) >> 2) + *(s-2) + (y == 184 ? 0 : *(s-(VGAScreen->pitch-1)))) & 0x0f)) >> 4) | (*s & 0xf0); s++; } s += VGAScreen->pitch - 264; } } void blit_background_row(SDL_Surface *surface, int x, int y, Uint8 **map) { assert(surface->format->BitsPerPixel == 8); Uint8 *pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x, *pixels_ll = (Uint8 *)surface->pixels, // lower limit *pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (int y = 0; y < 28; y++) { // not drawing on screen yet; skip y if ((pixels + (12 * 24)) < pixels_ll) { pixels += surface->pitch; continue; } for (int tile = 0; tile < 12; tile++) { Uint8 *data = *(map + tile); // no tile; skip tile if (data == NULL) { pixels += 24; continue; } data += y * 24; for (int x = 24; x; x--) { if (pixels >= pixels_ul) return; if (pixels >= pixels_ll && *data != 0) *pixels = *data; pixels++; data++; } } pixels += surface->pitch - 12 * 24; } } void blit_background_row_blend(SDL_Surface *surface, int x, int y, Uint8 **map) { assert(surface->format->BitsPerPixel == 8); Uint8 *pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x, *pixels_ll = (Uint8 *)surface->pixels, // lower limit *pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (int y = 0; y < 28; y++) { // not drawing on screen yet; skip y if ((pixels + (12 * 24)) < pixels_ll) { pixels += surface->pitch; continue; } for (int tile = 0; tile < 12; tile++) { Uint8 *data = *(map + tile); // no tile; skip tile if (data == NULL) { pixels += 24; continue; } data += y * 24; for (int x = 24; x; x--) { if (pixels >= pixels_ul) return; if (pixels >= pixels_ll && *data != 0) *pixels = (*data & 0xf0) | (((*pixels & 0x0f) + (*data & 0x0f)) / 2); pixels++; data++; } } pixels += surface->pitch - 12 * 24; } } void draw_background_1(SDL_Surface *surface) { SDL_FillRect(surface, NULL, 0); Uint8 **map = (Uint8 **)mapYPos + mapXbpPos - 12; for (int i = -1; i < 7; i++) { blit_background_row(surface, mapXPos, (i * 28) + backPos, map); map += 14; } } void draw_background_2(SDL_Surface *surface) { if (map2YDelayMax > 1 && backMove2 < 2) backMove2 = (map2YDelay == 1) ? 1 : 0; if (background2 != 0) { // water effect combines background 1 and 2 by synchronizing the x coordinate int x = smoothies[1] ? mapXPos : mapX2Pos; Uint8 **map = (Uint8 **)mapY2Pos + (smoothies[1] ? mapXbpPos : mapX2bpPos) - 12; for (int i = -1; i < 7; i++) { blit_background_row(surface, x, (i * 28) + backPos2, map); map += 14; } } /*Set Movement of background*/ if (--map2YDelay == 0) { map2YDelay = map2YDelayMax; backPos2 += backMove2; if (backPos2 > 27) { backPos2 -= 28; mapY2--; mapY2Pos -= 14; /*Map Width*/ } } } void draw_background_2_blend(SDL_Surface *surface) { if (map2YDelayMax > 1 && backMove2 < 2) backMove2 = (map2YDelay == 1) ? 1 : 0; Uint8 **map = (Uint8 **)mapY2Pos + mapX2bpPos - 12; for (int i = -1; i < 7; i++) { blit_background_row_blend(surface, mapX2Pos, (i * 28) + backPos2, map); map += 14; } /*Set Movement of background*/ if (--map2YDelay == 0) { map2YDelay = map2YDelayMax; backPos2 += backMove2; if (backPos2 > 27) { backPos2 -= 28; mapY2--; mapY2Pos -= 14; /*Map Width*/ } } } void draw_background_3(SDL_Surface *surface) { /* Movement of background */ backPos3 += backMove3; if (backPos3 > 27) { backPos3 -= 28; mapY3--; mapY3Pos -= 15; /*Map Width*/ } Uint8 **map = (Uint8 **)mapY3Pos + mapX3bpPos - 12; for (int i = -1; i < 7; i++) { blit_background_row(surface, mapX3Pos, (i * 28) + backPos3, map); map += 15; } } void JE_filterScreen(JE_shortint col, JE_shortint int_) { Uint8 *s = NULL; /* screen pointer, 8-bit specific */ int x, y; unsigned int temp; if (filterFade) { levelBrightness += levelBrightnessChg; if ((filterFadeStart && levelBrightness < -14) || levelBrightness > 14) { levelBrightnessChg = -levelBrightnessChg; filterFadeStart = false; levelFilter = levelFilterNew; } if (!filterFadeStart && levelBrightness == 0) { filterFade = false; levelBrightness = -99; } } if (col != -99 && filtrationAvail) { s = VGAScreen->pixels; s += 24; col <<= 4; for (y = 184; y; y--) { for (x = 264; x; x--) { *s = col | (*s & 0x0f); s++; } s += VGAScreen->pitch - 264; } } if (int_ != -99 && explosionTransparent) { s = VGAScreen->pixels; s += 24; for (y = 184; y; y--) { for (x = 264; x; x--) { temp = (*s & 0x0f) + int_; *s = (*s & 0xf0) | (temp >= 0x1f ? 0 : (temp >= 0x0f ? 0x0f : temp)); s++; } s += VGAScreen->pitch - 264; } } } void JE_checkSmoothies(void) { anySmoothies = (processorType > 2 && (smoothies[1-1] || smoothies[2-1])) || (processorType > 1 && (smoothies[3-1] || smoothies[4-1] || smoothies[5-1])); } void lava_filter(SDL_Surface *dst, SDL_Surface *src) { assert(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 8); /* we don't need to check for over-reading the pixel surfaces since we only * read from the top 185+1 scanlines, and there should be 320 */ const int dst_pitch = dst->pitch; Uint8 *dst_pixel = (Uint8 *)dst->pixels + (185 * dst_pitch); const Uint8 * const dst_pixel_ll = (Uint8 *)dst->pixels; // lower limit const int src_pitch = src->pitch; const Uint8 *src_pixel = (Uint8 *)src->pixels + (185 * src->pitch); const Uint8 * const src_pixel_ll = (Uint8 *)src->pixels; // lower limit int w = 320 * 185 - 1; for (int y = 185 - 1; y >= 0; --y) { dst_pixel -= (dst_pitch - 320); // in case pitch is not 320 src_pixel -= (src_pitch - 320); // in case pitch is not 320 for (int x = 320 - 1; x >= 0; x -= 8) { int waver = abs(((w >> 9) & 0x0f) - 8) - 1; w -= 8; for (int xi = 8 - 1; xi >= 0; --xi) { --dst_pixel; --src_pixel; // value is average value of source pixel (2x), destination pixel above, and destination pixel below (all with waver) // hue is red Uint8 value = 0; if (src_pixel + waver >= src_pixel_ll) value += (*(src_pixel + waver) & 0x0f) * 2; value += *(dst_pixel + waver + dst_pitch) & 0x0f; if (dst_pixel + waver - dst_pitch >= dst_pixel_ll) value += *(dst_pixel + waver - dst_pitch) & 0x0f; *dst_pixel = (value / 4) | 0x70; } } } } void water_filter(SDL_Surface *dst, SDL_Surface *src) { assert(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 8); Uint8 hue = smoothie_data[1] << 4; /* we don't need to check for over-reading the pixel surfaces since we only * read from the top 185+1 scanlines, and there should be 320 */ const int dst_pitch = dst->pitch; Uint8 *dst_pixel = (Uint8 *)dst->pixels + (185 * dst_pitch); const Uint8 *src_pixel = (Uint8 *)src->pixels + (185 * src->pitch); int w = 320 * 185 - 1; for (int y = 185 - 1; y >= 0; --y) { dst_pixel -= (dst_pitch - 320); // in case pitch is not 320 src_pixel -= (src->pitch - 320); // in case pitch is not 320 for (int x = 320 - 1; x >= 0; x -= 8) { int waver = abs(((w >> 10) & 0x07) - 4) - 1; w -= 8; for (int xi = 8 - 1; xi >= 0; --xi) { --dst_pixel; --src_pixel; // pixel is copied from source if not blue // otherwise, value is average of value of source pixel and destination pixel below (with waver) if ((*src_pixel & 0x30) == 0) { *dst_pixel = *src_pixel; } else { Uint8 value = *src_pixel & 0x0f; value += *(dst_pixel + waver + dst_pitch) & 0x0f; *dst_pixel = (value / 2) | hue; } } } } } void iced_blur_filter(SDL_Surface *dst, SDL_Surface *src) { assert(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 8); Uint8 *dst_pixel = dst->pixels; const Uint8 *src_pixel = src->pixels; for (int y = 0; y < 184; ++y) { for (int x = 0; x < 320; ++x) { // value is average value of source pixel and destination pixel // hue is icy blue const Uint8 value = (*src_pixel & 0x0f) + (*dst_pixel & 0x0f); *dst_pixel = (value / 2) | 0x80; ++dst_pixel; ++src_pixel; } dst_pixel += (dst->pitch - 320); // in case pitch is not 320 src_pixel += (src->pitch - 320); // in case pitch is not 320 } } void blur_filter(SDL_Surface *dst, SDL_Surface *src) { assert(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 8); Uint8 *dst_pixel = dst->pixels; const Uint8 *src_pixel = src->pixels; for (int y = 0; y < 184; ++y) { for (int x = 0; x < 320; ++x) { // value is average value of source pixel and destination pixel // hue is source pixel hue const Uint8 value = (*src_pixel & 0x0f) + (*dst_pixel & 0x0f); *dst_pixel = (value / 2) | (*src_pixel & 0xf0); ++dst_pixel; ++src_pixel; } dst_pixel += (dst->pitch - 320); // in case pitch is not 320 src_pixel += (src->pitch - 320); // in case pitch is not 320 } } /* Background Starfield */ typedef struct { Uint8 color; JE_word position; // relies on overflow wrap-around int speed; } StarfieldStar; #define MAX_STARS 100 #define STARFIELD_HUE 0x90 static StarfieldStar starfield_stars[MAX_STARS]; int starfield_speed; void initialize_starfield(void) { for (int i = MAX_STARS-1; i >= 0; --i) { starfield_stars[i].position = mt_rand() % 320 + mt_rand() % 200 * VGAScreen->pitch; starfield_stars[i].speed = mt_rand() % 3 + 2; starfield_stars[i].color = mt_rand() % 16 + STARFIELD_HUE; } } void update_and_draw_starfield(SDL_Surface* surface, int move_speed) { Uint8* p = (Uint8*)surface->pixels; for (int i = MAX_STARS-1; i >= 0; --i) { StarfieldStar* star = &starfield_stars[i]; star->position += (star->speed + move_speed) * surface->pitch; if (star->position < 177 * surface->pitch) { if (p[star->position] == 0) { p[star->position] = star->color; } // If star is bright enough, draw surrounding pixels if (star->color - 4 >= STARFIELD_HUE) { if (p[star->position + 1] == 0) p[star->position + 1] = star->color - 4; if (star->position > 0 && p[star->position - 1] == 0) p[star->position - 1] = star->color - 4; if (p[star->position + surface->pitch] == 0) p[star->position + surface->pitch] = star->color - 4; if (star->position >= surface->pitch && p[star->position - surface->pitch] == 0) p[star->position - surface->pitch] = star->color - 4; } } } } opentyrian-2.1.20221123/src/backgrnd.h000066400000000000000000000045311432005211200171430ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef BACKGRND_H #define BACKGRND_H #include "opentyr.h" #include "SDL.h" #include extern JE_word backPos, backPos2, backPos3; extern JE_word backMove, backMove2, backMove3; extern JE_word mapX, mapY, mapX2, mapX3, mapY2, mapY3; extern JE_byte **mapYPos, **mapY2Pos, **mapY3Pos; extern JE_word mapXPos, oldMapXOfs, mapXOfs, mapX2Ofs, mapX2Pos, mapX3Pos, oldMapX3Ofs, mapX3Ofs, tempMapXOfs; extern intptr_t mapXbpPos, mapX2bpPos, mapX3bpPos; extern JE_byte map1YDelay, map1YDelayMax, map2YDelay, map2YDelayMax; extern JE_boolean anySmoothies; // if yes, I want one :D extern JE_byte smoothie_data[9]; extern int starfield_speed; void JE_darkenBackground(JE_word neat); void blit_background_row(SDL_Surface *surface, int x, int y, Uint8 **map); void blit_background_row_blend(SDL_Surface *surface, int x, int y, Uint8 **map); void draw_background_1(SDL_Surface *surface); void draw_background_2(SDL_Surface *surface); void draw_background_2_blend(SDL_Surface *surface); void draw_background_3(SDL_Surface *surface); void JE_filterScreen(JE_shortint col, JE_shortint generic_int); void JE_checkSmoothies(void); void lava_filter(SDL_Surface *dst, SDL_Surface *src); void water_filter(SDL_Surface *dst, SDL_Surface *src); void iced_blur_filter(SDL_Surface *dst, SDL_Surface *src); void blur_filter(SDL_Surface *dst, SDL_Surface *src); /*smoothies #5 is used for 3*/ /*smoothies #9 is a vertical flip*/ void initialize_starfield(void); void update_and_draw_starfield(SDL_Surface* surface, int move_speed); #endif /* BACKGRND_H */ opentyrian-2.1.20221123/src/config.c000066400000000000000000000650061432005211200166340ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include "episodes.h" #include "file.h" #include "joystick.h" #include "loudness.h" #include "mtrand.h" #include "nortsong.h" #include "opentyr.h" #include "player.h" #include "varz.h" #include "vga256d.h" #include "video.h" #include "video_scale.h" #include #include #ifdef _MSC_VER #include #define mkdir _mkdir #else #include #endif /* Configuration Load/Save handler */ const JE_byte cryptKey[10] = /* [1..10] */ { 15, 50, 89, 240, 147, 34, 86, 9, 32, 208 }; const DosKeySettings defaultDosKeySettings = { 72, 80, 75, 77, 57, 28, 29, 56 }; const KeySettings defaultKeySettings = { SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_SPACE, SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_LALT, }; static const char *const keySettingNames[] = { "up", "down", "left", "right", "fire", "change fire", "left sidekick", "right sidekick", }; const char defaultHighScoreNames[34][23] = /* [1..34] of string [22] */ {/*1P*/ /*TYR*/ "The Prime Chair", /*13*/ "Transon Lohk", "Javi Onukala", "Mantori", "Nortaneous", "Dougan", "Reid", "General Zinglon", "Late Gyges Phildren", "Vykromod", "Beppo", "Borogar", "ShipMaster Carlos", /*OTHER*/ "Jill", /*5*/ "Darcy", "Jake Stone", "Malvineous Havershim", "Marta Louise Velasquez", /*JAZZ*/ "Jazz Jackrabbit", /*3*/ "Eva Earlong", "Devan Shell", /*OMF*/ "Crystal Devroe", /*11*/ "Steffan Tommas", "Milano Angston", "Christian", "Shirro", "Jean-Paul", "Ibrahim Hothe", "Angel", "Cossette Akira", "Raven", "Hans Kreissack", /*DARE*/ "Tyler", /*2*/ "Rennis the Rat Guard" }; const char defaultTeamNames[22][25] = /* [1..22] of string [24] */ { "Jackrabbits", "Team Tyrian", "The Elam Brothers", "Dare to Dream Team", "Pinball Freaks", "Extreme Pinball Freaks", "Team Vykromod", "Epic All-Stars", "Hans Keissack's WARriors", "Team Overkill", "Pied Pipers", "Gencore Growlers", "Microsol Masters", "Beta Warriors", "Team Loco", "The Shellians", "Jungle Jills", "Murderous Malvineous", "The Traffic Department", "Clan Mikal", "Clan Patrok", "Carlos' Crawlers" }; const JE_EditorItemAvailType initialItemAvail = { 1,1,1,0,0,1,1,0,1,1,1,1,1,0,1,0,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, /* Front/Rear Weapons 1-38 */ 0,0,0,0,0,0,0,0,0,0,1, /* Fill */ 1,0,0,0,0,1,0,0,0,1,1,0,1,0,0,0,0,0, /* Sidekicks 51-68 */ 0,0,0,0,0,0,0,0,0,0,0, /* Fill */ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* Special Weapons 81-93 */ 0,0,0,0,0 /* Fill */ }; /* Last 2 bytes = Word * * Max Value = 1680 * X div 60 = Armor (1-28) * X div 168 = Shield (1-12) * X div 280 = Engine (1-06) */ JE_boolean smoothies[9] = /* [1..9] */ { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; JE_byte starShowVGASpecialCode; /* CubeData */ JE_word lastCubeMax, cubeMax; JE_word cubeList[4]; /* [1..4] */ /* High-Score Stuff */ JE_boolean gameHasRepeated; // can only get highscore on first play-through /* Difficulty */ JE_shortint difficultyLevel, oldDifficultyLevel, initialDifficulty; // can only get highscore on initial episode /* Player Stuff */ uint power, lastPower, powerAdd; JE_byte shieldWait, shieldT; JE_byte shotRepeat[11], shotMultiPos[11]; JE_boolean portConfigChange, portConfigDone; /* Level Data */ char lastLevelName[11], levelName[11]; /* string [10] */ JE_byte mainLevel, nextLevel, saveLevel; /*Current Level #*/ /* Keyboard Junk */ DosKeySettings dosKeySettings; KeySettings keySettings; /* Configuration */ JE_shortint levelFilter, levelFilterNew, levelBrightness, levelBrightnessChg; JE_boolean filtrationAvail, filterActive, filterFade, filterFadeStart; JE_boolean gameJustLoaded; JE_boolean galagaMode; JE_boolean extraGame; JE_boolean twoPlayerMode, twoPlayerLinked, onePlayerAction, superTyrian; JE_boolean trentWin = false; JE_byte superArcadeMode; JE_byte superArcadePowerUp; JE_real linkGunDirec; JE_byte inputDevice[2] = { 1, 2 }; // 0:any 1:keyboard 2:mouse 3+:joystick JE_byte secretHint; JE_byte background3over; JE_byte background2over; JE_byte gammaCorrection; JE_boolean superPause = false; JE_boolean explosionTransparent, youAreCheating, displayScore, background2, smoothScroll, wild, superWild, starActive, topEnemyOver, skyEnemyOverAll, background2notTransparent; JE_byte soundEffects; // dummy value for config JE_byte versionNum; /* SW 1.0 and SW/Reg 1.1 = 0 or 1 * EA 1.2 = 2 */ JE_byte fastPlay; JE_boolean pentiumMode; /* Savegame files */ JE_byte gameSpeed; JE_byte processorType; /* 1=386 2=486 3=Pentium Hyper */ JE_SaveFilesType saveFiles; /*array[1..saveLevelnum] of savefiletype;*/ JE_SaveGameTemp saveTemp; JE_word editorLevel; /*Initial value 800*/ Config opentyrian_config; // implicitly initialized bool load_opentyrian_config(void) { // defaults fullscreen_display = -1; set_scaler_by_name("Scale2x"); memcpy(keySettings, defaultKeySettings, sizeof(keySettings)); Config *config = &opentyrian_config; FILE *file = dir_fopen_warn(get_user_directory(), "opentyrian.cfg", "r"); if (file == NULL) return false; if (!config_parse(config, file)) { fclose(file); return false; } ConfigSection *section; section = config_find_section(config, "video", NULL); if (section != NULL) { config_get_int_option(section, "fullscreen", &fullscreen_display); const char *scaler; if (config_get_string_option(section, "scaler", &scaler)) set_scaler_by_name(scaler); const char *scaling_mode; if (config_get_string_option(section, "scaling_mode", &scaling_mode)) set_scaling_mode_by_name(scaling_mode); } section = config_find_section(config, "keyboard", NULL); if (section != NULL) { for (size_t i = 0; i < COUNTOF(keySettings); ++i) { const char *keyName; if (config_get_string_option(section, keySettingNames[i], &keyName)) { SDL_Scancode scancode = SDL_GetScancodeFromName(keyName); if (scancode != SDL_SCANCODE_UNKNOWN) keySettings[i] = scancode; } } } fclose(file); return true; } bool save_opentyrian_config(void) { Config *config = &opentyrian_config; ConfigSection *section; section = config_find_or_add_section(config, "video", NULL); if (section == NULL) exit(EXIT_FAILURE); // out of memory config_set_int_option(section, "fullscreen", fullscreen_display); config_set_string_option(section, "scaler", scalers[scaler].name); config_set_string_option(section, "scaling_mode", scaling_mode_names[scaling_mode]); section = config_find_or_add_section(config, "keyboard", NULL); if (section == NULL) exit(EXIT_FAILURE); // out of memory for (size_t i = 0; i < COUNTOF(keySettings); ++i) { const char *keyName = SDL_GetScancodeName(keySettings[i]); if (keyName[0] == '\0') keyName = NULL; config_set_string_option(section, keySettingNames[i], keyName); } #ifndef TARGET_WIN32 mkdir(get_user_directory(), 0700); #else mkdir(get_user_directory()); #endif FILE *file = dir_fopen(get_user_directory(), "opentyrian.cfg", "w"); if (file == NULL) return false; config_write(config, file); #if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE fsync(fileno(file)); #endif fclose(file); return true; } static void playeritems_to_pitems(JE_PItemsType pItems, PlayerItems *items, JE_byte initial_episode_num) { pItems[0] = items->weapon[FRONT_WEAPON].id; pItems[1] = items->weapon[REAR_WEAPON].id; pItems[2] = items->super_arcade_mode; pItems[3] = items->sidekick[LEFT_SIDEKICK]; pItems[4] = items->sidekick[RIGHT_SIDEKICK]; pItems[5] = items->generator; pItems[6] = items->sidekick_level; pItems[7] = items->sidekick_series; pItems[8] = initial_episode_num; pItems[9] = items->shield; pItems[10] = items->special; pItems[11] = items->ship; } static void pitems_to_playeritems(PlayerItems *items, JE_PItemsType pItems, JE_byte *initial_episode_num) { items->weapon[FRONT_WEAPON].id = pItems[0]; items->weapon[REAR_WEAPON].id = pItems[1]; items->super_arcade_mode = pItems[2]; items->sidekick[LEFT_SIDEKICK] = pItems[3]; items->sidekick[RIGHT_SIDEKICK] = pItems[4]; items->generator = pItems[5]; items->sidekick_level = pItems[6]; items->sidekick_series = pItems[7]; if (initial_episode_num != NULL) *initial_episode_num = pItems[8]; items->shield = pItems[9]; items->special = pItems[10]; items->ship = pItems[11]; } void JE_saveGame(JE_byte slot, const char *name) { saveFiles[slot-1].initialDifficulty = initialDifficulty; saveFiles[slot-1].gameHasRepeated = gameHasRepeated; saveFiles[slot-1].level = saveLevel; if (superTyrian) player[0].items.super_arcade_mode = SA_SUPERTYRIAN; else if (superArcadeMode == SA_NONE && onePlayerAction) player[0].items.super_arcade_mode = SA_ARCADE; else player[0].items.super_arcade_mode = superArcadeMode; playeritems_to_pitems(saveFiles[slot-1].items, &player[0].items, initial_episode_num); if (twoPlayerMode) playeritems_to_pitems(saveFiles[slot-1].lastItems, &player[1].items, 0); else playeritems_to_pitems(saveFiles[slot-1].lastItems, &player[0].last_items, 0); saveFiles[slot-1].score = player[0].cash; saveFiles[slot-1].score2 = player[1].cash; memcpy(&saveFiles[slot-1].levelName, &lastLevelName, sizeof(lastLevelName)); saveFiles[slot-1].cubes = lastCubeMax; if (strcmp(lastLevelName, "Completed") == 0) { temp = episodeNum - 1; if (temp < 1) { temp = EPISODE_AVAILABLE; /* JE: {Episodemax is 4 for completion purposes} */ } saveFiles[slot-1].episode = temp; } else { saveFiles[slot-1].episode = episodeNum; } saveFiles[slot-1].difficulty = difficultyLevel; saveFiles[slot-1].secretHint = secretHint; saveFiles[slot-1].input1 = inputDevice[0]; saveFiles[slot-1].input2 = inputDevice[1]; strcpy(saveFiles[slot-1].name, name); for (uint port = 0; port < 2; ++port) { // if two-player, use first player's front and second player's rear weapon saveFiles[slot-1].power[port] = player[twoPlayerMode ? port : 0].items.weapon[port].power; } JE_saveConfiguration(); } void JE_loadGame(JE_byte slot) { superTyrian = false; onePlayerAction = false; twoPlayerMode = false; extraGame = false; galagaMode = false; initialDifficulty = saveFiles[slot-1].initialDifficulty; gameHasRepeated = saveFiles[slot-1].gameHasRepeated; twoPlayerMode = (slot-1) > 10; difficultyLevel = saveFiles[slot-1].difficulty; pitems_to_playeritems(&player[0].items, saveFiles[slot-1].items, &initial_episode_num); superArcadeMode = player[0].items.super_arcade_mode; if (superArcadeMode == SA_SUPERTYRIAN) superTyrian = true; if (superArcadeMode != SA_NONE) onePlayerAction = true; if (superArcadeMode > SA_NORTSHIPZ) superArcadeMode = SA_NONE; if (twoPlayerMode) { onePlayerAction = false; pitems_to_playeritems(&player[1].items, saveFiles[slot-1].lastItems, NULL); } else { pitems_to_playeritems(&player[0].last_items, saveFiles[slot-1].lastItems, NULL); } /* Compatibility with old version */ if (player[1].items.sidekick_level < 101) { player[1].items.sidekick_level = 101; player[1].items.sidekick_series = player[1].items.sidekick[LEFT_SIDEKICK]; } player[0].cash = saveFiles[slot-1].score; player[1].cash = saveFiles[slot-1].score2; mainLevel = saveFiles[slot-1].level; cubeMax = saveFiles[slot-1].cubes; lastCubeMax = cubeMax; secretHint = saveFiles[slot-1].secretHint; inputDevice[0] = saveFiles[slot-1].input1; inputDevice[1] = saveFiles[slot-1].input2; for (uint port = 0; port < 2; ++port) { // if two-player, use first player's front and second player's rear weapon player[twoPlayerMode ? port : 0].items.weapon[port].power = saveFiles[slot-1].power[port]; } int episode = saveFiles[slot-1].episode; memcpy(&levelName, &saveFiles[slot-1].levelName, sizeof(levelName)); if (strcmp(levelName, "Completed") == 0) { if (episode == EPISODE_AVAILABLE) episode = 1; else if (episode < EPISODE_AVAILABLE) episode++; /* Increment episode. Episode EPISODE_AVAILABLE goes to 1. */ } JE_initEpisode(episode); saveLevel = mainLevel; memcpy(&lastLevelName, &levelName, sizeof(levelName)); } void JE_initProcessorType(void) { /* SYN: Originally this proc looked at your hardware specs and chose appropriate options. We don't care, so I'll just set decent defaults here. */ wild = false; superWild = false; smoothScroll = true; explosionTransparent = true; filtrationAvail = false; background2 = true; displayScore = true; switch (processorType) { case 1: /* 386 */ background2 = false; displayScore = false; explosionTransparent = false; break; case 2: /* 486 - Default */ break; case 3: /* High Detail */ smoothScroll = false; break; case 4: /* Pentium */ wild = true; filtrationAvail = true; break; case 5: /* Nonstandard VGA */ smoothScroll = false; break; case 6: /* SuperWild */ wild = true; superWild = true; filtrationAvail = true; break; } switch (gameSpeed) { case 1: /* Slug Mode */ fastPlay = 3; break; case 2: /* Slower */ fastPlay = 4; break; case 3: /* Slow */ fastPlay = 5; break; case 4: /* Normal */ fastPlay = 0; break; case 5: /* Pentium Hyper */ fastPlay = 1; break; } } void JE_setNewGameSpeed(void) { pentiumMode = false; Uint16 speed; switch (fastPlay) { default: assert(false); // fall through case 0: // Normal speed = 0x4300; smoothScroll = true; frameCountMax = 2; break; case 1: // Pentium Hyper speed = 0x3000; smoothScroll = true; frameCountMax = 2; break; case 2: speed = 0x2000; smoothScroll = false; frameCountMax = 2; break; case 3: // Slug mode speed = 0x5300; smoothScroll = true; frameCountMax = 4; break; case 4: // Slower speed = 0x4300; smoothScroll = true; frameCountMax = 3; break; case 5: // Slow speed = 0x4300; smoothScroll = true; frameCountMax = 2; pentiumMode = true; break; } setDelaySpeed(speed); setDelay(frameCountMax); } void JE_encryptSaveTemp(void) { JE_SaveGameTemp s3; JE_word x; JE_byte y; memcpy(&s3, &saveTemp, sizeof(s3)); y = 0; for (x = 0; x < SAVE_FILE_SIZE; x++) { y += s3[x]; } saveTemp[SAVE_FILE_SIZE] = y; y = 0; for (x = 0; x < SAVE_FILE_SIZE; x++) { y -= s3[x]; } saveTemp[SAVE_FILE_SIZE+1] = y; y = 1; for (x = 0; x < SAVE_FILE_SIZE; x++) { y = (y * s3[x]) + 1; } saveTemp[SAVE_FILE_SIZE+2] = y; y = 0; for (x = 0; x < SAVE_FILE_SIZE; x++) { y = y ^ s3[x]; } saveTemp[SAVE_FILE_SIZE+3] = y; for (x = 0; x < SAVE_FILE_SIZE; x++) { saveTemp[x] = saveTemp[x] ^ cryptKey[(x+1) % 10]; if (x > 0) { saveTemp[x] = saveTemp[x] ^ saveTemp[x - 1]; } } } void JE_decryptSaveTemp(void) { JE_boolean correct = true; JE_SaveGameTemp s2; int x; JE_byte y; /* Decrypt save game file */ for (x = (SAVE_FILE_SIZE - 1); x >= 0; x--) { s2[x] = (JE_byte)saveTemp[x] ^ (JE_byte)(cryptKey[(x+1) % 10]); if (x > 0) { s2[x] ^= (JE_byte)saveTemp[x - 1]; } } /* for (x = 0; x < SAVE_FILE_SIZE; x++) printf("%c", s2[x]); */ /* Check save file for correctitude */ y = 0; for (x = 0; x < SAVE_FILE_SIZE; x++) { y += s2[x]; } if (saveTemp[SAVE_FILE_SIZE] != y) { correct = false; printf("Failed additive checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE], y); } y = 0; for (x = 0; x < SAVE_FILE_SIZE; x++) { y -= s2[x]; } if (saveTemp[SAVE_FILE_SIZE+1] != y) { correct = false; printf("Failed subtractive checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE+1], y); } y = 1; for (x = 0; x < SAVE_FILE_SIZE; x++) { y = (y * s2[x]) + 1; } if (saveTemp[SAVE_FILE_SIZE+2] != y) { correct = false; printf("Failed multiplicative checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE+2], y); } y = 0; for (x = 0; x < SAVE_FILE_SIZE; x++) { y = y ^ s2[x]; } if (saveTemp[SAVE_FILE_SIZE+3] != y) { correct = false; printf("Failed XOR'd checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE+3], y); } /* Barf and die if save file doesn't validate */ if (!correct) { fprintf(stderr, "Error reading save file!\n"); exit(255); } /* Keep decrypted version plz */ memcpy(&saveTemp, &s2, sizeof(s2)); } const char *get_user_directory(void) { static char user_dir[500] = ""; if (strlen(user_dir) == 0) { #ifndef TARGET_WIN32 char *xdg_config_home = getenv("XDG_CONFIG_HOME"); if (xdg_config_home != NULL) { snprintf(user_dir, sizeof(user_dir), "%s/opentyrian", xdg_config_home); } else { char *home = getenv("HOME"); if (home != NULL) { snprintf(user_dir, sizeof(user_dir), "%s/.config/opentyrian", home); } else { strcpy(user_dir, "."); } } #else strcpy(user_dir, "."); #endif } return user_dir; } // for compatibility Uint8 joyButtonAssign[4] = {1, 4, 5, 5}; Uint8 inputDevice_ = 0, jConfigure = 0, midiPort = 1; void JE_loadConfiguration(void) { FILE *fi; int z; JE_byte *p; int y; fi = dir_fopen_warn(get_user_directory(), "tyrian.cfg", "rb"); if (fi && ftell_eof(fi) == 28) { background2 = 0; fread_bool_die(&background2, fi); fread_u8_die(&gameSpeed, 1, fi); fread_u8_die(&inputDevice_, 1, fi); fread_u8_die(&jConfigure, 1, fi); fread_u8_die(&versionNum, 1, fi); fread_u8_die(&processorType, 1, fi); fread_u8_die(&midiPort, 1, fi); fread_u8_die(&soundEffects, 1, fi); fread_u8_die(&gammaCorrection, 1, fi); fread_s8_die(&difficultyLevel, 1, fi); fread_u8_die(joyButtonAssign, 4, fi); fread_u16_die(&tyrMusicVolume, 1, fi); fread_u16_die(&fxVolume, 1, fi); fread_u8_die(inputDevice, 2, fi); fread_u8_die(dosKeySettings, 8, fi); fclose(fi); } else { printf("\nInvalid or missing TYRIAN.CFG! Continuing using defaults.\n\n"); soundEffects = 1; memcpy(&dosKeySettings, &defaultDosKeySettings, sizeof(dosKeySettings)); background2 = true; tyrMusicVolume = 191; fxVolume = 191; gammaCorrection = 0; processorType = 3; gameSpeed = 4; } load_opentyrian_config(); if (tyrMusicVolume > 255) tyrMusicVolume = 255; if (fxVolume > 255) fxVolume = 255; set_volume(tyrMusicVolume, fxVolume); fi = dir_fopen_warn(get_user_directory(), "tyrian.sav", "rb"); if (fi) { fseek(fi, 0, SEEK_SET); fread_die(saveTemp, 1, sizeof(saveTemp), fi); JE_decryptSaveTemp(); /* SYN: The original mostly blasted the save file into raw memory. However, our lives are not so easy, because the C struct is necessarily a different size. So instead we have to loop through each record and load fields manually. *emo tear* :'( */ p = saveTemp; for (z = 0; z < SAVE_FILES_NUM; z++) { memcpy(&saveFiles[z].encode, p, sizeof(JE_word)); p += 2; saveFiles[z].encode = SDL_SwapLE16(saveFiles[z].encode); memcpy(&saveFiles[z].level, p, sizeof(JE_word)); p += 2; saveFiles[z].level = SDL_SwapLE16(saveFiles[z].level); memcpy(&saveFiles[z].items, p, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType); memcpy(&saveFiles[z].score, p, sizeof(JE_longint)); p += 4; saveFiles[z].score = SDL_SwapLE32(saveFiles[z].score); memcpy(&saveFiles[z].score2, p, sizeof(JE_longint)); p += 4; saveFiles[z].score2 = SDL_SwapLE32(saveFiles[z].score2); /* SYN: Pascal strings are prefixed by a byte holding the length! */ memset(&saveFiles[z].levelName, 0, sizeof(saveFiles[z].levelName)); memcpy(&saveFiles[z].levelName, &p[1], *p); p += 10; /* This was a BYTE array, not a STRING, in the original. Go fig. */ memcpy(&saveFiles[z].name, p, 14); p += 14; memcpy(&saveFiles[z].cubes, p, sizeof(JE_byte)); p++; memcpy(&saveFiles[z].power, p, sizeof(JE_byte) * 2); p += 2; memcpy(&saveFiles[z].episode, p, sizeof(JE_byte)); p++; memcpy(&saveFiles[z].lastItems, p, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType); memcpy(&saveFiles[z].difficulty, p, sizeof(JE_byte)); p++; memcpy(&saveFiles[z].secretHint, p, sizeof(JE_byte)); p++; memcpy(&saveFiles[z].input1, p, sizeof(JE_byte)); p++; memcpy(&saveFiles[z].input2, p, sizeof(JE_byte)); p++; /* booleans were 1 byte in pascal -- working around it */ Uint8 temp; memcpy(&temp, p, 1); p++; saveFiles[z].gameHasRepeated = temp != 0; memcpy(&saveFiles[z].initialDifficulty, p, sizeof(JE_byte)); p++; memcpy(&saveFiles[z].highScore1, p, sizeof(JE_longint)); p += 4; saveFiles[z].highScore1 = SDL_SwapLE32(saveFiles[z].highScore1); memcpy(&saveFiles[z].highScore2, p, sizeof(JE_longint)); p += 4; saveFiles[z].highScore2 = SDL_SwapLE32(saveFiles[z].highScore2); memset(&saveFiles[z].highScoreName, 0, sizeof(saveFiles[z].highScoreName)); memcpy(&saveFiles[z].highScoreName, &p[1], *p); p += 30; memcpy(&saveFiles[z].highScoreDiff, p, sizeof(JE_byte)); p++; } /* SYN: This is truncating to bytes. I have no idea what this is doing or why. */ /* TODO: Figure out what this is about and make sure it isn't broken. */ editorLevel = (saveTemp[SIZEOF_SAVEGAMETEMP - 5] << 8) | saveTemp[SIZEOF_SAVEGAMETEMP - 6]; fclose(fi); } else { /* We didn't have a save file! Let's make up random stuff! */ editorLevel = 800; for (z = 0; z < 100; z++) { saveTemp[SAVE_FILES_SIZE + z] = initialItemAvail[z]; } for (z = 0; z < SAVE_FILES_NUM; z++) { saveFiles[z].level = 0; for (y = 0; y < 14; y++) { saveFiles[z].name[y] = ' '; } saveFiles[z].name[14] = 0; saveFiles[z].highScore1 = ((mt_rand() % 20) + 1) * 1000; if (z % 6 > 2) { saveFiles[z].highScore2 = ((mt_rand() % 20) + 1) * 1000; strcpy(saveFiles[z].highScoreName, defaultTeamNames[mt_rand() % 22]); } else { strcpy(saveFiles[z].highScoreName, defaultHighScoreNames[mt_rand() % 34]); } } } JE_initProcessorType(); } void JE_saveConfiguration(void) { FILE *f; JE_byte *p; int z; p = saveTemp; for (z = 0; z < SAVE_FILES_NUM; z++) { JE_SaveFileType tempSaveFile; memcpy(&tempSaveFile, &saveFiles[z], sizeof(tempSaveFile)); tempSaveFile.encode = SDL_SwapLE16(tempSaveFile.encode); memcpy(p, &tempSaveFile.encode, sizeof(JE_word)); p += 2; tempSaveFile.level = SDL_SwapLE16(tempSaveFile.level); memcpy(p, &tempSaveFile.level, sizeof(JE_word)); p += 2; memcpy(p, &tempSaveFile.items, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType); tempSaveFile.score = SDL_SwapLE32(tempSaveFile.score); memcpy(p, &tempSaveFile.score, sizeof(JE_longint)); p += 4; tempSaveFile.score2 = SDL_SwapLE32(tempSaveFile.score2); memcpy(p, &tempSaveFile.score2, sizeof(JE_longint)); p += 4; /* SYN: Pascal strings are prefixed by a byte holding the length! */ memset(p, 0, sizeof(tempSaveFile.levelName)); *p = strlen(tempSaveFile.levelName); memcpy(&p[1], &tempSaveFile.levelName, *p); p += 10; /* This was a BYTE array, not a STRING, in the original. Go fig. */ memcpy(p, &tempSaveFile.name, 14); p += 14; memcpy(p, &tempSaveFile.cubes, sizeof(JE_byte)); p++; memcpy(p, &tempSaveFile.power, sizeof(JE_byte) * 2); p += 2; memcpy(p, &tempSaveFile.episode, sizeof(JE_byte)); p++; memcpy(p, &tempSaveFile.lastItems, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType); memcpy(p, &tempSaveFile.difficulty, sizeof(JE_byte)); p++; memcpy(p, &tempSaveFile.secretHint, sizeof(JE_byte)); p++; memcpy(p, &tempSaveFile.input1, sizeof(JE_byte)); p++; memcpy(p, &tempSaveFile.input2, sizeof(JE_byte)); p++; /* booleans were 1 byte in pascal -- working around it */ Uint8 temp = tempSaveFile.gameHasRepeated != false; memcpy(p, &temp, 1); p++; memcpy(p, &tempSaveFile.initialDifficulty, sizeof(JE_byte)); p++; tempSaveFile.highScore1 = SDL_SwapLE32(tempSaveFile.highScore1); memcpy(p, &tempSaveFile.highScore1, sizeof(JE_longint)); p += 4; tempSaveFile.highScore2 = SDL_SwapLE32(tempSaveFile.highScore2); memcpy(p, &tempSaveFile.highScore2, sizeof(JE_longint)); p += 4; memset(p, 0, sizeof(tempSaveFile.highScoreName)); *p = strlen(tempSaveFile.highScoreName); memcpy(&p[1], &tempSaveFile.highScoreName, *p); p += 30; memcpy(p, &tempSaveFile.highScoreDiff, sizeof(JE_byte)); p++; } saveTemp[SIZEOF_SAVEGAMETEMP - 6] = editorLevel >> 8; saveTemp[SIZEOF_SAVEGAMETEMP - 5] = editorLevel; JE_encryptSaveTemp(); #ifndef TARGET_WIN32 mkdir(get_user_directory(), 0700); #else mkdir(get_user_directory()); #endif f = dir_fopen_warn(get_user_directory(), "tyrian.sav", "wb"); if (f != NULL) { fwrite_die(saveTemp, 1, sizeof(saveTemp), f); #if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE fsync(fileno(f)); #endif fclose(f); } JE_decryptSaveTemp(); f = dir_fopen_warn(get_user_directory(), "tyrian.cfg", "wb"); if (f != NULL) { fwrite_bool_die(&background2, f); fwrite_u8_die(&gameSpeed, 1, f); fwrite_u8_die(&inputDevice_, 1, f); fwrite_u8_die(&jConfigure, 1, f); fwrite_u8_die(&versionNum, 1, f); fwrite_u8_die(&processorType, 1, f); fwrite_u8_die(&midiPort, 1, f); fwrite_u8_die(&soundEffects, 1, f); fwrite_u8_die(&gammaCorrection, 1, f); fwrite_s8_die(&difficultyLevel, 1, f); fwrite_u8_die(joyButtonAssign, 4, f); fwrite_u16_die(&tyrMusicVolume, f); fwrite_u16_die(&fxVolume, f); fwrite_u8_die(inputDevice, 2, f); fwrite_u8_die(dosKeySettings, 8, f); #if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE fsync(fileno(f)); #endif fclose(f); } save_opentyrian_config(); } opentyrian-2.1.20221123/src/config.h000066400000000000000000000132041432005211200166320ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef CONFIG_H #define CONFIG_H #include "opentyr.h" #include "config_file.h" #include "SDL.h" #include #define SAVE_FILES_NUM (11 * 2) /* These are necessary because the size of the structure has changed from the original, but we need to know the original sizes in order to find things in TYRIAN.SAV */ #define SAVE_FILES_SIZE 2398 #define SIZEOF_SAVEGAMETEMP SAVE_FILES_SIZE + 4 + 100 #define SAVE_FILE_SIZE (SIZEOF_SAVEGAMETEMP - 4) /*#define SAVE_FILES_SIZE (2502 - 4) #define SAVE_FILE_SIZE (SAVE_FILES_SIZE)*/ enum { DIFFICULTY_WIMP = 0, DIFFICULTY_EASY, DIFFICULTY_NORMAL, DIFFICULTY_HARD, DIFFICULTY_IMPOSSIBLE, DIFFICULTY_INSANITY, DIFFICULTY_SUICIDE, DIFFICULTY_MANIACAL, DIFFICULTY_ZINGLON, // aka Lord of the Game DIFFICULTY_NORTANEOUS, DIFFICULTY_10, }; // NOTE: Do not reorder. This ordering corresponds to the keyboard // configuration menu and to the bits stored in demo files. enum { KEY_SETTING_UP, KEY_SETTING_DOWN, KEY_SETTING_LEFT, KEY_SETTING_RIGHT, KEY_SETTING_FIRE, KEY_SETTING_CHANGE_FIRE, KEY_SETTING_LEFT_SIDEKICK, KEY_SETTING_RIGHT_SIDEKICK, }; typedef JE_byte DosKeySettings[8]; // fka KeySettingType typedef SDL_Scancode KeySettings[8]; typedef JE_byte JE_PItemsType[12]; /* [1..12] */ typedef JE_byte JE_EditorItemAvailType[100]; /* [1..100] */ typedef struct { JE_word encode; JE_word level; JE_PItemsType items; JE_longint score; JE_longint score2; char levelName[11]; /* string [9]; */ /* SYN: Added one more byte to match lastLevelName below */ JE_char name[15]; /* [1..14] */ /* SYN: Added extra byte for null */ JE_byte cubes; JE_byte power[2]; /* [1..2] */ JE_byte episode; JE_PItemsType lastItems; JE_byte difficulty; JE_byte secretHint; JE_byte input1; JE_byte input2; JE_boolean gameHasRepeated; /*See if you went from one episode to another*/ JE_byte initialDifficulty; /* High Scores - Each episode has both sets of 1&2 player selections - with 3 in each */ JE_longint highScore1; JE_longint highScore2; // unused char highScoreName[30]; /* string [29] */ JE_byte highScoreDiff; } JE_SaveFileType; typedef JE_SaveFileType JE_SaveFilesType[SAVE_FILES_NUM]; /* [1..savefilesnum] */ typedef JE_byte JE_SaveGameTemp[SAVE_FILES_SIZE + 4 + 100]; /* [1..sizeof(savefilestype) + 4 + 100] */ extern const JE_byte cryptKey[10]; extern const DosKeySettings defaultDosKeySettings; // fka defaultKeySettings extern const KeySettings defaultKeySettings; extern const char defaultHighScoreNames[34][23]; extern const char defaultTeamNames[22][25]; extern const JE_EditorItemAvailType initialItemAvail; extern JE_boolean smoothies[9]; extern JE_byte starShowVGASpecialCode; extern JE_word lastCubeMax, cubeMax; extern JE_word cubeList[4]; extern JE_boolean gameHasRepeated; extern JE_shortint difficultyLevel, oldDifficultyLevel, initialDifficulty; extern uint power, lastPower, powerAdd; extern JE_byte shieldWait, shieldT; enum { SHOT_FRONT, SHOT_REAR, SHOT_LEFT_SIDEKICK, SHOT_RIGHT_SIDEKICK, SHOT_MISC, SHOT_P2_CHARGE, SHOT_P1_SUPERBOMB, SHOT_P2_SUPERBOMB, SHOT_SPECIAL, SHOT_NORTSPARKS, SHOT_SPECIAL2 }; extern JE_byte shotRepeat[11], shotMultiPos[11]; extern JE_boolean portConfigChange, portConfigDone; extern char lastLevelName[11], levelName[11]; extern JE_byte mainLevel, nextLevel, saveLevel; extern DosKeySettings dosKeySettings; // fka keySettings extern KeySettings keySettings; extern JE_shortint levelFilter, levelFilterNew, levelBrightness, levelBrightnessChg; extern JE_boolean filtrationAvail, filterActive, filterFade, filterFadeStart; extern JE_boolean gameJustLoaded; extern JE_boolean galagaMode; extern JE_boolean extraGame; extern JE_boolean twoPlayerMode, twoPlayerLinked, onePlayerAction, superTyrian, trentWin; extern JE_byte superArcadeMode; extern JE_byte superArcadePowerUp; extern JE_real linkGunDirec; extern JE_byte inputDevice[2]; extern JE_byte secretHint; extern JE_byte background3over; extern JE_byte background2over; extern JE_byte gammaCorrection; extern JE_boolean superPause, explosionTransparent, youAreCheating, displayScore, background2, smoothScroll, wild, superWild, starActive, topEnemyOver, skyEnemyOverAll, background2notTransparent; extern JE_byte versionNum; extern JE_byte fastPlay; extern JE_boolean pentiumMode; extern JE_byte gameSpeed; extern JE_byte processorType; extern JE_SaveFilesType saveFiles; extern JE_SaveGameTemp saveTemp; extern JE_word editorLevel; extern Config opentyrian_config; void JE_initProcessorType(void); void JE_setNewGameSpeed(void); const char *get_user_directory(void); void JE_loadConfiguration(void); void JE_saveConfiguration(void); void JE_saveGame(JE_byte slot, const char *name); void JE_loadGame(JE_byte slot); void JE_encryptSaveTemp(void); void JE_decryptSaveTemp(void); #endif /* CONFIG_H */ opentyrian-2.1.20221123/src/config_file.c000066400000000000000000000547251432005211200176410ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2015 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /*! * \file config_file.c * \author Carl Reinke * \date 2015 * \copyright GNU General Public License v2+ or Mozilla Public License 2.0 */ #include "config_file.h" #include #include #include #include #include /* potential size of decimal representation of type */ #define udecsizeof(t) ((CHAR_BIT * sizeof(t) / 3) + 1) #define sdecsizeof(t) (udecsizeof(t) + 1) extern void config_oom(void); void config_oom(void) { fprintf(stderr, "out of memory\n"); exit(EXIT_FAILURE); } /* string manipulators */ static ConfigString string_init_len(const char *s, size_t n) { ConfigString string; if (s == NULL) { CONFIG_STRING_LONG_TAG(string) = true; string.long_buf = NULL; } else { char is_long = n >= COUNTOF(string.short_buf); CONFIG_STRING_LONG_TAG(string) = is_long; char *buffer = is_long ? string.long_buf = malloc((n + 1) * sizeof(char)) : string.short_buf; if (buffer == NULL) config_oom(); memcpy(buffer, s, n * sizeof(char)); buffer[n] = '\0'; } return string; } static void string_deinit(ConfigString *string) { char is_long = CONFIG_STRING_LONG_TAG(*string); if (is_long) { free(string->long_buf); string->long_buf = NULL; } } static bool string_equal_len(ConfigString *string, const char *s, size_t n) { const char *cstr = config_string_to_cstr(string); return strncmp(cstr, s, n) == 0 && cstr[n] == '\0'; } /* config manipulators */ static void deinit_section(ConfigSection *section); static void deinit_option(ConfigOption *option); void config_init(Config *config) { assert(config != NULL); config->sections_count = 0; config->sections = NULL; } void config_deinit(Config *config) { assert(config != NULL); for (unsigned int s = 0; s < config->sections_count; ++s) { ConfigSection *section = &config->sections[s]; deinit_section(section); } free(config->sections); config->sections = NULL; } /* config section manipulators -- internal */ static void init_section(ConfigSection *section, const char *type, size_t type_len, const char *name, size_t name_len) { section->type = string_init_len(type, type_len); section->name = string_init_len(name, name_len); section->options_count = 0; section->options = NULL; } static void deinit_section(ConfigSection *section) { for (unsigned int o = 0; o < section->options_count; ++o) { ConfigOption *option = §ion->options[o]; deinit_option(option); } string_deinit(§ion->type); string_deinit(§ion->name); free(section->options); section->options = NULL; } /* config section accessors/manipulators -- by type, name */ ConfigSection *config_add_section_len(Config *config, const char *type, size_t type_len, const char *name, size_t name_len) { assert(config != NULL); assert(type != NULL); ConfigSection *sections = realloc(config->sections, (config->sections_count + 1) * sizeof(ConfigSection)); if (sections == NULL) return NULL; ConfigSection *section = §ions[config->sections_count]; config->sections_count += 1; config->sections = sections; init_section(section, type, type_len, name, name_len); return section; } ConfigSection *config_find_sections(Config *config, const char *type, ConfigSection **save) { assert(config != NULL); assert(type != NULL); ConfigSection *sections_end = &config->sections[config->sections_count]; ConfigSection *section = save != NULL && *save != NULL ? *save : &config->sections[0]; for (; section < sections_end; ++section) if (strcmp(config_string_to_cstr(§ion->type), type) == 0) break; if (save != NULL) *save = section; return section < sections_end ? section : NULL; } ConfigSection *config_find_section(Config *config, const char *type, const char *name) { assert(config != NULL); assert(type != NULL); ConfigSection *sections_end = &config->sections[config->sections_count]; for (ConfigSection *section = &config->sections[0]; section < sections_end; ++section) { if (strcmp(config_string_to_cstr(§ion->type), type) == 0) { const char *section_name = config_string_to_cstr(§ion->name); if ((section_name == NULL || name == NULL) ? section_name == name : strcmp(config_string_to_cstr(§ion->name), name) == 0) return section; } } return NULL; } ConfigSection *config_find_or_add_section(Config *config, const char *type, const char *name) { assert(config != NULL); assert(type != NULL); ConfigSection *section = config_find_section(config, type, name); if (section != NULL) return section; return config_add_section(config, type, name); } /* config option manipulators -- internal */ static void init_option_value(ConfigOption *option, const char *value, size_t value_len) { option->values_count = 0; option->v.value = string_init_len(value, value_len); } static void deinit_option_value(ConfigOption *option) { if (option->values_count != 0) { ConfigString *values_end = &option->v.values[option->values_count]; for (ConfigString *value = &option->v.values[0]; value < values_end; ++value) string_deinit(value); free(option->v.values); option->v.values = NULL; } else { string_deinit(&option->v.value); } } static void init_option(ConfigOption *option, const char *key, size_t key_len, const char *value, size_t value_len) { option->key = string_init_len(key, key_len); init_option_value(option, value, value_len); } static void deinit_option(ConfigOption *option) { string_deinit(&option->key); deinit_option_value(option); } static ConfigOption *append_option(ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len) { ConfigOption *options = realloc(section->options, (section->options_count + 1) * sizeof(ConfigSection)); if (options == NULL) return NULL; ConfigOption *option = &options[section->options_count]; section->options_count += 1; section->options = options; init_option(option, key, key_len, value, value_len); return option; } static ConfigOption *get_option_len(ConfigSection *section, const char *key, size_t key_len) { assert(section != NULL); assert(key != NULL); ConfigOption *options_end = §ion->options[section->options_count]; for (ConfigOption *option = §ion->options[0]; option < options_end; ++option) if (string_equal_len(&option->key, key, key_len)) return option; return NULL; } /* config option accessors/manipulators -- by key */ ConfigOption *config_set_option_len(ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len) { assert(section != NULL); assert(key != NULL); ConfigOption *option = get_option_len(section, key, key_len); if (option != NULL) return config_set_value_len(option, value, value_len); return append_option(section, key, key_len, value, value_len); } ConfigOption *config_get_option(const ConfigSection *section, const char *key) { assert(section != NULL); assert(key != NULL); ConfigOption *options_end = §ion->options[section->options_count]; for (ConfigOption *option = §ion->options[0]; option < options_end; ++option) if (strcmp(config_string_to_cstr(&option->key), key) == 0) return option; return NULL; } ConfigOption *config_get_or_set_option_len(ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len) { assert(section != NULL); assert(key != NULL); ConfigOption *option = get_option_len(section, key, key_len); if (option != NULL) return option; return append_option(section, key, key_len, value, value_len); } void config_set_string_option_len(ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len) { if (config_set_option_len(section, key, key_len, value, value_len) == NULL) config_oom(); } bool config_get_string_option(const ConfigSection *section, const char *key, const char **out_value) { assert(section != NULL); assert(key != NULL); ConfigOption *option = config_get_option(section, key); if (option != NULL) { const char *value = config_get_value(option); if (value != NULL) { *out_value = value; return true; } } return false; } const char *config_get_or_set_string_option(ConfigSection *section, const char *key, const char *value) { if (!config_get_string_option(section, key, &value)) config_set_string_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value)); return value; } static const char *const bool_values[][2] = { { "0", "1" }, { "no", "yes" }, { "off", "on" }, { "false", "true" }, }; void config_set_bool_option(ConfigSection *section, const char *key, bool value, ConfigBoolStyle style) { if (config_set_option(section, key, bool_values[style][value ? 1 : 0]) == NULL) config_oom(); } bool config_get_bool_option(const ConfigSection *section, const char *key, bool *out_value) { assert(section != NULL); assert(key != NULL); assert(out_value != NULL); const char *value; if (config_get_string_option(section, key, &value)) { for (size_t i = 0; i < COUNTOF(bool_values); ++i) { for (size_t j = 0; j < COUNTOF(bool_values[i]); ++j) { if (strcmp(value, bool_values[i][j]) == 0) { *out_value = j == 0 ? false : true; return true; } } } } return false; } bool config_get_or_set_bool_option(ConfigSection *section, const char *key, bool value, ConfigBoolStyle style) { if (!config_get_bool_option(section, key, &value)) config_set_bool_option(section, key, value, style); return value; } void config_set_int_option(ConfigSection *section, const char *key, int value) { assert(key != NULL); char buffer[sdecsizeof(int) + 1]; int buffer_len = snprintf(buffer, sizeof(buffer), "%i", value); if (config_set_option_len(section, key, strlen(key), buffer, buffer_len) == NULL) config_oom(); } bool config_get_int_option(const ConfigSection *section, const char *key, int *out_value) { assert(section != NULL); assert(key != NULL); assert(out_value != NULL); const char *value; if (config_get_string_option(section, key, &value)) { int i; int n; if (sscanf(value, "%i%n", &i, &n) > 0 && value[n] == '\0') /* must be entire string */ { *out_value = i; return true; } } return false; } int config_get_or_set_int_option(ConfigSection *section, const char *key, int value) { if (!config_get_int_option(section, key, &value)) config_set_int_option(section, key, value); return value; } void config_set_uint_option(ConfigSection *section, const char *key, unsigned int value) { assert(key != NULL); char buffer[udecsizeof(unsigned int) + 1]; int buffer_len = snprintf(buffer, sizeof(buffer), "%u", value); if (config_set_option_len(section, key, strlen(key), buffer, buffer_len) == NULL) config_oom(); } bool config_get_uint_option(const ConfigSection *section, const char *key, unsigned int *out_value) { assert(section != NULL); assert(key != NULL); assert(out_value != NULL); const char *value; if (config_get_string_option(section, key, &value)) { unsigned int u; int n; if (sscanf(value, "%u%n", &u, &n) > 0 && value[n] == '\0') /* must be entire string */ { *out_value = u; return true; } } return false; } unsigned int config_get_or_set_uint_option(ConfigSection *section, const char *key, unsigned int value) { if (!config_get_uint_option(section, key, &value)) config_set_uint_option(section, key, value); return value; } /* config option accessors/manipulators -- by reference */ ConfigOption *config_set_value_len(ConfigOption *option, const char *value, size_t value_len) { assert(option != NULL); deinit_option_value(option); init_option_value(option, value, value_len); return option; } ConfigOption *config_add_value_len(ConfigOption *option, const char *value, size_t value_len) { assert(option != NULL); assert(value != NULL); /* convert 'item' to 'list' */ if (option->values_count == 0 && config_string_to_cstr(&option->v.value) != NULL) { ConfigString option_value = option->v.value; ConfigString *values = malloc(2 * sizeof(ConfigString)); if (values == NULL) return NULL; option->v.values = values; option->v.values[0] = option_value; option->v.values[1] = string_init_len(value, value_len); option->values_count = 2; } else { ConfigString *values = realloc(option->v.values, (option->values_count + 1) * sizeof(ConfigString)); if (values == NULL) return NULL; option->v.values = values; option->v.values[option->values_count] = string_init_len(value, value_len); option->values_count += 1; } return option; } ConfigOption *config_remove_value(ConfigOption *option, unsigned int i) { assert(option != NULL); if (!config_is_value_list(option)) { if (i > 0) return NULL; config_set_value_len(option, NULL, 0); } else { if (i >= option->values_count) return NULL; string_deinit(&option->v.values[i]); memmove(&option->v.values[i], &option->v.values[i + 1], (option->values_count - i - 1) * sizeof(ConfigString)); if (option->values_count - 1 == 0) { option->v.value = string_init_len(NULL, 0); option->values_count = 0; } else { ConfigString *values = realloc(option->v.values, (option->values_count - 1) * sizeof(ConfigString)); if (values == NULL) return NULL; option->v.values = values; option->values_count -= 1; } } return option; } const char *config_get_value(const ConfigOption *option) { if (option == NULL || option->values_count != 0) return NULL; return config_string_to_cstr(&option->v.value); } /* config parser */ static bool is_whitespace(char c) { return c == '\t' || c == ' '; } static bool is_end(char c) { return c == '\0' || c == '\n' || c == '\r'; } static bool is_whitespace_or_end(char c) { return is_whitespace(c) || is_end(c); } typedef enum { INVALID_DIRECTIVE = 0, SECTION_DIRECTIVE, ITEM_DIRECTIVE, LIST_DIRECTIVE, } Directive; static Directive match_directive(const char *buffer, size_t *index) { size_t i = *index; while (is_whitespace(buffer[i])) ++i; Directive directive; if (strncmp("section", &buffer[i], 7) == 0) { directive = SECTION_DIRECTIVE; i += 7; } else if (strncmp("item", &buffer[i], 4) == 0) { directive = ITEM_DIRECTIVE; i += 4; } else if (strncmp("list", &buffer[i], 4) == 0) { directive = LIST_DIRECTIVE; i += 4; } else { return INVALID_DIRECTIVE; } if (!is_whitespace_or_end(buffer[i])) return INVALID_DIRECTIVE; *index = i; return directive; } static bool match_nonquote_field(const char *buffer, size_t *index, size_t *length) { size_t i = *index; for (; ; ++i) { char c = buffer[i]; if (is_whitespace_or_end(c)) { break; } else if (c <= ' ' || c > '~' || c == '#' || c == '\'' || c == '"') { return false; } } *length = i - *index; *index = i; return *length > 0; } static bool parse_quote_field(char *buffer, size_t *index, size_t *length) { size_t i = *index; size_t o = *index; char quote = buffer[i]; for (; ; ) { char c = buffer[++i]; if (c == quote) { ++i; break; } else if (c == '\\') { c = buffer[++i]; if (c == quote) { buffer[o++] = quote; } else { switch (c) { case 't': buffer[o++] = '\t'; break; case 'n': buffer[o++] = '\n'; break; case 'r': buffer[o++] = '\r'; break; case '\\': buffer[o++] = '\\'; break; case 'x': /* parse two hex digits */ c = buffer[++i]; char m = (c >= '0' && c <= '9') ? '0' : (c >= 'a' && c <= 'f') ? 'a' - 10 : (c >= 'A' && c <= 'F') ? 'A' - 10 : 0; if (m == 0) return false; char h = c - m; c = buffer[++i]; m = (c >= '0' && c <= '9') ? '0' : (c >= 'a' && c <= 'f') ? 'a' - 10 : (c >= 'A' && c <= 'F') ? 'A' - 10 : 0; if (m == 0) return false; buffer[o++] = (h << 4) | (c - m); break; default: return false; } } } else if (c >= ' ' && c <= '~') { buffer[o++] = c; } else { return false; } } *length = o - *index; *index = i; return true; } static bool parse_field(char *buffer, size_t *index, size_t *start, size_t *length) { size_t i = *index; while (is_whitespace(buffer[i])) ++i; *start = i; if (buffer[i] == '"' || buffer[i] == '\'') { if (!parse_quote_field(buffer, &i, length)) return false; } else { if (!match_nonquote_field(buffer, &i, length)) return false; } if (!is_whitespace_or_end(buffer[i])) return INVALID_DIRECTIVE; *index = i; return true; } bool config_parse(Config *config, FILE *file) { assert(config != NULL); assert(file != NULL); config_init(config); ConfigSection *section = NULL; ConfigOption *option = NULL; size_t buffer_cap = 128; char *buffer = malloc(buffer_cap * sizeof(char)); if (buffer == NULL) config_oom(); size_t buffer_end = 1; buffer[buffer_end - 1] = '\0'; for (size_t line = 0, next_line = 0; ; line = next_line) { /* find beginning of next line */ while (next_line < buffer_end) { char c = buffer[next_line]; if (c == '\0' && next_line == buffer_end - 1) { if (line > 0) { /* shift to front */ memmove(&buffer[0], &buffer[line], buffer_end - line); buffer_end -= line; next_line -= line; line = 0; } else if (buffer_end > 1) { /* need larger capacity */ buffer_cap *= 2; char *new_buffer = realloc(buffer, buffer_cap * sizeof(char)); if (new_buffer == NULL) config_oom(); buffer = new_buffer; } size_t read = fread(&buffer[buffer_end - 1], sizeof(char), buffer_cap - buffer_end, file); if (read == 0) break; buffer_end += read; buffer[buffer_end - 1] = '\0'; } else { ++next_line; if (c == '\n' || c == '\r') break; } } /* if at end of file */ if (next_line == line) break; size_t i = line; Directive directive = match_directive(buffer, &i); switch (directive) { case INVALID_DIRECTIVE: continue; case SECTION_DIRECTIVE: { size_t type_start; size_t type_length; if (!parse_field(buffer, &i, &type_start, &type_length)) continue; size_t name_start; size_t name_length; bool has_name = parse_field(buffer, &i, &name_start, &name_length); section = config_add_section_len(config, &buffer[type_start], type_length, has_name ? &buffer[name_start] : NULL, has_name ? name_length : 0); if (section == NULL) config_oom(); option = NULL; } break; case ITEM_DIRECTIVE: case LIST_DIRECTIVE: { if (section == NULL) continue; size_t key_start; size_t key_length; if (!parse_field(buffer, &i, &key_start, &key_length)) continue; size_t value_start; size_t value_length; if (!parse_field(buffer, &i, &value_start, &value_length)) continue; if (directive == ITEM_DIRECTIVE) { option = config_set_option_len(section, &buffer[key_start], key_length, &buffer[value_start], value_length); } else { if (option == NULL || !string_equal_len(&option->key, &buffer[key_start], key_length)) option = config_get_or_set_option_len(section, &buffer[key_start], key_length, NULL, 0); if (option != NULL) option = config_add_value_len(option, &buffer[value_start], value_length); } if (option == NULL) config_oom(); } break; } assert(i <= next_line); } free(buffer); return config; } /* config writer */ static void write_field(const ConfigString *field, FILE *file) { fputc('\'', file); char buffer[128]; size_t o = 0; for (const char *ci = config_string_to_cstr(field); *ci != '\0'; ++ci) { char c = *ci; size_t l; switch (c) { case '\t': case '\n': case '\r': case '\'': case '\\': l = 2; break; default: l = (c >= ' ' && c <= '~') ? 1 : 4; break; } if (o + l > COUNTOF(buffer)) { fwrite(buffer, sizeof(*buffer), o, file); o = 0; } switch (l) { case 1: buffer[o++] = c; break; case 2: switch (c) { case '\t': buffer[o++] = '\\'; buffer[o++] = 't'; break; case '\n': buffer[o++] = '\\'; buffer[o++] = 'n'; break; case '\r': buffer[o++] = '\\'; buffer[o++] = 'r'; break; case '\'': case '\\': buffer[o++] = '\\'; buffer[o++] = c; break; } break; case 4: buffer[o++] = '\\'; buffer[o++] = 'x'; char n = (c >> 4) & 0x0f; buffer[o++] = (n < 10 ? '0' : ('a' - 10)) + n; n = c & 0x0f; buffer[o++] = (n < 10 ? '0' : ('a' - 10)) + n; break; } } if (o > 0) fwrite(buffer, sizeof(*buffer), o, file); fputc('\'', file); } void config_write(const Config *config, FILE *file) { assert(config != NULL); assert(file != NULL); for (unsigned int s = 0; s < config->sections_count; ++s) { ConfigSection *section = &config->sections[s]; fputs("section ", file); write_field(§ion->type, file); if (config_string_to_cstr(§ion->name) != NULL) { fputc(' ', file); write_field(§ion->name, file); } fputc('\n', file); for (unsigned int o = 0; o < section->options_count; ++o) { ConfigOption *option = §ion->options[o]; if (option->values_count == 0 && config_string_to_cstr(&option->v.value) != NULL) { fputs("\titem ", file); write_field(&option->key, file); fputc(' ', file); write_field(&option->v.value, file); fputc('\n', file); } else { ConfigString *values_end = &option->v.values[option->values_count]; for (ConfigString *value = &option->v.values[0]; value < values_end; ++value) { fputs("\tlist ", file); write_field(&option->key, file); fputc(' ', file); write_field(value, file); fputc('\n', file); } } } fputc('\n', file); } } opentyrian-2.1.20221123/src/config_file.h000066400000000000000000000447151432005211200176440ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2015 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /*! * \file config_file.h * \author Carl Reinke * \date 2015 * \copyright GNU General Public License v2+ or Mozilla Public License 2.0 */ #ifndef CONFIG_FILE_H #define CONFIG_FILE_H #include #include #include #include #include #ifndef COMPILE_TIME_ASSERT /*! * \brief Cause compile error if compile-time computable condition fails. * * \param[in] name the unique identifier of the assertion * \param[in] cond the condition */ #define COMPILE_TIME_ASSERT(name, cond) typedef int assert_ ## name[(cond) * 2 - 1] #endif #ifndef COUNTOF /*! * \brief Calculate the number of elements in a fixed-length array. * * \param[in] a the fixed-length array * \return the number of elements in the array */ #define COUNTOF(a) (sizeof(a) / sizeof(*(a))) #endif /* string type */ /*! * \brief A short-string-optimizing string type. * * This struct allows for storing up to 15 characters (plus a terminating \c '\0') inline. For * longer strings memory will be allocated. * * The tag for this union is the last character of \p short_buf: * \li if \c '\0' then \p short_buf is valid, * \li otherwise \p long_buf is valid. */ typedef union { /*! * \brief The inline buffer for short strings. */ char short_buf[16]; /*! * \brief The buffer for long strings. * * May be \c NULL. */ char *long_buf; } ConfigString; /*! \cond suppress_doxygen */ COMPILE_TIME_ASSERT(string_short_buf_sufficient, sizeof(char *) + 1 <= COUNTOF(((ConfigString *)NULL)->short_buf)); /*! \endcond */ /*! \cond suppress_doxygen */ #define CONFIG_STRING_LONG_TAG(s) ((s).short_buf[COUNTOF((s).short_buf) - 1]) /*! \endcond */ /*! * \brief Return a C-string backed by a string. * * \param[in] string the string * \return the C-string */ static inline const char *config_string_to_cstr(const ConfigString *string) { assert(string != NULL); char is_long = CONFIG_STRING_LONG_TAG(*string); return is_long ? string->long_buf : string->short_buf; } /* config types */ /*! * \brief An option consisting of one (an item) or many (a list) values. */ typedef struct { /*! * \brief The key of the option. */ ConfigString key; /*! * \brief The number of values in the option if it is a 'list' option. * * If \c 0 then the option \e may be an 'item' option. * * \see ::ConfigOption::value */ unsigned int values_count; /*! * \brief The value or values. * * The tag for this union is \p value_count: * \li if \c 0 then \p value is valid, * \li otherwise \p values is valid. */ union { /*! * \brief The value of an 'item' option or an empty 'list' option. * * If this field is \c NULL then the option is an empty 'list' option. */ ConfigString value; /*! * \brief The values of a non-empty 'list' option. */ ConfigString *values; } v; } ConfigOption; /*! * \brief A section consisting of options. */ typedef struct { /*! * \brief The type of the section. */ ConfigString type; /*! * \brief The optional name of the section. * * May be \c NULL. */ ConfigString name; /*! * \brief The number of options in the section. */ unsigned int options_count; /*! * \brief The options in the section. * * \c NULL if \p options_count is \c 0. */ ConfigOption *options; } ConfigSection; /*! * \brief A configuration consisting of sections. */ typedef struct { /*! * \brief The number of sections in the configuration. */ unsigned int sections_count; /*! * \brief The sections in the configuration. * * \c NULL if \p sections_count is \c 0. */ ConfigSection *sections; } Config; /* config manipulators */ /*! * \brief Initialize a configuration. * * \param[in] config the configuration * \return void */ extern void config_init(Config *config); /*! * \brief Release any memory allocated inside a configuration. * * \param[in] config the configuration * \return void */ extern void config_deinit(Config *config); /*! * \brief Parse a configuration from a file. * * \param[in] config the uninitialized configuration * \param[in] file the file handle * \return whether parsing succeeded */ extern bool config_parse(Config *config, FILE *file); /*! * \brief Write a configuration to a file. * * \param[in] config the configuration * \param[in] file the file handle * \return void */ extern void config_write(const Config *config, FILE *file); /* config section accessors/manipulators -- by type, name */ /*! \see ::config_add_section() */ extern ConfigSection *config_add_section_len(Config *config, const char *type, size_t type_len, const char *name, size_t name_len); /*! * \brief Add a section to a configuration. * * \param[in] config the configuration to contain the section * \param[in] type the type of the section * \param[in] name the name of the section; may be \c NULL * \return the added section; \c NULL if out of memory */ static inline ConfigSection *config_add_section(Config *config, const char *type, const char *name) { assert(type != NULL); return config_add_section_len(config, type, strlen(type), name, name == NULL ? 0 : strlen(name)); } // TODO: extern Config *config_remove_section(Config *config, unsigned int i); /*! * \brief Iterate sections by type. * * \param[in] config the configuration containing the sections * \param[in] type the type of the section * \param[in,out] save the saved state of the iterator; initialize \c *save to \c NULL before * iteration * \return the section; \c NULL if iteration finished */ extern ConfigSection *config_find_sections(Config *config, const char *type, ConfigSection **save); /*! * \brief Find a section by type and name. * * \param[in] config the configuration containing the section * \param[in] type the type of the section * \param[in] name the name of the section * \return the section; \c NULL if it does not exist */ extern ConfigSection *config_find_section(Config *config, const char *type, const char *name); /*! * \brief Find a section by type and name, creating the section if it did not exist. * * \param[in] config the configuration containing the section * \param[in] type the type of the section * \param[in] name the name of the section; may be \c NULL * \return the section; \c NULL if out of memory */ extern ConfigSection *config_find_or_add_section(Config *config, const char *type, const char *name); /* config option accessors/manipulators -- by key */ /*! \see ::config_set_option() */ extern ConfigOption *config_set_option_len(ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len); /*! * \brief Set a value of an 'item' option by key, creating the option if necessary. * * \param[in] section the section containing the option * \param[in] key the option key * \param[in] value the item value; \c NULL to set an emtpy 'list' option instead of an 'item' * option (can be used to delete an 'item' option) * \return the option; \c NULL if out of memory */ static inline ConfigOption *config_set_option(ConfigSection *section, const char *key, const char *value) { assert(key != NULL); return config_set_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value)); } /*! * \brief Get an option by key. * * \param[in] section the section containing the option * \param[in] key the option key * \return the option; \c NULL if it does not exist */ extern ConfigOption *config_get_option(const ConfigSection *section, const char *key); /*! \see ::config_get_or_set_option() */ extern ConfigOption *config_get_or_set_option_len(ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len); /*! * \brief Get an option by key, creating an 'item' option if the option did not exist. * * \param[in] section the section containing the option * \param[in] key the option key * \param[in] value the default item value; \c NULL to set an empty 'list' option instead of an * 'item' option * \return the option; \c NULL if out of memory */ static inline ConfigOption *config_get_or_set_option(ConfigSection *section, const char *key, const char *value) { assert(key != NULL); return config_get_or_set_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value)); } /*! \see ::config_set_string_option() */ extern void config_set_string_option_len(ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len); /*! * \brief Set a string value of an 'item' option by key, creating the option if necessary. * * \param[in] section the section containing the option * \param[in] key the option key * \param[in] value the item value * \return void */ static inline void config_set_string_option(ConfigSection *section, const char *key, const char *value) { assert(key != NULL); config_set_string_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value)); } /*! * \brief Get a string value of an 'item' option by key. * * \param[in] section the section containing the option * \param[in] key the option key * \param[out] out_value the item value if a valid option exists; otherwise unset * \return whether \p out_value was set */ extern bool config_get_string_option(const ConfigSection *section, const char *key, const char **out_value); /*! * \brief Get a string value of an 'item' option by key, setting the option if it was invalid or * creating the option if it did not exist. * * \param[in] section the section containing the option * \param[in] key the option key * \param[in] value the default item value * \return the value */ extern const char *config_get_or_set_string_option(ConfigSection *section, const char *key, const char *value); /*! * \brief The styles of boolean values. */ typedef enum { ZERO_ONE = 0, NO_YES = 1, OFF_ON = 2, FALSE_TRUE = 3, } ConfigBoolStyle; /*! * \brief Set a boolean value of an 'item' option by key, creating the option if necessary. * * \param[in] section the section containing the option * \param[in] key the option key * \param[in] value the item value * \param[in] style the style of boolean value * \return void */ extern void config_set_bool_option(ConfigSection *section, const char *key, bool value, ConfigBoolStyle style); /*! * \brief Get a boolean value of an 'item' option by key. * * \param[in] section the section containing the option * \param[in] key the option key * \param[out] out_value the item value if a valid option exists; otherwise unset * \return whether \p out_value was set */ extern bool config_get_bool_option(const ConfigSection *section, const char *key, bool *out_value); /*! * \brief Get a boolean value of an 'item' option by key, setting the option if it was invalid or * creating the option if it did not exist. * * \param[in] section the section containing the option * \param[in] key the option key * \param[in] value the default item value * \param[in] style the style of boolean value * \return the value */ extern bool config_get_or_set_bool_option(ConfigSection *section, const char *key, bool value, ConfigBoolStyle style); /*! * \brief Set an integer value of an 'item' option by key, creating the option if necessary. * * \param[in] section the section containing the option * \param[in] key the option key * \param[in] value the item value * \return void */ extern void config_set_int_option(ConfigSection *section, const char *key, int value); /*! * \brief Get an integer value of an 'item' option by key. * * \param[in] section the section containing the option * \param[in] key the option key * \param[out] out_value the item value if a valid option exists; otherwise unset * \return whether \p out_value was set */ extern bool config_get_int_option(const ConfigSection *section, const char *key, int *out_value); /*! * \brief Get an integer value of an 'item' option by key, setting the option if it was invalid or * creating the option if it did not exist. * * \param[in] section the section containing the option * \param[in] key the option key * \param[in] value the default item value * \return the value */ extern int config_get_or_set_int_option(ConfigSection *section, const char *key, int value); /*! * \brief Set an unsigned integer value of an 'item' option by key, creating the option if * necessary. * * \param[in] section the section containing the option * \param[in] key the option key * \param[in] value the item value * \return void */ extern void config_set_uint_option(ConfigSection *section, const char *key, unsigned int value); /*! * \brief Get an unsigned integer value of an 'item' option by key. * * \param[in] section the section containing the option * \param[in] key the option key * \param[out] out_value the item value if a valid option exists; otherwise unset * \return whether \p out_value was set */ extern bool config_get_uint_option(const ConfigSection *section, const char *key, unsigned int *out_value); /*! * \brief Get an unsigned integer value of an 'item' option by key, setting the option if it was * invalid or creating the option if it did not exist. * * \param[in] section the section containing the option * \param[in] key the option key * \param[in] value the default item value * \return the value */ extern unsigned int config_get_or_set_uint_option(ConfigSection *section, const char *key, unsigned int value); /* config option accessors/manipulators -- by reference */ /*! \see ::config_set_value() */ extern ConfigOption *config_set_value_len(ConfigOption *option, const char *value, size_t value_len); /*! * \brief Set the value of an 'item' option. * * \param[in] option the option * \param[in] value the value * \return the option; \c NULL if out of memory */ static inline ConfigOption *config_set_value(ConfigOption *option, const char *value) { return config_set_value_len(option, value, value == NULL ? 0 : strlen(value)); } /*! \see ::config_add_value() */ extern ConfigOption *config_add_value_len(ConfigOption *option, const char *value, size_t value_len); /*! * \brief Add a value to a 'list' option. * * \param[in] option the option * \param[in] value the value * \return the option; \c NULL if out of memory */ static inline ConfigOption *config_add_value(ConfigOption *option, const char *value) { assert(value != NULL); return config_add_value_len(option, value, strlen(value)); } /*! * \brief Remove a value from a 'list' option. * * \param[in] option the option * \param[in] i the index of the value * \return the option; \c NULL if out of memory or invalid \p index */ extern ConfigOption *config_remove_value(ConfigOption *option, unsigned int i); /*! * \brief Get the value of an 'item' option. * * \param[in] option the option * \return the value; \c NULL if \p option was \c NULL or was a 'list' option */ extern const char *config_get_value(const ConfigOption *option); /*! * \brief Get the value that indicates whether the option is a 'list' option. * * \param[in] option the option * \return whether the option is a 'list' option */ static inline bool config_is_value_list(const ConfigOption *option) { assert(option != NULL); return option->values_count > 0 || config_string_to_cstr(&option->v.value) == NULL; } /*! * \brief Get the number of values assigned to the option. * * \param[in] option the option * \return \c 1 if the option is an 'item' option; the number of elements if the option is a 'list' * option */ static inline unsigned int config_get_value_count(const ConfigOption *option) { assert(option != NULL); return option->values_count == 0 ? (config_string_to_cstr(&option->v.value) == NULL ? 0 : 1) : option->values_count; } /*! * \brief Iterate over the values assigned to the option. * * \param[out] string_value the value variable to declare * \param[in] option the option */ #define foreach_option_value(string_value, option) \ for (ConfigOption *_option = (option); _option != NULL; _option = NULL) \ for (ConfigString *_values_begin = _option->values_count == 0 ? &_option->v.value : &_option->v.values[0], \ *_values_end = _option->values_count == 0 ? _values_begin + 1 : &_option->v.values[_option->values_count], \ *_value = _values_begin; _value < _values_end; ++_value) \ for (const char *(string_value) = config_string_to_cstr(_value); (string_value) != NULL; (string_value) = NULL) /*! * \brief Iterate over the values assigned to the option. * * \param[out] i the index variable to declare * \param[out] string_value the value variable to declare * \param[in] option the option */ #define foreach_option_i_value(i, string_value, option) \ for (unsigned int (i) = 0; (i) == 0; (i) = ~0) \ for (ConfigOption *_option = (option); _option != NULL; _option = NULL) \ for (ConfigString *_values_begin = _option->values_count == 0 ? &_option->v.value : &_option->v.values[0], \ *_values_end = _option->values_count == 0 ? _values_begin + 1 : &_option->v.values[_option->values_count], \ *_value = _values_begin; _value < _values_end; ++_value, (i) = _value - _values_begin) \ for (const char *(string_value) = config_string_to_cstr(_value); (string_value) != NULL; (string_value) = NULL) /*! * \brief Remove a value from an option during iteration. Should be followed by \c continue. */ #define foreach_remove_option_value() \ { \ extern void config_oom(void); \ unsigned int _value_i = _value - _values_begin; \ if (config_remove_value(_option, _value_i) == NULL) \ config_oom(); \ _values_begin = _option->values_count == 0 ? &_option->v.value : &_option->v.values[0]; \ _values_end = _option->values_count == 0 ? _values_begin + 1 : &_option->v.values[_option->values_count]; \ _value = _values_begin + _value_i - 1; \ } #endif opentyrian-2.1.20221123/src/destruct.c000066400000000000000000002236301432005211200172230ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* File notes: * Two players duke it out in a Scorched Earth style game. * Most of the variables referring to the players are global as * they are often edited and that's how the original was written. * * Currently this file is at its final stage for vanilla destruct. * Almost all of the left/right code duplications is gone. Most of the * functions have been examined and tightened up, none of the enums * start with '1', and the various large functions have been divided into * smaller chunks. * * Destruct also supports some 'hidden' configuration that's just too awesome * to not have available. Destruct has no configuration options in game, but * that doesn't stop us from changing various limiting vars and letting * people remap the keyboard. AIs may also be introduced here; fighting a * stateless AI isn't really challenging after all. * * This hidden config also allows for a hidden game mode! Though as a custom * game mode wouldn't show up in the data files it forces us to distinguish * between the constant DESTRUCT_MODES (5) and MAX_MODES (6). DESTRUCT_MODES * is only used with loaded data. * * Things I wanted to do but can't: Remove references to VGAScreen. For * a multitude of reasons this just isn't feasible. It would have been nice * to increase the playing field though... */ /*** Headers ***/ #include "destruct.h" #include "config.h" #include "config_file.h" #include "fonthand.h" #include "helptext.h" #include "keyboard.h" #include "loudness.h" #include "mtrand.h" #include "nortsong.h" #include "opentyr.h" #include "palette.h" #include "picload.h" #include "sprite.h" #include "varz.h" #include "vga256d.h" #include "video.h" #include /*** Defines ***/ #define UNIT_HEIGHT 12 #define MAX_KEY_OPTIONS 4 /*** Enums ***/ enum de_state_t { STATE_INIT, STATE_RELOAD, STATE_CONTINUE }; enum de_player_t { PLAYER_LEFT = 0, PLAYER_RIGHT = 1, MAX_PLAYERS = 2 }; enum de_team_t { TEAM_LEFT = 0, TEAM_RIGHT = 1, MAX_TEAMS = 2 }; enum de_mode_t { MODE_5CARDWAR = 0, MODE_TRADITIONAL, MODE_HELIASSAULT, MODE_HELIDEFENSE, MODE_OUTGUNNED, MODE_CUSTOM, MODE_FIRST = MODE_5CARDWAR, MODE_LAST = MODE_CUSTOM, MAX_MODES = 6, MODE_NONE = -1 }; enum de_unit_t { UNIT_TANK = 0, UNIT_NUKE, UNIT_DIRT, UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI, UNIT_FIRST = UNIT_TANK, UNIT_LAST = UNIT_HELI, MAX_UNITS = 8, UNIT_NONE = -1 }; enum de_shot_t { SHOT_TRACER = 0, SHOT_SMALL, SHOT_LARGE, SHOT_MICRO, SHOT_SUPER, SHOT_DEMO, SHOT_SMALLNUKE, SHOT_LARGENUKE, SHOT_SMALLDIRT, SHOT_LARGEDIRT, SHOT_MAGNET, SHOT_MINILASER, SHOT_MEGALASER, SHOT_LASERTRACER, SHOT_MEGABLAST, SHOT_MINI, SHOT_BOMB, SHOT_FIRST = SHOT_TRACER, SHOT_LAST = SHOT_BOMB, MAX_SHOT_TYPES = 17, SHOT_INVALID = -1 }; enum de_expl_t { EXPL_NONE, EXPL_MAGNET, EXPL_DIRT, EXPL_NORMAL }; /* this needs a better name */ enum de_trails_t { TRAILS_NONE, TRAILS_NORMAL, TRAILS_FULL }; enum de_pixel_t { PIXEL_BLACK = 0, PIXEL_DIRT = 25 }; enum de_mapflags_t { MAP_NORMAL = 0x00, MAP_WALLS = 0x01, MAP_RINGS = 0x02, MAP_HOLES = 0x04, MAP_FUZZY = 0x08, MAP_TALL = 0x10 }; /* keys and moves should line up. */ enum de_keys_t { KEY_LEFT = 0, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_CHANGE, KEY_FIRE, KEY_CYUP, KEY_CYDN, MAX_KEY = 8 }; enum de_move_t { MOVE_LEFT = 0, MOVE_RIGHT, MOVE_UP, MOVE_DOWN, MOVE_CHANGE, MOVE_FIRE, MOVE_CYUP, MOVE_CYDN, MAX_MOVE = 8 }; /* The tracerlaser is dummied out. It works but (probably due to the low * MAX_SHOTS) is not assigned to anything. The bomb does not work. */ /*** Structs ***/ struct destruct_config_s { unsigned int max_shots; unsigned int min_walls; unsigned int max_walls; unsigned int max_explosions; unsigned int max_installations; bool allow_custom; bool alwaysalias; bool jumper_straight[2]; bool ai[2]; }; struct destruct_unit_s { /* Positioning/movement */ unsigned int unitX; /* yep, one's an int and the other is a real */ float unitY; float unitYMov; bool isYInAir; /* What it is and what it fires */ enum de_unit_t unitType; enum de_shot_t shotType; /* What it's pointed */ float angle; float power; /* Misc */ int lastMove; unsigned int ani_frame; int health; }; struct destruct_shot_s { bool isAvailable; float x; float y; float xmov; float ymov; bool gravity; unsigned int shottype; //int shotdur; /* This looks to be unused */ unsigned int trailx[4], traily[4], trailc[4]; }; struct destruct_explo_s { bool isAvailable; unsigned int x, y; unsigned int explowidth; unsigned int explomax; unsigned int explofill; enum de_expl_t exploType; }; struct destruct_moves_s { bool actions[MAX_MOVE]; }; struct destruct_keys_s { SDL_Scancode Config[MAX_KEY][MAX_KEY_OPTIONS]; }; struct destruct_ai_s { int c_Angle, c_Power, c_Fire; unsigned int c_noDown; }; struct destruct_player_s { bool is_cpu; struct destruct_ai_s aiMemory; struct destruct_unit_s * unit; struct destruct_moves_s moves; struct destruct_keys_s keys; enum de_team_t team; unsigned int unitsRemaining; unsigned int unitSelected; unsigned int shotDelay; unsigned int score; }; struct destruct_wall_s { bool wallExist; unsigned int wallX, wallY; }; struct destruct_world_s { /* Map data & screen pointer */ unsigned int baseMap[320]; SDL_Surface * VGAScreen; struct destruct_wall_s * mapWalls; /* Map configuration */ enum de_mode_t destructMode; unsigned int mapFlags; }; /*** Function decs ***/ //Prep functions static void JE_destructMain(void); static void JE_introScreen(void); static enum de_mode_t JE_modeSelect(void); static void JE_helpScreen(void); static void JE_pauseScreen(void); //level generating functions static void JE_generateTerrain(void); static void DE_generateBaseTerrain(unsigned int, unsigned int *); static void DE_drawBaseTerrain(unsigned int *); static void DE_generateUnits(unsigned int *); static void DE_generateWalls(struct destruct_world_s *); static void DE_generateRings(SDL_Surface *, Uint8); static void DE_ResetLevel(void); static unsigned int JE_placementPosition(unsigned int, unsigned int, unsigned int *); //drawing functions static void JE_aliasDirt(SDL_Surface *); static void DE_RunTickDrawCrosshairs(void); static void DE_RunTickDrawHUD(void); static void DE_GravityDrawUnit(enum de_player_t, struct destruct_unit_s *); static void DE_RunTickAnimate(void); static void DE_RunTickDrawWalls(void); static void DE_DrawTrails(struct destruct_shot_s *, unsigned int, unsigned int, unsigned int); static void JE_tempScreenChecking(void); static void JE_superPixel(unsigned int, unsigned int); static void JE_pixCool(unsigned int, unsigned int, Uint8); //player functions static void DE_RunTickGetInput(void); static void DE_ProcessInput(void); static void DE_ResetPlayers(void); static void DE_ResetAI(void); static void DE_ResetActions(void); static void DE_RunTickAI(void); //unit functions static void DE_RaiseAngle(struct destruct_unit_s *); static void DE_LowerAngle(struct destruct_unit_s *); static void DE_RaisePower(struct destruct_unit_s *); static void DE_LowerPower(struct destruct_unit_s *); static void DE_CycleWeaponUp(struct destruct_unit_s *); static void DE_CycleWeaponDown(struct destruct_unit_s *); static void DE_RunMagnet(enum de_player_t, struct destruct_unit_s *); static void DE_GravityFlyUnit(struct destruct_unit_s *); static void DE_GravityLowerUnit(struct destruct_unit_s *); static void DE_DestroyUnit(enum de_player_t, struct destruct_unit_s *); static void DE_ResetUnits(void); static inline bool DE_isValidUnit(struct destruct_unit_s *); //weapon functions static void DE_ResetWeapons(void); static void DE_RunTickShots(void); static void DE_RunTickExplosions(void); static void DE_TestExplosionCollision(unsigned int, unsigned int); static void JE_makeExplosion(unsigned int, unsigned int, enum de_shot_t); static void DE_MakeShot(enum de_player_t, const struct destruct_unit_s *, int); //gameplay functions static enum de_state_t DE_RunTick(void); static void DE_RunTickCycleDeadUnits(void); static void DE_RunTickGravity(void); static bool DE_RunTickCheckEndgame(void); static bool JE_stabilityCheck(unsigned int, unsigned int); //sound static void DE_RunTickPlaySounds(void); static void JE_eSound(unsigned int); /*** Weapon configurations ***/ /* Part of me wants to leave these as bytes to save space. */ static const bool demolish[MAX_SHOT_TYPES] = {false, false, false, false, false, true, true, true, false, false, false, false, true, false, true, false, true}; //static const int shotGr[MAX_SHOT_TYPES] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101}; static const int shotTrail[MAX_SHOT_TYPES] = {TRAILS_NONE, TRAILS_NONE, TRAILS_NONE, TRAILS_NORMAL, TRAILS_NORMAL, TRAILS_NORMAL, TRAILS_FULL, TRAILS_FULL, TRAILS_NONE, TRAILS_NONE, TRAILS_NONE, TRAILS_NORMAL, TRAILS_FULL, TRAILS_NORMAL, TRAILS_FULL, TRAILS_NORMAL, TRAILS_NONE}; //static const int shotFuse[MAX_SHOT_TYPES] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}; static const int shotDelay[MAX_SHOT_TYPES] = {10, 30, 80, 20, 60, 100, 140, 200, 20, 60, 5, 15, 50, 5, 80, 16, 0}; static const int shotSound[MAX_SHOT_TYPES] = {S_SELECT, S_WEAPON_2, S_WEAPON_1, S_WEAPON_7, S_WEAPON_7, S_EXPLOSION_9, S_EXPLOSION_22, S_EXPLOSION_22, S_WEAPON_5, S_WEAPON_13, S_WEAPON_10, S_WEAPON_15, S_WEAPON_15, S_WEAPON_26, S_WEAPON_14, S_WEAPON_7, S_WEAPON_7}; static const int exploSize[MAX_SHOT_TYPES] = {4, 20, 30, 14, 22, 16, 40, 60, 10, 30, 0, 5, 10, 3, 15, 7, 0}; static const bool shotBounce[MAX_SHOT_TYPES] = {false, false, false, false, false, false, false, false, false, false, false, true, true, true, true, false, true}; static const int exploDensity[MAX_SHOT_TYPES] = { 2, 5, 10, 15, 20, 15, 25, 30, 40, 80, 0, 30, 30, 4, 30, 5, 0}; static const int shotDirt[MAX_SHOT_TYPES] = {EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_DIRT, EXPL_DIRT, EXPL_MAGNET, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NONE}; static const int shotColor[MAX_SHOT_TYPES] = {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 10, 10, 10, 16, 0}; static const int defaultWeapon[MAX_UNITS] = {SHOT_SMALL, SHOT_MICRO, SHOT_SMALLDIRT, SHOT_INVALID, SHOT_MAGNET, SHOT_MINILASER, SHOT_MICRO, SHOT_MINI}; static const int defaultCpuWeapon[MAX_UNITS] = {SHOT_SMALL, SHOT_MICRO, SHOT_DEMO, SHOT_INVALID, SHOT_MAGNET, SHOT_MINILASER, SHOT_MICRO, SHOT_MINI}; static const int defaultCpuWeaponB[MAX_UNITS] = {SHOT_DEMO, SHOT_SMALLNUKE, SHOT_DEMO, SHOT_INVALID, SHOT_MAGNET, SHOT_MEGALASER, SHOT_MICRO, SHOT_MINI}; static const int systemAngle[MAX_UNITS] = {true, true, true, false, false, true, false, false}; static const int baseDamage[MAX_UNITS] = {200, 120, 400, 300, 80, 150, 600, 40}; static const int systemAni[MAX_UNITS] = {false, false, false, true, false, false, false, true}; static bool weaponSystems[MAX_UNITS][MAX_SHOT_TYPES] = { {1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // normal {0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // nuke {0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, // dirt {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // worthless {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // magnet {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}, // laser {1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, // jumper {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0} // helicopter }; /* More constant configuration settings. */ /* Music that destruct will play. You can check out musmast.c to see what is what. */ static const JE_byte goodsel[14] /*[1..14]*/ = {1, 2, 6, 12, 13, 14, 17, 23, 24, 26, 28, 29, 32, 33}; /* Unit creation. Need to move this later: Doesn't belong here */ static JE_byte basetypes[10][11] /*[1..8, 1..11]*/ = /* [0] is amount of units*/ { {5, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_DIRT, UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI}, /*Normal*/ {1, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK}, /*Traditional*/ {4, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI}, /*Weak Heli attack fleet*/ {8, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_NUKE, UNIT_NUKE, UNIT_DIRT, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER}, /*Strong Heli defense fleet*/ {8, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI}, /*Strong Heli attack fleet*/ {4, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_NUKE, UNIT_DIRT, UNIT_MAGNET, UNIT_JUMPER, UNIT_JUMPER}, /*Weak Heli defense fleet*/ {8, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI, UNIT_TANK, UNIT_NUKE}, /*Overpowering fleet*/ {4, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_TANK, UNIT_LASER, UNIT_JUMPER, UNIT_HELI, UNIT_NUKE, UNIT_JUMPER}, /*Weak fleet*/ {5, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_DIRT, UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI}, /*Left custom*/ {5, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_DIRT, UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI}, /*Right custom*/ }; static const unsigned int baseLookup[MAX_PLAYERS][MAX_MODES] = { {0, 1, 3, 4, 6, 8}, {0, 1, 2, 5, 7, 9} }; static const JE_byte GraphicBase[MAX_PLAYERS][MAX_UNITS] = { { 1, 6, 11, 58, 63, 68, 96, 153}, { 20, 25, 30, 77, 82, 87, 115, 172} }; static const JE_byte ModeScore[MAX_PLAYERS][MAX_MODES] = { {1, 0, 0, 5, 0, 1}, {1, 0, 5, 0, 1, 1} }; static SDL_Scancode defaultKeyConfig[MAX_PLAYERS][MAX_KEY][MAX_KEY_OPTIONS] = { { {SDL_SCANCODE_C}, {SDL_SCANCODE_V}, {SDL_SCANCODE_A}, {SDL_SCANCODE_Z}, {SDL_SCANCODE_LALT}, {SDL_SCANCODE_X, SDL_SCANCODE_LSHIFT}, {SDL_SCANCODE_LCTRL}, {SDL_SCANCODE_SPACE} }, { {SDL_SCANCODE_LEFT, SDL_SCANCODE_KP_4}, {SDL_SCANCODE_RIGHT, SDL_SCANCODE_KP_6}, {SDL_SCANCODE_UP, SDL_SCANCODE_KP_8}, {SDL_SCANCODE_DOWN, SDL_SCANCODE_KP_2}, {SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_KP_5}, {SDL_SCANCODE_INSERT, SDL_SCANCODE_RETURN, SDL_SCANCODE_KP_0, SDL_SCANCODE_KP_ENTER}, {SDL_SCANCODE_PAGEUP, SDL_SCANCODE_KP_9}, {SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_KP_3} } }; /*** Globals ***/ static SDL_Surface *destructTempScreen; static JE_boolean destructFirstTime; static struct destruct_config_s config = { 40, 20, 20, 40, 10, false, false, {true, false}, {true, false} }; static struct destruct_player_s destruct_player[MAX_PLAYERS]; static struct destruct_world_s world; static struct destruct_shot_s * shotRec; static struct destruct_explo_s * exploRec; static const char *const player_names[] = { "left", "right", }; static const char *const key_names[] = { "left", "right", "up", "down", "change", "fire", "previous weapon", "next weapon", }; static const char *const unit_names[] = { "tank", "nuke", "dirt", "satellite", "magnet", "laser", "jumper", "heli", }; static enum de_unit_t get_unit_by_name(const char *unit_name) { for (enum de_unit_t unit = UNIT_FIRST; unit < MAX_UNITS; ++unit) if (strcmp(unit_name, unit_names[unit]) == 0) return unit; return UNIT_NONE; } static void load_destruct_config(Config *config_) { ConfigSection *section; section = config_find_or_add_section(config_, "destruct", NULL); if (section == NULL) exit(EXIT_FAILURE); // out of memory config.alwaysalias = config_get_or_set_bool_option(section, "antialias craters", false, NO_YES); weaponSystems[UNIT_LASER][SHOT_LASERTRACER] = config_get_or_set_bool_option(section, "tracer laser", false, OFF_ON); config.max_shots = config_get_or_set_int_option(section, "max shots", 40); config.max_explosions = config_get_or_set_int_option(section, "max explosions", 40); config.min_walls = config_get_or_set_int_option(section, "min walls", 20); config.max_walls = config_get_or_set_int_option(section, "max walls", 20); config.ai[0] = config_get_or_set_bool_option(section, "left ai", true, NO_YES); config.jumper_straight[0] = config_get_or_set_bool_option(section, "left jumper fires straight", true, NO_YES); config.ai[1] = config_get_or_set_bool_option(section, "right ai", false, NO_YES); config.jumper_straight[1] = config_get_or_set_bool_option(section, "right jumper fires straight", false, NO_YES); // keyboard controls for (int p = 0; p < MAX_PLAYERS; ++p) { section = config_find_section(config_, "destruct keyboard", player_names[p]); if (section == NULL) if ((section = config_add_section(config_, "destruct keyboard", player_names[p])) == NULL) exit(-1); ConfigOption *option; for (int k = 0; k < MAX_KEY; ++k) { if ((option = config_get_or_set_option(section, key_names[k], NULL)) == NULL) exit(-1); foreach_option_i_value(i, value, option) { SDL_Scancode key = SDL_GetScancodeFromName(value); if (key != SDL_SCANCODE_UNKNOWN && i < COUNTOF(defaultKeyConfig[p][k])) { defaultKeyConfig[p][k][i] = key; } else // invalid or excess { foreach_remove_option_value(); continue; } } if (config_get_value_count(option) > 0) { // unset remaining defaults for (unsigned int i = config_get_value_count(option); i < COUNTOF(defaultKeyConfig[p][k]); ++i) defaultKeyConfig[p][k][i] = SDL_SCANCODE_UNKNOWN; } else { // set defaults for (unsigned int i = 0; i < COUNTOF(defaultKeyConfig[p][k]); ++i) if (defaultKeyConfig[p][k][i] != SDL_SCANCODE_UNKNOWN) config_add_value(option, SDL_GetScancodeName(defaultKeyConfig[p][k][i])); } } } // custom destruct mode section = config_find_section(config_, "destruct custom", NULL); if (section == NULL) if ((section = config_add_section(config_, "destruct custom", NULL)) == NULL) exit(-1); config.allow_custom = config_get_or_set_bool_option(section, "enable", false, NO_YES); char buffer[15 + 1]; for (int p = 0; p < MAX_PLAYERS; ++p) { snprintf(buffer, sizeof(buffer), "%s num units", player_names[p]); basetypes[8 + p][0] = config_get_or_set_int_option(section, buffer, basetypes[8 + p][0]); ConfigOption *option; snprintf(buffer, sizeof(buffer), "%s unit", player_names[p]); if ((option = config_get_or_set_option(section, buffer, NULL)) == NULL) exit(-1); foreach_option_i_value(i, value, option) { enum de_unit_t unit = get_unit_by_name(value); if (unit != UNIT_NONE && 1 + i < COUNTOF(basetypes[8 + p])) { basetypes[8 + p][1 + i] = unit; } else // invalid or excess { foreach_remove_option_value(); continue; } } if (config_get_value_count(option) > 0) { // set remaining units to tank for (unsigned int i = config_get_value_count(option); 1 + i < COUNTOF(basetypes[8 + p]); ++i) { basetypes[8 + p][1 + i] = UNIT_TANK; config_add_value(option, unit_names[UNIT_TANK]); } } else { // set defaults for (unsigned int i = 0; 1 + i < COUNTOF(basetypes[8 + p]); ++i) config_add_value(option, unit_names[basetypes[8 + p][1 + i]]); } } } /*** Startup ***/ void JE_destructGame(void) { unsigned int i; /* This is the entry function. Any one-time actions we need to * perform can go in here. */ JE_clr256(VGAScreen); JE_showVGA(); load_destruct_config(&opentyrian_config); //malloc things that have customizable sizes shotRec = malloc(sizeof(struct destruct_shot_s) * config.max_shots); exploRec = malloc(sizeof(struct destruct_explo_s) * config.max_explosions); world.mapWalls = malloc(sizeof(struct destruct_wall_s) * config.max_walls); //Malloc enough structures to cover all of this session's possible needs. for (i = 0; i < 10; i++) config.max_installations = MAX(config.max_installations, basetypes[i][0]); destruct_player[PLAYER_LEFT ].unit = malloc(sizeof(struct destruct_unit_s) * config.max_installations); destruct_player[PLAYER_RIGHT].unit = malloc(sizeof(struct destruct_unit_s) * config.max_installations); destructTempScreen = game_screen; world.VGAScreen = VGAScreen; JE_loadCompShapes(&destructSpriteSheet, '~'); fade_black(1); JE_destructMain(); free_sprite2s(&destructSpriteSheet); //and of course exit actions go here. free(shotRec); free(exploRec); free(world.mapWalls); free(destruct_player[PLAYER_LEFT ].unit); free(destruct_player[PLAYER_RIGHT].unit); } static void JE_destructMain(void) { enum de_state_t curState; JE_loadPic(VGAScreen, 11, false); JE_introScreen(); DE_ResetPlayers(); destruct_player[PLAYER_LEFT ].is_cpu = config.ai[PLAYER_LEFT]; destruct_player[PLAYER_RIGHT].is_cpu = config.ai[PLAYER_RIGHT]; while (true) { world.destructMode = JE_modeSelect(); if (world.destructMode == MODE_NONE) break; /* User is quitting */ do { destructFirstTime = true; JE_loadPic(VGAScreen, 11, false); DE_ResetUnits(); DE_ResetLevel(); do { curState = DE_RunTick(); } while (curState == STATE_CONTINUE); fade_black(25); } while (curState == STATE_RELOAD); } } static void JE_introScreen(void) { memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch); JE_outText(VGAScreen, JE_fontCenter(specialName[7], TINY_FONT), 90, specialName[7], 12, 5); JE_outText(VGAScreen, JE_fontCenter(miscText[64], TINY_FONT), 180, miscText[64], 15, 2); JE_outText(VGAScreen, JE_fontCenter(miscText[65], TINY_FONT), 190, miscText[65], 15, 2); JE_showVGA(); fade_palette(colors, 15, 0, 255); newkey = false; while (!newkey) { service_SDL_events(false); SDL_Delay(16); } fade_black(15); memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch); JE_showVGA(); } /* JE_modeSelect * * This function prints the DESTRUCT mode selection menu. * The return value is the selected mode, or -1 (MODE_NONE) * if the user quits. */ static void DrawModeSelectMenu(enum de_mode_t mode) { int i; /* Helper function of JE_modeSelect. Do not use elsewhere. */ for (i = 0; i < DESTRUCT_MODES; i++) JE_textShade(VGAScreen, JE_fontCenter(destructModeName[i], TINY_FONT), 82 + i * 12, destructModeName[i], 12, (i == mode) * 4, FULL_SHADE); if (config.allow_custom == true) JE_textShade(VGAScreen, JE_fontCenter("Custom", TINY_FONT), 82 + i * 12, "Custom", 12, (i == mode) * 4, FULL_SHADE); } static enum de_mode_t JE_modeSelect(void) { enum de_mode_t mode; memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch); mode = MODE_5CARDWAR; // Draw the menu and fade us in DrawModeSelectMenu(mode); JE_showVGA(); fade_palette(colors, 15, 0, 255); /* Get input in a loop. */ while (true) { /* Re-draw the menu every iteration */ DrawModeSelectMenu(mode); JE_showVGA(); /* Grab keys */ newkey = false; do { service_SDL_events(false); SDL_Delay(16); } while (!newkey); /* See what was pressed */ if (keysactive[SDL_SCANCODE_ESCAPE]) { mode = MODE_NONE; /* User is quitting, return failure */ break; } if (keysactive[SDL_SCANCODE_RETURN]) { break; /* User has selected, return choice */ } if (keysactive[SDL_SCANCODE_UP]) { if (mode == MODE_FIRST) { if (config.allow_custom == true) mode = MODE_LAST; else mode = MODE_LAST-1; } else { mode--; } } if (keysactive[SDL_SCANCODE_DOWN]) { if (mode >= MODE_LAST-1) { if (config.allow_custom == true && mode == MODE_LAST-1) mode++; else mode = MODE_FIRST; } else { mode++; } } } fade_black(15); memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch); JE_showVGA(); return mode; } static void JE_generateTerrain(void) { /* The unique modifiers: Altered generation (really tall) Fuzzy hills Rings of dirt The non-unique ones;: Rings of not dirt (holes) Walls */ world.mapFlags = MAP_NORMAL; if (mt_rand() % 2 == 0) world.mapFlags |= MAP_WALLS; if (mt_rand() % 4 == 0) world.mapFlags |= MAP_HOLES; switch (mt_rand() % 4) { case 0: world.mapFlags |= MAP_FUZZY; break; case 1: world.mapFlags |= MAP_TALL; break; case 2: world.mapFlags |= MAP_RINGS; break; } play_song(goodsel[mt_rand() % 14] - 1); DE_generateBaseTerrain(world.mapFlags, world.baseMap); DE_generateUnits(world.baseMap); DE_generateWalls(&world); DE_drawBaseTerrain(world.baseMap); if (world.mapFlags & MAP_RINGS) DE_generateRings(world.VGAScreen, PIXEL_DIRT); if (world.mapFlags & MAP_HOLES) DE_generateRings(world.VGAScreen, PIXEL_BLACK); JE_aliasDirt(world.VGAScreen); JE_showVGA(); memcpy(destructTempScreen->pixels, VGAScreen->pixels, destructTempScreen->pitch * destructTempScreen->h); } static void DE_generateBaseTerrain(unsigned int mapFlags, unsigned int * baseWorld) { unsigned int i; unsigned int newheight, HeightMul; float sinewave, sinewave2, cosinewave, cosinewave2; /* The 'terrain' is actually the video buffer :). If it's brown, flu... er, * brown pixels are what we check for collisions with. */ /* The ranges here are between .01 and roughly 0.07283...*/ sinewave = mt_rand_lt1() * M_PI / 50 + 0.01f; sinewave2 = mt_rand_lt1() * M_PI / 50 + 0.01f; cosinewave = mt_rand_lt1() * M_PI / 50 + 0.01f; cosinewave2 = mt_rand_lt1() * M_PI / 50 + 0.01f; HeightMul = 20; /* This block just exists to mix things up. */ if (mapFlags & MAP_FUZZY) { sinewave = M_PI - mt_rand_lt1() * 0.3f; sinewave2 = M_PI - mt_rand_lt1() * 0.3f; } if (mapFlags & MAP_TALL) { HeightMul = 100; } /* Now compute a height for each of our lines. */ for (i = 1; i <= 318; i++) { newheight = roundf(sinf(sinewave * i) * HeightMul + sinf(sinewave2 * i) * 15 + cosf(cosinewave * i) * 10 + sinf(cosinewave2 * i) * 15) + 130; /* Bind it; we have mins and maxs */ if (newheight < 40) newheight = 40; else if (newheight > 195) newheight = 195; baseWorld[i] = newheight; } /* The base world has been created. */ } static void DE_drawBaseTerrain(unsigned int * baseWorld) { unsigned int i; for (i = 1; i <= 318; i++) { JE_rectangle(VGAScreen, i, baseWorld[i], i, 199, PIXEL_DIRT); } } static void DE_generateUnits(unsigned int * baseWorld) { unsigned int i, j, numSatellites; for (i = 0; i < MAX_PLAYERS; i++) { numSatellites = 0; destruct_player[i].unitsRemaining = 0; for (j = 0; j < basetypes[baseLookup[i][world.destructMode]][0]; j++) { /* Not everything is the same between players */ if (i == PLAYER_LEFT) { destruct_player[i].unit[j].unitX = (mt_rand() % 120) + 10; } else { destruct_player[i].unit[j].unitX = 320 - ((mt_rand() % 120) + 22); } destruct_player[i].unit[j].unitY = JE_placementPosition(destruct_player[i].unit[j].unitX - 1, 14, baseWorld); destruct_player[i].unit[j].unitType = basetypes[baseLookup[i][world.destructMode]][(mt_rand() % 10) + 1]; /* Sats are special cases since they are useless. They don't count * as active units and we can't have a team of all sats */ if (destruct_player[i].unit[j].unitType == UNIT_SATELLITE) { if (numSatellites == basetypes[baseLookup[i][world.destructMode]][0]) { destruct_player[i].unit[j].unitType = UNIT_TANK; destruct_player[i].unitsRemaining++; } else { /* Place the satellite. Note: Earlier we cleared * space with JE_placementPosition. Now we are randomly * placing the sat's Y. It can be generated in hills * and there is a clearing underneath it. This CAN * be fixed but won't be for classic. */ destruct_player[i].unit[j].unitY = 30 + (mt_rand() % 40); numSatellites++; } } else { destruct_player[i].unitsRemaining++; } /* Now just fill in the rest of the unit's values. */ destruct_player[i].unit[j].lastMove = 0; destruct_player[i].unit[j].unitYMov = 0; destruct_player[i].unit[j].isYInAir = false; destruct_player[i].unit[j].angle = 0; destruct_player[i].unit[j].power = (destruct_player[i].unit[j].unitType == UNIT_LASER) ? 6 : 3; destruct_player[i].unit[j].shotType = defaultWeapon[destruct_player[i].unit[j].unitType]; destruct_player[i].unit[j].health = baseDamage[destruct_player[i].unit[j].unitType]; destruct_player[i].unit[j].ani_frame = 0; } } } static void DE_generateWalls(struct destruct_world_s * gameWorld) { unsigned int i, j, wallX; unsigned int wallHeight, remainWalls; unsigned int tries; bool isGood; if ((world.mapFlags & MAP_WALLS) == false) { /* Just clear them out */ for (i = 0; i < config.max_walls; i++) { gameWorld->mapWalls[i].wallExist = false; } return; } remainWalls = (rand() % (config.max_walls - config.min_walls + 1)) + config.min_walls; do { /* Create a wall. Decide how tall the wall will be */ wallHeight = (mt_rand() % 5) + 1; if (wallHeight > remainWalls) { wallHeight = remainWalls; } /* Now find a good place to put the wall. */ tries = 0; do { isGood = true; wallX = (mt_rand() % 300) + 10; /* Is this X already occupied? In the original Tyrian we only * checked to make sure four units on each side were unobscured. * That's not very scalable; instead I will check every unit, * but I'll only try plotting an unobstructed X four times. * After that we'll cover up what may; having a few units * stuck behind walls makes things mildly interesting. */ for (i = 0; i < MAX_PLAYERS; i++) { for (j = 0; j < config.max_installations; j++) { if ((wallX > destruct_player[i].unit[j].unitX - 12) && (wallX < destruct_player[i].unit[j].unitX + 13)) { isGood = false; goto label_outer_break; /* I do feel that outer breaking is a legitimate goto use. */ } } } label_outer_break: tries++; } while (isGood == false && tries < 5); /* We now have a valid X. Create the wall. */ for (i = 1; i <= wallHeight; i++) { gameWorld->mapWalls[remainWalls - i].wallExist = true; gameWorld->mapWalls[remainWalls - i].wallX = wallX; gameWorld->mapWalls[remainWalls - i].wallY = JE_placementPosition(wallX, 12, gameWorld->baseMap) - 14 * i; } remainWalls -= wallHeight; } while (remainWalls != 0); } static void DE_generateRings(SDL_Surface * screen, Uint8 pixel) { unsigned int i, j, tempSize, rings; int tempPosX1, tempPosY1, tempPosX2, tempPosY2; float tempRadian; rings = mt_rand() % 6 + 1; for (i = 1; i <= rings; i++) { tempPosX1 = (mt_rand() % 320); tempPosY1 = (mt_rand() % 160) + 20; tempSize = (mt_rand() % 40) + 10; /*Size*/ for (j = 1; j <= tempSize * tempSize * 2; j++) { tempRadian = mt_rand_lt1() * (2 * M_PI); tempPosY2 = tempPosY1 + roundf(cosf(tempRadian) * (mt_rand_lt1() * 0.1f + 0.9f) * tempSize); tempPosX2 = tempPosX1 + roundf(sinf(tempRadian) * (mt_rand_lt1() * 0.1f + 0.9f) * tempSize); if ((tempPosY2 > 12) && (tempPosY2 < 200) && (tempPosX2 > 0) && (tempPosX2 < 319)) { ((Uint8 *)screen->pixels)[tempPosX2 + tempPosY2 * screen->pitch] = pixel; } } } } static unsigned int aliasDirtPixel(const SDL_Surface * screen, unsigned int x, unsigned int y, const Uint8 * s) { //A helper function used when aliasing dirt. That's a messy process; //let's contain the mess here. unsigned int newColor = PIXEL_BLACK; if ((y > 0) && (*(s - screen->pitch) == PIXEL_DIRT)) // look up newColor += 1; if ((y < screen->h - 1u) && (*(s + screen->pitch) == PIXEL_DIRT)) // look down newColor += 3; if ((x > 0) && (*(s - 1) == PIXEL_DIRT)) // look left newColor += 2; if ((x < screen->pitch - 1u) && (*(s + 1) == PIXEL_DIRT)) // look right newColor += 2; if (newColor != PIXEL_BLACK) return newColor + 16; // 16 must be the start of the brown pixels. return PIXEL_BLACK; } static void JE_aliasDirt(SDL_Surface * screen) { /* This complicated looking function goes through the whole screen * looking for brown pixels which just happen to be next to non-brown * pixels. It's an aliaser, just like it says. */ unsigned int x, y; /* This is a pointer to a screen. If you don't like pointer arithmetic, * you won't like this function. */ Uint8 *s = screen->pixels; s += 12 * screen->pitch; for (y = 12; y < (unsigned int)screen->h; y++) { for (x = 0; x < (unsigned int)screen->pitch; x++) { if (*s == PIXEL_BLACK) *s = aliasDirtPixel(screen, x, y, s); s++; } } } static unsigned int JE_placementPosition(unsigned int passed_x, unsigned int width, unsigned int * world) { unsigned int i, new_y; /* This is the function responsible for carving out chunks of land. * There's a bug here, but it's a pretty major gameplay altering one: * areas can be carved out for units that are aerial or in mountains. * This can result in huge caverns. Ergo, it's a feature :) * * I wondered if it might be better to not carve out land at all. * On testing I determined that was distracting and added nothing. */ new_y = 0; for (i = passed_x; i <= passed_x + width - 1; i++) { if (new_y < world[i]) new_y = world[i]; } for (i = passed_x; i <= passed_x + width - 1; i++) { world[i] = new_y; } return new_y; } static bool JE_stabilityCheck(unsigned int x, unsigned int y) { unsigned int i, numDirtPixels; Uint8 * s; numDirtPixels = 0; s = destructTempScreen->pixels; s += x + (y * destructTempScreen->pitch) - 1; /* Check the 12 pixels on the bottom border of our object */ for (i = 0; i < 12; i++) { if (*s == PIXEL_DIRT) numDirtPixels++; s++; } /* If there are fewer than 10 brown pixels we don't consider it a solid base */ return (numDirtPixels < 10); } static void JE_tempScreenChecking(void) /*and copy to vgascreen*/ { Uint8 *s = VGAScreen->pixels; s += 12 * VGAScreen->pitch; Uint8 *temps = destructTempScreen->pixels; temps += 12 * destructTempScreen->pitch; for (int y = 12; y < VGAScreen->h; y++) { for (int x = 0; x < VGAScreen->pitch; x++) { // This block is what fades out explosions. The palette from 241 // to 255 fades from a very dark red to a very bright yellow. if (*temps >= 241) { if (*temps == 241) *temps = PIXEL_BLACK; else (*temps)--; } // This block is for aliasing dirt. Computers are fast these days, // and it's fun. if (config.alwaysalias == true && *temps == PIXEL_BLACK) *temps = aliasDirtPixel(VGAScreen, x, y, temps); /* This is copying from our temp screen to VGAScreen */ *s = *temps; s++; temps++; } } } static void JE_makeExplosion(unsigned int tempPosX, unsigned int tempPosY, enum de_shot_t shottype) { unsigned int i, tempExploSize; /* First find an open explosion. If we can't find one, return.*/ for (i = 0; i < config.max_explosions; i++) if (exploRec[i].isAvailable == true) break; if (i == config.max_explosions) /* No empty slots */ return; exploRec[i].isAvailable = false; exploRec[i].x = tempPosX; exploRec[i].y = tempPosY; exploRec[i].explowidth = 2; if (shottype != SHOT_INVALID) { tempExploSize = exploSize[shottype]; if (tempExploSize < 5) JE_eSound(3); else if (tempExploSize < 15) JE_eSound(4); else if (tempExploSize < 20) JE_eSound(12); else if (tempExploSize < 40) JE_eSound(11); else { JE_eSound(12); JE_eSound(11); } exploRec[i].explomax = tempExploSize; exploRec[i].explofill = exploDensity[shottype]; exploRec[i].exploType = shotDirt[shottype]; } else { JE_eSound(4); exploRec[i].explomax = (mt_rand() % 40) + 10; exploRec[i].explofill = (mt_rand() % 60) + 20; exploRec[i].exploType = EXPL_NORMAL; } } static void JE_eSound(unsigned int sound) { static int exploSoundChannel = 0; if (++exploSoundChannel > 5) exploSoundChannel = 1; soundQueue[exploSoundChannel] = sound; } static void JE_superPixel(unsigned int tempPosX, unsigned int tempPosY) { const unsigned int starPattern[5][5] = { { 0, 0, 246, 0, 0 }, { 0, 247, 249, 247, 0 }, { 246, 249, 252, 249, 246 }, { 0, 247, 249, 247, 0 }, { 0, 0, 246, 0, 0 } }; const unsigned int starIntensity[5][5] = { { 0, 0, 1, 0, 0 }, { 0, 1, 2, 1, 0 }, { 1, 2, 4, 2, 1 }, { 0, 1, 2, 1, 0 }, { 0, 0, 1, 0, 0 } }; int x, y, maxX, maxY; unsigned int rowLen; Uint8 *s; maxX = destructTempScreen->pitch; maxY = destructTempScreen->h; rowLen = destructTempScreen->pitch; s = destructTempScreen->pixels; s += (rowLen * (tempPosY - 2)) + (tempPosX - 2); for (y = 0; y < 5; y++, s += rowLen - 5) { if ((signed)tempPosY + y - 2 < 0 || /* would be out of bounds */ (signed)tempPosY + y - 2 >= maxY) { continue; } for (x = 0; x < 5; x++, s++) { if ((signed)tempPosX + x - 2 < 0 || (signed)tempPosX + x - 2 >= maxX) { continue; } if (starPattern[y][x] == 0) continue; /* this is just to speed it up */ /* at this point *s is our pixel. Our constant arrays tell us what * to do with it. */ if (*s < starPattern[y][x]) *s = starPattern[y][x]; else if (*s + starIntensity[y][x] > 255) *s = 255; else *s += starIntensity[y][x]; } } } static void JE_helpScreen(void) { unsigned int i, j; //JE_getVGA(); didn't do anything anyway? fade_black(15); memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch); JE_clr256(VGAScreen); for (i = 0; i < 2; i++) { JE_outText(VGAScreen, 100, 5 + i * 90, destructHelp[i * 12 + 0], 2, 4); JE_outText(VGAScreen, 100, 15 + i * 90, destructHelp[i * 12 + 1], 2, 1); for (j = 3; j <= 12; j++) JE_outText(VGAScreen, ((j - 1) % 2) * 160 + 10, 15 + ((j - 1) / 2) * 12 + i * 90, destructHelp[i * 12 + j-1], 1, 3); } JE_outText(VGAScreen, 30, 190, destructHelp[24], 3, 4); JE_showVGA(); fade_palette(colors, 15, 0, 255); do /* wait until user hits a key */ { service_SDL_events(true); SDL_Delay(16); } while (!newkey); fade_black(15); memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch); JE_showVGA(); fade_palette(colors, 15, 0, 255); } static void JE_pauseScreen(void) { set_volume(tyrMusicVolume / 2, fxVolume); /* Save our current screen/game world. We don't want to screw it up while paused. */ memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch); JE_outText(VGAScreen, JE_fontCenter(miscText[22], TINY_FONT), 90, miscText[22], 12, 5); JE_showVGA(); do /* wait until user hits a key */ { service_SDL_events(true); SDL_Delay(16); } while (!newkey); /* Restore current screen & volume*/ memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch); JE_showVGA(); set_volume(tyrMusicVolume, fxVolume); } /* DE_ResetX * * The reset functions clear the state of whatever they are assigned to. */ static void DE_ResetUnits(void) { unsigned int p, u; for (p = 0; p < MAX_PLAYERS; ++p) for (u = 0; u < config.max_installations; ++u) destruct_player[p].unit[u].health = 0; } static void DE_ResetPlayers(void) { unsigned int i; for (i = 0; i < MAX_PLAYERS; ++i) { destruct_player[i].is_cpu = false; destruct_player[i].unitSelected = 0; destruct_player[i].shotDelay = 0; destruct_player[i].score = 0; destruct_player[i].aiMemory.c_Angle = 0; destruct_player[i].aiMemory.c_Power = 0; destruct_player[i].aiMemory.c_Fire = 0; destruct_player[i].aiMemory.c_noDown = 0; memcpy(destruct_player[i].keys.Config, defaultKeyConfig[i], sizeof(destruct_player[i].keys.Config)); } } static void DE_ResetWeapons(void) { unsigned int i; for (i = 0; i < config.max_shots; i++) shotRec[i].isAvailable = true; for (i = 0; i < config.max_explosions; i++) exploRec[i].isAvailable = true; } static void DE_ResetLevel(void) { /* Okay, let's prep the arena */ DE_ResetWeapons(); JE_generateTerrain(); DE_ResetAI(); } static void DE_ResetAI(void) { unsigned int i, j; struct destruct_unit_s * ptr; for (i = PLAYER_LEFT; i < MAX_PLAYERS; i++) { if (destruct_player[i].is_cpu == false) continue; ptr = destruct_player[i].unit; for (j = 0; j < config.max_installations; j++, ptr++) { if (DE_isValidUnit(ptr) == false) continue; if (systemAngle[ptr->unitType] || ptr->unitType == UNIT_HELI) ptr->angle = M_PI_4; else ptr->angle = 0; ptr->power = (ptr->unitType == UNIT_LASER) ? 6 : 4; if (world.mapFlags & MAP_WALLS) ptr->shotType = defaultCpuWeaponB[ptr->unitType]; else ptr->shotType = defaultCpuWeapon[ptr->unitType]; } } } static void DE_ResetActions(void) { unsigned int i; for (i = 0; i < MAX_PLAYERS; i++) { /* Zero it all. A memset would do the trick */ memset(&(destruct_player[i].moves), 0, sizeof(destruct_player[i].moves)); } } /* DE_RunTick * * Runs one tick. One tick involves handling physics, drawing crap, * moving projectiles and explosions, and getting input. * Returns true while the game is running or false if the game is * to be terminated. */ static enum de_state_t DE_RunTick(void) { static unsigned int endDelay; setDelay(1); memset(soundQueue, 0, sizeof(soundQueue)); JE_tempScreenChecking(); DE_ResetActions(); DE_RunTickCycleDeadUnits(); DE_RunTickGravity(); DE_RunTickAnimate(); DE_RunTickDrawWalls(); DE_RunTickExplosions(); DE_RunTickShots(); DE_RunTickAI(); DE_RunTickDrawCrosshairs(); DE_RunTickDrawHUD(); JE_showVGA(); if (destructFirstTime) { fade_palette(colors, 25, 0, 255); destructFirstTime = false; endDelay = 0; } DE_RunTickGetInput(); DE_ProcessInput(); if (endDelay > 0) { if (--endDelay == 0) return STATE_RELOAD; } else if (DE_RunTickCheckEndgame() == true) { endDelay = 80; } DE_RunTickPlaySounds(); /* The rest of this cruft needs to be put in appropriate sections */ if (keysactive[SDL_SCANCODE_F10]) { destruct_player[PLAYER_LEFT].is_cpu = !destruct_player[PLAYER_LEFT].is_cpu; keysactive[SDL_SCANCODE_F10] = false; } if (keysactive[SDL_SCANCODE_F11]) { destruct_player[PLAYER_RIGHT].is_cpu = !destruct_player[PLAYER_RIGHT].is_cpu; keysactive[SDL_SCANCODE_F11] = false; } if (keysactive[SDL_SCANCODE_P]) { JE_pauseScreen(); keysactive[lastkey_scan] = false; } if (keysactive[SDL_SCANCODE_F1]) { JE_helpScreen(); keysactive[lastkey_scan] = false; } wait_delay(); if (keysactive[SDL_SCANCODE_ESCAPE]) { keysactive[SDL_SCANCODE_ESCAPE] = false; return STATE_INIT; /* STATE_INIT drops us to the mode select */ } if (keysactive[SDL_SCANCODE_BACKSPACE]) { keysactive[SDL_SCANCODE_BACKSPACE] = false; return STATE_RELOAD; /* STATE_RELOAD creates a new map */ } return STATE_CONTINUE; } /* DE_RunTickX * * Handles something that we do once per tick, such as * track ammo and move explosions. */ static void DE_RunTickCycleDeadUnits(void) { unsigned int i; struct destruct_unit_s * unit; /* This code automatically switches the active unit if it is destroyed * and skips over the useless satellite */ for (i = 0; i < MAX_PLAYERS; i++) { if (destruct_player[i].unitsRemaining == 0) continue; unit = &(destruct_player[i].unit[destruct_player[i].unitSelected]); while (DE_isValidUnit(unit) == false || unit->shotType == SHOT_INVALID) { destruct_player[i].unitSelected++; unit++; if (destruct_player[i].unitSelected >= config.max_installations) { destruct_player[i].unitSelected = 0; unit = destruct_player[i].unit; } } } } static void DE_RunTickGravity(void) { unsigned int i, j; struct destruct_unit_s * unit; for (i = 0; i < MAX_PLAYERS; i++) { unit = destruct_player[i].unit; for (j = 0; j < config.max_installations; j++, unit++) { if (DE_isValidUnit(unit) == false) /* invalid unit */ continue; switch (unit->unitType) { case UNIT_SATELLITE: /* satellites don't fall down */ break; case UNIT_HELI: case UNIT_JUMPER: if (unit->isYInAir == true) /* unit is falling down, at least in theory */ { DE_GravityFlyUnit(unit); break; } /* else treat as a normal unit */ /* fall through */ default: DE_GravityLowerUnit(unit); } /* Draw the unit. */ DE_GravityDrawUnit(i, unit); } } } static void DE_GravityDrawUnit(enum de_player_t team, struct destruct_unit_s * unit) { unsigned int anim_index; anim_index = GraphicBase[team][unit->unitType] + unit->ani_frame; if (unit->unitType == UNIT_HELI) { /* Adjust animation index if we are traveling right or left. */ if (unit->lastMove < -2) anim_index += 5; else if (unit->lastMove > 2) anim_index += 10; } else /* This handles our cannons and the like */ { anim_index += floorf(unit->angle * 9.99f / M_PI); } blit_sprite2(VGAScreen, unit->unitX, roundf(unit->unitY) - 13, destructSpriteSheet, anim_index); } static void DE_GravityLowerUnit(struct destruct_unit_s * unit) { /* units fall at a constant speed. The heli is an odd case though; * we simply give it a downward velocity, but due to a buggy implementation * the chopper didn't lower until you tried to fly it up. Tyrian 2000 fixes * this by not making the chopper a special case. I've decided to actually * mix both; the chopper is given a slight downward acceleration (simulating * a 'rocky' takeoff), and it is lowered like a regular unit, but not as * quickly. */ if (unit->unitY < 199) /* checking takes time, don't check if it's at the bottom */ { if (JE_stabilityCheck(unit->unitX, roundf(unit->unitY))) { switch (unit->unitType) { case UNIT_HELI: unit->unitYMov = 1.5f; unit->unitY += 0.2f; break; default: unit->unitY += 1; } if (unit->unitY > 199) /* could be possible */ unit->unitY = 199; } } } static void DE_GravityFlyUnit(struct destruct_unit_s * unit) { if (unit->unitY + unit->unitYMov > 199) /* would hit bottom of screen */ { unit->unitY = 199; unit->unitYMov = 0; unit->isYInAir = false; return; } /* move the unit and alter acceleration */ unit->unitY += unit->unitYMov; if (unit->unitY < 24) /* This stops units from going above the screen */ { unit->unitYMov = 0; unit->unitY = 24; } if (unit->unitType == UNIT_HELI) /* helicopters fall more slowly */ unit->unitYMov += 0.0001f; else unit->unitYMov += 0.03f; if (!JE_stabilityCheck(unit->unitX, roundf(unit->unitY))) { unit->unitYMov = 0; unit->isYInAir = false; } } static void DE_RunTickAnimate(void) { unsigned int p, u; struct destruct_unit_s * ptr; for (p = 0; p < MAX_PLAYERS; ++p) { ptr = destruct_player[p].unit; for (u = 0; u < config.max_installations; ++u, ++ptr) { /* Don't mess with any unit that is unallocated * or doesn't animate and is set to frame 0 */ if (DE_isValidUnit(ptr) == false) continue; if (systemAni[ptr->unitType] == false && ptr->ani_frame == 0) continue; if (++(ptr->ani_frame) > 3) ptr->ani_frame = 0; } } } static void DE_RunTickDrawWalls(void) { unsigned int i; for (i = 0; i < config.max_walls; i++) if (world.mapWalls[i].wallExist) blit_sprite2(VGAScreen, world.mapWalls[i].wallX, world.mapWalls[i].wallY, destructSpriteSheet, 42); } static void DE_RunTickExplosions(void) { unsigned int i, j; int tempPosX, tempPosY; float tempRadian; /* Run through all open explosions. They are not sorted in any way */ for (i = 0; i < config.max_explosions; i++) { if (exploRec[i].isAvailable == true) continue; /* Nothing to do */ for (j = 0; j < exploRec[i].explofill; j++) { /* An explosion is comprised of multiple 'flares' that fan out. Calculate where this 'flare' will end up */ tempRadian = mt_rand_lt1() * (2 * M_PI); tempPosY = exploRec[i].y + roundf(cosf(tempRadian) * mt_rand_lt1() * exploRec[i].explowidth); tempPosX = exploRec[i].x + roundf(sinf(tempRadian) * mt_rand_lt1() * exploRec[i].explowidth); /* Our game allows explosions to wrap around. This looks to have * originally been a bug that was left in as being fun, but we are * going to replicate it w/o risking out of bound arrays. */ while (tempPosX < 0) tempPosX += 320; while (tempPosX > 320) tempPosX -= 320; /* We don't draw our explosion if it's out of bounds vertically */ if (tempPosY >= 200 || tempPosY <= 15) continue; /* And now the drawing. There are only two types of explosions * right now; dirt and flares. Dirt simply draws a brown pixel; * flares explode and have a star formation. */ switch (exploRec[i].exploType) { case EXPL_DIRT: ((Uint8 *)destructTempScreen->pixels)[tempPosX + tempPosY * destructTempScreen->pitch] = PIXEL_DIRT; break; case EXPL_NORMAL: JE_superPixel(tempPosX, tempPosY); DE_TestExplosionCollision(tempPosX, tempPosY); break; default: assert(false); break; } } /* Widen the explosion and delete it if necessary. */ exploRec[i].explowidth++; if (exploRec[i].explowidth == exploRec[i].explomax) { exploRec[i].isAvailable = true; } } } static void DE_TestExplosionCollision(unsigned int PosX, unsigned int PosY) { unsigned int i, j; struct destruct_unit_s * unit; for (i = PLAYER_LEFT; i < MAX_PLAYERS; i++) { unit = destruct_player[i].unit; for (j = 0; j < config.max_installations; j++, unit++) { if (DE_isValidUnit(unit) == true && PosX > unit->unitX && PosX < unit->unitX + 11 && PosY < unit->unitY && PosY > unit->unitY - 11) { unit->health--; if (unit->health <= 0) DE_DestroyUnit(i, unit); } } } } static void DE_DestroyUnit(enum de_player_t playerID, struct destruct_unit_s * unit) { /* This function call was an evil evil piece of brilliance before. Go on. * Look at the older revisions. It passed the result of a comparison. * MULTIPLIED. This is at least a little clearer... */ JE_makeExplosion(unit->unitX + 5, roundf(unit->unitY) - 5, (unit->unitType == UNIT_HELI) ? SHOT_SMALL : SHOT_INVALID); /* Helicopters explode like small shots do. Invalids are their own special case. */ if (unit->unitType != UNIT_SATELLITE) /* increment score */ { /* todo: change when teams are created. Hacky kludge for now.*/ destruct_player[playerID].unitsRemaining--; destruct_player[((playerID == PLAYER_LEFT) ? PLAYER_RIGHT : PLAYER_LEFT)].score++; } } static void DE_RunTickShots(void) { unsigned int i, j, k; unsigned int tempTrails; unsigned int tempPosX, tempPosY; struct destruct_unit_s * unit; for (i = 0; i < config.max_shots; i++) { if (shotRec[i].isAvailable == true) continue; /* Nothing to do */ /* Move the shot. Simple displacement */ shotRec[i].x += shotRec[i].xmov; shotRec[i].y += shotRec[i].ymov; /* If the shot can bounce off the map, bounce it */ if (shotBounce[shotRec[i].shottype]) { if (shotRec[i].y > 199 || shotRec[i].y < 14) { shotRec[i].y -= shotRec[i].ymov; shotRec[i].ymov = -shotRec[i].ymov; } if (shotRec[i].x < 1 || shotRec[i].x > 318) { shotRec[i].x -= shotRec[i].xmov; shotRec[i].xmov = -shotRec[i].xmov; } } else /* If it cannot, apply normal physics */ { shotRec[i].ymov += 0.05f; /* add gravity */ if (shotRec[i].y > 199) /* We hit the floor */ { shotRec[i].y -= shotRec[i].ymov; shotRec[i].ymov = -shotRec[i].ymov * 0.8f; /* bounce at reduced velocity */ /* Don't allow a bouncing shot to bounce straight up and down */ if (shotRec[i].xmov == 0) shotRec[i].xmov += mt_rand_lt1() - 0.5f; } } /* Shot has gone out of bounds. Eliminate it. */ if (shotRec[i].x > 318 || shotRec[i].x < 1) { shotRec[i].isAvailable = true; continue; } /* Now check for collisions. */ /* Don't bother checking for collisions above the map :) */ if (shotRec[i].y <= 14) continue; tempPosX = roundf(shotRec[i].x); tempPosY = roundf(shotRec[i].y); /*Check building hits*/ for (j = 0; j < MAX_PLAYERS; j++) { unit = destruct_player[j].unit; for (k = 0; k < config.max_installations; k++, unit++) { if (DE_isValidUnit(unit) == false) continue; if (tempPosX > unit->unitX && tempPosX < unit->unitX + 11 && tempPosY < unit->unitY && tempPosY > unit->unitY - 13) { shotRec[i].isAvailable = true; JE_makeExplosion(tempPosX, tempPosY, shotRec[i].shottype); } } } tempTrails = (shotColor[shotRec[i].shottype] << 4) - 3; JE_pixCool(tempPosX, tempPosY, tempTrails); /*Draw the shot trail (if applicable) */ switch (shotTrail[shotRec[i].shottype]) { case TRAILS_NONE: break; case TRAILS_NORMAL: DE_DrawTrails(&(shotRec[i]), 2, 4, tempTrails - 3); break; case TRAILS_FULL: DE_DrawTrails(&(shotRec[i]), 4, 3, tempTrails - 1); break; } /* Bounce off of or destroy walls */ for (j = 0; j < config.max_walls; j++) { if (world.mapWalls[j].wallExist == true && tempPosX >= world.mapWalls[j].wallX && tempPosX <= world.mapWalls[j].wallX + 11 && tempPosY >= world.mapWalls[j].wallY && tempPosY <= world.mapWalls[j].wallY + 14) { if (demolish[shotRec[i].shottype]) { /* Blow up the wall and remove the shot. */ world.mapWalls[j].wallExist = false; shotRec[i].isAvailable = true; JE_makeExplosion(tempPosX, tempPosY, shotRec[i].shottype); continue; } else { /* Otherwise, bounce. */ if (shotRec[i].x - shotRec[i].xmov < world.mapWalls[j].wallX || shotRec[i].x - shotRec[i].xmov > world.mapWalls[j].wallX + 11) { shotRec[i].xmov = -shotRec[i].xmov; } if (shotRec[i].y - shotRec[i].ymov < world.mapWalls[j].wallY || shotRec[i].y - shotRec[i].ymov > world.mapWalls[j].wallY + 14) { if (shotRec[i].ymov < 0) shotRec[i].ymov = -shotRec[i].ymov; else shotRec[i].ymov = -shotRec[i].ymov * 0.8f; } tempPosX = roundf(shotRec[i].x); tempPosY = roundf(shotRec[i].y); } } } /* Our last collision check, at least for now. We hit dirt. */ if ((((Uint8 *)destructTempScreen->pixels)[tempPosX + tempPosY * destructTempScreen->pitch]) == PIXEL_DIRT) { shotRec[i].isAvailable = true; JE_makeExplosion(tempPosX, tempPosY, shotRec[i].shottype); continue; } } } static void DE_DrawTrails(struct destruct_shot_s * shot, unsigned int count, unsigned int decay, unsigned int startColor) { int i; for (i = count-1; i >= 0; i--) /* going in reverse is important as it affects how we draw */ { if (shot->trailc[i] > 0 && shot->traily[i] > 12) /* If it exists and if it's not out of bounds, draw it. */ { JE_pixCool(shot->trailx[i], shot->traily[i], shot->trailc[i]); } if (i == 0) /* The first trail we create. */ { shot->trailx[i] = roundf(shot->x); shot->traily[i] = roundf(shot->y); shot->trailc[i] = startColor; } else /* The newer trails decay into the older trails.*/ { shot->trailx[i] = shot->trailx[i-1]; shot->traily[i] = shot->traily[i-1]; if (shot->trailc[i-1] > 0) { shot->trailc[i] = shot->trailc[i-1] - decay; } } } } static void DE_RunTickAI(void) { unsigned int i, j; struct destruct_player_s * ptrPlayer, * ptrTarget; struct destruct_unit_s * ptrUnit, * ptrCurUnit; for (i = 0; i < MAX_PLAYERS; i++) { ptrPlayer = &(destruct_player[i]); if (ptrPlayer->is_cpu == false) continue; /* I've been thinking, purely hypothetically, about what it would take * to have multiple computer opponents. The answer? A lot of crap * and a 'target' variable in the destruct_player struct. */ j = i + 1; if (j >= MAX_PLAYERS) j = 0; ptrTarget = &(destruct_player[j]); ptrCurUnit = &(ptrPlayer->unit[ptrPlayer->unitSelected]); /* This is the start of the original AI. Heh. AI. */ if (ptrPlayer->aiMemory.c_noDown > 0) ptrPlayer->aiMemory.c_noDown--; /* Until all structs are properly divvied up this must only apply to player1 */ if (mt_rand() % 100 > 80) { ptrPlayer->aiMemory.c_Angle += (mt_rand() % 3) - 1; if (ptrPlayer->aiMemory.c_Angle > 1) ptrPlayer->aiMemory.c_Angle = 1; else if (ptrPlayer->aiMemory.c_Angle < -1) ptrPlayer->aiMemory.c_Angle = -1; } if (mt_rand() % 100 > 90) { if (ptrPlayer->aiMemory.c_Angle > 0 && ptrCurUnit->angle > (M_PI_2) - (M_PI / 9)) ptrPlayer->aiMemory.c_Angle = 0; else if (ptrPlayer->aiMemory.c_Angle < 0 && ptrCurUnit->angle < M_PI / 8) ptrPlayer->aiMemory.c_Angle = 0; } if (mt_rand() % 100 > 93) { ptrPlayer->aiMemory.c_Power += (mt_rand() % 3) - 1; if (ptrPlayer->aiMemory.c_Power > 1) ptrPlayer->aiMemory.c_Power = 1; else if (ptrPlayer->aiMemory.c_Power < -1) ptrPlayer->aiMemory.c_Power = -1; } if (mt_rand() % 100 > 90) { if (ptrPlayer->aiMemory.c_Power > 0 && ptrCurUnit->power > 4) ptrPlayer->aiMemory.c_Power = 0; else if (ptrPlayer->aiMemory.c_Power < 0 && ptrCurUnit->power < 3) ptrPlayer->aiMemory.c_Power = 0; else if (ptrCurUnit->power < 2) ptrPlayer->aiMemory.c_Power = 1; } // prefer helicopter ptrUnit = ptrPlayer->unit; for (j = 0; j < config.max_installations; j++, ptrUnit++) { if (DE_isValidUnit(ptrUnit) && ptrUnit->unitType == UNIT_HELI) { ptrPlayer->unitSelected = j; break; } } if (ptrCurUnit->unitType == UNIT_HELI) { if (ptrCurUnit->isYInAir == false) { ptrPlayer->aiMemory.c_Power = 1; } if (mt_rand() % ptrCurUnit->unitX > 100) { ptrPlayer->aiMemory.c_Power = 1; } if (mt_rand() % 240 > ptrCurUnit->unitX) { ptrPlayer->moves.actions[MOVE_RIGHT] = true; } else if ((mt_rand() % 20) + 300 < ptrCurUnit->unitX) { ptrPlayer->moves.actions[MOVE_LEFT] = true; } else if (mt_rand() % 30 == 1) { ptrPlayer->aiMemory.c_Angle = (mt_rand() % 3) - 1; } if (ptrCurUnit->unitX > 295 && ptrCurUnit->lastMove > 1) { ptrPlayer->moves.actions[MOVE_LEFT] = true; ptrPlayer->moves.actions[MOVE_RIGHT] = false; } if (ptrCurUnit->unitType != UNIT_HELI || ptrCurUnit->lastMove > 3 || (ptrCurUnit->unitX > 160 && ptrCurUnit->lastMove > -3)) { if (mt_rand() % (int)roundf(ptrCurUnit->unitY) < 150 && ptrCurUnit->unitYMov < 0.01f && (ptrCurUnit->unitX < 160 || ptrCurUnit->lastMove < 2)) ptrPlayer->moves.actions[MOVE_FIRE] = true; ptrPlayer->aiMemory.c_noDown = (5 - abs(ptrCurUnit->lastMove)) * (5 - abs(ptrCurUnit->lastMove)) + 3; ptrPlayer->aiMemory.c_Power = 1; } else { ptrPlayer->moves.actions[MOVE_FIRE] = false; } ptrUnit = ptrTarget->unit; for (j = 0; j < config.max_installations; j++, ptrUnit++) { if (abs((int)ptrUnit->unitX - (int)ptrCurUnit->unitX) < 8) { /* I get it. This makes helicopters hover over * their enemies. */ if (ptrUnit->unitType == UNIT_SATELLITE) { ptrPlayer->moves.actions[MOVE_FIRE] = false; } else { ptrPlayer->moves.actions[MOVE_LEFT] = false; ptrPlayer->moves.actions[MOVE_RIGHT] = false; if (ptrCurUnit->lastMove < -1) ptrCurUnit->lastMove++; else if (ptrCurUnit->lastMove > 1) ptrCurUnit->lastMove--; } } } } else { ptrPlayer->moves.actions[MOVE_FIRE] = 1; } if (mt_rand() % 200 > 198) { ptrPlayer->moves.actions[MOVE_CHANGE] = true; ptrPlayer->aiMemory.c_Angle = 0; ptrPlayer->aiMemory.c_Power = 0; ptrPlayer->aiMemory.c_Fire = 0; } if (mt_rand() % 100 > 98 || ptrCurUnit->shotType == SHOT_TRACER) { ptrPlayer->moves.actions[MOVE_CYDN] = true; } if (ptrPlayer->aiMemory.c_Angle > 0) { ptrPlayer->moves.actions[MOVE_LEFT] = true; } if (ptrPlayer->aiMemory.c_Angle < 0) { ptrPlayer->moves.actions[MOVE_RIGHT] = true; } if (ptrPlayer->aiMemory.c_Power > 0) { ptrPlayer->moves.actions[MOVE_UP] = true; } if (ptrPlayer->aiMemory.c_Power < 0 && ptrPlayer->aiMemory.c_noDown == 0) { ptrPlayer->moves.actions[MOVE_DOWN] = true; } if (ptrPlayer->aiMemory.c_Fire > 0) { ptrPlayer->moves.actions[MOVE_FIRE] = true; } if (ptrCurUnit->unitYMov < -0.1f && ptrCurUnit->unitType == UNIT_HELI) { ptrPlayer->moves.actions[MOVE_FIRE] = false; } /* This last hack was down in the processing section. * What exactly it was doing there I do not know */ if (ptrCurUnit->unitType == UNIT_LASER || ptrCurUnit->isYInAir == true) ptrPlayer->aiMemory.c_Power = 0; } } static void DE_RunTickDrawCrosshairs(void) { unsigned int i; int tempPosX, tempPosY; int direction; struct destruct_unit_s * curUnit; /* Draw the crosshairs. Most vehicles aim left or right. Helis can aim * either way and this must be accounted for. */ for (i = 0; i < MAX_PLAYERS; i++) { direction = (i == PLAYER_LEFT) ? -1 : 1; curUnit = &(destruct_player[i].unit[destruct_player[i].unitSelected]); if (curUnit->unitType == UNIT_HELI) { tempPosX = curUnit->unitX + roundf(0.1f * curUnit->lastMove * curUnit->lastMove * curUnit->lastMove) + 5; tempPosY = roundf(curUnit->unitY) + 1; } else { tempPosX = roundf(curUnit->unitX + 6 - cosf(curUnit->angle) * (curUnit->power * 8 + 7) * direction); tempPosY = roundf(curUnit->unitY - 7 - sinf(curUnit->angle) * (curUnit->power * 8 + 7)); } /* Draw it. Clip away from the HUD though. */ if (tempPosY > 9) { if (tempPosY > 11) { if (tempPosY > 13) { /* Top pixel */ JE_pix(VGAScreen, tempPosX, tempPosY - 2, 3); } /* Middle three pixels */ JE_pix(VGAScreen, tempPosX + 3, tempPosY, 3); JE_pix(VGAScreen, tempPosX, tempPosY, 14); JE_pix(VGAScreen, tempPosX - 3, tempPosY, 3); } /* Bottom pixel */ JE_pix(VGAScreen, tempPosX, tempPosY + 2, 3); } } } static void DE_RunTickDrawHUD(void) { unsigned int i; unsigned int startX; char tempstr[16]; /* Max size needed: 16 assuming 10 digit int max. */ struct destruct_unit_s * curUnit; for (i = 0; i < MAX_PLAYERS; i++) { curUnit = &(destruct_player[i].unit[destruct_player[i].unitSelected]); startX = ((i == PLAYER_LEFT) ? 0 : 320 - 150); fill_rectangle_xy(VGAScreen, startX + 5, 3, startX + 14, 8, 241); JE_rectangle(VGAScreen, startX + 4, 2, startX + 15, 9, 242); JE_rectangle(VGAScreen, startX + 3, 1, startX + 16, 10, 240); fill_rectangle_xy(VGAScreen, startX + 18, 3, startX + 140, 8, 241); JE_rectangle(VGAScreen, startX + 17, 2, startX + 143, 9, 242); JE_rectangle(VGAScreen, startX + 16, 1, startX + 144, 10, 240); blit_sprite2(VGAScreen, startX + 4, 0, destructSpriteSheet, 191 + curUnit->shotType); JE_outText (VGAScreen, startX + 20, 3, weaponNames[curUnit->shotType], 15, 2); sprintf (tempstr, "dmg~%d~", curUnit->health); JE_outText (VGAScreen, startX + 75, 3, tempstr, 15, 0); sprintf (tempstr, "pts~%d~", destruct_player[i].score); JE_outText (VGAScreen, startX + 110, 3, tempstr, 15, 0); } } static void DE_RunTickGetInput(void) { unsigned int player_index, key_index, slot_index; SDL_Scancode key; /* destruct_player.keys holds our key config. Players will eventually be * allowed to can change their key mappings. destruct_player.moves and * destruct_player.keys line up; rather than manually checking left and * right we can just loop through the indexes and set the actions as * needed. */ service_SDL_events(true); for (player_index = 0; player_index < MAX_PLAYERS; player_index++) { for (key_index = 0; key_index < MAX_KEY; key_index++) { for (slot_index = 0; slot_index < MAX_KEY_OPTIONS; slot_index++) { key = destruct_player[player_index].keys.Config[key_index][slot_index]; if (key == SDL_SCANCODE_UNKNOWN) break; if (keysactive[key] == true) { /* The right key was clearly pressed */ destruct_player[player_index].moves.actions[key_index] = true; /* Some keys we want to toggle afterwards */ if (key_index == KEY_CHANGE || key_index == KEY_CYUP || key_index == KEY_CYDN) { keysactive[key] = false; } break; } } } } } static void DE_ProcessInput(void) { int direction; unsigned int player_index; struct destruct_unit_s * curUnit; for (player_index = 0; player_index < MAX_PLAYERS; player_index++) { if (destruct_player[player_index].unitsRemaining <= 0) continue; direction = (player_index == PLAYER_LEFT) ? -1 : 1; curUnit = &(destruct_player[player_index].unit[destruct_player[player_index].unitSelected]); if (systemAngle[curUnit->unitType] == true) /* selected unit may change shot angle */ { if (destruct_player[player_index].moves.actions[MOVE_LEFT] == true) { if (player_index == PLAYER_LEFT) DE_RaiseAngle(curUnit); else DE_LowerAngle(curUnit); } if (destruct_player[player_index].moves.actions[MOVE_RIGHT] == true) { if (player_index == PLAYER_LEFT) DE_LowerAngle(curUnit); else DE_RaiseAngle(curUnit); } } else if (curUnit->unitType == UNIT_HELI) { if (destruct_player[player_index].moves.actions[MOVE_LEFT] == true && curUnit->unitX > 5) { if (JE_stabilityCheck(curUnit->unitX - 5, roundf(curUnit->unitY))) { if (curUnit->lastMove > -5) curUnit->lastMove--; curUnit->unitX--; if (JE_stabilityCheck(curUnit->unitX, roundf(curUnit->unitY))) curUnit->isYInAir = true; } } if (destruct_player[player_index].moves.actions[MOVE_RIGHT] == true && curUnit->unitX < 305) { if (JE_stabilityCheck(curUnit->unitX + 5, roundf(curUnit->unitY))) { if (curUnit->lastMove < 5) curUnit->lastMove++; curUnit->unitX++; if (JE_stabilityCheck(curUnit->unitX, roundf(curUnit->unitY))) curUnit->isYInAir = true; } } } if (curUnit->unitType != UNIT_LASER) { /*increasepower*/ if (destruct_player[player_index].moves.actions[MOVE_UP] == true) { if (curUnit->unitType == UNIT_HELI) { curUnit->isYInAir = true; curUnit->unitYMov -= 0.1f; } else if (curUnit->unitType == UNIT_JUMPER && curUnit->isYInAir == false) { curUnit->unitYMov = -3; curUnit->isYInAir = true; } else { DE_RaisePower(curUnit); } } /*decreasepower*/ if (destruct_player[player_index].moves.actions[MOVE_DOWN] == true) { if (curUnit->unitType == UNIT_HELI && curUnit->isYInAir == true) { curUnit->unitYMov += 0.1f; } else { DE_LowerPower(curUnit); } } } /*up/down weapon. These just cycle until a valid weapon is found */ if (destruct_player[player_index].moves.actions[MOVE_CYUP] == true) DE_CycleWeaponUp(curUnit); if (destruct_player[player_index].moves.actions[MOVE_CYDN] == true) DE_CycleWeaponDown(curUnit); /* Change. Since change would change out curUnit pointer, let's just do it last. * Validity checking is performed at the beginning of the tick. */ if (destruct_player[player_index].moves.actions[MOVE_CHANGE] == true) { destruct_player[player_index].unitSelected++; if (destruct_player[player_index].unitSelected >= config.max_installations) destruct_player[player_index].unitSelected = 0; } /*Newshot*/ if (destruct_player[player_index].shotDelay > 0) destruct_player[player_index].shotDelay--; if (destruct_player[player_index].moves.actions[MOVE_FIRE] == true && destruct_player[player_index].shotDelay == 0) { destruct_player[player_index].shotDelay = shotDelay[curUnit->shotType]; switch (shotDirt[curUnit->shotType]) { case EXPL_NONE: break; case EXPL_MAGNET: DE_RunMagnet(player_index, curUnit); break; case EXPL_DIRT: case EXPL_NORMAL: DE_MakeShot(player_index, curUnit, direction); break; default: assert(false); } } } } static void DE_CycleWeaponUp(struct destruct_unit_s * unit) { do { unit->shotType++; if (unit->shotType > SHOT_LAST) unit->shotType = SHOT_FIRST; } while (weaponSystems[unit->unitType][unit->shotType] == 0); } static void DE_CycleWeaponDown(struct destruct_unit_s * unit) { do { unit->shotType--; if (unit->shotType < SHOT_FIRST) unit->shotType = SHOT_LAST; } while (weaponSystems[unit->unitType][unit->shotType] == 0); } static void DE_MakeShot(enum de_player_t curPlayer, const struct destruct_unit_s * curUnit, int direction) { unsigned int i; unsigned int shotIndex; /* First, find an empty shot struct we can use */ for (i = 0; ; i++) { if (i >= config.max_shots) return; /* no empty slots. Do nothing. */ if (shotRec[i].isAvailable) { shotIndex = i; break; } } /* Helis can't fire when they are on the ground. */ if (curUnit->unitType == UNIT_HELI && curUnit->isYInAir == false) return; /* Play the firing sound */ soundQueue[curPlayer] = shotSound[curUnit->shotType]; /* Create our shot. Some units have differing logic here */ switch (curUnit->unitType) { case UNIT_HELI: shotRec[shotIndex].x = curUnit->unitX + curUnit->lastMove * 2 + 5; shotRec[shotIndex].xmov = 0.02f * curUnit->lastMove * curUnit->lastMove * curUnit->lastMove; /* If we are trying in vain to move up off the screen, act differently.*/ if (destruct_player[curPlayer].moves.actions[MOVE_UP] && curUnit->unitY < 30) { shotRec[shotIndex].y = curUnit->unitY; shotRec[shotIndex].ymov = 0.1f; if (shotRec[shotIndex].xmov < 0) shotRec[shotIndex].xmov += 0.1f; else if (shotRec[shotIndex].xmov > 0) shotRec[shotIndex].xmov -= 0.1f; } else { shotRec[shotIndex].y = curUnit->unitY + 1; shotRec[shotIndex].ymov = 0.5f + curUnit->unitYMov * 0.1f; } break; case UNIT_JUMPER: /* Jumpers are normally only special for the left hand player. Bug? Or feature? */ if (config.jumper_straight[curPlayer]) { /* This is identical to the default case. * I considered letting the switch fall through * but that's more confusing to people who aren't used * to that quirk of switch. */ shotRec[shotIndex].x = curUnit->unitX + 6 - cosf(curUnit->angle) * 10 * direction; shotRec[shotIndex].y = curUnit->unitY - 7 - sinf(curUnit->angle) * 10; shotRec[shotIndex].xmov = -cosf(curUnit->angle) * curUnit->power * direction; shotRec[shotIndex].ymov = -sinf(curUnit->angle) * curUnit->power; } else { /* This is not identical to the default case. */ shotRec[shotIndex].x = curUnit->unitX + 2; shotRec[shotIndex].xmov = -cosf(curUnit->angle) * curUnit->power * direction; if (curUnit->isYInAir == true) { shotRec[shotIndex].ymov = 1; shotRec[shotIndex].y = curUnit->unitY + 2; } else { shotRec[shotIndex].ymov = -2; shotRec[shotIndex].y = curUnit->unitY - 12; } } break; default: shotRec[shotIndex].x = curUnit->unitX + 6 - cosf(curUnit->angle) * 10 * direction; shotRec[shotIndex].y = curUnit->unitY - 7 - sinf(curUnit->angle) * 10; shotRec[shotIndex].xmov = -cosf(curUnit->angle) * curUnit->power * direction; shotRec[shotIndex].ymov = -sinf(curUnit->angle) * curUnit->power; break; } /* Now set/clear out a few last details. */ shotRec[shotIndex].isAvailable = false; shotRec[shotIndex].shottype = curUnit->shotType; //shotRec[shotIndex].shotdur = shotFuse[shotRec[shotIndex].shottype]; shotRec[shotIndex].trailc[0] = 0; shotRec[shotIndex].trailc[1] = 0; shotRec[shotIndex].trailc[2] = 0; shotRec[shotIndex].trailc[3] = 0; } static void DE_RunMagnet(enum de_player_t curPlayer, struct destruct_unit_s * magnet) { unsigned int i; enum de_player_t curEnemy; int direction; struct destruct_unit_s * enemyUnit; curEnemy = (curPlayer == PLAYER_LEFT) ? PLAYER_RIGHT : PLAYER_LEFT; direction = (curPlayer == PLAYER_LEFT) ? -1 : 1; /* Push all shots that are in front of the magnet */ for (i = 0; i < config.max_shots; i++) { if (shotRec[i].isAvailable == false) { if ((curPlayer == PLAYER_LEFT && shotRec[i].x > magnet->unitX) || (curPlayer == PLAYER_RIGHT && shotRec[i].x < magnet->unitX)) { shotRec[i].xmov += magnet->power * 0.1f * -direction; } } } enemyUnit = destruct_player[curEnemy].unit; for (i = 0; i < config.max_installations; i++, enemyUnit++) /* magnets push coptors */ { if (DE_isValidUnit(enemyUnit) && enemyUnit->unitType == UNIT_HELI && enemyUnit->isYInAir == true) { if ((curEnemy == PLAYER_RIGHT && destruct_player[curEnemy].unit[i].unitX + 11 < 318) || (curEnemy == PLAYER_LEFT && destruct_player[curEnemy].unit[i].unitX > 1)) { enemyUnit->unitX -= 2 * direction; } } } magnet->ani_frame = 1; } static void DE_RaiseAngle(struct destruct_unit_s * unit) { unit->angle += 0.01f; if (unit->angle > M_PI_2 - 0.01f) unit->angle = M_PI_2 - 0.01f; } static void DE_LowerAngle(struct destruct_unit_s * unit) { unit->angle -= 0.01f; if (unit->angle < 0) unit->angle = 0; } static void DE_RaisePower(struct destruct_unit_s * unit) { unit->power += 0.05f; if (unit->power > 5) unit->power = 5; } static void DE_LowerPower(struct destruct_unit_s * unit) { unit->power -= 0.05f; if (unit->power < 1) unit->power = 1; } /* DE_isValidUnit * * Returns true if the unit's health is above 0 and false * otherwise. This mainly exists because the 'health' var * serves two roles and that can get confusing. */ static inline bool DE_isValidUnit(struct destruct_unit_s * unit) { return unit->health > 0; } static bool DE_RunTickCheckEndgame(void) { if (destruct_player[PLAYER_LEFT].unitsRemaining == 0) { destruct_player[PLAYER_RIGHT].score += ModeScore[PLAYER_LEFT][world.destructMode]; soundQueue[7] = V_CLEARED_PLATFORM; return true; } if (destruct_player[PLAYER_RIGHT].unitsRemaining == 0) { destruct_player[PLAYER_LEFT].score += ModeScore[PLAYER_RIGHT][world.destructMode]; soundQueue[7] = V_CLEARED_PLATFORM; return true; } return false; } static void DE_RunTickPlaySounds(void) { unsigned int i, tempSampleIndex, tempVolume; for (i = 0; i < COUNTOF(soundQueue); i++) { if (soundQueue[i] != S_NONE) { tempSampleIndex = soundQueue[i]; if (i == 7) tempVolume = fxPlayVol; else tempVolume = fxPlayVol / 2; multiSamplePlay(soundSamples[tempSampleIndex-1], soundSampleCount[tempSampleIndex-1], i, tempVolume); soundQueue[i] = S_NONE; } } } static void JE_pixCool(unsigned int x, unsigned int y, Uint8 c) { JE_pix(VGAScreen, x, y, c); JE_pix(VGAScreen, x - 1, y, c - 2); JE_pix(VGAScreen, x + 1, y, c - 2); JE_pix(VGAScreen, x, y - 1, c - 2); JE_pix(VGAScreen, x, y + 1, c - 2); } opentyrian-2.1.20221123/src/destruct.h000066400000000000000000000016761432005211200172340ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DESTRUCT_H #define DESTRUCT_H #include "opentyr.h" void JE_destructGame(void); #endif /* DESTRUCT_H */ opentyrian-2.1.20221123/src/editship.c000066400000000000000000000043631432005211200171770ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "editship.h" #include "config.h" #include "file.h" #include "opentyr.h" #define SAS (sizeof(JE_ShipsType) - 4) const JE_byte extraCryptKey[10] = { 58, 23, 16, 192, 254, 82, 113, 147, 62, 99 }; JE_boolean extraAvail; JE_ShipsType extraShips; void *extraShapes; JE_word extraShapeSize; void JE_decryptShips(void) { JE_boolean correct = true; JE_ShipsType s2; JE_byte y; for (int x = SAS - 1; x >= 0; x--) { s2[x] = extraShips[x] ^ extraCryptKey[(x + 1) % 10]; if (x > 0) s2[x] ^= extraShips[x - 1]; } /* <= Key Decryption Test (Reversed key) */ y = 0; for (uint x = 0; x < SAS; x++) y += s2[x]; if (extraShips[SAS + 0] != y) correct = false; y = 0; for (uint x = 0; x < SAS; x++) y -= s2[x]; if (extraShips[SAS + 1] != y) correct = false; y = 1; for (uint x = 0; x < SAS; x++) y = y * s2[x] + 1; if (extraShips[SAS + 2] != y) correct = false; y = 0; for (uint x = 0; x < SAS; x++) y ^= s2[x]; if (extraShips[SAS + 3] != y) correct = false; if (!correct) exit(255); memcpy(extraShips, s2, sizeof(extraShips)); } void JE_loadExtraShapes(void) { FILE *f = dir_fopen(get_user_directory(), "newsh$.shp", "rb"); if (f) { extraAvail = true; extraShapeSize = ftell_eof(f) - sizeof(extraShips); extraShapes = malloc(extraShapeSize); fread_die(extraShapes, extraShapeSize, 1, f); fread_die(extraShips, sizeof(extraShips), 1, f); JE_decryptShips(); fclose(f); } } opentyrian-2.1.20221123/src/editship.h000066400000000000000000000022571432005211200172040ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef EDITSHIP_H #define EDITSHIP_H #include "opentyr.h" // TODO: replace with less opaque type typedef JE_byte JE_ShipsType[154]; /* [1..154] */ extern JE_boolean extraAvail; extern JE_ShipsType extraShips; extern void *extraShapes; extern JE_word extraShapeSize; void JE_decryptShips(void); void JE_loadExtraShapes(void); #endif /* EDITSHIP_H */ opentyrian-2.1.20221123/src/episodes.c000066400000000000000000000211771432005211200172030ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "episodes.h" #include "config.h" #include "file.h" #include "lvllib.h" #include "lvlmast.h" #include "opentyr.h" /* MAIN Weapons Data */ JE_WeaponPortType weaponPort; JE_WeaponType weapons[WEAP_NUM + 1]; /* [0..weapnum] */ /* Items */ JE_PowerType powerSys; JE_ShipType ships; JE_OptionType options[OPTION_NUM + 1]; /* [0..optionnum] */ JE_ShieldType shields; JE_SpecialType special; /* Enemy data */ JE_EnemyDatType enemyDat; /* EPISODE variables */ JE_byte initial_episode_num, episodeNum = 0; JE_boolean episodeAvail[EPISODE_MAX]; /* [1..episodemax] */ char episode_file[13], cube_file[13]; JE_longint episode1DataLoc; /* Tells the game whether the level currently loaded is a bonus level. */ JE_boolean bonusLevel; /* Tells if the game jumped back to Episode 1 */ JE_boolean jumpBackToEpisode1; void JE_loadItemDat(void) { FILE *f = NULL; if (episodeNum <= 3) { f = dir_fopen_die(data_dir(), "tyrian.hdt", "rb"); fread_s32_die(&episode1DataLoc, 1, f); fseek(f, episode1DataLoc, SEEK_SET); } else { // episode 4 stores item data in the level file f = dir_fopen_die(data_dir(), levelFile, "rb"); fseek(f, lvlPos[lvlNum-1], SEEK_SET); } JE_word itemNum[7]; /* [1..7] */ fread_u16_die(itemNum, 7, f); for (int i = 0; i < WEAP_NUM + 1; ++i) { fread_u16_die(&weapons[i].drain, 1, f); fread_u8_die( &weapons[i].shotrepeat, 1, f); fread_u8_die( &weapons[i].multi, 1, f); fread_u16_die(&weapons[i].weapani, 1, f); fread_u8_die( &weapons[i].max, 1, f); fread_u8_die( &weapons[i].tx, 1, f); fread_u8_die( &weapons[i].ty, 1, f); fread_u8_die( &weapons[i].aim, 1, f); fread_u8_die( weapons[i].attack, 8, f); fread_u8_die( weapons[i].del, 8, f); fread_s8_die( weapons[i].sx, 8, f); fread_s8_die( weapons[i].sy, 8, f); fread_s8_die( weapons[i].bx, 8, f); fread_s8_die( weapons[i].by, 8, f); fread_u16_die( weapons[i].sg, 8, f); fread_s8_die( &weapons[i].acceleration, 1, f); fread_s8_die( &weapons[i].accelerationx, 1, f); fread_u8_die( &weapons[i].circlesize, 1, f); fread_u8_die( &weapons[i].sound, 1, f); fread_u8_die( &weapons[i].trail, 1, f); fread_u8_die( &weapons[i].shipblastfilter, 1, f); } for (int i = 0; i < PORT_NUM + 1; ++i) { Uint8 nameLen; fread_u8_die( &nameLen, 1, f); fread_die( &weaponPort[i].name, 1, 30, f); weaponPort[i].name[MIN(nameLen, 30)] = '\0'; fread_u8_die( &weaponPort[i].opnum, 1, f); fread_u16_die( weaponPort[i].op[0], 11, f); fread_u16_die( weaponPort[i].op[1], 11, f); fread_u16_die(&weaponPort[i].cost, 1, f); fread_u16_die(&weaponPort[i].itemgraphic, 1, f); fread_u16_die(&weaponPort[i].poweruse, 1, f); } for (int i = 0; i < SPECIAL_NUM + 1; ++i) { Uint8 nameLen; fread_u8_die( &nameLen, 1, f); fread_die( &special[i].name, 1, 30, f); special[i].name[MIN(nameLen, 30)] = '\0'; fread_u16_die(&special[i].itemgraphic, 1, f); fread_u8_die( &special[i].pwr, 1, f); fread_u8_die( &special[i].stype, 1, f); fread_u16_die(&special[i].wpn, 1, f); } for (int i = 0; i < POWER_NUM + 1; ++i) { Uint8 nameLen; fread_u8_die( &nameLen, 1, f); fread_die( &powerSys[i].name, 1, 30, f); powerSys[i].name[MIN(nameLen, 30)] = '\0'; fread_u16_die(&powerSys[i].itemgraphic, 1, f); fread_u8_die( &powerSys[i].power, 1, f); fread_s8_die( &powerSys[i].speed, 1, f); fread_u16_die(&powerSys[i].cost, 1, f); } for (int i = 0; i < SHIP_NUM + 1; ++i) { Uint8 nameLen; fread_u8_die( &nameLen, 1, f); fread_die( &ships[i].name, 1, 30, f); ships[i].name[MIN(nameLen, 30)] = '\0'; fread_u16_die(&ships[i].shipgraphic, 1, f); fread_u16_die(&ships[i].itemgraphic, 1, f); fread_u8_die( &ships[i].ani, 1, f); fread_s8_die( &ships[i].spd, 1, f); fread_u8_die( &ships[i].dmg, 1, f); fread_u16_die(&ships[i].cost, 1, f); fread_u8_die( &ships[i].bigshipgraphic, 1, f); } for (int i = 0; i < OPTION_NUM + 1; ++i) { Uint8 nameLen; fread_u8_die( &nameLen, 1, f); fread_die( &options[i].name, 1, 30, f); options[i].name[MIN(nameLen, 30)] = '\0'; fread_u8_die( &options[i].pwr, 1, f); fread_u16_die( &options[i].itemgraphic, 1, f); fread_u16_die( &options[i].cost, 1, f); fread_u8_die( &options[i].tr, 1, f); fread_u8_die( &options[i].option, 1, f); fread_s8_die( &options[i].opspd, 1, f); fread_u8_die( &options[i].ani, 1, f); fread_u16_die( options[i].gr, 20, f); fread_u8_die( &options[i].wport, 1, f); fread_u16_die( &options[i].wpnum, 1, f); fread_u8_die( &options[i].ammo, 1, f); fread_bool_die(&options[i].stop, f); fread_u8_die( &options[i].icongr, 1, f); } for (int i = 0; i < SHIELD_NUM + 1; ++i) { Uint8 nameLen; fread_u8_die( &nameLen, 1, f); fread_die( &shields[i].name, 1, 30, f); shields[i].name[MIN(nameLen, 30)] = '\0'; fread_u8_die( &shields[i].tpwr, 1, f); fread_u8_die( &shields[i].mpwr, 1, f); fread_u16_die(&shields[i].itemgraphic, 1, f); fread_u16_die(&shields[i].cost, 1, f); } for (int i = 0; i < ENEMY_NUM + 1; ++i) { fread_u8_die( &enemyDat[i].ani, 1, f); fread_u8_die( enemyDat[i].tur, 3, f); fread_u8_die( enemyDat[i].freq, 3, f); fread_s8_die( &enemyDat[i].xmove, 1, f); fread_s8_die( &enemyDat[i].ymove, 1, f); fread_s8_die( &enemyDat[i].xaccel, 1, f); fread_s8_die( &enemyDat[i].yaccel, 1, f); fread_s8_die( &enemyDat[i].xcaccel, 1, f); fread_s8_die( &enemyDat[i].ycaccel, 1, f); fread_s16_die(&enemyDat[i].startx, 1, f); fread_s16_die(&enemyDat[i].starty, 1, f); fread_s8_die( &enemyDat[i].startxc, 1, f); fread_s8_die( &enemyDat[i].startyc, 1, f); fread_u8_die( &enemyDat[i].armor, 1, f); fread_u8_die( &enemyDat[i].esize, 1, f); fread_u16_die( enemyDat[i].egraphic, 20, f); fread_u8_die( &enemyDat[i].explosiontype, 1, f); fread_u8_die( &enemyDat[i].animate, 1, f); fread_u8_die( &enemyDat[i].shapebank, 1, f); fread_s8_die( &enemyDat[i].xrev, 1, f); fread_s8_die( &enemyDat[i].yrev, 1, f); fread_u16_die(&enemyDat[i].dgr, 1, f); fread_s8_die( &enemyDat[i].dlevel, 1, f); fread_s8_die( &enemyDat[i].dani, 1, f); fread_u8_die( &enemyDat[i].elaunchfreq, 1, f); fread_u16_die(&enemyDat[i].elaunchtype, 1, f); fread_s16_die(&enemyDat[i].value, 1, f); fread_u16_die(&enemyDat[i].eenemydie, 1, f); } fclose(f); } void JE_initEpisode(JE_byte newEpisode) { if (newEpisode == episodeNum) return; episodeNum = newEpisode; snprintf(levelFile, sizeof(levelFile), "tyrian%d.lvl", episodeNum); snprintf(cube_file, sizeof(cube_file), "cubetxt%d.dat", episodeNum); snprintf(episode_file, sizeof(episode_file), "levels%d.dat", episodeNum); JE_analyzeLevel(); JE_loadItemDat(); } void JE_scanForEpisodes(void) { for (int i = 0; i < EPISODE_MAX; ++i) { char ep_file[20]; snprintf(ep_file, sizeof(ep_file), "tyrian%d.lvl", i + 1); episodeAvail[i] = dir_file_exists(data_dir(), ep_file); } } unsigned int JE_findNextEpisode(void) { unsigned int newEpisode = episodeNum; jumpBackToEpisode1 = false; while (true) { newEpisode++; if (newEpisode > EPISODE_MAX) { newEpisode = 1; jumpBackToEpisode1 = true; gameHasRepeated = true; } if (episodeAvail[newEpisode-1] || newEpisode == episodeNum) { break; } } return newEpisode; } opentyrian-2.1.20221123/src/episodes.h000066400000000000000000000104441432005211200172030ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef EPISODES_H #define EPISODES_H #include "opentyr.h" #include "lvlmast.h" /* Episodes and general data */ #define FIRST_LEVEL 1 #define EPISODE_MAX 5 #define EPISODE_AVAILABLE 4 typedef struct { JE_word drain; JE_byte shotrepeat; JE_byte multi; JE_word weapani; JE_byte max; JE_byte tx, ty, aim; JE_byte attack[8], del[8]; /* [1..8] */ JE_shortint sx[8], sy[8]; /* [1..8] */ JE_shortint bx[8], by[8]; /* [1..8] */ JE_word sg[8]; /* [1..8] */ JE_shortint acceleration, accelerationx; JE_byte circlesize; JE_byte sound; JE_byte trail; JE_byte shipblastfilter; } JE_WeaponType; typedef struct { char name[31]; /* string [30] */ JE_byte opnum; JE_word op[2][11]; /* [1..2, 1..11] */ JE_word cost; JE_word itemgraphic; JE_word poweruse; } JE_WeaponPortType[PORT_NUM + 1]; /* [0..portnum] */ typedef struct { char name[31]; /* string [30] */ JE_word itemgraphic; JE_byte power; JE_shortint speed; JE_word cost; } JE_PowerType[POWER_NUM + 1]; /* [0..powernum] */ typedef struct { char name[31]; /* string [30] */ JE_word itemgraphic; JE_byte pwr; JE_byte stype; JE_word wpn; } JE_SpecialType[SPECIAL_NUM + 1]; /* [0..specialnum] */ typedef struct { char name[31]; /* string [30] */ JE_byte pwr; JE_word itemgraphic; JE_word cost; JE_byte tr, option; JE_shortint opspd; JE_byte ani; JE_word gr[20]; /* [1..20] */ JE_byte wport; JE_word wpnum; JE_byte ammo; JE_boolean stop; JE_byte icongr; } JE_OptionType; typedef struct { char name[31]; /* string [30] */ JE_byte tpwr; JE_byte mpwr; JE_word itemgraphic; JE_word cost; } JE_ShieldType[SHIELD_NUM + 1]; /* [0..shieldnum] */ typedef struct { char name[31]; /* string [30] */ JE_word shipgraphic; JE_word itemgraphic; JE_byte ani; JE_shortint spd; JE_byte dmg; JE_word cost; JE_byte bigshipgraphic; } JE_ShipType[SHIP_NUM + 1]; /* [0..shipnum] */ /* EnemyData */ typedef struct { JE_byte ani; JE_byte tur[3]; /* [1..3] */ JE_byte freq[3]; /* [1..3] */ JE_shortint xmove; JE_shortint ymove; JE_shortint xaccel; JE_shortint yaccel; JE_shortint xcaccel; JE_shortint ycaccel; JE_integer startx; JE_integer starty; JE_shortint startxc; JE_shortint startyc; JE_byte armor; JE_byte esize; JE_word egraphic[20]; /* [1..20] */ JE_byte explosiontype; JE_byte animate; /* 0:Not Yet 1:Always 2:When Firing Only */ JE_byte shapebank; /* See LEVELMAK.DOC */ JE_shortint xrev, yrev; JE_word dgr; JE_shortint dlevel; JE_shortint dani; JE_byte elaunchfreq; JE_word elaunchtype; JE_integer value; JE_word eenemydie; } JE_EnemyDatType[ENEMY_NUM + 1]; /* [0..enemynum] */ extern JE_WeaponPortType weaponPort; extern JE_WeaponType weapons[WEAP_NUM + 1]; /* [0..weapnum] */ extern JE_PowerType powerSys; extern JE_ShipType ships; extern JE_OptionType options[OPTION_NUM + 1]; /* [0..optionnum] */ extern JE_ShieldType shields; extern JE_SpecialType special; extern JE_EnemyDatType enemyDat; extern JE_byte initial_episode_num, episodeNum; extern JE_boolean episodeAvail[EPISODE_MAX]; extern char episode_file[13], cube_file[13]; extern JE_longint episode1DataLoc; extern JE_boolean bonusLevel; extern JE_boolean jumpBackToEpisode1; void JE_loadItemDat(void); void JE_initEpisode(JE_byte newEpisode); unsigned int JE_findNextEpisode(void); void JE_scanForEpisodes(void); #endif /* EPISODES_H */ opentyrian-2.1.20221123/src/file.c000066400000000000000000000065251432005211200163070ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "file.h" #include "opentyr.h" #include "varz.h" #include "SDL.h" #include #include #include #include const char *custom_data_dir = NULL; // finds the Tyrian data directory const char *data_dir(void) { const char *const dirs[] = { custom_data_dir, TYRIAN_DIR, "data", ".", }; static const char *dir = NULL; if (dir != NULL) return dir; for (uint i = 0; i < COUNTOF(dirs); ++i) { if (dirs[i] == NULL) continue; FILE *f = dir_fopen(dirs[i], "tyrian1.lvl", "rb"); if (f) { fclose(f); dir = dirs[i]; break; } } if (dir == NULL) // data not found dir = ""; return dir; } // prepend directory and fopen FILE *dir_fopen(const char *dir, const char *file, const char *mode) { char *path = malloc(strlen(dir) + 1 + strlen(file) + 1); sprintf(path, "%s/%s", dir, file); FILE *f = fopen(path, mode); free(path); return f; } // warn when dir_fopen fails FILE *dir_fopen_warn(const char *dir, const char *file, const char *mode) { FILE *f = dir_fopen(dir, file, mode); if (f == NULL) fprintf(stderr, "warning: failed to open '%s': %s\n", file, strerror(errno)); return f; } // die when dir_fopen fails FILE *dir_fopen_die(const char *dir, const char *file, const char *mode) { FILE *f = dir_fopen(dir, file, mode); if (f == NULL) { fprintf(stderr, "error: failed to open '%s': %s\n", file, strerror(errno)); fprintf(stderr, "error: One or more of the required Tyrian " TYRIAN_VERSION " data files could not be found.\n" " Please read the README file.\n"); JE_tyrianHalt(1); } return f; } // check if file can be opened for reading bool dir_file_exists(const char *dir, const char *file) { FILE *f = dir_fopen(dir, file, "rb"); if (f != NULL) fclose(f); return (f != NULL); } // returns end-of-file position long ftell_eof(FILE *f) { long pos = ftell(f); fseek(f, 0, SEEK_END); long size = ftell(f); fseek(f, pos, SEEK_SET); return size; } void fread_die(void *buffer, size_t size, size_t count, FILE *stream) { size_t result = fread(buffer, size, count, stream); if (result != count) { fprintf(stderr, "error: An unexpected problem occurred while reading from a file.\n"); SDL_Quit(); exit(EXIT_FAILURE); } } void fwrite_die(const void *buffer, size_t size, size_t count, FILE *stream) { size_t result = fwrite(buffer, size, count, stream); if (result != count) { fprintf(stderr, "error: An unexpected problem occurred while writing to a file.\n"); SDL_Quit(); exit(EXIT_FAILURE); } } opentyrian-2.1.20221123/src/file.h000066400000000000000000000121051432005211200163030ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FILE_H #define FILE_H #include "SDL_endian.h" #include #include extern const char *custom_data_dir; const char *data_dir(void); FILE *dir_fopen(const char *dir, const char *file, const char *mode); FILE *dir_fopen_warn(const char *dir, const char *file, const char *mode); FILE *dir_fopen_die(const char *dir, const char *file, const char *mode); bool dir_file_exists(const char *dir, const char *file); long ftell_eof(FILE *f); void fread_die(void *buffer, size_t size, size_t count, FILE *stream); // 8-bit fread that dies if read fails static inline void fread_bool_die(bool *buffer, FILE *stream) { Uint8 temp; fread_die(&temp, sizeof(Uint8), 1, stream); *buffer = temp != 0; } // 8-bit fread static inline size_t fread_u8(Uint8 *buffer, size_t count, FILE *stream) { return fread(buffer, sizeof(Uint8), count, stream); } // 8-bit fread that dies if read fails static inline void fread_u8_die(Uint8 *buffer, size_t count, FILE *stream) { fread_die(buffer, sizeof(Uint8), count, stream); } // 8-bit fread that dies if read fails static inline void fread_s8_die(Sint8 *buffer, size_t count, FILE *stream) { fread_die(buffer, sizeof(Sint8), count, stream); } // 16-bit endian-swapping fread that dies if read fails static inline void fread_u16_die(Uint16 *buffer, size_t count, FILE *stream) { fread_die(buffer, sizeof(Uint16), count, stream); #if SDL_BYTEORDER == SDL_BIG_ENDIAN for (size_t i = 0; i < count; ++i) buffer[i] = SDL_Swap16(buffer[i]); #endif } // 16-bit endian-swapping fread that dies if read fails static inline void fread_s16_die(Sint16 *buffer, size_t count, FILE *stream) { fread_die(buffer, sizeof(Sint16), count, stream); #if SDL_BYTEORDER == SDL_BIG_ENDIAN for (size_t i = 0; i < count; ++i) buffer[i] = SDL_Swap16(buffer[i]); #endif } // 32-bit endian-swapping fread that dies if read fails static inline void fread_u32_die(Uint32 *buffer, size_t count, FILE *stream) { fread_die(buffer, sizeof(Uint32), count, stream); #if SDL_BYTEORDER == SDL_BIG_ENDIAN for (size_t i = 0; i < count; ++i) buffer[i] = SDL_Swap32(buffer[i]); #endif } // 32-bit endian-swapping fread that dies if read fails static inline void fread_s32_die(Sint32 *buffer, size_t count, FILE *stream) { fread_die(buffer, sizeof(Sint32), count, stream); #if SDL_BYTEORDER == SDL_BIG_ENDIAN for (size_t i = 0; i < count; ++i) buffer[i] = SDL_Swap32(buffer[i]); #endif } void fwrite_die(const void *buffer, size_t size, size_t count, FILE *stream); // 8-bit fwrite that dies if write fails static inline void fwrite_bool_die(const bool *buffer, FILE *stream) { Uint8 temp = *buffer ? 1 : 0; fwrite_die(&temp, sizeof(Uint8), 1, stream); } // 8-bit fwrite static inline size_t fwrite_u8(const Uint8 *buffer, size_t count, FILE *stream) { return fwrite(buffer, sizeof(Uint8), count, stream); } // 8-bit fwrite that dies if write fails static inline void fwrite_u8_die(const Uint8 *buffer, size_t count, FILE *stream) { fwrite_die(buffer, sizeof(Uint8), count, stream); } // 8-bit fwrite that dies if write fails static inline void fwrite_s8_die(const Sint8 *buffer, size_t count, FILE *stream) { fwrite_die(buffer, sizeof(Sint8), count, stream); } // 16-bit endian-swapping fwrite that dies if write fails static inline void fwrite_u16_die(const Uint16 *buffer, FILE *stream) { #if SDL_BYTEORDER == SDL_BIG_ENDIAN Uint16 temp = SDL_Swap16(*buffer); buffer = &temp; #endif fwrite_die(buffer, sizeof(Uint16), 1, stream); } // 16-bit endian-swapping fwrite that dies if write fails static inline void fwrite_s16_die(const Sint16 *buffer, FILE *stream) { #if SDL_BYTEORDER == SDL_BIG_ENDIAN Sint16 temp = SDL_Swap16(*buffer); buffer = &temp; #endif fwrite_die(buffer, sizeof(Sint16), 1, stream); } // 32-bit endian-swapping fwrite that dies if write fails static inline void fwrite_u32_die(const Uint32 *buffer, FILE *stream) { #if SDL_BYTEORDER == SDL_BIG_ENDIAN Uint32 temp = SDL_Swap32(*buffer); buffer = &temp; #endif fwrite_die(buffer, sizeof(Uint32), 1, stream); } // 32-bit endian-swapping fwrite that dies if write fails static inline void fwrite_s32_die(const Sint32 *buffer, FILE *stream) { #if SDL_BYTEORDER == SDL_BIG_ENDIAN Sint32 temp = SDL_Swap32(*buffer); buffer = &temp; #endif fwrite_die(buffer, sizeof(Sint32), 1, stream); } #endif // FILE_H opentyrian-2.1.20221123/src/font.c000066400000000000000000000210171432005211200163270ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "font.h" #include "fonthand.h" #include "sprite.h" /** * \file font.c * \brief Text drawing routines. */ /** * \brief Draws text in a color specified by hue and value and with a drop * shadow. * * A '~' in the text is not drawn but instead toggles highlighting which * increases \c value by 4. * * \li like JE_dString() if (black == false && shadow_dist == 2 && hue == 15) * \li like JE_textShade() with PART_SHADE if (black == true && shadow_dist == 1) * \li like JE_outTextAndDarken() if (black == false && shadow_dist == 1) * \li like JE_outTextAdjust() with shadow if (black == false && shadow_dist == 2) * * @param surface destination surface * @param x initial x-position in pixels; which direction(s) the text is drawn * from this position depends on the alignment * @param y initial upper y-position in pixels * @param text text to be drawn * @param font style/size of text * @param alignment left_aligned, centered, or right_aligned * @param hue hue component of text color * @param value value component of text color * @param black if true the shadow is drawn as solid black, if false the shadow * is drawn by darkening the pixels of the destination surface * @param shadow_dist distance in pixels that the shadow will be drawn away from * the text. (This is added to both the x and y positions, so a value of * 1 causes the shadow to be drawn 1 pixel right and 1 pixel lower than * the text.) */ void draw_font_hv_shadow(SDL_Surface *surface, int x, int y, const char *text, Font font, FontAlignment alignment, Uint8 hue, Sint8 value, bool black, int shadow_dist) { draw_font_dark(surface, x + shadow_dist, y + shadow_dist, text, font, alignment, black); draw_font_hv(surface, x, y, text, font, alignment, hue, value); } /** * \brief Draws text in a color specified by hue and value and with a * surrounding shadow. * * A '~' in the text is not drawn but instead toggles highlighting which * increases \c value by 4. * * \li like JE_textShade() with FULL_SHADE if (black == true && shadow_dist == 1) * * @param surface destination surface * @param x initial x-position in pixels; which direction(s) the text is drawn * from this position depends on the alignment * @param y initial upper y-position in pixels * @param text text to be drawn * @param font style/size of text * @param alignment left_aligned, centered, or right_aligned * @param hue hue component of text color * @param value value component of text color * @param black if true the shadow is drawn as solid black, if false the shadow * is drawn by darkening the pixels of the destination surface * @param shadow_dist distance in pixels that the shadows will be drawn away * from the text. (This distance is separately added to and subtracted * from the x position and y position, resulting in four shadows -- one * in each cardinal direction. If this shadow distance is small enough, * this produces a shadow that outlines the text.) */ void draw_font_hv_full_shadow(SDL_Surface *surface, int x, int y, const char *text, Font font, FontAlignment alignment, Uint8 hue, Sint8 value, bool black, int shadow_dist) { draw_font_dark(surface, x, y - shadow_dist, text, font, alignment, black); draw_font_dark(surface, x + shadow_dist, y, text, font, alignment, black); draw_font_dark(surface, x, y + shadow_dist, text, font, alignment, black); draw_font_dark(surface, x - shadow_dist, y, text, font, alignment, black); draw_font_hv(surface, x, y, text, font, alignment, hue, value); } /** * \brief Draws text in a color specified by hue and value. * * A '~' in the text is not drawn but instead toggles highlighting which * increases \c value by 4. * * \li like JE_outText() with (brightness >= 0) * \li like JE_outTextAdjust() without shadow * * @param surface destination surface * @param x initial x-position in pixels; which direction(s) the text is drawn * from this position depends on the alignment * @param y initial upper y-position in pixels * @param text text to be drawn * @param font style/size of text * @param alignment left_aligned, centered, or right_aligned * @param hue hue component of text color * @param value value component of text color */ void draw_font_hv(SDL_Surface *surface, int x, int y, const char *text, Font font, FontAlignment alignment, Uint8 hue, Sint8 value) { switch (alignment) { case left_aligned: break; case centered: x -= JE_textWidth(text, font) / 2; break; case right_aligned: x -= JE_textWidth(text, font); break; } bool highlight = false; for (; *text != '\0'; ++text) { int sprite_id = font_ascii[(unsigned char)*text]; switch (*text) { case ' ': x += 6; break; case '~': highlight = !highlight; if (highlight) value += 4; else value -= 4; break; default: if (sprite_id != -1 && sprite_exists(font, sprite_id)) { blit_sprite_hv(surface, x, y, font, sprite_id, hue, value); x += sprite(font, sprite_id)->width + 1; } break; } } } /** * \brief Draws blended text in a color specified by hue and value. * * Corresponds to blit_sprite_hv_blend() * * \li like JE_outTextModify() * * @param surface destination surface * @param x initial x-position in pixels; which direction(s) the text is drawn * from this position depends on the alignment * @param y initial upper y-position in pixels * @param text text to be drawn * @param font style/size of text * @param alignment left_aligned, centered, or right_aligned * @param hue hue component of text color * @param value value component of text color */ void draw_font_hv_blend(SDL_Surface *surface, int x, int y, const char *text, Font font, FontAlignment alignment, Uint8 hue, Sint8 value) { switch (alignment) { case left_aligned: break; case centered: x -= JE_textWidth(text, font) / 2; break; case right_aligned: x -= JE_textWidth(text, font); break; } for (; *text != '\0'; ++text) { int sprite_id = font_ascii[(unsigned char)*text]; switch (*text) { case ' ': x += 6; break; case '~': break; default: if (sprite_id != -1 && sprite_exists(font, sprite_id)) { blit_sprite_hv_blend(surface, x, y, font, sprite_id, hue, value); x += sprite(font, sprite_id)->width + 1; } break; } } } /** * \brief Draws darkened text. * * Corresponds to blit_sprite_dark() * * \li like JE_outText() with (brightness < 0) if (black == true) * * @param surface destination surface * @param x initial x-position in pixels; which direction(s) the text is drawn * from this position depends on the alignment * @param y initial upper y-position in pixels * @param text text to be drawn * @param font style/size of text * @param alignment left_aligned, centered, or right_aligned * @param black if true text is drawn as solid black, if false text is drawn by * darkening the pixels of the destination surface */ void draw_font_dark(SDL_Surface *surface, int x, int y, const char *text, Font font, FontAlignment alignment, bool black) { switch (alignment) { case left_aligned: break; case centered: x -= JE_textWidth(text, font) / 2; break; case right_aligned: x -= JE_textWidth(text, font); break; } for (; *text != '\0'; ++text) { int sprite_id = font_ascii[(unsigned char)*text]; switch (*text) { case ' ': x += 6; break; case '~': break; default: if (sprite_id != -1 && sprite_exists(font, sprite_id)) { blit_sprite_dark(surface, x, y, font, sprite_id, black); x += sprite(font, sprite_id)->width + 1; } break; } } } opentyrian-2.1.20221123/src/font.h000066400000000000000000000032561432005211200163410ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FONT_H #define FONT_H #include "SDL.h" #include typedef enum { large_font = 0, normal_font = 1, small_font = 2 } Font; typedef enum { left_aligned, centered, right_aligned } FontAlignment; void draw_font_hv_shadow(SDL_Surface *, int x, int y, const char *text, Font, FontAlignment, Uint8 hue, Sint8 value, bool black, int shadow_dist); void draw_font_hv_full_shadow(SDL_Surface *, int x, int y, const char *text, Font, FontAlignment, Uint8 hue, Sint8 value, bool black, int shadow_dist); void draw_font_hv(SDL_Surface *, int x, int y, const char *text, Font, FontAlignment, Uint8 hue, Sint8 value); void draw_font_hv_blend(SDL_Surface *, int x, int y, const char *text, Font, FontAlignment, Uint8 hue, Sint8 value); void draw_font_dark(SDL_Surface *, int x, int y, const char *text, Font, FontAlignment, bool black); #endif // FONT_H opentyrian-2.1.20221123/src/fonthand.c000066400000000000000000000207421432005211200171660ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "fonthand.h" #include "network.h" #include "nortsong.h" #include "nortvars.h" #include "opentyr.h" #include "params.h" #include "sprite.h" #include "vga256d.h" #include "video.h" const int font_ascii[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 33, 60, 61, 62, -1, 32, 64, 65, 63, 84, 29, 83, 28, 80, // !"#$%&'()*+,-./ 79, 70, 71, 72, 73, 74, 75, 76, 77, 78, 31, 30, -1, 85, -1, 27, // 0123456789:;<=>? -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // @ABCDEFGHIJKLMNO 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 68, 82, 69, -1, -1, // PQRSTUVWXYZ[\]^_ -1, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, // `abcdefghijklmno 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 66, 81, 67, -1, -1, // pqrstuvwxyz{|}~⌂ 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, -1, -1, -1, -1, -1, -1, -1, // áíóúñѪº¿ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; /* shape constants included in newshape.h */ JE_byte textGlowFont, textGlowBrightness = 6; JE_boolean levelWarningDisplay; JE_byte levelWarningLines; char levelWarningText[10][61]; /* [1..10] of string [60] */ JE_boolean warningRed; JE_byte warningSoundDelay; JE_word armorShipDelay; JE_byte warningCol; JE_shortint warningColChange; void JE_dString(SDL_Surface * screen, int x, int y, const char *s, unsigned int font) { const int defaultBrightness = -3; int bright = 0; for (int i = 0; s[i] != '\0'; ++i) { int sprite_id = font_ascii[(unsigned char)s[i]]; switch (s[i]) { case ' ': x += 6; break; case '~': bright = (bright == 0) ? 2 : 0; break; default: if (sprite_id != -1) { blit_sprite_dark(screen, x + 2, y + 2, font, sprite_id, false); blit_sprite_hv_unsafe(screen, x, y, font, sprite_id, 0xf, defaultBrightness + bright); x += sprite(font, sprite_id)->width + 1; } break; } } } int JE_fontCenter(const char *s, unsigned int font) { return 160 - (JE_textWidth(s, font) / 2); } int JE_textWidth(const char *s, unsigned int font) { int x = 0; for (int i = 0; s[i] != '\0'; ++i) { int sprite_id = font_ascii[(unsigned char)s[i]]; if (s[i] == ' ') x += 6; else if (sprite_id != -1) x += sprite(font, sprite_id)->width + 1; } return x; } void JE_textShade(SDL_Surface * screen, int x, int y, const char *s, unsigned int colorbank, int brightness, unsigned int shadetype) { switch (shadetype) { case PART_SHADE: JE_outText(screen, x+1, y+1, s, 0, -1); JE_outText(screen, x, y, s, colorbank, brightness); break; case FULL_SHADE: JE_outText(screen, x-1, y, s, 0, -1); JE_outText(screen, x+1, y, s, 0, -1); JE_outText(screen, x, y-1, s, 0, -1); JE_outText(screen, x, y+1, s, 0, -1); JE_outText(screen, x, y, s, colorbank, brightness); break; case DARKEN: JE_outTextAndDarken(screen, x+1, y+1, s, colorbank, brightness, TINY_FONT); break; case TRICK: JE_outTextModify(screen, x, y, s, colorbank, brightness, TINY_FONT); break; } } void JE_outText(SDL_Surface * screen, int x, int y, const char *s, unsigned int colorbank, int brightness) { int bright = 0; for (int i = 0; s[i] != '\0'; ++i) { int sprite_id = font_ascii[(unsigned char)s[i]]; switch (s[i]) { case ' ': x += 6; break; case '~': bright = (bright == 0) ? 4 : 0; break; default: if (sprite_id != -1 && sprite_exists(TINY_FONT, sprite_id)) { if (brightness >= 0) blit_sprite_hv_unsafe(screen, x, y, TINY_FONT, sprite_id, colorbank, brightness + bright); else blit_sprite_dark(screen, x, y, TINY_FONT, sprite_id, true); x += sprite(TINY_FONT, sprite_id)->width + 1; } break; } } } void JE_outTextModify(SDL_Surface * screen, int x, int y, const char *s, unsigned int filter, unsigned int brightness, unsigned int font) { for (int i = 0; s[i] != '\0'; ++i) { int sprite_id = font_ascii[(unsigned char)s[i]]; if (s[i] == ' ') { x += 6; } else if (sprite_id != -1) { blit_sprite_hv_blend(screen, x, y, font, sprite_id, filter, brightness); x += sprite(font, sprite_id)->width + 1; } } } void JE_outTextAdjust(SDL_Surface * screen, int x, int y, const char *s, unsigned int filter, int brightness, unsigned int font, JE_boolean shadow) { int bright = 0; for (int i = 0; s[i] != '\0'; ++i) { int sprite_id = font_ascii[(unsigned char)s[i]]; switch (s[i]) { case ' ': x += 6; break; case '~': bright = (bright == 0) ? 4 : 0; break; default: if (sprite_id != -1 && sprite_exists(TINY_FONT, sprite_id)) { if (shadow) blit_sprite_dark(screen, x + 2, y + 2, font, sprite_id, false); blit_sprite_hv(screen, x, y, font, sprite_id, filter, brightness + bright); x += sprite(font, sprite_id)->width + 1; } break; } } } void JE_outTextAndDarken(SDL_Surface * screen, int x, int y, const char *s, unsigned int colorbank, unsigned int brightness, unsigned int font) { int bright = 0; for (int i = 0; s[i] != '\0'; ++i) { int sprite_id = font_ascii[(unsigned char)s[i]]; switch (s[i]) { case ' ': x += 6; break; case '~': bright = (bright == 0) ? 4 : 0; break; default: if (sprite_id != -1 && sprite_exists(TINY_FONT, sprite_id)) { blit_sprite_dark(screen, x + 1, y + 1, font, sprite_id, false); blit_sprite_hv_unsafe(screen, x, y, font, sprite_id, colorbank, brightness + bright); x += sprite(font, sprite_id)->width + 1; } break; } } } void JE_updateWarning(SDL_Surface * screen) { if (getDelayTicks2() == 0) { /*Update Color Bars*/ warningCol += warningColChange; if (warningCol > 14 * 16 + 10 || warningCol < 14 * 16 + 4) { warningColChange = -warningColChange; } fill_rectangle_xy(screen, 0, 0, 319, 5, warningCol); fill_rectangle_xy(screen, 0, 194, 319, 199, warningCol); JE_showVGA(); setDelay2(6); if (warningSoundDelay > 0) { warningSoundDelay--; } else { warningSoundDelay = 14; JE_playSampleNum(S_WARNING); } } } void JE_outTextGlow(SDL_Surface * screen, int x, int y, const char *s) { JE_integer z; JE_byte c = 15; if (warningRed) { c = 7; } JE_outTextAdjust(screen, x - 1, y, s, 0, -12, textGlowFont, false); JE_outTextAdjust(screen, x, y - 1, s, 0, -12, textGlowFont, false); JE_outTextAdjust(screen, x + 1, y, s, 0, -12, textGlowFont, false); JE_outTextAdjust(screen, x, y + 1, s, 0, -12, textGlowFont, false); if (frameCountMax > 0) for (z = 1; z <= 12; z++) { setDelay(frameCountMax); JE_outTextAdjust(screen, x, y, s, c, z - 10, textGlowFont, false); if (JE_anyButton()) { frameCountMax = 0; } NETWORK_KEEP_ALIVE(); JE_showVGA(); wait_delay(); } for (z = (frameCountMax == 0) ? 6 : 12; z >= textGlowBrightness; z--) { setDelay(frameCountMax); JE_outTextAdjust(screen, x, y, s, c, z - 10, textGlowFont, false); if (JE_anyButton()) { frameCountMax = 0; } NETWORK_KEEP_ALIVE(); JE_showVGA(); wait_delay(); } textGlowBrightness = 6; } opentyrian-2.1.20221123/src/fonthand.h000066400000000000000000000045241432005211200171730ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FONTHAND_H #define FONTHAND_H #include "opentyr.h" #include "SDL.h" #define PART_SHADE 0 #define FULL_SHADE 1 #define DARKEN 2 #define TRICK 3 #define NO_SHADE 255 extern const int font_ascii[256]; extern JE_byte textGlowFont, textGlowBrightness; extern JE_boolean levelWarningDisplay; extern JE_byte levelWarningLines; extern char levelWarningText[10][61]; extern JE_boolean warningRed; extern JE_byte warningSoundDelay; extern JE_word armorShipDelay; extern JE_byte warningCol; extern JE_shortint warningColChange; void JE_dString(SDL_Surface * screen, int x, int y, const char *s, unsigned int font); int JE_fontCenter(const char *s, unsigned int font); int JE_textWidth(const char *s, unsigned int font); void JE_textShade(SDL_Surface * screen, int x, int y, const char *s, unsigned int colorbank, int brightness, unsigned int shadetype); void JE_outText(SDL_Surface * screen, int x, int y, const char *s, unsigned int colorbank, int brightness); void JE_outTextModify(SDL_Surface * screen, int x, int y, const char *s, unsigned int filter, unsigned int brightness, unsigned int font); void JE_outTextAdjust(SDL_Surface * screen, int x, int y, const char *s, unsigned int filter, int brightness, unsigned int font, bool shadow); void JE_outTextAndDarken(SDL_Surface * screen, int x, int y, const char *s, unsigned int colorbank, unsigned int brightness, unsigned int font); void JE_updateWarning(SDL_Surface * screen); void JE_outTextGlow(SDL_Surface * screen, int x, int y, const char *s); #endif /* FONTHAND_H */ opentyrian-2.1.20221123/src/game_menu.c000066400000000000000000002326071432005211200173270ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "game_menu.h" #include "backgrnd.h" #include "config.h" #include "file.h" #include "fonthand.h" #include "joystick.h" #include "keyboard.h" #include "loudness.h" #include "mainint.h" #include "mouse.h" #include "musmast.h" #include "network.h" #include "nortsong.h" #include "nortvars.h" #include "params.h" #include "pcxmast.h" #include "picload.h" #include "player.h" #include "shots.h" #include "sprite.h" #include "tyrian2.h" #include "varz.h" #include "vga256d.h" #include "video.h" #include enum { MENU_FULL_GAME = 0, MENU_UPGRADES = 1, MENU_OPTIONS = 2, MENU_PLAY_NEXT_LEVEL = 3, MENU_UPGRADE_SUB = 4, MENU_KEYBOARD_CONFIG = 5, MENU_LOAD_SAVE = 6, MENU_DATA_CUBES = 7, MENU_DATA_CUBE_SUB = 8, MENU_2_PLAYER_ARCADE = 9, MENU_1_PLAYER_ARCADE = 10, // Also networked games. MENU_LIMITED_OPTIONS = 11, // Hides save/load menus. MENU_JOYSTICK_CONFIG = 12, MENU_SUPER_TYRIAN = 13, }; /*** Structs ***/ struct cube_struct { char title[81]; char header[13]; int face_sprite; char text[90][36]; int last_line; }; /*** Globals ***/ static int joystick_config = 0; // which joystick is being configured in menu static JE_word yLoc; static JE_shortint yChg; static int newPal, curPal, oldPal; static JE_boolean quikSave; static JE_byte oldMenu; static JE_boolean backFromHelp; static JE_integer lastDirection; static JE_boolean firstMenu9, paletteChanged; static JE_MenuChoiceType menuChoices; static JE_integer col, colC; static JE_byte lastCurSel; static JE_integer curMenu; static JE_byte curSel[MENU_MAX]; /* [1..maxmenu] */ static JE_byte curItemType, curItem, cursor; static JE_boolean leftPower, rightPower, rightPowerAfford; static JE_byte currentCube; static JE_boolean keyboardUsed; static JE_byte planetAni, planetAniWait; static JE_byte currentDotNum, currentDotWait; static JE_real navX, navY, newNavX, newNavY; static JE_integer tempNavX, tempNavY; static JE_byte planetDots[5]; /* [1..5] */ static JE_integer planetDotX[5][10], planetDotY[5][10]; /* [1..5, 1..10] */ static PlayerItems old_items[2]; // TODO: should not be global if possible static struct cube_struct cube[4]; static const JE_MenuChoiceType menuChoicesDefault = { 7, 9, 8, 0, 0, 11, (SAVE_FILES_NUM / 2) + 2, 0, 0, 6, 4, 6, 7, 5 }; static const JE_byte menuEsc[MENU_MAX] = { 0, 1, 1, 1, 2, 3, 3, 1, 8, 0, 0, 11, 3, 0 }; static const JE_byte itemAvailMap[7] = { 1, 2, 3, 9, 4, 6, 7 }; static const JE_word planetX[21] = { 200, 150, 240, 300, 270, 280, 320, 260, 220, 150, 160, 210, 80, 240, 220, 180, 310, 330, 150, 240, 200 }; static const JE_word planetY[21] = { 40, 90, 90, 80, 170, 30, 50, 130, 120, 150, 220, 200, 80, 50, 160, 10, 55, 55, 90, 90, 40 }; static const uint cube_line_chars = sizeof(*cube->text) - 1; static const uint cube_line_width = 150; /*** Functions ***/ static Uint8 *playeritem_map(PlayerItems *items, uint i) { Uint8 *const map[] = { &items->ship, &items->weapon[FRONT_WEAPON].id, &items->weapon[REAR_WEAPON].id, &items->shield, &items->generator, &items->sidekick[LEFT_SIDEKICK], &items->sidekick[RIGHT_SIDEKICK], }; assert(i < COUNTOF(map)); return map[i]; } JE_longint JE_cashLeft(void) { JE_longint tempL = player[0].cash; JE_word itemNum = *playeritem_map(&player[0].items, curSel[MENU_UPGRADES] - 2); tempL -= JE_getCost(curSel[MENU_UPGRADES], itemNum); tempW = 0; switch (curSel[MENU_UPGRADES]) { case 3: case 4: for (uint i = 1; i < player[0].items.weapon[curSel[MENU_UPGRADES]-3].power; ++i) { tempW += weaponPort[itemNum].cost * i; tempL -= tempW; } break; } return tempL; } void JE_itemScreen(void) { bool quit = false; if (shopSpriteSheet.data == NULL) JE_loadCompShapes(&shopSpriteSheet, '1'); load_cubes(); VGAScreen = VGAScreenSeg; memcpy(menuChoices, menuChoicesDefault, sizeof(menuChoices)); play_song(songBuy); JE_loadPic(VGAScreen, 1, false); curPal = 1; newPal = 0; JE_showVGA(); set_palette(colors, 0, 255); col = 1; gameLoaded = false; curItemType = 1; cursor = 1; curItem = 0; for (unsigned int i = 0; i < COUNTOF(curSel); ++i) curSel[i] = 2; curMenu = MENU_FULL_GAME; int temp_weapon_power[7]; // assumes there'll never be more than 6 weapons to choose from, 7th is "Done" /* JE: (* Check for where Pitems and Select match up - if no match then add to the itemavail list *) */ for (int i = 0; i < 7; i++) { int item = *playeritem_map(&player[0].last_items, i); int slot = 0; for (; slot < itemAvailMax[itemAvailMap[i]-1]; ++slot) { if (itemAvail[itemAvailMap[i]-1][slot] == item) break; } if (slot == itemAvailMax[itemAvailMap[i]-1]) { itemAvail[itemAvailMap[i]-1][slot] = item; itemAvailMax[itemAvailMap[i]-1]++; } } memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); keyboardUsed = false; firstMenu9 = false; backFromHelp = false; /* JE: Sort items in merchant inventory */ for (int x = 0; x < 9; x++) { if (itemAvailMax[x] > 1) { for (temp = 0; temp < itemAvailMax[x] - 1; temp++) { for (temp2 = temp; temp2 < itemAvailMax[x]; temp2++) { if (itemAvail[x][temp] == 0 || (itemAvail[x][temp] > itemAvail[x][temp2] && itemAvail[x][temp2] != 0)) { temp3 = itemAvail[x][temp]; itemAvail[x][temp] = itemAvail[x][temp2]; itemAvail[x][temp2] = temp3; } } } } } do { quit = false; JE_getShipInfo(); if (curMenu == MENU_FULL_GAME) { if (twoPlayerMode) curMenu = MENU_2_PLAYER_ARCADE; if (isNetworkGame || onePlayerAction) curMenu = MENU_1_PLAYER_ARCADE; if (superTyrian) curMenu = MENU_SUPER_TYRIAN; } paletteChanged = false; leftPower = false; rightPower = false; /* SYN: note reindexing... "firstMenu9" refers to Menu 8 here :( */ if (curMenu != MENU_DATA_CUBE_SUB || firstMenu9) { memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); } if (curMenu == MENU_UPGRADES && (curSel[curMenu] == 3 || curSel[curMenu] == 4)) { // reset temp_weapon_power[] every time we select upgrading front or back const uint item = player[0].items.weapon[curSel[MENU_UPGRADES] - 3].id, item_power = player[0].items.weapon[curSel[MENU_UPGRADES] - 3].power, i = curSel[MENU_UPGRADES] - 2; // 1 or 2 (front or rear) // set power level of owned weapon for (int slot = 0; slot < itemAvailMax[itemAvailMap[i]-1]; ++slot) { if (itemAvail[itemAvailMap[i]-1][slot] == item) temp_weapon_power[slot] = item_power; else temp_weapon_power[slot] = 1; } // set power level for "Done" temp_weapon_power[itemAvailMax[itemAvailMap[i]-1]] = item_power; } if (curMenu == MENU_PLAY_NEXT_LEVEL) { planetAni = 0; keyboardUsed = false; currentDotNum = 0; currentDotWait = 8; planetAniWait = 3; JE_updateNavScreen(); } /* Draw menu title for everything but upgrade ship submenus */ if (curMenu != MENU_UPGRADE_SUB) { JE_drawMenuHeader(); } /* Draw menu choices for simple menus */ if ((curMenu >= MENU_FULL_GAME && curMenu <= MENU_PLAY_NEXT_LEVEL) || (curMenu >= MENU_2_PLAYER_ARCADE && curMenu <= MENU_LIMITED_OPTIONS) || curMenu == MENU_SUPER_TYRIAN) { JE_drawMenuChoices(); } /* Data cube icons */ if (curMenu == MENU_FULL_GAME) { for (int i = 1; i <= cubeMax; i++) { blit_sprite_dark(VGAScreen, 190 + i * 18 + 2, 37 + 1, OPTION_SHAPES, 34, false); blit_sprite(VGAScreen, 190 + i * 18, 37, OPTION_SHAPES, 34); // data cube } } /* load/save menu */ if (curMenu == MENU_LOAD_SAVE) { int min, max; if (twoPlayerMode) { min = 13; max = 24; } else { min = 2; max = 13; } for (int x = min; x <= max; x++) { /* Highlight if current selection */ temp2 = (x - min + 2 == curSel[curMenu]) ? 15 : 28; /* Write save game slot */ if (x == max) strcpy(tempStr, miscText[6-1]); else if (saveFiles[x-2].level == 0) strcpy(tempStr, miscText[3-1]); else strcpy(tempStr, saveFiles[x-2].name); int tempY = 38 + (x - min)*11; JE_textShade(VGAScreen, 163, tempY, tempStr, temp2 / 16, temp2 % 16 - 8, DARKEN); if (x < max) /* x == max isn't a save slot */ { /* Highlight if current selection */ temp2 = (x - min + 2 == curSel[curMenu]) ? 252 : 250; if (saveFiles[x-2].level == 0) { strcpy(tempStr, "-----"); /* Empty save slot */ } else { char buf[20]; strcpy(tempStr, saveFiles[x-2].levelName); snprintf(buf, sizeof buf, "%s%d", miscTextB[1-1], saveFiles[x-2].episode); JE_textShade(VGAScreen, 297, tempY, buf, temp2 / 16, temp2 % 16 - 8, DARKEN); } JE_textShade(VGAScreen, 245, tempY, tempStr, temp2 / 16, temp2 % 16 - 8, DARKEN); } JE_drawMenuHeader(); } } if (curMenu == MENU_KEYBOARD_CONFIG) { for (int x = 2; x <= 11; x++) { if (x == curSel[curMenu]) { temp2 = 15; } else { temp2 = 28; } JE_textShade(VGAScreen, 166, 38 + (x - 2)*12, menuInt[curMenu + 1][x-1], temp2 / 16, temp2 % 16 - 8, DARKEN); if (x < 10) /* 10 = reset to defaults, 11 = done */ { temp2 = (x == curSel[curMenu]) ? 252 : 250; JE_textShade(VGAScreen, 236, 38 + (x - 2)*12, SDL_GetScancodeName(keySettings[x-2]), temp2 / 16, temp2 % 16 - 8, DARKEN); } } menuChoices[MENU_KEYBOARD_CONFIG] = 11; } if (curMenu == MENU_JOYSTICK_CONFIG) { const char *const menu_item[] = { "JOYSTICK", "ANALOG AXES", " SENSITIVITY", " THRESHOLD", menuInt[6][1], menuInt[6][4], menuInt[6][2], menuInt[6][3], menuInt[6][5], menuInt[6][6], menuInt[6][7], menuInt[6][8], "MENU", "PAUSE", menuInt[6][9], menuInt[6][10] }; for (uint i = 0; i < COUNTOF(menu_item); i++) { int temp = (i == curSel[curMenu] - 2u) ? 15 : 28; JE_textShade(VGAScreen, 166, 38 + i * 8, menu_item[i], temp / 16, temp % 16 - 8, DARKEN); temp = (i == curSel[curMenu] - 2u) ? 252 : 250; char value[30] = ""; if (joysticks == 0 && i < 14) // no joysticks, everything disabled { sprintf(value, "-"); } else if (i == 0) // joystick number { sprintf(value, "%d", joystick_config + 1); } else if (i == 1) // joystick is analog { sprintf(value, "%s", joystick[joystick_config].analog ? "TRUE" : "FALSE"); } else if (i < 4) // joystick analog settings { if (!joystick[joystick_config].analog) temp -= 3; sprintf(value, "%d", i == 2 ? joystick[joystick_config].sensitivity : joystick[joystick_config].threshold); } else if (i < 14) // assignments { joystick_assignments_to_string(value, sizeof(value), joystick[joystick_config].assignment[i - 4]); } JE_textShade(VGAScreen, 236, 38 + i * 8, value, temp / 16, temp % 16 - 8, DARKEN); } menuChoices[curMenu] = COUNTOF(menu_item) + 1; } if (curMenu == MENU_UPGRADE_SUB) { /* Move cursor until we hit either "Done" or a weapon the player can afford */ while (curSel[MENU_UPGRADE_SUB] < menuChoices[MENU_UPGRADE_SUB] && JE_getCost(curSel[MENU_UPGRADES], itemAvail[itemAvailMap[curSel[MENU_UPGRADES]-2]-1][curSel[MENU_UPGRADE_SUB]-2]) > player[0].cash) { curSel[MENU_UPGRADE_SUB] += lastDirection; if (curSel[MENU_UPGRADE_SUB] < 2) curSel[MENU_UPGRADE_SUB] = menuChoices[MENU_UPGRADE_SUB]; else if (curSel[MENU_UPGRADE_SUB] > menuChoices[MENU_UPGRADE_SUB]) curSel[MENU_UPGRADE_SUB] = 2; } if (curSel[MENU_UPGRADE_SUB] == menuChoices[MENU_UPGRADE_SUB]) { /* If cursor on "Done", use previous weapon */ *playeritem_map(&player[0].items, curSel[MENU_UPGRADES] - 2) = *playeritem_map(&old_items[0], curSel[MENU_UPGRADES] - 2); } else { /* Otherwise display the selected weapon */ *playeritem_map(&player[0].items, curSel[MENU_UPGRADES] - 2) = itemAvail[itemAvailMap[curSel[MENU_UPGRADES]-2]-1][curSel[MENU_UPGRADE_SUB]-2]; } /* Get power level info for front and rear weapons */ if ((curSel[MENU_UPGRADES] == 3 || curSel[MENU_UPGRADES] == 4) && // front or rear weapon curSel[MENU_UPGRADE_SUB] < menuChoices[MENU_UPGRADE_SUB] && // not "Done" itemAvail[itemAvailMap[curSel[MENU_UPGRADES]-2]-1][curSel[MENU_UPGRADE_SUB]-2] != 0) // not "None" { const uint port = curSel[MENU_UPGRADES] - 3, // 0 or 1 (front or back) item_level = player[0].items.weapon[port].power; // calculate upgradeCost JE_getCost(curSel[MENU_UPGRADES], itemAvail[itemAvailMap[curSel[MENU_UPGRADES]-2]-1][curSel[MENU_UPGRADE_SUB]-2]); leftPower = item_level > 1; // can downgrade rightPower = item_level < 11; // can upgrade if (rightPower) rightPowerAfford = JE_cashLeft() >= upgradeCost; // can afford upgrade } else { /* Nothing else can be upgraded / downgraded */ leftPower = false; rightPower = false; } /* submenu title e.g., "Left Sidekick" */ JE_dString(VGAScreen, 74 + JE_fontCenter(menuInt[2][curSel[MENU_UPGRADES]-1], FONT_SHAPES), 10, menuInt[2][curSel[MENU_UPGRADES]-1], FONT_SHAPES); /* Iterate through all submenu options */ for (tempW = 1; tempW < menuChoices[curMenu]; tempW++) { int tempY = 40 + (tempW-1) * 26; /* Calculate y position */ uint temp_cost; /* Is this a item or None/DONE? */ if (tempW < menuChoices[MENU_UPGRADE_SUB] - 1) { /* Get base cost for choice */ temp_cost = JE_getCost(curSel[MENU_UPGRADES], itemAvail[itemAvailMap[curSel[MENU_UPGRADES]-2]-1][tempW-1]); } else { /* "None" is free :) */ temp_cost = 0; } int afford_shade = (temp_cost > player[0].cash) ? 4 : 0; // can player afford current weapon at all temp = itemAvail[itemAvailMap[curSel[MENU_UPGRADES]-2]-1][tempW-1]; /* Item ID */ switch (curSel[MENU_UPGRADES]-1) { case 1: /* ship */ if (temp > 90) snprintf(tempStr, sizeof(tempStr), "Custom Ship %d", temp - 90); else strcpy(tempStr, ships[temp].name); break; case 2: /* front and rear weapon */ case 3: strcpy(tempStr, weaponPort[temp].name); break; case 4: /* shields */ strcpy(tempStr, shields[temp].name); break; case 5: /* generator */ strcpy(tempStr, powerSys[temp].name); break; case 6: /* sidekicks */ case 7: strcpy(tempStr, options[temp].name); break; } if (tempW == curSel[curMenu]-1) temp2 = 15; else temp2 = 28; JE_getShipInfo(); /* item-owned marker */ if (temp == *playeritem_map(&old_items[0], curSel[MENU_UPGRADES] - 2) && temp != 0 && tempW != menuChoices[curMenu]-1) { fill_rectangle_xy(VGAScreen, 160, tempY+7, 300, tempY+11, 227); blit_sprite2(VGAScreen, 298, tempY+2, shopSpriteSheet, 247); } /* Draw DONE */ if (tempW == menuChoices[curMenu]-1) { strcpy(tempStr, miscText[13]); } JE_textShade(VGAScreen, 185, tempY, tempStr, temp2 / 16, temp2 % 16 - 8 - afford_shade, DARKEN); /* Draw icon if not DONE. NOTE: None is a normal item with a blank icon. */ if (tempW < menuChoices[curMenu]-1) { JE_drawItem(curSel[MENU_UPGRADES]-1, temp, 160, tempY-4); } /* Make selected text brighter */ temp2 = (tempW == curSel[curMenu]-1) ? 15 : 28; /* Draw Cost: if it's not the DONE option */ if (tempW != menuChoices[curMenu]-1) { char buf[20]; snprintf(buf, sizeof buf, "Cost: %d", temp_cost); JE_textShade(VGAScreen, 187, tempY+10, buf, temp2 / 16, temp2 % 16 - 8 - afford_shade, DARKEN); } } } /* Draw current money and shield/armor bars, when appropriate */ if (((curMenu <= MENU_OPTIONS || curMenu == MENU_KEYBOARD_CONFIG || curMenu == MENU_LOAD_SAVE || curMenu >= MENU_1_PLAYER_ARCADE) && !twoPlayerMode) || (curMenu == MENU_UPGRADE_SUB && (curSel[MENU_UPGRADES] >= 1 && curSel[MENU_UPGRADES] <= 6))) { if (curMenu != MENU_UPGRADE_SUB) { char buf[20]; snprintf(buf, sizeof buf, "%lu", player[0].cash); JE_textShade(VGAScreen, 65, 173, buf, 1, 6, DARKEN); } JE_barDrawShadow(VGAScreen, 42, 152, 3, 14, player[0].armor, 2, 13); JE_barDrawShadow(VGAScreen, 104, 152, 2, 14, shields[player[0].items.shield].mpwr * 2, 2, 13); } /* Draw crap on the left side of the screen, i.e. two player scores, ship graphic, etc. */ if ((curMenu >= MENU_FULL_GAME && curMenu <= MENU_OPTIONS) || curMenu == MENU_KEYBOARD_CONFIG || curMenu == MENU_LOAD_SAVE || curMenu >= MENU_2_PLAYER_ARCADE || (curMenu == MENU_UPGRADE_SUB && (curSel[MENU_UPGRADES] == 2 || curSel[MENU_UPGRADES] == 5))) { if (twoPlayerMode) { char buf[50]; for (uint i = 0; i < 2; ++i) { snprintf(buf, sizeof(buf), "%s %lu", miscText[40 + i], player[i].cash); JE_textShade(VGAScreen, 25, 50 + 10 * i, buf, 15, 0, FULL_SHADE); } } else if (superArcadeMode != SA_NONE || superTyrian) { helpBoxColor = 15; helpBoxBrightness = 4; if (!superTyrian) JE_helpBox(VGAScreen, 35, 25, superShips[superArcadeMode], 18); else JE_helpBox(VGAScreen, 35, 25, superShips[SA+3], 18); helpBoxBrightness = 1; JE_textShade(VGAScreen, 25, 50, superShips[SA+1], 15, 0, FULL_SHADE); JE_helpBox(VGAScreen, 25, 60, weaponPort[player[0].items.weapon[FRONT_WEAPON].id].name, 22); JE_textShade(VGAScreen, 25, 120, superShips[SA+2], 15, 0, FULL_SHADE); JE_helpBox(VGAScreen, 25, 130, special[player[0].items.special].name, 22); } else { draw_ship_illustration(); } } /* Changing the volume? */ if (curMenu == MENU_OPTIONS || curMenu == MENU_LIMITED_OPTIONS) { JE_barDrawShadow(VGAScreen, 225, 70, 1, music_disabled ? 12 : 16, tyrMusicVolume / 12, 3, 13); JE_barDrawShadow(VGAScreen, 225, 86, 1, samples_disabled ? 12 : 16, fxVolume / 12, 3, 13); } /* "firstmenu9" refers to menu 8 because of reindexing */ if (curMenu == MENU_DATA_CUBES || (curMenu == MENU_DATA_CUBE_SUB && (firstMenu9 || backFromHelp))) { firstMenu9 = false; menuChoices[MENU_DATA_CUBES] = cubeMax + 2; fill_rectangle_xy(VGAScreen, 1, 1, 145, 170, 0); blit_sprite(VGAScreenSeg, 1, 1, OPTION_SHAPES, 20); /* Portrait area background */ if (curMenu == MENU_DATA_CUBES) { if (cubeMax == 0) { JE_helpBox(VGAScreen, 166, 80, miscText[16 - 1], 30); tempW = 160; temp2 = 252; } else { for (int x = 1; x <= cubeMax; x++) { JE_drawCube(VGAScreenSeg, 166, 38 + (x - 1) * 28, 13, 0); if (x + 1 == curSel[curMenu]) { temp2 = 252; } else { temp2 = 250; } helpBoxColor = temp2 / 16; helpBoxBrightness = (temp2 % 16) - 8; helpBoxShadeType = DARKEN; JE_helpBox(VGAScreen, 192, 44 + (x - 1) * 28, cube[x - 1].title, 24); } int x = cubeMax + 1; if (x + 1 == curSel[curMenu]) { temp2 = 252; } else { temp2 = 250; } tempW = 44 + (x - 1) * 28; } JE_textShade(VGAScreen, 172, tempW, miscText[6 - 1], temp2 / 16, (temp2 % 16) - 8, DARKEN); } if (curSel[MENU_DATA_CUBES] < menuChoices[MENU_DATA_CUBES]) { const int face_sprite = cube[curSel[MENU_DATA_CUBES] - 2].face_sprite; if (face_sprite != -1) { const int face_x = 77 - (sprite(FACE_SHAPES, face_sprite)->width / 2), face_y = 92 - (sprite(FACE_SHAPES, face_sprite)->height / 2); blit_sprite(VGAScreenSeg, face_x, face_y, FACE_SHAPES, face_sprite); // datacube face // modify palette for face paletteChanged = true; temp2 = facepal[face_sprite]; newPal = 0; for (temp = 1; temp <= 255 - (3 * 16); temp++) colors[temp] = palettes[temp2][temp]; } } } /* 2 player input devices */ if (curMenu == MENU_2_PLAYER_ARCADE) { for (uint i = 0; i < COUNTOF(inputDevice); i++) { if (inputDevice[i] > 2 + joysticks) inputDevice[i] = inputDevice[i == 0 ? 1 : 0] == 1 ? 2 : 1; char temp[64]; if (joysticks > 1 && inputDevice[i] > 2) sprintf(temp, "%s %d", inputDevices[2], inputDevice[i] - 2); else sprintf(temp, "%s", inputDevices[inputDevice[i] - 1]); JE_dString(VGAScreen, 186, 38 + 2 * (i + 1) * 16, temp, SMALL_FONT_SHAPES); } } /* JE: { - Step VI - Help text for current cursor location } */ flash = false; /* JE: {Reset player weapons} */ memset(shotMultiPos, 0, sizeof(shotMultiPos)); JE_drawScore(); JE_drawMainMenuHelpText(); if (newPal > 0) /* can't reindex this :( */ { curPal = newPal; memcpy(colors, palettes[newPal - 1], sizeof(colors)); set_palette(palettes[newPal - 1], 0, 255); newPal = 0; } /* datacube title under face */ if ((curMenu == MENU_DATA_CUBES || curMenu == MENU_DATA_CUBE_SUB) && curSel[MENU_DATA_CUBES] < menuChoices[MENU_DATA_CUBES]) { JE_textShade(VGAScreen, 75 - JE_textWidth(cube[curSel[MENU_DATA_CUBES] - 2].header, TINY_FONT) / 2, 173, cube[curSel[MENU_DATA_CUBES] - 2].header, 14, 3, DARKEN); } /* SYN: Everything above was just drawing the screen. In the rest of it, we process any user input (and do a few other things) */ /* SYN: Let's start by getting fresh events from SDL */ service_SDL_events(true); if (constantPlay) { mainLevel = mapSection[mapPNum-1]; jumpSection = true; } else { do { /* Inner loop -- this handles animations on menus that need them and handles some keyboard events. Events it can't handle end the loop and fall through to the main keyboard handler below. Also, I think all timing is handled in here. Somehow. */ NETWORK_KEEP_ALIVE(); mouseCursor = MOUSE_POINTER_NORMAL; col += colC; if (col < -2 || col > 6) { colC = (-1 * colC); } // data cube reading if (curMenu == MENU_DATA_CUBE_SUB) { if (mouseX > 164 && mouseX < 299 && mouseY > 47 && mouseY < 153) { if (mouseY > 100) mouseCursor = MOUSE_POINTER_DOWN; else mouseCursor = MOUSE_POINTER_UP; } fill_rectangle_xy(VGAScreen, 160, 49, 310, 158, 228); if (yLoc + yChg < 0) { yChg = 0; yLoc = 0; } yLoc += yChg; temp = yLoc / 12; temp2 = yLoc % 12; tempW = 38 + 12 - temp2; temp3 = cube[curSel[MENU_DATA_CUBES] - 2].last_line; for (int x = temp + 1; x <= temp + 10; x++) { if (x <= temp3) { JE_outTextAndDarken(VGAScreen, 161, tempW, cube[curSel[MENU_DATA_CUBES] - 2].text[x-1], 14, 3, TINY_FONT); tempW += 12; } } fill_rectangle_xy(VGAScreen, 160, 39, 310, 48, 228); fill_rectangle_xy(VGAScreen, 160, 157, 310, 166, 228); int percent_read = (cube[currentCube].last_line <= 9) ? 100 : (yLoc * 100) / ((cube[currentCube].last_line - 9) * 12); char buf[55]; snprintf(buf, sizeof(buf), "%s %d%%", miscText[11], percent_read); JE_outTextAndDarken(VGAScreen, 176, 160, buf, 14, 1, TINY_FONT); JE_dString(VGAScreen, 260, 160, miscText[12], SMALL_FONT_SHAPES); if (temp2 == 0) yChg = 0; JE_mouseStart(); JE_showVGA(); if (backFromHelp) { fade_palette(colors, 10, 0, 255); backFromHelp = false; } JE_mouseReplace(); setDelay(1); } else { /* current menu is not 8 (read data cube) */ if (curMenu == MENU_PLAY_NEXT_LEVEL) { JE_updateNavScreen(); JE_drawMainMenuHelpText(); JE_drawMenuHeader(); JE_drawMenuChoices(); if (extraGame) JE_dString(VGAScreen, 170, 140, miscText[68 - 1], FONT_SHAPES); } if (curMenu == MENU_DATA_CUBES && curSel[MENU_DATA_CUBES] < menuChoices[MENU_DATA_CUBES]) { /* Draw flashy cube */ blit_sprite_hv_blend(VGAScreenSeg, 166, 38 + (curSel[MENU_DATA_CUBES] - 2) * 28, OPTION_SHAPES, 25, 13, col); } /* IF (curmenu = 5) AND (cursel [2] IN [3, 4, 6, 7, 8]) */ if (curMenu == MENU_UPGRADE_SUB && (curSel[MENU_UPGRADES] == 3 || curSel[MENU_UPGRADES] == 4 || (curSel[MENU_UPGRADES] >= 6 && curSel[MENU_UPGRADES] <= 8))) { setDelay(3); JE_weaponSimUpdate(); JE_drawScore(); service_SDL_events(false); if (newPal > 0) { curPal = newPal; set_palette(palettes[newPal - 1], 0, 255); newPal = 0; } JE_mouseStart(); if (paletteChanged) { set_palette(colors, 0, 255); paletteChanged = false; } JE_showVGA(); /* SYN: This is where it updates the screen for the weapon sim */ if (backFromHelp) { fade_palette(colors, 10, 0, 255); backFromHelp = false; } JE_mouseReplace(); } else /* current menu is anything but weapon sim or datacube */ { setDelay(2); JE_drawScore(); if (newPal > 0) { curPal = newPal; set_palette(palettes[newPal - 1], 0, 255); newPal = 0; } JE_mouseStart(); if (paletteChanged) { set_palette(colors, 0, 255); paletteChanged = false; } JE_showVGA(); /* SYN: This is the where the screen updates for most menus */ JE_mouseReplace(); if (backFromHelp) { fade_palette(colors, 10, 0, 255); backFromHelp = false; } } } wait_delay(); push_joysticks_as_keyboard(); service_SDL_events(false); mouseButton = JE_mousePosition(&mouseX, &mouseY); inputDetected = newkey || mouseButton > 0; if (curMenu != MENU_LOAD_SAVE) { if (keysactive[SDL_SCANCODE_S] && (keysactive[SDL_SCANCODE_LALT] || keysactive[SDL_SCANCODE_RALT])) { if (curMenu == MENU_DATA_CUBE_SUB || curMenu == MENU_DATA_CUBES) { curMenu = MENU_FULL_GAME; } quikSave = true; oldMenu = curMenu; curMenu = MENU_LOAD_SAVE; performSave = true; newPal = 1; oldPal = curPal; } if (keysactive[SDL_SCANCODE_L] && (keysactive[SDL_SCANCODE_LALT] || keysactive[SDL_SCANCODE_RALT])) { if (curMenu == MENU_DATA_CUBE_SUB || curMenu == MENU_DATA_CUBES) { curMenu = MENU_FULL_GAME; } quikSave = true; oldMenu = curMenu; curMenu = MENU_LOAD_SAVE; performSave = false; newPal = 1; oldPal = curPal; } } if (curMenu == MENU_DATA_CUBE_SUB) { if (mouseButton > 0 && mouseCursor != MOUSE_POINTER_NORMAL) { inputDetected = false; if (mouseCursor == MOUSE_POINTER_UP) yChg = -1; else yChg = 1; } if (keysactive[SDL_SCANCODE_PAGEUP]) { yChg = -2; inputDetected = false; } if (keysactive[SDL_SCANCODE_PAGEDOWN]) { yChg = 2; inputDetected = false; } bool joystick_up = false, joystick_down = false; for (int j = 0; j < joysticks; j++) { joystick_up |= joystick[j].direction[0]; joystick_down |= joystick[j].direction[2]; } if (keysactive[SDL_SCANCODE_UP] || joystick_up) { yChg = -1; inputDetected = false; } if (keysactive[SDL_SCANCODE_DOWN] || joystick_down) { yChg = 1; inputDetected = false; } if (yChg < 0 && yLoc == 0) { yChg = 0; } if (yChg > 0 && (yLoc / 12) > cube[currentCube].last_line - 10) { yChg = 0; } } } while (!inputDetected); } keyboardUsed = false; /* The rest of this just grabs input events, handles them, then proceeds on. */ if (mouseButton > 0) { lastDirection = 1; mouseButton = JE_mousePosition(&mouseX, &mouseY); if (curMenu == MENU_DATA_CUBES && cubeMax == 0) { curMenu = MENU_FULL_GAME; JE_playSampleNum(S_SPRING); newPal = 1; JE_wipeKey(); } if (curMenu == MENU_DATA_CUBE_SUB) { if ((mouseX > 258) && (mouseX < 290) && (mouseY > 159) && (mouseY < 171)) { curMenu = MENU_DATA_CUBES; JE_playSampleNum(S_SPRING); } } if (curMenu == MENU_OPTIONS || curMenu == MENU_LIMITED_OPTIONS) { if ((mouseX >= (225 - 4)) && (mouseY >= 70) && (mouseY <= 82)) { if (music_disabled) { music_disabled = false; restart_song(); } curSel[MENU_OPTIONS] = 4; tyrMusicVolume = (mouseX - (225 - 4)) / 4 * 12; if (tyrMusicVolume > 255) tyrMusicVolume = 255; } if ((mouseX >= (225 - 4)) && (mouseY >= 86) && (mouseY <= 98)) { samples_disabled = false; curSel[MENU_OPTIONS] = 5; fxVolume = (mouseX - (225 - 4)) / 4 * 12; if (fxVolume > 255) fxVolume = 255; } set_volume(tyrMusicVolume, fxVolume); JE_playSampleNum(S_CURSOR); } if (mouseY > 20 && mouseX > 170 && mouseX < 308 && curMenu != MENU_DATA_CUBE_SUB) { const JE_byte mouseSelectionY[MENU_MAX] = { 16, 16, 16, 16, 26, 12, 11, 28, 0, 16, 16, 16, 8, 16 }; int selection = (mouseY - 38) / mouseSelectionY[curMenu]+2; if (curMenu == MENU_2_PLAYER_ARCADE) { if (selection > 5) selection--; if (selection > 3) selection--; } if (curMenu == MENU_FULL_GAME) { if (selection > 7) selection = 7; } // is play next level screen? if (curMenu == MENU_PLAY_NEXT_LEVEL) { if (selection == menuChoices[curMenu] + 1) selection = menuChoices[curMenu]; } if (selection <= menuChoices[curMenu]) { if (curMenu == MENU_UPGRADE_SUB && selection == menuChoices[MENU_UPGRADE_SUB]) { player[0].cash = JE_cashLeft(); curMenu = MENU_UPGRADES; JE_playSampleNum(S_ITEM); } else { JE_playSampleNum(S_CLICK); if (curSel[curMenu] == selection) { JE_menuFunction(curSel[curMenu]); } else { if (curMenu == MENU_UPGRADE_SUB && JE_getCost(curSel[MENU_UPGRADES], itemAvail[itemAvailMap[curSel[MENU_UPGRADES]-2]-1][selection-2]) > player[0].cash) { JE_playSampleNum(S_CLINK); } else { if (curSel[MENU_UPGRADES] == 4) player[0].weapon_mode = 1; curSel[curMenu] = selection; } // in front or rear weapon upgrade screen? if (curMenu == MENU_UPGRADE_SUB && (curSel[MENU_UPGRADES] == 3 || curSel[MENU_UPGRADES] == 4)) { player[0].items.weapon[curSel[MENU_UPGRADES]-3].power = temp_weapon_power[curSel[MENU_UPGRADE_SUB]-2]; } } } } wait_noinput(false, true, false); } if (curMenu == MENU_UPGRADE_SUB && (curSel[MENU_UPGRADES] == 3 || curSel[MENU_UPGRADES] == 4)) { if ((mouseX >= 23) && (mouseX <= 36) && (mouseY >= 149) && (mouseY <= 168)) { JE_playSampleNum(S_CURSOR); switch (curSel[MENU_UPGRADES]) { case 3: case 4: if (leftPower) player[0].items.weapon[curSel[MENU_UPGRADES]-3].power = --temp_weapon_power[curSel[MENU_UPGRADE_SUB]-2]; else JE_playSampleNum(S_CLINK); break; } wait_noinput(false, true, false); } if ((mouseX >= 119) && (mouseX <= 131) && (mouseY >= 149) && (mouseY <= 168)) { JE_playSampleNum(S_CURSOR); switch (curSel[MENU_UPGRADES]) { case 3: case 4: if (rightPower && rightPowerAfford) player[0].items.weapon[curSel[MENU_UPGRADES]-3].power = ++temp_weapon_power[curSel[MENU_UPGRADE_SUB]-2]; else JE_playSampleNum(S_CLINK); break; } wait_noinput(false, true, false); } } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_SLASH: // if in rear weapon upgrade screen if (curMenu == MENU_UPGRADE_SUB && curSel[MENU_UPGRADES] == 4) { // cycle weapon modes if (++player[0].weapon_mode > weaponPort[player[0].items.weapon[REAR_WEAPON].id].opnum) player[0].weapon_mode = 1; } break; case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: keyboardUsed = true; // if front or rear weapon, update "Done" power level if (curMenu == MENU_UPGRADE_SUB && (curSel[MENU_UPGRADES] == 3 || curSel[MENU_UPGRADES] == 4)) temp_weapon_power[itemAvailMax[itemAvailMap[curSel[MENU_UPGRADES]-2]-1]] = player[0].items.weapon[curSel[MENU_UPGRADES]-3].power; JE_menuFunction(curSel[curMenu]); break; case SDL_SCANCODE_ESCAPE: keyboardUsed = true; JE_playSampleNum(S_SPRING); if (curMenu == MENU_LOAD_SAVE && quikSave) { curMenu = oldMenu; newPal = oldPal; } else if (menuEsc[curMenu] == 0) { if (JE_quitRequest()) { gameLoaded = true; mainLevel = 0; } } else { if (curMenu == MENU_UPGRADE_SUB) // leaving upgrade menu without buying { player[0].items = old_items[0]; curSel[MENU_UPGRADE_SUB] = lastCurSel; player[0].cash = JE_cashLeft(); } if (curMenu != MENU_DATA_CUBE_SUB) newPal = 1; curMenu = menuEsc[curMenu] - 1; } break; case SDL_SCANCODE_F1: if (!isNetworkGame) { fade_black(10); JE_helpSystem(2); play_song(songBuy); JE_loadPic(VGAScreen, 1, false); newPal = 1; switch (curMenu) { case 3: newPal = 18; break; case 7: case 8: break; } memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); curPal = newPal; memcpy(colors, palettes[newPal-1], sizeof(colors)); JE_showVGA(); newPal = 0; backFromHelp = true; } break; case SDL_SCANCODE_UP: keyboardUsed = true; lastDirection = -1; if (curMenu != MENU_DATA_CUBE_SUB) JE_playSampleNum(S_CURSOR); curSel[curMenu]--; if (curSel[curMenu] < 2) curSel[curMenu] = menuChoices[curMenu]; // if in front or rear weapon upgrade screen if (curMenu == MENU_UPGRADE_SUB && (curSel[MENU_UPGRADES] == 3 || curSel[MENU_UPGRADES] == 4)) { player[0].items.weapon[curSel[MENU_UPGRADES]-3].power = temp_weapon_power[curSel[MENU_UPGRADE_SUB]-2]; if (curSel[MENU_UPGRADES] == 4) player[0].weapon_mode = 1; } // if joystick config, skip disabled items when digital if (curMenu == MENU_JOYSTICK_CONFIG && joysticks > 0 && !joystick[joystick_config].analog && curSel[curMenu] == 5) { curSel[curMenu] = 3; } break; case SDL_SCANCODE_DOWN: keyboardUsed = true; lastDirection = 1; if (curMenu != MENU_DATA_CUBE_SUB) JE_playSampleNum(S_CURSOR); curSel[curMenu]++; if (curSel[curMenu] > menuChoices[curMenu]) curSel[curMenu] = 2; // if in front or rear weapon upgrade screen if (curMenu == MENU_UPGRADE_SUB && (curSel[MENU_UPGRADES] == 3 || curSel[MENU_UPGRADES] == 4)) { player[0].items.weapon[curSel[MENU_UPGRADES]-3].power = temp_weapon_power[curSel[MENU_UPGRADE_SUB]-2]; if (curSel[MENU_UPGRADES] == 4) player[0].weapon_mode = 1; } // if in joystick config, skip disabled items when digital if (curMenu == MENU_JOYSTICK_CONFIG && joysticks > 0 && !joystick[joystick_config].analog && curSel[curMenu] == 4) { curSel[curMenu] = 6; } break; case SDL_SCANCODE_HOME: if (curMenu == MENU_DATA_CUBE_SUB) yLoc = 0; break; case SDL_SCANCODE_END: if (curMenu == MENU_DATA_CUBE_SUB) yLoc = (cube[currentCube].last_line - 9) * 12; break; case SDL_SCANCODE_LEFT: if (curMenu == MENU_JOYSTICK_CONFIG) { if (joysticks > 0) { switch (curSel[curMenu]) { case 2: if (joystick_config == 0) joystick_config = joysticks; joystick_config--; break; case 3: joystick[joystick_config].analog = !joystick[joystick_config].analog; break; case 4: if (joystick[joystick_config].sensitivity == 0) joystick[joystick_config].sensitivity = 10; else joystick[joystick_config].sensitivity--; break; case 5: if (joystick[joystick_config].threshold == 0) joystick[joystick_config].threshold = 10; else joystick[joystick_config].threshold--; break; default: break; } } } if (curMenu == MENU_2_PLAYER_ARCADE) { switch (curSel[curMenu]) { case 3: case 4: JE_playSampleNum(S_CURSOR); int temp = curSel[curMenu] - 3; do { if (joysticks == 0) inputDevice[temp == 0 ? 1 : 0] = inputDevice[temp]; // swap controllers if (inputDevice[temp] <= 1) inputDevice[temp] = 2 + joysticks; else inputDevice[temp]--; } while (inputDevice[temp] == inputDevice[temp == 0 ? 1 : 0]); break; } } if (curMenu == MENU_OPTIONS || curMenu == MENU_UPGRADE_SUB || curMenu == MENU_LIMITED_OPTIONS) { JE_playSampleNum(S_CURSOR); } switch (curMenu) { case 2: case 11: switch (curSel[curMenu]) { case 4: JE_changeVolume(&tyrMusicVolume, -12, &fxVolume, 0); if (music_disabled) { music_disabled = false; restart_song(); } break; case 5: JE_changeVolume(&tyrMusicVolume, 0, &fxVolume, -12); samples_disabled = false; break; } break; case 4: switch (curSel[MENU_UPGRADES]) { case 3: case 4: if (leftPower) player[0].items.weapon[curSel[MENU_UPGRADES]-3].power = --temp_weapon_power[curSel[MENU_UPGRADE_SUB]-2]; else JE_playSampleNum(S_CLINK); break; } break; } break; case SDL_SCANCODE_RIGHT: if (curMenu == MENU_JOYSTICK_CONFIG) { if (joysticks > 0) { switch (curSel[curMenu]) { case 2: joystick_config++; joystick_config %= joysticks; break; case 3: joystick[joystick_config].analog = !joystick[joystick_config].analog; break; case 4: joystick[joystick_config].sensitivity++; joystick[joystick_config].sensitivity %= 11; break; case 5: joystick[joystick_config].threshold++; joystick[joystick_config].threshold %= 11; break; default: break; } } } if (curMenu == MENU_2_PLAYER_ARCADE) { switch (curSel[curMenu]) { case 3: case 4: JE_playSampleNum(S_CURSOR); int temp = curSel[curMenu] - 3; do { if (joysticks == 0) inputDevice[temp == 0 ? 1 : 0] = inputDevice[temp]; // swap controllers if (inputDevice[temp] >= 2 + joysticks) inputDevice[temp] = 1; else inputDevice[temp]++; } while (inputDevice[temp] == inputDevice[temp == 0 ? 1 : 0]); break; } } if (curMenu == MENU_OPTIONS || curMenu == MENU_UPGRADE_SUB || curMenu == MENU_LIMITED_OPTIONS) { JE_playSampleNum(S_CURSOR); } switch (curMenu) { case 2: case 11: switch (curSel[curMenu]) { case 4: JE_changeVolume(&tyrMusicVolume, 12, &fxVolume, 0); if (music_disabled) { music_disabled = false; restart_song(); } break; case 5: JE_changeVolume(&tyrMusicVolume, 0, &fxVolume, 12); samples_disabled = false; break; } break; case 4: switch (curSel[MENU_UPGRADES]) { case 3: case 4: if (rightPower && rightPowerAfford) player[0].items.weapon[curSel[MENU_UPGRADES]-3].power = ++temp_weapon_power[curSel[MENU_UPGRADE_SUB]-2]; else JE_playSampleNum(S_CLINK); break; } break; } break; default: break; } } } while (!(quit || gameLoaded || jumpSection)); #ifdef WITH_NETWORK if (!quit && isNetworkGame) { JE_barShade(VGAScreen, 3, 3, 316, 196); JE_barShade(VGAScreen, 1, 1, 318, 198); JE_dString(VGAScreen, 10, 160, "Waiting for other player.", SMALL_FONT_SHAPES); network_prepare(PACKET_WAITING); network_send(4); // PACKET_WAITING while (true) { service_SDL_events(false); JE_showVGA(); if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_WAITING) { network_update(); break; } network_update(); network_check(); SDL_Delay(16); } network_state_reset(); } if (isNetworkGame) { while (!network_is_sync()) { service_SDL_events(false); JE_showVGA(); network_check(); SDL_Delay(16); } } #endif if (gameLoaded) fade_black(10); } void draw_ship_illustration(void) { // full of evil hardcoding // ship { assert(player[0].items.ship > 0); const int sprite_id = (player[0].items.ship < COUNTOF(ships)) // shipedit ships get a default ? ships[player[0].items.ship].bigshipgraphic - 1 : 31; const int ship_x[6] = { 31, 0, 0, 0, 35, 31 }, ship_y[6] = { 36, 0, 0, 0, 33, 35 }; const int x = ship_x[sprite_id - 27], y = ship_y[sprite_id - 27]; blit_sprite(VGAScreenSeg, x, y, OPTION_SHAPES, sprite_id); } // generator { assert(player[0].items.generator > 0 && player[0].items.generator < 7); const int sprite_id = (player[0].items.generator == 1) // generator 1 and generator 2 have the same sprite ? player[0].items.generator + 15 : player[0].items.generator + 14; const int generator_x[5] = { 62, 64, 67, 66, 63 }, generator_y[5] = { 84, 85, 86, 84, 97 }; const int x = generator_x[sprite_id - 16], y = generator_y[sprite_id - 16]; blit_sprite(VGAScreenSeg, x, y, WEAPON_SHAPES, sprite_id); } const int weapon_sprites[43] = { -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 21, 5, 13, -1, 14, 15, 0, 14, 9, 8, 2, 15, 0, 13, 0, 8, 8, 11, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1 }; // front weapon if (player[0].items.weapon[FRONT_WEAPON].id > 0) { const int front_weapon_xy_list[43] = { -1, 4, 9, 3, 8, 2, 5, 10, 1, -1, -1, -1, -1, 7, 8, -1, -1, 0, -1, 4, 0, -1, -1, 3, -1, 4, -1, 4, -1, -1, -1, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 9 }; const int front_weapon_x[12] = { 59, 66, 66, 54, 61, 51, 58, 51, 61, 52, 53, 58 }; const int front_weapon_y[12] = { 38, 53, 41, 36, 48, 35, 41, 35, 53, 41, 39, 31 }; const int x = front_weapon_x[front_weapon_xy_list[player[0].items.weapon[FRONT_WEAPON].id]], y = front_weapon_y[front_weapon_xy_list[player[0].items.weapon[FRONT_WEAPON].id]]; blit_sprite(VGAScreenSeg, x, y, WEAPON_SHAPES, weapon_sprites[player[0].items.weapon[FRONT_WEAPON].id]); // ship illustration: front weapon } // rear weapon if (player[0].items.weapon[REAR_WEAPON].id > 0) { const int rear_weapon_xy_list[43] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, -1, 4, 5, -1, -1, 6, -1, -1, 1, 0, -1, 6, -1, 5, -1, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1 }; const int rear_weapon_x[7] = { 41, 27, 49, 43, 51, 39, 41 }; const int rear_weapon_y[7] = { 92, 92, 113, 102, 97, 96, 76 }; const int x = rear_weapon_x[rear_weapon_xy_list[player[0].items.weapon[REAR_WEAPON].id]], y = rear_weapon_y[rear_weapon_xy_list[player[0].items.weapon[REAR_WEAPON].id]]; blit_sprite(VGAScreenSeg, x, y, WEAPON_SHAPES, weapon_sprites[player[0].items.weapon[REAR_WEAPON].id]); } // sidekicks JE_drawItem(6, player[0].items.sidekick[LEFT_SIDEKICK], 3, 84); JE_drawItem(7, player[0].items.sidekick[RIGHT_SIDEKICK], 129, 84); // shield blit_sprite_hv(VGAScreenSeg, 28, 23, OPTION_SHAPES, 26, 15, shields[player[0].items.shield].mpwr - 10); } void load_cubes(void) { for (int cube_slot = 0; cube_slot < cubeMax; ++cube_slot) { memset(cube[cube_slot].text, 0, sizeof(cube->text)); load_cube(cube_slot, cubeList[cube_slot]); } } bool load_cube(int cube_slot, int cube_index) { FILE *f = dir_fopen_die(data_dir(), cube_file, "rb"); char buf[256]; // seek to the cube while (cube_index > 0) { read_encrypted_pascal_string(buf, sizeof(buf), f); if (buf[0] == '*') --cube_index; } str_pop_int(&buf[4], &cube[cube_slot].face_sprite); --cube[cube_slot].face_sprite; read_encrypted_pascal_string(cube[cube_slot].title, sizeof(cube[cube_slot].title), f); read_encrypted_pascal_string(cube[cube_slot].header, sizeof(cube[cube_slot].header), f); uint line = 0, line_chars = 0, line_width = 0; // for each line of decrypted text, split the line into words // and add them individually to the lines of wrapped text for (; ; ) { read_encrypted_pascal_string(buf, sizeof(buf), f); // end of data if (buf[0] == '*') break; // new paragraph if (strlen(buf) == 0) { if (line_chars == 0) line += 4; // subsequent new paragaphs indicate 4-line break else ++line; line_chars = 0; line_width = 0; continue; } uint word_start = 0; for (uint i = 0; ; ++i) { bool end_of_line = (buf[i] == '\0'), end_of_word = end_of_line || (buf[i] == ' '); if (end_of_word) { buf[i] = '\0'; char *word = &buf[word_start]; word_start = i + 1; uint word_chars = strlen(word), word_width = JE_textWidth(word, TINY_FONT); // word won't fit; no can do if (word_chars > cube_line_chars || word_width > cube_line_width) break; bool prepend_space = true; line_chars += word_chars + (prepend_space ? 1 : 0); line_width += word_width + (prepend_space ? 6 : 0); // word won't fit on current line; use next if (line_chars > cube_line_chars || line_width > cube_line_width) { ++line; line_chars = word_chars; line_width = word_width; prepend_space = false; } // append word if (line < COUNTOF(cube->text)) { if (prepend_space) strcat(cube[cube_slot].text[line], " "); strcat(cube[cube_slot].text[line], word); // track last line with text cube[cube_slot].last_line = line + 1; } } if (end_of_line) break; } } fclose(f); return true; } void JE_drawItem(JE_byte itemType, JE_word itemNum, JE_word x, JE_word y) { JE_word tempW = 0; if (itemNum > 0) { switch (itemType) { case 2: case 3: tempW = weaponPort[itemNum].itemgraphic; break; case 5: tempW = powerSys[itemNum].itemgraphic; break; case 6: case 7: tempW = options[itemNum].itemgraphic; break; case 4: tempW = shields[itemNum].itemgraphic; break; } if (itemType == 1) { if (itemNum > 90) { shipGrPtr = &spriteSheet9; shipGr = JE_SGr(itemNum - 90, &shipGrPtr); blit_sprite2x2(VGAScreen, x, y, *shipGrPtr, shipGr); } else { blit_sprite2x2(VGAScreen, x, y, spriteSheet9, ships[itemNum].shipgraphic); } } else if (tempW > 0) { blit_sprite2x2(VGAScreen, x, y, shopSpriteSheet, tempW); } } } void JE_drawMenuHeader(void) { switch (curMenu) { case MENU_DATA_CUBE_SUB: strcpy(tempStr, cube[curSel[MENU_DATA_CUBES]-2].header); break; case MENU_DATA_CUBES: strcpy(tempStr, menuInt[1][1]); break; case MENU_LOAD_SAVE: strcpy(tempStr, menuInt[3][performSave + 1]); break; default: strcpy(tempStr, menuInt[curMenu + 1][0]); break; } JE_dString(VGAScreen, 74 + JE_fontCenter(tempStr, FONT_SHAPES), 10, tempStr, FONT_SHAPES); } void JE_drawMenuChoices(void) { JE_byte x; char *str; for (x = 2; x <= menuChoices[curMenu]; x++) { int tempY = 38 + (x-1) * 16; if (curMenu == MENU_FULL_GAME) { if (x == 7) { tempY += 16; } } if (curMenu == MENU_2_PLAYER_ARCADE) { if (x > 3) { tempY += 16; } if (x > 4) { tempY += 16; } } if (!(curMenu == MENU_PLAY_NEXT_LEVEL && x == menuChoices[curMenu])) { tempY -= 16; } str = malloc(strlen(menuInt[curMenu + 1][x-1])+2); if (curSel[curMenu] == x) { str[0] = '~'; strcpy(str+1, menuInt[curMenu + 1][x-1]); } else { strcpy(str, menuInt[curMenu + 1][x-1]); } JE_dString(VGAScreen, 166, tempY, str, SMALL_FONT_SHAPES); free(str); } } void JE_updateNavScreen(void) { JE_byte x; /* minor issues: */ /* TODO: The scroll to the new planet is too fast, I think */ /* TODO: The starting coordinates for the scrolling effect may be wrong, the yellowish planet below Tyrian isn't visible for as many frames as in the original. */ tempNavX = roundf(navX); tempNavY = roundf(navY); fill_rectangle_xy(VGAScreen, 19, 16, 135, 169, 2); JE_drawNavLines(true); JE_drawNavLines(false); JE_drawDots(); for (x = 0; x < 11; x++) JE_drawPlanet(x); for (x = 0; x < menuChoices[MENU_PLAY_NEXT_LEVEL]-1; x++) { if (mapPlanet[x] > 11) JE_drawPlanet(mapPlanet[x] - 1); } if (mapOrigin > 11) JE_drawPlanet(mapOrigin - 1); blit_sprite(VGAScreenSeg, 0, 0, OPTION_SHAPES, 28); // navigation screen interface if (curSel[MENU_PLAY_NEXT_LEVEL] < menuChoices[MENU_PLAY_NEXT_LEVEL]) { const unsigned int origin_x_offset = sprite(PLANET_SHAPES, PGR[mapOrigin-1]-1)->width / 2, origin_y_offset = sprite(PLANET_SHAPES, PGR[mapOrigin-1]-1)->height / 2, dest_x_offset = sprite(PLANET_SHAPES, PGR[mapPlanet[curSel[MENU_PLAY_NEXT_LEVEL]-2] - 1]-1)->width / 2, dest_y_offset = sprite(PLANET_SHAPES, PGR[mapPlanet[curSel[MENU_PLAY_NEXT_LEVEL]-2] - 1]-1)->height / 2; newNavX = (planetX[mapOrigin-1] - origin_x_offset + planetX[mapPlanet[curSel[MENU_PLAY_NEXT_LEVEL]-2] - 1] - dest_x_offset) / 2.0f; newNavY = (planetY[mapOrigin-1] - origin_y_offset + planetY[mapPlanet[curSel[MENU_PLAY_NEXT_LEVEL]-2] - 1] - dest_y_offset) / 2.0f; } navX = navX + (newNavX - navX) / 2.0f; navY = navY + (newNavY - navY) / 2.0f; if (fabsf(newNavX - navX) < 1) navX = newNavX; if (fabsf(newNavY - navY) < 1) navY = newNavY; fill_rectangle_xy(VGAScreen, 314, 0, 319, 199, 230); if (planetAniWait > 0) { planetAniWait--; } else { planetAni++; if (planetAni > 14) planetAni = 0; planetAniWait = 3; } if (currentDotWait > 0) { currentDotWait--; } else { if (currentDotNum < planetDots[curSel[MENU_PLAY_NEXT_LEVEL]-2]) currentDotNum++; currentDotWait = 5; } } void JE_drawLines(SDL_Surface *surface, JE_boolean dark) { JE_byte x, y; JE_integer tempX, tempY; JE_integer tempX2, tempY2; JE_word tempW, tempW2; tempX2 = -10; tempY2 = 0; tempW = 0; for (x = 0; x < 20; x++) { tempW += 15; tempX = tempW - tempX2; if (tempX > 18 && tempX < 135) { if (dark) JE_rectangle(surface, tempX + 1, 0, tempX + 1, 199, 32+3); else JE_rectangle(surface, tempX, 0, tempX, 199, 32+5); } } tempW = 0; for (y = 0; y < 20; y++) { tempW += 15; tempY = tempW - tempY2; if (tempY > 15 && tempY < 169) { if (dark) JE_rectangle(surface, 0, tempY + 1, 319, tempY + 1, 32+3); else JE_rectangle(surface, 0, tempY, 319, tempY, 32+5); tempW2 = 0; for (x = 0; x < 20; x++) { tempW2 += 15; tempX = tempW2 - tempX2; if (tempX > 18 && tempX < 135) { JE_pix3(surface, tempX, tempY, 32+6); } } } } } /* SYN: This was originally PROC drawlines... yes, there were two different procs called drawlines in different scopes in the same file. Dammit, Jason, why do you do this to me? */ void JE_drawNavLines(JE_boolean dark) { JE_byte x, y; JE_integer tempX, tempY; JE_integer tempX2, tempY2; JE_word tempW, tempW2; tempX2 = tempNavX >> 1; tempY2 = tempNavY >> 1; tempW = 0; for (x = 1; x <= 20; x++) { tempW += 15; tempX = tempW - tempX2; if (tempX > 18 && tempX < 135) { if (dark) JE_rectangle(VGAScreen, tempX + 1, 16, tempX + 1, 169, 1); else JE_rectangle(VGAScreen, tempX, 16, tempX, 169, 5); } } tempW = 0; for (y = 1; y <= 20; y++) { tempW += 15; tempY = tempW - tempY2; if (tempY > 15 && tempY < 169) { if (dark) JE_rectangle(VGAScreen, 19, tempY + 1, 135, tempY + 1, 1); else JE_rectangle(VGAScreen, 8, tempY, 160, tempY, 5); tempW2 = 0; for (x = 0; x < 20; x++) { tempW2 += 15; tempX = tempW2 - tempX2; if (tempX > 18 && tempX < 135) JE_pix3(VGAScreen, tempX, tempY, 7); } } } } void JE_drawDots(void) { JE_byte x, y; JE_integer tempX, tempY; for (x = 0; x < mapPNum; x++) { for (y = 0; y < planetDots[x]; y++) { tempX = planetDotX[x][y] - tempNavX + 66 - 2; tempY = planetDotY[x][y] - tempNavY + 85 - 2; if (tempX > 0 && tempX < 140 && tempY > 0 && tempY < 168) blit_sprite(VGAScreenSeg, tempX, tempY, OPTION_SHAPES, (x == curSel[MENU_PLAY_NEXT_LEVEL]-2 && y < currentDotNum) ? 30 : 29); // navigation dots } } } void JE_drawPlanet(JE_byte planetNum) { JE_integer tempZ = PGR[planetNum]-1, tempX = planetX[planetNum] + 66 - tempNavX - sprite(PLANET_SHAPES, tempZ)->width / 2, tempY = planetY[planetNum] + 85 - tempNavY - sprite(PLANET_SHAPES, tempZ)->height / 2; if (tempX > -7 && tempX + sprite(PLANET_SHAPES, tempZ)->width < 170 && tempY > 0 && tempY < 160) { if (PAni[planetNum]) tempZ += planetAni; blit_sprite_dark(VGAScreenSeg, tempX + 3, tempY + 3, PLANET_SHAPES, tempZ, false); blit_sprite(VGAScreenSeg, tempX, tempY, PLANET_SHAPES, tempZ); // planets } } void JE_scaleBitmap(SDL_Surface *dst_bitmap, const SDL_Surface *src_bitmap, int x1, int y1, int x2, int y2) { /* This function scales one screen and writes the result to another. * The only code that calls it is the code run when you select 'ship * specs' from the main menu. * * Originally this used fixed point math. I haven't seen that in ages :). * But we're well past the point of needing that.*/ assert(src_bitmap != NULL && dst_bitmap != NULL); assert(x1 >= 0 && y1 >= 0 && x2 < src_bitmap->pitch && y2 < src_bitmap->h); int w = x2 - x1 + 1, h = y2 - y1 + 1; float base_skip_w = src_bitmap->pitch / (float)w, base_skip_h = src_bitmap->h / (float)h; float cumulative_skip_w, cumulative_skip_h; //Okay, it's time to loop through and add bits of A to a rectangle in B Uint8 *dst = dst_bitmap->pixels; /* 8-bit specific */ const Uint8 *src, *src_w; /* 8-bit specific */ dst += y1 * dst_bitmap->pitch + x1; cumulative_skip_h = 0; for (int i = 0; i < h; i++) { //this sets src to the beginning of our desired line src = src_w = (Uint8 *)(src_bitmap->pixels) + (src_bitmap->w * ((unsigned int)cumulative_skip_h)); cumulative_skip_h += base_skip_h; cumulative_skip_w = 0; for (int j = 0; j < w; j++) { //copy and move pointers *dst = *src; dst++; cumulative_skip_w += base_skip_w; src = src_w + ((unsigned int)cumulative_skip_w); //value is floored } dst += dst_bitmap->pitch - w; } } void JE_initWeaponView(void) { fill_rectangle_xy(VGAScreen, 8, 8, 144, 177, 0); player[0].sidekick[LEFT_SIDEKICK].x = 72 - 15; player[0].sidekick[LEFT_SIDEKICK].y = 120; player[0].sidekick[RIGHT_SIDEKICK].x = 72 + 15; player[0].sidekick[RIGHT_SIDEKICK].y = 120; player[0].x = 72; player[0].y = 110; player[0].delta_x_shot_move = 0; player[0].delta_y_shot_move = 0; player[0].last_x_explosion_follow = 72; player[0].last_y_explosion_follow = 110; power = 500; lastPower = 500; memset(shotAvail, 0, sizeof(shotAvail)); memset(shotRepeat, 1, sizeof(shotRepeat)); memset(shotMultiPos, 0, sizeof(shotMultiPos)); initialize_starfield(); } void JE_computeDots(void) { JE_integer tempX, tempY; JE_longint distX, distY; JE_byte x, y; for (x = 0; x < mapPNum; x++) { distX = (int)(planetX[mapPlanet[x]-1]) - (int)(planetX[mapOrigin-1]); distY = (int)(planetY[mapPlanet[x]-1]) - (int)(planetY[mapOrigin-1]); tempX = abs(distX) + abs(distY); if (tempX != 0) planetDots[x] = roundf(sqrtf(sqrtf((distX * distX) + (distY * distY)))) - 1; else planetDots[x] = 0; if (planetDots[x] > 10) planetDots[x] = 10; for (y = 0; y < planetDots[x]; y++) { tempX = JE_partWay(planetX[mapOrigin-1], planetX[mapPlanet[x]-1], planetDots[x], y); tempY = JE_partWay(planetY[mapOrigin-1], planetY[mapPlanet[x]-1], planetDots[x], y); /* ??? Why does it use temp? =P */ planetDotX[x][y] = tempX; planetDotY[x][y] = tempY; } } } JE_integer JE_partWay(JE_integer start, JE_integer finish, JE_byte dots, JE_byte dist) { return (finish - start) / (dots + 2) * (dist + 1) + start; } void JE_doShipSpecs(void) { /* This function is called whenever you select 'ship specs' in the * game menu. It draws the nice green tech screen and scales it onto * the main window. To do this we need two temp buffers, so we're going * to use VGAScreen and game_screen for the purpose (making things more * complex than they would be if we just malloc'd, but faster) * * Originally the whole system was pretty oddly designed. So I changed it. * Currently drawFunkyScreen creates the image, scaleInPicture draws it, * and doFunkyScreen ties everything together. Before it was more like * an oddly designed, unreusable, global sharing hierarchy. */ //create the image we want wait_noinput(true, true, true); JE_drawShipSpecs(game_screen, VGAScreen2); //reset VGAScreen2, which we clobbered JE_loadPic(VGAScreen2, 1, false); //draw it JE_playSampleNum(S_SPRING); JE_scaleInPicture(VGAScreen, game_screen); wait_input(true, true, true); } void JE_drawMainMenuHelpText(void) { char tempStr[67]; JE_byte temp; temp = curSel[curMenu] - 2; if (curMenu == MENU_JOYSTICK_CONFIG) // joystick settings menu help { const int help[16] = { 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 24, 11 }; memcpy(tempStr, mainMenuHelp[help[curSel[curMenu] - 2]], sizeof(tempStr)); } else if (curMenu < MENU_PLAY_NEXT_LEVEL || curMenu == MENU_2_PLAYER_ARCADE || curMenu > MENU_1_PLAYER_ARCADE) { memcpy(tempStr, mainMenuHelp[(menuHelp[curMenu][temp])-1], sizeof(tempStr)); } else if (curMenu == MENU_KEYBOARD_CONFIG && curSel[MENU_KEYBOARD_CONFIG] == 10) { memcpy(tempStr, mainMenuHelp[25-1], sizeof(tempStr)); } else if (leftPower || rightPower) { memcpy(tempStr, mainMenuHelp[24-1], sizeof(tempStr)); } else if (temp == menuChoices[curMenu] - 2 || (curMenu == MENU_DATA_CUBES && cubeMax == 0)) { memcpy(tempStr, mainMenuHelp[12-1], sizeof(tempStr)); } else { memcpy(tempStr, mainMenuHelp[17 + curMenu - 3], sizeof(tempStr)); } JE_textShade(VGAScreen, 10, 187, tempStr, 14, 1, DARKEN); } JE_boolean JE_quitRequest(void) { bool quit_selected = true, done = false; JE_clearKeyboard(); JE_wipeKey(); wait_noinput(true, true, true); JE_barShade(VGAScreen, 65, 55, 255, 155); while (!done) { Uint8 col = 8; int colC = 1; do { service_SDL_events(true); setDelay(4); blit_sprite(VGAScreen, 50, 50, OPTION_SHAPES, 35); // message box JE_textShade(VGAScreen, 70, 60, miscText[28], 0, 5, FULL_SHADE); JE_helpBox(VGAScreen, 70, 90, miscText[30], 30); col += colC; if (col > 8 || col < 2) colC = -colC; int temp_x, temp_c; temp_x = 54 + 45 - (JE_textWidth(miscText[9], FONT_SHAPES) / 2); temp_c = quit_selected ? col - 12 : -5; JE_outTextAdjust(VGAScreen, temp_x, 128, miscText[9], 15, temp_c, FONT_SHAPES, true); temp_x = 149 + 45 - (JE_textWidth(miscText[10], FONT_SHAPES) / 2); temp_c = !quit_selected ? col - 12 : -5; JE_outTextAdjust(VGAScreen, temp_x, 128, miscText[10], 15, temp_c, FONT_SHAPES, true); if (has_mouse) { JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); } else { JE_showVGA(); } wait_delay(); push_joysticks_as_keyboard(); service_SDL_events(false); } while (!newkey && !mousedown); if (mousedown) { if (lastmouse_y > 123 && lastmouse_y < 149) { if (lastmouse_x > 56 && lastmouse_x < 142) { quit_selected = true; done = true; } else if (lastmouse_x > 151 && lastmouse_x < 237) { quit_selected = false; done = true; } } mousedown = false; } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_LEFT: case SDL_SCANCODE_RIGHT: case SDL_SCANCODE_TAB: quit_selected = !quit_selected; JE_playSampleNum(S_CURSOR); break; case SDL_SCANCODE_RETURN: case SDL_SCANCODE_SPACE: done = true; break; case SDL_SCANCODE_ESCAPE: quit_selected = false; done = true; break; default: break; } } } JE_playSampleNum(quit_selected ? S_SPRING : S_CLICK); #ifdef WITH_NETWORK if (isNetworkGame && quit_selected) { network_prepare(PACKET_QUIT); network_send(4); // PACKET QUIT network_tyrian_halt(0, true); } #endif return quit_selected; } void JE_genItemMenu(JE_byte itemNum) { menuChoices[MENU_UPGRADE_SUB] = itemAvailMax[itemAvailMap[itemNum - 2] - 1] + 2; temp3 = 2; temp2 = *playeritem_map(&player[0].items, itemNum - 2); strcpy(menuInt[5][0], menuInt[2][itemNum - 1]); for (tempW = 0; tempW < itemAvailMax[itemAvailMap[itemNum - 2] - 1]; tempW++) { temp = itemAvail[itemAvailMap[itemNum - 2] - 1][tempW]; switch (itemNum) { case 2: strcpy(tempStr, ships[temp].name); break; case 3: case 4: strcpy(tempStr, weaponPort[temp].name); break; case 5: strcpy(tempStr, shields[temp].name); break; case 6: strcpy(tempStr, powerSys[temp].name); break; case 7: case 8: strcpy(tempStr, options[temp].name); break; } if (temp == temp2) { temp3 = tempW + 2; } strcpy(menuInt[5][tempW], tempStr); } strcpy(menuInt[5][tempW], miscText[13]); curSel[MENU_UPGRADE_SUB] = temp3; } void JE_scaleInPicture(SDL_Surface *dst, const SDL_Surface *src) { for (int i = 2; i <= 160; i += 2) { if (JE_anyButton()) break; JE_scaleBitmap(dst, src, 160 - i, 0, 160 + i - 1, 100 + roundf(i * 0.625f) - 1); JE_showVGA(); SDL_Delay(1); } } void JE_drawScore(void) { char cl[24]; if (curMenu == MENU_UPGRADE_SUB) { sprintf(cl, "%d", JE_cashLeft()); JE_textShade(VGAScreen, 65, 173, cl, 1, 6, DARKEN); } } void JE_menuFunction(JE_byte select) { JE_byte x; JE_word curSelect; col = 0; colC = -1; JE_playSampleNum(S_CLICK); curSelect = curSel[curMenu]; switch (curMenu) { case MENU_FULL_GAME: switch (select) { case 2: //cubes curMenu = MENU_DATA_CUBES; curSel[MENU_DATA_CUBES] = 2; break; case 3: //shipspecs JE_doShipSpecs(); break; case 4://upgradeship curMenu = MENU_UPGRADES; break; case 5: //options curMenu = MENU_OPTIONS; break; case 6: //nextlevel curMenu = MENU_PLAY_NEXT_LEVEL; newPal = 18; JE_computeDots(); navX = planetX[mapOrigin - 1]; navY = planetY[mapOrigin - 1]; newNavX = navX; newNavY = navY; menuChoices[MENU_PLAY_NEXT_LEVEL] = mapPNum + 2; curSel[MENU_PLAY_NEXT_LEVEL] = 2; strcpy(menuInt[4][0], "Next Level"); for (x = 0; x < mapPNum; x++) { temp = mapPlanet[x]; strcpy(menuInt[4][x + 1], pName[temp - 1]); } strcpy(menuInt[4][x + 1], miscText[5]); break; case 7: //quit if (JE_quitRequest()) { gameLoaded = true; mainLevel = 0; } break; } break; case MENU_UPGRADES: if (select == 9) //done { curMenu = MENU_FULL_GAME; } else // selected item to upgrade { old_items[0] = player[0].items; lastDirection = 1; JE_genItemMenu(select); JE_initWeaponView(); curMenu = MENU_UPGRADE_SUB; lastCurSel = curSel[MENU_UPGRADE_SUB]; player[0].cash = player[0].cash * 2 - JE_cashLeft(); } break; case MENU_OPTIONS: switch (select) { case 2: curMenu = MENU_LOAD_SAVE; performSave = false; quikSave = false; break; case 3: curMenu = MENU_LOAD_SAVE; performSave = true; quikSave = false; break; case 6: curMenu = MENU_JOYSTICK_CONFIG; break; case 7: curMenu = MENU_KEYBOARD_CONFIG; break; case 8: curMenu = MENU_FULL_GAME; break; } break; case MENU_PLAY_NEXT_LEVEL: if (select == menuChoices[MENU_PLAY_NEXT_LEVEL]) //exit { curMenu = MENU_FULL_GAME; newPal = 1; } else { mainLevel = mapSection[curSelect - 2]; jumpSection = true; } break; case MENU_UPGRADE_SUB: if (curSel[MENU_UPGRADE_SUB] < menuChoices[MENU_UPGRADE_SUB]) { // select done curSel[MENU_UPGRADE_SUB] = menuChoices[MENU_UPGRADE_SUB]; } else // if done is selected { JE_playSampleNum(S_ITEM); player[0].cash = JE_cashLeft(); curMenu = MENU_UPGRADES; } break; case MENU_KEYBOARD_CONFIG: if (curSelect == 10) /* reset to defaults */ { memcpy(keySettings, defaultKeySettings, sizeof(keySettings)); } else if (curSelect == 11) /* done */ { curMenu = isNetworkGame ? MENU_LIMITED_OPTIONS : MENU_OPTIONS; } else /* change key */ { temp2 = 254; int tempY = 38 + (curSelect - 2) * 12; JE_textShade(VGAScreen, 236, tempY, SDL_GetScancodeName(keySettings[curSelect-2]), (temp2 / 16), (temp2 % 16) - 8, DARKEN); JE_showVGA(); wait_noinput(true, true, true); col = 248; colC = 1; do { setDelay(1); col += colC; if (col < 243 || col > 248) { colC *= -1; } JE_rectangle(VGAScreen, 230, tempY - 2, 300, tempY + 7, col); poll_joysticks(); service_SDL_events(true); JE_showVGA(); wait_delay(); } while (!newkey && !mousedown && !joydown); if (newkey) { // already used? then swap for (uint i = 0; i < COUNTOF(keySettings); ++i) { if (keySettings[i] == lastkey_scan) { keySettings[i] = keySettings[curSelect-2]; break; } } if (lastkey_scan != SDL_SCANCODE_ESCAPE && // reserved for menu lastkey_scan != SDL_SCANCODE_F11 && // reserved for gamma lastkey_scan != SDL_SCANCODE_P) // reserved for pause { JE_playSampleNum(S_CLICK); keySettings[curSelect-2] = lastkey_scan; ++curSelect; } JE_wipeKey(); } } break; case MENU_LOAD_SAVE: if (curSelect == 13) { if (quikSave) { curMenu = oldMenu; newPal = oldPal; } else { curMenu = MENU_OPTIONS; } } else { if (twoPlayerMode) temp = 11; else temp = 0; JE_operation(curSelect - 1 + temp); if (quikSave) { curMenu = oldMenu; newPal = oldPal; } } break; case MENU_DATA_CUBES: if (curSelect == menuChoices[curMenu]) { curMenu = MENU_FULL_GAME; newPal = 1; } else { if (cubeMax > 0) { firstMenu9 = true; curMenu = MENU_DATA_CUBE_SUB; yLoc = 0; yChg = 0; currentCube = curSel[MENU_DATA_CUBES] - 2; } else { curMenu = MENU_FULL_GAME; newPal = 1; } } break; case MENU_DATA_CUBE_SUB: curMenu = MENU_DATA_CUBES; break; case MENU_2_PLAYER_ARCADE: switch (curSel[curMenu]) { case 2: mainLevel = mapSection[mapPNum-1]; jumpSection = true; break; case 3: case 4: JE_playSampleNum(S_CURSOR); int temp = curSel[curMenu] - 3; do { if (joysticks == 0) inputDevice[temp == 0 ? 1 : 0] = inputDevice[temp]; // swap controllers if (inputDevice[temp] >= 2 + joysticks) inputDevice[temp] = 1; else inputDevice[temp]++; } while (inputDevice[temp] == inputDevice[temp == 0 ? 1 : 0]); break; case 5: curMenu = MENU_OPTIONS; break; case 6: if (JE_quitRequest()) { gameLoaded = true; mainLevel = 0; } break; } break; case MENU_1_PLAYER_ARCADE: switch (curSel[curMenu]) { case 2: mainLevel = mapSection[mapPNum-1]; jumpSection = true; break; case 3: curMenu = isNetworkGame ? MENU_LIMITED_OPTIONS : MENU_OPTIONS; break; case 4: if (JE_quitRequest()) { gameLoaded = true; mainLevel = 0; } break; } break; case MENU_LIMITED_OPTIONS: switch (select) { case 2: curMenu = MENU_JOYSTICK_CONFIG; break; case 3: curMenu = MENU_KEYBOARD_CONFIG; break; case 6: curMenu = MENU_1_PLAYER_ARCADE; break; } break; case MENU_JOYSTICK_CONFIG: if (joysticks == 0 && select != 17) break; switch (select) { case 2: joystick_config++; joystick_config %= joysticks; break; case 3: joystick[joystick_config].analog = !joystick[joystick_config].analog; break; case 4: if (joystick[joystick_config].analog) { joystick[joystick_config].sensitivity++; joystick[joystick_config].sensitivity %= 11; } break; case 5: if (joystick[joystick_config].analog) { joystick[joystick_config].threshold++; joystick[joystick_config].threshold %= 11; } break; case 16: reset_joystick_assignments(joystick_config); break; case 17: curMenu = isNetworkGame ? MENU_LIMITED_OPTIONS : MENU_OPTIONS; break; default: if (joysticks == 0) break; // int temp = 254; // JE_textShade(VGAScreen, 236, 38 + i * 8, value, temp / 16, temp % 16 - 8, DARKEN); JE_rectangle(VGAScreen, 235, 21 + select * 8, 310, 30 + select * 8, 248); Joystick_assignment temp; if (detect_joystick_assignment(joystick_config, &temp)) { // if the detected assignment was already set, unset it for (uint i = 0; i < COUNTOF(*joystick->assignment); i++) { if (joystick_assignment_cmp(&temp, &joystick[joystick_config].assignment[select - 6][i])) { joystick[joystick_config].assignment[select - 6][i].type = NONE; goto joystick_assign_done; } } // if there is an empty assignment, set it for (uint i = 0; i < COUNTOF(*joystick->assignment); i++) { if (joystick[joystick_config].assignment[select - 6][i].type == NONE) { joystick[joystick_config].assignment[select - 6][i] = temp; goto joystick_assign_done; } } // if no assignments are empty, shift them all forward and set the last one for (uint i = 0; i < COUNTOF(*joystick->assignment); i++) { if (i == COUNTOF(*joystick->assignment) - 1) joystick[joystick_config].assignment[select - 6][i] = temp; else joystick[joystick_config].assignment[select - 6][i] = joystick[joystick_config].assignment[select - 6][i + 1]; } joystick_assign_done: curSelect++; poll_joysticks(); } } break; case MENU_SUPER_TYRIAN: switch (curSel[curMenu]) { case 2: mainLevel = mapSection[mapPNum-1]; jumpSection = true; break; case 3: JE_doShipSpecs(); break; case 4: curMenu = MENU_OPTIONS; break; case 5: if (JE_quitRequest()) { if (isNetworkGame) { JE_tyrianHalt(0); } gameLoaded = true; mainLevel = 0; } } break; } old_items[0] = player[0].items; } void JE_drawShipSpecs(SDL_Surface * screen, SDL_Surface * temp_screen) { /* In this function we create our ship description image. * * We use a temp screen for convenience. Bad design maybe (Jason!), * but it'll be okay (and the alternative is malloc/a large stack) */ int temp_x = 0, temp_y = 0, temp_index; Uint8 *src, *dst; //first, draw the text and other assorted flavoring. JE_clr256(screen); JE_drawLines(screen, true); JE_drawLines(screen, false); JE_rectangle(screen, 0, 0, 319, 199, 37); JE_rectangle(screen, 1, 1, 318, 198, 35); verticalHeight = 9; JE_outText(screen, 10, 2, ships[player[0].items.ship].name, 12, 3); JE_helpBox(screen, 100, 20, shipInfo[player[0].items.ship-1][0], 40); JE_helpBox(screen, 100, 100, shipInfo[player[0].items.ship-1][1], 40); verticalHeight = 7; JE_outText(screen, JE_fontCenter(miscText[4], TINY_FONT), 190, miscText[4], 12, 2); //now draw the green ship over that. //This hardcoded stuff is for positioning our little ship graphic if (player[0].items.ship > 90) { temp_index = 32; } else if (player[0].items.ship > 0) { temp_index = ships[player[0].items.ship].bigshipgraphic; } else { temp_index = ships[old_items[0].ship].bigshipgraphic; } switch (temp_index) { case 32: temp_x = 35; temp_y = 33; break; case 28: temp_x = 31; temp_y = 36; break; case 33: temp_x = 31; temp_y = 35; break; default: assert(0); } temp_x -= 30; //draw the ship into our temp buffer. JE_clr256(temp_screen); blit_sprite(temp_screen, temp_x, temp_y, OPTION_SHAPES, temp_index - 1); // ship illustration /* But wait! Our ship is fully colored, not green! * With a little work we could get the sprite dimensions and greenify * the area it resides in. For now, let's just greenify the (almost * entirely) black screen. * We can't work in place. In fact we'll need to overlay the result * To avoid our temp screen dependence this has been rewritten to * only write one line at a time.*/ dst = screen->pixels; src = temp_screen->pixels; for (int y = 0; y < screen->h; y++) { for (int x = 0; x < screen->pitch; x++) { int avg = 0; if (y > 0) avg += *(src - screen->pitch) & 0x0f; if (y < screen->h - 1) avg += *(src + screen->pitch) & 0x0f; if (x > 0) avg += *(src - 1) & 0x0f; if (x < screen->pitch - 1) avg += *(src + 1) & 0x0f; avg /= 4; if ((*src & 0x0f) > avg) *dst = (*src & 0x0f) | 0xc0; //else // *dst = 0; src++; dst++; } } } void JE_weaponSimUpdate(void) { char buf[32]; JE_weaponViewFrame(); if ((curSel[MENU_UPGRADES] == 3 || curSel[MENU_UPGRADES] == 4) && // front or rear weapon curSel[MENU_UPGRADE_SUB] < menuChoices[MENU_UPGRADE_SUB] && // not "Done" itemAvail[itemAvailMap[curSel[MENU_UPGRADES]-2]-1][curSel[MENU_UPGRADE_SUB]-2] != 0) // not "None" { if (leftPower) { sprintf(buf, "%d", downgradeCost); JE_outText(VGAScreen, 26, 137, buf, 1, 4); } else { blit_sprite(VGAScreenSeg, 24, 149, OPTION_SHAPES, 13); // downgrade disabled } if (rightPower) { if (!rightPowerAfford) { sprintf(buf, "%d", upgradeCost); JE_outText(VGAScreen, 108, 137, buf, 7, 4); blit_sprite(VGAScreenSeg, 119, 149, OPTION_SHAPES, 14); // upgrade disabled } else { sprintf(buf, "%d", upgradeCost); JE_outText(VGAScreen, 108, 137, buf, 1, 4); } } else { blit_sprite(VGAScreenSeg, 119, 149, OPTION_SHAPES, 14); // upgrade disabled } temp = player[0].items.weapon[curSel[MENU_UPGRADES]-3].power; for (int x = 1; x <= temp; x++) { fill_rectangle_xy(VGAScreen, 39 + x * 6, 151, 39 + x * 6 + 4, 151, 251); JE_pix(VGAScreen, 39 + x * 6, 151, 252); fill_rectangle_xy(VGAScreen, 39 + x * 6, 152, 39 + x * 6 + 4, 164, 250); fill_rectangle_xy(VGAScreen, 39 + x * 6, 165, 39 + x * 6 + 4, 165, 249); } sprintf(buf, "POWER: %d", temp); JE_outText(VGAScreen, 58, 137, buf, 15, 4); } else { leftPower = false; rightPower = false; blit_sprite(VGAScreenSeg, 20, 146, OPTION_SHAPES, 17); // hide power level interface } JE_drawItem(1, player[0].items.ship, player[0].x - 5, player[0].y - 7); } void JE_weaponViewFrame(void) { fill_rectangle_xy(VGAScreen, 8, 8, 143, 182, 0); /* JE: (* Port Configuration Display *) (* drawportconfigbuttons;*/ update_and_draw_starfield(VGAScreen, 1); mouseX = player[0].x; mouseY = player[0].y; // create shots in weapon simulator for (uint i = 0; i < 2; ++i) { if (shotRepeat[i] > 0) { --shotRepeat[i]; } else { const uint item = player[0].items.weapon[i].id, item_power = player[0].items.weapon[i].power - 1, item_mode = (i == REAR_WEAPON) ? player[0].weapon_mode - 1 : 0; b = player_shot_create(item, i, player[0].x, player[0].y, mouseX, mouseY, weaponPort[item].op[item_mode][item_power], 1); } } if (options[player[0].items.sidekick[LEFT_SIDEKICK]].wport > 0) { if (shotRepeat[SHOT_LEFT_SIDEKICK] > 0) { --shotRepeat[SHOT_LEFT_SIDEKICK]; } else { const uint item = player[0].items.sidekick[LEFT_SIDEKICK]; const int x = player[0].sidekick[LEFT_SIDEKICK].x, y = player[0].sidekick[LEFT_SIDEKICK].y; b = player_shot_create(options[item].wport, SHOT_LEFT_SIDEKICK, x, y, mouseX, mouseY, options[item].wpnum, 1); } } if (options[player[0].items.sidekick[RIGHT_SIDEKICK]].tr == 2) { player[0].sidekick[RIGHT_SIDEKICK].x = player[0].x; player[0].sidekick[RIGHT_SIDEKICK].y = MAX(10, player[0].y - 20); } else { player[0].sidekick[RIGHT_SIDEKICK].x = 72 + 15; player[0].sidekick[RIGHT_SIDEKICK].y = 120; } if (options[player[0].items.sidekick[RIGHT_SIDEKICK]].wport > 0) { if (shotRepeat[SHOT_RIGHT_SIDEKICK] > 0) { --shotRepeat[SHOT_RIGHT_SIDEKICK]; } else { const uint item = player[0].items.sidekick[RIGHT_SIDEKICK]; const int x = player[0].sidekick[RIGHT_SIDEKICK].x, y = player[0].sidekick[RIGHT_SIDEKICK].y; b = player_shot_create(options[item].wport, SHOT_RIGHT_SIDEKICK, x, y, mouseX, mouseY, options[item].wpnum, 1); } } simulate_player_shots(); blit_sprite(VGAScreenSeg, 0, 0, OPTION_SHAPES, 12); // upgrade interface /*========================Power Bar=========================*/ power += powerAdd; if (power > 900) power = 900; temp = power / 10; for (temp = 147 - temp; temp <= 146; temp++) { temp2 = 113 + (146 - temp) / 9 + 2; temp3 = (temp + 1) % 6; if (temp3 == 1) temp2 += 3; else if (temp3 != 0) temp2 += 2; JE_pix(VGAScreen, 141, temp, temp2 - 3); JE_pix(VGAScreen, 142, temp, temp2 - 3); JE_pix(VGAScreen, 143, temp, temp2 - 2); JE_pix(VGAScreen, 144, temp, temp2 - 1); fill_rectangle_xy(VGAScreen, 145, temp, 149, temp, temp2); if (temp2 - 3 < 112) temp2++; } temp = 147 - (power / 10); temp2 = 113 + (146 - temp) / 9 + 4; JE_pix(VGAScreen, 141, temp - 1, temp2 - 1); JE_pix(VGAScreen, 142, temp - 1, temp2 - 1); JE_pix(VGAScreen, 143, temp - 1, temp2 - 1); JE_pix(VGAScreen, 144, temp - 1, temp2 - 1); fill_rectangle_xy(VGAScreen, 145, temp-1, 149, temp-1, temp2); lastPower = temp; //JE_waitFrameCount(); TODO: didn't do anything? } opentyrian-2.1.20221123/src/game_menu.h000066400000000000000000000040711432005211200173240ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef GAME_MENU_H #define GAME_MENU_H #include "helptext.h" #include "opentyr.h" typedef JE_byte JE_MenuChoiceType[MENU_MAX]; JE_longint JE_cashLeft(void); void JE_itemScreen(void); void load_cubes(void); bool load_cube(int cube_slot, int cube_index); void JE_drawItem(JE_byte itemType, JE_word itemNum, JE_word x, JE_word y); void JE_drawMenuHeader(void); void JE_drawMenuChoices(void); void JE_updateNavScreen(void); void JE_drawNavLines(JE_boolean dark); void JE_drawLines(SDL_Surface *surface, JE_boolean dark); void JE_drawDots(void); void JE_drawPlanet(JE_byte planetNum); void draw_ship_illustration(void); void JE_scaleBitmap(SDL_Surface *dst, const SDL_Surface *src, int x1, int y1, int x2, int y2); void JE_initWeaponView(void); void JE_computeDots(void); JE_integer JE_partWay(JE_integer start, JE_integer finish, JE_byte dots, JE_byte dist); void JE_doShipSpecs(void); void JE_drawMainMenuHelpText(void); JE_boolean JE_quitRequest(void); void JE_genItemMenu(JE_byte itemnum); void JE_scaleInPicture(SDL_Surface *dst, const SDL_Surface *src); void JE_drawScore(void); void JE_menuFunction(JE_byte select); void JE_drawShipSpecs(SDL_Surface *, SDL_Surface *); void JE_weaponSimUpdate(void); void JE_weaponViewFrame(void); #endif // GAME_MENU_H opentyrian-2.1.20221123/src/helptext.c000066400000000000000000000321721432005211200172220ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "helptext.h" #include "config.h" #include "episodes.h" #include "file.h" #include "fonthand.h" #include "menus.h" #include "opentyr.h" #include "video.h" #include #include const JE_byte menuHelp[MENU_MAX][11] = /* [1..maxmenu, 1..11] */ { { 1, 34, 2, 3, 4, 5, 0, 0, 0, 0, 0 }, { 6, 7, 8, 9, 10, 11, 11, 12, 0, 0, 0 }, { 13, 14, 15, 15, 16, 17, 12, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 4, 30, 30, 3, 5, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 16, 17, 15, 15, 12, 0, 0, 0, 0, 0, 0 }, { 31, 31, 31, 31, 32, 12, 0, 0, 0, 0, 0 }, { 4, 34, 3, 5, 0, 0, 0, 0, 0, 0, 0 } }; JE_byte verticalHeight = 7; JE_byte helpBoxColor = 12; JE_byte helpBoxBrightness = 1; JE_byte helpBoxShadeType = FULL_SHADE; char helpTxt[39][231]; /* [1..39] of string [230] */ char pName[21][16]; /* [1..21] of string [15] */ char miscText[HELPTEXT_MISCTEXT_COUNT][42]; /* [1..68] of string [41] */ char miscTextB[HELPTEXT_MISCTEXTB_COUNT][HELPTEXT_MISCTEXTB_SIZE]; /* [1..5] of string [10] */ char keyName[8][18]; /* [1..8] of string [17] */ char menuText[7][HELPTEXT_MENUTEXT_SIZE]; /* [1..7] of string [20] */ char outputs[9][31]; /* [1..9] of string [30] */ char topicName[6][21]; /* [1..6] of string [20] */ char mainMenuHelp[HELPTEXT_MAINMENUHELP_COUNT][66]; /* [1..34] of string [65] */ char inGameText[6][21]; /* [1..6] of string [20] */ char detailLevel[6][13]; /* [1..6] of string [12] */ char gameSpeedText[5][13]; /* [1..5] of string [12] */ char inputDevices[3][13]; /* [1..3] of string [12] */ char networkText[HELPTEXT_NETWORKTEXT_COUNT][HELPTEXT_NETWORKTEXT_SIZE]; /* [1..4] of string [20] */ char difficultyNameB[11][21]; /* [0..9] of string [20] */ char joyButtonNames[5][21]; /* [1..5] of string [20] */ char superShips[HELPTEXT_SUPERSHIPS_COUNT][26]; /* [0..10] of string [25] */ char specialName[HELPTEXT_SPECIALNAME_COUNT][10]; /* [1..9] of string [9] */ char destructHelp[25][22]; /* [1..25] of string [21] */ char weaponNames[17][17]; /* [1..17] of string [16] */ char destructModeName[DESTRUCT_MODES][13]; /* [1..destructmodes] of string [12] */ char shipInfo[HELPTEXT_SHIPINFO_COUNT][2][256]; /* [1..13, 1..2] of string */ char menuInt[MENU_MAX+1][11][18]; /* [0..14, 1..11] of string [17] */ static void decrypt_string(char *s, size_t len) { static const unsigned char crypt_key[] = { 204, 129, 63, 255, 71, 19, 25, 62, 1, 99 }; if (len == 0) return; for (size_t i = len - 1; ; --i) { s[i] ^= crypt_key[i % sizeof(crypt_key)]; if (i == 0) break; s[i] ^= s[i - 1]; } } void read_encrypted_pascal_string(char *s, size_t size, FILE *f) { Uint8 len; char buffer[255]; fread_u8_die(&len, 1, f); fread_die(buffer, 1, len, f); if (size == 0) return; decrypt_string(buffer, len); assert(len < size); len = MIN(len, size - 1); memcpy(s, buffer, len); s[len] = '\0'; } void skip_pascal_string(FILE *f) { Uint8 len; char buffer[255]; fread_u8_die(&len, 1, f); fread_die(buffer, 1, len, f); } void JE_helpBox(SDL_Surface *screen, int x, int y, const char *message, unsigned int boxwidth) { JE_byte startpos, endpos, pos; JE_boolean endstring; char substring[256]; if (strlen(message) == 0) { return; } pos = 1; endpos = 0; endstring = false; do { startpos = endpos + 1; do { endpos = pos; do { pos++; if (pos == strlen(message)) { endstring = true; if ((unsigned)(pos - startpos) < boxwidth) { endpos = pos + 1; } } } while (!(message[pos-1] == ' ' || endstring)); } while (!((unsigned)(pos - startpos) > boxwidth || endstring)); SDL_strlcpy(substring, message + startpos - 1, MIN((size_t)(endpos - startpos + 1), sizeof(substring))); JE_textShade(screen, x, y, substring, helpBoxColor, helpBoxBrightness, helpBoxShadeType); y += verticalHeight; } while (!endstring); if (endpos != pos + 1) { JE_textShade(screen, x, y, message + endpos, helpBoxColor, helpBoxBrightness, helpBoxShadeType); } helpBoxColor = 12; helpBoxShadeType = FULL_SHADE; } void JE_HBox(SDL_Surface *screen, int x, int y, unsigned int messagenum, unsigned int boxwidth) { JE_helpBox(screen, x, y, helpTxt[messagenum-1], boxwidth); } void JE_loadHelpText(void) { const unsigned int menuInt_entries[MENU_MAX + 1] = { -1, 7, 9, 8, -1, -1, 11, -1, -1, -1, 6, 4, 6, 7, 5 }; FILE *f = dir_fopen_die(data_dir(), "tyrian.hdt", "rb"); fread_s32_die(&episode1DataLoc, 1, f); /*Online Help*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(helpTxt); ++i) read_encrypted_pascal_string(helpTxt[i], sizeof(helpTxt[i]), f); skip_pascal_string(f); /*Planet names*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(pName); ++i) read_encrypted_pascal_string(pName[i], sizeof(pName[i]), f); skip_pascal_string(f); /*Miscellaneous text*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(miscText); ++i) read_encrypted_pascal_string(miscText[i], sizeof(miscText[i]), f); skip_pascal_string(f); /*Little Miscellaneous text*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(miscTextB); ++i) read_encrypted_pascal_string(miscTextB[i], sizeof(miscTextB[i]), f); skip_pascal_string(f); /*Key names*/ skip_pascal_string(f); for (unsigned int i = 0; i < menuInt_entries[6]; ++i) read_encrypted_pascal_string(menuInt[6][i], sizeof(menuInt[6][i]), f); skip_pascal_string(f); /*Main Menu*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(menuText); ++i) read_encrypted_pascal_string(menuText[i], sizeof(menuText[i]), f); skip_pascal_string(f); /*Event text*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(outputs); ++i) read_encrypted_pascal_string(outputs[i], sizeof(outputs[i]), f); skip_pascal_string(f); /*Help topics*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(topicName); ++i) read_encrypted_pascal_string(topicName[i], sizeof(topicName[i]), f); skip_pascal_string(f); /*Main Menu Help*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(mainMenuHelp); ++i) read_encrypted_pascal_string(mainMenuHelp[i], sizeof(mainMenuHelp[i]), f); skip_pascal_string(f); /*Menu 1 - Main*/ skip_pascal_string(f); for (unsigned int i = 0; i < menuInt_entries[1]; ++i) read_encrypted_pascal_string(menuInt[1][i], sizeof(menuInt[1][i]), f); skip_pascal_string(f); /*Menu 2 - Items*/ skip_pascal_string(f); for (unsigned int i = 0; i < menuInt_entries[2]; ++i) read_encrypted_pascal_string(menuInt[2][i], sizeof(menuInt[2][i]), f); skip_pascal_string(f); /*Menu 3 - Options*/ skip_pascal_string(f); for (unsigned int i = 0; i < menuInt_entries[3]; ++i) read_encrypted_pascal_string(menuInt[3][i], sizeof(menuInt[3][i]), f); skip_pascal_string(f); /*InGame Menu*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(inGameText); ++i) read_encrypted_pascal_string(inGameText[i], sizeof(inGameText[i]), f); skip_pascal_string(f); /*Detail Level*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(detailLevel); ++i) read_encrypted_pascal_string(detailLevel[i], sizeof(detailLevel[i]), f); skip_pascal_string(f); /*Game speed text*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(gameSpeedText); ++i) read_encrypted_pascal_string(gameSpeedText[i], sizeof(gameSpeedText[i]), f); skip_pascal_string(f); // episode names skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(episode_name); ++i) read_encrypted_pascal_string(episode_name[i], sizeof(episode_name[i]), f); skip_pascal_string(f); // difficulty names skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(difficulty_name); ++i) read_encrypted_pascal_string(difficulty_name[i], sizeof(difficulty_name[i]), f); skip_pascal_string(f); // gameplay mode names skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(gameplay_name); ++i) read_encrypted_pascal_string(gameplay_name[i], sizeof(gameplay_name[i]), f); skip_pascal_string(f); /*Menu 10 - 2Player Main*/ skip_pascal_string(f); for (unsigned int i = 0; i < menuInt_entries[10]; ++i) read_encrypted_pascal_string(menuInt[10][i], sizeof(menuInt[10][i]), f); skip_pascal_string(f); /*Input Devices*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(inputDevices); ++i) read_encrypted_pascal_string(inputDevices[i], sizeof(inputDevices[i]), f); skip_pascal_string(f); /*Network text*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(networkText); ++i) read_encrypted_pascal_string(networkText[i], sizeof(networkText[i]), f); skip_pascal_string(f); /*Menu 11 - 2Player Network*/ skip_pascal_string(f); for (unsigned int i = 0; i < menuInt_entries[11]; ++i) read_encrypted_pascal_string(menuInt[11][i], sizeof(menuInt[11][i]), f); skip_pascal_string(f); /*HighScore Difficulty Names*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(difficultyNameB); ++i) read_encrypted_pascal_string(difficultyNameB[i], sizeof(difficultyNameB[i]), f); skip_pascal_string(f); /*Menu 12 - Network Options*/ skip_pascal_string(f); for (unsigned int i = 0; i < menuInt_entries[12]; ++i) read_encrypted_pascal_string(menuInt[12][i], sizeof(menuInt[12][i]), f); skip_pascal_string(f); /*Menu 13 - Joystick*/ skip_pascal_string(f); for (unsigned int i = 0; i < menuInt_entries[13]; ++i) read_encrypted_pascal_string(menuInt[13][i], sizeof(menuInt[13][i]), f); skip_pascal_string(f); /*Joystick Button Assignments*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(joyButtonNames); ++i) read_encrypted_pascal_string(joyButtonNames[i], sizeof(joyButtonNames[i]), f); skip_pascal_string(f); /*SuperShips - For Super Arcade Mode*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(superShips); ++i) read_encrypted_pascal_string(superShips[i], sizeof(superShips[i]), f); skip_pascal_string(f); /*SuperShips - For Super Arcade Mode*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(specialName); ++i) read_encrypted_pascal_string(specialName[i], sizeof(specialName[i]), f); skip_pascal_string(f); /*Secret DESTRUCT game*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(destructHelp); ++i) read_encrypted_pascal_string(destructHelp[i], sizeof(destructHelp[i]), f); skip_pascal_string(f); /*Secret DESTRUCT weapons*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(weaponNames); ++i) read_encrypted_pascal_string(weaponNames[i], sizeof(weaponNames[i]), f); skip_pascal_string(f); /*Secret DESTRUCT modes*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(destructModeName); ++i) read_encrypted_pascal_string(destructModeName[i], sizeof(destructModeName[i]), f); skip_pascal_string(f); /*NEW: Ship Info*/ skip_pascal_string(f); for (unsigned int i = 0; i < COUNTOF(shipInfo); ++i) { read_encrypted_pascal_string(shipInfo[i][0], sizeof(shipInfo[i][0]), f); read_encrypted_pascal_string(shipInfo[i][1], sizeof(shipInfo[i][1]), f); } skip_pascal_string(f); /*Menu 12 - Network Options*/ skip_pascal_string(f); for (unsigned int i = 0; i < menuInt_entries[14]; ++i) read_encrypted_pascal_string(menuInt[14][i], sizeof(menuInt[14][i]), f); fclose(f); } opentyrian-2.1.20221123/src/helptext.h000066400000000000000000000054421432005211200172270ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef HELPTEXT_H #define HELPTEXT_H #include "opentyr.h" #include "SDL.h" #include #define MENU_MAX 14 #define DESTRUCT_MODES 5 extern const JE_byte menuHelp[MENU_MAX][11]; /* [1..14, 1..11] */ extern JE_byte verticalHeight; extern JE_byte helpBoxColor, helpBoxBrightness, helpBoxShadeType; #define HELPTEXT_MISCTEXT_COUNT 68 #define HELPTEXT_MISCTEXTB_COUNT 5 #define HELPTEXT_MISCTEXTB_SIZE 11 #define HELPTEXT_MENUTEXT_SIZE 21 #define HELPTEXT_MAINMENUHELP_COUNT 34 #define HELPTEXT_NETWORKTEXT_COUNT 4 #define HELPTEXT_NETWORKTEXT_SIZE 22 #define HELPTEXT_SUPERSHIPS_COUNT 11 #define HELPTEXT_SPECIALNAME_COUNT 9 #define HELPTEXT_SHIPINFO_COUNT 13 extern char helpTxt[39][231]; extern char pName[21][16]; extern char miscText[HELPTEXT_MISCTEXT_COUNT][42]; extern char miscTextB[HELPTEXT_MISCTEXTB_COUNT][HELPTEXT_MISCTEXTB_SIZE]; extern char keyName[8][18]; extern char menuText[7][HELPTEXT_MENUTEXT_SIZE]; extern char outputs[9][31]; extern char topicName[6][21]; extern char mainMenuHelp[HELPTEXT_MAINMENUHELP_COUNT][66]; extern char inGameText[6][21]; extern char detailLevel[6][13]; extern char gameSpeedText[5][13]; extern char inputDevices[3][13]; extern char networkText[HELPTEXT_NETWORKTEXT_COUNT][HELPTEXT_NETWORKTEXT_SIZE]; extern char difficultyNameB[11][21]; extern char joyButtonNames[5][21]; extern char superShips[HELPTEXT_SUPERSHIPS_COUNT][26]; extern char specialName[HELPTEXT_SPECIALNAME_COUNT][10]; extern char destructHelp[25][22]; extern char weaponNames[17][17]; extern char destructModeName[DESTRUCT_MODES][13]; extern char shipInfo[HELPTEXT_SHIPINFO_COUNT][2][256]; extern char menuInt[MENU_MAX+1][11][18]; void read_encrypted_pascal_string(char *s, size_t size, FILE *f); void skip_pascal_string(FILE *f); void JE_helpBox(SDL_Surface *screen, int x, int y, const char *message, unsigned int boxwidth); void JE_HBox(SDL_Surface *screen, int x, int y, unsigned int messagenum, unsigned int boxwidth); void JE_loadHelpText(void); #endif /* HELPTEXT_H */ opentyrian-2.1.20221123/src/joystick.c000066400000000000000000000405721432005211200172270ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "joystick.h" #include "config.h" #include "config_file.h" #include "file.h" #include "keyboard.h" #include "nortsong.h" #include "opentyr.h" #include "params.h" #include "varz.h" #include "video.h" #include #include #include int joystick_axis_threshold(int j, int value); int check_assigned(SDL_Joystick *joystick_handle, const Joystick_assignment assignment[2]); const char *assignment_to_code(const Joystick_assignment *assignment); void code_to_assignment(Joystick_assignment *assignment, const char *buffer); int joystick_repeat_delay = 300; // milliseconds, repeat delay for buttons bool joydown = false; // any joystick buttons down, updated by poll_joysticks() bool ignore_joystick = false; int joysticks = 0; Joystick *joystick = NULL; static const int joystick_analog_max = 32767; // eliminates axis movement below the threshold int joystick_axis_threshold(int j, int value) { assert(j < joysticks); bool negative = value < 0; if (negative) value = -value; if (value <= joystick[j].threshold * 1000) return 0; value -= joystick[j].threshold * 1000; return negative ? -value : value; } // converts joystick axis to sane Tyrian-usable value (based on sensitivity) int joystick_axis_reduce(int j, int value) { assert(j < joysticks); value = joystick_axis_threshold(j, value); if (value == 0) return 0; return value / (3000 - 200 * joystick[j].sensitivity); } // converts analog joystick axes to an angle // returns false if axes are centered (there is no angle) bool joystick_analog_angle(int j, float *angle) { assert(j < joysticks); float x = joystick_axis_threshold(j, joystick[j].x), y = joystick_axis_threshold(j, joystick[j].y); if (x != 0) { *angle += atanf(-y / x); *angle += (x < 0) ? -M_PI_2 : M_PI_2; return true; } else if (y != 0) { *angle += y < 0 ? M_PI : 0; return true; } return false; } /* gives back value 0..joystick_analog_max indicating that one of the assigned * buttons has been pressed or that one of the assigned axes/hats has been moved * in the assigned direction */ int check_assigned(SDL_Joystick *joystick_handle, const Joystick_assignment assignment[2]) { int result = 0; for (int i = 0; i < 2; i++) { int temp = 0; switch (assignment[i].type) { case NONE: continue; case AXIS: temp = SDL_JoystickGetAxis(joystick_handle, assignment[i].num); if (assignment[i].negative_axis) temp = -temp; break; case BUTTON: temp = SDL_JoystickGetButton(joystick_handle, assignment[i].num) == 1 ? joystick_analog_max : 0; break; case HAT: temp = SDL_JoystickGetHat(joystick_handle, assignment[i].num); if (assignment[i].x_axis) temp &= SDL_HAT_LEFT | SDL_HAT_RIGHT; else temp &= SDL_HAT_UP | SDL_HAT_DOWN; if (assignment[i].negative_axis) temp &= SDL_HAT_LEFT | SDL_HAT_UP; else temp &= SDL_HAT_RIGHT | SDL_HAT_DOWN; temp = temp ? joystick_analog_max : 0; break; } if (temp > result) result = temp; } return result; } // updates joystick state void poll_joystick(int j) { assert(j < joysticks); if (joystick[j].handle == NULL) return; SDL_JoystickUpdate(); // indicates that a direction/action was pressed since last poll joystick[j].input_pressed = false; // indicates that an direction/action has been held long enough to fake a repeat press bool repeat = joystick[j].joystick_delay < SDL_GetTicks(); // update direction state for (uint d = 0; d < COUNTOF(joystick[j].direction); d++) { bool old = joystick[j].direction[d]; joystick[j].analog_direction[d] = check_assigned(joystick[j].handle, joystick[j].assignment[d]); joystick[j].direction[d] = joystick[j].analog_direction[d] > (joystick_analog_max / 2); joydown |= joystick[j].direction[d]; joystick[j].direction_pressed[d] = joystick[j].direction[d] && (!old || repeat); joystick[j].input_pressed |= joystick[j].direction_pressed[d]; } joystick[j].x = -joystick[j].analog_direction[3] + joystick[j].analog_direction[1]; joystick[j].y = -joystick[j].analog_direction[0] + joystick[j].analog_direction[2]; // update action state for (uint d = 0; d < COUNTOF(joystick[j].action); d++) { bool old = joystick[j].action[d]; joystick[j].action[d] = check_assigned(joystick[j].handle, joystick[j].assignment[d + COUNTOF(joystick[j].direction)]) > (joystick_analog_max / 2); joydown |= joystick[j].action[d]; joystick[j].action_pressed[d] = joystick[j].action[d] && (!old || repeat); joystick[j].input_pressed |= joystick[j].action_pressed[d]; } joystick[j].confirm = joystick[j].action[0] || joystick[j].action[4]; joystick[j].cancel = joystick[j].action[1] || joystick[j].action[5]; // if new input, reset press-repeat delay if (joystick[j].input_pressed) joystick[j].joystick_delay = SDL_GetTicks() + joystick_repeat_delay; } // updates all joystick states void poll_joysticks(void) { joydown = false; for (int j = 0; j < joysticks; j++) poll_joystick(j); } // sends SDL KEYDOWN and KEYUP events for a key void push_key(SDL_Scancode key) { SDL_Event e; memset(&e.key.keysym, 0, sizeof(e.key.keysym)); e.key.keysym.scancode = key; e.key.state = SDL_RELEASED; e.type = SDL_KEYDOWN; SDL_PushEvent(&e); e.type = SDL_KEYUP; SDL_PushEvent(&e); } // helps us be lazy by pretending joysticks are a keyboard (useful for menus) void push_joysticks_as_keyboard(void) { const SDL_Scancode confirm = SDL_SCANCODE_RETURN, cancel = SDL_SCANCODE_ESCAPE; const SDL_Scancode direction[4] = { SDL_SCANCODE_UP, SDL_SCANCODE_RIGHT, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT }; poll_joysticks(); for (int j = 0; j < joysticks; j++) { if (!joystick[j].input_pressed) continue; if (joystick[j].confirm) push_key(confirm); if (joystick[j].cancel) push_key(cancel); for (uint d = 0; d < COUNTOF(joystick[j].direction_pressed); d++) { if (joystick[j].direction_pressed[d]) push_key(direction[d]); } } } // initializes SDL joystick system and loads assignments for joysticks found void init_joysticks(void) { if (ignore_joystick) return; if (SDL_InitSubSystem(SDL_INIT_JOYSTICK)) { fprintf(stderr, "warning: failed to initialize joystick system: %s\n", SDL_GetError()); ignore_joystick = true; return; } SDL_JoystickEventState(SDL_IGNORE); joysticks = SDL_NumJoysticks(); joystick = malloc(joysticks * sizeof(*joystick)); for (int j = 0; j < joysticks; j++) { memset(&joystick[j], 0, sizeof(*joystick)); joystick[j].handle = SDL_JoystickOpen(j); if (joystick[j].handle != NULL) { printf("joystick detected: %s ", SDL_JoystickName(joystick[j].handle)); printf("(%d axes, %d buttons, %d hats)\n", SDL_JoystickNumAxes(joystick[j].handle), SDL_JoystickNumButtons(joystick[j].handle), SDL_JoystickNumHats(joystick[j].handle)); if (!load_joystick_assignments(&opentyrian_config, j)) reset_joystick_assignments(j); } } if (joysticks == 0) printf("no joysticks detected\n"); } // deinitializes SDL joystick system and saves joystick assignments void deinit_joysticks(void) { if (ignore_joystick) return; for (int j = 0; j < joysticks; j++) { if (joystick[j].handle != NULL) { save_joystick_assignments(&opentyrian_config, j); SDL_JoystickClose(joystick[j].handle); } } free(joystick); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); } void reset_joystick_assignments(int j) { assert(j < joysticks); // defaults: first 2 axes, first hat, first 6 buttons for (uint a = 0; a < COUNTOF(joystick[j].assignment); a++) { // clear assignments for (uint i = 0; i < COUNTOF(joystick[j].assignment[a]); i++) joystick[j].assignment[a][i].type = NONE; if (a < 4) { if (SDL_JoystickNumAxes(joystick[j].handle) >= 2) { joystick[j].assignment[a][0].type = AXIS; joystick[j].assignment[a][0].num = (a + 1) % 2; joystick[j].assignment[a][0].negative_axis = (a == 0 || a == 3); } if (SDL_JoystickNumHats(joystick[j].handle) >= 1) { joystick[j].assignment[a][1].type = HAT; joystick[j].assignment[a][1].num = 0; joystick[j].assignment[a][1].x_axis = (a == 1 || a == 3); joystick[j].assignment[a][1].negative_axis = (a == 0 || a == 3); } } else { if (a - 4 < (unsigned)SDL_JoystickNumButtons(joystick[j].handle)) { joystick[j].assignment[a][0].type = BUTTON; joystick[j].assignment[a][0].num = a - 4; } } } joystick[j].analog = false; joystick[j].sensitivity = 5; joystick[j].threshold = 5; } static const char* const assignment_names[] = { "up", "right", "down", "left", "fire", "change fire", "left sidekick", "right sidekick", "menu", "pause", }; bool load_joystick_assignments(Config *config, int j) { ConfigSection *section = config_find_section(config, "joystick", SDL_JoystickName(joystick[j].handle)); if (section == NULL) return false; if (!config_get_bool_option(section, "analog", &joystick[j].analog)) joystick[j].analog = false; joystick[j].sensitivity = config_get_or_set_int_option(section, "sensitivity", 5); joystick[j].threshold = config_get_or_set_int_option(section, "threshold", 5); for (size_t a = 0; a < COUNTOF(assignment_names); ++a) { for (unsigned int i = 0; i < COUNTOF(joystick[j].assignment[a]); ++i) joystick[j].assignment[a][i].type = NONE; ConfigOption *option = config_get_option(section, assignment_names[a]); if (option == NULL) continue; foreach_option_i_value(i, value, option) { if (i >= COUNTOF(joystick[j].assignment[a])) break; code_to_assignment(&joystick[j].assignment[a][i], value); } } return true; } bool save_joystick_assignments(Config *config, int j) { ConfigSection *section = config_find_or_add_section(config, "joystick", SDL_JoystickName(joystick[j].handle)); if (section == NULL) exit(EXIT_FAILURE); // out of memory config_set_bool_option(section, "analog", joystick[j].analog, NO_YES); config_set_int_option(section, "sensitivity", joystick[j].sensitivity); config_set_int_option(section, "threshold", joystick[j].threshold); for (size_t a = 0; a < COUNTOF(assignment_names); ++a) { ConfigOption *option = config_set_option(section, assignment_names[a], NULL); if (option == NULL) exit(EXIT_FAILURE); // out of memory option = config_set_value(option, NULL); if (option == NULL) exit(EXIT_FAILURE); // out of memory for (size_t i = 0; i < COUNTOF(joystick[j].assignment[a]); ++i) { if (joystick[j].assignment[a][i].type == NONE) continue; option = config_add_value(option, assignment_to_code(&joystick[j].assignment[a][i])); if (option == NULL) exit(EXIT_FAILURE); // out of memory } } return true; } // fills buffer with comma separated list of assigned joystick functions void joystick_assignments_to_string(char *buffer, size_t buffer_len, const Joystick_assignment *assignments) { strncpy(buffer, "", buffer_len); bool comma = false; for (uint i = 0; i < COUNTOF(*joystick->assignment); ++i) { if (assignments[i].type == NONE) continue; size_t len = snprintf(buffer, buffer_len, "%s%s", comma ? ", " : "", assignment_to_code(&assignments[i])); buffer += len; buffer_len -= len; comma = true; } } // reverse of assignment_to_code() void code_to_assignment(Joystick_assignment *assignment, const char *buffer) { memset(assignment, 0, sizeof(*assignment)); char axis = 0, direction = 0; if (sscanf(buffer, " AX %d%c", &assignment->num, &direction) == 2) assignment->type = AXIS; else if (sscanf(buffer, " BTN %d", &assignment->num) == 1) assignment->type = BUTTON; else if (sscanf(buffer, " H %d%c%c", &assignment->num, &axis, &direction) == 3) assignment->type = HAT; if (assignment->num == 0) assignment->type = NONE; else --assignment->num; assignment->x_axis = (toupper(axis) == 'X'); assignment->negative_axis = (toupper(direction) == '-'); } /* gives the short (6 or less characters) identifier for a joystick assignment * * two of these per direction/action is all that can fit on the joystick config screen, * assuming two digits for the axis/button/hat number */ const char *assignment_to_code(const Joystick_assignment *assignment) { static char name[7]; switch (assignment->type) { case NONE: strcpy(name, ""); break; case AXIS: snprintf(name, sizeof(name), "AX %d%c", assignment->num + 1, assignment->negative_axis ? '-' : '+'); break; case BUTTON: snprintf(name, sizeof(name), "BTN %d", assignment->num + 1); break; case HAT: snprintf(name, sizeof(name), "H %d%c%c", assignment->num + 1, assignment->x_axis ? 'X' : 'Y', assignment->negative_axis ? '-' : '+'); break; } return name; } // captures joystick input for configuring assignments // returns false if non-joystick input was detected // TODO: input from joystick other than the one being configured probably should not be ignored bool detect_joystick_assignment(int j, Joystick_assignment *assignment) { // get initial joystick state to compare against to see if anything was pressed const int axes = SDL_JoystickNumAxes(joystick[j].handle); Sint16 *axis = malloc(axes * sizeof(*axis)); for (int i = 0; i < axes; i++) axis[i] = SDL_JoystickGetAxis(joystick[j].handle, i); const int buttons = SDL_JoystickNumButtons(joystick[j].handle); Uint8 *button = malloc(buttons * sizeof(*button)); for (int i = 0; i < buttons; i++) button[i] = SDL_JoystickGetButton(joystick[j].handle, i); const int hats = SDL_JoystickNumHats(joystick[j].handle); Uint8 *hat = malloc(hats * sizeof(*hat)); for (int i = 0; i < hats; i++) hat[i] = SDL_JoystickGetHat(joystick[j].handle, i); bool detected = false; do { setDelay(1); SDL_JoystickUpdate(); for (int i = 0; i < axes; ++i) { Sint16 temp = SDL_JoystickGetAxis(joystick[j].handle, i); if (abs(temp - axis[i]) > joystick_analog_max * 2 / 3) { assignment->type = AXIS; assignment->num = i; assignment->negative_axis = temp < axis[i]; detected = true; break; } } for (int i = 0; i < buttons; ++i) { Uint8 new_button = SDL_JoystickGetButton(joystick[j].handle, i), changed = button[i] ^ new_button; if (!changed) continue; if (new_button == 0) // button was released { button[i] = new_button; } else // button was pressed { assignment->type = BUTTON; assignment->num = i; detected = true; break; } } for (int i = 0; i < hats; ++i) { Uint8 new_hat = SDL_JoystickGetHat(joystick[j].handle, i), changed = hat[i] ^ new_hat; if (!changed) continue; if ((new_hat & changed) == SDL_HAT_CENTERED) // hat was centered { hat[i] = new_hat; } else { assignment->type = HAT; assignment->num = i; assignment->x_axis = changed & (SDL_HAT_LEFT | SDL_HAT_RIGHT); assignment->negative_axis = changed & (SDL_HAT_LEFT | SDL_HAT_UP); detected = true; } } service_SDL_events(true); JE_showVGA(); wait_delay(); } while (!detected && !newkey && !newmouse); free(axis); free(button); free(hat); return detected; } // compares relevant parts of joystick assignments for equality bool joystick_assignment_cmp(const Joystick_assignment *a, const Joystick_assignment *b) { if (a->type == b->type) { switch (a->type) { case NONE: return true; case AXIS: return (a->num == b->num) && (a->negative_axis == b->negative_axis); case BUTTON: return (a->num == b->num); case HAT: return (a->num == b->num) && (a->x_axis == b->x_axis) && (a->negative_axis == b->negative_axis); } } return false; } opentyrian-2.1.20221123/src/joystick.h000066400000000000000000000047731432005211200172370ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef JOYSTICK_H #define JOYSTICK_H #include "opentyr.h" #include "config_file.h" #include "SDL.h" typedef enum { NONE, AXIS, BUTTON, HAT } Joystick_assignment_types; typedef struct { Joystick_assignment_types type; int num; // if hat bool x_axis; // else y_axis // if hat or axis bool negative_axis; // else positive } Joystick_assignment; typedef struct { SDL_Joystick *handle; Joystick_assignment assignment[10][2]; // 0-3: directions, 4-9: actions bool analog; int sensitivity, threshold; signed int x, y; int analog_direction[4]; bool direction[4], direction_pressed[4]; // up, right, down, left (_pressed, for emulating key presses) bool confirm, cancel; bool action[6], action_pressed[6]; // fire, mode swap, left fire, right fire, menu, pause Uint32 joystick_delay; bool input_pressed; } Joystick; extern int joystick_repeat_delay; extern bool joydown; extern bool ignore_joystick; extern int joysticks; extern Joystick *joystick; int joystick_axis_reduce(int j, int value); bool joystick_analog_angle(int j, float *angle); void poll_joystick(int j); void poll_joysticks(void); void push_key(SDL_Scancode key); void push_joysticks_as_keyboard(void); void init_joysticks(void); void deinit_joysticks(void); void reset_joystick_assignments(int j); bool load_joystick_assignments(Config* config, int j); bool save_joystick_assignments(Config* config, int j); void joystick_assignments_to_string(char *buffer, size_t buffer_len, const Joystick_assignment *assignments); bool detect_joystick_assignment(int j, Joystick_assignment *assignment); bool joystick_assignment_cmp(const Joystick_assignment *, const Joystick_assignment *); #endif /* JOYSTICK_H */ opentyrian-2.1.20221123/src/jukebox.c000066400000000000000000000111001432005211200170200ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "jukebox.h" #include "font.h" #include "joystick.h" #include "keyboard.h" #include "lds_play.h" #include "loudness.h" #include "mtrand.h" #include "nortsong.h" #include "opentyr.h" #include "palette.h" #include "sprite.h" #include "starlib.h" #include "vga_palette.h" #include "video.h" #include void jukebox(void) // FKA Setup.jukeboxGo { bool trigger_quit = false, // true when user wants to quit quitting = false; bool hide_text = false; bool fade_looped_songs = true, fading_song = false; bool stopped = false; bool fx = false; int fx_num = 0; int palette_fade_steps = 15; int diff[256][3]; init_step_fade_palette(diff, vga_palette, 0, 255); JE_starlib_init(); int fade_volume = tyrMusicVolume; for (; ; ) { if (!stopped && !audio_disabled) { if (songlooped && fade_looped_songs) fading_song = true; if (fading_song) { if (fade_volume > 5) { fade_volume -= 2; } else { fade_volume = tyrMusicVolume; fading_song = false; } set_volume(fade_volume, fxVolume); } if (!playing || (songlooped && fade_looped_songs && !fading_song)) play_song(mt_rand() % MUSIC_NUM); } setDelay(1); SDL_FillRect(VGAScreenSeg, NULL, 0); // starlib input needs to be rewritten JE_starlib_main(); push_joysticks_as_keyboard(); service_SDL_events(true); if (!hide_text) { char buffer[60]; if (fx) snprintf(buffer, sizeof(buffer), "%d %s", fx_num + 1, soundTitle[fx_num]); else snprintf(buffer, sizeof(buffer), "%d %s", song_playing + 1, musicTitle[song_playing]); const int x = VGAScreen->w / 2; draw_font_hv(VGAScreen, x, 170, "Press ESC to quit the jukebox.", small_font, centered, 1, 0); draw_font_hv(VGAScreen, x, 180, "Arrow keys change the song being played.", small_font, centered, 1, 0); draw_font_hv(VGAScreen, x, 190, buffer, small_font, centered, 1, 4); } if (palette_fade_steps > 0) step_fade_palette(diff, palette_fade_steps--, 0, 255); JE_showVGA(); wait_delay(); // quit on mouse click Uint16 x, y; if (JE_mousePosition(&x, &y) > 0) trigger_quit = true; if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_ESCAPE: // quit jukebox case SDL_SCANCODE_Q: trigger_quit = true; break; case SDL_SCANCODE_SPACE: hide_text = !hide_text; break; case SDL_SCANCODE_F: fading_song = !fading_song; break; case SDL_SCANCODE_N: fade_looped_songs = !fade_looped_songs; break; case SDL_SCANCODE_SLASH: // switch to sfx mode fx = !fx; break; case SDL_SCANCODE_COMMA: if (fx && --fx_num < 0) fx_num = SOUND_COUNT - 1; break; case SDL_SCANCODE_PERIOD: if (fx && ++fx_num >= SOUND_COUNT) fx_num = 0; break; case SDL_SCANCODE_SEMICOLON: if (fx) JE_playSampleNum(fx_num + 1); break; case SDL_SCANCODE_LEFT: case SDL_SCANCODE_UP: play_song((song_playing > 0 ? song_playing : MUSIC_NUM) - 1); stopped = false; break; case SDL_SCANCODE_RETURN: case SDL_SCANCODE_RIGHT: case SDL_SCANCODE_DOWN: play_song((song_playing + 1) % MUSIC_NUM); stopped = false; break; case SDL_SCANCODE_S: // stop song stop_song(); stopped = true; break; case SDL_SCANCODE_R: // restart song restart_song(); stopped = false; break; default: break; } } // user wants to quit, start fade-out if (trigger_quit && !quitting) { palette_fade_steps = 15; SDL_Color black = { 0, 0, 0 }; init_step_fade_solid(diff, black, 0, 255); quitting = true; } // if fade-out finished, we can finally quit if (quitting && palette_fade_steps == 0) break; } set_volume(tyrMusicVolume, fxVolume); } opentyrian-2.1.20221123/src/jukebox.h000066400000000000000000000016631432005211200170420ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef JUKEBOX_H #define JUKEBOX_H #include "opentyr.h" void jukebox(void); #endif /* JUKEBOX_H */ opentyrian-2.1.20221123/src/keyboard.c000066400000000000000000000135131432005211200171630ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "keyboard.h" #include "joystick.h" #include "mouse.h" #include "network.h" #include "opentyr.h" #include "video.h" #include "video_scale.h" #include "SDL.h" #include JE_boolean ESCPressed; JE_boolean newkey, newmouse, keydown, mousedown; SDL_Scancode lastkey_scan; SDL_Keymod lastkey_mod; Uint8 lastmouse_but; Sint32 lastmouse_x, lastmouse_y; JE_boolean mouse_pressed[3] = {false, false, false}; Sint32 mouse_x, mouse_y; bool windowHasFocus; Uint8 keysactive[SDL_NUM_SCANCODES]; bool new_text; char last_text[SDL_TEXTINPUTEVENT_TEXT_SIZE]; static bool mouseRelativeEnabled; // Relative mouse position in window coordinates. static Sint32 mouseWindowXRelative; static Sint32 mouseWindowYRelative; void flush_events_buffer(void) { SDL_Event ev; while (SDL_PollEvent(&ev)); } void wait_input(JE_boolean keyboard, JE_boolean mouse, JE_boolean joystick) { service_SDL_events(false); while (!((keyboard && keydown) || (mouse && mousedown) || (joystick && joydown))) { SDL_Delay(SDL_POLL_INTERVAL); push_joysticks_as_keyboard(); service_SDL_events(false); #ifdef WITH_NETWORK if (isNetworkGame) network_check(); #endif } } void wait_noinput(JE_boolean keyboard, JE_boolean mouse, JE_boolean joystick) { service_SDL_events(false); while ((keyboard && keydown) || (mouse && mousedown) || (joystick && joydown)) { SDL_Delay(SDL_POLL_INTERVAL); poll_joysticks(); service_SDL_events(false); #ifdef WITH_NETWORK if (isNetworkGame) network_check(); #endif } } void init_keyboard(void) { //SDL_EnableKeyRepeat(500, 60); TODO Find if SDL2 has an equivalent. newkey = newmouse = false; keydown = mousedown = false; SDL_ShowCursor(SDL_FALSE); #if SDL_VERSION_ATLEAST(2, 26, 0) SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE, "1"); #endif } void mouseSetRelative(bool enable) { SDL_SetRelativeMouseMode(enable && windowHasFocus); mouseRelativeEnabled = enable; mouseWindowXRelative = 0; mouseWindowYRelative = 0; } JE_word JE_mousePosition(JE_word *mouseX, JE_word *mouseY) { service_SDL_events(false); *mouseX = mouse_x; *mouseY = mouse_y; return mousedown ? lastmouse_but : 0; } void mouseGetRelativePosition(Sint32 *const out_x, Sint32 *const out_y) { service_SDL_events(false); scaleWindowDistanceToScreen(&mouseWindowXRelative, &mouseWindowYRelative); *out_x = mouseWindowXRelative; *out_y = mouseWindowYRelative; mouseWindowXRelative = 0; mouseWindowYRelative = 0; } void service_SDL_events(JE_boolean clear_new) { SDL_Event ev; if (clear_new) { newkey = false; newmouse = false; new_text = false; } while (SDL_PollEvent(&ev)) { switch (ev.type) { case SDL_WINDOWEVENT: switch (ev.window.event) { case SDL_WINDOWEVENT_FOCUS_LOST: windowHasFocus = false; mouseSetRelative(mouseRelativeEnabled); break; case SDL_WINDOWEVENT_FOCUS_GAINED: windowHasFocus = true; mouseSetRelative(mouseRelativeEnabled); break; case SDL_WINDOWEVENT_RESIZED: video_on_win_resize(); break; } break; case SDL_KEYDOWN: /* toggle fullscreen */ if (ev.key.keysym.mod & KMOD_ALT && ev.key.keysym.scancode == SDL_SCANCODE_RETURN) { toggle_fullscreen(); break; } keysactive[ev.key.keysym.scancode] = 1; newkey = true; lastkey_scan = ev.key.keysym.scancode; lastkey_mod = ev.key.keysym.mod; keydown = true; mouseInactive = true; return; case SDL_KEYUP: keysactive[ev.key.keysym.scancode] = 0; keydown = false; return; case SDL_MOUSEMOTION: mouse_x = ev.motion.x; mouse_y = ev.motion.y; mapWindowPointToScreen(&mouse_x, &mouse_y); if (mouseRelativeEnabled && windowHasFocus) { mouseWindowXRelative += ev.motion.xrel; mouseWindowYRelative += ev.motion.yrel; } // Show system mouse pointer if outside screen. SDL_ShowCursor(mouse_x < 0 || mouse_x >= vga_width || mouse_y < 0 || mouse_y >= vga_height ? SDL_TRUE : SDL_FALSE); if (ev.motion.xrel != 0 || ev.motion.yrel != 0) mouseInactive = false; break; case SDL_MOUSEBUTTONDOWN: mouseInactive = false; // fall through case SDL_MOUSEBUTTONUP: mapWindowPointToScreen(&ev.button.x, &ev.button.y); if (ev.type == SDL_MOUSEBUTTONDOWN) { newmouse = true; lastmouse_but = ev.button.button; lastmouse_x = ev.button.x; lastmouse_y = ev.button.y; mousedown = true; } else { mousedown = false; } switch (ev.button.button) { case SDL_BUTTON_LEFT: mouse_pressed[0] = mousedown; break; case SDL_BUTTON_RIGHT: mouse_pressed[1] = mousedown; break; case SDL_BUTTON_MIDDLE: mouse_pressed[2] = mousedown; break; } break; case SDL_TEXTINPUT: SDL_strlcpy(last_text, ev.text.text, COUNTOF(last_text)); new_text = true; break; case SDL_TEXTEDITING: break; case SDL_QUIT: /* TODO: Call the cleanup code here. */ exit(0); break; } } } void JE_clearKeyboard(void) { // /!\ Doesn't seems important. I think. D: } opentyrian-2.1.20221123/src/keyboard.h000066400000000000000000000035661432005211200171770ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KEYBOARD_H #define KEYBOARD_H #include "opentyr.h" #include "SDL.h" #include #define SDL_POLL_INTERVAL 10 extern JE_boolean ESCPressed; extern JE_boolean newkey, newmouse, keydown, mousedown; extern SDL_Scancode lastkey_scan; extern SDL_Keymod lastkey_mod; extern Uint8 lastmouse_but; extern Sint32 lastmouse_x, lastmouse_y; extern JE_boolean mouse_pressed[3]; extern Sint32 mouse_x, mouse_y; extern Uint8 keysactive[SDL_NUM_SCANCODES]; extern bool windowHasFocus; extern bool new_text; extern char last_text[SDL_TEXTINPUTEVENT_TEXT_SIZE]; void flush_events_buffer(void); void wait_input(JE_boolean keyboard, JE_boolean mouse, JE_boolean joystick); void wait_noinput(JE_boolean keyboard, JE_boolean mouse, JE_boolean joystick); void init_keyboard(void); void mouseSetRelative(bool enable); JE_word JE_mousePosition(JE_word *mouseX, JE_word *mouseY); void mouseGetRelativePosition(Sint32 *out_x, Sint32 *out_y); void service_SDL_events(JE_boolean clear_new); void sleep_game(void); void JE_clearKeyboard(void); #endif /* KEYBOARD_H */ opentyrian-2.1.20221123/src/lds_play.c000066400000000000000000000521131432005211200171710ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "lds_play.h" #include "file.h" #include "loudness.h" #include "opentyr.h" #include #include #include const unsigned char op_table[9] = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12}; /* A substantial amount of this code has been copied and adapted from adplug. Thanks, guys! Adplug is awesome! :D */ /* Note frequency table (16 notes / octave) */ static const Uint16 frequency[(13 * 15) - 3] = { 343, 344, 345, 347, 348, 349, 350, 352, 353, 354, 356, 357, 358, 359, 361, 362, 363, 365, 366, 367, 369, 370, 371, 373, 374, 375, 377, 378, 379, 381, 382, 384, 385, 386, 388, 389, 391, 392, 393, 395, 396, 398, 399, 401, 402, 403, 405, 406, 408, 409, 411, 412, 414, 415, 417, 418, 420, 421, 423, 424, 426, 427, 429, 430, 432, 434, 435, 437, 438, 440, 442, 443, 445, 446, 448, 450, 451, 453, 454, 456, 458, 459, 461, 463, 464, 466, 468, 469, 471, 473, 475, 476, 478, 480, 481, 483, 485, 487, 488, 490, 492, 494, 496, 497, 499, 501, 503, 505, 506, 508, 510, 512, 514, 516, 518, 519, 521, 523, 525, 527, 529, 531, 533, 535, 537, 538, 540, 542, 544, 546, 548, 550, 552, 554, 556, 558, 560, 562, 564, 566, 568, 571, 573, 575, 577, 579, 581, 583, 585, 587, 589, 591, 594, 596, 598, 600, 602, 604, 607, 609, 611, 613, 615, 618, 620, 622, 624, 627, 629, 631, 633, 636, 638, 640, 643, 645, 647, 650, 652, 654, 657, 659, 662, 664, 666, 669, 671, 674, 676, 678, 681, 683 }; /* Vibrato (sine) table */ static const Uint8 vibtab[25 + (13 * 3)] = { 0, 13, 25, 37, 50, 62, 74, 86, 98, 109, 120, 131, 142, 152, 162, 171, 180, 189, 197, 205, 212, 219, 225, 231, 236, 240, 244, 247, 250, 252, 254, 255, 255, 255, 254, 252, 250, 247, 244, 240, 236, 231, 225, 219, 212, 205, 197, 189, 180, 171, 162, 152, 142, 131, 120, 109, 98, 86, 74, 62, 50, 37, 25, 13 }; /* Tremolo (sine * sine) table */ static const Uint8 tremtab[128] = { 0, 0, 1, 1, 2, 4, 5, 7, 10, 12, 15, 18, 21, 25, 29, 33, 37, 42, 47, 52, 57, 62, 67, 73, 79, 85, 90, 97, 103, 109, 115, 121, 128, 134, 140, 146, 152, 158, 165, 170, 176, 182, 188, 193, 198, 203, 208, 213, 218, 222, 226, 230, 234, 237, 240, 243, 245, 248, 250, 251, 253, 254, 254, 255, 255, 255, 254, 254, 253, 251, 250, 248, 245, 243, 240, 237, 234, 230, 226, 222, 218, 213, 208, 203, 198, 193, 188, 182, 176, 170, 165, 158, 152, 146, 140, 134, 127, 121, 115, 109, 103, 97, 90, 85, 79, 73, 67, 62, 57, 52, 47, 42, 37, 33, 29, 25, 21, 18, 15, 12, 10, 7, 5, 4, 2, 1, 1, 0 }; static const Uint16 maxsound = 0x3f, maxpos = 0xff; static SoundBank *soundbank = NULL; static Channel channel[9]; static Position *positions = NULL; static Uint8 fmchip[0xff], jumping, fadeonoff, allvolume, hardfade, tempo_now, pattplay, tempo, regbd, chandelay[9], mode, pattlen; static Uint16 posplay, jumppos, speed; static Uint16 *patterns = NULL; static Uint16 numpatch, numposi, mainvolume; bool playing, songlooped; bool lds_load(FILE *f, unsigned int music_offset, unsigned int music_size) { SoundBank *sb; fseek(f, music_offset, SEEK_SET); /* load header */ fread_u8_die(&mode, 1, f); if (mode > 2) { fprintf(stderr, "error: failed to load music\n"); return false; } fread_u16_die(&speed, 1, f); fread_u8_die(&tempo, 1, f); fread_u8_die(&pattlen, 1, f); fread_u8_die(chandelay, 9, f); fread_u8_die(®bd, 1, f); /* load patches */ fread_u16_die(&numpatch, 1, f); free(soundbank); soundbank = malloc(sizeof(SoundBank) * numpatch); for (unsigned int i = 0; i < numpatch; i++) { sb = &soundbank[i]; fread_u8_die( &sb->mod_misc, 1, f); fread_u8_die( &sb->mod_vol, 1, f); fread_u8_die( &sb->mod_ad, 1, f); fread_u8_die( &sb->mod_sr, 1, f); fread_u8_die( &sb->mod_wave, 1, f); fread_u8_die( &sb->car_misc, 1, f); fread_u8_die( &sb->car_vol, 1, f); fread_u8_die( &sb->car_ad, 1, f); fread_u8_die( &sb->car_sr, 1, f); fread_u8_die( &sb->car_wave, 1, f); fread_u8_die( &sb->feedback, 1, f); fread_u8_die( &sb->keyoff, 1, f); fread_u8_die( &sb->portamento, 1, f); fread_u8_die( &sb->glide, 1, f); fread_u8_die( &sb->finetune, 1, f); fread_u8_die( &sb->vibrato, 1, f); fread_u8_die( &sb->vibdelay, 1, f); fread_u8_die( &sb->mod_trem, 1, f); fread_u8_die( &sb->car_trem, 1, f); fread_u8_die( &sb->tremwait, 1, f); fread_u8_die( &sb->arpeggio, 1, f); fread_u8_die( sb->arp_tab, 12, f); fread_u16_die(&sb->start, 1, f); fread_u16_die(&sb->size, 1, f); fread_u8_die( &sb->fms, 1, f); fread_u16_die(&sb->transp, 1, f); fread_u8_die( &sb->midinst, 1, f); fread_u8_die( &sb->midvelo, 1, f); fread_u8_die( &sb->midkey, 1, f); fread_u8_die( &sb->midtrans, 1, f); fread_u8_die( &sb->middum1, 1, f); fread_u8_die( &sb->middum2, 1, f); } /* load positions */ fread_u16_die(&numposi, 1, f); free(positions); positions = malloc(sizeof(Position) * 9 * numposi); for (unsigned int i = 0; i < numposi; i++) { for (unsigned int j = 0; j < 9; j++) { /* * patnum is a pointer inside the pattern space, but patterns are 16bit * word fields anyway, so it ought to be an even number (hopefully) and * we can just divide it by 2 to get our array index of 16bit words. */ fread_u16_die(&positions[i * 9 + j].patnum, 1, f); fread_u8_die( &positions[i * 9 + j].transpose, 1, f); positions[i * 9 + j].patnum /= 2; } } /* load patterns */ fseek(f, 2, SEEK_CUR); /* ignore # of digital sounds (dunno what this is for) */ unsigned int remaining = music_size - (ftell(f) - music_offset); size_t numpatterns = remaining / 2; free(patterns); patterns = malloc(sizeof(Uint16) * numpatterns); fread_u16_die(patterns, numpatterns, f); lds_rewind(); return true; } void lds_free(void) { free(soundbank); soundbank = NULL; free(positions); positions = NULL; free(patterns); patterns = NULL; } void lds_rewind(void) { int i; /* init all with 0 */ tempo_now = 3; playing = true; songlooped = false; jumping = fadeonoff = allvolume = hardfade = pattplay = posplay = jumppos = mainvolume = 0; memset(channel, 0, sizeof(channel)); memset(fmchip, 0, sizeof(fmchip)); /* OPL2 init */ opl_init(); /* Reset OPL chip */ opl_write(1, 0x20); opl_write(8, 0); opl_write(0xbd, regbd); for(i = 0; i < 9; i++) { opl_write(0x20 + op_table[i], 0); opl_write(0x23 + op_table[i], 0); opl_write(0x40 + op_table[i], 0x3f); opl_write(0x43 + op_table[i], 0x3f); opl_write(0x60 + op_table[i], 0xff); opl_write(0x63 + op_table[i], 0xff); opl_write(0x80 + op_table[i], 0xff); opl_write(0x83 + op_table[i], 0xff); opl_write(0xe0 + op_table[i], 0); opl_write(0xe3 + op_table[i], 0); opl_write(0xa0 + i, 0); opl_write(0xb0 + i, 0); opl_write(0xc0 + i, 0); } } void lds_fade(Uint8 speed) { fadeonoff = speed; } void lds_setregs(Uint8 reg, Uint8 val) { if(fmchip[reg] == val) return; fmchip[reg] = val; opl_write(reg, val); } void lds_setregs_adv(Uint8 reg, Uint8 mask, Uint8 val) { lds_setregs(reg, (fmchip[reg] & mask) | val); } int lds_update(void) { Uint16 comword, freq, octave, chan, tune, wibc, tremc, arpreg; int vbreak; Uint8 level, regnum, comhi, comlo; int i; Channel *c; if(!playing) return false; /* handle fading */ if(fadeonoff) { if(fadeonoff <= 128) { if(allvolume > fadeonoff || allvolume == 0) { allvolume -= fadeonoff; } else { allvolume = 1; fadeonoff = 0; if(hardfade != 0) { playing = false; hardfade = 0; for(i = 0; i < 9; i++) { channel[i].keycount = 1; } } } } else { if( (Uint8) ((allvolume + (0x100 - fadeonoff)) & 0xff) <= mainvolume) { allvolume += 0x100 - fadeonoff; } else { allvolume = mainvolume; fadeonoff = 0; } } } /* handle channel delay */ for(chan = 0; chan < 9; chan++) { c = &channel[chan]; if(c->chancheat.chandelay) { if(!(--c->chancheat.chandelay)) { lds_playsound(c->chancheat.sound, chan, c->chancheat.high); } } } /* handle notes */ if(!tempo_now && positions) { vbreak = false; for(chan = 0; chan < 9; chan++) { c = &channel[chan]; if(!c->packwait) { Uint16 patnum = positions[posplay * 9 + chan].patnum; Uint8 transpose = positions[posplay * 9 + chan].transpose; /*printf("> %p", positions);*/ comword = patterns[patnum + c->packpos]; comhi = comword >> 8; comlo = comword & 0xff; if(comword) { if(comhi == 0x80) { c->packwait = comlo; } else { if(comhi >= 0x80) { switch(comhi) { case 0xff: c->volcar = (((c->volcar & 0x3f) * comlo) >> 6) & 0x3f; if(fmchip[0xc0 + chan] & 1) c->volmod = (((c->volmod & 0x3f) * comlo) >> 6) & 0x3f; break; case 0xfe: tempo = comword & 0x3f; break; case 0xfd: c->nextvol = comlo; break; case 0xfc: playing = false; /* in real player there's also full keyoff here, but we don't need it */ break; case 0xfb: c->keycount = 1; break; case 0xfa: vbreak = true; jumppos = (posplay + 1) & maxpos; break; case 0xf9: vbreak = true; jumppos = comlo & maxpos; jumping = 1; if(jumppos < posplay) { songlooped = true; } break; case 0xf8: c->lasttune = 0; break; case 0xf7: c->vibwait = 0; /* PASCAL: c->vibspeed = ((comlo >> 4) & 15) + 2; */ c->vibspeed = (comlo >> 4) + 2; c->vibrate = (comlo & 15) + 1; break; case 0xf6: c->glideto = comlo; break; case 0xf5: c->finetune = comlo; break; case 0xf4: if(!hardfade) { allvolume = mainvolume = comlo; fadeonoff = 0; } break; case 0xf3: if(!hardfade) { fadeonoff = comlo; } break; case 0xf2: c->trmstay = comlo; break; case 0xf1: /* panorama */ case 0xf0: /* progch */ /* MIDI commands (unhandled) */ /*AdPlug_LogWrite("CldsPlayer(): not handling MIDI command 0x%x, " "value = 0x%x\n", comhi);*/ break; default: if(comhi < 0xa0) { c->glideto = comhi & 0x1f; } else { /*AdPlug_LogWrite("CldsPlayer(): unknown command 0x%x encountered!" " value = 0x%x\n", comhi, comlo);*/ } break; } } else { Uint8 sound; Uint16 high; Sint8 transp = transpose & 127; /* * Originally, in assembler code, the player first shifted * logically left the transpose byte by 1 and then shifted * arithmetically right the same byte to achieve the final, * signed transpose value. Since we can't do arithmetic shifts * in C, we just duplicate the 7th bit into the 8th one and * discard the 8th one completely. */ if(transpose & 64) { transp |= 128; } if(transpose & 128) { sound = (comlo + transp) & maxsound; high = comhi << 4; } else { sound = comlo & maxsound; high = (comhi + transp) << 4; } /* PASCAL: sound = comlo & maxsound; high = (comhi + (((transpose + 0x24) & 0xff) - 0x24)) << 4; */ if(!chandelay[chan]) { lds_playsound(sound, chan, high); } else { c->chancheat.chandelay = chandelay[chan]; c->chancheat.sound = sound; c->chancheat.high = high; } } } } c->packpos++; } else { c->packwait--; } } tempo_now = tempo; /* The continue table is updated here, but this is only used in the original player, which can be paused in the middle of a song and then unpaused. Since AdPlug does all this for us automatically, we don't have a continue table here. The continue table update code is noted here for reference only. if(!pattplay) { conttab[speed & maxcont].position = posplay & 0xff; conttab[speed & maxcont].tempo = tempo; } */ pattplay++; if(vbreak) { pattplay = 0; for(i = 0; i < 9; i++) { channel[i].packpos = channel[i].packwait = 0; } posplay = jumppos; } else { if(pattplay >= pattlen) { pattplay = 0; for(i = 0; i < 9; i++) { channel[i].packpos = channel[i].packwait = 0; } posplay = (posplay + 1) & maxpos; } } } else { tempo_now--; } /* make effects */ for(chan = 0; chan < 9; chan++) { c = &channel[chan]; regnum = op_table[chan]; if(c->keycount > 0) { if(c->keycount == 1) lds_setregs_adv(0xb0 + chan, 0xdf, 0); c->keycount--; } /* arpeggio */ if(c->arp_size == 0) arpreg = 0; else { arpreg = c->arp_tab[c->arp_pos] << 4; if(arpreg == 0x800) { if(c->arp_pos > 0) c->arp_tab[0] = c->arp_tab[c->arp_pos - 1]; c->arp_size = 1; c->arp_pos = 0; arpreg = c->arp_tab[0] << 4; } if(c->arp_count == c->arp_speed) { c->arp_pos++; if(c->arp_pos >= c->arp_size) c->arp_pos = 0; c->arp_count = 0; } else c->arp_count++; } /* glide & portamento */ if(c->lasttune && (c->lasttune != c->gototune)) { if(c->lasttune > c->gototune) { if(c->lasttune - c->gototune < c->portspeed) c->lasttune = c->gototune; else c->lasttune -= c->portspeed; } else { if(c->gototune - c->lasttune < c->portspeed) c->lasttune = c->gototune; else c->lasttune += c->portspeed; } if(arpreg >= 0x800) arpreg = c->lasttune - (arpreg ^ 0xff0) - 16; else arpreg += c->lasttune; freq = frequency[arpreg % (12 * 16)]; octave = arpreg / (12 * 16) - 1; lds_setregs(0xa0 + chan, freq & 0xff); lds_setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf); } else { /* vibrato */ if(!c->vibwait) { if(c->vibrate) { wibc = vibtab[c->vibcount & 0x3f] * c->vibrate; if((c->vibcount & 0x40) == 0) tune = c->lasttune + (wibc >> 8); else tune = c->lasttune - (wibc >> 8); if(arpreg >= 0x800) tune = tune - (arpreg ^ 0xff0) - 16; else tune += arpreg; freq = frequency[tune % (12 * 16)]; octave = tune / (12 * 16) - 1; lds_setregs(0xa0 + chan, freq & 0xff); lds_setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf); c->vibcount += c->vibspeed; } else if(c->arp_size != 0) { /* no vibrato, just arpeggio */ if(arpreg >= 0x800) tune = c->lasttune - (arpreg ^ 0xff0) - 16; else tune = c->lasttune + arpreg; freq = frequency[tune % (12 * 16)]; octave = tune / (12 * 16) - 1; lds_setregs(0xa0 + chan, freq & 0xff); lds_setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf); } } else { /* no vibrato, just arpeggio */ c->vibwait--; if(c->arp_size != 0) { if(arpreg >= 0x800) tune = c->lasttune - (arpreg ^ 0xff0) - 16; else tune = c->lasttune + arpreg; freq = frequency[tune % (12 * 16)]; octave = tune / (12 * 16) - 1; lds_setregs(0xa0 + chan, freq & 0xff); lds_setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf); } } } /* tremolo (modulator) */ if(!c->trmwait) { if(c->trmrate) { tremc = tremtab[c->trmcount & 0x7f] * c->trmrate; if((tremc >> 8) <= (c->volmod & 0x3f)) level = (c->volmod & 0x3f) - (tremc >> 8); else level = 0; if(allvolume != 0 && (fmchip[0xc0 + chan] & 1)) lds_setregs_adv(0x40 + regnum, 0xc0, ((level * allvolume) >> 8) ^ 0x3f); else lds_setregs_adv(0x40 + regnum, 0xc0, level ^ 0x3f); c->trmcount += c->trmspeed; } else if(allvolume != 0 && (fmchip[0xc0 + chan] & 1)) lds_setregs_adv(0x40 + regnum, 0xc0, ((((c->volmod & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f); else lds_setregs_adv(0x40 + regnum, 0xc0, (c->volmod ^ 0x3f) & 0x3f); } else { c->trmwait--; if(allvolume != 0 && (fmchip[0xc0 + chan] & 1)) lds_setregs_adv(0x40 + regnum, 0xc0, ((((c->volmod & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f); } /* tremolo (carrier) */ if(!c->trcwait) { if(c->trcrate) { tremc = tremtab[c->trccount & 0x7f] * c->trcrate; if((tremc >> 8) <= (c->volcar & 0x3f)) level = (c->volcar & 0x3f) - (tremc >> 8); else level = 0; if(allvolume != 0) lds_setregs_adv(0x43 + regnum, 0xc0, ((level * allvolume) >> 8) ^ 0x3f); else lds_setregs_adv(0x43 + regnum, 0xc0, level ^ 0x3f); c->trccount += c->trcspeed; } else if(allvolume != 0) lds_setregs_adv(0x43 + regnum, 0xc0, ((((c->volcar & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f); else lds_setregs_adv(0x43 + regnum, 0xc0, (c->volcar ^ 0x3f) & 0x3f); } else { c->trcwait--; if(allvolume != 0) lds_setregs_adv(0x43 + regnum, 0xc0, ((((c->volcar & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f); } } return (!playing || songlooped) ? false : true; } void lds_playsound(int inst_number, int channel_number, int tunehigh) { Channel *c = &channel[channel_number]; /* current channel */ SoundBank *i = &soundbank[inst_number]; /* current instrument */ Uint32 regnum = op_table[channel_number]; /* channel's OPL2 register */ Uint8 volcalc, octave; Uint16 freq; /* set fine tune */ tunehigh += ((i->finetune + c->finetune + 0x80) & 0xff) - 0x80; /* arpeggio handling */ if(!i->arpeggio) { Uint16 arpcalc = i->arp_tab[0] << 4; if(arpcalc > 0x800) tunehigh = tunehigh - (arpcalc ^ 0xff0) - 16; else tunehigh += arpcalc; } /* glide handling */ if(c->glideto != 0) { c->gototune = tunehigh; c->portspeed = c->glideto; c->glideto = c->finetune = 0; return; } /* set modulator registers */ lds_setregs(0x20 + regnum, i->mod_misc); volcalc = i->mod_vol; if(!c->nextvol || !(i->feedback & 1)) c->volmod = volcalc; else c->volmod = (volcalc & 0xc0) | ((((volcalc & 0x3f) * c->nextvol) >> 6)); if((i->feedback & 1) == 1 && allvolume != 0) lds_setregs(0x40 + regnum, ((c->volmod & 0xc0) | (((c->volmod & 0x3f) * allvolume) >> 8)) ^ 0x3f); else lds_setregs(0x40 + regnum, c->volmod ^ 0x3f); lds_setregs(0x60 + regnum, i->mod_ad); lds_setregs(0x80 + regnum, i->mod_sr); lds_setregs(0xe0 + regnum, i->mod_wave); /* Set carrier registers */ lds_setregs(0x23 + regnum, i->car_misc); volcalc = i->car_vol; if(!c->nextvol) c->volcar = volcalc; else c->volcar = (volcalc & 0xc0) | ((((volcalc & 0x3f) * c->nextvol) >> 6)); if(allvolume) lds_setregs(0x43 + regnum, ((c->volcar & 0xc0) | (((c->volcar & 0x3f) * allvolume) >> 8)) ^ 0x3f); else lds_setregs(0x43 + regnum, c->volcar ^ 0x3f); lds_setregs(0x63 + regnum, i->car_ad); lds_setregs(0x83 + regnum, i->car_sr); lds_setregs(0xe3 + regnum, i->car_wave); lds_setregs(0xc0 + channel_number, i->feedback); lds_setregs_adv(0xb0 + channel_number, 0xdf, 0); /* key off */ freq = frequency[tunehigh % (12 * 16)]; octave = tunehigh / (12 * 16) - 1; if(!i->glide) { if(!i->portamento || !c->lasttune) { lds_setregs(0xa0 + channel_number, freq & 0xff); lds_setregs(0xb0 + channel_number, (octave << 2) + 0x20 + (freq >> 8)); c->lasttune = c->gototune = tunehigh; } else { c->gototune = tunehigh; c->portspeed = i->portamento; lds_setregs_adv(0xb0 + channel_number, 0xdf, 0x20); /* key on */ } } else { lds_setregs(0xa0 + channel_number, freq & 0xff); lds_setregs(0xb0 + channel_number, (octave << 2) + 0x20 + (freq >> 8)); c->lasttune = tunehigh; c->gototune = tunehigh + ((i->glide + 0x80) & 0xff) - 0x80; /* set destination */ c->portspeed = i->portamento; } if(!i->vibrato) c->vibwait = c->vibspeed = c->vibrate = 0; else { c->vibwait = i->vibdelay; /* PASCAL: c->vibspeed = ((i->vibrato >> 4) & 15) + 1; */ c->vibspeed = (i->vibrato >> 4) + 2; c->vibrate = (i->vibrato & 15) + 1; } if(!(c->trmstay & 0xf0)) { c->trmwait = (i->tremwait & 0xf0) >> 3; /* PASCAL: c->trmspeed = (i->mod_trem >> 4) & 15; */ c->trmspeed = i->mod_trem >> 4; c->trmrate = i->mod_trem & 15; c->trmcount = 0; } if(!(c->trmstay & 0x0f)) { c->trcwait = (i->tremwait & 15) << 1; /* PASCAL: c->trcspeed = (i->car_trem >> 4) & 15; */ c->trcspeed = i->car_trem >> 4; c->trcrate = i->car_trem & 15; c->trccount = 0; } c->arp_size = i->arpeggio & 15; c->arp_speed = i->arpeggio >> 4; memcpy(c->arp_tab, i->arp_tab, 12); c->keycount = i->keyoff; c->nextvol = c->glideto = c->finetune = c->vibcount = c->arp_pos = c->arp_count = 0; } opentyrian-2.1.20221123/src/lds_play.h000066400000000000000000000047071432005211200172040ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LDS_PLAY_H #define LDS_PLAY_H #include "opentyr.h" #include extern bool playing, songlooped; int lds_update(void); bool lds_load(FILE *f, unsigned int music_offset, unsigned int music_size); void lds_free(void); void lds_rewind(void); void lds_fade(Uint8 speed); /*unsigned int getorders() { return numposi; } unsigned int getorder() { return posplay; } unsigned int getrow() { return pattplay; } unsigned int getspeed() { return speed; } unsigned int getinstruments() { return numpatch; }*/ typedef struct { unsigned char mod_misc, mod_vol, mod_ad, mod_sr, mod_wave, car_misc, car_vol, car_ad, car_sr, car_wave, feedback, keyoff, portamento, glide, finetune, vibrato, vibdelay, mod_trem, car_trem, tremwait, arpeggio, arp_tab[12]; unsigned short start, size; unsigned char fms; unsigned short transp; unsigned char midinst, midvelo, midkey, midtrans, middum1, middum2; } SoundBank; typedef struct { unsigned short gototune, lasttune, packpos; unsigned char finetune, glideto, portspeed, nextvol, volmod, volcar, vibwait, vibspeed, vibrate, trmstay, trmwait, trmspeed, trmrate, trmcount, trcwait, trcspeed, trcrate, trccount, arp_size, arp_speed, keycount, vibcount, arp_pos, arp_count, packwait, arp_tab[12]; struct { unsigned char chandelay, sound; unsigned short high; } chancheat; } Channel; typedef struct { unsigned short patnum; unsigned char transpose; } Position; void lds_playsound(int inst_number, int channel_number, int tunehigh); void lds_setregs(unsigned char reg, unsigned char val); void lds_setregs_adv(unsigned char reg, unsigned char mask, unsigned char val); #endif /* LDS_PLAY_H */ opentyrian-2.1.20221123/src/loudness.c000066400000000000000000000213141432005211200172150ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "loudness.h" #include "file.h" #include "lds_play.h" #include "nortsong.h" #include "opentyr.h" #include "params.h" #include #include #include #define OUTPUT_QUALITY 4 // 44.1 kHz int audioSampleRate = 0; bool music_stopped = true; unsigned int song_playing = 0; bool audio_disabled = false, music_disabled = false, samples_disabled = false; static SDL_AudioDeviceID audioDevice = 0; static Uint8 musicVolume = 255; static Uint8 sampleVolume = 255; static const float volumeRange = 30.0f; // dB // Fixed point Q20.12; needs to be able to store (10 * INT16_MIN/MAX) static Sint32 volumeFactorTable[256]; #define TO_FIXED(x) ((Sint32)((x) * (1 << 12))) #define FIXED_TO_INT(x) ((Sint32)((x) >> 12)) // Twice the Loudness update rate (in updates/second). In Tyrian, Loudness // updates were performed at the same rate as the game timer, which varied // depending on the game speed (~69.57 Hz at most game speeds). We don't have // the same limitations, so we'll keep the update rate constant, but we do want // to stick to integer math, so we'll update at 69.5 Hz. static const int ldsUpdate2Rate = 139; // 69.5 * 2 static int samplesPerLdsUpdate; static int samplesPerLdsUpdateFrac; static int samplesUntilLdsUpdate = 0; static int samplesUntilLdsUpdateFrac = 0; static FILE *music_file = NULL; static Uint32 *song_offset; static Uint16 song_count = 0; #define CHANNEL_COUNT 8 static const Sint16 *channelSamples[CHANNEL_COUNT]; static size_t channelSampleCount[CHANNEL_COUNT] = { 0 }; static Uint8 channelVolume[CHANNEL_COUNT]; #define CHANNEL_VOLUME_LEVELS 8 static void audioCallback(void *userdata, Uint8 *stream, int size); static void load_song(unsigned int song_num); bool init_audio(void) { if (audio_disabled) return false; SDL_AudioSpec ask, got; ask.freq = 11025 * OUTPUT_QUALITY; ask.format = AUDIO_S16SYS; ask.channels = 1; ask.samples = 256 * OUTPUT_QUALITY; // ~23 ms ask.callback = audioCallback; if (SDL_InitSubSystem(SDL_INIT_AUDIO)) { fprintf(stderr, "error: failed to initialize SDL audio: %s\n", SDL_GetError()); audio_disabled = true; return false; } int allowedChanges = SDL_AUDIO_ALLOW_FREQUENCY_CHANGE; #if SDL_VERSION_ATLEAST(2, 0, 9) allowedChanges |= SDL_AUDIO_ALLOW_SAMPLES_CHANGE; #endif audioDevice = SDL_OpenAudioDevice(/*device*/ NULL, /*iscapture*/ 0, &ask, &got, allowedChanges); if (audioDevice == 0) { fprintf(stderr, "error: SDL failed to open audio device: %s\n", SDL_GetError()); audio_disabled = true; return false; } audioSampleRate = got.freq; samplesPerLdsUpdate = 2 * (audioSampleRate / ldsUpdate2Rate); samplesPerLdsUpdateFrac = 2 * (audioSampleRate % ldsUpdate2Rate); volumeFactorTable[0] = 0; for (size_t i = 1; i < 256; ++i) volumeFactorTable[i] = TO_FIXED(powf(10, (255 - i) * (-volumeRange / (20.0f * 255)))); opl_init(); SDL_PauseAudioDevice(audioDevice, 0); // unpause return true; } static void audioCallback(void *userdata, Uint8 *stream, int size) { (void)userdata; Sint16 *const samples = (Sint16 *)stream; const int samplesCount = size / sizeof (Sint16); if (!music_disabled && !music_stopped) { Sint16 *remaining = samples; int remainingCount = samplesCount; while (remainingCount > 0) { if (samplesUntilLdsUpdate == 0) { lds_update(); // The number of samples that should be produced per Loudness // update is not an integer, but we can only produce an integer // number of samples, so we accumulate the fractional samples // until it amounts to a whole sample. samplesUntilLdsUpdate += samplesPerLdsUpdate; samplesUntilLdsUpdateFrac += samplesPerLdsUpdateFrac; if (samplesUntilLdsUpdateFrac >= ldsUpdate2Rate) { samplesUntilLdsUpdate += 1; samplesUntilLdsUpdateFrac -= ldsUpdate2Rate; } } int count = MIN(samplesUntilLdsUpdate, remainingCount); opl_update(remaining, count); remaining += count; remainingCount -= count; samplesUntilLdsUpdate -= count; } } else { for (int i = 0; i < samplesCount; ++i) samples[i] = 0; } Sint32 musicVolumeFactor = volumeFactorTable[musicVolume]; musicVolumeFactor *= 2; // OPL emulator is too quiet if (samples_disabled && !music_disabled) { // Mix music Sint16 *remaining = samples; int remainingCount = samplesCount; while (remainingCount > 0) { Sint32 sample = *remaining * musicVolumeFactor; sample = FIXED_TO_INT(sample); *remaining = MIN(MAX(INT16_MIN, sample), INT16_MAX); remaining += 1; remainingCount -= 1; } } else if (!samples_disabled) { Sint32 sampleVolumeFactor = volumeFactorTable[sampleVolume]; Sint32 sampleVolumeFactors[CHANNEL_VOLUME_LEVELS]; for (int i = 0; i < CHANNEL_VOLUME_LEVELS; ++i) sampleVolumeFactors[i] = sampleVolumeFactor * (i + 1) / CHANNEL_VOLUME_LEVELS; // Mix music and channels Sint16 *remaining = samples; int remainingCount = samplesCount; while (remainingCount > 0) { Sint32 sample = *remaining * musicVolumeFactor; for (size_t i = 0; i < CHANNEL_COUNT; ++i) { if (channelSampleCount[i] > 0) { sample += *channelSamples[i] * sampleVolumeFactors[channelVolume[i]]; channelSamples[i] += 1; channelSampleCount[i] -= 1; } } sample = FIXED_TO_INT(sample); *remaining = MIN(MAX(INT16_MIN, sample), INT16_MAX); remaining += 1; remainingCount -= 1; } } } void deinit_audio(void) { if (audio_disabled) return; if (audioDevice != 0) { SDL_PauseAudioDevice(audioDevice, 1); // pause SDL_CloseAudioDevice(audioDevice); audioDevice = 0; } SDL_QuitSubSystem(SDL_INIT_AUDIO); memset(channelSampleCount, 0, sizeof channelSampleCount); lds_free(); } void load_music(void) // FKA NortSong.loadSong { if (music_file == NULL) { music_file = dir_fopen_die(data_dir(), "music.mus", "rb"); fread_u16_die(&song_count, 1, music_file); song_offset = malloc((song_count + 1) * sizeof(*song_offset)); fread_u32_die(song_offset, song_count, music_file); song_offset[song_count] = ftell_eof(music_file); } } static void load_song(unsigned int song_num) // FKA NortSong.loadSong { if (song_num < song_count) { unsigned int song_size = song_offset[song_num + 1] - song_offset[song_num]; lds_load(music_file, song_offset[song_num], song_size); } else { fprintf(stderr, "warning: failed to load song %d\n", song_num + 1); } } void play_song(unsigned int song_num) // FKA NortSong.playSong { if (audio_disabled) return; if (song_num != song_playing) { SDL_LockAudioDevice(audioDevice); music_stopped = true; SDL_UnlockAudioDevice(audioDevice); load_song(song_num); song_playing = song_num; } SDL_LockAudioDevice(audioDevice); music_stopped = false; SDL_UnlockAudioDevice(audioDevice); } void restart_song(void) // FKA Player.selectSong(1) { if (audio_disabled) return; SDL_LockAudioDevice(audioDevice); lds_rewind(); music_stopped = false; SDL_UnlockAudioDevice(audioDevice); } void stop_song(void) // FKA Player.selectSong(0) { if (audio_disabled) return; SDL_LockAudioDevice(audioDevice); music_stopped = true; SDL_UnlockAudioDevice(audioDevice); } void fade_song(void) // FKA Player.selectSong($C001) { if (audio_disabled) return; SDL_LockAudioDevice(audioDevice); lds_fade(1); SDL_UnlockAudioDevice(audioDevice); } void set_volume(Uint8 musicVolume_, Uint8 sampleVolume_) // FKA NortSong.setVol and Player.setVol { if (audio_disabled) return; SDL_LockAudioDevice(audioDevice); musicVolume = musicVolume_; sampleVolume = sampleVolume_; SDL_UnlockAudioDevice(audioDevice); } void multiSamplePlay(const Sint16 *samples, size_t sampleCount, Uint8 chan, Uint8 vol) // FKA Player.multiSamplePlay { assert(chan < CHANNEL_COUNT); assert(vol < CHANNEL_VOLUME_LEVELS); if (audio_disabled || samples_disabled) return; SDL_LockAudioDevice(audioDevice); channelSamples[chan] = samples; channelSampleCount[chan] = sampleCount; channelVolume[chan] = vol; SDL_UnlockAudioDevice(audioDevice); } opentyrian-2.1.20221123/src/loudness.h000066400000000000000000000026121432005211200172220ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LOUDNESS_H #define LOUDNESS_H #include "opentyr.h" #include "opl.h" #include "SDL.h" extern int audioSampleRate; extern unsigned int song_playing; extern bool audio_disabled, music_disabled, samples_disabled; bool init_audio(void); void deinit_audio(void); void load_music(void); void play_song(unsigned int song_num); void restart_song(void); void stop_song(void); void fade_song(void); void set_volume(Uint8 musicVolume, Uint8 sampleVolume); void multiSamplePlay(const Sint16 *samples, size_t sampleCount, Uint8 chan, Uint8 vol); #endif /* LOUDNESS_H */ opentyrian-2.1.20221123/src/lvllib.c000066400000000000000000000022421432005211200166440ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "lvllib.h" #include "file.h" #include "opentyr.h" JE_LvlPosType lvlPos; char levelFile[13]; /* string [12] */ JE_word lvlNum; void JE_analyzeLevel(void) { FILE *f = dir_fopen_die(data_dir(), levelFile, "rb"); fread_u16_die(&lvlNum, 1, f); fread_s32_die(lvlPos, lvlNum, f); lvlPos[lvlNum] = ftell_eof(f); fclose(f); } opentyrian-2.1.20221123/src/lvllib.h000066400000000000000000000021231432005211200166470ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LVLLIB_H #define LVLLIB_H #include "opentyr.h" typedef JE_longint JE_LvlPosType[43]; /* [1..42 + 1] */ extern JE_LvlPosType lvlPos; extern char levelFile[13]; /* string [12] */ extern JE_word lvlNum; void JE_analyzeLevel(void); #endif /* LVLLIB_H */ opentyrian-2.1.20221123/src/lvlmast.c000066400000000000000000000021751432005211200170470ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "lvlmast.h" #include "opentyr.h" const JE_char shapeFile[34] = /* [1..34] */ { '2', '4', '7', '8', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', '5', '#', 'V', '0', '@', // [25] should be '&' rather than '5' '3', '^', '5', '9' }; opentyrian-2.1.20221123/src/lvlmast.h000066400000000000000000000023241432005211200170500ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LVLMAST_H #define LVLMAST_H #include "opentyr.h" #define EVENT_MAXIMUM 2500 #define WEAP_NUM 780 #define PORT_NUM 42 #define ARMOR_NUM 4 #define POWER_NUM 6 #define ENGINE_NUM 6 #define OPTION_NUM 30 #define SHIP_NUM 13 #define SHIELD_NUM 10 #define SPECIAL_NUM 46 #define ENEMY_NUM 850 extern const JE_char shapeFile[34]; /* [1..34] */ #endif /* LVLMAST_H */ opentyrian-2.1.20221123/src/mainint.c000066400000000000000000003616131432005211200170310ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mainint.h" #include "backgrnd.h" #include "config.h" #include "editship.h" #include "episodes.h" #include "file.h" #include "font.h" #include "fonthand.h" #include "helptext.h" #include "helptext.h" #include "joystick.h" #include "keyboard.h" #include "lds_play.h" #include "loudness.h" #include "menus.h" #include "mouse.h" #include "mtrand.h" #include "network.h" #include "nortsong.h" #include "nortvars.h" #include "opentyr.h" #include "palette.h" #include "params.h" #include "pcxmast.h" #include "picload.h" #include "player.h" #include "shots.h" #include "sndmast.h" #include "sprite.h" #include "varz.h" #include "vga256d.h" #include "video.h" #include #include #include bool button[4]; #define MAX_PAGE 8 #define TOPICS 6 const JE_byte topicStart[TOPICS] = { 0, 1, 2, 3, 7, 255 }; JE_shortint constantLastX; JE_word textErase; JE_word upgradeCost; JE_word downgradeCost; JE_boolean performSave; JE_boolean jumpSection; JE_boolean useLastBank; /* See if I want to use the last 16 colors for DisplayText */ bool pause_pressed = false, ingamemenu_pressed = false; /* Draws a message at the bottom text window on the playing screen */ void JE_drawTextWindow(const char *text) { if (textErase > 0) // erase current text blit_sprite(VGAScreenSeg, 16, 189, OPTION_SHAPES, 36); // in-game text area textErase = 100; JE_outText(VGAScreenSeg, 20, 190, text, 0, 4); } void JE_outCharGlow(JE_word x, JE_word y, const char *s) { JE_integer maxloc, loc, z; JE_shortint glowcol[60]; /* [1..60] */ JE_shortint glowcolc[60]; /* [1..60] */ JE_word textloc[60]; /* [1..60] */ JE_byte bank; setDelay2(1); bank = (warningRed) ? 7 : ((useLastBank) ? 15 : 14); if (s[0] == '\0') return; if (frameCountMax == 0) { JE_textShade(VGAScreen, x, y, s, bank, 0, PART_SHADE); JE_showVGA(); } else { maxloc = strlen(s); for (z = 0; z < 60; z++) { glowcol[z] = -8; glowcolc[z] = 1; } loc = x; for (z = 0; z < maxloc; z++) { textloc[z] = loc; int sprite_id = font_ascii[(unsigned char)s[z]]; if (s[z] == ' ') loc += 6; else if (sprite_id != -1) loc += sprite(TINY_FONT, sprite_id)->width + 1; } for (loc = 0; (unsigned)loc < strlen(s) + 28; loc++) { if (!ESCPressed) { setDelay(frameCountMax); NETWORK_KEEP_ALIVE(); int sprite_id = -1; for (z = loc - 28; z <= loc; z++) { if (z >= 0 && z < maxloc) { sprite_id = font_ascii[(unsigned char)s[z]]; if (sprite_id != -1) { blit_sprite_hv(VGAScreen, textloc[z], y, TINY_FONT, sprite_id, bank, glowcol[z]); glowcol[z] += glowcolc[z]; if (glowcol[z] > 9) glowcolc[z] = -1; } } } if (sprite_id != -1 && --z < maxloc) blit_sprite_dark(VGAScreen, textloc[z] + 1, y + 1, TINY_FONT, sprite_id, true); if (JE_anyButton()) frameCountMax = 0; do { if (levelWarningDisplay) JE_updateWarning(VGAScreen); SDL_Delay(16); } while (!(getDelayTicks() == 0 || ESCPressed)); JE_showVGA(); } } } } void JE_drawPortConfigButtons(void) // rear weapon pattern indicator { if (twoPlayerMode) return; if (player[0].weapon_mode == 1) { blit_sprite(VGAScreenSeg, 285, 44, OPTION_SHAPES, 18); // lit blit_sprite(VGAScreenSeg, 302, 44, OPTION_SHAPES, 19); // unlit } else // == 2 { blit_sprite(VGAScreenSeg, 285, 44, OPTION_SHAPES, 19); // unlit blit_sprite(VGAScreenSeg, 302, 44, OPTION_SHAPES, 18); // lit } } static bool helpSystemPage(Uint8 *topic, bool *restart); void JE_helpSystem(JE_byte startTopic) { if (shopSpriteSheet.data == NULL) JE_loadCompShapes(&shopSpriteSheet, '1'); // need mouse pointer sprites Uint8 topic = startTopic; bool restart = true; const size_t menuItemsCount = COUNTOF(topicName) - 1; size_t selectedIndex = 0; const int xCenter = 320 / 2; const int yMenuHeader = 30; const int yMenuItems = 60; const int dyMenuItems = 20; const int hMenuItem = 13; int wMenuItem[COUNTOF(topicName) - 1] = { 0 }; for (; ; ) { if (restart) { play_song(SONG_MAPVIEW); JE_loadPic(VGAScreen2, 2, false); } if (topic > 1) { if (!helpSystemPage(&topic, &restart)) return; selectedIndex = (size_t)topic - 1; topic = 1; continue; } // Restore background. memcpy(VGAScreen->pixels, VGAScreen2->pixels, (size_t)VGAScreen->pitch * VGAScreen->h); // Draw header. draw_font_hv_shadow(VGAScreen, xCenter, yMenuHeader, topicName[0], large_font, centered, 15, -3, false, 2); // Draw menu items. for (size_t i = 0; i < menuItemsCount; ++i) { const char *const text = topicName[i + 1]; wMenuItem[i] = JE_textWidth(text, normal_font); const int y = yMenuItems + dyMenuItems * i; const bool selected = i == selectedIndex; draw_font_hv_shadow(VGAScreen, xCenter, y, text, normal_font, centered, 15, -3 + (selected ? 2 : 0), false, 2); } mouseCursor = MOUSE_POINTER_NORMAL; if (restart) { fade_palette(colors, 10, 0, 255); restart = false; } service_SDL_events(true); JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); bool mouseMoved = false; do { SDL_Delay(16); Uint16 oldMouseX = mouse_x; Uint16 oldMouseY = mouse_y; push_joysticks_as_keyboard(); service_SDL_events(false); mouseMoved = mouse_x != oldMouseX || mouse_y != oldMouseY; } while (!(newkey || newmouse || mouseMoved)); // Handle interaction. bool action = false; bool done = false; if (mouseMoved || newmouse) { // Find menu item that was hovered or clicked. for (size_t i = 0; i < menuItemsCount; ++i) { const int xMenuItem = xCenter - wMenuItem[i] / 2; if (mouse_x >= xMenuItem && mouse_x < xMenuItem + wMenuItem[i]) { const int yMenuItem = yMenuItems + dyMenuItems * i; if (mouse_y >= yMenuItem && mouse_y < yMenuItem + hMenuItem) { if (selectedIndex != i) { JE_playSampleNum(S_CURSOR); selectedIndex = i; } if (newmouse && lastmouse_but == SDL_BUTTON_LEFT && lastmouse_x >= xMenuItem && lastmouse_x < xMenuItem + wMenuItem[i] && lastmouse_y >= yMenuItem && lastmouse_y < yMenuItem + hMenuItem) { action = true; } break; } } } } if (newmouse) { if (lastmouse_but == SDL_BUTTON_RIGHT) { JE_playSampleNum(S_SPRING); done = true; } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_UP: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == 0 ? menuItemsCount - 1 : selectedIndex - 1; break; } case SDL_SCANCODE_DOWN: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == menuItemsCount - 1 ? 0 : selectedIndex + 1; break; } case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: { action = true; break; } case SDL_SCANCODE_ESCAPE: { JE_playSampleNum(S_SPRING); done = true; break; } default: break; } } if (action) { JE_playSampleNum(S_SELECT); topic = selectedIndex + 2; if (selectedIndex == menuItemsCount - 1) done = true; } if (done) { fade_black(15); return; } } } static bool helpSystemPage(Uint8 *topic, bool *restart) { Uint8 page = topicStart[*topic - 1]; const int xCenter = 320 / 2; for (; ; ) { if (page == 0) { *topic = 1; return true; } else if (page > MAX_PAGE) { *topic = COUNTOF(topicName) - 1; return true; } for (Uint8 temp = 0; temp < COUNTOF(topicName); ++temp) { if (topicStart[temp] <= page) *topic = temp + 1; else break; } // Restore background. memcpy(VGAScreen->pixels, VGAScreen2->pixels, (size_t)VGAScreen->pitch * VGAScreen->h); fill_rectangle_wh(VGAScreen, 0, 192, 320, 8, 0); const char *const text = topicName[*topic - 1]; // Draw header. draw_font_hv_shadow(VGAScreen, xCenter, 1, text, normal_font, centered, 15, -3, false, 2); // Draw footer. JE_char buffer[128]; snprintf(buffer, sizeof buffer, "%s %d", miscText[24], page - topicStart[*topic - 1] + 1); draw_font_hv(VGAScreen, 10, 192, buffer, small_font, left_aligned, 13, 5); snprintf(buffer, sizeof buffer, "%s %d of %d", miscText[25], page, MAX_PAGE); draw_font_hv(VGAScreen, 320 - 10, 192, buffer, small_font, right_aligned, 13, 5); // Draw text. helpBoxBrightness = 3; verticalHeight = 8; switch (page) { case 1: /* One-Player Menu */ JE_HBox(VGAScreen, 10, 20, 2, 60); JE_HBox(VGAScreen, 10, 50, 5, 60); JE_HBox(VGAScreen, 10, 80, 21, 60); JE_HBox(VGAScreen, 10, 110, 1, 60); JE_HBox(VGAScreen, 10, 140, 28, 60); break; case 2: /* Two-Player Menu */ JE_HBox(VGAScreen, 10, 20, 1, 60); JE_HBox(VGAScreen, 10, 60, 2, 60); JE_HBox(VGAScreen, 10, 100, 21, 60); JE_HBox(VGAScreen, 10, 140, 28, 60); break; case 3: /* Upgrade Ship */ JE_HBox(VGAScreen, 10, 20, 5, 60); JE_HBox(VGAScreen, 10, 70, 6, 60); JE_HBox(VGAScreen, 10, 110, 7, 60); break; case 4: JE_HBox(VGAScreen, 10, 20, 8, 60); JE_HBox(VGAScreen, 10, 55, 9, 60); JE_HBox(VGAScreen, 10, 87, 10, 60); JE_HBox(VGAScreen, 10, 120, 11, 60); JE_HBox(VGAScreen, 10, 170, 13, 60); break; case 5: JE_HBox(VGAScreen, 10, 20, 14, 60); JE_HBox(VGAScreen, 10, 80, 15, 60); JE_HBox(VGAScreen, 10, 120, 16, 60); break; case 6: JE_HBox(VGAScreen, 10, 20, 17, 60); JE_HBox(VGAScreen, 10, 40, 18, 60); JE_HBox(VGAScreen, 10, 130, 20, 60); break; case 7: /* Options */ JE_HBox(VGAScreen, 10, 20, 21, 60); JE_HBox(VGAScreen, 10, 70, 22, 60); JE_HBox(VGAScreen, 10, 110, 23, 60); JE_HBox(VGAScreen, 10, 140, 24, 60); break; case 8: JE_HBox(VGAScreen, 10, 20, 25, 60); JE_HBox(VGAScreen, 10, 60, 26, 60); JE_HBox(VGAScreen, 10, 100, 27, 60); JE_HBox(VGAScreen, 10, 140, 28, 60); JE_HBox(VGAScreen, 10, 170, 29, 60); break; } helpBoxBrightness = 1; verticalHeight = 7; if (*restart) { fade_palette(colors, 10, 0, 255); *restart = false; } do { mouseCursor = mouse_x < xCenter ? MOUSE_POINTER_LEFT : MOUSE_POINTER_RIGHT; service_SDL_events(true); JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); SDL_Delay(16); push_joysticks_as_keyboard(); service_SDL_events(false); } while (!(newkey || newmouse)); // Handle interaction. bool done = false; if (newmouse) { switch (lastmouse_but) { case SDL_BUTTON_LEFT: { JE_playSampleNum(S_CURSOR); if (mouse_x < xCenter) page -= 1; else page += 1; break; } case SDL_BUTTON_RIGHT: { JE_playSampleNum(S_SPRING); done = true; break; } default: break; } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_LEFT: { JE_playSampleNum(S_CURSOR); page -= 1; break; } case SDL_SCANCODE_RIGHT: case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: { JE_playSampleNum(S_CURSOR); page += 1; break; } case SDL_SCANCODE_ESCAPE: { JE_playSampleNum(S_SPRING); done = true; break; } default: break; } } if (done) { fade_black(15); return false; } } } // cost to upgrade a weapon power from power-1 (where power == 0 indicates an unupgraded weapon) long weapon_upgrade_cost(long base_cost, unsigned int power) { assert(power <= 11); unsigned int temp = 0; // 0 1 3 6 10 15 21 29 ... for (; power > 0; power--) temp += power; return base_cost * temp; } ulong JE_getCost(JE_byte itemType, JE_word itemNum) { long cost = 0; switch (itemType) { case 2: cost = (itemNum > 90) ? 100 : ships[itemNum].cost; break; case 3: case 4: cost = weaponPort[itemNum].cost; const uint port = itemType - 3, item_power = player[0].items.weapon[port].power - 1; downgradeCost = weapon_upgrade_cost(cost, item_power); upgradeCost = weapon_upgrade_cost(cost, item_power + 1); break; case 5: cost = shields[itemNum].cost; break; case 6: cost = powerSys[itemNum].cost; break; case 7: case 8: cost = options[itemNum].cost; break; } return cost; } bool JE_loadScreen(void) { if (shopSpriteSheet.data == NULL) JE_loadCompShapes(&shopSpriteSheet, '1'); // need mouse pointer and arrow sprites bool restart = true; size_t playersIndex = 0; const size_t menuItemsCount = 12; size_t selectedIndex = 0; const int xCenter = 320 / 2; const int yMenuHeader = 5; const int xMenuItem = 10; const int xMenuItemName = xMenuItem; const int xMenuItemLastLevel = 120; const int xMenuItemEpisode = 250; const int wMenuItem = 300; const int yMenuItems = 30; const int dyMenuItems = 13; const int hMenuItem = 8; const int xLeftControl = 83; const int xRightControl = 213; const int wControl = 24; const int yControls = 179; for (; ; ) { if (restart) { JE_loadPic(VGAScreen2, 2, false); fill_rectangle_wh(VGAScreen2, 0, 192, 320, 8, 0); } // Restore background. memcpy(VGAScreen->pixels, VGAScreen2->pixels, (size_t)VGAScreen->pitch * VGAScreen->h); // Draw header. draw_font_hv_shadow(VGAScreen, xCenter, yMenuHeader, miscText[38 + playersIndex], large_font, centered, 15, -3, false, 2); // Draw menu items. for (size_t i = 0; i < menuItemsCount; ++i) { const int y = yMenuItems + dyMenuItems * i; const bool selected = i == selectedIndex; if (i == menuItemsCount - 1) { JE_textShade(VGAScreen, xMenuItemName, y, miscText[33], 13, selected ? 6 : 2, FULL_SHADE); continue; } const JE_SaveFileType *const saveFile = &saveFiles[playersIndex * 11 + i]; const bool disabled = saveFile->level == 0; char buffer[22]; if (disabled) { JE_textShade(VGAScreen, xMenuItemName, y, miscText[2], 13, selected ? 6 : 0, FULL_SHADE); snprintf(buffer, sizeof buffer, "%s -----", miscTextB[2]); JE_textShade(VGAScreen, xMenuItemLastLevel, y, buffer, 5, selected ? 6 : 0, FULL_SHADE); } else { JE_textShade(VGAScreen, xMenuItemName, y, saveFile->name, 13, selected ? 6 : 2, FULL_SHADE); snprintf(buffer, sizeof buffer, "%s %s", miscTextB[2], saveFile->levelName); JE_textShade(VGAScreen, xMenuItemLastLevel, y, buffer, 5, selected ? 6 : 2, FULL_SHADE); snprintf(buffer, sizeof buffer, "%s %u", miscTextB[1], saveFile->episode); JE_textShade(VGAScreen, xMenuItemEpisode, y, buffer, 5, selected ? 6 : 2, FULL_SHADE); } } // Draw paging controls. const bool leftControlVisible = playersIndex > 0; const bool rightControlVisible = playersIndex < 1; if (leftControlVisible) blit_sprite2x2(VGAScreen, xLeftControl, yControls, shopSpriteSheet, 279); if (rightControlVisible) blit_sprite2x2(VGAScreen, xRightControl, yControls, shopSpriteSheet, 281); helpBoxColor = 15; JE_helpBox(VGAScreen, 103, 182, miscText[55], 25); if (restart) { mouseCursor = MOUSE_POINTER_NORMAL; fade_palette(colors, 10, 0, 255); restart = false; } service_SDL_events(true); JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); bool mouseMoved = false; do { SDL_Delay(16); Uint16 oldMouseX = mouse_x; Uint16 oldMouseY = mouse_y; push_joysticks_as_keyboard(); service_SDL_events(false); mouseMoved = mouse_x != oldMouseX || mouse_y != oldMouseY; } while (!(newkey || newmouse || mouseMoved)); // Handle interaction. bool leftAction = false; bool rightAction = false; bool action = false; bool done = false; if (mouseMoved || newmouse) { if (leftControlVisible && mouse_y >= yControls && mouse_x >= xLeftControl && mouse_x < xLeftControl + wControl) { if (newmouse && lastmouse_but == SDL_BUTTON_LEFT) { JE_playSampleNum(S_CURSOR); leftAction = true; } } else if (rightControlVisible && mouse_y >= yControls && mouse_x >= xRightControl && mouse_x < xRightControl + wControl) { if (newmouse && lastmouse_but == SDL_BUTTON_LEFT) { JE_playSampleNum(S_CURSOR); rightAction = true; } } else { // Find menu item that was hovered or clicked. if (mouse_x >= xMenuItem && mouse_x < xMenuItem + wMenuItem) { for (size_t i = 0; i < menuItemsCount; ++i) { const int yMenuItem = yMenuItems + dyMenuItems * i; if (mouse_y >= yMenuItem && mouse_y < yMenuItem + hMenuItem) { if (selectedIndex != i) { JE_playSampleNum(S_CURSOR); selectedIndex = i; } if (newmouse && lastmouse_but == SDL_BUTTON_LEFT && lastmouse_x >= xMenuItem && lastmouse_x < xMenuItem + wMenuItem && lastmouse_y >= yMenuItem && lastmouse_y < yMenuItem + hMenuItem) { action = true; } break; } } } } } if (newmouse) { if (lastmouse_but == SDL_BUTTON_RIGHT) { JE_playSampleNum(S_SPRING); done = true; } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_LEFT: { JE_playSampleNum(S_CURSOR); leftAction = true; break; } case SDL_SCANCODE_RIGHT: { JE_playSampleNum(S_CURSOR); rightAction = true; break; } case SDL_SCANCODE_UP: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == 0 ? menuItemsCount - 1 : selectedIndex - 1; break; } case SDL_SCANCODE_DOWN: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == menuItemsCount - 1 ? 0 : selectedIndex + 1; break; } case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: { action = true; break; } case SDL_SCANCODE_ESCAPE: { JE_playSampleNum(S_SPRING); done = true; break; } default: break; } } if (leftAction) { playersIndex = playersIndex == 0 ? 1 : 0; } else if (rightAction) { playersIndex = playersIndex == 1 ? 0 : 1; } else if (action) { if (selectedIndex == menuItemsCount - 1) // "Exit to Main Menu" { JE_playSampleNum(S_SELECT); done = true; } else { const size_t saveFileIndex = playersIndex * 11 + selectedIndex; if (saveFiles[saveFileIndex].level == 0) // "EMPTY SLOT" { JE_playSampleNum(S_CLINK); } else { JE_playSampleNum(S_SELECT); performSave = false; JE_operation(saveFileIndex + 1); fade_black(15); return gameLoaded; } } } if (done) { fade_black(15); return false; } } } ulong JE_totalScore(const Player *this_player) { ulong temp = this_player->cash; temp += JE_getValue(2, this_player->items.ship); temp += JE_getValue(3, this_player->items.weapon[FRONT_WEAPON].id); temp += JE_getValue(4, this_player->items.weapon[REAR_WEAPON].id); temp += JE_getValue(5, this_player->items.shield); temp += JE_getValue(6, this_player->items.generator); temp += JE_getValue(7, this_player->items.sidekick[LEFT_SIDEKICK]); temp += JE_getValue(8, this_player->items.sidekick[RIGHT_SIDEKICK]); return temp; } JE_longint JE_getValue(JE_byte itemType, JE_word itemNum) { long value = 0; switch (itemType) { case 2: value = ships[itemNum].cost; break; case 3: case 4:; const long base_value = weaponPort[itemNum].cost; // if two-player, use first player's front and second player's rear weapon const uint port = itemType - 3; const uint item_power = player[twoPlayerMode ? port : 0].items.weapon[port].power - 1; value = base_value; for (unsigned int i = 1; i <= item_power; ++i) value += weapon_upgrade_cost(base_value, i); break; case 5: value = shields[itemNum].cost; break; case 6: value = powerSys[itemNum].cost; break; case 7: case 8: value = options[itemNum].cost; break; } return value; } void JE_nextEpisode(void) { strcpy(lastLevelName, "Completed"); if (episodeNum == initial_episode_num && !gameHasRepeated && episodeNum != EPISODE_AVAILABLE && !isNetworkGame && !constantPlay) { JE_highScoreCheck(); } unsigned int newEpisode = JE_findNextEpisode(); if (jumpBackToEpisode1) { if (episodeNum > 2 && !constantPlay) { JE_playCredits(); } // randomly give player the SuperCarrot if ((mt_rand() % 6) == 0) { player[0].items.ship = 2; // SuperCarrot player[0].items.weapon[FRONT_WEAPON].id = 23; // Banana Blast player[0].items.weapon[REAR_WEAPON].id = 24; // Banana Blast Rear for (uint i = 0; i < COUNTOF(player[0].items.weapon); ++i) player[0].items.weapon[i].power = 1; player[1].items.weapon[REAR_WEAPON].id = 24; // Banana Blast Rear player[0].last_items = player[0].items; } } if (newEpisode != episodeNum) JE_initEpisode(newEpisode); gameLoaded = true; mainLevel = FIRST_LEVEL; saveLevel = FIRST_LEVEL; play_song(26); JE_clr256(VGAScreen); memcpy(colors, palettes[6-1], sizeof(colors)); JE_dString(VGAScreen, JE_fontCenter(episode_name[episodeNum], SMALL_FONT_SHAPES), 130, episode_name[episodeNum], SMALL_FONT_SHAPES); JE_dString(VGAScreen, JE_fontCenter(miscText[5-1], SMALL_FONT_SHAPES), 185, miscText[5-1], SMALL_FONT_SHAPES); JE_showVGA(); fade_palette(colors, 15, 0, 255); JE_wipeKey(); if (!constantPlay) { do { NETWORK_KEEP_ALIVE(); SDL_Delay(16); } while (!JE_anyButton()); } fade_black(15); } void JE_initPlayerData(void) { /* JE: New Game Items/Data */ player[0].items.ship = 1; // USP Talon player[0].items.weapon[FRONT_WEAPON].id = 1; // Pulse Cannon player[0].items.weapon[REAR_WEAPON].id = 0; // None player[0].items.shield = 4; // Gencore High Energy Shield player[0].items.generator = 2; // Advanced MR-12 for (uint i = 0; i < COUNTOF(player[0].items.sidekick); ++i) player[0].items.sidekick[i] = 0; // None player[0].items.special = 0; // None player[0].last_items = player[0].items; player[1].items = player[0].items; player[1].items.weapon[REAR_WEAPON].id = 15; // Vulcan Cannon player[1].items.sidekick_level = 101; // 101, 102, 103 player[1].items.sidekick_series = 0; // None gameHasRepeated = false; onePlayerAction = false; superArcadeMode = SA_NONE; superTyrian = false; twoPlayerMode = false; secretHint = (mt_rand() % 3) + 1; for (uint p = 0; p < COUNTOF(player); ++p) { for (uint i = 0; i < COUNTOF(player->items.weapon); ++i) { player[p].items.weapon[i].power = 1; } player[p].weapon_mode = 1; player[p].armor = ships[player[p].items.ship].dmg; player[p].is_dragonwing = (p == 1); player[p].lives = &player[p].items.weapon[p].power; } mainLevel = FIRST_LEVEL; saveLevel = FIRST_LEVEL; strcpy(lastLevelName, miscText[19]); } void JE_sortHighScores(void) { JE_byte x; temp = 0; for (x = 0; x < 6; x++) { JE_sort(); temp += 3; } } void JE_highScoreScreen(void) { if (shopSpriteSheet.data == NULL) JE_loadCompShapes(&shopSpriteSheet, '1'); // need mouse pointer and arrow sprites bool restart = true; size_t episodeIndex = 0; // Save file only has space for scores for 3 episodes. const size_t episodeCount = 3; const int xCenter = 320 / 2; const int yMenuHeader = 3; const int yEpisodeHeader = 30; const int xLeftControl = 83; const int xRightControl = 213; const int wControl = 24; const int yControls = 179; for (; ; ) { if (restart) { JE_loadPic(VGAScreen2, 2, false); fill_rectangle_wh(VGAScreen2, 0, 192, 320, 8, 0); // Draw header. draw_font_hv_shadow(VGAScreen2, xCenter, yMenuHeader, miscText[50], large_font, centered, 15, -3, false, 2); } // Restore background and header. memcpy(VGAScreen->pixels, VGAScreen2->pixels, (size_t)VGAScreen->pitch * VGAScreen->h); const bool disabled = !episodeAvail[episodeIndex]; // Draw episode header. draw_font_hv_shadow(VGAScreen, xCenter, yEpisodeHeader, episode_name[episodeIndex + 1], normal_font, centered, 15, -3 + (disabled ? -4 : 0), false, 2); char buffer[29]; // Draw 1-player scores. draw_font_hv_shadow(VGAScreen, xCenter, 55, miscText[46], normal_font, centered, 15, -3, false, 2); for (Uint8 i = 0; i < 3; ++i) { const int y = 75 + 10 * i; const JE_SaveFileType *const saveFile = &saveFiles[episodeIndex * 6 + i]; const int rank = MIN(saveFile->highScoreDiff, COUNTOF(difficultyNameB) - 1); snprintf(buffer, sizeof buffer, "~#%d:~ %d", i + 1, saveFile->highScore1); JE_textShade(VGAScreen, 20, y, buffer, 15, 0, FULL_SHADE); JE_textShade(VGAScreen, 110, y, saveFile->highScoreName, 15, 2, FULL_SHADE); JE_textShade(VGAScreen, 250, y, difficultyNameB[rank], 15, rank + (rank == 0 ? 0 : -1), FULL_SHADE); } // Draw 2-player scores. draw_font_hv_shadow(VGAScreen, xCenter, 120, miscText[47], normal_font, centered, 15, -3, false, 2); for (Uint8 i = 0; i < 3; ++i) { const int y = 135 + 10 * i; const JE_SaveFileType *const saveFile = &saveFiles[episodeIndex * 6 + 3 + i]; const int rank = MIN(saveFile->highScoreDiff, COUNTOF(difficultyNameB) - 1); snprintf(buffer, sizeof buffer, "~#%d:~ %d", i + 1, saveFile->highScore1); JE_textShade(VGAScreen, 20, y, buffer, 15, 0, FULL_SHADE); JE_textShade(VGAScreen, 110, y, saveFile->highScoreName, 15, 2, FULL_SHADE); JE_textShade(VGAScreen, 250, y, difficultyNameB[rank], 15, rank + (rank == 0 ? 0 : -1), FULL_SHADE); } // Draw paging controls. const bool leftControlVisible = episodeIndex > 0; const bool rightControlVisible = episodeIndex < episodeCount - 1; if (leftControlVisible) blit_sprite2x2(VGAScreen, xLeftControl, yControls, shopSpriteSheet, 279); if (rightControlVisible) blit_sprite2x2(VGAScreen, xRightControl, yControls, shopSpriteSheet, 281); helpBoxColor = 15; JE_helpBox(VGAScreen, 103, 182, miscText[56], 25); if (restart) { mouseCursor = MOUSE_POINTER_NORMAL; fade_palette(colors, 10, 0, 255); restart = false; } do { service_SDL_events(true); JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); SDL_Delay(16); push_joysticks_as_keyboard(); service_SDL_events(false); } while (!(newkey || newmouse)); // Handle interaction. bool leftAction = false; bool rightAction = false; bool done = false; if (newmouse) { switch (lastmouse_but) { case SDL_BUTTON_LEFT: { if (leftControlVisible && mouse_y >= yControls && mouse_x >= xLeftControl && mouse_x < xLeftControl + wControl) { JE_playSampleNum(S_CURSOR); leftAction = true; } else if (rightControlVisible && mouse_y >= yControls && mouse_x >= xRightControl && mouse_x < xRightControl + wControl) { JE_playSampleNum(S_CURSOR); rightAction = true; } break; } case SDL_BUTTON_RIGHT: { JE_playSampleNum(S_SPRING); done = true; break; } default: break; } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_LEFT: { JE_playSampleNum(S_CURSOR); leftAction = true; break; } case SDL_SCANCODE_RIGHT: { JE_playSampleNum(S_CURSOR); rightAction = true; break; } case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: case SDL_SCANCODE_ESCAPE: { JE_playSampleNum(S_SPRING); done = true; break; } default: break; } } if (leftAction) { episodeIndex = episodeIndex == 0 ? episodeCount - 1 : episodeIndex - 1; } else if (rightAction) { episodeIndex = episodeIndex == episodeCount - 1 ? 0 : episodeIndex + 1; } if (done) { fade_black(15); return; } } } void JE_gammaCorrect_func(JE_byte *col, JE_real r) { int temp = roundf(*col * r); if (temp > 255) { temp = 255; } *col = temp; } void JE_gammaCorrect(Palette *colorBuffer, JE_byte gamma) { int x; JE_real r = 1 + (JE_real)gamma / 10; for (x = 0; x < 256; x++) { JE_gammaCorrect_func(&(*colorBuffer)[x].r, r); JE_gammaCorrect_func(&(*colorBuffer)[x].g, r); JE_gammaCorrect_func(&(*colorBuffer)[x].b, r); } } JE_boolean JE_gammaCheck(void) { bool temp = keysactive[SDL_SCANCODE_F11] != 0; if (temp) { keysactive[SDL_SCANCODE_F11] = false; newkey = false; gammaCorrection = (gammaCorrection + 1) % 4; memcpy(colors, palettes[pcxpal[3-1]], sizeof(colors)); JE_gammaCorrect(&colors, gammaCorrection); set_palette(colors, 0, 255); } return temp; } void JE_doInGameSetup(void) { mouseSetRelative(false); haltGame = false; #ifdef WITH_NETWORK if (isNetworkGame) { network_prepare(PACKET_GAME_MENU); network_send(4); // PACKET_GAME_MENU while (true) { service_SDL_events(false); if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_GAME_MENU) { network_update(); break; } network_update(); network_check(); SDL_Delay(16); } } #endif if (yourInGameMenuRequest) { if (JE_inGameSetup()) { reallyEndLevel = true; playerEndLevel = true; } quitRequested = false; keysactive[SDL_SCANCODE_ESCAPE] = false; #ifdef WITH_NETWORK if (isNetworkGame) { if (!playerEndLevel) { network_prepare(PACKET_WAITING); network_send(4); // PACKET_WAITING } else { network_prepare(PACKET_GAME_QUIT); network_send(4); // PACKET_GAMEQUIT } } #endif } #ifdef WITH_NETWORK if (isNetworkGame) { SDL_Surface *temp_surface = VGAScreen; VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ if (!yourInGameMenuRequest) { JE_barShade(VGAScreen, 3, 60, 257, 80); /*Help Box*/ JE_barShade(VGAScreen, 5, 62, 255, 78); JE_dString(VGAScreen, 10, 65, "Other player in options menu.", SMALL_FONT_SHAPES); JE_showVGA(); while (true) { service_SDL_events(false); JE_showVGA(); if (packet_in[0]) { if (SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_WAITING) { network_check(); break; } else if (SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_GAME_QUIT) { reallyEndLevel = true; playerEndLevel = true; network_check(); break; } } network_update(); network_check(); SDL_Delay(16); } } else { /* JE_barShade(3, 160, 257, 180); /-*Help Box*-/ JE_barShade(5, 162, 255, 178); tempScreenSeg = VGAScreen; JE_dString(VGAScreen, 10, 165, "Waiting for other player.", SMALL_FONT_SHAPES); JE_showVGA(); */ } while (!network_is_sync()) { service_SDL_events(false); network_check(); SDL_Delay(16); } VGAScreen = temp_surface; /* side-effect of game_screen */ } #endif yourInGameMenuRequest = false; //skipStarShowVGA = true; mouseSetRelative(true); } JE_boolean JE_inGameSetup(void) { bool result = false; SDL_Surface *temp_surface = VGAScreen; VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ enum MenuItemIndex { MENU_ITEM_MUSIC_VOLUME = 0, MENU_ITEM_EFFECTS_VOLUME, MENU_ITEM_DETAIL_LEVEL, MENU_ITEM_GAME_SPEED, MENU_ITEM_RETURN_TO_GAME, MENU_ITEM_QUIT, }; const size_t helpIndexes[] = { 14, 14, 27, 28, 25, 26 }; if (shopSpriteSheet.data == NULL) JE_loadCompShapes(&shopSpriteSheet, '1'); // need mouse pointer sprites bool restart = true; const size_t menuItemsCount = COUNTOF(inGameText); size_t selectedIndex = MENU_ITEM_MUSIC_VOLUME; const int yMenuItems = 20; const int dyMenuItems = 20; const int xMenuItem = 10; const int xMenuItemName = xMenuItem; const int wMenuItemName = 110; const int xMenuItemValue = xMenuItemName + wMenuItemName; const int wMenuItemValue = 90; const int wMenuItem = wMenuItemName + wMenuItemValue; const int hMenuItem = 13; for (bool done = false; !done; ) { if (restart) { // Main box JE_barShade(VGAScreen, 3, 13, 217, 137); JE_barShade(VGAScreen, 5, 15, 215, 135); // Help box JE_barShade(VGAScreen, 3, 143, 257, 157); JE_barShade(VGAScreen, 5, 145, 255, 155); memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); mouseCursor = MOUSE_POINTER_NORMAL; restart = false; } // Restore background. memcpy(VGAScreen->pixels, VGAScreen2->pixels, (size_t)VGAScreen->pitch * VGAScreen->h); // Draw menu items. for (size_t i = 0; i < menuItemsCount; ++i) { const int y = yMenuItems + dyMenuItems * i; const char *const name = inGameText[i]; const bool selected = i == selectedIndex; draw_font_hv_shadow(VGAScreen, xMenuItemName, y, name, normal_font, left_aligned, 15, -4 + (selected ? 2 : 0), false, 2); switch (i) { case MENU_ITEM_MUSIC_VOLUME: { JE_barDrawShadow(VGAScreen, xMenuItemValue, y, 1, music_disabled ? 12 : 16, (tyrMusicVolume + 6) / 12, 3, 13); break; } case MENU_ITEM_EFFECTS_VOLUME: { JE_barDrawShadow(VGAScreen, xMenuItemValue, y, 1, samples_disabled ? 12 : 16, (fxVolume + 6) / 12, 3, 13); break; } case MENU_ITEM_DETAIL_LEVEL: { draw_font_hv_shadow(VGAScreen, xMenuItemValue, y, detailLevel[processorType-1], normal_font, left_aligned, 15, -4 + (selected ? 2 : 0), false, 2); break; } case MENU_ITEM_GAME_SPEED: { draw_font_hv_shadow(VGAScreen, xMenuItemValue, y, gameSpeedText[gameSpeed-1], normal_font, left_aligned, 15, -4 + (selected ? 2 : 0), false, 2); break; } } } // Draw help text. JE_outTextAdjust(VGAScreen, 10, 147, mainMenuHelp[helpIndexes[selectedIndex]], 14, 6, TINY_FONT, true); service_SDL_events(true); JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); bool mouseMoved = false; do { SDL_Delay(16); Uint16 oldMouseX = mouse_x; Uint16 oldMouseY = mouse_y; push_joysticks_as_keyboard(); service_SDL_events(false); NETWORK_KEEP_ALIVE(); mouseMoved = mouse_x != oldMouseX || mouse_y != oldMouseY; } while (!(newkey || newmouse || mouseMoved)); // Handle interaction. bool action = false; bool leftAction = false; bool rightAction = false; if (mouseMoved || newmouse) { // Find menu item that was hovered or clicked. if (mouse_x >= xMenuItem && mouse_x < xMenuItem + wMenuItem) { for (size_t i = 0; i < menuItemsCount; ++i) { const int yMenuItem = yMenuItems + dyMenuItems * i; if (mouse_y >= yMenuItem && mouse_y < yMenuItem + hMenuItem) { if (selectedIndex != i) { JE_playSampleNum(S_CURSOR); selectedIndex = i; } if (newmouse && lastmouse_but == SDL_BUTTON_LEFT && lastmouse_x >= xMenuItem && lastmouse_x < xMenuItem + wMenuItem && lastmouse_y >= yMenuItem && lastmouse_y < yMenuItem + hMenuItem) { // Act on menu item via name. if (lastmouse_x >= xMenuItemName && lastmouse_x < xMenuItemName + wMenuItemName) { action = true; } // Act on menu item via value. else if (lastmouse_x >= xMenuItemValue && lastmouse_x < xMenuItemValue + wMenuItemValue) { switch (i) { case MENU_ITEM_MUSIC_VOLUME: { JE_playSampleNum(S_CURSOR); const int w = ((255 + 6) / 12) * (3 + 1) - 1; int value = (lastmouse_x - xMenuItemValue) * 255 / (w - 1); tyrMusicVolume = MIN(MAX(0, value), 255); set_volume(tyrMusicVolume, fxVolume); break; } case MENU_ITEM_EFFECTS_VOLUME: { const int w = ((255 + 6) / 12) * (3 + 1) - 1; int value = (lastmouse_x - xMenuItemValue) * 255 / (w - 1); fxVolume = MIN(MAX(0, value), 255); set_volume(tyrMusicVolume, fxVolume); JE_playSampleNum(S_CURSOR); break; } case MENU_ITEM_DETAIL_LEVEL: case MENU_ITEM_GAME_SPEED: { rightAction = true; break; } default: break; } } } break; } } } } if (newmouse) { if (lastmouse_but == SDL_BUTTON_RIGHT) { JE_playSampleNum(S_SPRING); done = true; } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_UP: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == 0 ? menuItemsCount - 1 : selectedIndex - 1; break; } case SDL_SCANCODE_DOWN: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == menuItemsCount - 1 ? 0 : selectedIndex + 1; break; } case SDL_SCANCODE_LEFT: { leftAction = true; break; } case SDL_SCANCODE_RIGHT: { rightAction = true; break; } case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: { action = true; break; } case SDL_SCANCODE_ESCAPE: { JE_playSampleNum(S_SPRING); done = true; break; } case SDL_SCANCODE_W: { if (selectedIndex == MENU_ITEM_DETAIL_LEVEL) { processorType = 6; JE_initProcessorType(); } break; } default: break; } } if (action) { switch (selectedIndex) { case MENU_ITEM_MUSIC_VOLUME: { JE_playSampleNum(S_SELECT); music_disabled = !music_disabled; break; } case MENU_ITEM_EFFECTS_VOLUME: { samples_disabled = !samples_disabled; JE_playSampleNum(S_SELECT); break; } case MENU_ITEM_RETURN_TO_GAME: { JE_playSampleNum(S_SELECT); done = true; break; } case MENU_ITEM_QUIT: { JE_playSampleNum(S_SELECT); if (constantPlay) JE_tyrianHalt(0); if (isNetworkGame) { /*Tell other computer to exit*/ haltGame = true; playerEndLevel = true; } result = true; done = true; break; } default: break; } } else if (leftAction) { switch (selectedIndex) { case MENU_ITEM_MUSIC_VOLUME: { JE_playSampleNum(S_CURSOR); JE_changeVolume(&tyrMusicVolume, -12, &fxVolume, 0); break; } case MENU_ITEM_EFFECTS_VOLUME: { JE_changeVolume(&tyrMusicVolume, 0, &fxVolume, -12); JE_playSampleNum(S_CURSOR); break; } case MENU_ITEM_DETAIL_LEVEL: { JE_playSampleNum(S_CURSOR); processorType = processorType > 1 ? processorType - 1 : 4; JE_initProcessorType(); JE_setNewGameSpeed(); break; } case MENU_ITEM_GAME_SPEED: { JE_playSampleNum(S_CURSOR); gameSpeed = gameSpeed > 1 ? gameSpeed - 1 : 5; JE_initProcessorType(); JE_setNewGameSpeed(); break; } default: break; } } else if (rightAction) { switch (selectedIndex) { case MENU_ITEM_MUSIC_VOLUME: { JE_playSampleNum(S_CURSOR); JE_changeVolume(&tyrMusicVolume, 12, &fxVolume, 0); break; } case MENU_ITEM_EFFECTS_VOLUME: { JE_changeVolume(&tyrMusicVolume, 0, &fxVolume, 12); JE_playSampleNum(S_CURSOR); break; } case MENU_ITEM_DETAIL_LEVEL: { JE_playSampleNum(S_CURSOR); processorType = processorType < 4 ? processorType + 1 : 1; JE_initProcessorType(); JE_setNewGameSpeed(); break; } case MENU_ITEM_GAME_SPEED: { JE_playSampleNum(S_CURSOR); gameSpeed = gameSpeed < 5 ? gameSpeed + 1 : 1; JE_initProcessorType(); JE_setNewGameSpeed(); break; } default: break; } } } VGAScreen = temp_surface; /* side-effect of game_screen */ return result; } void JE_inGameHelp(void) { SDL_Surface *temp_surface = VGAScreen; VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ //tempScreenSeg = VGAScreenSeg; JE_clearKeyboard(); JE_wipeKey(); JE_barShade(VGAScreen, 1, 1, 262, 182); /*Main Box*/ JE_barShade(VGAScreen, 3, 3, 260, 180); JE_barShade(VGAScreen, 5, 5, 258, 178); JE_barShade(VGAScreen, 7, 7, 256, 176); fill_rectangle_xy(VGAScreen, 9, 9, 254, 174, 0); if (twoPlayerMode) // Two-Player Help { helpBoxColor = 3; helpBoxBrightness = 3; JE_HBox(VGAScreen, 20, 4, 36, 50); // weapon help blit_sprite(VGAScreenSeg, 2, 21, OPTION_SHAPES, 43); helpBoxColor = 5; helpBoxBrightness = 3; JE_HBox(VGAScreen, 55, 20, 37, 40); // sidekick help blit_sprite(VGAScreenSeg, 5, 36, OPTION_SHAPES, 41); helpBoxColor = 5; helpBoxBrightness = 3; JE_HBox(VGAScreen, 40, 43, 34, 44); // shield/armor help blit_sprite(VGAScreenSeg, 2, 79, OPTION_SHAPES, 42); helpBoxColor = 5; helpBoxBrightness = 3; JE_HBox(VGAScreen, 54, 84, 35, 40); helpBoxColor = 5; helpBoxBrightness = 3; JE_HBox(VGAScreen, 5, 126, 38, 55); helpBoxColor = 5; helpBoxBrightness = 3; JE_HBox(VGAScreen, 5, 160, 39, 55); } else { // power bar help blit_sprite(VGAScreenSeg, 15, 5, OPTION_SHAPES, 40); helpBoxColor = 5; helpBoxBrightness = 3; JE_HBox(VGAScreen, 40, 10, 31, 45); // weapon help blit_sprite(VGAScreenSeg, 5, 37, OPTION_SHAPES, 39); helpBoxColor = 5; helpBoxBrightness = 3; JE_HBox(VGAScreen, 40, 40, 32, 44); helpBoxColor = 5; helpBoxBrightness = 3; JE_HBox(VGAScreen, 40, 60, 33, 44); // sidekick help blit_sprite(VGAScreenSeg, 5, 98, OPTION_SHAPES, 41); helpBoxColor = 5; helpBoxBrightness = 3; JE_HBox(VGAScreen, 40, 103, 34, 44); // shield/armor help blit_sprite(VGAScreenSeg, 2, 138, OPTION_SHAPES, 42); helpBoxColor = 5; helpBoxBrightness = 3; JE_HBox(VGAScreen, 54, 143, 35, 40); } // "press a key" blit_sprite(VGAScreenSeg, 16, 189, OPTION_SHAPES, 36); // in-game text area JE_outText(VGAScreenSeg, 120 - JE_textWidth(miscText[5-1], TINY_FONT) / 2 + 20, 190, miscText[5-1], 0, 4); do { service_SDL_events(true); JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); SDL_Delay(16); push_joysticks_as_keyboard(); service_SDL_events(false); NETWORK_KEEP_ALIVE(); } while (!(newkey || newmouse)); textErase = 1; VGAScreen = temp_surface; } void JE_highScoreCheck(void) { if (shopSpriteSheet.data == NULL) JE_loadCompShapes(&shopSpriteSheet, '1'); // need mouse pointer sprite Sint32 temp_score; for (int temp_p = 0; temp_p < (twoPlayerMode ? 2 : 1); ++temp_p) { JE_sortHighScores(); int p = temp_p; if (twoPlayerMode) { // ask for the highest scorer first if (player[0].cash < player[1].cash) p = (temp_p == 0) ? 1 : 0; temp_score = (p == 0) ? player[0].cash : player[1].cash; } else { // single player highscore includes cost of upgrades temp_score = JE_totalScore(&player[0]); } int slot; const int first_slot = (initial_episode_num - 1) * 6 + (twoPlayerMode ? 3 : 0), slot_limit = first_slot + 3; for (slot = first_slot; slot < slot_limit; ++slot) { if (temp_score > saveFiles[slot].highScore1) break; } // did you get a high score? if (slot < slot_limit) { // shift down old scores for (int i = slot_limit - 1; i > slot; --i) { saveFiles[i].highScore1 = saveFiles[i - 1].highScore1; strcpy(saveFiles[i].highScoreName, saveFiles[i - 1].highScoreName); } wait_noinput(false, true, false); JE_clr256(VGAScreen); JE_showVGA(); memcpy(colors, palettes[0], sizeof(colors)); play_song(33); { /* Enter Thy name */ JE_byte flash = 8 * 16 + 10; JE_boolean fadein = true; JE_boolean quit = false, cancel = false; char stemp[30], tempstr[30]; char buffer[256]; strcpy(stemp, " "); temp = 0; JE_barShade(VGAScreen, 65, 55, 255, 155); do { service_SDL_events(true); JE_dString(VGAScreen, JE_fontCenter(miscText[51], FONT_SHAPES), 3, miscText[51], FONT_SHAPES); temp3 = twoPlayerMode ? 58 + p : 53; JE_dString(VGAScreen, JE_fontCenter(miscText[temp3-1], SMALL_FONT_SHAPES), 30, miscText[temp3-1], SMALL_FONT_SHAPES); blit_sprite(VGAScreenSeg, 50, 50, OPTION_SHAPES, 35); // message box if (twoPlayerMode) { sprintf(buffer, "%s %s", miscText[48 + p], miscText[53]); JE_textShade(VGAScreen, 60, 55, buffer, 11, 4, FULL_SHADE); } else { JE_textShade(VGAScreen, 60, 55, miscText[53], 11, 4, FULL_SHADE); } sprintf(buffer, "%s %d", miscText[37], temp_score); JE_textShade(VGAScreen, 70, 70, buffer, 11, 4, FULL_SHADE); do { flash = (flash == 8 * 16 + 10) ? 8 * 16 + 2 : 8 * 16 + 10; temp3 = (temp3 == 6) ? 2 : 6; strncpy(tempstr, stemp, temp); tempstr[temp] = '\0'; JE_outText(VGAScreen, 65, 89, tempstr, 8, 3); tempW = 65 + JE_textWidth(tempstr, TINY_FONT); JE_barShade(VGAScreen, tempW + 2, 90, tempW + 6, 95); fill_rectangle_xy(VGAScreen, tempW + 1, 89, tempW + 5, 94, flash); for (int i = 0; i < 14; i++) { setDelay(1); JE_mouseStart(); JE_showVGA(); if (fadein) { fade_palette(colors, 15, 0, 255); fadein = false; } JE_mouseReplace(); push_joysticks_as_keyboard(); service_wait_delay(); if (newkey || newmouse) break; } } while (!newkey && !newmouse && !new_text); if (!playing) play_song(31); if (mouseButton > 0) { if (mouseX > 56 && mouseX < 142 && mouseY > 123 && mouseY < 149) { quit = true; } else if (mouseX > 151 && mouseX < 237 && mouseY > 123 && mouseY < 149) { quit = true; cancel = true; } } else if (new_text) { for (size_t ti = 0U; last_text[ti] != '\0'; ++ti) { const char c = (unsigned char)last_text[ti] <= 127U ? toupper(last_text[ti]) : 0; if ((c == ' ' || font_ascii[(unsigned char)c] != -1) && temp < 28) { stemp[temp] = c; temp += 1; } } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_BACKSPACE: case SDL_SCANCODE_DELETE: if (temp) { temp--; stemp[temp] = ' '; } break; case SDL_SCANCODE_ESCAPE: quit = true; cancel = true; break; case SDL_SCANCODE_RETURN: quit = true; break; default: break; } } } while (!quit); if (!cancel) { saveFiles[slot].highScore1 = temp_score; strcpy(saveFiles[slot].highScoreName, stemp); saveFiles[slot].highScoreDiff = difficultyLevel; } fade_black(15); JE_loadPic(VGAScreen, 2, false); JE_dString(VGAScreen, JE_fontCenter(miscText[50], FONT_SHAPES), 10, miscText[50], FONT_SHAPES); JE_dString(VGAScreen, JE_fontCenter(episode_name[episodeNum], SMALL_FONT_SHAPES), 35, episode_name[episodeNum], SMALL_FONT_SHAPES); for (int i = first_slot; i < slot_limit; ++i) { if (i != slot) { sprintf(buffer, "~#%d:~ %d", (i - first_slot + 1), saveFiles[i].highScore1); JE_textShade(VGAScreen, 20, ((i - first_slot + 1) * 12) + 65, buffer, 15, 0, FULL_SHADE); JE_textShade(VGAScreen, 150, ((i - first_slot + 1) * 12) + 65, saveFiles[i].highScoreName, 15, 2, FULL_SHADE); } } JE_showVGA(); fade_palette(colors, 15, 0, 255); sprintf(buffer, "~#%d:~ %d", (slot - first_slot + 1), saveFiles[slot].highScore1); frameCountMax = 6; textGlowFont = TINY_FONT; textGlowBrightness = 10; JE_outTextGlow(VGAScreenSeg, 20, (slot - first_slot + 1) * 12 + 65, buffer); textGlowBrightness = 10; JE_outTextGlow(VGAScreenSeg, 150, (slot - first_slot + 1) * 12 + 65, saveFiles[slot].highScoreName); textGlowBrightness = 10; JE_outTextGlow(VGAScreenSeg, JE_fontCenter(miscText[4], TINY_FONT), 180, miscText[4]); JE_showVGA(); if (frameCountMax != 0) wait_input(true, true, true); fade_black(15); } } } } // increases game difficulty based on player's total score / total of players' scores void adjust_difficulty(void) { const float score_multiplier[10] = { 0, // Wimp (doesn't exist) 0.4f, // Easy 0.8f, // Normal 1.3f, // Hard 1.6f, // Impossible 2, // Insanity 2, // Suicide 3, // Maniacal 3, // Zinglon 3, // Nortaneous }; assert(initialDifficulty > 0 && initialDifficulty < 10); const ulong score = twoPlayerMode ? (player[0].cash + player[1].cash) : JE_totalScore(&player[0]), adjusted_score = roundf(score * score_multiplier[initialDifficulty]); uint new_difficulty = 0; if (twoPlayerMode) { if (adjusted_score < 10000) new_difficulty = DIFFICULTY_EASY; else if (adjusted_score < 20000) new_difficulty = DIFFICULTY_NORMAL; else if (adjusted_score < 50000) new_difficulty = DIFFICULTY_HARD; else if (adjusted_score < 80000) new_difficulty = DIFFICULTY_IMPOSSIBLE; else if (adjusted_score < 125000) new_difficulty = DIFFICULTY_INSANITY; else if (adjusted_score < 200000) new_difficulty = DIFFICULTY_SUICIDE; else if (adjusted_score < 400000) new_difficulty = DIFFICULTY_MANIACAL; else if (adjusted_score < 600000) new_difficulty = DIFFICULTY_ZINGLON; else new_difficulty = DIFFICULTY_NORTANEOUS; } else { if (adjusted_score < 40000) new_difficulty = DIFFICULTY_EASY; else if (adjusted_score < 70000) new_difficulty = DIFFICULTY_NORMAL; else if (adjusted_score < 150000) new_difficulty = DIFFICULTY_HARD; else if (adjusted_score < 300000) new_difficulty = DIFFICULTY_IMPOSSIBLE; else if (adjusted_score < 600000) new_difficulty = DIFFICULTY_INSANITY; else if (adjusted_score < 1000000) new_difficulty = DIFFICULTY_SUICIDE; else if (adjusted_score < 2000000) new_difficulty = DIFFICULTY_MANIACAL; else if (adjusted_score < 3000000) new_difficulty = DIFFICULTY_ZINGLON; else new_difficulty = DIFFICULTY_NORTANEOUS; } difficultyLevel = MAX((unsigned)difficultyLevel, new_difficulty); } bool load_next_demo(void) { if (++demo_num > 5) demo_num = 1; char demo_filename[9]; snprintf(demo_filename, sizeof(demo_filename), "demo.%d", demo_num); demo_file = dir_fopen_die(data_dir(), demo_filename, "rb"); // TODO: only play demos from existing file (instead of dying) difficultyLevel = DIFFICULTY_NORMAL; bonusLevelCurrent = false; Uint8 temp; fread_u8_die(&temp, 1, demo_file); JE_initEpisode(temp); fread_die(levelName, 1, 10, demo_file); levelName[10] = '\0'; fread_u8_die(&lvlFileNum, 1, demo_file); fread_u8_die(&player[0].items.weapon[FRONT_WEAPON].id, 1, demo_file); fread_u8_die(&player[0].items.weapon[REAR_WEAPON].id, 1, demo_file); fread_u8_die(&player[0].items.super_arcade_mode, 1, demo_file); fread_u8_die(&player[0].items.sidekick[LEFT_SIDEKICK], 1, demo_file); fread_u8_die(&player[0].items.sidekick[RIGHT_SIDEKICK], 1, demo_file); fread_u8_die(&player[0].items.generator, 1, demo_file); fread_u8_die(&player[0].items.sidekick_level, 1, demo_file); // could probably ignore fread_u8_die(&player[0].items.sidekick_series, 1, demo_file); // could probably ignore fread_u8_die(&initial_episode_num, 1, demo_file); // could probably ignore fread_u8_die(&player[0].items.shield, 1, demo_file); fread_u8_die(&player[0].items.special, 1, demo_file); fread_u8_die(&player[0].items.ship, 1, demo_file); for (uint i = 0; i < 2; ++i) fread_u8_die(&player[0].items.weapon[i].power, 1, demo_file); Uint8 unused[3]; fread_u8_die(unused, 3, demo_file); fread_u8_die(&levelSong, 1, demo_file); demo_keys = 0; Uint8 temp2[2] = { 0, 0 }; fread_u8(temp2, 2, demo_file); demo_keys_wait = (temp2[0] << 8) | temp2[1]; printf("loaded demo '%s'\n", demo_filename); return true; } bool replay_demo_keys(void) { while (demo_keys_wait == 0) { demo_keys = 0; fread_u8(&demo_keys, 1, demo_file); Uint8 temp2[2] = { 0, 0 }; fread_u8(temp2, 2, demo_file); demo_keys_wait = (temp2[0] << 8) | temp2[1]; if (feof(demo_file)) { // no more keys return false; } } demo_keys_wait--; if (demo_keys & (1 << 0)) player[0].y -= CURRENT_KEY_SPEED; if (demo_keys & (1 << 1)) player[0].y += CURRENT_KEY_SPEED; if (demo_keys & (1 << 2)) player[0].x -= CURRENT_KEY_SPEED; if (demo_keys & (1 << 3)) player[0].x += CURRENT_KEY_SPEED; button[0] = (bool)(demo_keys & (1 << 4)); button[3] = (bool)(demo_keys & (1 << 5)); button[1] = (bool)(demo_keys & (1 << 6)); button[2] = (bool)(demo_keys & (1 << 7)); return true; } /*Street Fighter codes*/ void JE_SFCodes(JE_byte playerNum_, JE_integer PX_, JE_integer PY_, JE_integer mouseX_, JE_integer mouseY_) { JE_byte temp, temp2, temp3, temp4, temp5; uint ship = player[playerNum_-1].items.ship; /*Get direction*/ if (playerNum_ == 2 && ship < 15) { ship = 0; } if (ship < 15) { temp2 = (mouseY_ > PY_) + /*UP*/ (mouseY_ < PY_) + /*DOWN*/ (PX_ < mouseX_) + /*LEFT*/ (PX_ > mouseX_); /*RIGHT*/ temp = (mouseY_ > PY_) * 1 + /*UP*/ (mouseY_ < PY_) * 2 + /*DOWN*/ (PX_ < mouseX_) * 3 + /*LEFT*/ (PX_ > mouseX_) * 4; /*RIGHT*/ if (temp == 0) // no direction being pressed { if (!button[0]) // if fire button is released { temp = 9; temp2 = 1; } else { temp2 = 0; temp = 99; } } if (temp2 == 1) // if exactly one direction pressed or fire button is released { temp += button[0] * 4; temp3 = superTyrian ? 21 : 3; for (temp2 = 0; temp2 < temp3; temp2++) { /*Use SuperTyrian ShipCombos or not?*/ temp5 = superTyrian ? shipCombosB[temp2] : shipCombos[ship][temp2]; // temp5 == selected combo in ship if (temp5 == 0) /* combo doesn't exists */ { // mark twiddles as cancelled/finished SFCurrentCode[playerNum_-1][temp2] = 0; } else { // get next combo key temp4 = keyboardCombos[temp5-1][SFCurrentCode[playerNum_-1][temp2]]; // correct key if (temp4 == temp) { SFCurrentCode[playerNum_-1][temp2]++; temp4 = keyboardCombos[temp5-1][SFCurrentCode[playerNum_-1][temp2]]; if (temp4 > 100 && temp4 <= 100 + SPECIAL_NUM) { SFCurrentCode[playerNum_-1][temp2] = 0; SFExecuted[playerNum_-1] = temp4 - 100; } } else { if ((temp != 9) && (temp4 - 1) % 4 != (temp - 1) % 4 && (SFCurrentCode[playerNum_-1][temp2] == 0 || keyboardCombos[temp5-1][SFCurrentCode[playerNum_-1][temp2]-1] != temp)) { SFCurrentCode[playerNum_-1][temp2] = 0; } } } } } } } void JE_sort(void) { JE_byte a, b; for (a = 0; a < 2; a++) { for (b = a + 1; b < 3; b++) { if (saveFiles[temp + a].highScore1 < saveFiles[temp + b].highScore1) { JE_longint tempLI; char tempStr[30]; JE_byte tempByte; tempLI = saveFiles[temp + a].highScore1; saveFiles[temp + a].highScore1 = saveFiles[temp + b].highScore1; saveFiles[temp + b].highScore1 = tempLI; strcpy(tempStr, saveFiles[temp + a].highScoreName); strcpy(saveFiles[temp + a].highScoreName, saveFiles[temp + b].highScoreName); strcpy(saveFiles[temp + b].highScoreName, tempStr); tempByte = saveFiles[temp + a].highScoreDiff; saveFiles[temp + a].highScoreDiff = saveFiles[temp + b].highScoreDiff; saveFiles[temp + b].highScoreDiff = tempByte; } } } } void JE_playCredits(void) { enum { lines_max = 131 }; enum { line_max_length = 65 }; char credstr[lines_max][line_max_length + 1]; int lines = 0; JE_byte currentpic = 0, fade = 0; JE_shortint fadechg = 1; JE_byte currentship = 0; JE_integer shipx = 0, shipxwait = 0; JE_shortint shipxc = 0, shipxca = 0; load_sprites_file(EXTRA_SHAPES, "estsc.shp"); setDelay2(1000); play_song(8); // load credits text FILE *f = dir_fopen_die(data_dir(), "tyrian.cdt", "rb"); for (lines = 0; lines < lines_max; ++lines) { read_encrypted_pascal_string(credstr[lines], sizeof(credstr[lines]), f); } fclose(f); memcpy(colors, palettes[6-1], sizeof(colors)); JE_clr256(VGAScreen); JE_showVGA(); fade_palette(colors, 2, 0, 255); //tempScreenSeg = VGAScreenSeg; const int ticks_max = lines * 20 * 3; for (int ticks = 0; ticks < ticks_max; ++ticks) { setDelay(1); JE_clr256(VGAScreen); blit_sprite_hv(VGAScreenSeg, 319 - sprite(EXTRA_SHAPES, currentpic)->width, 100 - (sprite(EXTRA_SHAPES, currentpic)->height / 2), EXTRA_SHAPES, currentpic, 0x0, fade - 15); fade += fadechg; if (fade == 0 && fadechg == -1) { fadechg = 1; ++currentpic; if (currentpic >= sprite_table[EXTRA_SHAPES].count) currentpic = 0; } if (fade == 15) fadechg = 0; if (getDelayTicks2() == 0) { fadechg = -1; setDelay2(900); } if (ticks % 200 == 0) { currentship = (mt_rand() % 11) + 1; shipxwait = (mt_rand() % 80) + 10; if ((mt_rand() % 2) == 1) { shipx = 1; shipxc = 0; shipxca = 1; } else { shipx = 900; shipxc = 0; shipxca = -1; } } shipxwait--; if (shipxwait == 0) { if (shipx == 1 || shipx == 900) shipxc = 0; shipxca = -shipxca; shipxwait = (mt_rand() % 40) + 15; } shipxc += shipxca; shipx += shipxc; if (shipx < 1) { shipx = 1; shipxwait = 1; } if (shipx > 900) { shipx = 900; shipxwait = 1; } int tmp_unknown = shipxc * shipxc; if (450 + tmp_unknown < 0 || 450 + tmp_unknown > 900) { if (shipxca < 0 && shipxc < 0) shipxwait = 1; if (shipxca > 0 && shipxc > 0) shipxwait = 1; } uint ship_sprite = ships[currentship].shipgraphic; if (shipxc < -10) ship_sprite -= (shipxc < -20) ? 4 : 2; else if (shipxc > 10) ship_sprite += (shipxc > 20) ? 4 : 2; blit_sprite2x2(VGAScreen, shipx / 40, 184 - (ticks % 200), spriteSheet9, ship_sprite); const int bottom_line = (ticks / 3) / 20; int y = 20 - ((ticks / 3) % 20); for (int line = bottom_line - 10; line < bottom_line; ++line) { if (line >= 0 && line < lines_max) { if (strcmp(&credstr[line][0], ".") != 0 && strlen(credstr[line])) { const Uint8 color = credstr[line][0] - 65; const char *text = &credstr[line][1]; const int x = 110 - JE_textWidth(text, SMALL_FONT_SHAPES) / 2; JE_outTextAdjust(VGAScreen, x + abs((y / 18) % 4 - 2) - 1, y - 1, text, color, -8, SMALL_FONT_SHAPES, false); JE_outTextAdjust(VGAScreen, x, y, text, color, -2, SMALL_FONT_SHAPES, false); } } y += 20; } fill_rectangle_xy(VGAScreen, 0, 0, 319, 10, 0); fill_rectangle_xy(VGAScreen, 0, 190, 319, 199, 0); if (currentpic == sprite_table[EXTRA_SHAPES].count - 1) JE_outTextAdjust(VGAScreen, 5, 180, miscText[54], 2, -2, SMALL_FONT_SHAPES, false); // levels-in-episode if (bottom_line == lines_max - 8) fade_song(); if (ticks == ticks_max - 1) { --ticks; play_song(9); } NETWORK_KEEP_ALIVE(); JE_showVGA(); wait_delay(); if (JE_anyButton()) break; } fade_black(10); free_sprites(EXTRA_SHAPES); } void JE_endLevelAni(void) { JE_word x, y; JE_byte temp; char tempStr[256]; Sint8 i; if (!constantPlay) { // grant shipedit privileges // special if (player[0].items.special < 21) saveTemp[SAVE_FILES_SIZE + 81 + player[0].items.special] = 1; for (uint p = 0; p < COUNTOF(player); ++p) { // front, rear for (uint i = 0; i < COUNTOF(player[p].items.weapon); ++i) saveTemp[SAVE_FILES_SIZE + player[p].items.weapon[i].id] = 1; // options for (uint i = 0; i < COUNTOF(player[p].items.sidekick); ++i) saveTemp[SAVE_FILES_SIZE + 51 + player[p].items.sidekick[i]] = 1; } } adjust_difficulty(); player[0].last_items = player[0].items; strcpy(lastLevelName, levelName); JE_wipeKey(); frameCountMax = 4; textGlowFont = SMALL_FONT_SHAPES; SDL_Color white = { 255, 255, 255 }; set_colors(white, 254, 254); if (!levelTimer || levelTimerCountdown > 0 || !(episodeNum == 4)) JE_playSampleNum(V_LEVEL_END); else play_song(21); if (bonusLevel) { JE_outTextGlow(VGAScreenSeg, 20, 20, miscText[17-1]); } else if (all_players_alive()) { sprintf(tempStr, "%s %s", miscText[27-1], levelName); // "Completed" JE_outTextGlow(VGAScreenSeg, 20, 20, tempStr); } else { sprintf(tempStr, "%s %s", miscText[62-1], levelName); // "Exiting" JE_outTextGlow(VGAScreenSeg, 20, 20, tempStr); } if (twoPlayerMode) { for (uint i = 0; i < 2; ++i) { snprintf(tempStr, sizeof(tempStr), "%s %lu", miscText[40 + i], player[i].cash); JE_outTextGlow(VGAScreenSeg, 30, 50 + 20 * i, tempStr); } } else { sprintf(tempStr, "%s %lu", miscText[28-1], player[0].cash); JE_outTextGlow(VGAScreenSeg, 30, 50, tempStr); } temp = (totalEnemy == 0) ? 0 : roundf(enemyKilled * 100 / totalEnemy); sprintf(tempStr, "%s %d%%", miscText[63-1], temp); JE_outTextGlow(VGAScreenSeg, 40, 90, tempStr); if (!constantPlay) editorLevel += temp / 5; if (!onePlayerAction && !twoPlayerMode) { JE_outTextGlow(VGAScreenSeg, 30, 120, miscText[4-1]); /*Cubes*/ if (cubeMax > 0) { if (cubeMax > 4) cubeMax = 4; if (frameCountMax != 0) frameCountMax = 1; for (temp = 1; temp <= cubeMax; temp++) { NETWORK_KEEP_ALIVE(); JE_playSampleNum(S_ITEM); x = 20 + 30 * temp; y = 135; JE_drawCube(VGAScreenSeg, x, y, 9, 0); JE_showVGA(); for (i = -15; i <= 10; i++) { setDelay(frameCountMax); blit_sprite_hv(VGAScreenSeg, x, y, OPTION_SHAPES, 25, 0x9, i); if (JE_anyButton()) frameCountMax = 0; JE_showVGA(); wait_delay(); } for (i = 10; i >= 0; i--) { setDelay(frameCountMax); blit_sprite_hv(VGAScreenSeg, x, y, OPTION_SHAPES, 25, 0x9, i); if (JE_anyButton()) frameCountMax = 0; JE_showVGA(); wait_delay(); } } } else { JE_outTextGlow(VGAScreenSeg, 50, 135, miscText[15-1]); } } if (frameCountMax != 0) { frameCountMax = 6; temp = 1; } else { temp = 0; } temp2 = twoPlayerMode ? 150 : 160; JE_outTextGlow(VGAScreenSeg, 90, temp2, miscText[5-1]); if (!constantPlay) { do { setDelay(1); NETWORK_KEEP_ALIVE(); wait_delay(); } while (!(JE_anyButton() || (frameCountMax == 0 && temp == 1))); } wait_noinput(false, false, true); // TODO: should up the joystick repeat temporarily instead fade_black(15); JE_clr256(VGAScreen); } void JE_drawCube(SDL_Surface * screen, JE_word x, JE_word y, JE_byte filter, JE_byte brightness) { blit_sprite_dark(screen, x + 4, y + 4, OPTION_SHAPES, 25, false); blit_sprite_dark(screen, x + 3, y + 3, OPTION_SHAPES, 25, false); blit_sprite_hv(screen, x, y, OPTION_SHAPES, 25, filter, brightness); } void JE_handleChat(void) { // STUB(); Annoying piece of crap =P } bool str_pop_int(char *str, int *val) { bool success = false; char buf[256]; assert(strlen(str) < sizeof(buf)); // grab the value from str char *end; *val = strtol(str, &end, 10); if (end != str) { success = true; // shift the rest to the beginning strcpy(buf, end); strcpy(str, buf); } return success; } void JE_operation(JE_byte slot) { JE_byte flash; char stemp[21]; char tempStr[51]; if (!performSave) { if (saveFiles[slot-1].level > 0) { gameJustLoaded = true; JE_loadGame(slot); gameLoaded = true; } } else if (slot % 11 != 0) { strcpy(stemp, " "); memcpy(stemp, saveFiles[slot-1].name, strlen(saveFiles[slot-1].name)); temp = strlen(stemp); while (stemp[temp-1] == ' ' && --temp); flash = 8 * 16 + 10; wait_noinput(false, true, false); JE_barShade(VGAScreen, 65, 55, 255, 155); bool quit = false; while (!quit) { service_SDL_events(true); blit_sprite(VGAScreen, 50, 50, OPTION_SHAPES, 35); // message box JE_textShade(VGAScreen, 60, 55, miscText[1-1], 11, 4, DARKEN); JE_textShade(VGAScreen, 70, 70, levelName, 11, 4, DARKEN); do { flash = (flash == 8 * 16 + 10) ? 8 * 16 + 2 : 8 * 16 + 10; temp3 = (temp3 == 6) ? 2 : 6; strcpy(tempStr, miscText[2-1]); strncat(tempStr, stemp, temp); JE_outText(VGAScreen, 65, 89, tempStr, 8, 3); tempW = 65 + JE_textWidth(tempStr, TINY_FONT); JE_barShade(VGAScreen, tempW + 2, 90, tempW + 6, 95); fill_rectangle_xy(VGAScreen, tempW + 1, 89, tempW + 5, 94, flash); int text_x = 54 + 45 - (JE_textWidth(miscText[9], FONT_SHAPES) / 2); JE_outTextAdjust(VGAScreen, text_x, 128, miscText[9], 15, -5, FONT_SHAPES, true); text_x = 149 + 45 - (JE_textWidth(miscText[10], FONT_SHAPES) / 2); JE_outTextAdjust(VGAScreen, text_x, 128, miscText[10], 15, -5, FONT_SHAPES, true); for (int i = 0; i < 14; i++) { setDelay(1); push_joysticks_as_keyboard(); service_wait_delay(); JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); if (newkey || newmouse || new_text) break; } } while (!newkey && !newmouse && !new_text); if (mouseButton > 0) { if (lastmouse_x > 56 && lastmouse_x < 142 && lastmouse_y > 123 && lastmouse_y < 149) { quit = true; JE_saveGame(slot, stemp); JE_playSampleNum(S_SELECT); } else if (lastmouse_x > 151 && lastmouse_x < 237 && lastmouse_y > 123 && lastmouse_y < 149) { quit = true; JE_playSampleNum(S_SPRING); } } else if (new_text) { for (size_t ti = 0U; last_text[ti] != '\0'; ++ti) { const char c = (unsigned char)last_text[ti] <= 127U ? toupper(last_text[ti]) : 0; if ((c == ' ' || font_ascii[(unsigned char)c] != -1) && temp < 14) { JE_playSampleNum(S_CURSOR); stemp[temp] = c; temp += 1; } } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_BACKSPACE: case SDL_SCANCODE_DELETE: if (temp) { temp--; stemp[temp] = ' '; JE_playSampleNum(S_CLICK); } break; case SDL_SCANCODE_ESCAPE: quit = true; JE_playSampleNum(S_SPRING); break; case SDL_SCANCODE_RETURN: quit = true; JE_saveGame(slot, stemp); JE_playSampleNum(S_SELECT); break; default: break; } } } } wait_noinput(false, true, false); } void JE_inGameDisplays(void) { char stemp[21]; char tempstr[256]; for (uint i = 0; i < ((twoPlayerMode && !galagaMode) ? 2 : 1); ++i) { snprintf(tempstr, sizeof(tempstr), "%lu", player[i].cash); JE_textShade(VGAScreen, 30 + 200 * i, 175, tempstr, 2, 4, FULL_SHADE); } /*Special Weapon?*/ if (player[0].items.special > 0) blit_sprite2x2(VGAScreen, 25, 1, spriteSheet10, special[player[0].items.special].itemgraphic); /*Lives Left*/ if (onePlayerAction || twoPlayerMode) { for (int temp = 0; temp < (onePlayerAction ? 1 : 2); temp++) { const uint extra_lives = *player[temp].lives - 1; int y = (temp == 0 && player[0].items.special > 0) ? 35 : 15; tempW = (temp == 0) ? 30: 270; if (extra_lives >= 5) { blit_sprite2(VGAScreen, tempW, y, spriteSheet9, 285); tempW = (temp == 0) ? 45 : 250; sprintf(tempstr, "%d", extra_lives); JE_textShade(VGAScreen, tempW, y + 3, tempstr, 15, 1, FULL_SHADE); } else if (extra_lives >= 1) { for (uint i = 0; i < extra_lives; ++i) { blit_sprite2(VGAScreen, tempW, y, spriteSheet9, 285); tempW += (temp == 0) ? 12 : -12; } } strcpy(stemp, (temp == 0) ? miscText[49-1] : miscText[50-1]); if (isNetworkGame) { strcpy(stemp, JE_getName(temp+1)); } tempW = (temp == 0) ? 28 : (285 - JE_textWidth(stemp, TINY_FONT)); JE_textShade(VGAScreen, tempW, y - 7, stemp, 2, 6, FULL_SHADE); } } /*Super Bombs!!*/ for (uint i = 0; i < COUNTOF(player); ++i) { int x = (i == 0) ? 30 : 270; for (uint j = player[i].superbombs; j > 0; --j) { blit_sprite2(VGAScreen, x, 160, spriteSheet9, 304); x += (i == 0) ? 12 : -12; } } if (youAreCheating) { JE_outText(VGAScreen, 90, 170, "Cheaters always prosper.", 3, 4); } } void JE_mainKeyboardInput(void) { JE_gammaCheck(); /* { Network Request Commands } */ if (!isNetworkGame) { /* { Edited Ships } for Player 1 */ if (extraAvail && keysactive[SDL_SCANCODE_TAB] && !isNetworkGame && !superTyrian) { for (x = SDL_SCANCODE_1; x <= SDL_SCANCODE_0; x++) { if (keysactive[x]) { int z = x - SDL_SCANCODE_1 + 1; player[0].items.ship = 90 + z; /*Ships*/ z = (z - 1) * 15; player[0].items.weapon[FRONT_WEAPON].id = extraShips[z + 1]; player[0].items.weapon[REAR_WEAPON].id = extraShips[z + 2]; player[0].items.special = extraShips[z + 3]; player[0].items.sidekick[LEFT_SIDEKICK] = extraShips[z + 4]; player[0].items.sidekick[RIGHT_SIDEKICK] = extraShips[z + 5]; player[0].items.generator = extraShips[z + 6]; /*Armor*/ player[0].items.shield = extraShips[z + 8]; memset(shotMultiPos, 0, sizeof(shotMultiPos)); if (player[0].weapon_mode > JE_portConfigs()) player[0].weapon_mode = 1; tempW = player[0].armor; JE_getShipInfo(); if (player[0].armor > tempW && editShip1) player[0].armor = tempW; else editShip1 = true; SDL_Surface *temp_surface = VGAScreen; VGAScreen = VGAScreenSeg; JE_wipeShieldArmorBars(); JE_drawArmor(); JE_drawShield(); VGAScreen = temp_surface; JE_drawOptions(); keysactive[x] = false; } } } /* for Player 2 */ if (extraAvail && keysactive[SDL_SCANCODE_CAPSLOCK] && !isNetworkGame && !superTyrian) { for (x = SDL_SCANCODE_1; x <= SDL_SCANCODE_0; x++) { if (keysactive[x]) { int z = x - SDL_SCANCODE_1 + 1; player[1].items.ship = 90 + z; z = (z - 1) * 15; player[1].items.weapon[FRONT_WEAPON].id = extraShips[z + 1]; player[1].items.weapon[REAR_WEAPON].id = extraShips[z + 2]; player[1].items.special = extraShips[z + 3]; player[1].items.sidekick[LEFT_SIDEKICK] = extraShips[z + 4]; player[1].items.sidekick[RIGHT_SIDEKICK] = extraShips[z + 5]; player[1].items.generator = extraShips[z + 6]; /*Armor*/ player[1].items.shield = extraShips[z + 8]; memset(shotMultiPos, 0, sizeof(shotMultiPos)); if (player[1].weapon_mode > JE_portConfigs()) player[1].weapon_mode = 1; tempW = player[1].armor; JE_getShipInfo(); if (player[1].armor > tempW && editShip2) player[1].armor = tempW; else editShip2 = true; SDL_Surface *temp_surface = VGAScreen; VGAScreen = VGAScreenSeg; JE_wipeShieldArmorBars(); JE_drawArmor(); JE_drawShield(); VGAScreen = temp_surface; JE_drawOptions(); keysactive[x] = false; } } } } /* { In-Game Help } */ if (keysactive[SDL_SCANCODE_F1]) { if (isNetworkGame) { helpRequest = true; } else { JE_inGameHelp(); skipStarShowVGA = true; } } /* {!Activate Nort Ship!} */ if (keysactive[SDL_SCANCODE_F2] && keysactive[SDL_SCANCODE_F4] && keysactive[SDL_SCANCODE_F6] && keysactive[SDL_SCANCODE_F7] && keysactive[SDL_SCANCODE_F9] && keysactive[SDL_SCANCODE_BACKSLASH] && keysactive[SDL_SCANCODE_SLASH]) { if (isNetworkGame) { nortShipRequest = true; } else { player[0].items.ship = 12; // Nort Ship player[0].items.special = 13; // Astral Zone player[0].items.weapon[FRONT_WEAPON].id = 36; // NortShip Super Pulse player[0].items.weapon[REAR_WEAPON].id = 37; // NortShip Spreader shipGr = 1; } } /* {Cheating} */ if (!isNetworkGame && !twoPlayerMode && !superTyrian && superArcadeMode == SA_NONE) { if (keysactive[SDL_SCANCODE_F2] && keysactive[SDL_SCANCODE_F3] && keysactive[SDL_SCANCODE_F6]) { youAreCheating = !youAreCheating; keysactive[SDL_SCANCODE_F2] = false; } if (keysactive[SDL_SCANCODE_F2] && keysactive[SDL_SCANCODE_F3] && (keysactive[SDL_SCANCODE_F4] || keysactive[SDL_SCANCODE_F5])) { for (uint i = 0; i < COUNTOF(player); ++i) player[i].armor = 0; youAreCheating = !youAreCheating; JE_drawTextWindow(miscText[63-1]); } if (constantPlay && keysactive[SDL_SCANCODE_C]) { youAreCheating = !youAreCheating; keysactive[SDL_SCANCODE_C] = false; } } if (superTyrian) { youAreCheating = false; } /* {Personal Commands} */ /* {DEBUG} */ if (keysactive[SDL_SCANCODE_F10] && keysactive[SDL_SCANCODE_BACKSPACE]) { keysactive[SDL_SCANCODE_F10] = false; debug = !debug; debugHist = 1; debugHistCount = 1; /* YKS: clock ticks since midnight replaced by SDL_GetTicks */ lastDebugTime = SDL_GetTicks(); } /* {CHEAT-SKIP LEVEL} */ if (keysactive[SDL_SCANCODE_F2] && keysactive[SDL_SCANCODE_F6] && (keysactive[SDL_SCANCODE_F7] || keysactive[SDL_SCANCODE_F8]) && !keysactive[SDL_SCANCODE_F9] && !superTyrian && superArcadeMode == SA_NONE) { if (isNetworkGame) { skipLevelRequest = true; } else { levelTimer = true; levelTimerCountdown = 0; endLevel = true; levelEnd = 40; } } /* pause game */ pause_pressed = pause_pressed || keysactive[SDL_SCANCODE_P]; /* in-game setup */ ingamemenu_pressed = ingamemenu_pressed || keysactive[SDL_SCANCODE_ESCAPE]; if (keysactive[SDL_SCANCODE_BACKSPACE]) { /* toggle screenshot pause */ if (keysactive[SDL_SCANCODE_NUMLOCKCLEAR]) superPause = !superPause; /* {SMOOTHIES} */ if (keysactive[SDL_SCANCODE_F12] && keysactive[SDL_SCANCODE_SCROLLLOCK]) { for (temp = SDL_SCANCODE_2; temp <= SDL_SCANCODE_9; temp++) if (keysactive[temp]) smoothies[temp-SDL_SCANCODE_2] = !smoothies[temp-SDL_SCANCODE_2]; if (keysactive[SDL_SCANCODE_0]) smoothies[8] = !smoothies[8]; } else /* {CYCLE THROUGH FILTER COLORS} */ if (keysactive[SDL_SCANCODE_MINUS]) { if (levelFilter == -99) { levelFilter = 0; } else { levelFilter++; if (levelFilter == 16) levelFilter = -99; } } else /* {HYPER-SPEED} */ if (keysactive[SDL_SCANCODE_1]) { fastPlay++; if (fastPlay > 2) fastPlay = 0; keysactive[SDL_SCANCODE_1] = false; JE_setNewGameSpeed(); } /* {IN-GAME RANDOM MUSIC SELECTION} */ if (keysactive[SDL_SCANCODE_SCROLLLOCK]) play_song(mt_rand() % MUSIC_NUM); } } void JE_pauseGame(void) { mouseSetRelative(false); JE_boolean done = false; JE_word mouseX, mouseY; SDL_Surface *temp_surface = VGAScreen; VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ //tempScreenSeg = VGAScreenSeg; // sega000 if (!superPause) { JE_dString(VGAScreenSeg, 120, 90, miscText[22], FONT_SHAPES); VGAScreen = VGAScreenSeg; JE_showVGA(); } set_volume(tyrMusicVolume / 2, fxVolume); #ifdef WITH_NETWORK if (isNetworkGame) { network_prepare(PACKET_GAME_PAUSE); network_send(4); // PACKET_GAME_PAUSE while (true) { service_SDL_events(false); if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_GAME_PAUSE) { network_update(); break; } network_update(); network_check(); SDL_Delay(16); } } #endif wait_noinput(false, false, true); // TODO: should up the joystick repeat temporarily instead do { setDelay(2); push_joysticks_as_keyboard(); service_SDL_events(true); if ((newkey && lastkey_scan != SDL_SCANCODE_LCTRL && lastkey_scan != SDL_SCANCODE_RCTRL && lastkey_scan != SDL_SCANCODE_LALT && lastkey_scan != SDL_SCANCODE_RALT) || JE_mousePosition(&mouseX, &mouseY) > 0) { #ifdef WITH_NETWORK if (isNetworkGame) { network_prepare(PACKET_WAITING); network_send(4); // PACKET_WAITING } #endif done = true; } #ifdef WITH_NETWORK if (isNetworkGame) { network_check(); if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_WAITING) { network_check(); done = true; } } #endif wait_delay(); } while (!done); #ifdef WITH_NETWORK if (isNetworkGame) { while (!network_is_sync()) { service_SDL_events(false); network_check(); SDL_Delay(16); } } #endif set_volume(tyrMusicVolume, fxVolume); //skipStarShowVGA = true; VGAScreen = temp_surface; /* side-effect of game_screen */ mouseSetRelative(true); } void JE_playerMovement(Player *this_player, JE_byte inputDevice, JE_byte playerNum_, JE_word shipGr_, Sprite2_array *shipGrPtr_, JE_word *mouseX_, JE_word *mouseY_) { JE_integer mouseXC, mouseYC; JE_integer accelXC, accelYC; if (playerNum_ == 2 || !twoPlayerMode) { tempW = weaponPort[this_player->items.weapon[REAR_WEAPON].id].opnum; if (this_player->weapon_mode > tempW) this_player->weapon_mode = 1; } #ifdef WITH_NETWORK if (isNetworkGame && thisPlayerNum == playerNum_) { network_state_prepare(); memset(&packet_state_out[0]->data[4], 0, 10); } #endif redo: if (isNetworkGame) { inputDevice = 0; } mouseXC = 0; mouseYC = 0; accelXC = 0; accelYC = 0; bool link_gun_analog = false; float link_gun_angle = 0; /* Draw Player */ if (!this_player->is_alive) { if (this_player->exploding_ticks > 0) { --this_player->exploding_ticks; if (levelEndFxWait > 0) { levelEndFxWait--; } else { levelEndFxWait = (mt_rand() % 6) + 3; if ((mt_rand() % 3) == 1) soundQueue[6] = S_EXPLOSION_9; else soundQueue[5] = S_EXPLOSION_11; } int explosion_x = this_player->x + (mt_rand() % 32) - 16; int explosion_y = this_player->y + (mt_rand() % 32) - 16; JE_setupExplosionLarge(false, 0, explosion_x, explosion_y + 7); JE_setupExplosionLarge(false, 0, this_player->x, this_player->y + 7); if (levelEnd > 0) levelEnd--; } else { if (twoPlayerMode || onePlayerAction) // if arcade mode { if (*this_player->lives > 1) // respawn if any extra lives { --(*this_player->lives); reallyEndLevel = false; shotMultiPos[playerNum_-1] = 0; calc_purple_balls_needed(this_player); twoPlayerLinked = false; if (galagaMode) twoPlayerMode = false; this_player->y = 160; this_player->invulnerable_ticks = 100; this_player->is_alive = true; endLevel = false; if (galagaMode || episodeNum == 4) this_player->armor = this_player->initial_armor; else this_player->armor = this_player->initial_armor / 2; if (galagaMode) this_player->shield = 0; else this_player->shield = this_player->shield_max / 2; VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ JE_drawArmor(); JE_drawShield(); VGAScreen = game_screen; /* side-effect of game_screen */ goto redo; } else { if (galagaMode) twoPlayerMode = false; if (allPlayersGone && isNetworkGame) reallyEndLevel = true; } } } } else if (constantDie) { // finished exploding? start dying again if (this_player->exploding_ticks == 0) { this_player->shield = 0; if (this_player->armor > 0) { --this_player->armor; } else { this_player->is_alive = false; this_player->exploding_ticks = 60; levelEnd = 40; } JE_wipeShieldArmorBars(); VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ JE_drawArmor(); VGAScreen = game_screen; /* side-effect of game_screen */ // as if instant death weren't enough, player also gets infinite lives in order to enjoy an infinite number of deaths -_- if (*player[0].lives < 11) ++(*player[0].lives); } } if (!this_player->is_alive) { explosionFollowAmountX = explosionFollowAmountY = 0; return; } if (!endLevel) { *mouseX_ = this_player->x; *mouseY_ = this_player->y; button[1-1] = false; button[2-1] = false; button[3-1] = false; button[4-1] = false; /* --- Movement Routine Beginning --- */ if (!isNetworkGame || playerNum_ == thisPlayerNum) { if (endLevel) { this_player->y -= 2; } else { if (record_demo || play_demo) inputDevice = 1; // keyboard is required device for demo recording // demo playback input if (play_demo) { if (!replay_demo_keys()) { endLevel = true; levelEnd = 40; } } /* joystick input */ if ((inputDevice == 0 || inputDevice >= 3) && joysticks > 0) { int j = inputDevice == 0 ? 0 : inputDevice - 3; int j_max = inputDevice == 0 ? joysticks : inputDevice - 3 + 1; for (; j < j_max; j++) { poll_joystick(j); if (joystick[j].analog) { mouseXC += joystick_axis_reduce(j, joystick[j].x); mouseYC += joystick_axis_reduce(j, joystick[j].y); link_gun_analog = joystick_analog_angle(j, &link_gun_angle); } else { this_player->x += (joystick[j].direction[3] ? -CURRENT_KEY_SPEED : 0) + (joystick[j].direction[1] ? CURRENT_KEY_SPEED : 0); this_player->y += (joystick[j].direction[0] ? -CURRENT_KEY_SPEED : 0) + (joystick[j].direction[2] ? CURRENT_KEY_SPEED : 0); } button[0] |= joystick[j].action[0]; button[1] |= joystick[j].action[2]; button[2] |= joystick[j].action[3]; button[3] |= joystick[j].action_pressed[1]; ingamemenu_pressed |= joystick[j].action_pressed[4]; pause_pressed |= joystick[j].action_pressed[5]; } } service_SDL_events(false); /* mouse input */ if ((inputDevice == 0 || inputDevice == 2) && has_mouse) { button[0] |= mouse_pressed[0]; button[1] |= mouse_pressed[1]; button[2] |= mouse_has_three_buttons ? mouse_pressed[2] : mouse_pressed[1]; Sint32 mouseXR; Sint32 mouseYR; mouseGetRelativePosition(&mouseXR, &mouseYR); mouseXC += mouseXR; mouseYC += mouseYR; } /* keyboard input */ if ((inputDevice == 0 || inputDevice == 1) && !play_demo) { if (keysactive[keySettings[KEY_SETTING_UP]]) this_player->y -= CURRENT_KEY_SPEED; if (keysactive[keySettings[KEY_SETTING_DOWN]]) this_player->y += CURRENT_KEY_SPEED; if (keysactive[keySettings[KEY_SETTING_LEFT]]) this_player->x -= CURRENT_KEY_SPEED; if (keysactive[keySettings[KEY_SETTING_RIGHT]]) this_player->x += CURRENT_KEY_SPEED; button[0] = button[0] || keysactive[keySettings[KEY_SETTING_FIRE]]; button[3] = button[3] || keysactive[keySettings[KEY_SETTING_CHANGE_FIRE]]; button[1] = button[1] || keysactive[keySettings[KEY_SETTING_LEFT_SIDEKICK]]; button[2] = button[2] || keysactive[keySettings[KEY_SETTING_RIGHT_SIDEKICK]]; if (constantPlay) { for (unsigned int i = 0; i < 4; i++) button[i] = true; ++this_player->y; this_player->x += constantLastX; } // TODO: check if demo recording still works if (record_demo) { bool new_input = false; for (unsigned int i = 0; i < 8; i++) { bool temp = demo_keys & (1 << i); if (temp != keysactive[keySettings[i]]) new_input = true; } demo_keys_wait++; if (new_input) { Uint8 temp2[2] = { demo_keys_wait >> 8, demo_keys_wait }; fwrite_u8(temp2, 2, demo_file); demo_keys = 0; for (unsigned int i = 0; i < 8; i++) demo_keys |= keysactive[keySettings[i]] ? (1 << i) : 0; fwrite_u8(&demo_keys, 1, demo_file); demo_keys_wait = 0; } } } if (smoothies[9-1]) { *mouseY_ = this_player->y - (*mouseY_ - this_player->y); mouseYC = -mouseYC; } accelXC += this_player->x - *mouseX_; accelYC += this_player->y - *mouseY_; if (mouseXC > 30) mouseXC = 30; else if (mouseXC < -30) mouseXC = -30; if (mouseYC > 30) mouseYC = 30; else if (mouseYC < -30) mouseYC = -30; if (mouseXC > 0) this_player->x += (mouseXC + 3) / 4; else if (mouseXC < 0) this_player->x += (mouseXC - 3) / 4; if (mouseYC > 0) this_player->y += (mouseYC + 3) / 4; else if (mouseYC < 0) this_player->y += (mouseYC - 3) / 4; if (mouseXC > 3) accelXC++; else if (mouseXC < -2) accelXC--; if (mouseYC > 2) accelYC++; else if (mouseYC < -2) accelYC--; } /*endLevel*/ #ifdef WITH_NETWORK if (isNetworkGame && playerNum_ == thisPlayerNum) { Uint16 buttons = 0; for (int i = 4 - 1; i >= 0; i--) { buttons <<= 1; buttons |= button[i]; } SDLNet_Write16(this_player->x - *mouseX_, &packet_state_out[0]->data[4]); SDLNet_Write16(this_player->y - *mouseY_, &packet_state_out[0]->data[6]); SDLNet_Write16(accelXC, &packet_state_out[0]->data[8]); SDLNet_Write16(accelYC, &packet_state_out[0]->data[10]); SDLNet_Write16(buttons, &packet_state_out[0]->data[12]); this_player->x = *mouseX_; this_player->y = *mouseY_; button[0] = false; button[1] = false; button[2] = false; button[3] = false; accelXC = 0; accelYC = 0; } #endif } /*isNetworkGame*/ /* --- Movement Routine Ending --- */ moveOk = true; #ifdef WITH_NETWORK if (isNetworkGame && !network_state_is_reset()) { if (playerNum_ != thisPlayerNum) { if (thisPlayerNum == 2) difficultyLevel = SDLNet_Read16(&packet_state_in[0]->data[16]); Uint16 buttons = SDLNet_Read16(&packet_state_in[0]->data[12]); for (int i = 0; i < 4; i++) { button[i] = buttons & 1; buttons >>= 1; } this_player->x += (Sint16)SDLNet_Read16(&packet_state_in[0]->data[4]); this_player->y += (Sint16)SDLNet_Read16(&packet_state_in[0]->data[6]); accelXC = (Sint16)SDLNet_Read16(&packet_state_in[0]->data[8]); accelYC = (Sint16)SDLNet_Read16(&packet_state_in[0]->data[10]); } else { Uint16 buttons = SDLNet_Read16(&packet_state_out[network_delay]->data[12]); for (int i = 0; i < 4; i++) { button[i] = buttons & 1; buttons >>= 1; } this_player->x += (Sint16)SDLNet_Read16(&packet_state_out[network_delay]->data[4]); this_player->y += (Sint16)SDLNet_Read16(&packet_state_out[network_delay]->data[6]); accelXC = (Sint16)SDLNet_Read16(&packet_state_out[network_delay]->data[8]); accelYC = (Sint16)SDLNet_Read16(&packet_state_out[network_delay]->data[10]); } } #endif /*Street-Fighter codes*/ JE_SFCodes(playerNum_, this_player->x, this_player->y, *mouseX_, *mouseY_); if (moveOk) { /* END OF MOVEMENT ROUTINES */ /*Linking Routines*/ if (twoPlayerMode && !twoPlayerLinked && this_player->x == *mouseX_ && this_player->y == *mouseY_ && abs(player[0].x - player[1].x) < 8 && abs(player[0].y - player[1].y) < 8 && player[0].is_alive && player[1].is_alive && !galagaMode) { twoPlayerLinked = true; } if (playerNum_ == 1 && (button[3-1] || button[2-1]) && !galagaMode) twoPlayerLinked = false; if (twoPlayerMode && twoPlayerLinked && playerNum_ == 2 && (this_player->x != *mouseX_ || this_player->y != *mouseY_)) { if (button[0]) { if (link_gun_analog) { linkGunDirec = link_gun_angle; } else { JE_real tempR; if (abs(this_player->x - *mouseX_) > abs(this_player->y - *mouseY_)) tempR = (this_player->x - *mouseX_ > 0) ? M_PI_2 : (M_PI + M_PI_2); else tempR = (this_player->y - *mouseY_ > 0) ? 0 : M_PI; if (fabsf(linkGunDirec - tempR) < 0.3f) linkGunDirec = tempR; else if (linkGunDirec < tempR && linkGunDirec - tempR > -3.24f) linkGunDirec += 0.2f; else if (linkGunDirec - tempR < M_PI) linkGunDirec -= 0.2f; else linkGunDirec += 0.2f; } if (linkGunDirec >= (2 * M_PI)) linkGunDirec -= (2 * M_PI); else if (linkGunDirec < 0) linkGunDirec += (2 * M_PI); } else if (!galagaMode) { twoPlayerLinked = false; } } } } if (levelEnd > 0 && all_players_dead()) reallyEndLevel = true; /* End Level Fade-Out */ if (this_player->is_alive && endLevel) { if (levelEnd == 0) { reallyEndLevel = true; } else { this_player->y -= levelEndWarp; if (this_player->y < -200) reallyEndLevel = true; int trail_spacing = 1; int trail_y = this_player->y; int num_trails = abs(41 - levelEnd); if (num_trails > 20) num_trails = 20; for (int i = 0; i < num_trails; i++) { trail_y += trail_spacing; trail_spacing++; } for (int i = 1; i < num_trails; i++) { trail_y -= trail_spacing; trail_spacing--; if (trail_y > 0 && trail_y < 170) { if (shipGr_ == 0) { blit_sprite2x2(VGAScreen, this_player->x - 17, trail_y - 7, *shipGrPtr_, 13); blit_sprite2x2(VGAScreen, this_player->x + 7 , trail_y - 7, *shipGrPtr_, 51); } else if (shipGr_ == 1) { blit_sprite2x2(VGAScreen, this_player->x - 17, trail_y - 7, *shipGrPtr_, 220); blit_sprite2x2(VGAScreen, this_player->x + 7 , trail_y - 7, *shipGrPtr_, 222); } else { blit_sprite2x2(VGAScreen, this_player->x - 5, trail_y - 7, *shipGrPtr_, shipGr_); } } } } } if (play_demo) JE_dString(VGAScreen, 115, 10, miscText[7], SMALL_FONT_SHAPES); // insert coin if (this_player->is_alive && !endLevel) { if (!twoPlayerLinked || playerNum_ < 2) { if (!twoPlayerMode || shipGr2 != 0) // if not dragonwing { if (this_player->sidekick[LEFT_SIDEKICK].style == 0) { this_player->sidekick[LEFT_SIDEKICK].x = *mouseX_ - 14; this_player->sidekick[LEFT_SIDEKICK].y = *mouseY_; } if (this_player->sidekick[RIGHT_SIDEKICK].style == 0) { this_player->sidekick[RIGHT_SIDEKICK].x = *mouseX_ + 16; this_player->sidekick[RIGHT_SIDEKICK].y = *mouseY_; } } if (this_player->x_friction_ticks > 0) { --this_player->x_friction_ticks; } else { this_player->x_friction_ticks = 1; if (this_player->x_velocity < 0) ++this_player->x_velocity; else if (this_player->x_velocity > 0) --this_player->x_velocity; } if (this_player->y_friction_ticks > 0) { --this_player->y_friction_ticks; } else { this_player->y_friction_ticks = 2; if (this_player->y_velocity < 0) ++this_player->y_velocity; else if (this_player->y_velocity > 0) --this_player->y_velocity; } this_player->x_velocity += accelXC; this_player->y_velocity += accelYC; this_player->x_velocity = MIN(MAX(-4, this_player->x_velocity), 4); this_player->y_velocity = MIN(MAX(-4, this_player->y_velocity), 4); this_player->x += this_player->x_velocity; this_player->y += this_player->y_velocity; // if player moved, add new ship x, y history entry if (this_player->x - *mouseX_ != 0 || this_player->y - *mouseY_ != 0) { for (uint i = 1; i < COUNTOF(player->old_x); ++i) { this_player->old_x[i - 1] = this_player->old_x[i]; this_player->old_y[i - 1] = this_player->old_y[i]; } this_player->old_x[COUNTOF(player->old_x) - 1] = this_player->x; this_player->old_y[COUNTOF(player->old_x) - 1] = this_player->y; } } else /*twoPlayerLinked*/ { if (shipGr_ == 0) this_player->x = player[0].x - 1; else this_player->x = player[0].x; this_player->y = player[0].y + 8; this_player->x_velocity = player[0].x_velocity; this_player->y_velocity = 4; // turret direction marker/shield shotMultiPos[SHOT_MISC] = 0; b = player_shot_create(0, SHOT_MISC, this_player->x + 1 + roundf(sinf(linkGunDirec + 0.2f) * 26), this_player->y + roundf(cosf(linkGunDirec + 0.2f) * 26), *mouseX_, *mouseY_, 148, playerNum_); shotMultiPos[SHOT_MISC] = 0; b = player_shot_create(0, SHOT_MISC, this_player->x + 1 + roundf(sinf(linkGunDirec - 0.2f) * 26), this_player->y + roundf(cosf(linkGunDirec - 0.2f) * 26), *mouseX_, *mouseY_, 148, playerNum_); shotMultiPos[SHOT_MISC] = 0; b = player_shot_create(0, SHOT_MISC, this_player->x + 1 + roundf(sinf(linkGunDirec) * 26), this_player->y + roundf(cosf(linkGunDirec) * 26), *mouseX_, *mouseY_, 147, playerNum_); if (shotRepeat[SHOT_REAR] > 0) { --shotRepeat[SHOT_REAR]; } else if (button[1-1]) { shotMultiPos[SHOT_REAR] = 0; b = player_shot_create(0, SHOT_REAR, this_player->x + 1 + roundf(sinf(linkGunDirec) * 20), this_player->y + roundf(cosf(linkGunDirec) * 20), *mouseX_, *mouseY_, linkGunWeapons[this_player->items.weapon[REAR_WEAPON].id-1], playerNum_); player_shot_set_direction(b, this_player->items.weapon[REAR_WEAPON].id, linkGunDirec); } } } if (!endLevel) { if (this_player->x > 256) { this_player->x = 256; constantLastX = -constantLastX; } if (this_player->x < 40) { this_player->x = 40; constantLastX = -constantLastX; } if (isNetworkGame && playerNum_ == 1) { if (this_player->y > 154) this_player->y = 154; } else { if (this_player->y > 160) this_player->y = 160; } if (this_player->y < 10) this_player->y = 10; // Determines the ship banking sprite to display, depending on horizontal velocity and acceleration int ship_banking = this_player->x_velocity / 2 + (this_player->x - *mouseX_) / 6; ship_banking = MAX(-2, MIN(ship_banking, 2)); int ship_sprite = ship_banking * 2 + shipGr_; explosionFollowAmountX = this_player->x - this_player->last_x_explosion_follow; explosionFollowAmountY = this_player->y - this_player->last_y_explosion_follow; if (explosionFollowAmountY < 0) explosionFollowAmountY = 0; this_player->last_x_explosion_follow = this_player->x; this_player->last_y_explosion_follow = this_player->y; if (shipGr_ == 0) { if (background2) { blit_sprite2x2_darken(VGAScreen, this_player->x - 17 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shipGrPtr_, ship_sprite + 13); blit_sprite2x2_darken(VGAScreen, this_player->x + 7 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shipGrPtr_, ship_sprite + 51); if (superWild) { blit_sprite2x2_darken(VGAScreen, this_player->x - 16 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shipGrPtr_, ship_sprite + 13); blit_sprite2x2_darken(VGAScreen, this_player->x + 6 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shipGrPtr_, ship_sprite + 51); } } } else if (shipGr_ == 1) { if (background2) { blit_sprite2x2_darken(VGAScreen, this_player->x - 17 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shipGrPtr_, 220); blit_sprite2x2_darken(VGAScreen, this_player->x + 7 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shipGrPtr_, 222); } } else { if (background2) { blit_sprite2x2_darken(VGAScreen, this_player->x - 5 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shipGrPtr_, ship_sprite); if (superWild) { blit_sprite2x2_darken(VGAScreen, this_player->x - 4 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shipGrPtr_, ship_sprite); } } } if (this_player->invulnerable_ticks > 0) { --this_player->invulnerable_ticks; if (shipGr_ == 0) { blit_sprite2x2_blend(VGAScreen, this_player->x - 17, this_player->y - 7, *shipGrPtr_, ship_sprite + 13); blit_sprite2x2_blend(VGAScreen, this_player->x + 7 , this_player->y - 7, *shipGrPtr_, ship_sprite + 51); } else if (shipGr_ == 1) { blit_sprite2x2_blend(VGAScreen, this_player->x - 17, this_player->y - 7, *shipGrPtr_, 220); blit_sprite2x2_blend(VGAScreen, this_player->x + 7 , this_player->y - 7, *shipGrPtr_, 222); } else blit_sprite2x2_blend(VGAScreen, this_player->x - 5, this_player->y - 7, *shipGrPtr_, ship_sprite); } else { if (shipGr_ == 0) { blit_sprite2x2(VGAScreen, this_player->x - 17, this_player->y - 7, *shipGrPtr_, ship_sprite + 13); blit_sprite2x2(VGAScreen, this_player->x + 7, this_player->y - 7, *shipGrPtr_, ship_sprite + 51); } else if (shipGr_ == 1) { blit_sprite2x2(VGAScreen, this_player->x - 17, this_player->y - 7, *shipGrPtr_, 220); blit_sprite2x2(VGAScreen, this_player->x + 7, this_player->y - 7, *shipGrPtr_, 222); int ship_banking = 0; switch (ship_sprite) { case 5: blit_sprite2(VGAScreen, this_player->x - 17, this_player->y + 7, *shipGrPtr_, 40); tempW = this_player->x - 7; ship_banking = -2; break; case 3: blit_sprite2(VGAScreen, this_player->x - 17, this_player->y + 7, *shipGrPtr_, 39); tempW = this_player->x - 7; ship_banking = -1; break; case 1: ship_banking = 0; break; case -1: blit_sprite2(VGAScreen, this_player->x + 19, this_player->y + 7, *shipGrPtr_, 58); tempW = this_player->x + 9; ship_banking = 1; break; case -3: blit_sprite2(VGAScreen, this_player->x + 19, this_player->y + 7, *shipGrPtr_, 59); tempW = this_player->x + 9; ship_banking = 2; break; } if (ship_banking != 0) // NortSparks { if (shotRepeat[SHOT_NORTSPARKS] > 0) { --shotRepeat[SHOT_NORTSPARKS]; } else { b = player_shot_create(0, SHOT_NORTSPARKS, tempW + (mt_rand() % 8) - 4, this_player->y + (mt_rand() % 8) - 4, *mouseX_, *mouseY_, 671, 1); shotRepeat[SHOT_NORTSPARKS] = abs(ship_banking) - 1; } } } else { blit_sprite2x2(VGAScreen, this_player->x - 5, this_player->y - 7, *shipGrPtr_, ship_sprite); } } /*Options Location*/ if (playerNum_ == 2 && shipGr_ == 0) // if dragonwing { if (this_player->sidekick[LEFT_SIDEKICK].style == 0) { this_player->sidekick[LEFT_SIDEKICK].x = this_player->x - 14 + ship_banking * 2; this_player->sidekick[LEFT_SIDEKICK].y = this_player->y; } if (this_player->sidekick[RIGHT_SIDEKICK].style == 0) { this_player->sidekick[RIGHT_SIDEKICK].x = this_player->x + 17 + ship_banking * 2; this_player->sidekick[RIGHT_SIDEKICK].y = this_player->y; } } } // !endLevel if (moveOk) { if (this_player->is_alive) { if (!endLevel) { this_player->delta_x_shot_move = this_player->x - this_player->last_x_shot_move; this_player->delta_y_shot_move = this_player->y - this_player->last_y_shot_move; /* PLAYER SHOT Change */ if (button[4-1]) { portConfigChange = true; if (portConfigDone) { shotMultiPos[SHOT_REAR] = 0; if (superArcadeMode != SA_NONE && superArcadeMode <= SA_NORTSHIPZ) { shotMultiPos[SHOT_SPECIAL] = 0; shotMultiPos[SHOT_SPECIAL2] = 0; if (player[0].items.special == SASpecialWeapon[superArcadeMode-1]) { player[0].items.special = SASpecialWeaponB[superArcadeMode-1]; this_player->weapon_mode = 2; } else { player[0].items.special = SASpecialWeapon[superArcadeMode-1]; this_player->weapon_mode = 1; } } else if (++this_player->weapon_mode > JE_portConfigs()) this_player->weapon_mode = 1; JE_drawPortConfigButtons(); portConfigDone = false; } } /* PLAYER SHOT Creation */ /*SpecialShot*/ if (!galagaMode) JE_doSpecialShot(playerNum_, &this_player->armor, &this_player->shield); /*Normal Main Weapons*/ if (!(twoPlayerLinked && playerNum_ == 2)) { int min, max; if (!twoPlayerMode) min = 1, max = 2; else min = max = playerNum_; for (temp = min - 1; temp < max; temp++) { const uint item = this_player->items.weapon[temp].id; if (item > 0) { if (shotRepeat[temp] > 0) { --shotRepeat[temp]; } else if (button[1-1]) { const uint item_power = galagaMode ? 0 : this_player->items.weapon[temp].power - 1, item_mode = (temp == REAR_WEAPON) ? this_player->weapon_mode - 1 : 0; b = player_shot_create(item, temp, this_player->x, this_player->y, *mouseX_, *mouseY_, weaponPort[item].op[item_mode][item_power], playerNum_); } } } } /*Super Charge Weapons*/ if (playerNum_ == 2) { if (!twoPlayerLinked) blit_sprite2(VGAScreen, this_player->x + (shipGr_ == 0) + 1, this_player->y - 13, spriteSheet10, 77 + chargeLevel + chargeGr * 19); if (chargeGrWait > 0) { chargeGrWait--; } else { chargeGr++; if (chargeGr == 4) chargeGr = 0; chargeGrWait = 3; } if (chargeLevel > 0) { fill_rectangle_xy(VGAScreenSeg, 269, 107 + (chargeLevel - 1) * 3, 275, 108 + (chargeLevel - 1) * 3, 193); } if (chargeWait > 0) { chargeWait--; } else { if (chargeLevel < chargeMax) chargeLevel++; chargeWait = 28 - this_player->items.weapon[REAR_WEAPON].power * 2; if (difficultyLevel > DIFFICULTY_HARD) chargeWait -= 5; } if (chargeLevel > 0) fill_rectangle_xy(VGAScreenSeg, 269, 107 + (chargeLevel - 1) * 3, 275, 108 + (chargeLevel - 1) * 3, 204); if (shotRepeat[SHOT_P2_CHARGE] > 0) { --shotRepeat[SHOT_P2_CHARGE]; } else if (button[1-1] && (!twoPlayerLinked || chargeLevel > 0)) { shotMultiPos[SHOT_P2_CHARGE] = 0; b = player_shot_create(16, SHOT_P2_CHARGE, this_player->x, this_player->y, *mouseX_, *mouseY_, chargeGunWeapons[player[1].items.weapon[REAR_WEAPON].id-1] + chargeLevel, playerNum_); if (chargeLevel > 0) fill_rectangle_xy(VGAScreenSeg, 269, 107 + (chargeLevel - 1) * 3, 275, 108 + (chargeLevel - 1) * 3, 193); chargeLevel = 0; chargeWait = 30 - this_player->items.weapon[REAR_WEAPON].power * 2; } } /*SUPER BOMB*/ temp = playerNum_; if (temp == 0) temp = 1; /*Get whether player 1 or 2*/ if (player[temp-1].superbombs > 0) { if (shotRepeat[SHOT_P1_SUPERBOMB + temp-1] > 0) { --shotRepeat[SHOT_P1_SUPERBOMB + temp-1]; } else if (button[3-1] || button[2-1]) { --player[temp-1].superbombs; shotMultiPos[SHOT_P1_SUPERBOMB + temp-1] = 0; b = player_shot_create(16, SHOT_P1_SUPERBOMB + temp-1, this_player->x, this_player->y, *mouseX_, *mouseY_, 535, playerNum_); } } // sidekicks if (this_player->sidekick[LEFT_SIDEKICK].style == 4 && this_player->sidekick[RIGHT_SIDEKICK].style == 4) optionSatelliteRotate += 0.2f; else if (this_player->sidekick[LEFT_SIDEKICK].style == 4 || this_player->sidekick[RIGHT_SIDEKICK].style == 4) optionSatelliteRotate += 0.15f; switch (this_player->sidekick[LEFT_SIDEKICK].style) { case 1: // trailing case 3: this_player->sidekick[LEFT_SIDEKICK].x = this_player->old_x[COUNTOF(player->old_x) / 2 - 1]; this_player->sidekick[LEFT_SIDEKICK].y = this_player->old_y[COUNTOF(player->old_x) / 2 - 1]; break; case 2: // front-mounted this_player->sidekick[LEFT_SIDEKICK].x = this_player->x; this_player->sidekick[LEFT_SIDEKICK].y = MAX(10, this_player->y - 20); break; case 4: // orbiting this_player->sidekick[LEFT_SIDEKICK].x = this_player->x + roundf(sinf(optionSatelliteRotate) * 20); this_player->sidekick[LEFT_SIDEKICK].y = this_player->y + roundf(cosf(optionSatelliteRotate) * 20); break; } switch (this_player->sidekick[RIGHT_SIDEKICK].style) { case 4: // orbiting this_player->sidekick[RIGHT_SIDEKICK].x = this_player->x - roundf(sinf(optionSatelliteRotate) * 20); this_player->sidekick[RIGHT_SIDEKICK].y = this_player->y - roundf(cosf(optionSatelliteRotate) * 20); break; case 1: // trailing case 3: this_player->sidekick[RIGHT_SIDEKICK].x = this_player->old_x[0]; this_player->sidekick[RIGHT_SIDEKICK].y = this_player->old_y[0]; break; case 2: // front-mounted if (!optionAttachmentLinked) { this_player->sidekick[RIGHT_SIDEKICK].y += optionAttachmentMove / 2; if (optionAttachmentMove >= -2) { if (optionAttachmentReturn) temp = 2; else temp = 0; if (this_player->sidekick[RIGHT_SIDEKICK].y > (this_player->y - 20) + 5) { temp = 2; optionAttachmentMove -= 1 + optionAttachmentReturn; } else if (this_player->sidekick[RIGHT_SIDEKICK].y > (this_player->y - 20) - 0) { temp = 3; if (optionAttachmentMove > 0) optionAttachmentMove--; else optionAttachmentMove++; } else if (this_player->sidekick[RIGHT_SIDEKICK].y > (this_player->y - 20) - 5) { temp = 2; optionAttachmentMove++; } else if (optionAttachmentMove < 2 + optionAttachmentReturn * 4) { optionAttachmentMove += 1 + optionAttachmentReturn; } if (optionAttachmentReturn) temp = temp * 2; if (abs(this_player->sidekick[RIGHT_SIDEKICK].x - this_player->x) < temp) temp = 1; if (this_player->sidekick[RIGHT_SIDEKICK].x > this_player->x) this_player->sidekick[RIGHT_SIDEKICK].x -= temp; else if (this_player->sidekick[RIGHT_SIDEKICK].x < this_player->x) this_player->sidekick[RIGHT_SIDEKICK].x += temp; if (abs(this_player->sidekick[RIGHT_SIDEKICK].y - (this_player->y - 20)) + abs(this_player->sidekick[RIGHT_SIDEKICK].x - this_player->x) < 8) { optionAttachmentLinked = true; soundQueue[2] = S_CLINK; } if (button[3-1]) optionAttachmentReturn = true; } else // sidekick needs to catch up to player { optionAttachmentMove += 1 + optionAttachmentReturn; JE_setupExplosion(this_player->sidekick[RIGHT_SIDEKICK].x + 1, this_player->sidekick[RIGHT_SIDEKICK].y + 10, 0, 0, false, false); } } else { this_player->sidekick[RIGHT_SIDEKICK].x = this_player->x; this_player->sidekick[RIGHT_SIDEKICK].y = this_player->y - 20; if (button[3-1]) { optionAttachmentLinked = false; optionAttachmentReturn = false; optionAttachmentMove = -20; soundQueue[3] = S_WEAPON_26; } } if (this_player->sidekick[RIGHT_SIDEKICK].y < 10) this_player->sidekick[RIGHT_SIDEKICK].y = 10; break; } if (playerNum_ == 2 || !twoPlayerMode) // if player has sidekicks { for (uint i = 0; i < COUNTOF(player->items.sidekick); ++i) { uint shot_i = (i == 0) ? SHOT_LEFT_SIDEKICK : SHOT_RIGHT_SIDEKICK; JE_OptionType *this_option = &options[this_player->items.sidekick[i]]; // fire/refill sidekick if (this_option->wport > 0) { if (shotRepeat[shot_i] > 0) { --shotRepeat[shot_i]; } else { const int ammo_max = this_player->sidekick[i].ammo_max; if (ammo_max > 0) // sidekick has limited ammo { if (this_player->sidekick[i].ammo_refill_ticks > 0) { --this_player->sidekick[i].ammo_refill_ticks; } else // refill one ammo { this_player->sidekick[i].ammo_refill_ticks = this_player->sidekick[i].ammo_refill_ticks_max; if (this_player->sidekick[i].ammo < ammo_max) ++this_player->sidekick[i].ammo; // draw sidekick refill ammo gauge const int y = hud_sidekick_y[twoPlayerMode ? 1 : 0][i] + 13; draw_segmented_gauge(VGAScreenSeg, 284, y, 112, 2, 2, MAX(1, ammo_max / 10), this_player->sidekick[i].ammo); } if (button[1 + i] && this_player->sidekick[i].ammo > 0) { b = player_shot_create(this_option->wport, shot_i, this_player->sidekick[i].x, this_player->sidekick[i].y, *mouseX_, *mouseY_, this_option->wpnum + this_player->sidekick[i].charge, playerNum_); --this_player->sidekick[i].ammo; if (this_player->sidekick[i].charge > 0) { shotMultiPos[shot_i] = 0; this_player->sidekick[i].charge = 0; } this_player->sidekick[i].charge_ticks = 20; this_player->sidekick[i].animation_enabled = true; // draw sidekick discharge ammo gauge const int y = hud_sidekick_y[twoPlayerMode ? 1 : 0][i] + 13; fill_rectangle_xy(VGAScreenSeg, 284, y, 312, y + 2, 0); draw_segmented_gauge(VGAScreenSeg, 284, y, 112, 2, 2, MAX(1, ammo_max / 10), this_player->sidekick[i].ammo); } } else // has infinite ammo { if (button[0] || button[1 + i]) { b = player_shot_create(this_option->wport, shot_i, this_player->sidekick[i].x, this_player->sidekick[i].y, *mouseX_, *mouseY_, this_option->wpnum + this_player->sidekick[i].charge, playerNum_); if (this_player->sidekick[i].charge > 0) { shotMultiPos[shot_i] = 0; this_player->sidekick[i].charge = 0; } this_player->sidekick[i].charge_ticks = 20; this_player->sidekick[i].animation_enabled = true; } } } } } } // end of if player has sidekicks } // !endLevel } // this_player->is_alive } // moveOK // draw sidekicks if ((playerNum_ == 2 || !twoPlayerMode) && !endLevel) { for (uint i = 0; i < COUNTOF(this_player->sidekick); ++i) { JE_OptionType *this_option = &options[this_player->items.sidekick[i]]; if (this_option->option > 0) { if (this_player->sidekick[i].animation_enabled) { if (++this_player->sidekick[i].animation_frame >= this_option->ani) { this_player->sidekick[i].animation_frame = 0; this_player->sidekick[i].animation_enabled = (this_option->option == 1); } } const int x = this_player->sidekick[i].x, y = this_player->sidekick[i].y; const uint sprite = this_option->gr[this_player->sidekick[i].animation_frame] + this_player->sidekick[i].charge; if (this_player->sidekick[i].style == 1 || this_player->sidekick[i].style == 2) blit_sprite2x2(VGAScreen, x - 6, y, spriteSheet10, sprite); else blit_sprite2(VGAScreen, x, y, spriteSheet9, sprite); } if (--this_player->sidekick[i].charge_ticks == 0) { if (this_player->sidekick[i].charge < this_option->pwr) ++this_player->sidekick[i].charge; this_player->sidekick[i].charge_ticks = 20; } } } } void JE_mainGamePlayerFunctions(void) { /*PLAYER MOVEMENT/MOUSE ROUTINES*/ if (endLevel && levelEnd > 0) { levelEnd--; levelEndWarp++; } /*Reset Street-Fighter commands*/ memset(SFExecuted, 0, sizeof(SFExecuted)); portConfigChange = false; if (twoPlayerMode) { JE_playerMovement(&player[0], !galagaMode ? inputDevice[0] : 0, 1, shipGr, shipGrPtr, &mouseX, &mouseY); JE_playerMovement(&player[1], !galagaMode ? inputDevice[1] : 0, 2, shipGr2, shipGr2ptr, &mouseXB, &mouseYB); } else { JE_playerMovement(&player[0], 0, 1, shipGr, shipGrPtr, &mouseX, &mouseY); } /* == Parallax Map Scrolling == */ if (twoPlayerMode) tempX = (player[0].x + player[1].x) / 2; else tempX = player[0].x; tempW = floorf((260.0f - (tempX - 36.0f)) / (260.0f - 36.0f) * (24.0f * 3.0f) - 1.0f); mapX3Ofs = tempW; mapX3Pos = mapX3Ofs % 24; mapX3bpPos = 1 - (mapX3Ofs / 24); mapX2Ofs = (tempW * 2) / 3; mapX2Pos = mapX2Ofs % 24; mapX2bpPos = 1 - (mapX2Ofs / 24); oldMapXOfs = mapXOfs; mapXOfs = mapX2Ofs / 2; mapXPos = mapXOfs % 24; mapXbpPos = 1 - (mapXOfs / 24); if (background3x1) { mapX3Ofs = mapXOfs; mapX3Pos = mapXPos; mapX3bpPos = mapXbpPos - 1; } } const char *JE_getName(JE_byte pnum) { if (pnum == thisPlayerNum && network_player_name[0] != '\0') return network_player_name; else if (network_opponent_name[0] != '\0') return network_opponent_name; return miscText[47 + pnum]; } void JE_playerCollide(Player *this_player, JE_byte playerNum_) { char tempStr[256]; for (int z = 0; z < 100; z++) { if (enemyAvail[z] != 1) { int enemy_screen_x = enemy[z].ex + enemy[z].mapoffset; if (abs(this_player->x - enemy_screen_x) < 12 && abs(this_player->y - enemy[z].ey) < 14) { /*Collide*/ int evalue = enemy[z].evalue; if (evalue > 29999) { if (evalue == 30000) // spawn dragonwing in galaga mode, otherwise just a purple ball { this_player->cash += 100; if (!galagaMode) { handle_got_purple_ball(this_player); } else { // spawn the dragonwing? if (twoPlayerMode) this_player->cash += 2400; twoPlayerMode = true; twoPlayerLinked = true; player[1].items.weapon[REAR_WEAPON].power = 1; player[1].armor = 10; player[1].is_alive = true; } enemyAvail[z] = 1; soundQueue[7] = S_POWERUP; } else if (superArcadeMode != SA_NONE && evalue > 30000) { shotMultiPos[SHOT_FRONT] = 0; shotRepeat[SHOT_FRONT] = 10; tempW = SAWeapon[superArcadeMode-1][evalue - 30000-1]; // if picked up already-owned weapon, power weapon up if (tempW == player[0].items.weapon[FRONT_WEAPON].id) { this_player->cash += 1000; power_up_weapon(this_player, FRONT_WEAPON); } // else weapon also gives purple ball else { handle_got_purple_ball(this_player); } player[0].items.weapon[FRONT_WEAPON].id = tempW; this_player->cash += 200; soundQueue[7] = S_POWERUP; enemyAvail[z] = 1; } else if (evalue > 32100) { if (playerNum_ == 1) { this_player->cash += 250; player[0].items.special = evalue - 32100; shotMultiPos[SHOT_SPECIAL] = 0; shotRepeat[SHOT_SPECIAL] = 10; shotMultiPos[SHOT_SPECIAL2] = 0; shotRepeat[SHOT_SPECIAL2] = 0; if (isNetworkGame) sprintf(tempStr, "%s %s %s", JE_getName(1), miscTextB[4-1], special[evalue - 32100].name); else if (twoPlayerMode) sprintf(tempStr, "%s %s", miscText[43-1], special[evalue - 32100].name); else sprintf(tempStr, "%s %s", miscText[64-1], special[evalue - 32100].name); JE_drawTextWindow(tempStr); soundQueue[7] = S_POWERUP; enemyAvail[z] = 1; } } else if (evalue > 32000) { if (playerNum_ == 2) { enemyAvail[z] = 1; if (isNetworkGame) sprintf(tempStr, "%s %s %s", JE_getName(2), miscTextB[4-1], options[evalue - 32000].name); else sprintf(tempStr, "%s %s", miscText[44-1], options[evalue - 32000].name); JE_drawTextWindow(tempStr); // if picked up a different sidekick than player already has, then reset sidekicks to least powerful, else power them up if (evalue - 32000u != player[1].items.sidekick_series) { player[1].items.sidekick_series = evalue - 32000; player[1].items.sidekick_level = 101; } else if (player[1].items.sidekick_level < 103) { ++player[1].items.sidekick_level; } uint temp = player[1].items.sidekick_level - 100 - 1; for (uint i = 0; i < COUNTOF(player[1].items.sidekick); ++i) player[1].items.sidekick[i] = optionSelect[player[1].items.sidekick_series][temp][i]; shotMultiPos[SHOT_LEFT_SIDEKICK] = 0; shotMultiPos[SHOT_RIGHT_SIDEKICK] = 0; JE_drawOptions(); soundQueue[7] = S_POWERUP; } else if (onePlayerAction) { enemyAvail[z] = 1; sprintf(tempStr, "%s %s", miscText[64-1], options[evalue - 32000].name); JE_drawTextWindow(tempStr); for (uint i = 0; i < COUNTOF(player[0].items.sidekick); ++i) player[0].items.sidekick[i] = evalue - 32000; shotMultiPos[SHOT_LEFT_SIDEKICK] = 0; shotMultiPos[SHOT_RIGHT_SIDEKICK] = 0; JE_drawOptions(); soundQueue[7] = S_POWERUP; } if (enemyAvail[z] == 1) this_player->cash += 250; } else if (evalue > 31000) { this_player->cash += 250; if (playerNum_ == 2) { if (isNetworkGame) sprintf(tempStr, "%s %s %s", JE_getName(2), miscTextB[4-1], weaponPort[evalue - 31000].name); else sprintf(tempStr, "%s %s", miscText[44-1], weaponPort[evalue - 31000].name); JE_drawTextWindow(tempStr); player[1].items.weapon[REAR_WEAPON].id = evalue - 31000; shotMultiPos[SHOT_REAR] = 0; enemyAvail[z] = 1; soundQueue[7] = S_POWERUP; } else if (onePlayerAction) { sprintf(tempStr, "%s %s", miscText[64-1], weaponPort[evalue - 31000].name); JE_drawTextWindow(tempStr); player[0].items.weapon[REAR_WEAPON].id = evalue - 31000; shotMultiPos[SHOT_REAR] = 0; enemyAvail[z] = 1; soundQueue[7] = S_POWERUP; if (player[0].items.weapon[REAR_WEAPON].power == 0) // does this ever happen? player[0].items.weapon[REAR_WEAPON].power = 1; } } else if (evalue > 30000) { if (playerNum_ == 1 && twoPlayerMode) { if (isNetworkGame) sprintf(tempStr, "%s %s %s", JE_getName(1), miscTextB[4-1], weaponPort[evalue - 30000].name); else sprintf(tempStr, "%s %s", miscText[43-1], weaponPort[evalue - 30000].name); JE_drawTextWindow(tempStr); player[0].items.weapon[FRONT_WEAPON].id = evalue - 30000; shotMultiPos[SHOT_FRONT] = 0; enemyAvail[z] = 1; soundQueue[7] = S_POWERUP; } else if (onePlayerAction) { sprintf(tempStr, "%s %s", miscText[64-1], weaponPort[evalue - 30000].name); JE_drawTextWindow(tempStr); player[0].items.weapon[FRONT_WEAPON].id = evalue - 30000; shotMultiPos[SHOT_FRONT] = 0; enemyAvail[z] = 1; soundQueue[7] = S_POWERUP; } if (enemyAvail[z] == 1) { player[0].items.special = specialArcadeWeapon[evalue - 30000-1]; if (player[0].items.special > 0) { shotMultiPos[SHOT_SPECIAL] = 0; shotRepeat[SHOT_SPECIAL] = 0; shotMultiPos[SHOT_SPECIAL2] = 0; shotRepeat[SHOT_SPECIAL2] = 0; } this_player->cash += 250; } } } else if (evalue > 20000) { if (twoPlayerLinked) { // share the armor evenly between linked players for (uint i = 0; i < COUNTOF(player); ++i) { player[i].armor += (evalue - 20000) / COUNTOF(player); if (player[i].armor > 28) player[i].armor = 28; } } else { this_player->armor += evalue - 20000; if (this_player->armor > 28) this_player->armor = 28; } enemyAvail[z] = 1; VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ JE_drawArmor(); VGAScreen = game_screen; /* side-effect of game_screen */ soundQueue[7] = S_POWERUP; } else if (evalue > 10000 && enemyAvail[z] == 2) { if (!bonusLevel) { play_song(30); /*Zanac*/ bonusLevel = true; nextLevel = evalue - 10000; enemyAvail[z] = 1; displayTime = 150; } } else if (enemy[z].scoreitem) { enemyAvail[z] = 1; soundQueue[7] = S_ITEM; if (evalue == 1) { cubeMax++; soundQueue[3] = V_DATA_CUBE; } else if (evalue == -1) // got front weapon powerup { if (isNetworkGame) sprintf(tempStr, "%s %s %s", JE_getName(1), miscTextB[4-1], miscText[45-1]); else if (twoPlayerMode) sprintf(tempStr, "%s %s", miscText[43-1], miscText[45-1]); else strcpy(tempStr, miscText[45-1]); JE_drawTextWindow(tempStr); power_up_weapon(&player[0], FRONT_WEAPON); soundQueue[7] = S_POWERUP; } else if (evalue == -2) // got rear weapon powerup { if (isNetworkGame) sprintf(tempStr, "%s %s %s", JE_getName(2), miscTextB[4-1], miscText[46-1]); else if (twoPlayerMode) sprintf(tempStr, "%s %s", miscText[44-1], miscText[46-1]); else strcpy(tempStr, miscText[46-1]); JE_drawTextWindow(tempStr); power_up_weapon(twoPlayerMode ? &player[1] : &player[0], REAR_WEAPON); soundQueue[7] = S_POWERUP; } else if (evalue == -3) { // picked up orbiting asteroid killer shotMultiPos[SHOT_MISC] = 0; b = player_shot_create(0, SHOT_MISC, this_player->x, this_player->y, mouseX, mouseY, 104, playerNum_); shotAvail[z] = 0; } else if (evalue == -4) { if (player[playerNum_-1].superbombs < 10) ++player[playerNum_-1].superbombs; } else if (evalue == -5) { player[0].items.weapon[FRONT_WEAPON].id = 25; // HOT DOG! player[0].items.weapon[REAR_WEAPON].id = 26; player[1].items.weapon[REAR_WEAPON].id = 26; player[0].last_items = player[0].items; for (uint i = 0; i < COUNTOF(player); ++i) player[i].weapon_mode = 1; memset(shotMultiPos, 0, sizeof(shotMultiPos)); } else if (twoPlayerLinked) { // players get equal share of pick-up cash when linked for (uint i = 0; i < COUNTOF(player); ++i) player[i].cash += evalue / COUNTOF(player); } else { this_player->cash += evalue; } JE_setupExplosion(enemy_screen_x, enemy[z].ey, 0, enemyDat[enemy[z].enemytype].explosiontype, true, false); } else if (this_player->invulnerable_ticks == 0 && enemyAvail[z] == 0 && (enemyDat[enemy[z].enemytype].explosiontype & 1) == 0) // explosiontype & 1 == 0: not ground enemy { int armorleft = enemy[z].armorleft; if (armorleft > damageRate) armorleft = damageRate; JE_playerDamage(armorleft, this_player); // player ship gets push-back from collision if (enemy[z].armorleft > 0) { this_player->x_velocity += (enemy[z].exc * enemy[z].armorleft) / 2; this_player->y_velocity += (enemy[z].eyc * enemy[z].armorleft) / 2; } int armorleft2 = enemy[z].armorleft; if (armorleft2 == 255) armorleft2 = 30000; temp = enemy[z].linknum; if (temp == 0) temp = 255; b = z; if (armorleft2 > armorleft) { // damage enemy if (enemy[z].armorleft != 255) enemy[z].armorleft -= armorleft; soundQueue[5] = S_ENEMY_HIT; } else { // kill enemy for (temp2 = 0; temp2 < 100; temp2++) { if (enemyAvail[temp2] != 1) { temp3 = enemy[temp2].linknum; if (temp2 == b || (temp != 255 && (temp == temp3 || temp - 100 == temp3 || (temp3 > 40 && temp3 / 20 == temp / 20 && temp3 <= temp)))) { int enemy_screen_x = enemy[temp2].ex + enemy[temp2].mapoffset; enemy[temp2].linknum = 0; enemyAvail[temp2] = 1; if (enemyDat[enemy[temp2].enemytype].esize == 1) { JE_setupExplosionLarge(enemy[temp2].enemyground, enemy[temp2].explonum, enemy_screen_x, enemy[temp2].ey); soundQueue[6] = S_EXPLOSION_9; } else { JE_setupExplosion(enemy_screen_x, enemy[temp2].ey, 0, 1, false, false); soundQueue[5] = S_EXPLOSION_4; } } } } enemyAvail[z] = 1; } } } } } } opentyrian-2.1.20221123/src/mainint.h000066400000000000000000000060771432005211200170360ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MAININT_H #define MAININT_H #include "config.h" #include "opentyr.h" #include "palette.h" #include "player.h" #include "sprite.h" extern bool button[4]; // fire, left fire, right fire, mode swap extern JE_shortint constantLastX; extern JE_word textErase; extern JE_word upgradeCost; extern JE_word downgradeCost; extern JE_boolean performSave; extern JE_boolean jumpSection; extern JE_boolean useLastBank; extern bool pause_pressed, ingamemenu_pressed; /*void JE_textMenuWait(JE_word waittime, JE_boolean dogamma);*/ void JE_drawTextWindow(const char *text); void JE_initPlayerData(void); void JE_highScoreScreen(void); void JE_gammaCorrect_func(JE_byte *col, JE_real r); void JE_gammaCorrect(Palette *colorBuffer, JE_byte gamma); JE_boolean JE_gammaCheck(void); /* void JE_textMenuWait(JE_word *waitTime, JE_boolean doGamma); /!\ In setup.h */ void JE_nextEpisode(void); void JE_helpSystem(JE_byte startTopic); void JE_doInGameSetup(void); JE_boolean JE_inGameSetup(void); void JE_inGameHelp(void); void JE_sortHighScores(void); void JE_highScoreCheck(void); void adjust_difficulty(void); bool load_next_demo(void); bool replay_demo_keys(void); void JE_SFCodes(JE_byte playerNum_, JE_integer PX_, JE_integer PY_, JE_integer mouseX_, JE_integer mouseY_); void JE_sort(void); long weapon_upgrade_cost(long base_cost, unsigned int power); ulong JE_getCost(JE_byte itemType, JE_word itemNum); JE_longint JE_getValue(JE_byte itemType, JE_word itemNum); ulong JE_totalScore(const Player *); void JE_drawPortConfigButtons(void); void JE_outCharGlow(JE_word x, JE_word y, const char *s); void JE_playCredits(void); void JE_endLevelAni(void); void JE_drawCube(SDL_Surface * screen, JE_word x, JE_word y, JE_byte filter, JE_byte brightness); void JE_handleChat(void); bool str_pop_int(char *str, int *val); bool JE_loadScreen(void); void JE_operation(JE_byte slot); void JE_inGameDisplays(void); void JE_mainKeyboardInput(void); void JE_pauseGame(void); void JE_playerMovement(Player *this_player, JE_byte inputDevice, JE_byte playerNum, JE_word shipGr, Sprite2_array *shipGrPtr_, JE_word *mouseX, JE_word *mouseY); void JE_mainGamePlayerFunctions(void); const char *JE_getName(JE_byte pnum); void JE_playerCollide(Player *this_player, JE_byte playerNum); #endif /* MAININT_H */ opentyrian-2.1.20221123/src/menus.c000066400000000000000000000334031432005211200165120ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "menus.h" #include "config.h" #include "episodes.h" #include "font.h" #include "fonthand.h" #include "joystick.h" #include "keyboard.h" #include "mouse.h" #include "network.h" #include "nortsong.h" #include "opentyr.h" #include "palette.h" #include "picload.h" #include "sprite.h" #include "vga256d.h" #include "video.h" char episode_name[6][31]; char difficulty_name[7][21]; char gameplay_name[5][26]; bool gameplaySelect(void) { enum MenuItemIndex { MENU_ITEM_1_PLAYER_FULL_GAME = 0, MENU_ITEM_1_PLAYER_ARCADE, MENU_ITEM_2_PLAYER_ARCADE, MENU_ITEM_NETWORK, }; if (shopSpriteSheet.data == NULL) JE_loadCompShapes(&shopSpriteSheet, '1'); // need mouse pointer sprites bool restart = true; const size_t menuItemsCount = COUNTOF(gameplay_name) - 1; size_t selectedIndex = MENU_ITEM_1_PLAYER_FULL_GAME; const int xCenter = 320 / 2; const int yMenuHeader = 20; const int yMenuItems = 54; const int dyMenuItems = 24; const int hMenuItem = 13; int wMenuItem[COUNTOF(gameplay_name) - 1] = { 0 }; for (; ; ) { if (restart) { JE_loadPic(VGAScreen2, 2, false); // Draw header. draw_font_hv_shadow(VGAScreen2, xCenter, yMenuHeader, gameplay_name[0], large_font, centered, 15, -3, false, 2); } // Restore background and header. memcpy(VGAScreen->pixels, VGAScreen2->pixels, (size_t)VGAScreen->pitch * VGAScreen->h); // Draw menu items. for (size_t i = 0; i < menuItemsCount; ++i) { const char *const text = gameplay_name[i + 1]; wMenuItem[i] = JE_textWidth(text, normal_font); const int x = xCenter - wMenuItem[i] / 2; const int y = yMenuItems + dyMenuItems * i; const bool selected = i == selectedIndex; const bool disabled = i == MENU_ITEM_NETWORK; draw_font_hv_shadow(VGAScreen, x, y, text, normal_font, left_aligned, 15, -4 + (selected ? 2 : 0) + (disabled ? -4 : 0), false, 2); } if (restart) { mouseCursor = MOUSE_POINTER_NORMAL; fade_palette(colors, 10, 0, 255); restart = false; } service_SDL_events(true); JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); bool mouseMoved = false; do { SDL_Delay(16); Uint16 oldMouseX = mouse_x; Uint16 oldMouseY = mouse_y; push_joysticks_as_keyboard(); service_SDL_events(false); mouseMoved = mouse_x != oldMouseX || mouse_y != oldMouseY; } while (!(newkey || newmouse || mouseMoved)); // Handle interaction. bool action = false; bool cancel = false; if (mouseMoved || newmouse) { // Find menu item that was hovered or clicked. for (size_t i = 0; i < menuItemsCount; ++i) { const int xMenuItem = xCenter - wMenuItem[i] / 2; if (mouse_x >= xMenuItem && mouse_x < xMenuItem + wMenuItem[i]) { const int yMenuItem = yMenuItems + dyMenuItems * i; if (mouse_y >= yMenuItem && mouse_y < yMenuItem + hMenuItem) { if (selectedIndex != i) { JE_playSampleNum(S_CURSOR); selectedIndex = i; } if (newmouse && lastmouse_but == SDL_BUTTON_LEFT && lastmouse_x >= xMenuItem && lastmouse_x < xMenuItem + wMenuItem[i] && lastmouse_y >= yMenuItem && lastmouse_y < yMenuItem + hMenuItem) { action = true; } break; } } } } if (newmouse) { if (lastmouse_but == SDL_BUTTON_RIGHT) { JE_playSampleNum(S_SPRING); cancel = true; } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_UP: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == 0 ? menuItemsCount - 1 : selectedIndex - 1; break; } case SDL_SCANCODE_DOWN: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == menuItemsCount - 1 ? 0 : selectedIndex + 1; break; } case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: { action = true; break; } case SDL_SCANCODE_ESCAPE: { JE_playSampleNum(S_SPRING); cancel = true; break; } default: break; } } if (action) { switch (selectedIndex) { case MENU_ITEM_1_PLAYER_FULL_GAME: case MENU_ITEM_1_PLAYER_ARCADE: case MENU_ITEM_2_PLAYER_ARCADE: { JE_playSampleNum(S_SELECT); fade_black(10); onePlayerAction = selectedIndex == MENU_ITEM_1_PLAYER_ARCADE; twoPlayerMode = selectedIndex == MENU_ITEM_2_PLAYER_ARCADE; return true; } case MENU_ITEM_NETWORK: { JE_playSampleNum(S_SPRING); break; } default: break; } } if (cancel) { fade_black(15); return false; } } } bool episodeSelect(void) { if (shopSpriteSheet.data == NULL) JE_loadCompShapes(&shopSpriteSheet, '1'); // need mouse pointer sprites bool restart = true; const size_t menuItemsCount = EPISODE_AVAILABLE; size_t selectedIndex = 0; const int xCenter = 320 / 2; const int yMenuHeader = 20; const int xMenuItem = 20; const int yMenuItems = 50; const int dyMenuItems = 30; const int hMenuItem = 13; int wMenuItem[EPISODE_AVAILABLE] = { 0 }; for (; ; ) { if (restart) { JE_loadPic(VGAScreen2, 2, false); // Draw header. draw_font_hv_shadow(VGAScreen2, xCenter, yMenuHeader, episode_name[0], large_font, centered, 15, -3, false, 2); } // Restore background and header. memcpy(VGAScreen->pixels, VGAScreen2->pixels, (size_t)VGAScreen->pitch * VGAScreen->h); // Draw menu items. for (size_t i = 0; i < menuItemsCount; ++i) { const char *const text = episode_name[i + 1]; wMenuItem[i] = JE_textWidth(text, normal_font); const int y = yMenuItems + dyMenuItems * i; const bool selected = i == selectedIndex; const bool disabled = !episodeAvail[i]; draw_font_hv_shadow(VGAScreen, xMenuItem, y, text, normal_font, left_aligned, 15, -4 + (selected ? 2 : 0) + (disabled ? -4 : 0), false, 2); } if (restart) { mouseCursor = MOUSE_POINTER_NORMAL; fade_palette(colors, 10, 0, 255); restart = false; } service_SDL_events(true); JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); bool mouseMoved = false; do { SDL_Delay(16); Uint16 oldMouseX = mouse_x; Uint16 oldMouseY = mouse_y; push_joysticks_as_keyboard(); service_SDL_events(false); NETWORK_KEEP_ALIVE(); mouseMoved = mouse_x != oldMouseX || mouse_y != oldMouseY; } while (!(newkey || newmouse || mouseMoved)); // Handle interaction. bool action = false; bool cancel = false; if (mouseMoved || newmouse) { // Find menu item that was hovered or clicked. for (size_t i = 0; i < menuItemsCount; ++i) { if (mouse_x >= xMenuItem && mouse_x < xMenuItem + wMenuItem[i]) { const int yMenuItem = yMenuItems + dyMenuItems * i; if (mouse_y >= yMenuItem && mouse_y < yMenuItem + hMenuItem) { if (selectedIndex != i) { JE_playSampleNum(S_CURSOR); selectedIndex = i; } if (newmouse && lastmouse_but == SDL_BUTTON_LEFT && lastmouse_x >= xMenuItem && lastmouse_x < xMenuItem + wMenuItem[i] && lastmouse_y >= yMenuItem && lastmouse_y < yMenuItem + hMenuItem) { action = true; } break; } } } } if (newmouse) { if (lastmouse_but == SDL_BUTTON_RIGHT) { JE_playSampleNum(S_SPRING); cancel = true; } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_UP: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == 0 ? menuItemsCount - 1 : selectedIndex - 1; break; } case SDL_SCANCODE_DOWN: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == menuItemsCount - 1 ? 0 : selectedIndex + 1; break; } case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: { action = true; break; } case SDL_SCANCODE_ESCAPE: { JE_playSampleNum(S_SPRING); cancel = true; break; } default: break; } } if (action) { if (episodeAvail[selectedIndex]) { JE_playSampleNum(S_SELECT); fade_black(10); JE_initEpisode(selectedIndex + 1); initial_episode_num = episodeNum; return true; } else { JE_playSampleNum(S_SPRING); } } if (cancel) { fade_black(15); return false; } } } bool difficultySelect(void) { if (shopSpriteSheet.data == NULL) JE_loadCompShapes(&shopSpriteSheet, '1'); // need mouse pointer sprites bool restart = true; const size_t menuItemsCount = COUNTOF(difficulty_name) - 1; size_t menuItemsVisibleCount = menuItemsCount - 3; size_t selectedIndex = 1; size_t lordProgress = 0; const int xCenter = 320 / 2; const int yMenuHeader = 20; const int yMenuItems = 54; const int dyMenuItems = 24; const int hMenuItem = 13; int wMenuItem[COUNTOF(difficulty_name) - 1] = { 0 }; for (; ; ) { if (restart) { JE_loadPic(VGAScreen2, 2, false); // Draw header. draw_font_hv_shadow(VGAScreen2, xCenter, yMenuHeader, difficulty_name[0], large_font, centered, 15, -3, false, 2); } // Restore background and header. memcpy(VGAScreen->pixels, VGAScreen2->pixels, (size_t)VGAScreen->pitch * VGAScreen->h); // Draw menu items. for (size_t i = 0; i < menuItemsVisibleCount; ++i) { const char *const text = difficulty_name[i + 1]; wMenuItem[i] = JE_textWidth(text, normal_font); const int x = xCenter - wMenuItem[i] / 2; const int y = yMenuItems + dyMenuItems * i; const bool selected = i == selectedIndex; draw_font_hv_shadow(VGAScreen, x, y, text, normal_font, left_aligned, 15, -4 + (selected ? 2 : 0), false, 2); } if (restart) { mouseCursor = MOUSE_POINTER_NORMAL; fade_palette(colors, 10, 0, 255); restart = false; } service_SDL_events(true); JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); bool mouseMoved = false; do { SDL_Delay(16); Uint16 oldMouseX = mouse_x; Uint16 oldMouseY = mouse_y; push_joysticks_as_keyboard(); service_SDL_events(false); NETWORK_KEEP_ALIVE(); mouseMoved = mouse_x != oldMouseX || mouse_y != oldMouseY; } while (!(newkey || newmouse || mouseMoved)); // Handle interaction. if (menuItemsVisibleCount == 5) { const SDL_Scancode lordKeys[] = { SDL_SCANCODE_L, SDL_SCANCODE_O, SDL_SCANCODE_R, SDL_SCANCODE_D }; for (size_t i = 0; ; ++i) { if (i == COUNTOF(lordKeys)) { menuItemsVisibleCount = 6; break; } if (!keysactive[lordKeys[i]]) break; } if (newkey) { // Due to key jamming, holding down 4 keys simultaneous may not be possible, so an // alternate input method is also provided. if (lordProgress < COUNTOF(lordKeys) && lastkey_scan == lordKeys[lordProgress]) { lordProgress += 1; if (lordProgress == COUNTOF(lordKeys)) menuItemsVisibleCount = 6; } else { lordProgress = 0; } } } if (SDL_GetModState() & KMOD_SHIFT) { if (menuItemsVisibleCount == 4 && keysactive[SDL_SCANCODE_RIGHTBRACKET]) menuItemsVisibleCount = 5; if (menuItemsVisibleCount == 3 && keysactive[SDL_SCANCODE_G]) menuItemsVisibleCount = 4; } bool action = false; bool cancel = false; if (mouseMoved || newmouse) { // Find menu item that was hovered or clicked. for (size_t i = 0; i < menuItemsVisibleCount; ++i) { const int xMenuItem = xCenter - wMenuItem[i] / 2; if (mouse_x >= xMenuItem && mouse_x < xMenuItem + wMenuItem[i]) { const int yMenuItem = yMenuItems + dyMenuItems * i; if (mouse_y >= yMenuItem && mouse_y < yMenuItem + hMenuItem) { if (selectedIndex != i) { JE_playSampleNum(S_CURSOR); selectedIndex = i; } if (newmouse && lastmouse_but == SDL_BUTTON_LEFT && lastmouse_x >= xMenuItem && lastmouse_x < xMenuItem + wMenuItem[i] && lastmouse_y >= yMenuItem && lastmouse_y < yMenuItem + hMenuItem) { action = true; } break; } } } } if (newmouse) { if (lastmouse_but == SDL_BUTTON_RIGHT) { JE_playSampleNum(S_SPRING); cancel = true; } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_UP: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == 0 ? menuItemsVisibleCount - 1 : selectedIndex - 1; break; } case SDL_SCANCODE_DOWN: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == menuItemsVisibleCount - 1 ? 0 : selectedIndex + 1; break; } case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: { action = true; break; } case SDL_SCANCODE_ESCAPE: { JE_playSampleNum(S_SPRING); cancel = true; break; } default: break; } } if (action) { JE_playSampleNum(S_SELECT); switch (selectedIndex) { case 0: difficultyLevel = DIFFICULTY_EASY; break; case 1: difficultyLevel = DIFFICULTY_NORMAL; break; case 2: difficultyLevel = DIFFICULTY_HARD; break; case 3: difficultyLevel = DIFFICULTY_IMPOSSIBLE; break; case 4: difficultyLevel = DIFFICULTY_SUICIDE; break; case 5: difficultyLevel = DIFFICULTY_ZINGLON; break; } fade_black(10); return true; } if (cancel) { fade_black(15); return false; } } } opentyrian-2.1.20221123/src/menus.h000066400000000000000000000021231432005211200165120ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MENUS_H #define MENUS_H #include "opentyr.h" extern char episode_name[6][31]; extern char difficulty_name[7][21]; extern char gameplay_name[5][26]; bool gameplaySelect(void); bool episodeSelect(void); bool difficultySelect(void); #endif /* MENUS_H */ opentyrian-2.1.20221123/src/mouse.c000066400000000000000000000105431432005211200165130ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mouse.h" #include "keyboard.h" #include "nortvars.h" #include "sprite.h" #include "video.h" #include "vga256d.h" #if defined(TARGET_GP2X) || defined(TARGET_DINGUX) bool has_mouse = false; #else bool has_mouse = true; #endif bool mouse_has_three_buttons = true; bool mouseInactive = true; JE_byte mouseCursor; JE_word mouseX, mouseY, mouseButton; JE_word mouseXB, mouseYB; static JE_word mouseGrabX, mouseGrabY; static JE_byte mouseGrabShape[24 * 28]; static void JE_drawShapeTypeOne(JE_word x, JE_word y, JE_byte *shape) { JE_word xloop = 0, yloop = 0; JE_byte *p = shape; /* shape pointer */ Uint8 *s; /* screen pointer, 8-bit specific */ Uint8 *s_limit; /* buffer boundary */ s = (Uint8 *)VGAScreen->pixels; s += y * VGAScreen->pitch + x; s_limit = (Uint8 *)VGAScreen->pixels; s_limit += VGAScreen->h * VGAScreen->pitch; for (yloop = 0; yloop < 28; yloop++) { for (xloop = 0; xloop < 24; xloop++) { if (s >= s_limit) return; *s = *p; s++; p++; } s -= 24; s += VGAScreen->pitch; } } static void JE_grabShapeTypeOne(JE_word x, JE_word y, JE_byte *shape) { JE_word xloop = 0, yloop = 0; JE_byte *p = shape; /* shape pointer */ Uint8 *s; /* screen pointer, 8-bit specific */ Uint8 *s_limit; /* buffer boundary */ s = (Uint8 *)VGAScreen->pixels; s += y * VGAScreen->pitch + x; s_limit = (Uint8 *)VGAScreen->pixels; s_limit += VGAScreen->h * VGAScreen->pitch; for (yloop = 0; yloop < 28; yloop++) { for (xloop = 0; xloop < 24; xloop++) { if (s >= s_limit) return; *p = *s; s++; p++; } s -= 24; s += VGAScreen->pitch; } } typedef struct { Uint16 index; Uint8 x; Uint8 y; Uint8 w; Uint8 h; Uint8 fx; Uint8 fy; } MousePointerSpriteInfo; static const MousePointerSpriteInfo mousePointerSprites[] = // fka mouseCursorGr { { 273, 0, 0, 11, 16, 0, 0 }, { 275, 0, 0, 21, 16, 10, 8 }, { 277, 0, 0, 21, 16, 10, 7 }, { 279, 0, 0, 16, 21, 8, 10 }, { 281, 8, 0, 16, 21, 7, 10 }, }; void JE_mouseStart(void) { if (has_mouse) { service_SDL_events(false); mouseButton = mousedown ? lastmouse_but : 0; /* incorrect, possibly unimportant */ const MousePointerSpriteInfo *spriteInfo = &mousePointerSprites[mouseCursor]; mouseGrabX = MIN(MAX(spriteInfo->fx, mouse_x), 320 - (spriteInfo->w - spriteInfo->fx)) - spriteInfo->fx; mouseGrabY = MIN(MAX(spriteInfo->fy, mouse_y), 200 - (spriteInfo->h - spriteInfo->fy)) - spriteInfo->fy; JE_grabShapeTypeOne(mouseGrabX, mouseGrabY, mouseGrabShape); if (!mouseInactive) { const Sint32 x = mouse_x - spriteInfo->x - spriteInfo->fx; const Sint32 y = mouse_y - spriteInfo->y - spriteInfo->fy; blit_sprite2x2_clip(VGAScreen, x, y, shopSpriteSheet, spriteInfo->index); } } } void JE_mouseStartFilter(Uint8 filter) { if (has_mouse) { mouseButton = mousedown ? lastmouse_but : 0; /* incorrect, possibly unimportant */ const MousePointerSpriteInfo *spriteInfo = &mousePointerSprites[mouseCursor]; mouseGrabX = MIN(MAX(spriteInfo->fx, mouse_x), 320 - (spriteInfo->w - spriteInfo->fx)) - spriteInfo->fx; mouseGrabY = MIN(MAX(spriteInfo->fy, mouse_y), 200 - (spriteInfo->h - spriteInfo->fy)) - spriteInfo->fy; JE_grabShapeTypeOne(mouseGrabX, mouseGrabY, mouseGrabShape); if (!mouseInactive) { const Sint32 x = mouse_x - spriteInfo->x - spriteInfo->fx; const Sint32 y = mouse_y - spriteInfo->y - spriteInfo->fy; blit_sprite2x2_filter_clip(VGAScreen, x, y, shopSpriteSheet, spriteInfo->index, filter); } } } void JE_mouseReplace(void) { if (has_mouse) JE_drawShapeTypeOne(mouseGrabX, mouseGrabY, mouseGrabShape); } opentyrian-2.1.20221123/src/mouse.h000066400000000000000000000025041432005211200165160ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MOUSE_H #define MOUSE_H #include "opentyr.h" #include "SDL.h" enum { MOUSE_POINTER_NORMAL = 0, MOUSE_POINTER_UP, MOUSE_POINTER_DOWN, MOUSE_POINTER_LEFT, MOUSE_POINTER_RIGHT, }; extern bool has_mouse; extern bool mouse_has_three_buttons; extern bool mouseInactive; extern JE_byte mouseCursor; extern JE_word mouseX, mouseY, mouseButton; extern JE_word mouseXB, mouseYB; void JE_mouseStart(void); void JE_mouseStartFilter(Uint8 filter); void JE_mouseReplace(void); #endif /* MOUSE_H */ opentyrian-2.1.20221123/src/mtrand.c000066400000000000000000000065631432005211200166570ustar00rootroot00000000000000/* Copyright (C) 1997--2004, Makoto Matsumoto, Takuji Nishimura, and Eric Landry; All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of its contributors may not 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. Any feedback is very welcome. http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) Reference: M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3--30. */ #include "mtrand.h" /* Period parameters */ #define N 624 #define M 397 #define MATRIX_A 0x9908b0dfUL /* constant vector a */ #define UPPER_MASK 0x80000000UL /* most significant w-r bits */ #define LOWER_MASK 0x7fffffffUL /* least significant r bits */ static unsigned long x[N]; /* the array for the state vector */ static unsigned long *p0, *p1, *pm; void mt_srand(unsigned long s) { int i; x[0] = s & 0xffffffffUL; for (i = 1; i < N; ++i) { x[i] = (1812433253UL * (x[i - 1] ^ (x[i - 1] >> 30)) + i) & 0xffffffffUL; /* for >32 bit machines */ } p0 = x; p1 = x + 1; pm = x + M; } /* generates a random number on the interval [0,0xffffffff] */ unsigned long mt_rand(void) { unsigned long y; if (!p0) { /* Default seed */ mt_srand(5489UL); } /* Twisted feedback */ y = *p0 = *pm++ ^ (((*p0 & UPPER_MASK) | (*p1 & LOWER_MASK)) >> 1) ^ ((~(*p1 & 1)+1) & MATRIX_A); p0 = p1++; if (pm == x + N) { pm = x; } if (p1 == x + N) { p1 = x; } /* Temper */ y ^= y >> 11; y ^= y << 7 & 0x9d2c5680UL; y ^= y << 15 & 0xefc60000UL; y ^= y >> 18; return y; } /* generates a random number on the interval [0,1]. */ float mt_rand_1(void) { return ((float)mt_rand() / (float)MT_RAND_MAX); } /* generates a random number on the interval [0,1). */ float mt_rand_lt1(void) { /* MT_RAND_MAX must be a float before adding one to it! */ return ((float)mt_rand() / ((float)MT_RAND_MAX + 1.0f)); } opentyrian-2.1.20221123/src/mtrand.h000066400000000000000000000020241432005211200166500ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MTRAND_H #define MTRAND_H #define MT_RAND_MAX 0xffffffffUL void mt_srand(unsigned long s); unsigned long mt_rand(void); float mt_rand_1(void); float mt_rand_lt1(void); #endif /* MTRAND_H */ opentyrian-2.1.20221123/src/musmast.c000066400000000000000000000034051432005211200170530ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "musmast.h" #include "opentyr.h" JE_byte songBuy; const char musicTitle[MUSIC_NUM][48] = { "Asteroid Dance Part 2", "Asteroid Dance Part 1", "Buy/Sell Music", "CAMANIS", "CAMANISE", "Deli Shop Quartet", "Deli Shop Quartet No. 2", "Ending Number 1", "Ending Number 2", "End of Level", "Game Over Solo", "Gryphons of the West", "Somebody pick up the Gryphone", "Gyges, Will You Please Help Me?", "I speak Gygese", "Halloween Ramble", "Tunneling Trolls", "Tyrian, The Level", "The MusicMan", "The Navigator", "Come Back to Me, Savara", "Come Back again to Savara", "Space Journey 1", "Space Journey 2", "The final edge", "START5", "Parlance", "Torm - The Gathering", "TRANSON", "Tyrian: The Song", "ZANAC3", "ZANACS", "Return me to Savara", "High Score Table", "One Mustn't Fall", "Sarah's Song", "A Field for Mag", "Rock Garden", "Quest for Peace", "Composition in Q", "BEER" }; JE_boolean musicFade; opentyrian-2.1.20221123/src/musmast.h000066400000000000000000000023311432005211200170550ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MUSMAST_H #define MUSMAST_H #include "opentyr.h" #define DEFAULT_SONG_BUY 2 #define SONG_LEVELEND 9 #define SONG_GAMEOVER 10 #define SONG_MAPVIEW 19 #define SONG_ENDGAME1 7 #define SONG_ZANAC 31 #define SONG_TITLE 29 #define MUSIC_NUM 41 extern JE_byte songBuy; extern const char musicTitle[MUSIC_NUM][48]; extern JE_boolean musicFade; #endif /* MUSMAST_H */ opentyrian-2.1.20221123/src/network.c000066400000000000000000000466621432005211200170670ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "network.h" #include "episodes.h" #include "fonthand.h" #include "helptext.h" #include "joystick.h" #include "keyboard.h" #include "mainint.h" #include "nortvars.h" #include "opentyr.h" #include "picload.h" #include "sprite.h" #include "varz.h" #include "video.h" #include /* HERE BE DRAGONS! * * When I wrote this code I thought it was wonderful... that thought was very * wrong. It works, but good luck understanding how... I don't anymore. * * Hopefully it'll be rewritten some day. */ #define NET_VERSION 2 // increment whenever networking changes might create incompatibility #define NET_PORT 1333 // UDP #define NET_PACKET_SIZE 256 #define NET_PACKET_QUEUE 16 #define NET_RETRY 640 // ticks to wait for packet acknowledgment before resending #define NET_RESEND 320 // ticks to wait before requesting unreceived game packet #define NET_KEEP_ALIVE 1600 // ticks to wait between keep-alive packets #define NET_TIME_OUT 16000 // ticks to wait before considering connection dead bool isNetworkGame = false; int network_delay = 1 + 1; // minimum is 1 + 0 char *network_opponent_host = NULL; Uint16 network_player_port = NET_PORT, network_opponent_port = NET_PORT; static char empty_string[] = ""; char *network_player_name = empty_string, *network_opponent_name = empty_string; #ifdef WITH_NETWORK static UDPsocket socket; static IPaddress ip; UDPpacket *packet_out_temp; static UDPpacket *packet_temp; UDPpacket *packet_in[NET_PACKET_QUEUE] = { NULL }, *packet_out[NET_PACKET_QUEUE] = { NULL }; static Uint16 last_out_sync = 0, queue_in_sync = 0, queue_out_sync = 0, last_ack_sync = 0; static Uint32 last_in_tick = 0, last_out_tick = 0; UDPpacket *packet_state_in[NET_PACKET_QUEUE] = { NULL }; static UDPpacket *packet_state_in_xor[NET_PACKET_QUEUE] = { NULL }; UDPpacket *packet_state_out[NET_PACKET_QUEUE] = { NULL }; static Uint16 last_state_in_sync = 0, last_state_out_sync = 0; static Uint32 last_state_in_tick = 0; static bool net_initialized = false; static bool connected = false, quit = false; #endif uint thisPlayerNum = 0; /* Player number on this PC (1 or 2) */ JE_boolean haltGame = false; JE_boolean moveOk; /* Special Requests */ JE_boolean pauseRequest, skipLevelRequest, helpRequest, nortShipRequest; JE_boolean yourInGameMenuRequest, inGameMenuRequest; #ifdef WITH_NETWORK static void packet_copy(UDPpacket *dst, UDPpacket *src) { void *temp = dst->data; memcpy(dst, src, sizeof(*dst)); dst->data = temp; memcpy(dst->data, src->data, src->len); } static void packets_shift_up(UDPpacket **packet, int max_packets) { if (packet[0]) { SDLNet_FreePacket(packet[0]); } for (int i = 0; i < max_packets - 1; i++) { packet[i] = packet[i + 1]; } packet[max_packets - 1] = NULL; } static void packets_shift_down(UDPpacket **packet, int max_packets) { if (packet[max_packets - 1]) { SDLNet_FreePacket(packet[max_packets - 1]); } for (int i = max_packets - 1; i > 0; i--) { packet[i] = packet[i - 1]; } packet[0] = NULL; } // prepare new packet for sending void network_prepare(Uint16 type) { SDLNet_Write16(type, &packet_out_temp->data[0]); SDLNet_Write16(last_out_sync, &packet_out_temp->data[2]); } // send packet but don't expect acknowledgment of delivery static bool network_send_no_ack(int len) { packet_out_temp->len = len; if (!SDLNet_UDP_Send(socket, 0, packet_out_temp)) { printf("SDLNet_UDP_Send: %s\n", SDL_GetError()); return false; } return true; } // send packet and place it in queue to be acknowledged bool network_send(int len) { bool temp = network_send_no_ack(len); Uint16 i = last_out_sync - queue_out_sync; if (i < NET_PACKET_QUEUE) { packet_out[i] = SDLNet_AllocPacket(NET_PACKET_SIZE); packet_copy(packet_out[i], packet_out_temp); } else { // connection is probably bad now fprintf(stderr, "warning: outbound packet queue overflow\n"); return false; } last_out_sync++; if (network_is_sync()) last_out_tick = SDL_GetTicks(); return temp; } // send acknowledgment packet static int network_acknowledge(Uint16 sync) { SDLNet_Write16(PACKET_ACKNOWLEDGE, &packet_out_temp->data[0]); SDLNet_Write16(sync, &packet_out_temp->data[2]); network_send_no_ack(4); return 0; } // activity lately? static bool network_is_alive(void) { return (SDL_GetTicks() - last_in_tick < NET_TIME_OUT || SDL_GetTicks() - last_state_in_tick < NET_TIME_OUT); } // poll for new packets received, check that connection is alive, resend queued packets if necessary int network_check(void) { if (!net_initialized) return -1; if (connected) { // timeout if (!network_is_alive()) { if (!quit) network_tyrian_halt(2, false); } // keep-alive static Uint32 keep_alive_tick = 0; if (SDL_GetTicks() - keep_alive_tick > NET_KEEP_ALIVE) { network_prepare(PACKET_KEEP_ALIVE); network_send_no_ack(4); keep_alive_tick = SDL_GetTicks(); } } // retry if (packet_out[0] && SDL_GetTicks() - last_out_tick > NET_RETRY) { if (!SDLNet_UDP_Send(socket, 0, packet_out[0])) { printf("SDLNet_UDP_Send: %s\n", SDL_GetError()); return -1; } last_out_tick = SDL_GetTicks(); } switch (SDLNet_UDP_Recv(socket, packet_temp)) { case -1: printf("SDLNet_UDP_Recv: %s\n", SDL_GetError()); return -1; break; case 0: break; default: if (packet_temp->channel == 0 && packet_temp->len >= 4) { switch (SDLNet_Read16(&packet_temp->data[0])) { case PACKET_ACKNOWLEDGE: if ((Uint16)(SDLNet_Read16(&packet_temp->data[2]) - last_ack_sync) < NET_PACKET_QUEUE) { last_ack_sync = SDLNet_Read16(&packet_temp->data[2]); } { Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - queue_out_sync; if (i < NET_PACKET_QUEUE) { if (packet_out[i]) { SDLNet_FreePacket(packet_out[i]); packet_out[i] = NULL; } } } // remove acknowledged packets from queue while (packet_out[0] == NULL && (Uint16)(last_ack_sync - queue_out_sync) < NET_PACKET_QUEUE) { packets_shift_up(packet_out, NET_PACKET_QUEUE); queue_out_sync++; } last_in_tick = SDL_GetTicks(); break; case PACKET_CONNECT: queue_in_sync = SDLNet_Read16(&packet_temp->data[2]); for (int i = 0; i < NET_PACKET_QUEUE; i++) { if (packet_in[i]) { SDLNet_FreePacket(packet_in[i]); packet_in[i] = NULL; } } // fall through case PACKET_DETAILS: case PACKET_WAITING: case PACKET_BUSY: case PACKET_GAME_QUIT: case PACKET_GAME_PAUSE: case PACKET_GAME_MENU: { Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - queue_in_sync; if (i < NET_PACKET_QUEUE) { if (packet_in[i] == NULL) packet_in[i] = SDLNet_AllocPacket(NET_PACKET_SIZE); packet_copy(packet_in[i], packet_temp); } else { // inbound packet queue overflow/underflow // under normal circumstances, this is okay } } network_acknowledge(SDLNet_Read16(&packet_temp->data[2])); // fall through case PACKET_KEEP_ALIVE: last_in_tick = SDL_GetTicks(); break; case PACKET_QUIT: if (!quit) { network_prepare(PACKET_QUIT); network_send(4); // PACKET_QUIT } network_acknowledge(SDLNet_Read16(&packet_temp->data[2])); if (!quit) network_tyrian_halt(1, true); break; case PACKET_STATE: // place packet in queue if within limits { Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - last_state_in_sync + 1; if (i < NET_PACKET_QUEUE) { if (packet_state_in[i] == NULL) packet_state_in[i] = SDLNet_AllocPacket(NET_PACKET_SIZE); packet_copy(packet_state_in[i], packet_temp); } } break; case PACKET_STATE_XOR: // place packet in queue if within limits { Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - last_state_in_sync + 1; if (i < NET_PACKET_QUEUE) { if (packet_state_in_xor[i] == NULL) { packet_state_in_xor[i] = SDLNet_AllocPacket(NET_PACKET_SIZE); packet_copy(packet_state_in_xor[i], packet_temp); } else if (SDLNet_Read16(&packet_state_in_xor[i]->data[0]) != PACKET_STATE_XOR) { for (int j = 4; j < packet_state_in_xor[i]->len; j++) packet_state_in_xor[i]->data[j] ^= packet_temp->data[j]; SDLNet_Write16(PACKET_STATE_XOR, &packet_state_in_xor[i]->data[0]); } } } break; case PACKET_STATE_RESEND: // resend requested state packet if still available { Uint16 i = last_state_out_sync - SDLNet_Read16(&packet_temp->data[2]); if (i > 0 && i < NET_PACKET_QUEUE) { if (packet_state_out[i]) { if (!SDLNet_UDP_Send(socket, 0, packet_state_out[i])) { printf("SDLNet_UDP_Send: %s\n", SDL_GetError()); return -1; } } } } break; default: fprintf(stderr, "warning: bad packet %d received\n", SDLNet_Read16(&packet_temp->data[0])); return 0; break; } return 1; } break; } return 0; } // discard working packet, now processing next packet in queue bool network_update(void) { if (packet_in[0]) { packets_shift_up(packet_in, NET_PACKET_QUEUE); queue_in_sync++; return true; } return false; } // has opponent gotten all the packets we've sent? bool network_is_sync(void) { return (queue_out_sync - last_ack_sync == 1); } // prepare new state for sending void network_state_prepare(void) { if (packet_state_out[0]) { fprintf(stderr, "warning: state packet overwritten (previous packet remains unsent)\n"); } else { packet_state_out[0] = SDLNet_AllocPacket(NET_PACKET_SIZE); packet_state_out[0]->len = 28; } SDLNet_Write16(PACKET_STATE, &packet_state_out[0]->data[0]); SDLNet_Write16(last_state_out_sync, &packet_state_out[0]->data[2]); memset(&packet_state_out[0]->data[4], 0, 28 - 4); } // send state packet, xor packet if applicable int network_state_send(void) { if (!SDLNet_UDP_Send(socket, 0, packet_state_out[0])) { printf("SDLNet_UDP_Send: %s\n", SDL_GetError()); return -1; } // send xor of last network_delay packets if (network_delay > 1 && (last_state_out_sync + 1) % network_delay == 0 && packet_state_out[network_delay - 1] != NULL) { packet_copy(packet_temp, packet_state_out[0]); SDLNet_Write16(PACKET_STATE_XOR, &packet_temp->data[0]); for (int i = 1; i < network_delay; i++) for (int j = 4; j < packet_temp->len; j++) packet_temp->data[j] ^= packet_state_out[i]->data[j]; if (!SDLNet_UDP_Send(socket, 0, packet_temp)) { printf("SDLNet_UDP_Send: %s\n", SDL_GetError()); return -1; } } packets_shift_down(packet_state_out, NET_PACKET_QUEUE); last_state_out_sync++; return 0; } // receive state packet, wait until received bool network_state_update(void) { if (network_state_is_reset()) { return 0; } else { packets_shift_up(packet_state_in, NET_PACKET_QUEUE); packets_shift_up(packet_state_in_xor, NET_PACKET_QUEUE); last_state_in_sync++; // current xor packet index int x = network_delay - (last_state_in_sync - 1) % network_delay - 1; // loop until needed packet is available while (!packet_state_in[0]) { // xor the packet from thin air, if possible if (packet_state_in_xor[x] && SDLNet_Read16(&packet_state_in_xor[x]->data[0]) == PACKET_STATE_XOR) { // check for all other required packets bool okay = true; for (int i = 1; i <= x; i++) { if (packet_state_in[i] == NULL) { okay = false; break; } } if (okay) { packet_state_in[0] = SDLNet_AllocPacket(NET_PACKET_SIZE); packet_copy(packet_state_in[0], packet_state_in_xor[x]); for (int i = 1; i <= x; i++) for (int j = 4; j < packet_state_in[0]->len; j++) packet_state_in[0]->data[j] ^= packet_state_in[i]->data[j]; break; } } static Uint32 resend_tick = 0; if (SDL_GetTicks() - last_state_in_tick > NET_RESEND && SDL_GetTicks() - resend_tick > NET_RESEND) { SDLNet_Write16(PACKET_STATE_RESEND, &packet_out_temp->data[0]); SDLNet_Write16(last_state_in_sync - 1, &packet_out_temp->data[2]); network_send_no_ack(4); // PACKET_RESEND resend_tick = SDL_GetTicks(); } if (network_check() == 0) SDL_Delay(1); } if (network_delay > 1) { // process the current in packet against the xor queue if (packet_state_in_xor[x] == NULL) { packet_state_in_xor[x] = SDLNet_AllocPacket(NET_PACKET_SIZE); packet_copy(packet_state_in_xor[x], packet_state_in[0]); packet_state_in_xor[x]->status = 0; } else { for (int j = 4; j < packet_state_in_xor[x]->len; j++) packet_state_in_xor[x]->data[j] ^= packet_state_in[0]->data[j]; } } last_state_in_tick = SDL_GetTicks(); } return 1; } // ignore first network_delay states of level bool network_state_is_reset(void) { return (last_state_out_sync < network_delay); } // reset queues for new level void network_state_reset(void) { last_state_in_sync = last_state_out_sync = 0; for (int i = 0; i < NET_PACKET_QUEUE; i++) { if (packet_state_in[i]) { SDLNet_FreePacket(packet_state_in[i]); packet_state_in[i] = NULL; } } for (int i = 0; i < NET_PACKET_QUEUE; i++) { if (packet_state_in_xor[i]) { SDLNet_FreePacket(packet_state_in_xor[i]); packet_state_in_xor[i] = NULL; } } for (int i = 0; i < NET_PACKET_QUEUE; i++) { if (packet_state_out[i]) { SDLNet_FreePacket(packet_state_out[i]); packet_state_out[i] = NULL; } } last_state_in_tick = SDL_GetTicks(); } // attempt to punch through firewall by firing off UDP packets at the opponent // exchange game information int network_connect(void) { SDLNet_ResolveHost(&ip, network_opponent_host, network_opponent_port); SDLNet_UDP_Bind(socket, 0, &ip); Uint16 episodes = 0, episodes_local = 0; assert(EPISODE_MAX <= 16); for (int i = EPISODE_MAX - 1; i >= 0; i--) { episodes <<= 1; episodes |= (episodeAvail[i] != 0); } episodes_local = episodes; assert(NET_PACKET_SIZE - 12 >= 20 + 1); if (strlen(network_player_name) > 20) network_player_name[20] = '\0'; connect_reset: network_prepare(PACKET_CONNECT); SDLNet_Write16(NET_VERSION, &packet_out_temp->data[4]); SDLNet_Write16(network_delay, &packet_out_temp->data[6]); SDLNet_Write16(episodes_local, &packet_out_temp->data[8]); SDLNet_Write16(thisPlayerNum, &packet_out_temp->data[10]); strcpy((char *)&packet_out_temp->data[12], network_player_name); network_send(12 + strlen(network_player_name) + 1); // PACKET_CONNECT // until opponent sends connect packet while (true) { push_joysticks_as_keyboard(); service_SDL_events(false); if (newkey && lastkey_scan == SDL_SCANCODE_ESCAPE) network_tyrian_halt(0, false); // never timeout last_in_tick = SDL_GetTicks(); if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_CONNECT) break; network_update(); network_check(); SDL_Delay(16); } connect_again: if (SDLNet_Read16(&packet_in[0]->data[4]) != NET_VERSION) { fprintf(stderr, "error: network version did not match opponent's\n"); network_tyrian_halt(4, true); } if (SDLNet_Read16(&packet_in[0]->data[6]) != network_delay) { fprintf(stderr, "error: network delay did not match opponent's\n"); network_tyrian_halt(5, true); } if (SDLNet_Read16(&packet_in[0]->data[10]) == thisPlayerNum) { fprintf(stderr, "error: player number conflicts with opponent's\n"); network_tyrian_halt(6, true); } episodes = SDLNet_Read16(&packet_in[0]->data[8]); for (int i = 0; i < EPISODE_MAX; i++) { episodeAvail[i] &= (episodes & 1); episodes >>= 1; } network_opponent_name = malloc(packet_in[0]->len - 12 + 1); strcpy(network_opponent_name, (char *)&packet_in[0]->data[12]); network_update(); // until opponent has acknowledged while (!network_is_sync()) { service_SDL_events(false); // got a duplicate packet; process it again (but why?) if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_CONNECT) goto connect_again; network_check(); // maybe opponent didn't get our packet if (SDL_GetTicks() - last_out_tick > NET_RETRY) goto connect_reset; SDL_Delay(16); } // send another packet since sometimes the network syncs without both connect packets exchanged // there should be a better way to handle this network_prepare(PACKET_CONNECT); SDLNet_Write16(NET_VERSION, &packet_out_temp->data[4]); SDLNet_Write16(network_delay, &packet_out_temp->data[6]); SDLNet_Write16(episodes_local, &packet_out_temp->data[8]); SDLNet_Write16(thisPlayerNum, &packet_out_temp->data[10]); strcpy((char *)&packet_out_temp->data[12], network_player_name); network_send(12 + strlen(network_player_name) + 1); // PACKET_CONNECT connected = true; return 0; } // something has gone wrong :( void network_tyrian_halt(unsigned int err, bool attempt_sync) { const char *const err_msg[] = { "Quitting...", "Other player quit the game.", "Network connection was lost.", "Network connection failed.", "Network version mismatch.", "Network delay mismatch.", "Network player number conflict.", }; quit = true; if (err >= COUNTOF(err_msg)) err = 0; fade_black(10); VGAScreen = VGAScreenSeg; JE_loadPic(VGAScreen, 2, false); JE_dString(VGAScreen, JE_fontCenter(err_msg[err], SMALL_FONT_SHAPES), 140, err_msg[err], SMALL_FONT_SHAPES); JE_showVGA(); fade_palette(colors, 10, 0, 255); if (attempt_sync) { while (!network_is_sync() && network_is_alive()) { service_SDL_events(false); network_check(); SDL_Delay(16); } } if (err) { while (!JE_anyButton()) SDL_Delay(16); } fade_black(10); SDLNet_Quit(); JE_tyrianHalt(5); } int network_init(void) { printf("Initializing network...\n"); if (network_delay * 2 > NET_PACKET_QUEUE - 2) { fprintf(stderr, "error: network delay would overflow packet queue\n"); return -4; } if (SDLNet_Init() == -1) { fprintf(stderr, "error: SDLNet_Init: %s\n", SDLNet_GetError()); return -1; } socket = SDLNet_UDP_Open(network_player_port); if (!socket) { fprintf(stderr, "error: SDLNet_UDP_Open: %s\n", SDLNet_GetError()); return -2; } packet_temp = SDLNet_AllocPacket(NET_PACKET_SIZE); packet_out_temp = SDLNet_AllocPacket(NET_PACKET_SIZE); if (!packet_temp || !packet_out_temp) { printf("SDLNet_AllocPacket: %s\n", SDLNet_GetError()); return -3; } net_initialized = true; return 0; } #endif void JE_clearSpecialRequests(void) { pauseRequest = false; inGameMenuRequest = false; skipLevelRequest = false; helpRequest = false; nortShipRequest = false; } opentyrian-2.1.20221123/src/network.h000066400000000000000000000054521432005211200170640ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef NETWORK_H #define NETWORK_H #include "opentyr.h" #include "SDL.h" #ifdef WITH_NETWORK # include "SDL_net.h" #endif #define PACKET_ACKNOWLEDGE 0x00 // #define PACKET_KEEP_ALIVE 0x01 // #define PACKET_CONNECT 0x10 // version, delay, episodes, player_number, name #define PACKET_DETAILS 0x11 // episode, difficulty #define PACKET_QUIT 0x20 // #define PACKET_WAITING 0x21 // #define PACKET_BUSY 0x22 // #define PACKET_GAME_QUIT 0x30 // #define PACKET_GAME_PAUSE 0x31 // #define PACKET_GAME_MENU 0x32 // #define PACKET_STATE_RESEND 0x40 // state_id #define PACKET_STATE 0x41 // (not acknowledged) #define PACKET_STATE_XOR 0x42 // (not acknowledged) extern bool isNetworkGame; extern int network_delay; extern char *network_opponent_host; extern Uint16 network_player_port, network_opponent_port; extern char *network_player_name, *network_opponent_name; #ifdef WITH_NETWORK extern UDPpacket *packet_out_temp; extern UDPpacket *packet_in[], *packet_out[], *packet_state_in[], *packet_state_out[]; #endif extern uint thisPlayerNum; extern JE_boolean haltGame; extern JE_boolean moveOk; extern JE_boolean pauseRequest, skipLevelRequest, helpRequest, nortShipRequest; extern JE_boolean yourInGameMenuRequest, inGameMenuRequest; #ifdef WITH_NETWORK void network_prepare(Uint16 type); bool network_send(int len); int network_check(void); bool network_update(void); bool network_is_sync(void); void network_state_prepare(void); int network_state_send(void); bool network_state_update(void); bool network_state_is_reset(void); void network_state_reset(void); int network_connect(void); void network_tyrian_halt(unsigned int err, bool attempt_sync); int network_init(void); void JE_clearSpecialRequests(void); #define NETWORK_KEEP_ALIVE() \ if (isNetworkGame) \ network_check(); #else #define NETWORK_KEEP_ALIVE() #endif #endif /* NETWORK_H */ opentyrian-2.1.20221123/src/nortsong.c000066400000000000000000000146561432005211200172450ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "nortsong.h" #include "file.h" #include "joystick.h" #include "keyboard.h" #include "loudness.h" #include "musmast.h" #include "opentyr.h" #include "params.h" #include "sndmast.h" #include "vga256d.h" #include "SDL.h" JE_word frameCountMax; Sint16 *soundSamples[SOUND_COUNT] = { NULL }; /* [1..soundnum + 9] */ // FKA digiFx size_t soundSampleCount[SOUND_COUNT] = { 0 }; /* [1..soundnum + 9] */ // FKA fxSize JE_word tyrMusicVolume, fxVolume; const JE_word fxPlayVol = 4; JE_word tempVolume; // The period of the x86 programmable interval timer in milliseconds. static const float pitPeriod = (12.0f / 14318180.0f) * 1000.0f; static Uint16 delaySpeed = 0x4300; static float delayPeriod = 0x4300 * ((12.0f / 14318180.0f) * 1000.0f); static Uint32 target = 0; static Uint32 target2 = 0; void setDelay(int delay) // FKA NortSong.frameCount { target = SDL_GetTicks() + delay * delayPeriod; } void setDelay2(int delay) // FKA NortSong.frameCount2 { target2 = SDL_GetTicks() + delay * delayPeriod; } Uint32 getDelayTicks(void) // FKA NortSong.frameCount { Sint32 delay = target - SDL_GetTicks(); return MAX(0, delay); } Uint32 getDelayTicks2(void) // FKA NortSong.frameCount2 { Sint32 delay = target2 - SDL_GetTicks(); return MAX(0, delay); } void wait_delay(void) { Sint32 delay = target - SDL_GetTicks(); if (delay > 0) SDL_Delay(delay); } void service_wait_delay(void) { for (; ; ) { service_SDL_events(false); Sint32 delay = target - SDL_GetTicks(); if (delay <= 0) return; SDL_Delay(MIN(delay, SDL_POLL_INTERVAL)); } } void wait_delayorinput(void) { for (; ; ) { service_SDL_events(false); poll_joysticks(); if (newkey || mousedown || joydown) { newkey = false; return; } Sint32 delay = target - SDL_GetTicks(); if (delay <= 0) return; SDL_Delay(MIN(delay, SDL_POLL_INTERVAL)); } } void loadSndFile(bool xmas) { FILE *f; f = dir_fopen_die(data_dir(), "tyrian.snd", "rb"); Uint16 sfxCount; Uint32 sfxPositions[SFX_COUNT + 1]; // Read number of sounds. fread_u16_die(&sfxCount, 1, f); if (sfxCount != SFX_COUNT) goto die; // Read positions of sounds. fread_u32_die(sfxPositions, sfxCount, f); // Determine end of last sound. fseek(f, 0, SEEK_END); sfxPositions[sfxCount] = ftell(f); // Read samples. for (size_t i = 0; i < sfxCount; ++i) { soundSampleCount[i] = sfxPositions[i + 1] - sfxPositions[i]; // Sound size cannot exceed 64 KiB. if (soundSampleCount[i] > UINT16_MAX) goto die; free(soundSamples[i]); soundSamples[i] = malloc(soundSampleCount[i]); fseek(f, sfxPositions[i], SEEK_SET); fread_u8_die((Uint8 *)soundSamples[i], soundSampleCount[i], f); } fclose(f); f = dir_fopen_die(data_dir(), xmas ? "voicesc.snd" : "voices.snd", "rb"); Uint16 voiceCount; Uint32 voicePositions[VOICE_COUNT + 1]; // Read number of sounds. fread_u16_die(&voiceCount, 1, f); if (voiceCount != VOICE_COUNT) goto die; // Read positions of sounds. fread_u32_die(voicePositions, voiceCount, f); // Determine end of last sound. fseek(f, 0, SEEK_END); voicePositions[voiceCount] = ftell(f); for (size_t vi = 0; vi < voiceCount; ++vi) { size_t i = SFX_COUNT + vi; soundSampleCount[i] = voicePositions[vi + 1] - voicePositions[vi]; // Voice sounds have some bad data at the end. soundSampleCount[i] = soundSampleCount[i] >= 100 ? soundSampleCount[i] - 100 : 0; // Sound size cannot exceed 64 KiB. if (soundSampleCount[i] > UINT16_MAX) goto die; free(soundSamples[i]); soundSamples[i] = malloc(soundSampleCount[i]); fseek(f, voicePositions[vi], SEEK_SET); fread_u8_die((Uint8 *)soundSamples[i], soundSampleCount[i], f); } fclose(f); // Convert samples to output sample format and rate. SDL_AudioCVT cvt; if (SDL_BuildAudioCVT(&cvt, AUDIO_S8, 1, 11025, AUDIO_S16SYS, 1, audioSampleRate) < 0) { fprintf(stderr, "error: Failed to build audio converter: %s\n", SDL_GetError()); for (int i = 0; i < SOUND_COUNT; ++i) soundSampleCount[i] = 0; return; } size_t maxSampleSize = 0; for (size_t i = 0; i < SOUND_COUNT; ++i) maxSampleSize = MAX(maxSampleSize, soundSampleCount[i]); cvt.buf = malloc(maxSampleSize * cvt.len_mult); for (size_t i = 0; i < SOUND_COUNT; ++i) { cvt.len = soundSampleCount[i]; memcpy(cvt.buf, soundSamples[i], cvt.len); if (SDL_ConvertAudio(&cvt)) { fprintf(stderr, "error: Failed to convert audio: %s\n", SDL_GetError()); soundSampleCount[i] = 0; continue; } free(soundSamples[i]); soundSamples[i] = malloc(cvt.len_cvt); memcpy(soundSamples[i], cvt.buf, cvt.len_cvt); soundSampleCount[i] = cvt.len_cvt / sizeof (Sint16); } free(cvt.buf); return; die: fprintf(stderr, "error: Unexpected data was read from a file.\n"); SDL_Quit(); exit(EXIT_FAILURE); } void JE_playSampleNum(JE_byte samplenum) { multiSamplePlay(soundSamples[samplenum-1], soundSampleCount[samplenum-1], 0, fxPlayVol); } void setDelaySpeed(Uint16 speed) // FKA NortSong.speed and NortSong.setTimerInt { delaySpeed = speed; delayPeriod = speed * pitPeriod; } void JE_changeVolume(JE_word *music, int music_delta, JE_word *sample, int sample_delta) { int music_temp = *music + music_delta, sample_temp = *sample + sample_delta; if (music_delta) { if (music_temp > 255) { music_temp = 255; JE_playSampleNum(S_CLINK); } else if (music_temp < 0) { music_temp = 0; JE_playSampleNum(S_CLINK); } } if (sample_delta) { if (sample_temp > 255) { sample_temp = 255; JE_playSampleNum(S_CLINK); } else if (sample_temp < 0) { sample_temp = 0; JE_playSampleNum(S_CLINK); } } *music = music_temp; *sample = sample_temp; set_volume(*music, *sample); } opentyrian-2.1.20221123/src/nortsong.h000066400000000000000000000031041432005211200172340ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef NORTSONG_H #define NORTSONG_H #include "opentyr.h" #include "musmast.h" #include "sndmast.h" #include "SDL.h" extern JE_word frameCountMax; extern Sint16 *soundSamples[SOUND_COUNT]; extern size_t soundSampleCount[SOUND_COUNT]; extern JE_word tyrMusicVolume, fxVolume; extern const JE_word fxPlayVol; extern JE_word tempVolume; void setDelay(int delay); void setDelay2(int delay); Uint32 getDelayTicks(void); Uint32 getDelayTicks2(void); void wait_delay(void); void service_wait_delay(void); void wait_delayorinput(void); void setDelaySpeed(Uint16 speed); void JE_changeVolume(JE_word *music, int music_delta, JE_word *sample, int sample_delta); void loadSndFile(bool xmas); void JE_playSampleNum(JE_byte samplenum); #endif /* NORTSONG_H */ opentyrian-2.1.20221123/src/nortvars.c000066400000000000000000000043101432005211200172340ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "nortvars.h" #include "file.h" #include "joystick.h" #include "keyboard.h" #include "opentyr.h" #include "vga256d.h" #include "video.h" #include #include JE_boolean inputDetected; JE_boolean JE_anyButton(void) { poll_joysticks(); service_SDL_events(true); return newkey || mousedown || joydown; } void JE_dBar3(SDL_Surface *surface, JE_integer x, JE_integer y, JE_integer num, JE_integer col) { JE_byte z; JE_byte zWait = 2; col += 2; for (z = 0; z <= num; z++) { JE_rectangle(surface, x, y - 1, x + 8, y, col); /* SEGa000 */ if (zWait > 0) { zWait--; } else { col++; zWait = 1; } y -= 2; } } void JE_barDrawShadow(SDL_Surface *surface, JE_word x, JE_word y, JE_word res, JE_word col, JE_word amt, JE_word xsize, JE_word ysize) { xsize--; ysize--; for (int z = 1; z <= amt / res; z++) { JE_barShade(surface, x+2, y+2, x+xsize+2, y+ysize+2); fill_rectangle_xy(surface, x, y, x+xsize, y+ysize, col+12); fill_rectangle_xy(surface, x, y, x+xsize, y, col+13); JE_pix(surface, x, y, col+15); fill_rectangle_xy(surface, x, y+ysize, x+xsize, y+ysize, col+11); x += xsize + 2; } amt %= res; if (amt > 0) { JE_barShade(surface, x+2, y+2, x+xsize+2, y+ysize+2); fill_rectangle_xy(surface, x,y, x+xsize, y+ysize, col+(12 / res * amt)); } } void JE_wipeKey(void) { // /!\ Doesn't seems to affect anything. } opentyrian-2.1.20221123/src/nortvars.h000066400000000000000000000023701432005211200172450ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef NORTVARS_H #define NORTVARS_H #include "opentyr.h" #include "SDL.h" extern JE_boolean inputDetected; JE_boolean JE_anyButton(void); void JE_dBar3(SDL_Surface *surface, JE_integer x, JE_integer y, JE_integer num, JE_integer col); void JE_barDrawShadow(SDL_Surface *surface, JE_word x, JE_word y, JE_word res, JE_word col, JE_word amt, JE_word xsize, JE_word ysize); void JE_wipeKey(void); #endif /* NORTVARS_H */ opentyrian-2.1.20221123/src/opentyr.c000066400000000000000000000505711432005211200170700ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "opentyr.h" #include "config.h" #include "destruct.h" #include "editship.h" #include "episodes.h" #include "file.h" #include "font.h" #include "fonthand.h" #include "helptext.h" #include "joystick.h" #include "jukebox.h" #include "keyboard.h" #include "loudness.h" #include "mainint.h" #include "mouse.h" #include "mtrand.h" #include "network.h" #include "nortsong.h" #include "nortvars.h" #include "opentyrian_version.h" #include "palette.h" #include "params.h" #include "picload.h" #include "sprite.h" #include "tyrian2.h" #include "varz.h" #include "vga256d.h" #include "video.h" #include "video_scale.h" #include "xmas.h" #include "SDL.h" #include #include #include #include #include const char *opentyrian_str = "OpenTyrian"; const char *opentyrian_version = OPENTYRIAN_VERSION; static size_t getDisplayPickerItemsCount(void) { return 1 + (size_t)SDL_GetNumVideoDisplays(); } static const char *getDisplayPickerItem(size_t i, char *buffer, size_t bufferSize) { if (i == 0) return "Window"; snprintf(buffer, bufferSize, "Display %d", (int)i); return buffer; } static size_t getScalerPickerItemsCount(void) { return (size_t)scalers_count; } static const char *getScalerPickerItem(size_t i, char *buffer, size_t bufferSize) { (void)buffer, (void)bufferSize; return scalers[i].name; } static size_t getScalingModePickerItemsCount(void) { return (size_t)ScalingMode_MAX; } static const char *getScalingModePickerItem(size_t i, char *buffer, size_t bufferSize) { (void)buffer, (void)bufferSize; return scaling_mode_names[i]; } void setupMenu(void) { typedef enum { MENU_ITEM_NONE = 0, MENU_ITEM_DONE, MENU_ITEM_GRAPHICS, MENU_ITEM_SOUND, MENU_ITEM_JUKEBOX, MENU_ITEM_DESTRUCT, MENU_ITEM_DISPLAY, MENU_ITEM_SCALER, MENU_ITEM_SCALING_MODE, MENU_ITEM_MUSIC_VOLUME, MENU_ITEM_SOUND_VOLUME, } MenuItemId; typedef enum { MENU_NONE = 0, MENU_SETUP, MENU_GRAPHICS, MENU_SOUND, } MenuId; typedef struct { MenuItemId id; const char *name; const char *description; size_t (*getPickerItemsCount)(void); const char *(*getPickerItem)(size_t i, char *buffer, size_t bufferSize); } MenuItem; typedef struct { const char *header; const MenuItem items[6]; } Menu; static const Menu menus[] = { [MENU_SETUP] = { .header = "Setup", .items = { { MENU_ITEM_GRAPHICS, "Graphics...", "Change the graphics settings." }, { MENU_ITEM_SOUND, "Sound...", "Change the sound settings." }, { MENU_ITEM_JUKEBOX, "Jukebox", "Listen to the music of Tyrian." }, // { MENU_ITEM_DESTRUCT, "Destruct", "Play a bonus mini-game." }, { MENU_ITEM_DONE, "Done", "Return to the main menu." }, { -1 } }, }, [MENU_GRAPHICS] = { .header = "Graphics", .items = { { MENU_ITEM_DISPLAY, "Display:", "Change the display mode.", getDisplayPickerItemsCount, getDisplayPickerItem }, { MENU_ITEM_SCALER, "Scaler:", "Change the pixel art scaling algorithm.", getScalerPickerItemsCount, getScalerPickerItem }, { MENU_ITEM_SCALING_MODE, "Scaling Mode:", "Change the scaling mode.", getScalingModePickerItemsCount, getScalingModePickerItem }, { MENU_ITEM_DONE, "Done", "Return to the previous menu." }, { -1 } }, }, [MENU_SOUND] = { .header = "Sound", .items = { { MENU_ITEM_MUSIC_VOLUME, "Music Volume", "Change volume with the left/right arrow keys." }, { MENU_ITEM_SOUND_VOLUME, "Sound Volume", "Change volume with the left/right arrow keys." }, { MENU_ITEM_DONE, "Done", "Return to the previous menu." }, { -1 } }, }, }; char buffer[100]; if (shopSpriteSheet.data == NULL) JE_loadCompShapes(&shopSpriteSheet, '1'); // need mouse pointer sprites bool restart = true; MenuId menuParents[COUNTOF(menus)] = { MENU_NONE }; size_t selectedMenuItemIndexes[COUNTOF(menus)] = { 0 }; MenuId currentMenu = MENU_SETUP; MenuItemId currentPicker = MENU_ITEM_NONE; size_t pickerSelectedIndex = 0; const int xCenter = 320 / 2; const int yMenuHeader = 4; const int xMenuItem = 45; const int xMenuItemName = xMenuItem; const int wMenuItemName = 135; const int xMenuItemValue = xMenuItemName + wMenuItemName; const int wMenuItemValue = 95; const int wMenuItem = wMenuItemName + wMenuItemValue; const int yMenuItems = 37; const int dyMenuItems = 21; const int hMenuItem = 13; for (; ; ) { if (restart) { JE_loadPic(VGAScreen2, 2, false); fill_rectangle_wh(VGAScreen2, 0, 192, 320, 8, 0); } // Restore background. memcpy(VGAScreen->pixels, VGAScreen2->pixels, (size_t)VGAScreen->pitch * VGAScreen->h); const Menu *menu = &menus[currentMenu]; // Draw header. draw_font_hv_shadow(VGAScreen, xCenter, yMenuHeader, menu->header, large_font, centered, 15, -3, false, 2); int yPicker = 0; const int dyPickerItem = 15; const int dyPickerItemPadding = 2; const int hPickerItem = dyPickerItem - dyPickerItemPadding; size_t *const selectedMenuItemIndex = &selectedMenuItemIndexes[currentMenu]; const MenuItem *const menuItems = menu->items; // Draw menu items. size_t menuItemsCount = 0; for (size_t i = 0; menuItems[i].id != (MenuItemId)-1; ++i) { menuItemsCount += 1; const MenuItem *const menuItem = &menuItems[i]; const int y = yMenuItems + dyMenuItems * i; const bool selected = i == *selectedMenuItemIndex; const bool disabled = currentPicker != MENU_ITEM_NONE && !selected; if (selected) yPicker = y; const char *const name = menuItem->name; draw_font_hv_shadow(VGAScreen, xMenuItemName, y, name, normal_font, left_aligned, 15, -3 + (selected ? 2 : 0) + (disabled ? -4 : 0), false, 2); switch (menuItem->id) { case MENU_ITEM_DISPLAY:; const char *value = "Window"; if (fullscreen_display >= 0) { snprintf(buffer, sizeof(buffer), "Display %d", fullscreen_display + 1); value = buffer; } draw_font_hv_shadow(VGAScreen, xMenuItemValue, y, value, normal_font, left_aligned, 15, -3 + (selected ? 2 : 0) + (disabled ? -4 : 0), false, 2); break; case MENU_ITEM_SCALER: draw_font_hv_shadow(VGAScreen, xMenuItemValue, y, scalers[scaler].name, normal_font, left_aligned, 15, -3 + (selected ? 2 : 0) + (disabled ? -4 : 0), false, 2); break; case MENU_ITEM_SCALING_MODE: draw_font_hv_shadow(VGAScreen, xMenuItemValue, y, scaling_mode_names[scaling_mode], normal_font, left_aligned, 15, -3 + (selected ? 2 : 0) + (disabled ? -4 : 0), false, 2); break; case MENU_ITEM_MUSIC_VOLUME: JE_barDrawShadow(VGAScreen, xMenuItemValue, y, 1, music_disabled ? 170 : 174, (tyrMusicVolume + 4) / 8, 2, 10); JE_rectangle(VGAScreen, xMenuItemValue - 2, y - 2, xMenuItemValue + 96, y + 11, 242); break; case MENU_ITEM_SOUND_VOLUME: JE_barDrawShadow(VGAScreen, xMenuItemValue, y, 1, samples_disabled ? 170 : 174, (fxVolume + 4) / 8, 2, 10); JE_rectangle(VGAScreen, xMenuItemValue - 2, y - 2, xMenuItemValue + 96, y + 11, 242); break; default: break; } } // Draw status text. JE_textShade(VGAScreen, xMenuItemName, 190, menuItems[*selectedMenuItemIndex].description, 15, 4, PART_SHADE); // Draw picker box and items. if (currentPicker != MENU_ITEM_NONE) { const MenuItem *selectedMenuItem = &menuItems[*selectedMenuItemIndex]; const size_t pickerItemsCount = selectedMenuItem->getPickerItemsCount(); const int hPicker = dyPickerItem * pickerItemsCount - dyPickerItemPadding; yPicker = MIN(yPicker, 200 - 10 - (hPicker + 5 + 2)); JE_rectangle(VGAScreen, xMenuItemValue - 5, yPicker- 3, xMenuItemValue + wMenuItemValue + 5 - 1, yPicker + hPicker + 3 - 1, 248); JE_rectangle(VGAScreen, xMenuItemValue - 4, yPicker- 4, xMenuItemValue + wMenuItemValue + 4 - 1, yPicker + hPicker + 4 - 1, 250); JE_rectangle(VGAScreen, xMenuItemValue - 3, yPicker- 5, xMenuItemValue + wMenuItemValue + 3 - 1, yPicker + hPicker + 5 - 1, 248); fill_rectangle_wh(VGAScreen, xMenuItemValue - 2, yPicker - 2, wMenuItemValue + 2 + 2, hPicker + 2 + 2, 224); for (size_t i = 0; i < pickerItemsCount; ++i) { const int y = yPicker + dyPickerItem * (int)i; const bool selected = i == pickerSelectedIndex; const char *value = selectedMenuItem->getPickerItem(i, buffer, sizeof buffer); draw_font_hv_shadow(VGAScreen, xMenuItemValue, y, value, normal_font, left_aligned, 15, -3 + (selected ? 2 : 0), false, 2); } } if (restart) { mouseCursor = MOUSE_POINTER_NORMAL; fade_palette(colors, 10, 0, 255); restart = false; } service_SDL_events(true); JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); bool mouseMoved = false; int oldFullscreenDisplay = fullscreen_display; do { SDL_Delay(16); Uint16 oldMouseX = mouse_x; Uint16 oldMouseY = mouse_y; push_joysticks_as_keyboard(); service_SDL_events(false); mouseMoved = mouse_x != oldMouseX || mouse_y != oldMouseY; } while (!(newkey || newmouse || mouseMoved || fullscreen_display != oldFullscreenDisplay)); if (currentPicker == MENU_ITEM_NONE) { // Handle menu item interaction. bool action = false; if (mouseMoved || newmouse) { // Find menu item name or value that was hovered or clicked. if (mouse_x >= xMenuItem && mouse_x < xMenuItem + wMenuItem) { for (size_t i = 0; i < menuItemsCount; ++i) { const int yMenuItem = yMenuItems + dyMenuItems * i; if (mouse_y >= yMenuItem && mouse_y < yMenuItem + hMenuItem) { if (*selectedMenuItemIndex != i) { JE_playSampleNum(S_CURSOR); *selectedMenuItemIndex = i; } if (newmouse && lastmouse_but == SDL_BUTTON_LEFT && lastmouse_y >= yMenuItem && lastmouse_y < yMenuItem + hMenuItem) { // Act on menu item via name. if (lastmouse_x >= xMenuItemName && lastmouse_x < xMenuItemName + wMenuItemName) { action = true; } // Act on menu item via value. else if (lastmouse_x >= xMenuItemValue && lastmouse_x < xMenuItemValue + wMenuItemValue) { switch (menuItems[*selectedMenuItemIndex].id) { case MENU_ITEM_DISPLAY: case MENU_ITEM_SCALER: case MENU_ITEM_SCALING_MODE: { action = true; break; } case MENU_ITEM_MUSIC_VOLUME: { JE_playSampleNum(S_CURSOR); int value = (lastmouse_x - xMenuItemValue) * 255 / (wMenuItemValue - 1); tyrMusicVolume = MIN(MAX(0, value), 255); set_volume(tyrMusicVolume, fxVolume); break; } case MENU_ITEM_SOUND_VOLUME: { int value = (lastmouse_x - xMenuItemValue) * 255 / (wMenuItemValue - 1); fxVolume = MIN(MAX(0, value), 255); set_volume(tyrMusicVolume, fxVolume); JE_playSampleNum(S_CURSOR); break; } default: break; } } } break; } } } } if (newmouse) { if (lastmouse_but == SDL_BUTTON_RIGHT) { JE_playSampleNum(S_SPRING); currentMenu = menuParents[currentMenu]; } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_UP: { JE_playSampleNum(S_CURSOR); *selectedMenuItemIndex = *selectedMenuItemIndex == 0 ? menuItemsCount - 1 : *selectedMenuItemIndex - 1; break; } case SDL_SCANCODE_DOWN: { JE_playSampleNum(S_CURSOR); *selectedMenuItemIndex = *selectedMenuItemIndex == menuItemsCount - 1 ? 0 : *selectedMenuItemIndex + 1; break; } case SDL_SCANCODE_LEFT: { switch (menuItems[*selectedMenuItemIndex].id) { case MENU_ITEM_MUSIC_VOLUME: { JE_playSampleNum(S_CURSOR); JE_changeVolume(&tyrMusicVolume, -8, &fxVolume, 0); break; } case MENU_ITEM_SOUND_VOLUME: { JE_changeVolume(&tyrMusicVolume, 0, &fxVolume, -8); JE_playSampleNum(S_CURSOR); break; } default: break; } break; } case SDL_SCANCODE_RIGHT: { switch (menuItems[*selectedMenuItemIndex].id) { case MENU_ITEM_MUSIC_VOLUME: { JE_playSampleNum(S_CURSOR); JE_changeVolume(&tyrMusicVolume, 8, &fxVolume, 0); break; } case MENU_ITEM_SOUND_VOLUME: { JE_changeVolume(&tyrMusicVolume, 0, &fxVolume, 8); JE_playSampleNum(S_CURSOR); break; } default: break; } break; } case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: { action = true; break; } case SDL_SCANCODE_ESCAPE: { JE_playSampleNum(S_SPRING); currentMenu = menuParents[currentMenu]; break; } default: break; } } if (action) { const MenuItemId selectedMenuItemId = menuItems[*selectedMenuItemIndex].id; switch (selectedMenuItemId) { case MENU_ITEM_DONE: { JE_playSampleNum(S_SELECT); currentMenu = menuParents[currentMenu]; break; } case MENU_ITEM_GRAPHICS: { JE_playSampleNum(S_SELECT); menuParents[MENU_GRAPHICS] = currentMenu; currentMenu = MENU_GRAPHICS; selectedMenuItemIndexes[currentMenu] = 0; break; } case MENU_ITEM_SOUND: { JE_playSampleNum(S_SELECT); menuParents[MENU_SOUND] = currentMenu; currentMenu = MENU_SOUND; selectedMenuItemIndexes[currentMenu] = 0; break; } case MENU_ITEM_JUKEBOX: { JE_playSampleNum(S_SELECT); fade_black(10); jukebox(); restart = true; break; } case MENU_ITEM_DESTRUCT: { JE_playSampleNum(S_SELECT); fade_black(10); JE_destructGame(); restart = true; break; } case MENU_ITEM_DISPLAY: { JE_playSampleNum(S_CLICK); currentPicker = selectedMenuItemId; pickerSelectedIndex = (size_t)(fullscreen_display + 1); break; } case MENU_ITEM_SCALER: { JE_playSampleNum(S_CLICK); currentPicker = selectedMenuItemId; pickerSelectedIndex = scaler; break; } case MENU_ITEM_SCALING_MODE: { JE_playSampleNum(S_CLICK); currentPicker = selectedMenuItemId; pickerSelectedIndex = scaling_mode; break; } case MENU_ITEM_MUSIC_VOLUME: { JE_playSampleNum(S_CLICK); music_disabled = !music_disabled; if (!music_disabled) restart_song(); break; } case MENU_ITEM_SOUND_VOLUME: { samples_disabled = !samples_disabled; JE_playSampleNum(S_CLICK); break; } default: break; } } if (currentMenu == MENU_NONE) { fade_black(10); return; } } else { const MenuItem *selectedMenuItem = &menuItems[*selectedMenuItemIndex]; // Handle picker interaction. bool action = false; if (mouseMoved || newmouse) { const size_t pickerItemsCount = selectedMenuItem->getPickerItemsCount(); // Find picker item that was hovered clicked. if (mouse_x >= xMenuItemValue && mouse_x < xMenuItemValue + wMenuItemValue) { for (size_t i = 0; i < pickerItemsCount; ++i) { const int yPickerItem = yPicker + dyPickerItem * i; if (mouse_y >= yPickerItem && mouse_y < yPickerItem + hPickerItem) { if (pickerSelectedIndex != i) { JE_playSampleNum(S_CURSOR); pickerSelectedIndex = i; } // Act on picker item. if (newmouse && lastmouse_but == SDL_BUTTON_LEFT && lastmouse_x >= xMenuItemValue && lastmouse_y < xMenuItemValue + wMenuItemName && lastmouse_y >= yPickerItem && lastmouse_y < yPickerItem + hPickerItem) { action = true; } } } } } if (newmouse) { if (lastmouse_but == SDL_BUTTON_RIGHT) { JE_playSampleNum(S_SPRING); currentPicker = MENU_ITEM_NONE; } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_UP: { JE_playSampleNum(S_CURSOR); const size_t pickerItemsCount = selectedMenuItem->getPickerItemsCount(); pickerSelectedIndex = pickerSelectedIndex == 0 ? pickerItemsCount - 1 : pickerSelectedIndex - 1; break; } case SDL_SCANCODE_DOWN: { JE_playSampleNum(S_CURSOR); const size_t pickerItemsCount = selectedMenuItem->getPickerItemsCount(); pickerSelectedIndex = pickerSelectedIndex == pickerItemsCount - 1 ? 0 : pickerSelectedIndex + 1; break; } case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: { action = true; break; } case SDL_SCANCODE_ESCAPE: { JE_playSampleNum(S_SPRING); currentPicker = MENU_ITEM_NONE; break; } default: break; } } if (action) { JE_playSampleNum(S_CLICK); switch (selectedMenuItem->id) { case MENU_ITEM_DISPLAY: { if ((int)pickerSelectedIndex - 1 != fullscreen_display) reinit_fullscreen((int)pickerSelectedIndex - 1); break; } case MENU_ITEM_SCALER: { if (pickerSelectedIndex != scaler) { const int oldScaler = scaler; if (!init_scaler(pickerSelectedIndex) && // try new scaler !init_scaler(oldScaler)) // revert on fail { exit(EXIT_FAILURE); } } break; } case MENU_ITEM_SCALING_MODE: { scaling_mode = pickerSelectedIndex; break; } default: break; } currentPicker = MENU_ITEM_NONE; } } } } int main(int argc, char *argv[]) { mt_srand(time(NULL)); printf("\nWelcome to... >> %s %s <<\n\n", opentyrian_str, opentyrian_version); printf("Copyright (C) 2022 The OpenTyrian Development Team\n\n"); printf("This program comes with ABSOLUTELY NO WARRANTY.\n"); printf("This is free software, and you are welcome to redistribute it\n"); printf("under certain conditions. See the file COPYING for details.\n\n"); if (SDL_Init(0)) { printf("Failed to initialize SDL: %s\n", SDL_GetError()); return -1; } JE_loadConfiguration(); xmas = xmas_time(); // arg handler may override JE_paramCheck(argc, argv); JE_scanForEpisodes(); init_video(); init_keyboard(); init_joysticks(); printf("assuming mouse detected\n"); // SDL can't tell us if there isn't one if (xmas && (!dir_file_exists(data_dir(), "tyrianc.shp") || !dir_file_exists(data_dir(), "voicesc.snd"))) { xmas = false; fprintf(stderr, "warning: Christmas is missing.\n"); } JE_loadPals(); JE_loadMainShapeTables(xmas ? "tyrianc.shp" : "tyrian.shp"); if (xmas && !xmas_prompt()) { xmas = false; free_main_shape_tables(); JE_loadMainShapeTables("tyrian.shp"); } /* Default Options */ youAreCheating = false; smoothScroll = true; loadDestruct = false; if (!audio_disabled) { printf("initializing SDL audio...\n"); init_audio(); load_music(); loadSndFile(xmas); } else { printf("audio disabled\n"); } if (record_demo) printf("demo recording enabled (input limited to keyboard)\n"); JE_loadExtraShapes(); /*Editship*/ JE_loadHelpText(); /*debuginfo("Help text complete");*/ if (isNetworkGame) { #ifdef WITH_NETWORK if (network_init()) { network_tyrian_halt(3, false); } #else fprintf(stderr, "OpenTyrian was compiled without networking support."); JE_tyrianHalt(5); #endif } #ifdef NDEBUG if (!isNetworkGame) intro_logos(); #endif for (; ; ) { JE_initPlayerData(); JE_sortHighScores(); play_demo = false; stopped_demo = false; gameLoaded = false; jumpSection = false; #ifdef WITH_NETWORK if (isNetworkGame) { networkStartScreen(); } else #endif { if (!titleScreen()) { // Player quit from title screen. break; } } if (loadDestruct) { JE_destructGame(); loadDestruct = false; } else { JE_main(); if (trentWin) { // Player beat SuperTyrian. break; } } } JE_tyrianHalt(0); return 0; } opentyrian-2.1.20221123/src/opentyr.h000066400000000000000000000033321432005211200170660ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef OPENTYR_H #define OPENTYR_H #include "SDL_types.h" #include #include #define COUNTOF(x) ((unsigned)(sizeof(x) / sizeof *(x))) // use only on arrays! #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) #ifndef M_PI #define M_PI 3.14159265358979323846 // pi #endif #ifndef M_PI_2 #define M_PI_2 1.57079632679489661923 // pi/2 #endif #ifndef M_PI_4 #define M_PI_4 0.78539816339744830962 // pi/4 #endif typedef unsigned int uint; typedef unsigned long ulong; // Pascal types, yuck. typedef Sint32 JE_longint; typedef Sint16 JE_integer; typedef Sint8 JE_shortint; typedef Uint16 JE_word; typedef Uint8 JE_byte; typedef bool JE_boolean; typedef char JE_char; typedef float JE_real; #define TYRIAN_VERSION "2.1" extern const char *opentyrian_str; extern const char *opentyrian_version; void setupMenu(void); #endif /* OPENTYR_H */ opentyrian-2.1.20221123/src/opentyrian_version.h000066400000000000000000000002431432005211200213210ustar00rootroot00000000000000#ifndef OPENTYRIAN_VERSION_H #define OPENTYRIAN_VERSION_H #ifndef OPENTYRIAN_VERSION #define OPENTYRIAN_VERSION "v2.1.???" #endif #endif // OPENTYRIAN_VERSION_H opentyrian-2.1.20221123/src/opl.c000066400000000000000000001465441432005211200161700ustar00rootroot00000000000000/* * Copyright (C) 2002-2010 The DOSBox Team * OPL2/OPL3 emulation library * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman * Copyright (C) 1998-2001 Ken Silverman * Ken Silverman's official web site: "http://www.advsys.net/ken" */ #include "opl.h" #include #include #include // rand() #include // memset() #define fltype double /* define attribution that inlines/forces inlining of a function (optional) */ #define OPL_INLINE inline #undef NUM_CHANNELS #if defined(OPLTYPE_IS_OPL3) #define NUM_CHANNELS 18 #else #define NUM_CHANNELS 9 #endif #define MAXOPERATORS (NUM_CHANNELS*2) #define FL05 ((fltype)0.5) #define FL2 ((fltype)2.0) #define PI ((fltype)3.1415926535897932384626433832795) #define FIXEDPT 0x10000 // fixed-point calculations using 16+16 #define FIXEDPT_LFO 0x1000000 // fixed-point calculations using 8+24 #define WAVEPREC 1024 // waveform precision (10 bits) #define INTFREQU ((fltype)(14318180.0 / 288.0)) // clocking of the chip #define OF_TYPE_ATT 0 #define OF_TYPE_DEC 1 #define OF_TYPE_REL 2 #define OF_TYPE_SUS 3 #define OF_TYPE_SUS_NOKEEP 4 #define OF_TYPE_OFF 5 #define ARC_CONTROL 0x00 #define ARC_TVS_KSR_MUL 0x20 #define ARC_KSL_OUTLEV 0x40 #define ARC_ATTR_DECR 0x60 #define ARC_SUSL_RELR 0x80 #define ARC_FREQ_NUM 0xa0 #define ARC_KON_BNUM 0xb0 #define ARC_PERC_MODE 0xbd #define ARC_FEEDBACK 0xc0 #define ARC_WAVE_SEL 0xe0 #define ARC_SECONDSET 0x100 // second operator set for OPL3 #define OP_ACT_OFF 0x00 #define OP_ACT_NORMAL 0x01 // regular channel activated (bitmasked) #define OP_ACT_PERC 0x02 // percussion channel activated (bitmasked) #define BLOCKBUF_SIZE 512 // vibrato constants #define VIBTAB_SIZE 8 #define VIBFAC 70/50000 // no braces, integer mul/div // tremolo constants and table #define TREMTAB_SIZE 53 #define TREM_FREQ ((fltype)(3.7)) // tremolo at 3.7hz /* operator struct definition For OPL2 all 9 channels consist of two operators each, carrier and modulator. Channel x has operators x as modulator and operators (9+x) as carrier. For OPL3 all 18 channels consist either of two operators (2op mode) or four operators (4op mode) which is determined through register4 of the second adlib register set. Only the channels 0,1,2 (first set) and 9,10,11 (second set) can act as 4op channels. The two additional operators for a channel y come from the 2op channel y+3 so the operators y, (9+y), y+3, (9+y)+3 make up a 4op channel. */ typedef struct operator_struct { Bit32s cval, lastcval; // current output/last output (used for feedback) Bit32u tcount, wfpos, tinc; // time (position in waveform) and time increment fltype amp, step_amp; // and amplification (envelope) fltype vol; // volume fltype sustain_level; // sustain level Bit32s mfbi; // feedback amount fltype a0, a1, a2, a3; // attack rate function coefficients fltype decaymul, releasemul; // decay/release rate functions Bit32u op_state; // current state of operator (attack/decay/sustain/release/off) Bit32u toff; Bit32s freq_high; // highest three bits of the frequency, used for vibrato calculations Bit16s* cur_wform; // start of selected waveform Bit32u cur_wmask; // mask for selected waveform Bit32u act_state; // activity state (regular, percussion) bool sus_keep; // keep sustain level when decay finished bool vibrato,tremolo; // vibrato/tremolo enable bits // variables used to provide non-continuous envelopes Bit32u generator_pos; // for non-standard sample rates we need to determine how many samples have passed Bits cur_env_step; // current (standardized) sample position Bits env_step_a,env_step_d,env_step_r; // number of std samples of one step (for attack/decay/release mode) Bit8u step_skip_pos_a; // position of 8-cyclic step skipping (always 2^x to check against mask) Bits env_step_skip_a; // bitmask that determines if a step is skipped (respective bit is zero then) #if defined(OPLTYPE_IS_OPL3) bool is_4op,is_4op_attached; // base of a 4op channel/part of a 4op channel Bit32s left_pan,right_pan; // opl3 stereo panning amount #endif } op_type; // per-chip variables static op_type op[MAXOPERATORS]; static Bits int_samplerate; static Bit8u status; static Bit32u opl_index; #if defined(OPLTYPE_IS_OPL3) static Bit8u adlibreg[512]; // adlib register set (including second set) static Bit8u wave_sel[44]; // waveform selection #else static Bit8u adlibreg[256]; // adlib register set static Bit8u wave_sel[22]; // waveform selection #endif // vibrato/tremolo increment/counter static Bit32u vibtab_pos; static Bit32u vibtab_add; static Bit32u tremtab_pos; static Bit32u tremtab_add; // enable an operator void enable_operator(Bitu regbase, op_type* op_pt, Bit32u act_type); // functions to change parameters of an operator void change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt); void change_attackrate(Bitu regbase, op_type* op_pt); void change_decayrate(Bitu regbase, op_type* op_pt); void change_releaserate(Bitu regbase, op_type* op_pt); void change_sustainlevel(Bitu regbase, op_type* op_pt); void change_waveform(Bitu regbase, op_type* op_pt); void change_keepsustain(Bitu regbase, op_type* op_pt); void change_vibrato(Bitu regbase, op_type* op_pt); void change_feedback(Bitu chanbase, op_type* op_pt); static Bit32u generator_add; // should be a chip parameter static fltype recipsamp; // inverse of sampling rate static Bit16s wavtable[WAVEPREC*3]; // wave form table // vibrato/tremolo tables static Bit32s vib_table[VIBTAB_SIZE]; static Bit32s trem_table[TREMTAB_SIZE*2]; static Bit32s vibval_const[BLOCKBUF_SIZE]; static Bit32s tremval_const[BLOCKBUF_SIZE]; // vibrato value tables (used per-operator) static Bit32s vibval_var1[BLOCKBUF_SIZE]; static Bit32s vibval_var2[BLOCKBUF_SIZE]; //static Bit32s vibval_var3[BLOCKBUF_SIZE]; //static Bit32s vibval_var4[BLOCKBUF_SIZE]; // vibrato/trmolo value table pointers static Bit32s *vibval1, *vibval2, *vibval3, *vibval4; static Bit32s *tremval1, *tremval2, *tremval3, *tremval4; // key scale level lookup table static const fltype kslmul[4] = { 0.0, 0.5, 0.25, 1.0 // -> 0, 3, 1.5, 6 dB/oct }; // frequency multiplicator lookup table static const fltype frqmul_tab[16] = { 0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15 }; // calculated frequency multiplication values (depend on sampling rate) static fltype frqmul[16]; // key scale levels static Bit8u kslev[8][16]; // map a channel number to the register offset of the modulator (=register base) static const Bit8u modulatorbase[9] = { 0,1,2, 8,9,10, 16,17,18 }; // map a register base to a modulator operator number or operator number #if defined(OPLTYPE_IS_OPL3) static const Bit8u regbase2modop[44] = { 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8, // first set 18,19,20,18,19,20,0,0,21,22,23,21,22,23,0,0,24,25,26,24,25,26 // second set }; static const Bit8u regbase2op[44] = { 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17, // first set 18,19,20,27,28,29,0,0,21,22,23,30,31,32,0,0,24,25,26,33,34,35 // second set }; #else static const Bit8u regbase2modop[22] = { 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8 }; static const Bit8u regbase2op[22] = { 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17 }; #endif // start of the waveform static const Bit32u waveform[8] = { WAVEPREC, WAVEPREC>>1, WAVEPREC, (WAVEPREC*3)>>2, 0, 0, (WAVEPREC*5)>>2, WAVEPREC<<1 }; // length of the waveform as mask static const Bit32u wavemask[8] = { WAVEPREC-1, WAVEPREC-1, (WAVEPREC>>1)-1, (WAVEPREC>>1)-1, WAVEPREC-1, ((WAVEPREC*3)>>2)-1, WAVEPREC>>1, WAVEPREC-1 }; // where the first entry resides static const Bit32u wavestart[8] = { 0, WAVEPREC>>1, 0, WAVEPREC>>2, 0, 0, 0, WAVEPREC>>3 }; // envelope generator function constants static const fltype attackconst[4] = { (fltype)(1/2.82624), (fltype)(1/2.25280), (fltype)(1/1.88416), (fltype)(1/1.59744) }; static const fltype decrelconst[4] = { (fltype)(1/39.28064), (fltype)(1/31.41608), (fltype)(1/26.17344), (fltype)(1/22.44608) }; void operator_advance(op_type* op_pt, Bit32s vib) { op_pt->wfpos = op_pt->tcount; // waveform position // advance waveform time op_pt->tcount += op_pt->tinc; op_pt->tcount += (Bit32s)(op_pt->tinc)*vib/FIXEDPT; op_pt->generator_pos += generator_add; } void operator_advance_drums(op_type* op_pt1, Bit32s vib1, op_type* op_pt2, Bit32s vib2, op_type* op_pt3, Bit32s vib3) { Bit32u c1 = op_pt1->tcount/FIXEDPT; Bit32u c3 = op_pt3->tcount/FIXEDPT; Bit32u phasebit = (((c1 & 0x88) ^ ((c1<<5) & 0x80)) | ((c3 ^ (c3<<2)) & 0x20)) ? 0x02 : 0x00; Bit32u noisebit = rand()&1; Bit32u snare_phase_bit = (((Bitu)((op_pt1->tcount/FIXEDPT) / 0x100))&1); //Hihat Bit32u inttm = (phasebit<<8) | (0x34<<(phasebit ^ (noisebit<<1))); op_pt1->wfpos = inttm*FIXEDPT; // waveform position // advance waveform time op_pt1->tcount += op_pt1->tinc; op_pt1->tcount += (Bit32s)(op_pt1->tinc)*vib1/FIXEDPT; op_pt1->generator_pos += generator_add; //Snare inttm = ((1+snare_phase_bit) ^ noisebit)<<8; op_pt2->wfpos = inttm*FIXEDPT; // waveform position // advance waveform time op_pt2->tcount += op_pt2->tinc; op_pt2->tcount += (Bit32s)(op_pt2->tinc)*vib2/FIXEDPT; op_pt2->generator_pos += generator_add; //Cymbal inttm = (1+phasebit)<<8; op_pt3->wfpos = inttm*FIXEDPT; // waveform position // advance waveform time op_pt3->tcount += op_pt3->tinc; op_pt3->tcount += (Bit32s)(op_pt3->tinc)*vib3/FIXEDPT; op_pt3->generator_pos += generator_add; } // output level is sustained, mode changes only when operator is turned off (->release) // or when the keep-sustained bit is turned off (->sustain_nokeep) void operator_output(op_type* op_pt, Bit32s modulator, Bit32s trem) { if (op_pt->op_state != OF_TYPE_OFF) { op_pt->lastcval = op_pt->cval; Bit32u i = (Bit32u)((op_pt->wfpos+modulator)/FIXEDPT); // wform: -16384 to 16383 (0x4000) // trem : 32768 to 65535 (0x10000) // step_amp: 0.0 to 1.0 // vol : 1/2^14 to 1/2^29 (/0x4000; /1../0x8000) op_pt->cval = (Bit32s)(op_pt->step_amp*op_pt->vol*op_pt->cur_wform[i&op_pt->cur_wmask]*trem/16.0); } } // no action, operator is off void operator_off(op_type* op_pt) { (void) op_pt; } // output level is sustained, mode changes only when operator is turned off (->release) // or when the keep-sustained bit is turned off (->sustain_nokeep) void operator_sustain(op_type* op_pt) { Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples for (Bit32u ct=0; ctcur_env_step++; } op_pt->generator_pos -= num_steps_add*FIXEDPT; } // operator in release mode, if output level reaches zero the operator is turned off void operator_release(op_type* op_pt) { // ??? boundary? if (op_pt->amp > 0.00000001) { // release phase op_pt->amp *= op_pt->releasemul; } Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples for (Bit32u ct=0; ctcur_env_step++; // sample counter if ((op_pt->cur_env_step & op_pt->env_step_r)==0) { if (op_pt->amp <= 0.00000001) { // release phase finished, turn off this operator op_pt->amp = 0.0; if (op_pt->op_state == OF_TYPE_REL) { op_pt->op_state = OF_TYPE_OFF; } } op_pt->step_amp = op_pt->amp; } } op_pt->generator_pos -= num_steps_add*FIXEDPT; } // operator in decay mode, if sustain level is reached the output level is either // kept (sustain level keep enabled) or the operator is switched into release mode void operator_decay(op_type* op_pt) { if (op_pt->amp > op_pt->sustain_level) { // decay phase op_pt->amp *= op_pt->decaymul; } Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples for (Bit32u ct=0; ctcur_env_step++; if ((op_pt->cur_env_step & op_pt->env_step_d)==0) { if (op_pt->amp <= op_pt->sustain_level) { // decay phase finished, sustain level reached if (op_pt->sus_keep) { // keep sustain level (until turned off) op_pt->op_state = OF_TYPE_SUS; op_pt->amp = op_pt->sustain_level; } else { // next: release phase op_pt->op_state = OF_TYPE_SUS_NOKEEP; } } op_pt->step_amp = op_pt->amp; } } op_pt->generator_pos -= num_steps_add*FIXEDPT; } // operator in attack mode, if full output level is reached, // the operator is switched into decay mode void operator_attack(op_type* op_pt) { op_pt->amp = ((op_pt->a3*op_pt->amp + op_pt->a2)*op_pt->amp + op_pt->a1)*op_pt->amp + op_pt->a0; Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples for (Bit32u ct=0; ctcur_env_step++; // next sample if ((op_pt->cur_env_step & op_pt->env_step_a)==0) { // check if next step already reached if (op_pt->amp > 1.0) { // attack phase finished, next: decay op_pt->op_state = OF_TYPE_DEC; op_pt->amp = 1.0; op_pt->step_amp = 1.0; } op_pt->step_skip_pos_a <<= 1; if (op_pt->step_skip_pos_a==0) op_pt->step_skip_pos_a = 1; if (op_pt->step_skip_pos_a & op_pt->env_step_skip_a) { // check if required to skip next step op_pt->step_amp = op_pt->amp; } } } op_pt->generator_pos -= num_steps_add*FIXEDPT; } typedef void (*optype_fptr)(op_type*); const optype_fptr opfuncs[6] = { operator_attack, operator_decay, operator_release, operator_sustain, // sustain phase (keeping level) operator_release, // sustain_nokeep phase (release-style) operator_off }; void change_attackrate(Bitu regbase, op_type* op_pt) { Bits attackrate = adlibreg[ARC_ATTR_DECR+regbase]>>4; if (attackrate) { fltype f = (fltype)(pow(FL2,(fltype)attackrate+(op_pt->toff>>2)-1)*attackconst[op_pt->toff&3]*recipsamp); // attack rate coefficients op_pt->a0 = (fltype)(0.0377*f); op_pt->a1 = (fltype)(10.73*f+1); op_pt->a2 = (fltype)(-17.57*f); op_pt->a3 = (fltype)(7.42*f); Bits step_skip = attackrate*4 + op_pt->toff; Bits steps = step_skip >> 2; op_pt->env_step_a = (1<<(steps<=12?12-steps:0))-1; Bits step_num = (step_skip<=48)?(4-(step_skip&3)):0; static const Bit8u step_skip_mask[5] = {0xff, 0xfe, 0xee, 0xba, 0xaa}; op_pt->env_step_skip_a = step_skip_mask[step_num]; #if defined(OPLTYPE_IS_OPL3) if (step_skip>=60) { #else if (step_skip>=62) { #endif op_pt->a0 = (fltype)(2.0); // something that triggers an immediate transition to amp:=1.0 op_pt->a1 = (fltype)(0.0); op_pt->a2 = (fltype)(0.0); op_pt->a3 = (fltype)(0.0); } } else { // attack disabled op_pt->a0 = 0.0; op_pt->a1 = 1.0; op_pt->a2 = 0.0; op_pt->a3 = 0.0; op_pt->env_step_a = 0; op_pt->env_step_skip_a = 0; } } void change_decayrate(Bitu regbase, op_type* op_pt) { Bits decayrate = adlibreg[ARC_ATTR_DECR+regbase]&15; // decaymul should be 1.0 when decayrate==0 if (decayrate) { fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*recipsamp); op_pt->decaymul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(decayrate+(op_pt->toff>>2))))); Bits steps = (decayrate*4 + op_pt->toff) >> 2; op_pt->env_step_d = (1<<(steps<=12?12-steps:0))-1; } else { op_pt->decaymul = 1.0; op_pt->env_step_d = 0; } } void change_releaserate(Bitu regbase, op_type* op_pt) { Bits releaserate = adlibreg[ARC_SUSL_RELR+regbase]&15; // releasemul should be 1.0 when releaserate==0 if (releaserate) { fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*recipsamp); op_pt->releasemul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(releaserate+(op_pt->toff>>2))))); Bits steps = (releaserate*4 + op_pt->toff) >> 2; op_pt->env_step_r = (1<<(steps<=12?12-steps:0))-1; } else { op_pt->releasemul = 1.0; op_pt->env_step_r = 0; } } void change_sustainlevel(Bitu regbase, op_type* op_pt) { Bits sustainlevel = adlibreg[ARC_SUSL_RELR+regbase]>>4; // sustainlevel should be 0.0 when sustainlevel==15 (max) if (sustainlevel<15) { op_pt->sustain_level = (fltype)(pow(FL2,(fltype)sustainlevel * (-FL05))); } else { op_pt->sustain_level = 0.0; } } void change_waveform(Bitu regbase, op_type* op_pt) { #if defined(OPLTYPE_IS_OPL3) if (regbase>=ARC_SECONDSET) regbase -= (ARC_SECONDSET-22); // second set starts at 22 #endif // waveform selection op_pt->cur_wmask = wavemask[wave_sel[regbase]]; op_pt->cur_wform = &wavtable[waveform[wave_sel[regbase]]]; // (might need to be adapted to waveform type here...) } void change_keepsustain(Bitu regbase, op_type* op_pt) { op_pt->sus_keep = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x20)>0; if (op_pt->op_state==OF_TYPE_SUS) { if (!op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS_NOKEEP; } else if (op_pt->op_state==OF_TYPE_SUS_NOKEEP) { if (op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS; } } // enable/disable vibrato/tremolo LFO effects void change_vibrato(Bitu regbase, op_type* op_pt) { op_pt->vibrato = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x40)!=0; op_pt->tremolo = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x80)!=0; } // change amount of self-feedback void change_feedback(Bitu chanbase, op_type* op_pt) { Bits feedback = adlibreg[ARC_FEEDBACK+chanbase]&14; if (feedback) op_pt->mfbi = (Bit32s)(pow(FL2,(fltype)((feedback>>1)+8))); else op_pt->mfbi = 0; } void change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt) { // frequency Bit32u frn = ((((Bit32u)adlibreg[ARC_KON_BNUM+chanbase])&3)<<8) + (Bit32u)adlibreg[ARC_FREQ_NUM+chanbase]; // block number/octave Bit32u oct = ((((Bit32u)adlibreg[ARC_KON_BNUM+chanbase])>>2)&7); op_pt->freq_high = (Bit32s)((frn>>7)&7); // keysplit Bit32u note_sel = (adlibreg[8]>>6)&1; op_pt->toff = ((frn>>9)&(note_sel^1)) | ((frn>>8)¬e_sel); op_pt->toff += (oct<<1); // envelope scaling (KSR) if (!(adlibreg[ARC_TVS_KSR_MUL+regbase]&0x10)) op_pt->toff >>= 2; // 20+a0+b0: op_pt->tinc = (Bit32u)((((fltype)(frn<>6]*kslev[oct][frn>>6]); op_pt->vol = (fltype)(pow(FL2,(fltype)(vol_in * -0.125 - 14))); // operator frequency changed, care about features that depend on it change_attackrate(regbase,op_pt); change_decayrate(regbase,op_pt); change_releaserate(regbase,op_pt); } void enable_operator(Bitu regbase, op_type* op_pt, Bit32u act_type) { // check if this is really an off-on transition if (op_pt->act_state == OP_ACT_OFF) { Bits wselbase = regbase; if (wselbase>=ARC_SECONDSET) wselbase -= (ARC_SECONDSET-22); // second set starts at 22 op_pt->tcount = wavestart[wave_sel[wselbase]]*FIXEDPT; // start with attack mode op_pt->op_state = OF_TYPE_ATT; op_pt->act_state |= act_type; } } void disable_operator(op_type* op_pt, Bit32u act_type) { // check if this is really an on-off transition if (op_pt->act_state != OP_ACT_OFF) { op_pt->act_state &= (~act_type); if (op_pt->act_state == OP_ACT_OFF) { if (op_pt->op_state != OF_TYPE_OFF) op_pt->op_state = OF_TYPE_REL; } } } void adlib_init(Bit32u samplerate) { Bits i, j, oct; int_samplerate = samplerate; generator_add = (Bit32u)(INTFREQU*FIXEDPT/int_samplerate); memset((void *)adlibreg,0,sizeof(adlibreg)); memset((void *)op,0,sizeof(op_type)*MAXOPERATORS); memset((void *)wave_sel,0,sizeof(wave_sel)); for (i=0;i=0;i--) { frqmul[i] = (fltype)(frqmul_tab[i]*INTFREQU/(fltype)WAVEPREC*(fltype)FIXEDPT*recipsamp); } status = 0; opl_index = 0; // create vibrato table vib_table[0] = 8; vib_table[1] = 4; vib_table[2] = 0; vib_table[3] = -4; for (i=4; i -0.5/6 to 0) for (i=14; i<41; i++) trem_table_int[i] = -i+14; // downwards (26 to 0 -> 0 to -1/6) for (i=41; i<53; i++) trem_table_int[i] = i-40-26; // upwards (1 to 12 -> -1/6 to -0.5/6) for (i=0; i>1);i++) { wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1) )*PI*2/WAVEPREC)); wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1)+1)*PI*2/WAVEPREC)); wavtable[i] = wavtable[(i<<1) +WAVEPREC]; // alternative: (zero-less) /* wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+1)*PI/WAVEPREC)); wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+3)*PI/WAVEPREC)); wavtable[i] = wavtable[(i<<1)-1+WAVEPREC]; */ } for (i=0;i<(WAVEPREC>>3);i++) { wavtable[i+(WAVEPREC<<1)] = wavtable[i+(WAVEPREC>>3)]-16384; wavtable[i+((WAVEPREC*17)>>3)] = wavtable[i+(WAVEPREC>>2)]+16384; } // key scale level table verified ([table in book]*8/3) kslev[7][0] = 0; kslev[7][1] = 24; kslev[7][2] = 32; kslev[7][3] = 37; kslev[7][4] = 40; kslev[7][5] = 43; kslev[7][6] = 45; kslev[7][7] = 47; kslev[7][8] = 48; for (i=9;i<16;i++) kslev[7][i] = (Bit8u)(i+41); for (j=6;j>=0;j--) { for (i=0;i<16;i++) { oct = (Bits)kslev[j+1][i]-8; if (oct < 0) oct = 0; kslev[j][i] = (Bit8u)oct; } } } } void adlib_write(Bitu idx, Bit8u val) { Bit32u second_set = idx&0x100; adlibreg[idx] = val; switch (idx&0xf0) { case ARC_CONTROL: // here we check for the second set registers, too: switch (idx) { case 0x02: // timer1 counter case 0x03: // timer2 counter break; case 0x04: // IRQ reset, timer mask/start if (val&0x80) { // clear IRQ bits in status register status &= ~0x60; } else { status = 0; } break; #if defined(OPLTYPE_IS_OPL3) case 0x04|ARC_SECONDSET: // 4op enable/disable switches for each possible channel op[0].is_4op = (val&1)>0; op[3].is_4op_attached = op[0].is_4op; op[1].is_4op = (val&2)>0; op[4].is_4op_attached = op[1].is_4op; op[2].is_4op = (val&4)>0; op[5].is_4op_attached = op[2].is_4op; op[18].is_4op = (val&8)>0; op[21].is_4op_attached = op[18].is_4op; op[19].is_4op = (val&16)>0; op[22].is_4op_attached = op[19].is_4op; op[20].is_4op = (val&32)>0; op[23].is_4op_attached = op[20].is_4op; break; case 0x05|ARC_SECONDSET: break; #endif case 0x08: // CSW, note select break; default: break; } break; case ARC_TVS_KSR_MUL: case ARC_TVS_KSR_MUL+0x10: { // tremolo/vibrato/sustain keeping enabled; key scale rate; frequency multiplication int num = idx&7; Bitu base = (idx-ARC_TVS_KSR_MUL)&0xff; if ((num<6) && (base<22)) { Bitu modop = regbase2modop[second_set?(base+22):base]; Bitu regbase = base+second_set; Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; // change tremolo/vibrato and sustain keeping of this operator op_type* op_ptr = &op[modop+((num<3) ? 0 : 9)]; change_keepsustain(regbase,op_ptr); change_vibrato(regbase,op_ptr); // change frequency calculations of this operator as // key scale rate and frequency multiplicator can be changed #if defined(OPLTYPE_IS_OPL3) if ((adlibreg[0x105]&1) && (op[modop].is_4op_attached)) { // operator uses frequency of channel change_frequency(chanbase-3,regbase,op_ptr); } else { change_frequency(chanbase,regbase,op_ptr); } #else change_frequency(chanbase,base,op_ptr); #endif } } break; case ARC_KSL_OUTLEV: case ARC_KSL_OUTLEV+0x10: { // key scale level; output rate int num = idx&7; Bitu base = (idx-ARC_KSL_OUTLEV)&0xff; if ((num<6) && (base<22)) { Bitu modop = regbase2modop[second_set?(base+22):base]; Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; // change frequency calculations of this operator as // key scale level and output rate can be changed op_type* op_ptr = &op[modop+((num<3) ? 0 : 9)]; #if defined(OPLTYPE_IS_OPL3) Bitu regbase = base+second_set; if ((adlibreg[0x105]&1) && (op[modop].is_4op_attached)) { // operator uses frequency of channel change_frequency(chanbase-3,regbase,op_ptr); } else { change_frequency(chanbase,regbase,op_ptr); } #else change_frequency(chanbase,base,op_ptr); #endif } } break; case ARC_ATTR_DECR: case ARC_ATTR_DECR+0x10: { // attack/decay rates int num = idx&7; Bitu base = (idx-ARC_ATTR_DECR)&0xff; if ((num<6) && (base<22)) { Bitu regbase = base+second_set; // change attack rate and decay rate of this operator op_type* op_ptr = &op[regbase2op[second_set?(base+22):base]]; change_attackrate(regbase,op_ptr); change_decayrate(regbase,op_ptr); } } break; case ARC_SUSL_RELR: case ARC_SUSL_RELR+0x10: { // sustain level; release rate int num = idx&7; Bitu base = (idx-ARC_SUSL_RELR)&0xff; if ((num<6) && (base<22)) { Bitu regbase = base+second_set; // change sustain level and release rate of this operator op_type* op_ptr = &op[regbase2op[second_set?(base+22):base]]; change_releaserate(regbase,op_ptr); change_sustainlevel(regbase,op_ptr); } } break; case ARC_FREQ_NUM: { // 0xa0-0xa8 low8 frequency Bitu base = (idx-ARC_FREQ_NUM)&0xff; if (base<9) { Bits opbase = second_set?(base+18):base; #if defined(OPLTYPE_IS_OPL3) if ((adlibreg[0x105]&1) && op[opbase].is_4op_attached) break; #endif // regbase of modulator: Bits modbase = modulatorbase[base]+second_set; Bitu chanbase = base+second_set; change_frequency(chanbase,modbase,&op[opbase]); change_frequency(chanbase,modbase+3,&op[opbase+9]); #if defined(OPLTYPE_IS_OPL3) // for 4op channels all four operators are modified to the frequency of the channel if ((adlibreg[0x105]&1) && op[second_set?(base+18):base].is_4op) { change_frequency(chanbase,modbase+8,&op[opbase+3]); change_frequency(chanbase,modbase+3+8,&op[opbase+3+9]); } #endif } } break; case ARC_KON_BNUM: { if (idx == ARC_PERC_MODE) { #if defined(OPLTYPE_IS_OPL3) if (second_set) return; #endif if ((val&0x30) == 0x30) { // BassDrum active enable_operator(16,&op[6],OP_ACT_PERC); change_frequency(6,16,&op[6]); enable_operator(16+3,&op[6+9],OP_ACT_PERC); change_frequency(6,16+3,&op[6+9]); } else { disable_operator(&op[6],OP_ACT_PERC); disable_operator(&op[6+9],OP_ACT_PERC); } if ((val&0x28) == 0x28) { // Snare active enable_operator(17+3,&op[16],OP_ACT_PERC); change_frequency(7,17+3,&op[16]); } else { disable_operator(&op[16],OP_ACT_PERC); } if ((val&0x24) == 0x24) { // TomTom active enable_operator(18,&op[8],OP_ACT_PERC); change_frequency(8,18,&op[8]); } else { disable_operator(&op[8],OP_ACT_PERC); } if ((val&0x22) == 0x22) { // Cymbal active enable_operator(18+3,&op[8+9],OP_ACT_PERC); change_frequency(8,18+3,&op[8+9]); } else { disable_operator(&op[8+9],OP_ACT_PERC); } if ((val&0x21) == 0x21) { // Hihat active enable_operator(17,&op[7],OP_ACT_PERC); change_frequency(7,17,&op[7]); } else { disable_operator(&op[7],OP_ACT_PERC); } break; } // regular 0xb0-0xb8 Bitu base = (idx-ARC_KON_BNUM)&0xff; if (base<9) { Bits opbase = second_set?(base+18):base; #if defined(OPLTYPE_IS_OPL3) if ((adlibreg[0x105]&1) && op[opbase].is_4op_attached) break; #endif // regbase of modulator: Bits modbase = modulatorbase[base]+second_set; if (val&32) { // operator switched on enable_operator(modbase,&op[opbase],OP_ACT_NORMAL); // modulator (if 2op) enable_operator(modbase+3,&op[opbase+9],OP_ACT_NORMAL); // carrier (if 2op) #if defined(OPLTYPE_IS_OPL3) // for 4op channels all four operators are switched on if ((adlibreg[0x105]&1) && op[opbase].is_4op) { // turn on chan+3 operators as well enable_operator(modbase+8,&op[opbase+3],OP_ACT_NORMAL); enable_operator(modbase+3+8,&op[opbase+3+9],OP_ACT_NORMAL); } #endif } else { // operator switched off disable_operator(&op[opbase],OP_ACT_NORMAL); disable_operator(&op[opbase+9],OP_ACT_NORMAL); #if defined(OPLTYPE_IS_OPL3) // for 4op channels all four operators are switched off if ((adlibreg[0x105]&1) && op[opbase].is_4op) { // turn off chan+3 operators as well disable_operator(&op[opbase+3],OP_ACT_NORMAL); disable_operator(&op[opbase+3+9],OP_ACT_NORMAL); } #endif } Bitu chanbase = base+second_set; // change frequency calculations of modulator and carrier (2op) as // the frequency of the channel has changed change_frequency(chanbase,modbase,&op[opbase]); change_frequency(chanbase,modbase+3,&op[opbase+9]); #if defined(OPLTYPE_IS_OPL3) // for 4op channels all four operators are modified to the frequency of the channel if ((adlibreg[0x105]&1) && op[second_set?(base+18):base].is_4op) { // change frequency calculations of chan+3 operators as well change_frequency(chanbase,modbase+8,&op[opbase+3]); change_frequency(chanbase,modbase+3+8,&op[opbase+3+9]); } #endif } } break; case ARC_FEEDBACK: { // 0xc0-0xc8 feedback/modulation type (AM/FM) Bitu base = (idx-ARC_FEEDBACK)&0xff; if (base<9) { Bits opbase = second_set?(base+18):base; Bitu chanbase = base+second_set; change_feedback(chanbase,&op[opbase]); #if defined(OPLTYPE_IS_OPL3) // OPL3 panning op[opbase].left_pan = ((val&0x10)>>4); op[opbase].right_pan = ((val&0x20)>>5); #endif } } break; case ARC_WAVE_SEL: case ARC_WAVE_SEL+0x10: { int num = idx&7; Bitu base = (idx-ARC_WAVE_SEL)&0xff; if ((num<6) && (base<22)) { #if defined(OPLTYPE_IS_OPL3) Bits wselbase = second_set?(base+22):base; // for easier mapping onto wave_sel[] // change waveform if (adlibreg[0x105]&1) wave_sel[wselbase] = val&7; // opl3 mode enabled, all waveforms accessible else wave_sel[wselbase] = val&3; op_type* op_ptr = &op[regbase2modop[wselbase]+((num<3) ? 0 : 9)]; change_waveform(wselbase,op_ptr); #else if (adlibreg[0x01]&0x20) { // wave selection enabled, change waveform wave_sel[base] = val&3; op_type* op_ptr = &op[regbase2modop[base]+((num<3) ? 0 : 9)]; change_waveform(base,op_ptr); } #endif } } break; default: break; } } Bitu adlib_reg_read(Bitu port) { #if defined(OPLTYPE_IS_OPL3) // opl3-detection routines require ret&6 to be zero if ((port&1)==0) { return status; } return 0x00; #else // opl2-detection routines require ret&6 to be 6 if ((port&1)==0) { return status|6; } return 0xff; #endif } void adlib_write_index(Bitu port, Bit8u val) { (void) port; opl_index = val; #if defined(OPLTYPE_IS_OPL3) if ((port&3)!=0) { // possibly second set if (((adlibreg[0x105]&1)!=0) || (opl_index==5)) opl_index |= ARC_SECONDSET; } #endif } OPL_INLINE static void clipit16(Bit32s ival, Bit16s* outval) { if (ival<32768) { if (ival>-32769) { *outval=(Bit16s)ival; } else { *outval = -32768; } } else { *outval = 32767; } } // be careful with this // uses cptr and chanval, outputs into outbufl(/outbufr) // for opl3 check if opl3-mode is enabled (which uses stereo panning) #undef CHANVAL_OUT #if defined(OPLTYPE_IS_OPL3) #define CHANVAL_OUT \ if (adlibreg[0x105]&1) { \ outbufl[i] += chanval*cptr[0].left_pan; \ outbufr[i] += chanval*cptr[0].right_pan; \ } else { \ outbufl[i] += chanval; \ } #else #define CHANVAL_OUT \ outbufl[i] += chanval; #endif void adlib_getsample(Bit16s* sndptr, Bits numsamples) { Bits i, endsamples; op_type* cptr; Bit32s outbufl[BLOCKBUF_SIZE]; #if defined(OPLTYPE_IS_OPL3) // second output buffer (right channel for opl3 stereo) Bit32s outbufr[BLOCKBUF_SIZE]; #endif // vibrato/tremolo lookup tables (global, to possibly be used by all operators) Bit32s vib_lut[BLOCKBUF_SIZE]; Bit32s trem_lut[BLOCKBUF_SIZE]; Bits samples_to_process = numsamples; for (Bits cursmp=0; cursmpBLOCKBUF_SIZE) endsamples = BLOCKBUF_SIZE; memset((void*)&outbufl,0,endsamples*sizeof(Bit32s)); #if defined(OPLTYPE_IS_OPL3) // clear second output buffer (opl3 stereo) if (adlibreg[0x105]&1) memset((void*)&outbufr,0,endsamples*sizeof(Bit32s)); #endif // calculate vibrato/tremolo lookup tables Bit32s vib_tshift = ((adlibreg[ARC_PERC_MODE]&0x40)==0) ? 1 : 0; // 14cents/7cents switching for (i=0;i=VIBTAB_SIZE) vibtab_pos-=VIBTAB_SIZE*FIXEDPT_LFO; vib_lut[i] = vib_table[vibtab_pos/FIXEDPT_LFO]>>vib_tshift; // 14cents (14/100 of a semitone) or 7cents // cycle through tremolo table tremtab_pos += tremtab_add; if (tremtab_pos/FIXEDPT_LFO>=TREMTAB_SIZE) tremtab_pos-=TREMTAB_SIZE*FIXEDPT_LFO; if (adlibreg[ARC_PERC_MODE]&0x80) trem_lut[i] = trem_table[tremtab_pos/FIXEDPT_LFO]; else trem_lut[i] = trem_table[TREMTAB_SIZE+tremtab_pos/FIXEDPT_LFO]; } if (adlibreg[ARC_PERC_MODE]&0x20) { //BassDrum cptr = &op[6]; if (adlibreg[ARC_FEEDBACK+6]&1) { // additive synthesis if (cptr[9].op_state != OF_TYPE_OFF) { if (cptr[9].vibrato) { vibval1 = vibval_var1; for (i=0;i=0; cur_ch--) { // skip drum/percussion operators if ((adlibreg[ARC_PERC_MODE]&0x20) && (cur_ch >= 6) && (cur_ch < 9)) continue; Bitu k = cur_ch; #if defined(OPLTYPE_IS_OPL3) if (cur_ch < 9) { cptr = &op[cur_ch]; } else { cptr = &op[cur_ch+9]; // second set is operator18-operator35 k += (-9+256); // second set uses registers 0x100 onwards } // check if this operator is part of a 4-op if ((adlibreg[0x105]&1) && cptr->is_4op_attached) continue; #else cptr = &op[cur_ch]; #endif // check for FM/AM if (adlibreg[ARC_FEEDBACK+k]&1) { #if defined(OPLTYPE_IS_OPL3) if ((adlibreg[0x105]&1) && cptr->is_4op) { if (adlibreg[ARC_FEEDBACK+k+3]&1) { // AM-AM-style synthesis (op1[fb] + (op2 * op3) + op4) if (cptr[0].op_state != OF_TYPE_OFF) { if (cptr[0].vibrato) { vibval1 = vibval_var1; for (i=0;iis_4op) { if (adlibreg[ARC_FEEDBACK+k+3]&1) { // FM-AM-style synthesis ((op1[fb] * op2) + (op3 * op4)) if ((cptr[0].op_state != OF_TYPE_OFF) || (cptr[9].op_state != OF_TYPE_OFF)) { if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) { vibval1 = vibval_var1; for (i=0;i typedef uintptr_t Bitu; typedef intptr_t Bits; typedef uint32_t Bit32u; typedef int32_t Bit32s; typedef uint16_t Bit16u; typedef int16_t Bit16s; typedef uint8_t Bit8u; typedef int8_t Bit8s; // general functions void adlib_init(Bit32u samplerate); void adlib_write(Bitu idx, Bit8u val); void adlib_getsample(Bit16s* sndptr, Bits numsamples); Bitu adlib_reg_read(Bitu port); void adlib_write_index(Bitu port, Bit8u val); #define opl_init() adlib_init(audioSampleRate) #define opl_write(reg, val) adlib_write(reg, val) #define opl_update(buf, num) adlib_getsample(buf, num) #endif /* OPL_H */ opentyrian-2.1.20221123/src/palette.c000066400000000000000000000120301432005211200170120ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "palette.h" #include "file.h" #include "nortsong.h" #include "opentyr.h" #include "video.h" #include static Uint32 rgb_to_yuv(int r, int g, int b); #define PALETTE_COUNT 23 Palette palettes[PALETTE_COUNT]; int palette_count; static Palette palette; Uint32 rgb_palette[256], yuv_palette[256]; Palette colors; void JE_loadPals(void) { FILE *f = dir_fopen_die(data_dir(), "palette.dat", "rb"); palette_count = ftell_eof(f) / (256 * 3); assert(palette_count == PALETTE_COUNT); for (int p = 0; p < palette_count; ++p) { for (int i = 0; i < 256; ++i) { // The VGA hardware palette used only 6 bits per component, so the values need to be rescaled to // 8 bits. The naive way to do this is to simply do (c << 2), padding it with 0's, however this // makes the maximum value 252 instead of the proper 255. A trick to fix this is to use the upper 2 // bits of the original value instead. This ensures that the value goes to 255 as the original goes // to 63. Uint8 rgb[3]; fread_u8_die(rgb, 3, f); palettes[p][i].r = (rgb[0] << 2) | (rgb[0] >> 4); palettes[p][i].g = (rgb[1] << 2) | (rgb[1] >> 4); palettes[p][i].b = (rgb[2] << 2) | (rgb[2] >> 4); } } fclose(f); } void set_palette(Palette colors, unsigned int first_color, unsigned int last_color) { for (uint i = first_color; i <= last_color; ++i) { palette[i] = colors[i]; rgb_palette[i] = SDL_MapRGB(main_window_tex_format, palette[i].r, palette[i].g, palette[i].b); yuv_palette[i] = rgb_to_yuv(palette[i].r, palette[i].g, palette[i].b); } } void set_colors(SDL_Color color, unsigned int first_color, unsigned int last_color) { for (uint i = first_color; i <= last_color; ++i) { palette[i] = color; rgb_palette[i] = SDL_MapRGB(main_window_tex_format, palette[i].r, palette[i].g, palette[i].b); yuv_palette[i] = rgb_to_yuv(palette[i].r, palette[i].g, palette[i].b); } } void init_step_fade_palette(int diff[256][3], Palette colors, unsigned int first_color, unsigned int last_color) { for (unsigned int i = first_color; i <= last_color; i++) { diff[i][0] = (int)colors[i].r - palette[i].r; diff[i][1] = (int)colors[i].g - palette[i].g; diff[i][2] = (int)colors[i].b - palette[i].b; } } void init_step_fade_solid(int diff[256][3], SDL_Color color, unsigned int first_color, unsigned int last_color) { for (unsigned int i = first_color; i <= last_color; i++) { diff[i][0] = (int)color.r - palette[i].r; diff[i][1] = (int)color.g - palette[i].g; diff[i][2] = (int)color.b - palette[i].b; } } void step_fade_palette(int diff[256][3], int steps, unsigned int first_color, unsigned int last_color) { assert(steps > 0); for (unsigned int i = first_color; i <= last_color; i++) { const int delta[3] = { diff[i][0] / steps, diff[i][1] / steps, diff[i][2] / steps }; diff[i][0] -= delta[0]; diff[i][1] -= delta[1]; diff[i][2] -= delta[2]; palette[i].r += delta[0]; palette[i].g += delta[1]; palette[i].b += delta[2]; rgb_palette[i] = SDL_MapRGB(main_window_tex_format, palette[i].r, palette[i].g, palette[i].b); yuv_palette[i] = rgb_to_yuv(palette[i].r, palette[i].g, palette[i].b); } } void fade_palette(Palette colors, int steps, unsigned int first_color, unsigned int last_color) { assert(steps > 0); static int diff[256][3]; init_step_fade_palette(diff, colors, first_color, last_color); for (; steps > 0; steps--) { setDelay(1); step_fade_palette(diff, steps, first_color, last_color); JE_showVGA(); service_wait_delay(); } } void fade_solid(SDL_Color color, int steps, unsigned int first_color, unsigned int last_color) { assert(steps > 0); static int diff[256][3]; init_step_fade_solid(diff, color, first_color, last_color); for (; steps > 0; steps--) { setDelay(1); step_fade_palette(diff, steps, first_color, last_color); JE_showVGA(); service_wait_delay(); } } void fade_black(int steps) { SDL_Color black = { 0, 0, 0 }; fade_solid(black, steps, 0, 255); } void fade_white(int steps) { SDL_Color white = { 255, 255, 255 }; fade_solid(white, steps, 0, 255); } static Uint32 rgb_to_yuv(int r, int g, int b) { int y = (r + g + b) >> 2, u = 128 + ((r - b) >> 2), v = 128 + ((-r + 2 * g - b) >> 3); return (y << 16) + (u << 8) + v; } opentyrian-2.1.20221123/src/palette.h000066400000000000000000000035661432005211200170350ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PALETTE_H #define PALETTE_H #include "opentyr.h" #include "SDL.h" typedef SDL_Color Palette[256]; extern Palette palettes[]; extern int palette_count; extern Uint32 rgb_palette[256], yuv_palette[256]; extern Palette colors; // TODO: get rid of this void JE_loadPals(void); void set_palette(Palette colors, unsigned int first_color, unsigned int last_color); void set_colors(SDL_Color color, unsigned int first_color, unsigned int last_color); void init_step_fade_palette(int diff[256][3], Palette colors, unsigned int first_color, unsigned int last_color); void init_step_fade_solid(int diff[256][3], SDL_Color color, unsigned int first_color, unsigned int last_color); void step_fade_palette(int diff[256][3], int steps, unsigned int first_color, unsigned int last_color); void fade_palette(Palette colors, int steps, unsigned int first_color, unsigned int last_color); void fade_solid(SDL_Color color, int steps, unsigned int first_color, unsigned int last_color); void fade_black(int steps); void fade_white(int steps); #endif /* PALETTE_H */ opentyrian-2.1.20221123/src/params.c000066400000000000000000000147161432005211200166540ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "params.h" #include "arg_parse.h" #include "file.h" #include "joystick.h" #include "loudness.h" #include "network.h" #include "opentyr.h" #include "varz.h" #include "xmas.h" #include #include #include #include #include JE_boolean richMode = false, constantPlay = false, constantDie = false; /* YKS: Note: LOOT cheat had non letters removed. */ const char pars[][9] = { "LOOT", "RECORD", "NOJOY", "CONSTANT", "DEATH", "NOSOUND", "NOXMAS", "YESXMAS" }; void JE_paramCheck(int argc, char *argv[]) { const Options options[] = { { 'h', 'h', "help", false }, { 's', 's', "no-sound", false }, { 'j', 'j', "no-joystick", false }, { 'x', 'x', "no-xmas", false }, { 't', 't', "data", true }, { 'n', 'n', "net", true }, { 256, 0, "net-player-name", true }, // TODO: no short codes because there should { 257, 0, "net-player-number", true }, // be a menu for entering these in the future { 'p', 'p', "net-port", true }, { 'd', 'd', "net-delay", true }, { 'X', 'X', "xmas", false }, { 'c', 'c', "constant", false }, { 'k', 'k', "death", false }, { 'r', 'r', "record", false }, { 'l', 'l', "loot", false }, { 0, 0, NULL, false} }; Option option; for (; ; ) { option = parse_args(argc, (const char **)argv, options); if (option.value == NOT_OPTION) break; switch (option.value) { case INVALID_OPTION: case AMBIGUOUS_OPTION: case OPTION_MISSING_ARG: fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); exit(EXIT_FAILURE); break; case 'h': printf("Usage: %s [OPTION...]\n\n" "Options:\n" " -h, --help Show help about options\n\n" " -s, --no-sound Disable audio\n" " -j, --no-joystick Disable joystick/gamepad input\n" " -x, --no-xmas Disable Christmas mode\n\n" " -t, --data=DIR Set Tyrian data directory\n\n" " -n, --net=HOST[:PORT] Start a networked game\n" " --net-player-name=NAME Sets local player name in a networked game\n" " --net-player-number=NUMBER Sets local player number in a networked game\n" " (1 or 2)\n" " -p, --net-port=PORT Local port to bind (default is 1333)\n" " -d, --net-delay=FRAMES Set lag-compensation delay (default is 1)\n", argv[0]); exit(0); break; case 's': // Disables sound/music usage audio_disabled = true; break; case 'j': // Disables joystick detection ignore_joystick = true; break; case 'x': xmas = false; break; // set custom Tyrian data directory case 't': custom_data_dir = option.arg; break; case 'n': isNetworkGame = true; intptr_t temp = (intptr_t)strchr(option.arg, ':'); if (temp) { temp -= (intptr_t)option.arg; int temp_port = atoi(&option.arg[temp + 1]); if (temp_port > 0 && temp_port < 49152) network_opponent_port = temp_port; else { fprintf(stderr, "%s: error: invalid network port number\n", argv[0]); exit(EXIT_FAILURE); } network_opponent_host = malloc(temp + 1); SDL_strlcpy(network_opponent_host, option.arg, temp + 1); } else { network_opponent_host = malloc(strlen(option.arg) + 1); strcpy(network_opponent_host, option.arg); } break; case 256: // --net-player-name network_player_name = malloc(strlen(option.arg) + 1); strcpy(network_player_name, option.arg); break; case 257: // --net-player-number { int temp = atoi(option.arg); if (temp >= 1 && temp <= 2) thisPlayerNum = temp; else { fprintf(stderr, "%s: error: invalid network player number\n", argv[0]); exit(EXIT_FAILURE); } break; } case 'p': { int temp = atoi(option.arg); if (temp > 0 && temp < 49152) network_player_port = temp; else { fprintf(stderr, "%s: error: invalid network port number\n", argv[0]); exit(EXIT_FAILURE); } break; } case 'd': { int temp; if (sscanf(option.arg, "%d", &temp) == 1) network_delay = 1 + temp; else { fprintf(stderr, "%s: error: invalid network delay value\n", argv[0]); exit(EXIT_FAILURE); } break; } case 'X': xmas = true; break; case 'c': /* Constant play for testing purposes (C key activates invincibility) This might be useful for publishers to see everything - especially those who can't play it */ constantPlay = true; break; case 'k': constantDie = true; break; case 'r': record_demo = true; break; case 'l': // Gives you mucho bucks richMode = true; break; default: assert(false); break; } } // legacy parameter support for (int i = option.argn; i < argc; ++i) { for (uint j = 0; j < strlen(argv[i]); ++j) argv[i][j] = toupper((unsigned char)argv[i][j]); for (uint j = 0; j < COUNTOF(pars); ++j) { if (strcmp(argv[i], pars[j]) == 0) { switch (j) { case 0: richMode = true; break; case 1: record_demo = true; break; case 2: ignore_joystick = true; break; case 3: constantPlay = true; break; case 4: constantDie = true; break; case 5: audio_disabled = true; break; case 6: xmas = false; break; case 7: xmas = true; break; default: assert(false); break; } } } } } opentyrian-2.1.20221123/src/params.h000066400000000000000000000020001432005211200166400ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PARAMS_H #define PARAMS_H #include "opentyr.h" extern JE_boolean richMode, constantPlay, constantDie; void JE_paramCheck(int argc, char *argv[]); #endif /* PARAMS_H */ opentyrian-2.1.20221123/src/pcxload.c000066400000000000000000000033511432005211200170140ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "pcxload.h" #include "file.h" #include "opentyr.h" #include "palette.h" #include "video.h" #include void JE_loadPCX(const char *file) // this is only meant to load tshp2.pcx { Uint8 *s = VGAScreen->pixels; /* 8-bit specific */ FILE *f = dir_fopen_die(data_dir(), file, "rb"); fseek(f, -769, SEEK_END); Uint8 temp; fread_u8_die(&temp, 1, f); if (temp == 12) { for (int i = 0; i < 256; i++) { Uint8 rgb[3]; fread_u8_die(rgb, 3, f); colors[i].r = rgb[0]; colors[i].g = rgb[1]; colors[i].b = rgb[2]; } } fseek(f, 128, SEEK_SET); for (int i = 0; i < 320 * 200; ) { Uint8 p; fread_u8_die(&p, 1, f); if ((p & 0xc0) == 0xc0) { i += (p & 0x3f); fread_u8_die(&temp, 1, f); memset(s, temp, (p & 0x3f)); s += (p & 0x3f); } else { i++; *s = p; s++; } if (i && (i % 320 == 0)) { s += VGAScreen->pitch - 320; } } fclose(f); } opentyrian-2.1.20221123/src/pcxload.h000066400000000000000000000017021432005211200170170ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PCXLOAD_H #define PCXLOAD_H #include "opentyr.h" void JE_loadPCX(const char *file); #endif /* PCXLOAD_H */ opentyrian-2.1.20221123/src/pcxmast.c000066400000000000000000000021261432005211200170400ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "pcxmast.h" #include "opentyr.h" const JE_byte pcxpal[PCX_NUM] = /* [1..PCXnum] */ { 0, 7, 5, 8, 10, 5, 18, 19, 19, 20, 21, 22, 5}; /*FACEMAX*/ const JE_byte facepal[12] = /* [1..12] */ { 1, 2, 3, 4, 6, 9, 11, 12, 16, 13, 14, 15}; JE_pcxpostype pcxpos; opentyrian-2.1.20221123/src/pcxmast.h000066400000000000000000000022071432005211200170450ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PCXMAST_H #define PCXMAST_H #include "opentyr.h" #define PCX_NUM 13 typedef JE_longint JE_pcxpostype[PCX_NUM + 1]; /* [1..PCXnum + 1] */ extern const JE_byte pcxpal[PCX_NUM]; /* [1..PCXnum] */ extern const JE_byte facepal[12]; /* [1..12] */ extern JE_pcxpostype pcxpos; #endif /* PCXMAST_H */ opentyrian-2.1.20221123/src/picload.c000066400000000000000000000037501432005211200170000ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "picload.h" #include "file.h" #include "opentyr.h" #include "palette.h" #include "pcxmast.h" #include "video.h" #include #include void JE_loadPic(SDL_Surface *screen, JE_byte PCXnumber, JE_boolean storepal) { PCXnumber--; FILE *f = dir_fopen_die(data_dir(), "tyrian.pic", "rb"); static bool first = true; if (first) { first = false; Uint16 temp; fread_u16_die(&temp, 1, f); fread_s32_die(pcxpos, PCX_NUM, f); pcxpos[PCX_NUM] = ftell_eof(f); } unsigned int size = pcxpos[PCXnumber + 1] - pcxpos[PCXnumber]; Uint8 *buffer = malloc(size); fseek(f, pcxpos[PCXnumber], SEEK_SET); fread_u8_die(buffer, size, f); fclose(f); Uint8 *p = buffer; Uint8 *s; /* screen pointer, 8-bit specific */ s = (Uint8 *)screen->pixels; for (int i = 0; i < 320 * 200; ) { if ((*p & 0xc0) == 0xc0) { i += (*p & 0x3f); memset(s, *(p + 1), (*p & 0x3f)); s += (*p & 0x3f); p += 2; } else { i++; *s = *p; s++; p++; } if (i && (i % 320 == 0)) { s += screen->pitch - 320; } } free(buffer); memcpy(colors, palettes[pcxpal[PCXnumber]], sizeof(colors)); if (storepal) set_palette(colors, 0, 255); } opentyrian-2.1.20221123/src/picload.h000066400000000000000000000017761432005211200170130ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PICLOAD_H #define PICLOAD_H #include "opentyr.h" #include "SDL.h" void JE_loadPic(SDL_Surface *screen, JE_byte PCXnumber, JE_boolean storepal); #endif /* PICLOAD_H */ opentyrian-2.1.20221123/src/player.c000066400000000000000000000034731432005211200166630ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "player.h" Player player[2]; void calc_purple_balls_needed(Player *this_player) { static const uint purple_balls_required[12] = { 1, 1, 2, 4, 8, 12, 16, 20, 25, 30, 40, 50 }; this_player->purple_balls_needed = purple_balls_required[*this_player->lives]; } bool power_up_weapon(Player *this_player, uint port) { const bool can_power_up = this_player->items.weapon[port].id != 0 && // not None this_player->items.weapon[port].power < 11; // not at max power if (can_power_up) { ++this_player->items.weapon[port].power; shotMultiPos[port] = 0; // TODO: should be part of Player structure calc_purple_balls_needed(this_player); } else // cash consolation prize { this_player->cash += 1000; } return can_power_up; } void handle_got_purple_ball(Player *this_player) { if (this_player->purple_balls_needed > 1) --this_player->purple_balls_needed; else power_up_weapon(this_player, this_player->is_dragonwing ? REAR_WEAPON : FRONT_WEAPON); } opentyrian-2.1.20221123/src/player.h000066400000000000000000000055251432005211200166700ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PLAYER_H #define PLAYER_H #include "config.h" #include "opentyr.h" enum { FRONT_WEAPON = 0, REAR_WEAPON = 1 }; enum { LEFT_SIDEKICK = 0, RIGHT_SIDEKICK = 1 }; typedef struct { Uint8 ship; Uint8 generator; Uint8 shield; struct { Uint8 id; Uint8 power; } weapon[2]; Uint8 sidekick[2]; Uint8 special; // Dragonwing only: // repeatedly collecting the same powerup gives a series of sidekick upgrades Uint8 sidekick_series; Uint8 sidekick_level; // Single-player only Uint8 super_arcade_mode; // stored as an item for compatibility :( } PlayerItems; typedef struct { ulong cash; PlayerItems items, last_items; bool is_dragonwing; // i.e., is player 2 Uint8 *lives; // calculatable uint shield_max; uint initial_armor; uint shot_hit_area_x, shot_hit_area_y; // state bool is_alive; uint invulnerable_ticks; // ticks until ship can be damaged uint exploding_ticks; // ticks until ship done exploding uint shield; uint armor; uint weapon_mode; uint superbombs; uint purple_balls_needed; int x, y; int old_x[20], old_y[20]; int x_velocity, y_velocity; uint x_friction_ticks, y_friction_ticks; // ticks until friction is applied int delta_x_shot_move, delta_y_shot_move; int last_x_shot_move, last_y_shot_move; int last_x_explosion_follow, last_y_explosion_follow; struct { // calculatable int ammo_max; uint ammo_refill_ticks_max; uint style; // affects movement and size // state int x, y; int ammo; uint ammo_refill_ticks; bool animation_enabled; uint animation_frame; uint charge; uint charge_ticks; } sidekick[2]; } Player; extern Player player[2]; static inline bool all_players_dead(void) { return (!player[0].is_alive && (!twoPlayerMode || !player[1].is_alive)); } static inline bool all_players_alive(void) { return (player[0].is_alive && (!twoPlayerMode || player[1].is_alive)); } void calc_purple_balls_needed(Player *); bool power_up_weapon(Player *, uint port); void handle_got_purple_ball(Player *); #endif // PLAYER_H opentyrian-2.1.20221123/src/shots.c000066400000000000000000000310521432005211200165210ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2013 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "shots.h" #include "player.h" #include "sprite.h" #include "video.h" #include "varz.h" // I'm pretty sure the last extra entry is never used. PlayerShotDataType playerShotData[MAX_PWEAPON + 1]; /* [1..MaxPWeapon+1] */ JE_byte shotAvail[MAX_PWEAPON]; /* [1..MaxPWeapon] */ /*0:Avail 1-255:Duration left*/ void simulate_player_shots(void) { /* Player Shot Images */ for (int z = 0; z < MAX_PWEAPON; z++) { if (shotAvail[z] != 0) { shotAvail[z]--; if (z != MAX_PWEAPON - 1) { PlayerShotDataType* shot = &playerShotData[z]; shot->shotXM += shot->shotXC; if (shot->shotXM <= 100) shot->shotX += shot->shotXM; shot->shotYM += shot->shotYC; shot->shotY += shot->shotYM; if (shot->shotYM > 100) { shot->shotY -= 120; shot->shotY += player[0].delta_y_shot_move; } if (shot->shotComplicated != 0) { shot->shotDevX += shot->shotDirX; shot->shotX += shot->shotDevX; if (abs(shot->shotDevX) == shot->shotCirSizeX) shot->shotDirX = -shot->shotDirX; shot->shotDevY += shot->shotDirY; shot->shotY += shot->shotDevY; if (abs(shot->shotDevY) == shot->shotCirSizeY) shot->shotDirY = -shot->shotDirY; /*Double Speed Circle Shots - add a second copy of above loop*/ } int tempShotX = shot->shotX; int tempShotY = shot->shotY; if (shot->shotX < 0 || shot->shotX > 140 || shot->shotY < 0 || shot->shotY > 170) { shotAvail[z] = 0; goto draw_player_shot_loop_end; } /* if (shot->shotTrail != 255) { if (shot->shotTrail == 98) { JE_setupExplosion(shot->shotX - shot->shotXM, shot->shotY - shot->shotYM, shot->shotTrail); } else { JE_setupExplosion(shot->shotX, shot->shotY, shot->shotTrail); } }*/ JE_word anim_frame = shot->shotGr + shot->shotAni; if (++shot->shotAni == shot->shotAniMax) shot->shotAni = 0; if (anim_frame < 60000) { if (anim_frame > 1000) anim_frame = anim_frame % 1000; if (anim_frame > 500) blit_sprite2(VGAScreen, tempShotX+1, tempShotY, spriteSheet12, anim_frame - 500); else blit_sprite2(VGAScreen, tempShotX+1, tempShotY, spriteSheet8, anim_frame); } } draw_player_shot_loop_end: ; } } } static const JE_word linkMultiGr[17] /* [0..16] */ = {77,221,183,301,1,282,164,202,58,201,163,281,39,300,182,220,77}; static const JE_word linkSonicGr[17] /* [0..16] */ = {85,242,131,303,47,284,150,223,66,224,149,283,9,302,130,243,85}; static const JE_word linkMult2Gr[17] /* [0..16] */ = {78,299,295,297,2,278,276,280,59,279,275,277,40,296,294,298,78}; void player_shot_set_direction(JE_integer shot_id, uint weapon_id, JE_real direction) { PlayerShotDataType* shot = &playerShotData[shot_id]; shot->shotXM = -roundf(sinf(direction) * shot->shotYM); shot->shotYM = -roundf(cosf(direction) * shot->shotYM); // Some weapons have sprites for each direction, use those. int rounded_dir; switch (weapon_id) { case 27: case 32: case 10: rounded_dir = roundf(direction * (16 / (2 * M_PI))); /*16 directions*/ shot->shotGr = linkMultiGr[rounded_dir]; break; case 28: case 33: case 11: rounded_dir = roundf(direction * (16 / (2 * M_PI))); /*16 directions*/ shot->shotGr = linkSonicGr[rounded_dir]; break; case 30: case 35: case 14: if (direction > M_PI_2 && direction < M_PI + M_PI_2) { shot->shotYC = 1; } break; case 38: case 22: rounded_dir = roundf(direction * (16 / (2 * M_PI))); /*16 directions*/ shot->shotGr = linkMult2Gr[rounded_dir]; break; } } bool player_shot_move_and_draw( int shot_id, bool* out_is_special, int* out_shotx, int* out_shoty, JE_integer* out_shot_damage, JE_byte* out_blast_filter, JE_byte* out_chain, JE_byte* out_playerNum, JE_word* out_special_radiusw, JE_word* out_special_radiush) { PlayerShotDataType* shot = &playerShotData[shot_id]; shotAvail[shot_id]--; if (shot_id != MAX_PWEAPON - 1) { shot->shotXM += shot->shotXC; shot->shotX += shot->shotXM; JE_integer tmp_shotXM = shot->shotXM; if (shot->shotXM > 100) { if (shot->shotXM == 101) { shot->shotX -= 101; shot->shotX += player[shot->playerNumber-1].delta_x_shot_move; shot->shotY += player[shot->playerNumber-1].delta_y_shot_move; } else { shot->shotX -= 120; shot->shotX += player[shot->playerNumber-1].delta_x_shot_move; } } shot->shotYM += shot->shotYC; shot->shotY += shot->shotYM; if (shot->shotYM > 100) { shot->shotY -= 120; shot->shotY += player[shot->playerNumber-1].delta_y_shot_move; } if (shot->shotComplicated != 0) { shot->shotDevX += shot->shotDirX; shot->shotX += shot->shotDevX; if (abs(shot->shotDevX) == shot->shotCirSizeX) shot->shotDirX = -shot->shotDirX; shot->shotDevY += shot->shotDirY; shot->shotY += shot->shotDevY; if (abs(shot->shotDevY) == shot->shotCirSizeY) shot->shotDirY = -shot->shotDirY; /*Double Speed Circle Shots - add a second copy of above loop*/ } *out_shotx = shot->shotX; *out_shoty = shot->shotY; if (shot->shotX < -34 || shot->shotX > 290 || shot->shotY < -15 || shot->shotY > 190) { shotAvail[shot_id] = 0; return false; } if (shot->shotTrail != 255) { if (shot->shotTrail == 98) JE_setupExplosion(shot->shotX - shot->shotXM, shot->shotY - shot->shotYM, 0, shot->shotTrail, false, false); else JE_setupExplosion(shot->shotX, shot->shotY, 0, shot->shotTrail, false, false); } if (shot->aimAtEnemy != 0) { if (--shot->aimDelay == 0) { shot->aimDelay = shot->aimDelayMax; if (enemyAvail[shot->aimAtEnemy - 1] != 1) { if (shot->shotX < enemy[shot->aimAtEnemy - 1].ex) shot->shotXM++; else shot->shotXM--; if (shot->shotY < enemy[shot->aimAtEnemy - 1].ey) shot->shotYM++; else shot->shotYM--; } else { if (shot->shotXM > 0) shot->shotXM++; else shot->shotXM--; } } } JE_word sprite_frame = shot->shotGr + shot->shotAni; if (++shot->shotAni == shot->shotAniMax) shot->shotAni = 0; *out_shot_damage = shot->shotDmg; *out_blast_filter = shot->shotBlastFilter; *out_chain = shot->chainReaction; *out_playerNum = shot->playerNumber; *out_is_special = sprite_frame > 60000; if (*out_is_special) { blit_sprite_blend(VGAScreen, *out_shotx+1, *out_shoty, OPTION_SHAPES, sprite_frame - 60001); *out_special_radiusw = sprite(OPTION_SHAPES, sprite_frame - 60001)->width / 2; *out_special_radiush = sprite(OPTION_SHAPES, sprite_frame - 60001)->height / 2; } else { if (sprite_frame > 1000) { JE_doSP(*out_shotx+1 + 6, *out_shoty + 6, 5, 3, (sprite_frame / 1000) << 4); sprite_frame = sprite_frame % 1000; } if (sprite_frame > 500) { if (background2 && *out_shoty + shadowYDist < 190 && tmp_shotXM < 100) blit_sprite2_darken(VGAScreen, *out_shotx+1, *out_shoty + shadowYDist, spriteSheet12, sprite_frame - 500); blit_sprite2(VGAScreen, *out_shotx+1, *out_shoty, spriteSheet12, sprite_frame - 500); } else { if (background2 && *out_shoty + shadowYDist < 190 && tmp_shotXM < 100) blit_sprite2_darken(VGAScreen, *out_shotx+1, *out_shoty + shadowYDist, spriteSheet8, sprite_frame); blit_sprite2(VGAScreen, *out_shotx+1, *out_shoty, spriteSheet8, sprite_frame); } } } return true; } JE_integer player_shot_create(JE_word portNum, uint bay_i, JE_word PX, JE_word PY, JE_word mouseX, JE_word mouseY, JE_word wpNum, JE_byte playerNum) { static const JE_byte soundChannel[11] /* [1..11] */ = {0, 2, 4, 4, 2, 2, 5, 5, 1, 4, 1}; // Bounds check if (portNum > PORT_NUM || wpNum <= 0 || wpNum > WEAP_NUM) return MAX_PWEAPON; const JE_WeaponType* weapon = &weapons[wpNum]; if (power < weaponPort[portNum].poweruse) return MAX_PWEAPON; power -= weaponPort[portNum].poweruse; if (weapon->sound > 0) soundQueue[soundChannel[bay_i]] = weapon->sound; int shot_id = MAX_PWEAPON; /*Rot*/ for (int multi_i = 1; multi_i <= weapon->multi; multi_i++) { for (shot_id = 0; shot_id < MAX_PWEAPON; shot_id++) if (shotAvail[shot_id] == 0) break; if (shot_id == MAX_PWEAPON) return MAX_PWEAPON; if (shotMultiPos[bay_i] == weapon->max || shotMultiPos[bay_i] > 8) shotMultiPos[bay_i] = 1; else shotMultiPos[bay_i]++; PlayerShotDataType* shot = &playerShotData[shot_id]; shot->chainReaction = 0; shot->playerNumber = playerNum; shot->shotAni = 0; shot->shotComplicated = weapon->circlesize != 0; if (weapon->circlesize == 0) { shot->shotDevX = 0; shot->shotDirX = 0; shot->shotDevY = 0; shot->shotDirY = 0; shot->shotCirSizeX = 0; shot->shotCirSizeY = 0; } else { JE_byte circsize = weapon->circlesize; if (circsize > 19) { JE_byte circsize_mod20 = circsize % 20; shot->shotCirSizeX = circsize_mod20; shot->shotDevX = circsize_mod20 >> 1; circsize = circsize / 20; shot->shotCirSizeY = circsize; shot->shotDevY = circsize >> 1; } else { shot->shotCirSizeX = circsize; shot->shotCirSizeY = circsize; shot->shotDevX = circsize >> 1; shot->shotDevY = circsize >> 1; } shot->shotDirX = 1; shot->shotDirY = -1; } shot->shotTrail = weapon->trail; if (weapon->attack[shotMultiPos[bay_i]-1] > 99 && weapon->attack[shotMultiPos[bay_i]-1] < 250) { shot->chainReaction = weapon->attack[shotMultiPos[bay_i]-1] - 100; shot->shotDmg = 1; } else { shot->shotDmg = weapon->attack[shotMultiPos[bay_i]-1]; } shot->shotBlastFilter = weapon->shipblastfilter; JE_integer tmp_by = weapon->by[shotMultiPos[bay_i]-1]; /*Note: Only front selection used for player shots...*/ shot->shotX = PX + weapon->bx[shotMultiPos[bay_i]-1]; shot->shotY = PY + tmp_by; shot->shotYC = -weapon->acceleration; shot->shotXC = weapon->accelerationx; shot->shotXM = weapon->sx[shotMultiPos[bay_i]-1]; // Not sure what this field does exactly. JE_byte del = weapon->del[shotMultiPos[bay_i]-1]; if (del == 121) { shot->shotTrail = 0; del = 255; } shot->shotGr = weapon->sg[shotMultiPos[bay_i]-1]; if (shot->shotGr == 0) shotAvail[shot_id] = 0; else shotAvail[shot_id] = del; if (del > 100 && del < 120) shot->shotAniMax = (del - 100 + 1); else shot->shotAniMax = weapon->weapani + 1; if (del == 99 || del == 98) { tmp_by = PX - mouseX; if (tmp_by < -5) tmp_by = -5; else if (tmp_by > 5) tmp_by = 5; shot->shotXM += tmp_by; } if (del == 99 || del == 100) { tmp_by = PY - mouseY - weapon->sy[shotMultiPos[bay_i]-1]; if (tmp_by < -4) tmp_by = -4; else if (tmp_by > 4) tmp_by = 4; shot->shotYM = tmp_by; } else if (weapon->sy[shotMultiPos[bay_i]-1] == 98) { shot->shotYM = 0; shot->shotYC = -1; } else if (weapon->sy[shotMultiPos[bay_i]-1] > 100) { shot->shotYM = weapon->sy[shotMultiPos[bay_i]-1]; shot->shotY -= player[shot->playerNumber-1].delta_y_shot_move; } else { shot->shotYM = -weapon->sy[shotMultiPos[bay_i]-1]; } if (weapon->sx[shotMultiPos[bay_i]-1] > 100) { shot->shotXM = weapon->sx[shotMultiPos[bay_i]-1]; shot->shotX -= player[shot->playerNumber-1].delta_x_shot_move; if (shot->shotXM == 101) shot->shotY -= player[shot->playerNumber-1].delta_y_shot_move; } if (weapon->aim > 5) /*Guided Shot*/ { uint best_dist = 65000; JE_byte closest_enemy = 0; /*Find Closest Enemy*/ for (x = 0; x < 100; x++) { if (enemyAvail[x] != 1 && !enemy[x].scoreitem) { y = abs(enemy[x].ex - shot->shotX) + abs(enemy[x].ey - shot->shotY); if (y < best_dist) { best_dist = y; closest_enemy = x + 1; } } } shot->aimAtEnemy = closest_enemy; shot->aimDelay = 5; shot->aimDelayMax = weapon->aim - 5; } else { shot->aimAtEnemy = 0; } shotRepeat[bay_i] = weapon->shotrepeat; } return shot_id; } opentyrian-2.1.20221123/src/shots.h000066400000000000000000000043201432005211200165240ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2013 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SHOTS_H #define SHOTS_H #include "opentyr.h" typedef struct { JE_integer shotX, shotY, shotXM, shotYM, shotXC, shotYC; JE_boolean shotComplicated; JE_integer shotDevX, shotDirX, shotDevY, shotDirY, shotCirSizeX, shotCirSizeY; JE_byte shotTrail; JE_word shotGr, shotAni, shotAniMax; Uint8 shotDmg; JE_byte shotBlastFilter, chainReaction, playerNumber, aimAtEnemy, aimDelay, aimDelayMax; } PlayerShotDataType; #define MAX_PWEAPON 81 /* 81*/ extern PlayerShotDataType playerShotData[MAX_PWEAPON + 1]; extern JE_byte shotAvail[MAX_PWEAPON]; /** Used in the shop to show weapon previews. */ void simulate_player_shots(void); /** Points shot movement in the specified direction. Used for the turret gun. */ void player_shot_set_direction(JE_integer shot_id, uint weapon_id, JE_real direction); /** Moves and draws a shot. Does \b not collide it with enemies. * \return False if the shot went off-screen, true otherwise. */ bool player_shot_move_and_draw( int shot_id, bool *out_is_special, int *out_shotx, int *out_shoty, JE_integer *out_shot_damage, JE_byte *out_blast_filter, JE_byte *out_chain, JE_byte *out_playerNum, JE_word *out_special_radiusw, JE_word *out_special_radiush); /** Creates a player shot. */ JE_integer player_shot_create( JE_word portnum, uint shot_i, JE_word px, JE_word py, JE_word mousex, JE_word mousey, JE_word wpnum, JE_byte playernum); #endif // SHOTS_H opentyrian-2.1.20221123/src/sizebuf.c000066400000000000000000000077211432005211200170360ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This file is largely based on (and named after) a set of common reading/ * writing functions used in Quake engines. Its purpose is to allow extraction * of bytes, words, and dwords in a safe, endian adjusted environment and should * probably be used in any situation where checking for buffer overflows * manually makes the code a godawful mess. * * Currently this is only used by the animation decoding. * * This file is written with the intention of being easily converted into a * class capable of throwing exceptions if data is out of range. * * If an operation fails, subsequent operations will also fail. The sizebuf * is assumed to be in an invalid state. This COULD be changed pretty easily * and in normal Quake IIRC it is. But our MO is to bail on failure, not * figure out what went wrong (making throws perfect). */ #include "sizebuf.h" #include "SDL_endian.h" #include #include #include /* Construct buffer with the passed array and size */ void SZ_Init(sizebuf_t * sz, Uint8 * buf, unsigned int size) { sz->data = buf; sz->bufferLen = size; sz->bufferPos = 0; sz->error = false; } /* Check error flags */ bool SZ_Error(sizebuf_t * sz) { return sz->error; } /* mimic memset */ void SZ_Memset(sizebuf_t * sz, int value, size_t count) { /* Do bounds checking before writing */ if (sz->error || sz->bufferPos + count > sz->bufferLen) { sz->error = true; return; } /* Memset and increment pointer */ memset(sz->data + sz->bufferPos, value, count); sz->bufferPos += count; } /* Mimic memcpy. */ void SZ_Memcpy2(sizebuf_t * sz, sizebuf_t * bf, size_t count) { /* State checking */ if (sz->error || sz->bufferPos + count > sz->bufferLen) { sz->error = true; return; } if (bf->error || bf->bufferPos + count > bf->bufferLen) { bf->error = true; return; } /* Memcpy & increment */ memcpy(sz->data + sz->bufferPos, bf->data + bf->bufferPos, count); sz->bufferPos += count; bf->bufferPos += count; } /* Reposition buffer pointer */ void SZ_Seek(sizebuf_t * sz, long count, int mode) { /* Okay, it's reasonable to reset the error bool on seeking... */ switch (mode) { case SEEK_SET: sz->bufferPos = count; break; case SEEK_CUR: sz->bufferPos += count; break; case SEEK_END: sz->bufferPos = sz->bufferLen - count; break; default: assert(false); } /* Check errors */ if (sz->bufferPos > sz->bufferLen) sz->error = true; else sz->error = false; } /* The code below makes use of pointer casts, similar to what is in efread. * It's not the ONLY way to write ints to a stream, but it's probably the * cleanest of the lot. Better to have it here than littered all over the code. */ unsigned int MSG_ReadByte(sizebuf_t * sz) { unsigned int ret; if (sz->error || sz->bufferPos + 1 > sz->bufferLen) { sz->error = true; return 0; } ret = sz->data[sz->bufferPos]; sz->bufferPos += 1; return ret; } unsigned int MSG_ReadWord(sizebuf_t * sz) { unsigned int ret; if (sz->error || sz->bufferPos + 2 > sz->bufferLen) { sz->error = true; return 0; } ret = SDL_SwapLE16(*((Uint16 *)(sz->data + sz->bufferPos))); sz->bufferPos += 2; return ret; } opentyrian-2.1.20221123/src/sizebuf.h000066400000000000000000000026271432005211200170430ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SIZEBUF_H #define SIZEBUF_H #include "opentyr.h" typedef struct sizebuf_s { Uint8 *data; unsigned int bufferLen; unsigned int bufferPos; bool error; } sizebuf_t; void SZ_Init(sizebuf_t *, Uint8 *, unsigned int); /* C style constructor */ bool SZ_Error(sizebuf_t *); void SZ_Memset(sizebuf_t *, int, size_t); /* memset with a sizebuf */ void SZ_Memcpy2(sizebuf_t *, sizebuf_t *, size_t); /* memcpy with a sizebuf */ void SZ_Seek(sizebuf_t *, long, int); /* fseek with a sizebuf. */ unsigned int MSG_ReadByte(sizebuf_t *); unsigned int MSG_ReadWord(sizebuf_t *); #endif opentyrian-2.1.20221123/src/sndmast.c000066400000000000000000000031501432005211200170300ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sndmast.h" #include "opentyr.h" const char soundTitle[SOUND_COUNT][9] = /* [1..soundnum + 9] of string [8] */ { "SCALEDN2", /*1*/ "F2", /*2*/ "TEMP10", "EXPLSM", "PASS3", /*5*/ "TEMP2", "BYPASS1", "EXP1RT", "EXPLLOW", "TEMP13", /*10*/ "EXPRETAP", "MT2BOOM", "TEMP3", "LAZB", /*28K*/ "LAZGUN2", /*15*/ "SPRING", "WARNING", "ITEM", "HIT2", /*14K*/ "MACHNGUN", /*20*/ "HYPERD2", "EXPLHUG", "CLINK1", "CLICK", "SCALEDN1", /*25*/ "TEMP11", "TEMP16", "SMALL1", "POWERUP", "VOICE1", "VOICE2", "VOICE3", "VOICE4", "VOICE5", "VOICE6", "VOICE7", "VOICE8", "VOICE9" }; const JE_byte windowTextSamples[9] = /* [1..9] */ { V_DANGER, V_BOSS, V_ENEMIES, V_CLEARED_PLATFORM, V_DANGER, V_SPIKES, V_ACCELERATE, V_DANGER, V_ENEMIES }; opentyrian-2.1.20221123/src/sndmast.h000066400000000000000000000045111432005211200170370ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SNDMAST_H #define SNDMAST_H #include "opentyr.h" #define SFX_COUNT 29 #define VOICE_COUNT 9 #define SOUND_COUNT (SFX_COUNT + VOICE_COUNT) enum { S_NONE = 0, S_WEAPON_1 = 1, S_WEAPON_2 = 2, S_ENEMY_HIT = 3, S_EXPLOSION_4 = 4, S_WEAPON_5 = 5, S_WEAPON_6 = 6, S_WEAPON_7 = 7, S_SELECT = 8, S_EXPLOSION_8 = 8, S_EXPLOSION_9 = 9, S_WEAPON_10 = 10, S_EXPLOSION_11 = 11, S_EXPLOSION_12 = 12, S_WEAPON_13 = 13, S_WEAPON_14 = 14, S_WEAPON_15 = 15, S_SPRING = 16, S_WARNING = 17, S_ITEM = 18, S_HULL_HIT = 19, S_MACHINE_GUN = 20, S_SOUL_OF_ZINGLON = 21, S_EXPLOSION_22 = 22, S_CLINK = 23, S_CLICK = 24, S_WEAPON_25 = 25, S_WEAPON_26 = 26, S_SHIELD_HIT = 27, S_CURSOR = 28, S_POWERUP = 29, V_CLEARED_PLATFORM = 30, // "Cleared enemy platform." V_BOSS = 31, // "Large enemy approaching." V_ENEMIES = 32, // "Enemies ahead." V_GOOD_LUCK = 33, // "Good luck." V_LEVEL_END = 34, // "Level completed." V_DANGER = 35, // "Danger." V_SPIKES = 36, // "Warning: spikes ahead." V_DATA_CUBE = 37, // "Data acquired." V_ACCELERATE = 38, // "Unexplained speed increase." }; extern const char soundTitle[SOUND_COUNT][9]; extern const JE_byte windowTextSamples[9]; #endif /* SNDMAST_H */ opentyrian-2.1.20221123/src/sprite.c000066400000000000000000000535151432005211200166770ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sprite.h" #include "file.h" #include "opentyr.h" #include "video.h" #include #include #include Sprite_array sprite_table[SPRITE_TABLES_MAX]; Sprite2_array shopSpriteSheet; Sprite2_array explosionSpriteSheet; Sprite2_array enemySpriteSheets[4]; Uint8 enemySpriteSheetIds[4]; Sprite2_array destructSpriteSheet; Sprite2_array spriteSheet8; Sprite2_array spriteSheet9; Sprite2_array spriteSheet10; Sprite2_array spriteSheet11; Sprite2_array spriteSheet12; void load_sprites_file(unsigned int table, const char *filename) { free_sprites(table); FILE *f = dir_fopen_die(data_dir(), filename, "rb"); load_sprites(table, f); fclose(f); } void load_sprites(unsigned int table, FILE *f) { free_sprites(table); Uint16 temp; fread_u16_die(&temp, 1, f); sprite_table[table].count = temp; assert(sprite_table[table].count <= SPRITES_PER_TABLE_MAX); for (unsigned int i = 0; i < sprite_table[table].count; ++i) { Sprite * const cur_sprite = sprite(table, i); bool populated; fread_bool_die(&populated, f); if (!populated) // sprite is empty continue; fread_u16_die(&cur_sprite->width, 1, f); fread_u16_die(&cur_sprite->height, 1, f); fread_u16_die(&cur_sprite->size, 1, f); cur_sprite->data = malloc(cur_sprite->size); fread_u8_die(cur_sprite->data, cur_sprite->size, f); } } void free_sprites(unsigned int table) { for (unsigned int i = 0; i < sprite_table[table].count; ++i) { Sprite * const cur_sprite = sprite(table, i); cur_sprite->width = 0; cur_sprite->height = 0; cur_sprite->size = 0; free(cur_sprite->data); cur_sprite->data = NULL; } sprite_table[table].count = 0; } // does not clip on left or right edges of surface void blit_sprite(SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index) { if (index >= sprite_table[table].count || !sprite_exists(table, index)) { assert(false); return; } const Sprite * const cur_sprite = sprite(table, index); const Uint8 *data = cur_sprite->data; const Uint8 * const data_ul = data + cur_sprite->size; const unsigned int width = cur_sprite->width; unsigned int x_offset = 0; assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (; data < data_ul; ++data) { switch (*data) { case 255: // transparent pixels data++; // next byte tells how many pixels += *data; x_offset += *data; break; case 254: // next pixel row pixels += width - x_offset; x_offset = width; break; case 253: // 1 transparent pixel pixels++; x_offset++; break; default: // set a pixel if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = *data; pixels++; x_offset++; break; } if (x_offset >= width) { pixels += surface->pitch - x_offset; x_offset = 0; } } } // does not clip on left or right edges of surface void blit_sprite_blend(SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index) { if (index >= sprite_table[table].count || !sprite_exists(table, index)) { assert(false); return; } const Sprite * const cur_sprite = sprite(table, index); const Uint8 *data = cur_sprite->data; const Uint8 * const data_ul = data + cur_sprite->size; const unsigned int width = cur_sprite->width; unsigned int x_offset = 0; assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (; data < data_ul; ++data) { switch (*data) { case 255: // transparent pixels data++; // next byte tells how many pixels += *data; x_offset += *data; break; case 254: // next pixel row pixels += width - x_offset; x_offset = width; break; case 253: // 1 transparent pixel pixels++; x_offset++; break; default: // set a pixel if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = (*data & 0xf0) | (((*pixels & 0x0f) + (*data & 0x0f)) / 2); pixels++; x_offset++; break; } if (x_offset >= width) { pixels += surface->pitch - x_offset; x_offset = 0; } } } // does not clip on left or right edges of surface // unsafe because it doesn't check that value won't overflow into hue // we can replace it when we know that we don't rely on that 'feature' void blit_sprite_hv_unsafe(SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value) { if (index >= sprite_table[table].count || !sprite_exists(table, index)) { assert(false); return; } hue <<= 4; const Sprite * const cur_sprite = sprite(table, index); const Uint8 *data = cur_sprite->data; const Uint8 * const data_ul = data + cur_sprite->size; const unsigned int width = cur_sprite->width; unsigned int x_offset = 0; assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (; data < data_ul; ++data) { switch (*data) { case 255: // transparent pixels data++; // next byte tells how many pixels += *data; x_offset += *data; break; case 254: // next pixel row pixels += width - x_offset; x_offset = width; break; case 253: // 1 transparent pixel pixels++; x_offset++; break; default: // set a pixel if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = hue | ((*data & 0x0f) + value); pixels++; x_offset++; break; } if (x_offset >= width) { pixels += surface->pitch - x_offset; x_offset = 0; } } } // does not clip on left or right edges of surface void blit_sprite_hv(SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value) { if (index >= sprite_table[table].count || !sprite_exists(table, index)) { assert(false); return; } hue <<= 4; const Sprite * const cur_sprite = sprite(table, index); const Uint8 *data = cur_sprite->data; const Uint8 * const data_ul = data + cur_sprite->size; const unsigned int width = cur_sprite->width; unsigned int x_offset = 0; assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (; data < data_ul; ++data) { switch (*data) { case 255: // transparent pixels data++; // next byte tells how many pixels += *data; x_offset += *data; break; case 254: // next pixel row pixels += width - x_offset; x_offset = width; break; case 253: // 1 transparent pixel pixels++; x_offset++; break; default: // set a pixel if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) { Uint8 temp_value = (*data & 0x0f) + value; if (temp_value > 0xf) temp_value = (temp_value >= 0x1f) ? 0x0 : 0xf; *pixels = hue | temp_value; } pixels++; x_offset++; break; } if (x_offset >= width) { pixels += surface->pitch - x_offset; x_offset = 0; } } } // does not clip on left or right edges of surface void blit_sprite_hv_blend(SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value) { if (index >= sprite_table[table].count || !sprite_exists(table, index)) { assert(false); return; } hue <<= 4; const Sprite * const cur_sprite = sprite(table, index); const Uint8 *data = cur_sprite->data; const Uint8 * const data_ul = data + cur_sprite->size; const unsigned int width = cur_sprite->width; unsigned int x_offset = 0; assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (; data < data_ul; ++data) { switch (*data) { case 255: // transparent pixels data++; // next byte tells how many pixels += *data; x_offset += *data; break; case 254: // next pixel row pixels += width - x_offset; x_offset = width; break; case 253: // 1 transparent pixel pixels++; x_offset++; break; default: // set a pixel if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) { Uint8 temp_value = (*data & 0x0f) + value; if (temp_value > 0xf) temp_value = (temp_value >= 0x1f) ? 0x0 : 0xf; *pixels = hue | (((*pixels & 0x0f) + temp_value) / 2); } pixels++; x_offset++; break; } if (x_offset >= width) { pixels += surface->pitch - x_offset; x_offset = 0; } } } // does not clip on left or right edges of surface void blit_sprite_dark(SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index, bool black) { if (index >= sprite_table[table].count || !sprite_exists(table, index)) { assert(false); return; } const Sprite * const cur_sprite = sprite(table, index); const Uint8 *data = cur_sprite->data; const Uint8 * const data_ul = data + cur_sprite->size; const unsigned int width = cur_sprite->width; unsigned int x_offset = 0; assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (; data < data_ul; ++data) { switch (*data) { case 255: // transparent pixels data++; // next byte tells how many pixels += *data; x_offset += *data; break; case 254: // next pixel row pixels += width - x_offset; x_offset = width; break; case 253: // 1 transparent pixel pixels++; x_offset++; break; default: // set a pixel if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = black ? 0x00 : ((*pixels & 0xf0) | ((*pixels & 0x0f) / 2)); pixels++; x_offset++; break; } if (x_offset >= width) { pixels += surface->pitch - x_offset; x_offset = 0; } } } void JE_loadCompShapes(Sprite2_array *sprite2s, char s) { free_sprite2s(sprite2s); char buffer[20]; snprintf(buffer, sizeof(buffer), "newsh%c.shp", tolower((unsigned char)s)); FILE *f = dir_fopen_die(data_dir(), buffer, "rb"); sprite2s->size = ftell_eof(f); JE_loadCompShapesB(sprite2s, f); fclose(f); } void JE_loadCompShapesB(Sprite2_array *sprite2s, FILE *f) { assert(sprite2s->data == NULL); sprite2s->data = malloc(sprite2s->size); fread_u8_die(sprite2s->data, sprite2s->size, f); } void free_sprite2s(Sprite2_array *sprite2s) { free(sprite2s->data); sprite2s->data = NULL; sprite2s->size = 0; } // does not clip on left or right edges of surface void blit_sprite2(SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index) { assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); for (; *data != 0x0f; ++data) { pixels += *data & 0x0f; // second nibble: transparent pixel count unsigned int count = (*data & 0xf0) >> 4; // first nibble: opaque pixel count if (count == 0) // move to next pixel row { pixels += VGAScreen->pitch - 12; } else { while (count--) { ++data; if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = *data; ++pixels; } } } } void blit_sprite2_clip(SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index) { assert(surface->format->BitsPerPixel == 8); const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); for (; *data != 0x0f; ++data) { if (y >= surface->h) return; Uint8 skip_count = *data & 0x0f; Uint8 fill_count = (*data >> 4) & 0x0f; x += skip_count; if (fill_count == 0) // move to next pixel row { y += 1; x -= 12; } else if (y >= 0) { Uint8 *const pixel_row = (Uint8 *)surface->pixels + (y * surface->pitch); do { ++data; if (x >= 0 && x < surface->pitch) pixel_row[x] = *data; x += 1; } while (--fill_count); } else { data += fill_count; x += fill_count; } } } // does not clip on left or right edges of surface void blit_sprite2_blend(SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index) { assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); for (; *data != 0x0f; ++data) { pixels += *data & 0x0f; // second nibble: transparent pixel count unsigned int count = (*data & 0xf0) >> 4; // first nibble: opaque pixel count if (count == 0) // move to next pixel row { pixels += VGAScreen->pitch - 12; } else { while (count--) { ++data; if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = (((*data & 0x0f) + (*pixels & 0x0f)) / 2) | (*data & 0xf0); ++pixels; } } } } // does not clip on left or right edges of surface void blit_sprite2_darken(SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index) { assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); for (; *data != 0x0f; ++data) { pixels += *data & 0x0f; // second nibble: transparent pixel count unsigned int count = (*data & 0xf0) >> 4; // first nibble: opaque pixel count if (count == 0) // move to next pixel row { pixels += VGAScreen->pitch - 12; } else { while (count--) { ++data; if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = ((*pixels & 0x0f) / 2) + (*pixels & 0xf0); ++pixels; } } } } // does not clip on left or right edges of surface void blit_sprite2_filter(SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index, Uint8 filter) { assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); for (; *data != 0x0f; ++data) { pixels += *data & 0x0f; // second nibble: transparent pixel count unsigned int count = (*data & 0xf0) >> 4; // first nibble: opaque pixel count if (count == 0) // move to next pixel row { pixels += VGAScreen->pitch - 12; } else { while (count--) { ++data; if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = filter | (*data & 0x0f); ++pixels; } } } } void blit_sprite2_filter_clip(SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index, Uint8 filter) { assert(surface->format->BitsPerPixel == 8); const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); for (; *data != 0x0f; ++data) { if (y >= surface->h) return; Uint8 skip_count = *data & 0x0f; Uint8 fill_count = (*data >> 4) & 0x0f; x += skip_count; if (fill_count == 0) // move to next pixel row { y += 1; x -= 12; } else if (y >= 0) { Uint8 *const pixel_row = (Uint8 *)surface->pixels + (y * surface->pitch); do { ++data; if (x >= 0 && x < surface->pitch) pixel_row[x] = filter | (*data & 0x0f);; x += 1; } while (--fill_count); } else { data += fill_count; x += fill_count; } } } // does not clip on left or right edges of surface void blit_sprite2x2(SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index) { blit_sprite2(surface, x, y, sprite2s, index); blit_sprite2(surface, x + 12, y, sprite2s, index + 1); blit_sprite2(surface, x, y + 14, sprite2s, index + 19); blit_sprite2(surface, x + 12, y + 14, sprite2s, index + 20); } void blit_sprite2x2_clip(SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index) { blit_sprite2_clip(surface, x, y, sprite2s, index); blit_sprite2_clip(surface, x + 12, y, sprite2s, index + 1); blit_sprite2_clip(surface, x, y + 14, sprite2s, index + 19); blit_sprite2_clip(surface, x + 12, y + 14, sprite2s, index + 20); } // does not clip on left or right edges of surface void blit_sprite2x2_blend(SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index) { blit_sprite2_blend(surface, x, y, sprite2s, index); blit_sprite2_blend(surface, x + 12, y, sprite2s, index + 1); blit_sprite2_blend(surface, x, y + 14, sprite2s, index + 19); blit_sprite2_blend(surface, x + 12, y + 14, sprite2s, index + 20); } // does not clip on left or right edges of surface void blit_sprite2x2_darken(SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index) { blit_sprite2_darken(surface, x, y, sprite2s, index); blit_sprite2_darken(surface, x + 12, y, sprite2s, index + 1); blit_sprite2_darken(surface, x, y + 14, sprite2s, index + 19); blit_sprite2_darken(surface, x + 12, y + 14, sprite2s, index + 20); } // does not clip on left or right edges of surface void blit_sprite2x2_filter(SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index, Uint8 filter) { blit_sprite2_filter(surface, x, y, sprite2s, index, filter); blit_sprite2_filter(surface, x + 12, y, sprite2s, index + 1, filter); blit_sprite2_filter(surface, x, y + 14, sprite2s, index + 19, filter); blit_sprite2_filter(surface, x + 12, y + 14, sprite2s, index + 20, filter); } void blit_sprite2x2_filter_clip(SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index, Uint8 filter) { blit_sprite2_filter_clip(surface, x, y, sprite2s, index, filter); blit_sprite2_filter_clip(surface, x + 12, y, sprite2s, index + 1, filter); blit_sprite2_filter_clip(surface, x, y + 14, sprite2s, index + 19, filter); blit_sprite2_filter_clip(surface, x + 12, y + 14, sprite2s, index + 20, filter); } void JE_loadMainShapeTables(const char *shpfile) { enum { SHP_NUM = 12 }; FILE *f = dir_fopen_die(data_dir(), shpfile, "rb"); JE_word shpNumb; JE_longint shpPos[SHP_NUM + 1]; // +1 for storing file length fread_u16_die(&shpNumb, 1, f); assert(shpNumb + 1u == COUNTOF(shpPos)); fread_s32_die(shpPos, shpNumb, f); fseek(f, 0, SEEK_END); for (unsigned int i = shpNumb; i < COUNTOF(shpPos); ++i) shpPos[i] = ftell(f); int i; // fonts, interface, option sprites for (i = 0; i < 7; i++) { fseek(f, shpPos[i], SEEK_SET); load_sprites(i, f); } // player shot sprites spriteSheet8.size = shpPos[i + 1] - shpPos[i]; JE_loadCompShapesB(&spriteSheet8, f); i++; // player ship sprites spriteSheet9.size = shpPos[i + 1] - shpPos[i]; JE_loadCompShapesB(&spriteSheet9 , f); i++; // power-up sprites spriteSheet10.size = shpPos[i + 1] - shpPos[i]; JE_loadCompShapesB(&spriteSheet10, f); i++; // coins, datacubes, etc sprites spriteSheet11.size = shpPos[i + 1] - shpPos[i]; JE_loadCompShapesB(&spriteSheet11, f); i++; // more player shot sprites spriteSheet12.size = shpPos[i + 1] - shpPos[i]; JE_loadCompShapesB(&spriteSheet12, f); fclose(f); } void free_main_shape_tables(void) { for (uint i = 0; i < COUNTOF(sprite_table); ++i) free_sprites(i); free_sprite2s(&spriteSheet8); free_sprite2s(&spriteSheet9); free_sprite2s(&spriteSheet10); free_sprite2s(&spriteSheet11); free_sprite2s(&spriteSheet12); } opentyrian-2.1.20221123/src/sprite.h000066400000000000000000000125131432005211200166750ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SPRITE_H #define SPRITE_H #include "opentyr.h" #include "SDL.h" #include #include #define FONT_SHAPES 0 #define SMALL_FONT_SHAPES 1 #define TINY_FONT 2 #define PLANET_SHAPES 3 #define FACE_SHAPES 4 #define OPTION_SHAPES 5 /*Also contains help shapes*/ #define WEAPON_SHAPES 6 #define EXTRA_SHAPES 7 /*Used for Ending pics*/ #define SPRITE_TABLES_MAX 8 #define SPRITES_PER_TABLE_MAX 151 typedef struct { Uint16 width, height; Uint16 size; Uint8 *data; } Sprite; typedef struct { unsigned int count; Sprite sprite[SPRITES_PER_TABLE_MAX]; } Sprite_array; extern Sprite_array sprite_table[SPRITE_TABLES_MAX]; // fka shapearray, shapex, shapey, shapesize, shapexist, maxshape static inline Sprite *sprite(unsigned int table, unsigned int index) { assert(table < COUNTOF(sprite_table)); assert(index < COUNTOF(sprite_table->sprite)); return &sprite_table[table].sprite[index]; } static inline bool sprite_exists(unsigned int table, unsigned int index) { return (sprite(table, index)->data != NULL); } static inline Uint16 get_sprite_width(unsigned int table, unsigned int index) { return (sprite_exists(table, index) ? sprite(table, index)->width : 0); } static inline Uint16 get_sprite_height(unsigned int table, unsigned int index) { return (sprite_exists(table, index) ? sprite(table, index)->height : 0); } void load_sprites_file(unsigned int table, const char *filename); void load_sprites(unsigned int table, FILE *f); void free_sprites(unsigned int table); void blit_sprite(SDL_Surface *, int x, int y, unsigned int table, unsigned int index); // JE_newDrawCShapeNum void blit_sprite_blend(SDL_Surface *, int x, int y, unsigned int table, unsigned int index); // JE_newDrawCShapeTrick void blit_sprite_hv_unsafe(SDL_Surface *, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value); // JE_newDrawCShapeBright void blit_sprite_hv(SDL_Surface *, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value); // JE_newDrawCShapeAdjust void blit_sprite_hv_blend(SDL_Surface *, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value); // JE_newDrawCShapeModify void blit_sprite_dark(SDL_Surface *, int x, int y, unsigned int table, unsigned int index, bool black); // JE_newDrawCShapeDarken, JE_newDrawCShapeShadow typedef struct { size_t size; Uint8 *data; } Sprite2_array; // Shop icons and arrows sprite sheet. extern Sprite2_array shopSpriteSheet; // fka shapes6 // Explosions sprite sheet. extern Sprite2_array explosionSpriteSheet; // fka shapes6 // Enemy sprite sheet banks. extern Sprite2_array enemySpriteSheets[4]; // fka eShapes1, eShapes2, eShapes3, eShapes4 extern Uint8 enemySpriteSheetIds[4]; // fka enemyShapeTables // Destruct sprite sheet. extern Sprite2_array destructSpriteSheet; // fka shapes6 // Static sprite sheets. Player shots, player ships, power-ups, coins, etc. extern Sprite2_array spriteSheet8; // fka shapesC1 extern Sprite2_array spriteSheet9; // fka shapes9 extern Sprite2_array spriteSheet10; // fka eShapes6 extern Sprite2_array spriteSheet11; // fka eShapes5 extern Sprite2_array spriteSheet12; // fka shapesW2 void JE_loadCompShapes(Sprite2_array *, char s); void JE_loadCompShapesB(Sprite2_array *, FILE *f); void free_sprite2s(Sprite2_array *); void blit_sprite2(SDL_Surface *, int x, int y, Sprite2_array, unsigned int index); void blit_sprite2_clip(SDL_Surface *, int x, int y, Sprite2_array, unsigned int index); void blit_sprite2_blend(SDL_Surface *, int x, int y, Sprite2_array, unsigned int index); void blit_sprite2_darken(SDL_Surface *, int x, int y, Sprite2_array, unsigned int index); void blit_sprite2_filter(SDL_Surface *, int x, int y, Sprite2_array, unsigned int index, Uint8 filter); void blit_sprite2_filter_clip(SDL_Surface *, int x, int y, Sprite2_array, unsigned int index, Uint8 filter); void blit_sprite2x2(SDL_Surface *, int x, int y, Sprite2_array, unsigned int index); void blit_sprite2x2_clip(SDL_Surface *, int x, int y, Sprite2_array, unsigned int index); void blit_sprite2x2_blend(SDL_Surface *, int x, int y, Sprite2_array, unsigned int index); void blit_sprite2x2_darken(SDL_Surface *, int x, int y, Sprite2_array, unsigned int index); void blit_sprite2x2_filter(SDL_Surface *, int x, int y, Sprite2_array, unsigned int index, Uint8 filter); void blit_sprite2x2_filter_clip(SDL_Surface *, int x, int y, Sprite2_array, unsigned int index, Uint8 filter); void JE_loadMainShapeTables(const char *shpfile); void free_main_shape_tables(void); #endif // SPRITE_H opentyrian-2.1.20221123/src/starlib.c000066400000000000000000000250331432005211200170230ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "starlib.h" #include "keyboard.h" #include "mtrand.h" #include "opentyr.h" #include "video.h" #include #define starlib_MAX_STARS 1000 #define MAX_TYPES 14 struct JE_StarType { JE_integer spX, spY, spZ; JE_integer lastX, lastY; }; static int tempX, tempY; static JE_boolean run; static struct JE_StarType star[starlib_MAX_STARS]; static JE_byte setup; static JE_word stepCounter; static JE_word nsp2; static JE_shortint nspVar2Inc; /* JE: new sprite pointer */ static JE_real nsp; static JE_real nspVarInc; static JE_real nspVarVarInc; static JE_word changeTime; static JE_boolean doChange; static JE_boolean grayB; static JE_integer starlib_speed; static JE_shortint speedChange; static JE_byte pColor; void JE_starlib_main(void) { int off; JE_word i; JE_integer tempZ; JE_byte tempCol; struct JE_StarType *stars; Uint8 *surf; JE_wackyCol(); grayB = false; starlib_speed += speedChange; for (stars = star, i = starlib_MAX_STARS; i > 0; stars++, i--) { /* Make a pointer to the screen... */ surf = VGAScreen->pixels; /* Calculate the offset to where we wish to draw */ off = (stars->lastX)+(stars->lastY)*320; /* We don't want trails in our star field. Erase the old graphic */ if (off >= 640 && off < (320*200)-640) { surf[off] = 0; /* Shade Level 0 */ surf[off-1] = 0; /* Shade Level 1, 2 */ surf[off+1] = 0; surf[off-2] = 0; surf[off+2] = 0; surf[off-320] = 0; surf[off+320] = 0; surf[off-640] = 0; surf[off+640] = 0; } /* Move star */ tempZ = stars->spZ; tempX = (stars->spX / tempZ) + 160; tempY = (stars->spY / tempZ) + 100; tempZ -= starlib_speed; /* If star is out of range, make a new one */ if (tempZ <= 0 || tempY == 0 || tempY > 198 || tempX > 318 || tempX < 1) { stars->spZ = 500; JE_newStar(); stars->spX = tempX; stars->spY = tempY; } else /* Otherwise, update & draw it */ { stars->lastX = tempX; stars->lastY = tempY; stars->spZ = tempZ; off = tempX+tempY*320; if (grayB) tempCol = tempZ >> 1; else tempCol = pColor+((tempZ >> 4) & 31); /* Draw the pixel! */ if (off >= 640 && off < (320*200)-640) { surf[off] = tempCol; tempCol += 72; surf[off-1] = tempCol; surf[off+1] = tempCol; surf[off-320] = tempCol; surf[off+320] = tempCol; tempCol += 72; surf[off-2] = tempCol; surf[off+2] = tempCol; surf[off-640] = tempCol; surf[off+640] = tempCol; } } } if (newkey) { char key = 0; if ((lastkey_mod & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_GUI)) == KMOD_NONE) { switch (lastkey_scan) { case SDL_SCANCODE_C: key = 'c'; break; case SDL_SCANCODE_P: key = 'p'; break; case SDL_SCANCODE_S: key = 's'; break; case SDL_SCANCODE_X: key = 'x'; break; case SDL_SCANCODE_1: key = '1'; break; case SDL_SCANCODE_2: key = '2'; break; case SDL_SCANCODE_3: key = '3'; break; case SDL_SCANCODE_4: key = '4'; break; case SDL_SCANCODE_5: key = '5'; break; case SDL_SCANCODE_6: key = '6'; break; case SDL_SCANCODE_7: key = '7'; break; case SDL_SCANCODE_8: key = '8'; break; case SDL_SCANCODE_9: key = '9'; break; case SDL_SCANCODE_0: key = '0'; break; case SDL_SCANCODE_ESCAPE: key = 27; break; case SDL_SCANCODE_MINUS: key = '-'; break; case SDL_SCANCODE_LEFTBRACKET: key = '['; break; case SDL_SCANCODE_RIGHTBRACKET: key = ']'; break; case SDL_SCANCODE_GRAVE: key = '`'; break; case SDL_SCANCODE_KP_MINUS: key = '-'; break; case SDL_SCANCODE_KP_PLUS: key = '+'; break; case SDL_SCANCODE_KP_1: key = '1'; break; case SDL_SCANCODE_KP_2: key = '2'; break; case SDL_SCANCODE_KP_3: key = '3'; break; case SDL_SCANCODE_KP_4: key = '4'; break; case SDL_SCANCODE_KP_5: key = '5'; break; case SDL_SCANCODE_KP_6: key = '6'; break; case SDL_SCANCODE_KP_7: key = '7'; break; case SDL_SCANCODE_KP_8: key = '8'; break; case SDL_SCANCODE_KP_9: key = '9'; break; case SDL_SCANCODE_KP_0: key = '0'; break; default: break; } } else if ((lastkey_mod & KMOD_SHIFT) != KMOD_NONE && (lastkey_mod & (KMOD_CTRL | KMOD_ALT | KMOD_GUI)) == KMOD_NONE) { switch (lastkey_scan) { case SDL_SCANCODE_C: key = 'C'; break; case SDL_SCANCODE_P: key = 'P'; break; case SDL_SCANCODE_S: key = 'S'; break; case SDL_SCANCODE_X: key = 'X'; break; case SDL_SCANCODE_1: key = '!'; break; case SDL_SCANCODE_2: key = '@'; break; case SDL_SCANCODE_3: key = '#'; break; case SDL_SCANCODE_4: key = '$'; break; case SDL_SCANCODE_EQUALS: key = '+'; break; case SDL_SCANCODE_LEFTBRACKET: key = '{'; break; case SDL_SCANCODE_RIGHTBRACKET: key = '}'; break; default: break; } } switch (toupper(key)) { case '+': starlib_speed++; speedChange = 0; break; case '-': starlib_speed--; speedChange = 0; break; case '1': JE_changeSetup(1); break; case '2': JE_changeSetup(2); break; case '3': JE_changeSetup(3); break; case '4': JE_changeSetup(4); break; case '5': JE_changeSetup(5); break; case '6': JE_changeSetup(6); break; case '7': JE_changeSetup(7); break; case '8': JE_changeSetup(8); break; case '9': JE_changeSetup(9); break; case '0': JE_changeSetup(10); break; case '!': JE_changeSetup(11); break; case '@': JE_changeSetup(12); break; case '#': JE_changeSetup(13); break; case '$': JE_changeSetup(14); break; case 'C': JE_resetValues(); break; case 'S': nspVarVarInc = mt_rand_1() * 0.01f - 0.005f; break; case 'X': case 27: run = false; break; case '[': pColor--; break; case ']': pColor++; break; case '{': pColor -= 72; break; case '}': pColor += 72; break; case '`': doChange = !doChange; break; case 'P': wait_noinput(true, false, false); wait_input(true, false, false); break; default: break; } } if (doChange) { stepCounter++; if (stepCounter > changeTime) { JE_changeSetup(0); } } if ((mt_rand() % 1000) == 1) { nspVarVarInc = mt_rand_1() * 0.01f - 0.005f; } nspVarInc += nspVarVarInc; } void JE_wackyCol(void) { /* YKS: Does nothing */ } void JE_starlib_init(void) { static JE_boolean initialized = false; if (!initialized) { initialized = true; JE_resetValues(); JE_changeSetup(2); doChange = true; /* RANDOMIZE; */ for (int x = 0; x < starlib_MAX_STARS; x++) { star[x].spX = (mt_rand() % 64000) - 32000; star[x].spY = (mt_rand() % 40000) - 20000; star[x].spZ = x+1; } } } void JE_resetValues(void) { nsp2 = 1; nspVar2Inc = 1; nspVarInc = 0.1f; nspVarVarInc = 0.0001f; nsp = 0; pColor = 32; starlib_speed = 2; speedChange = 0; } void JE_changeSetup(JE_byte setupType) { stepCounter = 0; changeTime = (mt_rand() % 1000); if (setupType > 0) setup = setupType; else setup = mt_rand() % (MAX_TYPES + 1); if (setup == 1) nspVarInc = 0.1f; if (nspVarInc > 2.2f) nspVarInc = 0.1f; } void JE_newStar(void) { if (setup == 0) { tempX = (mt_rand() % 64000) - 32000; tempY = (mt_rand() % 40000) - 20000; } else { nsp = nsp + nspVarInc; /* YKS: < lol */ switch (setup) { case 1: tempX = (int)(sinf(nsp / 30) * 20000); tempY = (mt_rand() % 40000) - 20000; break; case 2: tempX = (int)(cosf(nsp) * 20000); tempY = (int)(sinf(nsp) * 20000); break; case 3: tempX = (int)(cosf(nsp * 15) * 100) * ((int)(nsp / 6) % 200); tempY = (int)(sinf(nsp * 15) * 100) * ((int)(nsp / 6) % 200); break; case 4: tempX = (int)(sinf(nsp / 60) * 20000); tempY = (int)(cosf(nsp) * (int)(sinf(nsp / 200) * 300) * 100); break; case 5: tempX = (int)(sinf(nsp / 2) * 20000); tempY = (int)(cosf(nsp) * (int)(sinf(nsp / 200) * 300) * 100); break; case 6: tempX = (int)(sinf(nsp) * 40000); tempY = (int)(cosf(nsp) * 20000); break; case 8: tempX = (int)(sinf(nsp / 2) * 40000); tempY = (int)(cosf(nsp) * 20000); break; case 7: tempX = mt_rand() % 65535; if ((mt_rand() % 2) == 0) tempY = (int)(cosf(nsp / 80) * 10000) + 15000; else tempY = 50000 - (int)(cosf(nsp / 80) * 13000); break; case 9: nsp2 += nspVar2Inc; if ((nsp2 == 65535) || (nsp2 == 0)) nspVar2Inc = -nspVar2Inc; tempX = (int)(cosf(sinf(nsp2 / 10.0f) + (nsp / 500)) * 32000); tempY = (int)(sinf(cosf(nsp2 / 10.0f) + (nsp / 500)) * 30000); break; case 10: nsp2 += nspVar2Inc; if ((nsp2 == 65535) || (nsp2 == 0)) nspVar2Inc = -nspVar2Inc; tempX = (int)(cosf(sinf(nsp2 / 5.0f) + (nsp / 100)) * 32000); tempY = (int)(sinf(cosf(nsp2 / 5.0f) + (nsp / 100)) * 30000); break;; case 11: nsp2 += nspVar2Inc; if ((nsp2 == 65535) || (nsp2 == 0)) nspVar2Inc = -nspVar2Inc; tempX = (int)(cosf(sinf(nsp2 / 1000.0f) + (nsp / 2)) * 32000); tempY = (int)(sinf(cosf(nsp2 / 1000.0f) + (nsp / 2)) * 30000); break; case 12: if (nsp != 0) { nsp2 += nspVar2Inc; if ((nsp2 == 65535) || (nsp2 == 0)) nspVar2Inc = -nspVar2Inc; tempX = (int)(cosf(sinf(nsp2 / 2.0f) / (sqrtf(fabsf(nsp)) / 10.0f + 1) + (nsp2 / 100.0f)) * 32000); tempY = (int)(sinf(cosf(nsp2 / 2.0f) / (sqrtf(fabsf(nsp)) / 10.0f + 1) + (nsp2 / 100.0f)) * 30000); } break; case 13: if (nsp != 0) { nsp2 += nspVar2Inc; if ((nsp2 == 65535) || (nsp2 == 0)) nspVar2Inc = -nspVar2Inc; tempX = (int)(cosf(sinf(nsp2 / 10.0f) / 2 + (nsp / 20)) * 32000); tempY = (int)(sinf(sinf(nsp2 / 11.0f) / 2 + (nsp / 20)) * 30000); } break; case 14: nsp2 += nspVar2Inc; tempX = (int)((sinf(nsp) + cosf(nsp2 / 1000.0f) * 3) * 12000); tempY = (int)(cosf(nsp) * 10000) + nsp2; break; } } } opentyrian-2.1.20221123/src/starlib.h000066400000000000000000000021111432005211200170200ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef STARLIB_H #define STARLIB_H #include "opentyr.h" void JE_starlib_main(void); void JE_wackyCol(void); void JE_starlib_init(void); void JE_resetValues(void); void JE_changeSetup(JE_byte setupType); void JE_newStar(void); #endif /* STARLIB_H */ opentyrian-2.1.20221123/src/tyrian2.c000066400000000000000000003626101432005211200167600ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tyrian2.h" #include "animlib.h" #include "backgrnd.h" #include "episodes.h" #include "file.h" #include "font.h" #include "fonthand.h" #include "game_menu.h" #include "joystick.h" #include "keyboard.h" #include "lds_play.h" #include "loudness.h" #include "lvllib.h" #include "menus.h" #include "mainint.h" #include "mouse.h" #include "mtrand.h" #include "network.h" #include "nortsong.h" #include "nortvars.h" #include "opentyr.h" #include "params.h" #include "pcxload.h" #include "pcxmast.h" #include "picload.h" #include "shots.h" #include "sprite.h" #include "vga256d.h" #include "video.h" #include #include #include #include #include #include inline static void blit_enemy(SDL_Surface *surface, unsigned int i, signed int x_offset, signed int y_offset, signed int sprite_offset); boss_bar_t boss_bar[2]; /* Level Event Data */ JE_boolean quit, loadLevelOk; struct JE_EventRecType eventRec[EVENT_MAXIMUM]; /* [1..eventMaximum] */ JE_word levelEnemyMax; JE_word levelEnemyFrequency; JE_word levelEnemy[40]; /* [1..40] */ char tempStr[31]; /* Data used for ItemScreen procedure to indicate items available */ JE_byte itemAvail[9][10]; /* [1..9, 1..10] */ JE_byte itemAvailMax[9]; /* [1..9] */ void JE_starShowVGA(void) { JE_byte *src; Uint8 *s = NULL; /* screen pointer, 8-bit specific */ int x, y, lightx, lighty, lightdist; if (!playerEndLevel && !skipStarShowVGA) { s = VGAScreenSeg->pixels; src = game_screen->pixels; src += 24; if (smoothScroll != 0 /*&& thisPlayerNum != 2*/) { wait_delay(); setDelay(frameCountMax); } if (starShowVGASpecialCode == 1) { src += game_screen->pitch * 183; for (y = 0; y < 184; y++) { memmove(s, src, 264); s += VGAScreenSeg->pitch; src -= game_screen->pitch; } } else if (starShowVGASpecialCode == 2 && processorType >= 2) { lighty = 172 - player[0].y; lightx = 281 - player[0].x; for (y = 184; y; y--) { if (lighty > y) { for (x = 320 - 56; x; x--) { *s = (*src & 0xf0) | ((*src >> 2) & 0x03); s++; src++; } } else { for (x = 320 - 56; x; x--) { lightdist = abs(lightx - x) + lighty; if (lightdist < y) *s = *src; else if (lightdist - y <= 5) *s = (*src & 0xf0) | (((*src & 0x0f) + (3 * (5 - (lightdist - y)))) / 4); else *s = (*src & 0xf0) | ((*src & 0x0f) >> 2); s++; src++; } } s += 56 + VGAScreenSeg->pitch - 320; src += 56 + VGAScreenSeg->pitch - 320; } } else { for (y = 0; y < 184; y++) { memmove(s, src, 264); s += VGAScreenSeg->pitch; src += game_screen->pitch; } } JE_showVGA(); } quitRequested = false; skipStarShowVGA = false; } inline static void blit_enemy(SDL_Surface *surface, unsigned int i, signed int x_offset, signed int y_offset, signed int sprite_offset) { if (enemy[i].sprite2s == NULL) { fprintf(stderr, "warning: enemy %d sprite missing\n", i); return; } const int x = enemy[i].ex + x_offset + tempMapXOfs, y = enemy[i].ey + y_offset; const unsigned int index = enemy[i].egr[enemy[i].enemycycle - 1] + sprite_offset; if (enemy[i].filter != 0) blit_sprite2_filter(surface, x, y, *enemy[i].sprite2s, index, enemy[i].filter); else blit_sprite2(surface, x, y, *enemy[i].sprite2s, index); } void JE_drawEnemy(int enemyOffset) // actually does a whole lot more than just drawing { player[0].x -= 25; for (int i = enemyOffset - 25; i < enemyOffset; i++) { if (enemyAvail[i] != 1) { enemy[i].mapoffset = tempMapXOfs; if (enemy[i].xaccel && enemy[i].xaccel - 89u > mt_rand() % 11) { if (player[0].x > enemy[i].ex) { if (enemy[i].exc < enemy[i].xaccel - 89) enemy[i].exc++; } else { if (enemy[i].exc >= 0 || -enemy[i].exc < enemy[i].xaccel - 89) enemy[i].exc--; } } if (enemy[i].yaccel && enemy[i].yaccel - 89u > mt_rand() % 11) { if (player[0].y > enemy[i].ey) { if (enemy[i].eyc < enemy[i].yaccel - 89) enemy[i].eyc++; } else { if (enemy[i].eyc >= 0 || -enemy[i].eyc < enemy[i].yaccel - 89) enemy[i].eyc--; } } if (enemy[i].ex + tempMapXOfs > -29 && enemy[i].ex + tempMapXOfs < 300) { if (enemy[i].aniactive == 1) { enemy[i].enemycycle++; if (enemy[i].enemycycle == enemy[i].animax) enemy[i].aniactive = enemy[i].aniwhenfire; else if (enemy[i].enemycycle > enemy[i].ani) enemy[i].enemycycle = enemy[i].animin; } if (enemy[i].egr[enemy[i].enemycycle - 1] == 999) goto enemy_gone; if (enemy[i].size == 1) // 2x2 enemy { if (enemy[i].ey > -13) { blit_enemy(VGAScreen, i, -6, -7, 0); blit_enemy(VGAScreen, i, 6, -7, 1); } if (enemy[i].ey > -26 && enemy[i].ey < 182) { blit_enemy(VGAScreen, i, -6, 7, 19); blit_enemy(VGAScreen, i, 6, 7, 20); } } else { if (enemy[i].ey > -13) blit_enemy(VGAScreen, i, 0, 0, 0); } enemy[i].filter = 0; } if (enemy[i].excc) { if (--enemy[i].exccw <= 0) { if (enemy[i].exc == enemy[i].exrev) { enemy[i].excc = -enemy[i].excc; enemy[i].exrev = -enemy[i].exrev; enemy[i].exccadd = -enemy[i].exccadd; } else { enemy[i].exc += enemy[i].exccadd; enemy[i].exccw = enemy[i].exccwmax; if (enemy[i].exc == enemy[i].exrev) { enemy[i].excc = -enemy[i].excc; enemy[i].exrev = -enemy[i].exrev; enemy[i].exccadd = -enemy[i].exccadd; } } } } if (enemy[i].eycc) { if (--enemy[i].eyccw <= 0) { if (enemy[i].eyc == enemy[i].eyrev) { enemy[i].eycc = -enemy[i].eycc; enemy[i].eyrev = -enemy[i].eyrev; enemy[i].eyccadd = -enemy[i].eyccadd; } else { enemy[i].eyc += enemy[i].eyccadd; enemy[i].eyccw = enemy[i].eyccwmax; if (enemy[i].eyc == enemy[i].eyrev) { enemy[i].eycc = -enemy[i].eycc; enemy[i].eyrev = -enemy[i].eyrev; enemy[i].eyccadd = -enemy[i].eyccadd; } } } } enemy[i].ey += enemy[i].fixedmovey; enemy[i].ex += enemy[i].exc; if (enemy[i].ex < -80 || enemy[i].ex > 340) goto enemy_gone; enemy[i].ey += enemy[i].eyc; if (enemy[i].ey < -112 || enemy[i].ey > 190) goto enemy_gone; goto enemy_still_exists; enemy_gone: /* enemy[i].egr[10] &= 0x00ff; madness? */ enemyAvail[i] = 1; goto draw_enemy_end; enemy_still_exists: /*X bounce*/ if (enemy[i].ex <= enemy[i].xminbounce || enemy[i].ex >= enemy[i].xmaxbounce) enemy[i].exc = -enemy[i].exc; /*Y bounce*/ if (enemy[i].ey <= enemy[i].yminbounce || enemy[i].ey >= enemy[i].ymaxbounce) enemy[i].eyc = -enemy[i].eyc; /* Evalue != 0 - score item at boundary */ if (enemy[i].scoreitem) { if (enemy[i].ex < -5) enemy[i].ex++; if (enemy[i].ex > 245) enemy[i].ex--; } enemy[i].ey += tempBackMove; if (enemy[i].ex <= -24 || enemy[i].ex >= 296) goto draw_enemy_end; tempX = enemy[i].ex; tempY = enemy[i].ey; temp = enemy[i].enemytype; /* Enemy Shots */ if (enemy[i].edamaged == 1) goto draw_enemy_end; enemyOnScreen++; if (enemy[i].iced) { enemy[i].iced--; if (enemy[i].enemyground != 0) { enemy[i].filter = 0x09; } goto draw_enemy_end; } for (int j = 3; j > 0; j--) { if (enemy[i].freq[j-1]) { temp3 = enemy[i].tur[j-1]; if (--enemy[i].eshotwait[j-1] == 0 && temp3) { enemy[i].eshotwait[j-1] = enemy[i].freq[j-1]; if (difficultyLevel > DIFFICULTY_NORMAL) { enemy[i].eshotwait[j-1] = (enemy[i].eshotwait[j-1] / 2) + 1; if (difficultyLevel > DIFFICULTY_MANIACAL) enemy[i].eshotwait[j-1] = (enemy[i].eshotwait[j-1] / 2) + 1; } if (galagaMode && (enemy[i].eyc == 0 || (mt_rand() % 400) >= galagaShotFreq)) goto draw_enemy_end; switch (temp3) { case 252: /* Savara Boss DualMissile */ if (enemy[i].ey > 20) { JE_setupExplosion(tempX - 8 + tempMapXOfs, tempY - 20 - backMove * 8, -2, 6, false, false); JE_setupExplosion(tempX + 4 + tempMapXOfs, tempY - 20 - backMove * 8, -2, 6, false, false); } break; case 251:; /* Suck-O-Magnet */ const int attractivity = 4 - (abs(player[0].x - tempX) + abs(player[0].y - tempY)) / 100; player[0].x_velocity += (player[0].x > tempX) ? -attractivity : attractivity; break; case 253: /* Left ShortRange Magnet */ if (abs(player[0].x + 25 - 14 - tempX) < 24 && abs(player[0].y - tempY) < 28) { player[0].x_velocity += 2; } if (twoPlayerMode && (abs(player[1].x - 14 - tempX) < 24 && abs(player[1].y - tempY) < 28)) { player[1].x_velocity += 2; } break; case 254: /* Left ShortRange Magnet */ if (abs(player[0].x + 25 - 14 - tempX) < 24 && abs(player[0].y - tempY) < 28) { player[0].x_velocity -= 2; } if (twoPlayerMode && (abs(player[1].x - 14 - tempX) < 24 && abs(player[1].y - tempY) < 28)) { player[1].x_velocity -= 2; } break; case 255: /* Magneto RePulse!! */ if (difficultyLevel != DIFFICULTY_EASY) /*DIF*/ { if (j == 3) { enemy[i].filter = 0x70; } else { const int repulsivity = 4 - (abs(player[0].x - tempX) + abs(player[0].y - tempY)) / 20; if (repulsivity > 0) player[0].x_velocity += (player[0].x > tempX) ? repulsivity : -repulsivity; } } break; default: /*Rot*/ for (int tempCount = weapons[temp3].multi; tempCount > 0; tempCount--) { for (b = 0; b < ENEMY_SHOT_MAX; b++) { if (enemyShotAvail[b] == 1) break; } if (b == ENEMY_SHOT_MAX) goto draw_enemy_end; enemyShotAvail[b] = !enemyShotAvail[b]; if (weapons[temp3].sound > 0) { do { temp = mt_rand() % 8; } while (temp == 3); soundQueue[temp] = weapons[temp3].sound; } if (enemy[i].aniactive == 2) enemy[i].aniactive = 1; if (++enemy[i].eshotmultipos[j-1] > weapons[temp3].max) enemy[i].eshotmultipos[j-1] = 1; int tempPos = enemy[i].eshotmultipos[j-1] - 1; if (j == 1) temp2 = 4; enemyShot[b].sx = tempX + weapons[temp3].bx[tempPos] + tempMapXOfs; enemyShot[b].sy = tempY + weapons[temp3].by[tempPos]; enemyShot[b].sdmg = weapons[temp3].attack[tempPos]; enemyShot[b].tx = weapons[temp3].tx; enemyShot[b].ty = weapons[temp3].ty; enemyShot[b].duration = weapons[temp3].del[tempPos]; enemyShot[b].animate = 0; enemyShot[b].animax = weapons[temp3].weapani; enemyShot[b].sgr = weapons[temp3].sg[tempPos]; switch (j) { case 1: enemyShot[b].syc = weapons[temp3].acceleration; enemyShot[b].sxc = weapons[temp3].accelerationx; enemyShot[b].sxm = weapons[temp3].sx[tempPos]; enemyShot[b].sym = weapons[temp3].sy[tempPos]; break; case 3: enemyShot[b].sxc = -weapons[temp3].acceleration; enemyShot[b].syc = weapons[temp3].accelerationx; enemyShot[b].sxm = -weapons[temp3].sy[tempPos]; enemyShot[b].sym = -weapons[temp3].sx[tempPos]; break; case 2: enemyShot[b].sxc = weapons[temp3].acceleration; enemyShot[b].syc = -weapons[temp3].acceleration; enemyShot[b].sxm = weapons[temp3].sy[tempPos]; enemyShot[b].sym = -weapons[temp3].sx[tempPos]; break; } if (weapons[temp3].aim > 0) { int aim = weapons[temp3].aim; /*DIF*/ if (difficultyLevel > DIFFICULTY_NORMAL) { aim += difficultyLevel - 2; } JE_word target_x = player[0].x; JE_word target_y = player[0].y; if (twoPlayerMode) { // fire at live player(s) if (player[0].is_alive && !player[1].is_alive) temp = 0; else if (player[1].is_alive && !player[0].is_alive) temp = 1; else temp = mt_rand() % 2; if (temp == 1) { target_x = player[1].x - 25; target_y = player[1].y; } } int relative_x = (target_x + 25) - tempX - tempMapXOfs - 4; if (relative_x == 0) relative_x = 1; int relative_y = target_y - tempY; if (relative_y == 0) relative_y = 1; const int longest_side = MAX(abs(relative_x), abs(relative_y)); enemyShot[b].sxm = roundf((float)relative_x / longest_side * aim); enemyShot[b].sym = roundf((float)relative_y / longest_side * aim); } } break; } } } } /* Enemy Launch Routine */ if (enemy[i].launchfreq) { if (--enemy[i].launchwait == 0) { enemy[i].launchwait = enemy[i].launchfreq; if (enemy[i].launchspecial != 0) { /*Type 1 : Must be inline with player*/ if (abs(enemy[i].ey - player[0].y) > 5) goto draw_enemy_end; } if (enemy[i].aniactive == 2) { enemy[i].aniactive = 1; } if (enemy[i].launchtype == 0) goto draw_enemy_end; tempW = enemy[i].launchtype; b = JE_newEnemy(enemyOffset == 50 ? 75 : enemyOffset - 25, tempW, 0); /*Launch Enemy Placement*/ if (b > 0) { struct JE_SingleEnemyType* e = &enemy[b-1]; e->ex = tempX; e->ey = tempY + enemyDat[e->enemytype].startyc; if (e->size == 0) e->ey -= 7; if (e->launchtype > 0 && e->launchfreq == 0) { if (e->launchtype > 90) { e->ex += mt_rand() % ((e->launchtype - 90) * 4) - (e->launchtype - 90) * 2; } else { int target_x = (player[0].x + 25) - tempX - tempMapXOfs - 4; if (target_x == 0) target_x = 1; int tempI5 = player[0].y - tempY; if (tempI5 == 0) tempI5 = 1; const int longest_side = MAX(abs(target_x), abs(tempI5)); e->exc = roundf(((float)target_x / longest_side) * e->launchtype); e->eyc = roundf(((float)tempI5 / longest_side) * e->launchtype); } } do { temp = mt_rand() % 8; } while (temp == 3); soundQueue[temp] = randomEnemyLaunchSounds[(mt_rand() % 3)]; if (enemy[i].launchspecial == 1 && enemy[i].linknum < 100) { e->linknum = enemy[i].linknum; } } } } } draw_enemy_end: ; } player[0].x += 25; } void JE_main(void) { char buffer[256]; int lastEnemyOnScreen; /* NOTE: BEGIN MAIN PROGRAM HERE AFTER LOADING A GAME OR STARTING A NEW ONE */ /* ----------- GAME ROUTINES ------------------------------------- */ /* We need to jump to the beginning to make space for the routines */ /* --------------------------------------------------------------- */ goto start_level_first; /*------------------------------GAME LOOP-----------------------------------*/ /* Startlevel is called after a previous level is over. If the first level is started for a gaming session, startlevelfirst is called instead and this code is skipped. The code here finishes the level and prepares for the loadmap function. */ start_level: mouseSetRelative(false); if (galagaMode) twoPlayerMode = false; JE_clearKeyboard(); free_sprite2s(&enemySpriteSheets[0]); free_sprite2s(&enemySpriteSheets[1]); free_sprite2s(&enemySpriteSheets[2]); free_sprite2s(&enemySpriteSheets[3]); /* Normal speed */ if (fastPlay != 0) { smoothScroll = true; Uint16 speed = 0x4300; setDelaySpeed(speed); } if (play_demo || record_demo) { if (demo_file) { fclose(demo_file); demo_file = NULL; } if (play_demo) { stop_song(); fade_black(10); wait_noinput(true, true, true); } } difficultyLevel = oldDifficultyLevel; /*Return difficulty to normal*/ if (!play_demo) { if ((!all_players_dead() || normalBonusLevelCurrent || bonusLevelCurrent) && !playerEndLevel) { mainLevel = nextLevel; JE_endLevelAni(); fade_song(); } else { fade_song(); fade_black(10); JE_loadGame(twoPlayerMode ? 22 : 11); if (doNotSaveBackup) { superTyrian = false; onePlayerAction = false; player[0].items.super_arcade_mode = SA_NONE; } if (bonusLevelCurrent && !playerEndLevel) { mainLevel = nextLevel; } } } doNotSaveBackup = false; if (play_demo) return; start_level_first: set_volume(tyrMusicVolume, fxVolume); endLevel = false; reallyEndLevel = false; playerEndLevel = false; extraGame = false; doNotSaveBackup = false; JE_loadMap(); if (mainLevel == 0) // if quit itemscreen return; // back to titlescreen if (!play_demo) mouseSetRelative(true); fade_song(); for (uint i = 0; i < COUNTOF(player); ++i) player[i].is_alive = true; oldDifficultyLevel = difficultyLevel; if (episodeNum == EPISODE_AVAILABLE) difficultyLevel--; if (difficultyLevel < DIFFICULTY_EASY) difficultyLevel = DIFFICULTY_EASY; player[0].x = 100; player[0].y = 180; player[1].x = 190; player[1].y = 180; assert(COUNTOF(player->old_x) == COUNTOF(player->old_y)); for (uint i = 0; i < COUNTOF(player); ++i) { for (uint j = 0; j < COUNTOF(player->old_x); ++j) { player[i].old_x[j] = player[i].x - (19 - j); player[i].old_y[j] = player[i].y - 18; } player[i].last_x_shot_move = player[i].x; player[i].last_y_shot_move = player[i].y; } JE_loadPic(VGAScreen, twoPlayerMode ? 6 : 3, false); JE_drawOptions(); JE_outText(VGAScreen, 268, twoPlayerMode ? 76 : 118, levelName, 12, 4); JE_showVGA(); JE_gammaCorrect(&colors, gammaCorrection); fade_palette(colors, 50, 0, 255); if (explosionSpriteSheet.data == NULL) JE_loadCompShapes(&explosionSpriteSheet, '6'); /* MAPX will already be set correctly */ mapY = 300 - 8; mapY2 = 600 - 8; mapY3 = 600 - 8; mapYPos = &megaData1.mainmap[mapY][0] - 1; mapY2Pos = &megaData2.mainmap[mapY2][0] - 1; mapY3Pos = &megaData3.mainmap[mapY3][0] - 1; mapXPos = 0; mapXOfs = 0; mapX2Pos = 0; mapX3Pos = 0; mapX3Ofs = 0; mapXbpPos = 0; mapX2bpPos = 0; mapX3bpPos = 0; map1YDelay = 1; map1YDelayMax = 1; map2YDelay = 1; map2YDelayMax = 1; musicFade = false; backPos = 0; backPos2 = 0; backPos3 = 0; power = 0; starfield_speed = 1; /* Setup player ship graphics */ JE_getShipInfo(); for (uint i = 0; i < COUNTOF(player); ++i) { player[i].x_velocity = 0; player[i].y_velocity = 0; player[i].invulnerable_ticks = 100; } newkey = newmouse = false; /* Initialize Level Data and Debug Mode */ levelEnd = 255; levelEndWarp = -4; levelEndFxWait = 0; warningCol = 120; warningColChange = 1; warningSoundDelay = 0; armorShipDelay = 50; bonusLevel = false; readyToEndLevel = false; firstGameOver = true; eventLoc = 1; curLoc = 0; backMove = 1; backMove2 = 2; backMove3 = 3; explodeMove = 2; enemiesActive = true; for (temp = 0; temp < 3; temp++) { button[temp] = false; } stopBackgrounds = false; stopBackgroundNum = 0; background3x1 = false; background3x1b = false; background3over = 0; background2over = 1; topEnemyOver = false; skyEnemyOverAll = false; smallEnemyAdjust = false; starActive = true; enemyContinualDamage = false; levelEnemyFrequency = 96; quitRequested = false; for (unsigned int i = 0; i < COUNTOF(boss_bar); i++) boss_bar[i].link_num = 0; forceEvents = false; /*Force events to continue if background movement = 0*/ superEnemy254Jump = 0; /*When Enemy with PL 254 dies*/ /* Filter Status */ filterActive = true; filterFade = true; filterFadeStart = false; levelFilter = -99; levelBrightness = -14; levelBrightnessChg = 1; background2notTransparent = false; uint old_weapon_bar[2] = { 0, 0 }; // only redrawn when they change /* Initially erase power bars */ lastPower = power / 10; /* Initial Text */ JE_drawTextWindow(miscText[20]); /* Setup Armor/Shield Data */ shieldWait = 1; shieldT = shields[player[0].items.shield].tpwr * 20; for (uint i = 0; i < COUNTOF(player); ++i) { player[i].shield = shields[player[i].items.shield].mpwr; player[i].shield_max = player[i].shield * 2; } JE_drawShield(); JE_drawArmor(); for (uint i = 0; i < COUNTOF(player); ++i) player[i].superbombs = 0; /* Set cubes to 0 */ cubeMax = 0; /* Secret Level Display */ flash = 0; flashChange = 1; displayTime = 0; play_song(levelSong - 1); JE_drawPortConfigButtons(); /* --- MAIN LOOP --- */ newkey = false; #ifdef WITH_NETWORK if (isNetworkGame) { JE_clearSpecialRequests(); mt_srand(32402394); } #endif initialize_starfield(); JE_setNewGameSpeed(); set_volume(tyrMusicVolume, fxVolume); /*Save backup game*/ if (!play_demo && !doNotSaveBackup) { temp = twoPlayerMode ? 22 : 11; JE_saveGame(temp, "LAST LEVEL "); } if (!play_demo && record_demo) { Uint8 new_demo_num = 0; do { sprintf(tempStr, "demorec.%d", new_demo_num++); } while (dir_file_exists(get_user_directory(), tempStr)); // until file doesn't exist demo_file = dir_fopen_warn(get_user_directory(), tempStr, "wb"); if (!demo_file) exit(1); fwrite_u8_die(&episodeNum, 1, demo_file); // Pad string buffer with NULs. for (size_t i = 1; i < 10; ++i) if (levelName[i - 1] == '\0') levelName[i] = '\0'; fwrite_u8_die((Uint8 *)levelName, 10, demo_file); fwrite_u8_die(&lvlFileNum, 1, demo_file); fwrite_u8_die(&player[0].items.weapon[FRONT_WEAPON].id, 1, demo_file); fwrite_u8_die(&player[0].items.weapon[REAR_WEAPON].id, 1, demo_file); fwrite_u8_die(&player[0].items.super_arcade_mode, 1, demo_file); fwrite_u8_die(&player[0].items.sidekick[LEFT_SIDEKICK], 1, demo_file); fwrite_u8_die(&player[0].items.sidekick[RIGHT_SIDEKICK], 1, demo_file); fwrite_u8_die(&player[0].items.generator, 1, demo_file); fwrite_u8_die(&player[0].items.sidekick_level, 1, demo_file); fwrite_u8_die(&player[0].items.sidekick_series, 1, demo_file); fwrite_u8_die(&initial_episode_num, 1, demo_file); fwrite_u8_die(&player[0].items.shield, 1, demo_file); fwrite_u8_die(&player[0].items.special, 1, demo_file); fwrite_u8_die(&player[0].items.ship, 1, demo_file); for (uint i = 0; i < 2; ++i) fwrite_u8_die(&player[0].items.weapon[i].power, 1, demo_file); Uint8 unused[3] = { 0, 0, 0 }; fwrite_u8_die(unused, 3, demo_file); fwrite_u8_die(&levelSong, 1, demo_file); demo_keys = 0; demo_keys_wait = 0; } twoPlayerLinked = false; linkGunDirec = M_PI; for (uint i = 0; i < COUNTOF(player); ++i) calc_purple_balls_needed(&player[i]); damageRate = 2; /*Normal Rate for Collision Damage*/ chargeWait = 5; chargeLevel = 0; chargeMax = 5; chargeGr = 0; chargeGrWait = 3; portConfigChange = false; /*Destruction Ratio*/ totalEnemy = 0; enemyKilled = 0; astralDuration = 0; superArcadePowerUp = 1; yourInGameMenuRequest = false; constantLastX = -1; for (uint i = 0; i < COUNTOF(player); ++i) player[i].exploding_ticks = 0; if (isNetworkGame) { JE_loadItemDat(); } memset(enemyAvail, 1, sizeof(enemyAvail)); for (uint i = 0; i < COUNTOF(enemyShotAvail); i++) enemyShotAvail[i] = 1; /*Initialize Shots*/ memset(playerShotData, 0, sizeof(playerShotData)); memset(shotAvail, 0, sizeof(shotAvail)); memset(shotMultiPos, 0, sizeof(shotMultiPos)); memset(shotRepeat, 1, sizeof(shotRepeat)); memset(button, 0, sizeof(button)); memset(globalFlags, 0, sizeof(globalFlags)); memset(explosions, 0, sizeof(explosions)); memset(rep_explosions, 0, sizeof(rep_explosions)); /* --- Clear Sound Queue --- */ memset(soundQueue, 0, sizeof(soundQueue)); soundQueue[3] = V_GOOD_LUCK; memset(enemySpriteSheetIds, 0, sizeof(enemySpriteSheetIds)); memset(enemy, 0, sizeof(enemy)); memset(SFCurrentCode, 0, sizeof(SFCurrentCode)); memset(SFExecuted, 0, sizeof(SFExecuted)); zinglonDuration = 0; specialWait = 0; nextSpecialWait = 0; optionAttachmentMove = 0; /*Launch the Attachments!*/ optionAttachmentLinked = true; editShip1 = false; editShip2 = false; memset(smoothies, 0, sizeof(smoothies)); levelTimer = false; randomExplosions = false; last_superpixel = 0; memset(superpixels, 0, sizeof(superpixels)); returnActive = false; galagaShotFreq = 0; if (galagaMode) { difficultyLevel = DIFFICULTY_NORMAL; } galagaLife = 10000; JE_drawOptionLevel(); // keeps map from scrolling past the top BKwrap1 = BKwrap1to = &megaData1.mainmap[1][0]; BKwrap2 = BKwrap2to = &megaData2.mainmap[1][0]; BKwrap3 = BKwrap3to = &megaData3.mainmap[1][0]; level_loop: //tempScreenSeg = game_screen; /* side-effect of game_screen */ if (isNetworkGame) { smoothies[9-1] = false; smoothies[6-1] = false; } else { starShowVGASpecialCode = smoothies[9-1] + (smoothies[6-1] << 1); } /*Background Wrapping*/ if (mapYPos <= BKwrap1) mapYPos = BKwrap1to; if (mapY2Pos <= BKwrap2) mapY2Pos = BKwrap2to; if (mapY3Pos <= BKwrap3) mapY3Pos = BKwrap3to; allPlayersGone = all_players_dead() && ((*player[0].lives == 1 && player[0].exploding_ticks == 0) || (!onePlayerAction && !twoPlayerMode)) && ((*player[1].lives == 1 && player[1].exploding_ticks == 0) || !twoPlayerMode); /*-----MUSIC FADE------*/ if (musicFade) { if (tempVolume > 10) { tempVolume--; set_volume(tempVolume, fxVolume); } else { musicFade = false; } } if (!allPlayersGone && levelEnd > 0 && endLevel) { play_song(9); musicFade = false; } else if (!playing && firstGameOver) { play_song(levelSong - 1); } if (!endLevel) // draw HUD { VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ /*-----------------------Message Bar------------------------*/ if (textErase > 0 && --textErase == 0) blit_sprite(VGAScreenSeg, 16, 189, OPTION_SHAPES, 36); // in-game message area /*------------------------Shield Gen-------------------------*/ if (galagaMode) { for (uint i = 0; i < COUNTOF(player); ++i) player[i].shield = 0; // spawned dragonwing died :( if (*player[1].lives == 0 || player[1].armor == 0) twoPlayerMode = false; if (player[0].cash >= (unsigned)galagaLife) { soundQueue[6] = S_EXPLOSION_11; soundQueue[7] = S_SOUL_OF_ZINGLON; if (*player[0].lives < 11) ++(*player[0].lives); else player[0].cash += 1000; if (galagaLife == 10000) galagaLife = 20000; else galagaLife += 25000; } } else // not galagaMode { if (twoPlayerMode) { if (--shieldWait == 0) { shieldWait = 15; for (uint i = 0; i < COUNTOF(player); ++i) { if (player[i].shield < player[i].shield_max && player[i].is_alive) ++player[i].shield; } JE_drawShield(); } } else if (player[0].is_alive && player[0].shield < player[0].shield_max && power > shieldT) { if (--shieldWait == 0) { shieldWait = 15; power -= shieldT; ++player[0].shield; if (player[1].shield < player[0].shield_max) ++player[1].shield; JE_drawShield(); } } } /*---------------------Weapon Display-------------------------*/ for (uint i = 0; i < 2; ++i) { uint item_power = player[twoPlayerMode ? i : 0].items.weapon[i].power; if (old_weapon_bar[i] != item_power) { old_weapon_bar[i] = item_power; int x = twoPlayerMode ? 286 : 289, y = (i == 0) ? (twoPlayerMode ? 6 : 17) : (twoPlayerMode ? 100 : 38); fill_rectangle_xy(VGAScreenSeg, x, y, x + 1 + 10 * 2, y + 2, 0); for (uint j = 1; j <= item_power; ++j) { JE_rectangle(VGAScreen, x, y, x + 1, y + 2, 115 + j); /* SEGa000 */ x += 2; } } } /*------------------------Power Bar-------------------------*/ if (twoPlayerMode || onePlayerAction) { power = 900; } else { power += powerAdd; if (power > 900) power = 900; temp = power / 10; if (temp != lastPower) { if (temp > lastPower) fill_rectangle_xy(VGAScreenSeg, 269, 113 - 11 - temp, 276, 114 - 11 - lastPower, 113 + temp / 7); else fill_rectangle_xy(VGAScreenSeg, 269, 113 - 11 - lastPower, 276, 114 - 11 - temp, 0); lastPower = temp; } } oldMapX3Ofs = mapX3Ofs; enemyOnScreen = 0; } /* use game_screen for all the generic drawing functions */ VGAScreen = game_screen; /*---------------------------EVENTS-------------------------*/ while (eventRec[eventLoc-1].eventtime <= curLoc && eventLoc <= maxEvent) JE_eventSystem(); if (isNetworkGame && reallyEndLevel) goto start_level; /* SMOOTHIES! */ JE_checkSmoothies(); if (anySmoothies) VGAScreen = VGAScreen2; // this makes things complicated, but we do it anyway :( /* --- BACKGROUNDS --- */ /* --- BACKGROUND 1 --- */ if (forceEvents && !backMove) curLoc++; if (map1YDelayMax > 1 && backMove < 2) backMove = (map1YDelay == 1) ? 1 : 0; /*Draw background*/ if (astralDuration == 0) draw_background_1(VGAScreen); else JE_clr256(VGAScreen); /*Set Movement of background 1*/ if (--map1YDelay == 0) { map1YDelay = map1YDelayMax; curLoc += backMove; backPos += backMove; if (backPos > 27) { backPos -= 28; mapY--; mapYPos -= 14; /*Map Width*/ } } if (starActive || astralDuration > 0) update_and_draw_starfield(VGAScreen, starfield_speed); if (processorType > 1 && smoothies[5-1]) { iced_blur_filter(game_screen, VGAScreen); VGAScreen = game_screen; } /*-----------------------BACKGROUNDS------------------------*/ /*-----------------------BACKGROUND 2------------------------*/ if (background2over == 3) { draw_background_2(VGAScreen); background2 = true; } if (background2over == 0) { if (!(smoothies[2-1] && processorType < 4) && !(smoothies[1-1] && processorType == 3)) { if (wild && !background2notTransparent) draw_background_2_blend(VGAScreen); else draw_background_2(VGAScreen); } } if (smoothies[0] && processorType > 2 && smoothie_data[0] == 0) { lava_filter(game_screen, VGAScreen); VGAScreen = game_screen; } if (smoothies[2-1] && processorType > 2) { water_filter(game_screen, VGAScreen); VGAScreen = game_screen; } /*-----------------------Ground Enemy------------------------*/ lastEnemyOnScreen = enemyOnScreen; tempMapXOfs = mapXOfs; tempBackMove = backMove; JE_drawEnemy(50); JE_drawEnemy(100); if (enemyOnScreen == 0 || enemyOnScreen == lastEnemyOnScreen) { if (stopBackgroundNum == 1) stopBackgroundNum = 9; } if (smoothies[0] && processorType > 2 && smoothie_data[0] > 0) { lava_filter(game_screen, VGAScreen); VGAScreen = game_screen; } if (superWild) { neat += 3; JE_darkenBackground(neat); } /*-----------------------BACKGROUNDS------------------------*/ /*-----------------------BACKGROUND 2------------------------*/ if (!(smoothies[2-1] && processorType < 4) && !(smoothies[1-1] && processorType == 3)) { if (background2over == 1) { if (wild && !background2notTransparent) draw_background_2_blend(VGAScreen); else draw_background_2(VGAScreen); } } if (superWild) { neat++; JE_darkenBackground(neat); } if (background3over == 2) draw_background_3(VGAScreen); /* New Enemy */ if (enemiesActive && mt_rand() % 100 > levelEnemyFrequency) { tempW = levelEnemy[mt_rand() % levelEnemyMax]; if (tempW == 2) soundQueue[3] = S_WEAPON_7; b = JE_newEnemy(0, tempW, 0); } if (processorType > 1 && smoothies[3-1]) { iced_blur_filter(game_screen, VGAScreen); VGAScreen = game_screen; } if (processorType > 1 && smoothies[4-1]) { blur_filter(game_screen, VGAScreen); VGAScreen = game_screen; } /* Draw Sky Enemy */ if (!skyEnemyOverAll) { lastEnemyOnScreen = enemyOnScreen; tempMapXOfs = mapX2Ofs; tempBackMove = 0; JE_drawEnemy(25); if (enemyOnScreen == lastEnemyOnScreen) { if (stopBackgroundNum == 2) stopBackgroundNum = 9; } } if (background3over == 0) draw_background_3(VGAScreen); /* Draw Top Enemy */ if (!topEnemyOver) { tempMapXOfs = (background3x1 == 0) ? oldMapX3Ofs : mapXOfs; tempBackMove = backMove3; JE_drawEnemy(75); } /* Player Shot Images */ for (int z = 0; z < MAX_PWEAPON; z++) { if (shotAvail[z] != 0) { bool is_special = false; int tempShotX = 0, tempShotY = 0; JE_byte chain; JE_byte playerNum; JE_word tempX2, tempY2; JE_integer damage; if (!player_shot_move_and_draw(z, &is_special, &tempShotX, &tempShotY, &damage, &temp2, &chain, &playerNum, &tempX2, &tempY2)) { goto draw_player_shot_loop_end; } for (b = 0; b < 100; b++) { if (enemyAvail[b] == 0) { bool collided; if (z == MAX_PWEAPON - 1) { temp = 25 - abs(zinglonDuration - 25); collided = abs(enemy[b].ex + enemy[b].mapoffset - (player[0].x + 7)) < temp; temp2 = 9; chain = 0; damage = 10; } else if (is_special) { collided = ((enemy[b].enemycycle == 0) && (abs(enemy[b].ex + enemy[b].mapoffset - tempShotX - tempX2) < (25 + tempX2)) && (abs(enemy[b].ey - tempShotY - 12 - tempY2) < (29 + tempY2))) || ((enemy[b].enemycycle > 0) && (abs(enemy[b].ex + enemy[b].mapoffset - tempShotX - tempX2) < (13 + tempX2)) && (abs(enemy[b].ey - tempShotY - 6 - tempY2) < (15 + tempY2))); } else { collided = ((enemy[b].enemycycle == 0) && (abs(enemy[b].ex + enemy[b].mapoffset - tempShotX) < 25) && (abs(enemy[b].ey - tempShotY - 12) < 29)) || ((enemy[b].enemycycle > 0) && (abs(enemy[b].ex + enemy[b].mapoffset - tempShotX) < 13) && (abs(enemy[b].ey - tempShotY - 6) < 15)); } if (collided) { if (chain > 0) { shotMultiPos[SHOT_MISC] = 0; b = player_shot_create(0, SHOT_MISC, tempShotX, tempShotY, mouseX, mouseY, chain, playerNum); shotAvail[z] = 0; goto draw_player_shot_loop_end; } infiniteShot = false; if (damage == 99) { damage = 0; doIced = 40; enemy[b].iced = 40; } else { doIced = 0; if (damage >= 250) { damage = damage - 250; infiniteShot = true; } } int armorleft = enemy[b].armorleft; temp = enemy[b].linknum; if (temp == 0) temp = 255; if (enemy[b].armorleft < 255) { for (unsigned int i = 0; i < COUNTOF(boss_bar); i++) if (temp == boss_bar[i].link_num) boss_bar[i].color = 6; if (enemy[b].enemyground) enemy[b].filter = temp2; for (unsigned int e = 0; e < COUNTOF(enemy); e++) { if (enemy[e].linknum == temp && enemyAvail[e] != 1 && enemy[e].enemyground != 0) { if (doIced) enemy[e].iced = doIced; enemy[e].filter = temp2; } } } if (armorleft > damage) { if (z != MAX_PWEAPON - 1) { if (enemy[b].armorleft != 255) { enemy[b].armorleft -= damage; JE_setupExplosion(tempShotX, tempShotY, 0, 0, false, false); } else { JE_doSP(tempShotX + 6, tempShotY + 6, damage / 2 + 3, damage / 4 + 2, temp2); } } soundQueue[5] = S_ENEMY_HIT; if ((armorleft - damage <= enemy[b].edlevel) && ((!enemy[b].edamaged) ^ (enemy[b].edani < 0))) { for (temp3 = 0; temp3 < 100; temp3++) { if (enemyAvail[temp3] != 1) { int linknum = enemy[temp3].linknum; if ( (temp3 == b) || ( (temp != 255) && ( ((enemy[temp3].edlevel > 0) && (linknum == temp)) || ( (enemyContinualDamage && (temp - 100 == linknum)) || ((linknum > 40) && (linknum / 20 == temp / 20) && (linknum <= temp)) ) ) ) ) { enemy[temp3].enemycycle = 1; enemy[temp3].edamaged = !enemy[temp3].edamaged; if (enemy[temp3].edani != 0) { enemy[temp3].ani = abs(enemy[temp3].edani); enemy[temp3].aniactive = 1; enemy[temp3].animax = 0; enemy[temp3].animin = enemy[temp3].edgr; enemy[temp3].enemycycle = enemy[temp3].animin - 1; } else if (enemy[temp3].edgr > 0) { enemy[temp3].egr[1-1] = enemy[temp3].edgr; enemy[temp3].ani = 1; enemy[temp3].aniactive = 0; enemy[temp3].animax = 0; enemy[temp3].animin = 1; } else { enemyAvail[temp3] = 1; enemyKilled++; } enemy[temp3].aniwhenfire = 0; if (enemy[temp3].armorleft > (unsigned char)enemy[temp3].edlevel) enemy[temp3].armorleft = enemy[temp3].edlevel; tempX = enemy[temp3].ex + enemy[temp3].mapoffset; tempY = enemy[temp3].ey; if (enemyDat[enemy[temp3].enemytype].esize != 1) JE_setupExplosion(tempX, tempY - 6, 0, 1, false, false); else JE_setupExplosionLarge(enemy[temp3].enemyground, enemy[temp3].explonum / 2, tempX, tempY); } } } } } else { if ((temp == 254) && (superEnemy254Jump > 0)) JE_eventJump(superEnemy254Jump); for (temp2 = 0; temp2 < 100; temp2++) { if (enemyAvail[temp2] != 1) { temp3 = enemy[temp2].linknum; if ((temp2 == b) || (temp == 254) || ((temp != 255) && ((temp == temp3) || (temp - 100 == temp3) || ((temp3 > 40) && (temp3 / 20 == temp / 20) && (temp3 <= temp))))) { int enemy_screen_x = enemy[temp2].ex + enemy[temp2].mapoffset; if (enemy[temp2].special) { assert((unsigned int) enemy[temp2].flagnum-1 < COUNTOF(globalFlags)); globalFlags[enemy[temp2].flagnum-1] = enemy[temp2].setto; } if ((enemy[temp2].enemydie > 0) && !((superArcadeMode != SA_NONE) && (enemyDat[enemy[temp2].enemydie].value == 30000))) { int temp_b = b; tempW = enemy[temp2].enemydie; int enemy_offset = temp2 - (temp2 % 25); if (enemyDat[tempW].value > 30000) { enemy_offset = 0; } b = JE_newEnemy(enemy_offset, tempW, 0); if (b != 0) { if ((superArcadeMode != SA_NONE) && (enemy[b-1].evalue > 30000)) { superArcadePowerUp++; if (superArcadePowerUp > 5) superArcadePowerUp = 1; enemy[b-1].egr[1-1] = 5 + superArcadePowerUp * 2; enemy[b-1].evalue = 30000 + superArcadePowerUp; } if (enemy[b-1].evalue != 0) enemy[b-1].scoreitem = true; else enemy[b-1].scoreitem = false; enemy[b-1].ex = enemy[temp2].ex; enemy[b-1].ey = enemy[temp2].ey; } b = temp_b; } if ((enemy[temp2].evalue > 0) && (enemy[temp2].evalue < 10000)) { if (enemy[temp2].evalue == 1) { cubeMax++; } else { // in galaga mode player 2 is sidekick, so give cash to player 1 player[galagaMode ? 0 : playerNum - 1].cash += enemy[temp2].evalue; } } if ((enemy[temp2].edlevel == -1) && (temp == temp3)) { enemy[temp2].edlevel = 0; enemyAvail[temp2] = 2; enemy[temp2].egr[1-1] = enemy[temp2].edgr; enemy[temp2].ani = 1; enemy[temp2].aniactive = 0; enemy[temp2].animax = 0; enemy[temp2].animin = 1; enemy[temp2].edamaged = true; enemy[temp2].enemycycle = 1; } else { enemyAvail[temp2] = 1; enemyKilled++; } if (enemyDat[enemy[temp2].enemytype].esize == 1) { JE_setupExplosionLarge(enemy[temp2].enemyground, enemy[temp2].explonum, enemy_screen_x, enemy[temp2].ey); soundQueue[6] = S_EXPLOSION_9; } else { JE_setupExplosion(enemy_screen_x, enemy[temp2].ey, 0, 1, false, false); soundQueue[6] = S_EXPLOSION_8; } } } } } if (infiniteShot) { damage += 250; } else if (z != MAX_PWEAPON - 1) { if (damage <= armorleft) { shotAvail[z] = 0; goto draw_player_shot_loop_end; } else { playerShotData[z].shotDmg -= armorleft; } } } } } draw_player_shot_loop_end: ; } } /* Player movement indicators for shots that track your ship */ for (uint i = 0; i < COUNTOF(player); ++i) { player[i].last_x_shot_move = player[i].x; player[i].last_y_shot_move = player[i].y; } /*=================================*/ /*=======Collisions Detection======*/ /*=================================*/ for (uint i = 0; i < (twoPlayerMode ? 2 : 1); ++i) if (player[i].is_alive && !endLevel) JE_playerCollide(&player[i], i + 1); if (firstGameOver) JE_mainGamePlayerFunctions(); /*--------PLAYER DRAW+MOVEMENT---------*/ if (!endLevel) { /*MAIN DRAWING IS STOPPED STARTING HERE*/ /* Draw Enemy Shots */ for (int z = 0; z < ENEMY_SHOT_MAX; z++) { if (enemyShotAvail[z] == 0) { enemyShot[z].sxm += enemyShot[z].sxc; enemyShot[z].sx += enemyShot[z].sxm; if (enemyShot[z].tx != 0) { if (enemyShot[z].sx > player[0].x) { if (enemyShot[z].sxm > -enemyShot[z].tx) enemyShot[z].sxm--; } else { if (enemyShot[z].sxm < enemyShot[z].tx) enemyShot[z].sxm++; } } enemyShot[z].sym += enemyShot[z].syc; enemyShot[z].sy += enemyShot[z].sym; if (enemyShot[z].ty != 0) { if (enemyShot[z].sy > player[0].y) { if (enemyShot[z].sym > -enemyShot[z].ty) enemyShot[z].sym--; } else { if (enemyShot[z].sym < enemyShot[z].ty) enemyShot[z].sym++; } } if (enemyShot[z].duration-- == 0 || enemyShot[z].sy > 190 || enemyShot[z].sy <= -14 || enemyShot[z].sx > 275 || enemyShot[z].sx <= 0) { enemyShotAvail[z] = true; } else // check if shot collided with player { for (uint i = 0; i < (twoPlayerMode ? 2 : 1); ++i) { if (player[i].is_alive && enemyShot[z].sx > player[i].x - (signed)player[i].shot_hit_area_x && enemyShot[z].sx < player[i].x + (signed)player[i].shot_hit_area_x && enemyShot[z].sy > player[i].y - (signed)player[i].shot_hit_area_y && enemyShot[z].sy < player[i].y + (signed)player[i].shot_hit_area_y) { tempX = enemyShot[z].sx; tempY = enemyShot[z].sy; temp = enemyShot[z].sdmg; enemyShotAvail[z] = true; JE_setupExplosion(tempX, tempY, 0, 0, false, false); if (player[i].invulnerable_ticks == 0) { if ((temp = JE_playerDamage(temp, &player[i])) > 0) { player[i].x_velocity += (enemyShot[z].sxm * temp) / 2; player[i].y_velocity += (enemyShot[z].sym * temp) / 2; } } break; } } if (enemyShotAvail[z] == false) { if (enemyShot[z].animax != 0) { if (++enemyShot[z].animate >= enemyShot[z].animax) enemyShot[z].animate = 0; } if (enemyShot[z].sgr >= 500) blit_sprite2(VGAScreen, enemyShot[z].sx, enemyShot[z].sy, spriteSheet12, enemyShot[z].sgr + enemyShot[z].animate - 500); else blit_sprite2(VGAScreen, enemyShot[z].sx, enemyShot[z].sy, spriteSheet8, enemyShot[z].sgr + enemyShot[z].animate); } } } } } if (background3over == 1) draw_background_3(VGAScreen); /* Draw Top Enemy */ if (topEnemyOver) { tempMapXOfs = (background3x1 == 0) ? oldMapX3Ofs : oldMapXOfs; tempBackMove = backMove3; JE_drawEnemy(75); } /* Draw Sky Enemy */ if (skyEnemyOverAll) { lastEnemyOnScreen = enemyOnScreen; tempMapXOfs = mapX2Ofs; tempBackMove = 0; JE_drawEnemy(25); if (enemyOnScreen == lastEnemyOnScreen) { if (stopBackgroundNum == 2) stopBackgroundNum = 9; } } /*-------------------------- Sequenced Explosions -------------------------*/ enemyStillExploding = false; for (int i = 0; i < MAX_REPEATING_EXPLOSIONS; i++) { if (rep_explosions[i].ttl != 0) { enemyStillExploding = true; if (rep_explosions[i].delay > 0) { rep_explosions[i].delay--; continue; } rep_explosions[i].y += backMove2 + 1; tempX = rep_explosions[i].x + (mt_rand() % 24) - 12; tempY = rep_explosions[i].y + (mt_rand() % 27) - 24; if (rep_explosions[i].big) { JE_setupExplosionLarge(false, 2, tempX, tempY); if (rep_explosions[i].ttl == 1 || mt_rand() % 5 == 1) soundQueue[7] = S_EXPLOSION_11; else soundQueue[6] = S_EXPLOSION_9; rep_explosions[i].delay = 4 + (mt_rand() % 3); } else { JE_setupExplosion(tempX, tempY, 0, 1, false, false); soundQueue[5] = S_EXPLOSION_4; rep_explosions[i].delay = 3; } rep_explosions[i].ttl--; } } /*---------------------------- Draw Explosions ----------------------------*/ for (int j = 0; j < MAX_EXPLOSIONS; j++) { if (explosions[j].ttl != 0) { if (explosions[j].fixed_position != true) { explosions[j].sprite++; explosions[j].y += explodeMove; } else if (explosions[j].follow_player == true) { explosions[j].x += explosionFollowAmountX; explosions[j].y += explosionFollowAmountY; } explosions[j].y += explosions[j].delta_y; explosions[j].x += explosions[j].delta_x; if (explosions[j].y > 200 - 14) { explosions[j].ttl = 0; } else { if (explosionTransparent) blit_sprite2_blend(VGAScreen, explosions[j].x, explosions[j].y, explosionSpriteSheet, explosions[j].sprite + 1); else blit_sprite2(VGAScreen, explosions[j].x, explosions[j].y, explosionSpriteSheet, explosions[j].sprite + 1); explosions[j].ttl--; } } } if (!portConfigChange) portConfigDone = true; /*-----------------------BACKGROUNDS------------------------*/ /*-----------------------BACKGROUND 2------------------------*/ if (!(smoothies[2-1] && processorType < 4) && !(smoothies[1-1] && processorType == 3)) { if (background2over == 2) { if (wild && !background2notTransparent) draw_background_2_blend(VGAScreen); else draw_background_2(VGAScreen); } } /*-------------------------Warning---------------------------*/ if ((player[0].is_alive && player[0].armor < 6) || (twoPlayerMode && !galagaMode && player[1].is_alive && player[1].armor < 6)) { int armor_amount = (player[0].is_alive && player[0].armor < 6) ? player[0].armor : player[1].armor; if (armorShipDelay > 0) { armorShipDelay--; } else { tempW = 560; b = JE_newEnemy(50, tempW, 0); if (b > 0) { enemy[b-1].enemydie = 560 + (mt_rand() % 3) + 1; enemy[b-1].eyc -= backMove3; enemy[b-1].armorleft = 4; } armorShipDelay = 500; } if ((player[0].is_alive && player[0].armor < 6 && (!isNetworkGame || thisPlayerNum == 1)) || (twoPlayerMode && player[1].is_alive && player[1].armor < 6 && (!isNetworkGame || thisPlayerNum == 2))) { tempW = armor_amount * 4 + 8; if (warningSoundDelay > tempW) warningSoundDelay = tempW; if (warningSoundDelay > 1) { warningSoundDelay--; } else { soundQueue[7] = S_WARNING; warningSoundDelay = tempW; } warningCol += warningColChange; if (warningCol > 113 + (14 - (armor_amount * 2))) { warningColChange = -warningColChange; warningCol = 113 + (14 - (armor_amount * 2)); } else if (warningCol < 113) { warningColChange = -warningColChange; } fill_rectangle_xy(VGAScreen, 24, 181, 138, 183, warningCol); fill_rectangle_xy(VGAScreen, 175, 181, 287, 183, warningCol); fill_rectangle_xy(VGAScreen, 24, 0, 287, 3, warningCol); JE_outText(VGAScreen, 140, 178, "WARNING", 7, (warningCol % 16) / 2); } } /*------- Random Explosions --------*/ if (randomExplosions && mt_rand() % 10 == 1) JE_setupExplosionLarge(false, 20, mt_rand() % 280, mt_rand() % 180); /*=================================*/ /*=======The Sound Routine=========*/ /*=================================*/ if (firstGameOver) { temp = 0; for (temp2 = 0; temp2 < COUNTOF(soundQueue); temp2++) { if (soundQueue[temp2] != S_NONE) { temp = soundQueue[temp2]; if (temp2 == 3) temp3 = fxPlayVol; else if (temp == 15) temp3 = fxPlayVol / 4; else /*Lightning*/ temp3 = fxPlayVol / 2; multiSamplePlay(soundSamples[temp-1], soundSampleCount[temp-1], temp2, temp3); soundQueue[temp2] = S_NONE; } } } if (returnActive && enemyOnScreen == 0) { JE_eventJump(65535); returnActive = false; } /*------- DEbug ---------*/ debugTime = SDL_GetTicks(); tempW = lastmouse_but; tempX = mouse_x; tempY = mouse_y; if (debug) { for (size_t i = 0; i < 9; i++) { tempStr[i] = '0' + smoothies[i]; } tempStr[9] = '\0'; sprintf(buffer, "SM = %s", tempStr); JE_outText(VGAScreen, 30, 70, buffer, 4, 0); sprintf(buffer, "Memory left = %d", -1); JE_outText(VGAScreen, 30, 80, buffer, 4, 0); sprintf(buffer, "Enemies onscreen = %d", enemyOnScreen); JE_outText(VGAScreen, 30, 90, buffer, 6, 0); debugHist = debugHist + abs((JE_longint)debugTime - (JE_longint)lastDebugTime); debugHistCount++; sprintf(tempStr, "%2.3f", 1000.0f / roundf(debugHist / debugHistCount)); sprintf(buffer, "X:%d Y:%-5d %s FPS %d %d %d %d", (mapX - 1) * 12 + player[0].x, curLoc, tempStr, player[0].x_velocity, player[0].y_velocity, player[0].x, player[0].y); JE_outText(VGAScreen, 45, 175, buffer, 15, 3); lastDebugTime = debugTime; } if (displayTime > 0) { displayTime--; JE_outTextAndDarken(VGAScreen, 90, 10, miscText[59], 15, (JE_byte)flash - 8, FONT_SHAPES); flash += flashChange; if (flash > 4 || flash == 0) flashChange = -flashChange; } /*Pentium Speed Mode?*/ if (pentiumMode) { frameCountMax = (frameCountMax == 2) ? 3 : 2; } /*-------- Level Timer ---------*/ if (levelTimer && levelTimerCountdown > 0) { levelTimerCountdown--; if (levelTimerCountdown == 0) JE_eventJump(levelTimerJumpTo); if (levelTimerCountdown > 200) { if (levelTimerCountdown % 100 == 0) soundQueue[7] = S_WARNING; if (levelTimerCountdown % 10 == 0) soundQueue[6] = S_CLICK; } else if (levelTimerCountdown % 20 == 0) { soundQueue[7] = S_WARNING; } JE_textShade (VGAScreen, 140, 6, miscText[66], 7, (levelTimerCountdown % 20) / 3, FULL_SHADE); sprintf(buffer, "%.1f", levelTimerCountdown / 100.0f); JE_dString (VGAScreen, 100, 2, buffer, SMALL_FONT_SHAPES); } /*GAME OVER*/ if (!constantPlay && !constantDie) { if (allPlayersGone) { if (player[0].exploding_ticks > 0 || player[1].exploding_ticks > 0) { if (galagaMode) player[1].exploding_ticks = 0; musicFade = true; } else { if (play_demo || normalBonusLevelCurrent || bonusLevelCurrent) reallyEndLevel = true; else JE_dString(VGAScreen, 120, 60, miscText[21], FONT_SHAPES); // game over if (firstGameOver) { if (!play_demo) { play_song(SONG_GAMEOVER); set_volume(tyrMusicVolume, fxVolume); } firstGameOver = false; } if (!play_demo) { push_joysticks_as_keyboard(); service_SDL_events(true); if ((newkey || button[0] || button[1] || button[2]) || newmouse) { reallyEndLevel = true; } } if (isNetworkGame) reallyEndLevel = true; } } } if (play_demo) // input kills demo { push_joysticks_as_keyboard(); service_SDL_events(false); if (newkey || newmouse) { reallyEndLevel = true; stopped_demo = true; } } else // input handling for pausing, menu, cheats { service_SDL_events(false); if (newkey) { skipStarShowVGA = false; JE_mainKeyboardInput(); newkey = false; if (skipStarShowVGA) goto level_loop; } if (pause_pressed || !windowHasFocus) { pause_pressed = false; if (isNetworkGame) pauseRequest = true; else JE_pauseGame(); } if (ingamemenu_pressed) { ingamemenu_pressed = false; if (isNetworkGame) { inGameMenuRequest = true; } else { yourInGameMenuRequest = true; JE_doInGameSetup(); skipStarShowVGA = true; } } } /*Network Update*/ #ifdef WITH_NETWORK if (isNetworkGame) { if (!reallyEndLevel) { Uint16 requests = (pauseRequest == true) | (inGameMenuRequest == true) << 1 | (skipLevelRequest == true) << 2 | (nortShipRequest == true) << 3; SDLNet_Write16(requests, &packet_state_out[0]->data[14]); SDLNet_Write16(difficultyLevel, &packet_state_out[0]->data[16]); SDLNet_Write16(player[0].x, &packet_state_out[0]->data[18]); SDLNet_Write16(player[1].x, &packet_state_out[0]->data[20]); SDLNet_Write16(player[0].y, &packet_state_out[0]->data[22]); SDLNet_Write16(player[1].y, &packet_state_out[0]->data[24]); SDLNet_Write16(curLoc, &packet_state_out[0]->data[26]); network_state_send(); if (network_state_update()) { assert(SDLNet_Read16(&packet_state_in[0]->data[26]) == SDLNet_Read16(&packet_state_out[network_delay]->data[26])); requests = SDLNet_Read16(&packet_state_in[0]->data[14]) ^ SDLNet_Read16(&packet_state_out[network_delay]->data[14]); if (requests & 1) { JE_pauseGame(); } if (requests & 2) { yourInGameMenuRequest = SDLNet_Read16(&packet_state_out[network_delay]->data[14]) & 2; JE_doInGameSetup(); yourInGameMenuRequest = false; if (haltGame) reallyEndLevel = true; } if (requests & 4) { levelTimer = true; levelTimerCountdown = 0; endLevel = true; levelEnd = 40; } if (requests & 8) // nortship { player[0].items.ship = 12; // Nort Ship player[0].items.special = 13; // Astral Zone player[0].items.weapon[FRONT_WEAPON].id = 36; // NortShip Super Pulse player[0].items.weapon[REAR_WEAPON].id = 37; // NortShip Spreader shipGr = 1; } for (int i = 0; i < 2; i++) { if (SDLNet_Read16(&packet_state_in[0]->data[18 + i * 2]) != SDLNet_Read16(&packet_state_out[network_delay]->data[18 + i * 2]) || SDLNet_Read16(&packet_state_in[0]->data[20 + i * 2]) != SDLNet_Read16(&packet_state_out[network_delay]->data[20 + i * 2])) { char temp[64]; sprintf(temp, "Player %d is unsynchronized!", i + 1); JE_textShade(game_screen, 40, 110 + i * 10, temp, 9, 2, FULL_SHADE); } } } } JE_clearSpecialRequests(); } #endif /** Test **/ JE_drawSP(); /*Filtration*/ if (filterActive) { JE_filterScreen(levelFilter, levelBrightness); } draw_boss_bar(); JE_inGameDisplays(); VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ JE_starShowVGA(); /*Start backgrounds if no enemies on screen End level if number of enemies left to kill equals 0.*/ if (stopBackgroundNum == 9 && backMove == 0 && !enemyStillExploding) { backMove = 1; backMove2 = 2; backMove3 = 3; explodeMove = 2; stopBackgroundNum = 0; stopBackgrounds = false; if (waitToEndLevel) { endLevel = true; levelEnd = 40; } if (allPlayersGone) { reallyEndLevel = true; } } if (!endLevel && enemyOnScreen == 0) { if (readyToEndLevel && !enemyStillExploding) { if (levelTimerCountdown > 0) { levelTimer = false; } readyToEndLevel = false; endLevel = true; levelEnd = 40; if (allPlayersGone) { reallyEndLevel = true; } } if (stopBackgrounds) { stopBackgrounds = false; backMove = 1; backMove2 = 2; backMove3 = 3; explodeMove = 2; } } /*Other Network Functions*/ JE_handleChat(); if (reallyEndLevel) { goto start_level; } goto level_loop; } /* --- Load Level/Map Data --- */ void JE_loadMap(void) { JE_DanCShape shape; JE_word x, y; JE_integer yy; JE_word mapSh[3][128]; /* [1..3, 0..127] */ JE_byte *ref[3][128]; /* [1..3, 0..127] */ char s[256]; JE_byte mapBuf[15 * 600]; /* [1..15 * 600] */ JE_word bufLoc; char buffer[256]; int i; Uint8 pic_buffer[320*200]; /* screen buffer, 8-bit specific */ Uint8 *vga, *pic, *vga2; /* screen pointer, 8-bit specific */ lastCubeMax = cubeMax; /*Defaults*/ songBuy = DEFAULT_SONG_BUY; /*Item Screen default song*/ /* Load LEVELS.DAT - Section = MAINLEVEL */ saveLevel = mainLevel; new_game: galagaMode = false; useLastBank = false; extraGame = false; haltGame = false; gameLoaded = false; if (!play_demo) { do { FILE *ep_f = dir_fopen_die(data_dir(), episode_file, "rb"); jumpSection = false; loadLevelOk = false; /* Seek Section # Mainlevel */ int x = 0; while (x < mainLevel) { read_encrypted_pascal_string(s, sizeof(s), ep_f); if (s[0] == '*') { x++; s[0] = ' '; } } ESCPressed = false; do { if (gameLoaded) { fclose(ep_f); if (mainLevel == 0) // if quit itemscreen return; // back to title screen else goto new_game; } strcpy(s, " "); read_encrypted_pascal_string(s, sizeof(s), ep_f); if (s[0] == ']') { switch (s[1]) { case 'A': JE_playAnim("tyrend.anm", 0, 7); break; case 'G': mapOrigin = atoi(s + 4); mapPNum = atoi(s + 7); for (i = 0; i < mapPNum; i++) { mapPlanet[i] = atoi(s + 1 + (i + 1) * 8); mapSection[i] = atoi(s + 4 + (i + 1) * 8); } break; case '?': temp = atoi(s + 4); for (i = 0; i < temp; i++) { cubeList[i] = atoi(s + 3 + (i + 1) * 4); } if (cubeMax > temp) cubeMax = temp; break; case '!': cubeMax = atoi(s + 4); /*Auto set CubeMax*/ break; case '+': temp = atoi(s + 4); cubeMax += temp; if (cubeMax > 4) cubeMax = 4; break; case 'g': galagaMode = true; /*GALAGA mode*/ player[1].items = player[0].items; player[1].items.weapon[REAR_WEAPON].id = 15; // Vulcan Cannon for (uint i = 0; i < COUNTOF(player[1].items.sidekick); ++i) player[1].items.sidekick[i] = 0; // None break; case 'x': extraGame = true; break; case 'e': // ENGAGE mode, used for mini-games doNotSaveBackup = true; constantDie = false; onePlayerAction = true; superTyrian = true; twoPlayerMode = false; player[0].cash = 0; player[0].items.ship = 13; // The Stalker 21.126 player[0].items.weapon[FRONT_WEAPON].id = 39; // Atomic RailGun player[0].items.weapon[REAR_WEAPON].id = 0; // None for (uint i = 0; i < COUNTOF(player[0].items.sidekick); ++i) player[0].items.sidekick[i] = 0; // None player[0].items.generator = 2; // Advanced MR-12 player[0].items.shield = 4; // Advanced Integrity Field player[0].items.special = 0; // None player[0].items.weapon[FRONT_WEAPON].power = 3; player[0].items.weapon[REAR_WEAPON].power = 1; break; case 'J': // section jump temp = atoi(s + 3); mainLevel = temp; jumpSection = true; break; case '2': // two-player section jump temp = atoi(s + 3); if (twoPlayerMode || onePlayerAction) { mainLevel = temp; jumpSection = true; } break; case 'w': // Stalker 21.126 section jump temp = atoi(s + 3); /*Allowed to go to Time War?*/ if (player[0].items.ship == 13) { mainLevel = temp; jumpSection = true; } break; case 't': temp = atoi(s + 3); if (levelTimer && levelTimerCountdown == 0) { mainLevel = temp; jumpSection = true; } break; case 'l': temp = atoi(s + 3); if (!all_players_alive()) { mainLevel = temp; jumpSection = true; } break; case 's': saveLevel = mainLevel; break; /*store savepoint*/ case 'b': if (twoPlayerMode) temp = 22; else temp = 11; JE_saveGame(11, "LAST LEVEL "); break; case 'i': temp = atoi(s + 3); songBuy = temp - 1; break; case 'I': /*Load Items Available Information*/ memset(&itemAvail, 0, sizeof(itemAvail)); for (int i = 0; i < 9; ++i) { read_encrypted_pascal_string(s, sizeof(s), ep_f); char buf[256]; strncpy(buf, (strlen(s) > 8) ? s + 8 : "", sizeof(buf)); int j = 0, temp; while (str_pop_int(buf, &temp)) itemAvail[i][j++] = temp; itemAvailMax[i] = j; } JE_itemScreen(); break; case 'L': nextLevel = atoi(s + 9); SDL_strlcpy(levelName, s + 13, 10); levelSong = atoi(s + 22); if (nextLevel == 0) { nextLevel = mainLevel + 1; } lvlFileNum = atoi(s + 25); loadLevelOk = true; bonusLevelCurrent = (strlen(s) > 28) & (s[28] == '$'); normalBonusLevelCurrent = (strlen(s) > 27) & (s[27] == '$'); gameJustLoaded = false; break; case '@': useLastBank = !useLastBank; break; case 'Q': ESCPressed = false; temp = secretHint + (mt_rand() % 3) * 3; if (twoPlayerMode) { for (uint i = 0; i < 2; ++i) snprintf(levelWarningText[i], sizeof(*levelWarningText), "%s %lu", miscText[40], player[i].cash); strcpy(levelWarningText[2], ""); levelWarningLines = 3; } else { sprintf(levelWarningText[0], "%s %lu", miscText[37], JE_totalScore(&player[0])); strcpy(levelWarningText[1], ""); levelWarningLines = 2; } for (x = 0; x < temp - 1; x++) { do { read_encrypted_pascal_string(s, sizeof(s), ep_f); } while (s[0] != '#'); } do { read_encrypted_pascal_string(s, sizeof(s), ep_f); strcpy(levelWarningText[levelWarningLines], s); levelWarningLines++; } while (s[0] != '#'); levelWarningLines--; JE_wipeKey(); frameCountMax = 4; if (!constantPlay) JE_displayText(); fade_black(15); JE_nextEpisode(); if (jumpBackToEpisode1 && !twoPlayerMode) { JE_loadPic(VGAScreen, 1, false); // huh? JE_clr256(VGAScreen); if (superTyrian) { // if completed Zinglon's Revenge, show SuperTyrian and Destruct codes // if completed SuperTyrian, show Nort-Ship Z code superArcadeMode = (initialDifficulty == DIFFICULTY_ZINGLON) ? 8 : 1; } if (superArcadeMode < SA_ENGAGE) { if (SANextShip[superArcadeMode] == SA_ENGAGE) { sprintf(buffer, "%s %s", miscTextB[4], pName[0]); JE_dString(VGAScreen, JE_fontCenter(buffer, FONT_SHAPES), 100, buffer, FONT_SHAPES); sprintf(buffer, "Or play... %s", specialName[7]); JE_dString(VGAScreen, 80, 180, buffer, SMALL_FONT_SHAPES); } else { JE_dString(VGAScreen, JE_fontCenter(superShips[0], FONT_SHAPES), 30, superShips[0], FONT_SHAPES); JE_dString(VGAScreen, JE_fontCenter(superShips[SANextShip[superArcadeMode]], SMALL_FONT_SHAPES), 100, superShips[SANextShip[superArcadeMode]], SMALL_FONT_SHAPES); } if (SANextShip[superArcadeMode] < SA_NORTSHIPZ) blit_sprite2x2(VGAScreen, 148, 70, spriteSheet9, ships[SAShip[SANextShip[superArcadeMode]-1]].shipgraphic); else if (SANextShip[superArcadeMode] == SA_NORTSHIPZ) trentWin = true; sprintf(buffer, "Type %s at Title", specialName[SANextShip[superArcadeMode]-1]); JE_dString(VGAScreen, JE_fontCenter(buffer, SMALL_FONT_SHAPES), 160, buffer, SMALL_FONT_SHAPES); JE_showVGA(); fade_palette(colors, 50, 0, 255); if (!constantPlay) wait_input(true, true, true); } jumpSection = true; if (isNetworkGame) JE_readTextSync(); if (superTyrian) { fade_black(10); // back to titlescreen mainLevel = 0; return; } } break; case 'P': if (!constantPlay) { tempX = atoi(s + 3); if (tempX > 900) { memcpy(colors, palettes[pcxpal[tempX-1 - 900]], sizeof(colors)); JE_clr256(VGAScreen); JE_showVGA(); fade_palette(colors, 1, 0, 255); } else { if (tempX == 0) JE_loadPCX("tshp2.pcx"); else JE_loadPic(VGAScreen, tempX, false); JE_showVGA(); fade_palette(colors, 10, 0, 255); } } break; case 'U': if (!constantPlay) { memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); tempX = atoi(s + 3); JE_loadPic(VGAScreen, tempX, false); memcpy(pic_buffer, VGAScreen->pixels, sizeof(pic_buffer)); service_SDL_events(true); for (int z = 0; z <= 199; z++) { if (!newkey) { vga = VGAScreen->pixels; vga2 = VGAScreen2->pixels; pic = pic_buffer + (199 - z) * 320; setDelay(1); for (y = 0; y <= 199; y++) { if (y <= z) { memcpy(vga, pic, 320); pic += 320; } else { memcpy(vga, vga2, VGAScreen->pitch); vga2 += VGAScreen->pitch; } vga += VGAScreen->pitch; } JE_showVGA(); if (isNetworkGame) { /* TODO: NETWORK */ } service_wait_delay(); } } memcpy(VGAScreen->pixels, pic_buffer, sizeof(pic_buffer)); } break; case 'V': if (!constantPlay) { /* TODO: NETWORK */ memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); tempX = atoi(s + 3); JE_loadPic(VGAScreen, tempX, false); memcpy(pic_buffer, VGAScreen->pixels, sizeof(pic_buffer)); service_SDL_events(true); for (int z = 0; z <= 199; z++) { if (!newkey) { vga = VGAScreen->pixels; vga2 = VGAScreen2->pixels; pic = pic_buffer; setDelay(1); for (y = 0; y < 199; y++) { if (y <= 199 - z) { memcpy(vga, vga2, VGAScreen->pitch); vga2 += VGAScreen->pitch; } else { memcpy(vga, pic, 320); pic += 320; } vga += VGAScreen->pitch; } JE_showVGA(); if (isNetworkGame) { /* TODO: NETWORK */ } service_wait_delay(); } } memcpy(VGAScreen->pixels, pic_buffer, sizeof(pic_buffer)); } break; case 'R': if (!constantPlay) { /* TODO: NETWORK */ memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); tempX = atoi(s + 3); JE_loadPic(VGAScreen, tempX, false); memcpy(pic_buffer, VGAScreen->pixels, sizeof(pic_buffer)); service_SDL_events(true); for (int z = 0; z <= 318; z++) { if (!newkey) { vga = VGAScreen->pixels; vga2 = VGAScreen2->pixels; pic = pic_buffer; setDelay(1); for (y = 0; y < 200; y++) { memcpy(vga, vga2 + z, 319 - z); vga += 320 - z; vga2 += VGAScreen2->pitch; memcpy(vga, pic, z + 1); vga += z; pic += 320; } JE_showVGA(); if (isNetworkGame) { /* TODO: NETWORK */ } service_wait_delay(); } } memcpy(VGAScreen->pixels, pic_buffer, sizeof(pic_buffer)); } break; case 'C': if (!isNetworkGame) { fade_black(10); } JE_clr256(VGAScreen); JE_showVGA(); memcpy(colors, palettes[7], sizeof(colors)); set_palette(colors, 0, 255); break; case 'B': if (!isNetworkGame) { fade_black(10); } break; case 'F': if (!isNetworkGame) { fade_white(100); fade_black(30); } JE_clr256(VGAScreen); JE_showVGA(); break; case 'W': if (!constantPlay) { if (!ESCPressed) { JE_wipeKey(); warningCol = 14 * 16 + 5; warningColChange = 1; warningSoundDelay = 0; levelWarningDisplay = (s[2] == 'y'); levelWarningLines = 0; frameCountMax = atoi(s + 4); setDelay2(6); warningRed = frameCountMax / 10; frameCountMax = frameCountMax % 10; do { read_encrypted_pascal_string(s, sizeof(s), ep_f); if (s[0] != '#') { strcpy(levelWarningText[levelWarningLines], s); levelWarningLines++; } } while (!(s[0] == '#')); JE_displayText(); newkey = false; } } break; case 'H': if (initialDifficulty < DIFFICULTY_HARD) { mainLevel = atoi(s + 4); jumpSection = true; } break; case 'h': if (initialDifficulty > DIFFICULTY_NORMAL) { read_encrypted_pascal_string(s, sizeof(s), ep_f); } break; case 'S': if (isNetworkGame) { JE_readTextSync(); } break; case 'n': ESCPressed = false; break; case 'M': temp = atoi(s + 3); play_song(temp - 1); break; } } } while (!(loadLevelOk || jumpSection)); fclose(ep_f); } while (!loadLevelOk); } if (play_demo) load_next_demo(); else fade_black(50); FILE *level_f = dir_fopen_die(data_dir(), levelFile, "rb"); fseek(level_f, lvlPos[(lvlFileNum-1) * 2], SEEK_SET); JE_char char_mapFile; JE_char char_shapeFile; fread_die(&char_mapFile, 1, 1, level_f); fread_die(&char_shapeFile, 1, 1, level_f); fread_u16_die(&mapX, 1, level_f); fread_u16_die(&mapX2, 1, level_f); fread_u16_die(&mapX3, 1, level_f); fread_u16_die(&levelEnemyMax, 1, level_f); fread_u16_die(levelEnemy, levelEnemyMax, level_f); fread_u16_die(&maxEvent, 1, level_f); for (x = 0; x < maxEvent; x++) { fread_u16_die(&eventRec[x].eventtime, 1, level_f); fread_u8_die( &eventRec[x].eventtype, 1, level_f); fread_s16_die(&eventRec[x].eventdat, 1, level_f); fread_s16_die(&eventRec[x].eventdat2, 1, level_f); fread_s8_die( &eventRec[x].eventdat3, 1, level_f); fread_s8_die( &eventRec[x].eventdat5, 1, level_f); fread_s8_die( &eventRec[x].eventdat6, 1, level_f); fread_u8_die( &eventRec[x].eventdat4, 1, level_f); } eventRec[x].eventtime = 65500; /*Not needed but just in case*/ /*debuginfo('Level loaded.');*/ /*debuginfo('Loading Map');*/ /* MAP SHAPE LOOKUP TABLE - Each map is directly after level */ for (temp = 0; temp < 3; temp++) { fread_u16_die(mapSh[temp], sizeof(*mapSh) / sizeof(JE_word), level_f); for (temp2 = 0; temp2 < 128; temp2++) { mapSh[temp][temp2] = SDL_Swap16(mapSh[temp][temp2]); } } /* Read Shapes.DAT */ sprintf(tempStr, "shapes%c.dat", tolower((unsigned char)char_shapeFile)); FILE *shpFile = dir_fopen_die(data_dir(), tempStr, "rb"); for (int z = 0; z < 600; z++) { JE_boolean shapeBlank; fread_bool_die(&shapeBlank, shpFile); if (shapeBlank) memset(shape, 0, sizeof(shape)); else fread_u8_die(shape, sizeof(shape), shpFile); /* Match 1 */ for (int x = 0; x <= 71; ++x) { if (mapSh[0][x] == z+1) { memcpy(megaData1.shapes[x].sh, shape, sizeof(JE_DanCShape)); ref[0][x] = megaData1.shapes[x].sh; } } /* Match 2 */ for (int x = 0; x <= 71; ++x) { if (mapSh[1][x] == z+1) { if (x != 71 && !shapeBlank) { memcpy(megaData2.shapes[x].sh, shape, sizeof(JE_DanCShape)); y = 1; for (yy = 0; yy < (24 * 28) >> 1; yy++) if (shape[yy] == 0) y = 0; megaData2.shapes[x].fill = y; ref[1][x] = megaData2.shapes[x].sh; } else { ref[1][x] = NULL; } } } /*Match 3*/ for (int x = 0; x <= 71; ++x) { if (mapSh[2][x] == z+1) { if (x < 70 && !shapeBlank) { memcpy(megaData3.shapes[x].sh, shape, sizeof(JE_DanCShape)); y = 1; for (yy = 0; yy < (24 * 28) >> 1; yy++) if (shape[yy] == 0) y = 0; megaData3.shapes[x].fill = y; ref[2][x] = megaData3.shapes[x].sh; } else { ref[2][x] = NULL; } } } } fclose(shpFile); fread_u8_die(mapBuf, 14 * 300, level_f); bufLoc = 0; /* MAP NUMBER 1 */ for (y = 0; y < 300; y++) { for (x = 0; x < 14; x++) { megaData1.mainmap[y][x] = ref[0][mapBuf[bufLoc]]; bufLoc++; } } fread_u8_die(mapBuf, 14 * 600, level_f); bufLoc = 0; /* MAP NUMBER 2 */ for (y = 0; y < 600; y++) { for (x = 0; x < 14; x++) { megaData2.mainmap[y][x] = ref[1][mapBuf[bufLoc]]; bufLoc++; } } fread_u8_die(mapBuf, 15 * 600, level_f); bufLoc = 0; /* MAP NUMBER 3 */ for (y = 0; y < 600; y++) { for (x = 0; x < 15; x++) { megaData3.mainmap[y][x] = ref[2][mapBuf[bufLoc]]; bufLoc++; } } fclose(level_f); /* Note: The map data is automatically calculated with the correct mapsh value and then the pointer is calculated using the formula (MAPSH-1)*168. Then, we'll automatically add S2Ofs to get the exact offset location into the shape table! This makes it VERY FAST! */ /*debuginfo('Map file done.');*/ /* End of find loop for LEVEL??.DAT */ } #ifdef WITH_NETWORK void networkStartScreen(void) { JE_loadPic(VGAScreen, 2, false); memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); JE_dString(VGAScreen, JE_fontCenter("Waiting for other player.", SMALL_FONT_SHAPES), 140, "Waiting for other player.", SMALL_FONT_SHAPES); JE_showVGA(); fade_palette(colors, 10, 0, 255); network_connect(); twoPlayerMode = true; if (thisPlayerNum == 1) { fade_black(10); if (episodeSelect() && difficultySelect()) { initialDifficulty = difficultyLevel; difficultyLevel++; /*Make it one step harder for 2-player mode!*/ network_prepare(PACKET_DETAILS); SDLNet_Write16(episodeNum, &packet_out_temp->data[4]); SDLNet_Write16(difficultyLevel, &packet_out_temp->data[6]); network_send(8); // PACKET_DETAILS } else { network_prepare(PACKET_QUIT); network_send(4); // PACKET QUIT network_tyrian_halt(0, true); } } else { memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); JE_dString(VGAScreen, JE_fontCenter(networkText[4 - 1], SMALL_FONT_SHAPES), 140, networkText[4 - 1], SMALL_FONT_SHAPES); JE_showVGA(); // until opponent sends details packet while (true) { service_SDL_events(false); JE_showVGA(); if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_DETAILS) break; network_update(); network_check(); SDL_Delay(16); } JE_initEpisode(SDLNet_Read16(&packet_in[0]->data[4])); difficultyLevel = SDLNet_Read16(&packet_in[0]->data[6]); initialDifficulty = difficultyLevel - 1; fade_black(10); network_update(); } for (uint i = 0; i < COUNTOF(player); ++i) player[i].cash = 0; player[0].items.ship = 11; // Silver Ship while (!network_is_sync()) { service_SDL_events(false); JE_showVGA(); network_check(); SDL_Delay(16); } } #endif /* WITH_NETWORK */ bool titleScreen(void) { enum MenuItemIndex { MENU_ITEM_NEW_GAME = 0, MENU_ITEM_LOAD_GAME, MENU_ITEM_HIGH_SCORES, MENU_ITEM_INSTRUCTIONS, MENU_ITEM_SETUP, MENU_ITEM_DEMO, MENU_ITEM_QUIT, }; SDL_strlcpy(menuText[4], "Setup", sizeof menuText[4]); // override "Ordering Info" if (shopSpriteSheet.data == NULL) JE_loadCompShapes(&shopSpriteSheet, '1'); // need mouse pointer sprites bool restart = true; size_t selectedIndex = MENU_ITEM_NEW_GAME; size_t specialNameProgress[SA_ENGAGE] = { 0 }; const int xCenter = VGAScreen->w / 2; const int yMenuItems = 104; const int hMenuItem = 13; int wMenuItem[COUNTOF(menuText)] = { 0 }; for (; ; ) { if (restart) { play_song(SONG_TITLE); JE_loadPic(VGAScreen, 4, false); draw_font_hv_shadow(VGAScreen, 2, 192, opentyrian_version, small_font, left_aligned, 15, 0, false, 1); if (moveTyrianLogoUp) { memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); blit_sprite(VGAScreenSeg, 11, 62, PLANET_SHAPES, 146); // tyrian logo fade_palette(colors, 10, 0, 255 - 16); for (int y = 60; y >= 4; y -= 2) { setDelay(2); memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); blit_sprite(VGAScreenSeg, 11, y, PLANET_SHAPES, 146); // tyrian logo JE_showVGA(); service_wait_delay(); } moveTyrianLogoUp = false; } else { blit_sprite(VGAScreenSeg, 11, 4, PLANET_SHAPES, 146); // tyrian logo fade_palette(colors, 10, 0, 255 - 16); } // Draw menu items. for (size_t i = 0; i < COUNTOF(menuText); ++i) { const char *const text = menuText[i]; wMenuItem[i] = JE_textWidth(text, normal_font); const int x = xCenter - wMenuItem[i] / 2; const int y = yMenuItems + hMenuItem * i; draw_font_hv(VGAScreen, x - 1, y - 1, menuText[i], normal_font, left_aligned, 15, -10); draw_font_hv(VGAScreen, x + 1, y + 1, menuText[i], normal_font, left_aligned, 15, -10); draw_font_hv(VGAScreen, x + 1, y - 1, menuText[i], normal_font, left_aligned, 15, -10); draw_font_hv(VGAScreen, x - 1, y + 1, menuText[i], normal_font, left_aligned, 15, -10); draw_font_hv(VGAScreen, x, y, menuText[i], normal_font, left_aligned, 15, -3); } memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); mouseCursor = MOUSE_POINTER_NORMAL; // Fade in menu items. fade_palette(colors, 20, 255 - 16 + 1, 255); restart = false; } memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); // Highlight selected menu item. draw_font_hv(VGAScreen, VGAScreen->w / 2, yMenuItems + hMenuItem * selectedIndex, menuText[selectedIndex], normal_font, centered, 15, -1); service_SDL_events(true); JE_mouseStartFilter(0xF0); JE_showVGA(); JE_mouseReplace(); const Uint32 idleStartTick = SDL_GetTicks(); bool mouseMoved = false; do { // Play demo after idle for 30 seconds. if (SDL_GetTicks() - idleStartTick > 30000) { fade_black(15); play_demo = true; return true; } SDL_Delay(16); Uint16 oldMouseX = mouse_x; Uint16 oldMouseY = mouse_y; push_joysticks_as_keyboard(); service_SDL_events(false); mouseMoved = mouse_x != oldMouseX || mouse_y != oldMouseY; } while (!(newkey || new_text || newmouse || mouseMoved)); // Handle interaction. bool action = false; bool done = false; if (mouseMoved || newmouse) { // Find menu item that was hovered or clicked. for (size_t i = 0; i < COUNTOF(menuText); ++i) { const int xMenuItem = xCenter - wMenuItem[i] / 2; if (mouse_x >= xMenuItem && mouse_x < xMenuItem + wMenuItem[i]) { const int yMenuItem = yMenuItems + hMenuItem * i; if (mouse_y >= yMenuItem && mouse_y < yMenuItem + hMenuItem) { if (selectedIndex != i) { JE_playSampleNum(S_CURSOR); selectedIndex = i; } if (newmouse && lastmouse_but == SDL_BUTTON_LEFT && lastmouse_x >= xMenuItem && lastmouse_x < xMenuItem + wMenuItem[i] && lastmouse_y >= yMenuItem && lastmouse_y < yMenuItem + hMenuItem) { action = true; } break; } } } } if (newmouse) { if (lastmouse_but == SDL_BUTTON_RIGHT) { JE_playSampleNum(S_SPRING); done = true; } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_UP: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == 0 ? COUNTOF(menuText) - 1 : selectedIndex - 1; break; } case SDL_SCANCODE_DOWN: { JE_playSampleNum(S_CURSOR); selectedIndex = selectedIndex == COUNTOF(menuText) - 1 ? 0 : selectedIndex + 1; break; } case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: { action = true; break; } case SDL_SCANCODE_ESCAPE: { JE_playSampleNum(S_SPRING); done = true; } default: break; } } if (new_text) { for (size_t ti = 0U; last_text[ti] != '\0'; ++ti) { const char c = toupper(last_text[ti]); for (size_t i = 0; i < SA_ENGAGE; i++) { if (specialNameProgress[i] >= COUNTOF(specialName[i]) - 1 || c != specialName[i][specialNameProgress[i]]) { specialNameProgress[i] = 0; continue; } specialNameProgress[i]++; if (specialName[i][specialNameProgress[i]] == '\0') { if (i + 1 == SA_DESTRUCT) { fade_black(10); loadDestruct = true; return true; } else if (i + 1 == SA_ENGAGE) { JE_playSampleNum(V_DATA_CUBE); JE_whoa(); set_colors((SDL_Color) { 0, 0, 0 }, 0, 255); newSuperTyrianGame(); return true; } else { fade_black(10); if (newSuperArcadeGame(i)) return true; restart = true; } } } } } if (action) { JE_playSampleNum(S_SELECT); switch (selectedIndex) { case MENU_ITEM_NEW_GAME: { fade_black(15); if (newGame()) return true; restart = true; break; } case MENU_ITEM_LOAD_GAME: { fade_black(15); if (JE_loadScreen()) return true; restart = true; break; } case MENU_ITEM_HIGH_SCORES: { fade_black(15); JE_highScoreScreen(); restart = true; break; } case MENU_ITEM_INSTRUCTIONS: { fade_black(15); JE_helpSystem(1); restart = true; break; } case MENU_ITEM_SETUP: { fade_black(15); setupMenu(); restart = true; break; } case MENU_ITEM_DEMO: { fade_black(15); play_demo = true; return true; } case MENU_ITEM_QUIT: { fade_black(15); return false; } default: break; } } if (done) { fade_black(15); return false; } } } bool newGame(void) { if (gameplaySelect()) { if (episodeSelect() && difficultySelect()) gameLoaded = true; initialDifficulty = difficultyLevel; if (onePlayerAction) { player[0].cash = 0; player[0].items.ship = 8; // Stalker } else if (twoPlayerMode) { for (uint i = 0; i < COUNTOF(player); ++i) player[i].cash = 0; player[0].items.ship = 11; // Silver Ship difficultyLevel++; inputDevice[0] = 1; inputDevice[1] = 2; } else if (richMode) { player[0].cash = 1000000; } else if (gameLoaded) { // allows player to smuggle arcade/super-arcade ships into full game const ulong initial_cash[] = { 10000, 15000, 20000, 30000 }; assert(episodeNum >= 1 && episodeNum <= EPISODE_AVAILABLE); player[0].cash = initial_cash[episodeNum - 1]; } } return gameLoaded; } bool newSuperArcadeGame(unsigned int i) { player[0].items.ship = SAShip[i]; if (episodeSelect() && difficultySelect()) { /* Start special mode! */ JE_loadPic(VGAScreen, 1, false); JE_clr256(VGAScreen); JE_dString(VGAScreen, JE_fontCenter(superShips[0], FONT_SHAPES), 30, superShips[0], FONT_SHAPES); JE_dString(VGAScreen, JE_fontCenter(superShips[i + 1], SMALL_FONT_SHAPES), 100, superShips[i + 1], SMALL_FONT_SHAPES); tempW = ships[player[0].items.ship].shipgraphic; if (tempW != 1) blit_sprite2x2(VGAScreen, 148, 70, spriteSheet9, tempW); JE_showVGA(); fade_palette(colors, 50, 0, 255); wait_input(true, true, true); twoPlayerMode = false; onePlayerAction = true; superArcadeMode = i + 1; gameLoaded = true; initialDifficulty = ++difficultyLevel; player[0].cash = 0; player[0].items.weapon[FRONT_WEAPON].id = SAWeapon[i][0]; player[0].items.special = SASpecialWeapon[i]; if (superArcadeMode == SA_NORTSHIPZ) { for (uint i = 0; i < COUNTOF(player[0].items.sidekick); ++i) player[0].items.sidekick[i] = 24; // Companion Ship Quicksilver } fade_black(10); } return gameLoaded; } void newSuperTyrianGame(void) { /* SuperTyrian */ initialDifficulty = keysactive[SDL_SCANCODE_SCROLLLOCK] ? DIFFICULTY_SUICIDE : DIFFICULTY_ZINGLON; JE_clr256(VGAScreen); JE_outText(VGAScreen, 10, 10, "Cheat codes have been disabled.", 15, 4); if (initialDifficulty == DIFFICULTY_ZINGLON) JE_outText(VGAScreen, 10, 20, "Difficulty level has been set to Lord of Game.", 15, 4); else JE_outText(VGAScreen, 10, 20, "Difficulty level has been set to Suicide.", 15, 4); JE_outText(VGAScreen, 10, 30, "It is imperative that you discover the special codes.", 15, 4); if (initialDifficulty == DIFFICULTY_ZINGLON) JE_outText(VGAScreen, 10, 40, "(Next time, for an easier challenge hold down SCROLL LOCK.)", 15, 4); JE_outText(VGAScreen, 10, 60, "Prepare to play...", 15, 4); char buf[10 + 1 + 15 + 1]; snprintf(buf, sizeof(buf), "%s %s", miscTextB[4], pName[0]); JE_dString(VGAScreen, JE_fontCenter(buf, FONT_SHAPES), 110, buf, FONT_SHAPES); play_song(16); JE_playSampleNum(V_DANGER); JE_showVGA(); fade_palette(colors, 10, 0, 255); wait_noinput(true, true, true); wait_input(true, true, true); JE_initEpisode(1); constantDie = false; superTyrian = true; onePlayerAction = true; gameLoaded = true; difficultyLevel = initialDifficulty; player[0].cash = 0; player[0].items.ship = 13; // The Stalker 21.126 player[0].items.weapon[FRONT_WEAPON].id = 39; // Atomic RailGun fade_black(10); } void intro_logos(void) { moveTyrianLogoUp = true; SDL_FillRect(VGAScreen, NULL, 0); fade_white(25); JE_loadPic(VGAScreen, 10, false); JE_showVGA(); fade_palette(colors, 25, 0, 255); setDelay(200); wait_delayorinput(); fade_black(10); JE_loadPic(VGAScreen, 12, false); JE_showVGA(); fade_palette(colors, 10, 0, 255); setDelay(200); wait_delayorinput(); fade_black(10); } void JE_readTextSync(void) { #if 0 // this function seems to be unnecessary JE_clr256(VGAScreen); JE_showVGA(); JE_loadPic(VGAScreen, 1, true); JE_barShade(VGAScreen, 3, 3, 316, 196); JE_barShade(VGAScreen, 1, 1, 318, 198); JE_dString(VGAScreen, 10, 160, "Waiting for other player.", SMALL_FONT_SHAPES); JE_showVGA(); /* TODO: NETWORK */ do { setjasondelay(2); /* TODO: NETWORK */ wait_delay(); } while (0 /* TODO: NETWORK */); #endif } void JE_displayText(void) { /* Display Warning Text */ tempY = 55; if (warningRed) { tempY = 2; } for (temp = 0; temp < levelWarningLines; temp++) { if (!ESCPressed) { JE_outCharGlow(10, tempY, levelWarningText[temp]); if (haltGame) { JE_tyrianHalt(5); } tempY += 10; } } if (frameCountMax != 0) { frameCountMax = 6; temp = 1; } else { temp = 0; } textGlowFont = TINY_FONT; tempW = 184; if (warningRed) tempW = 7 * 16 + 6; JE_outCharGlow(JE_fontCenter(miscText[4], TINY_FONT), tempW, miscText[4]); do { if (levelWarningDisplay) JE_updateWarning(VGAScreen); setDelay(1); NETWORK_KEEP_ALIVE(); wait_delay(); } while (!(JE_anyButton() || (frameCountMax == 0 && temp == 1) || ESCPressed)); levelWarningDisplay = false; } Sint16 JE_newEnemy(int enemyOffset, Uint16 eDatI, Sint16 uniqueShapeTableI) { for (int i = enemyOffset; i < enemyOffset + 25; ++i) { if (enemyAvail[i] == 1) { enemyAvail[i] = JE_makeEnemy(&enemy[i], eDatI, uniqueShapeTableI); return i + 1; } } return 0; } uint JE_makeEnemy(struct JE_SingleEnemyType *enemy, Uint16 eDatI, Sint16 uniqueShapeTableI) { uint avail; JE_byte shapeTableI; if (superArcadeMode != SA_NONE && eDatI == 534) eDatI = 533; if (uniqueShapeTableI > 0) { shapeTableI = uniqueShapeTableI; } else { shapeTableI = enemyDat[eDatI].shapebank; } Sprite2_array *sprite2s = NULL; if (shapeTableI == 21) { sprite2s = &spriteSheet11; // Coins&Gems } else if (shapeTableI == 26) { sprite2s = &spriteSheet10; // Two-Player Stuff } else { for (size_t i = 0; i < COUNTOF(enemySpriteSheetIds); ++i) if (shapeTableI == enemySpriteSheetIds[i]) sprite2s = &enemySpriteSheets[i]; } if (sprite2s != NULL) enemy->sprite2s = sprite2s; else // Use shape table value from previous enemy that occupied the enemy slot. (Ex. APPROACH.) fprintf(stderr, "warning: ignoring sprite from unloaded shape table %d\n", shapeTableI); enemy->enemydatofs = &enemyDat[eDatI]; enemy->mapoffset = 0; for (uint i = 0; i < 3; ++i) { enemy->eshotmultipos[i] = 0; } enemy->enemyground = (enemyDat[eDatI].explosiontype & 1) == 0; enemy->explonum = enemyDat[eDatI].explosiontype >> 1; enemy->launchfreq = enemyDat[eDatI].elaunchfreq; enemy->launchwait = enemyDat[eDatI].elaunchfreq; enemy->launchtype = enemyDat[eDatI].elaunchtype % 1000; enemy->launchspecial = enemyDat[eDatI].elaunchtype / 1000; enemy->xaccel = enemyDat[eDatI].xaccel; enemy->yaccel = enemyDat[eDatI].yaccel; enemy->xminbounce = -10000; enemy->xmaxbounce = 10000; enemy->yminbounce = -10000; enemy->ymaxbounce = 10000; /*Far enough away to be impossible to reach*/ for (uint i = 0; i < 3; ++i) { enemy->tur[i] = enemyDat[eDatI].tur[i]; } enemy->ani = enemyDat[eDatI].ani; enemy->animin = 1; switch (enemyDat[eDatI].animate) { case 0: enemy->enemycycle = 1; enemy->aniactive = 0; enemy->animax = 0; enemy->aniwhenfire = 0; break; case 1: enemy->enemycycle = 0; enemy->aniactive = 1; enemy->animax = 0; enemy->aniwhenfire = 0; break; case 2: enemy->enemycycle = 1; enemy->aniactive = 2; enemy->animax = enemy->ani; enemy->aniwhenfire = 2; break; } if (enemyDat[eDatI].startxc != 0) enemy->ex = enemyDat[eDatI].startx + (mt_rand() % (enemyDat[eDatI].startxc * 2)) - enemyDat[eDatI].startxc + 1; else enemy->ex = enemyDat[eDatI].startx + 1; if (enemyDat[eDatI].startyc != 0) enemy->ey = enemyDat[eDatI].starty + (mt_rand() % (enemyDat[eDatI].startyc * 2)) - enemyDat[eDatI].startyc + 1; else enemy->ey = enemyDat[eDatI].starty + 1; enemy->exc = enemyDat[eDatI].xmove; enemy->eyc = enemyDat[eDatI].ymove; enemy->excc = enemyDat[eDatI].xcaccel; enemy->eycc = enemyDat[eDatI].ycaccel; enemy->exccw = abs(enemy->excc); enemy->exccwmax = enemy->exccw; enemy->eyccw = abs(enemy->eycc); enemy->eyccwmax = enemy->eyccw; enemy->exccadd = (enemy->excc > 0) ? 1 : -1; enemy->eyccadd = (enemy->eycc > 0) ? 1 : -1; enemy->special = false; enemy->iced = 0; if (enemyDat[eDatI].xrev == 0) enemy->exrev = 100; else if (enemyDat[eDatI].xrev == -99) enemy->exrev = 0; else enemy->exrev = enemyDat[eDatI].xrev; if (enemyDat[eDatI].yrev == 0) enemy->eyrev = 100; else if (enemyDat[eDatI].yrev == -99) enemy->eyrev = 0; else enemy->eyrev = enemyDat[eDatI].yrev; enemy->exca = (enemy->xaccel > 0) ? 1 : -1; enemy->eyca = (enemy->yaccel > 0) ? 1 : -1; enemy->enemytype = eDatI; for (uint i = 0; i < 3; ++i) { if (enemy->tur[i] == 252) enemy->eshotwait[i] = 1; else if (enemy->tur[i] > 0) enemy->eshotwait[i] = 20; else enemy->eshotwait[i] = 255; } for (uint i = 0; i < 20; ++i) enemy->egr[i] = enemyDat[eDatI].egraphic[i]; enemy->size = enemyDat[eDatI].esize; enemy->linknum = 0; enemy->edamaged = enemyDat[eDatI].dani < 0; enemy->enemydie = enemyDat[eDatI].eenemydie; enemy->freq[1-1] = enemyDat[eDatI].freq[1-1]; enemy->freq[2-1] = enemyDat[eDatI].freq[2-1]; enemy->freq[3-1] = enemyDat[eDatI].freq[3-1]; enemy->edani = enemyDat[eDatI].dani; enemy->edgr = enemyDat[eDatI].dgr; enemy->edlevel = enemyDat[eDatI].dlevel; enemy->fixedmovey = 0; enemy->filter = 0x00; int tempValue = 0; if (enemyDat[eDatI].value > 1 && enemyDat[eDatI].value < 10000) { switch (difficultyLevel) { case -1: case DIFFICULTY_WIMP: tempValue = enemyDat[eDatI].value * 0.75f; break; case DIFFICULTY_EASY: case DIFFICULTY_NORMAL: tempValue = enemyDat[eDatI].value; break; case DIFFICULTY_HARD: tempValue = enemyDat[eDatI].value * 1.125f; break; case DIFFICULTY_IMPOSSIBLE: tempValue = enemyDat[eDatI].value * 1.5f; break; case DIFFICULTY_INSANITY: tempValue = enemyDat[eDatI].value * 2; break; case DIFFICULTY_SUICIDE: tempValue = enemyDat[eDatI].value * 2.5f; break; case DIFFICULTY_MANIACAL: case DIFFICULTY_ZINGLON: tempValue = enemyDat[eDatI].value * 4; break; case DIFFICULTY_NORTANEOUS: case DIFFICULTY_10: tempValue = enemyDat[eDatI].value * 8; break; } if (tempValue > 10000) tempValue = 10000; enemy->evalue = tempValue; } else { enemy->evalue = enemyDat[eDatI].value; } int tempArmor = 1; if (enemyDat[eDatI].armor > 0) { if (enemyDat[eDatI].armor != 255) { switch (difficultyLevel) { case -1: case DIFFICULTY_WIMP: tempArmor = enemyDat[eDatI].armor * 0.5f + 1; break; case DIFFICULTY_EASY: tempArmor = enemyDat[eDatI].armor * 0.75f + 1; break; case DIFFICULTY_NORMAL: tempArmor = enemyDat[eDatI].armor; break; case DIFFICULTY_HARD: tempArmor = enemyDat[eDatI].armor * 1.2f; break; case DIFFICULTY_IMPOSSIBLE: tempArmor = enemyDat[eDatI].armor * 1.5f; break; case DIFFICULTY_INSANITY: tempArmor = enemyDat[eDatI].armor * 1.8f; break; case DIFFICULTY_SUICIDE: tempArmor = enemyDat[eDatI].armor * 2; break; case DIFFICULTY_MANIACAL: tempArmor = enemyDat[eDatI].armor * 3; break; case DIFFICULTY_ZINGLON: tempArmor = enemyDat[eDatI].armor * 4; break; case DIFFICULTY_NORTANEOUS: case DIFFICULTY_10: tempArmor = enemyDat[eDatI].armor * 8; break; } if (tempArmor > 254) { tempArmor = 254; } } else { tempArmor = 255; } enemy->armorleft = tempArmor; avail = 0; enemy->scoreitem = false; } else { avail = 2; enemy->armorleft = 255; if (enemy->evalue != 0) enemy->scoreitem = true; } if (!enemy->scoreitem) { totalEnemy++; /*Destruction ratio*/ } /* indicates what to set ENEMYAVAIL to */ return avail; } void JE_createNewEventEnemy(JE_byte enemyTypeOfs, JE_word enemyOffset, Sint16 uniqueShapeTableI) { int i; b = 0; for (i = enemyOffset; i < enemyOffset + 25; i++) { if (enemyAvail[i] == 1) { b = i + 1; break; } } if (b == 0) return; tempW = eventRec[eventLoc-1].eventdat + enemyTypeOfs; enemyAvail[b-1] = JE_makeEnemy(&enemy[b-1], tempW, uniqueShapeTableI); if (eventRec[eventLoc-1].eventdat2 != -99) { switch (enemyOffset) { case 0: enemy[b-1].ex = eventRec[eventLoc-1].eventdat2 - (mapX - 1) * 24; enemy[b-1].ey -= backMove2; break; case 25: case 75: enemy[b-1].ex = eventRec[eventLoc-1].eventdat2 - (mapX - 1) * 24 - 12; enemy[b-1].ey -= backMove; break; case 50: if (background3x1) enemy[b-1].ex = eventRec[eventLoc-1].eventdat2 - (mapX - 1) * 24 - 12; else enemy[b-1].ex = eventRec[eventLoc-1].eventdat2 - mapX3 * 24 - 24 * 2 + 6; enemy[b-1].ey -= backMove3; if (background3x1b) enemy[b-1].ex -= 6; break; } enemy[b-1].ey = -28; if (background3x1b && enemyOffset == 50) enemy[b-1].ey += 4; } if (smallEnemyAdjust && enemy[b-1].size == 0) { enemy[b-1].ex -= 10; enemy[b-1].ey -= 7; } enemy[b-1].ey += eventRec[eventLoc-1].eventdat5; enemy[b-1].eyc += eventRec[eventLoc-1].eventdat3; enemy[b-1].linknum = eventRec[eventLoc-1].eventdat4; enemy[b-1].fixedmovey = eventRec[eventLoc-1].eventdat6; } void JE_eventJump(JE_word jump) { JE_word tempW; if (jump == 65535) { curLoc = returnLoc; } else { returnLoc = curLoc + 1; curLoc = jump; } tempW = 0; do { tempW++; } while (!(eventRec[tempW-1].eventtime >= curLoc)); eventLoc = tempW - 1; } bool JE_searchFor/*enemy*/(JE_byte PLType, JE_byte* out_index) { int found_id = -1; for (int i = 0; i < 100; i++) { if (enemyAvail[i] == 0 && enemy[i].linknum == PLType) { found_id = i; if (galagaMode) enemy[i].evalue += enemy[i].evalue; } } if (found_id != -1) { if (out_index) *out_index = found_id; return true; } else { return false; } } void JE_eventSystem(void) { switch (eventRec[eventLoc-1].eventtype) { case 1: starfield_speed = eventRec[eventLoc-1].eventdat; break; case 2: map1YDelay = 1; map1YDelayMax = 1; map2YDelay = 1; map2YDelayMax = 1; backMove = eventRec[eventLoc-1].eventdat; backMove2 = eventRec[eventLoc-1].eventdat2; if (backMove2 > 0) explodeMove = backMove2; else explodeMove = backMove; backMove3 = eventRec[eventLoc-1].eventdat3; if (backMove > 0) stopBackgroundNum = 0; break; case 3: backMove = 1; map1YDelay = 3; map1YDelayMax = 3; backMove2 = 1; map2YDelay = 2; map2YDelayMax = 2; backMove3 = 1; break; case 4: stopBackgrounds = true; switch (eventRec[eventLoc-1].eventdat) { case 0: case 1: stopBackgroundNum = 1; break; case 2: stopBackgroundNum = 2; break; case 3: stopBackgroundNum = 3; break; } break; case 5: // load enemy shape banks { const Uint8 newEnemyShapeTables[] = { eventRec[eventLoc-1].eventdat > 0 ? eventRec[eventLoc-1].eventdat : 0, eventRec[eventLoc-1].eventdat2 > 0 ? eventRec[eventLoc-1].eventdat2 : 0, eventRec[eventLoc-1].eventdat3 > 0 ? eventRec[eventLoc-1].eventdat3 : 0, eventRec[eventLoc-1].eventdat4 > 0 ? eventRec[eventLoc-1].eventdat4 : 0, }; for (unsigned int i = 0; i < COUNTOF(newEnemyShapeTables); ++i) { if (enemySpriteSheetIds[i] != newEnemyShapeTables[i]) { if (newEnemyShapeTables[i] > 0) { assert(newEnemyShapeTables[i] <= COUNTOF(shapeFile)); JE_loadCompShapes(&enemySpriteSheets[i], shapeFile[newEnemyShapeTables[i] - 1]); } else free_sprite2s(&enemySpriteSheets[i]); enemySpriteSheetIds[i] = newEnemyShapeTables[i]; } } } break; case 6: /* Ground Enemy */ JE_createNewEventEnemy(0, 25, 0); break; case 7: /* Top Enemy */ JE_createNewEventEnemy(0, 50, 0); break; case 8: starActive = false; break; case 9: starActive = true; break; case 10: /* Ground Enemy 2 */ JE_createNewEventEnemy(0, 75, 0); break; case 11: if (allPlayersGone || eventRec[eventLoc-1].eventdat == 1) { reallyEndLevel = true; } else if (!endLevel) { readyToEndLevel = false; endLevel = true; levelEnd = 40; } break; case 12: /* Custom 4x4 Ground Enemy */ { uint temp = 0; switch (eventRec[eventLoc-1].eventdat6) { case 0: case 1: temp = 25; break; case 2: temp = 0; break; case 3: temp = 50; break; case 4: temp = 75; break; } eventRec[eventLoc-1].eventdat6 = 0; /* We use EVENTDAT6 for the background */ JE_createNewEventEnemy(0, temp, 0); JE_createNewEventEnemy(1, temp, 0); if (b > 0) enemy[b-1].ex += 24; JE_createNewEventEnemy(2, temp, 0); if (b > 0) enemy[b-1].ey -= 28; JE_createNewEventEnemy(3, temp, 0); if (b > 0) { enemy[b-1].ex += 24; enemy[b-1].ey -= 28; } break; } case 13: enemiesActive = false; break; case 14: enemiesActive = true; break; case 15: /* Sky Enemy */ JE_createNewEventEnemy(0, 0, 0); break; case 16: if (eventRec[eventLoc-1].eventdat > 9) { fprintf(stderr, "warning: event 16: bad event data\n"); } else { JE_drawTextWindow(outputs[eventRec[eventLoc-1].eventdat-1]); soundQueue[3] = windowTextSamples[eventRec[eventLoc-1].eventdat-1]; } break; case 17: /* Ground Bottom */ JE_createNewEventEnemy(0, 25, 0); if (b > 0) enemy[b-1].ey = 190 + eventRec[eventLoc-1].eventdat5; break; case 18: /* Sky Enemy on Bottom */ JE_createNewEventEnemy(0, 0, 0); if (b > 0) enemy[b-1].ey = 190 + eventRec[eventLoc-1].eventdat5; break; case 19: /* Enemy Global Move */ { int initial_i = 0, max_i = 0; bool all_enemies = false; if (eventRec[eventLoc-1].eventdat3 > 79 && eventRec[eventLoc-1].eventdat3 < 90) { initial_i = 0; max_i = 100; all_enemies = false; eventRec[eventLoc-1].eventdat4 = newPL[eventRec[eventLoc-1].eventdat3 - 80]; } else { switch (eventRec[eventLoc-1].eventdat3) { case 0: initial_i = 0; max_i = 100; all_enemies = false; break; case 2: initial_i = 0; max_i = 25; all_enemies = true; break; case 1: initial_i = 25; max_i = 50; all_enemies = true; break; case 3: initial_i = 50; max_i = 75; all_enemies = true; break; case 99: initial_i = 0; max_i = 100; all_enemies = true; break; } } for (int i = initial_i; i < max_i; i++) { if (all_enemies || enemy[i].linknum == eventRec[eventLoc-1].eventdat4) { if (eventRec[eventLoc-1].eventdat != -99) enemy[i].exc = eventRec[eventLoc-1].eventdat; if (eventRec[eventLoc-1].eventdat2 != -99) enemy[i].eyc = eventRec[eventLoc-1].eventdat2; if (eventRec[eventLoc-1].eventdat6 != 0) enemy[i].fixedmovey = eventRec[eventLoc-1].eventdat6; if (eventRec[eventLoc-1].eventdat6 == -99) enemy[i].fixedmovey = 0; if (eventRec[eventLoc-1].eventdat5 > 0) enemy[i].enemycycle = eventRec[eventLoc-1].eventdat5; } } break; } case 20: /* Enemy Global Accel */ if (eventRec[eventLoc-1].eventdat3 > 79 && eventRec[eventLoc-1].eventdat3 < 90) eventRec[eventLoc-1].eventdat4 = newPL[eventRec[eventLoc-1].eventdat3 - 80]; for (temp = 0; temp < 100; temp++) { if (enemyAvail[temp] != 1 && (enemy[temp].linknum == eventRec[eventLoc-1].eventdat4 || eventRec[eventLoc-1].eventdat4 == 0)) { if (eventRec[eventLoc-1].eventdat != -99) { enemy[temp].excc = eventRec[eventLoc-1].eventdat; enemy[temp].exccw = abs(eventRec[eventLoc-1].eventdat); enemy[temp].exccwmax = abs(eventRec[eventLoc-1].eventdat); if (eventRec[eventLoc-1].eventdat > 0) enemy[temp].exccadd = 1; else enemy[temp].exccadd = -1; } if (eventRec[eventLoc-1].eventdat2 != -99) { enemy[temp].eycc = eventRec[eventLoc-1].eventdat2; enemy[temp].eyccw = abs(eventRec[eventLoc-1].eventdat2); enemy[temp].eyccwmax = abs(eventRec[eventLoc-1].eventdat2); if (eventRec[eventLoc-1].eventdat2 > 0) enemy[temp].eyccadd = 1; else enemy[temp].eyccadd = -1; } if (eventRec[eventLoc-1].eventdat5 > 0) { enemy[temp].enemycycle = eventRec[eventLoc-1].eventdat5; } if (eventRec[eventLoc-1].eventdat6 > 0) { enemy[temp].ani = eventRec[eventLoc-1].eventdat6; enemy[temp].animin = eventRec[eventLoc-1].eventdat5; enemy[temp].animax = 0; enemy[temp].aniactive = 1; } } } break; case 21: background3over = 1; break; case 22: background3over = 0; break; case 23: /* Sky Enemy on Bottom */ JE_createNewEventEnemy(0, 50, 0); if (b > 0) enemy[b-1].ey = 180 + eventRec[eventLoc-1].eventdat5; break; case 24: /* Enemy Global Animate */ for (temp = 0; temp < 100; temp++) { if (enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) { enemy[temp].aniactive = 1; enemy[temp].aniwhenfire = 0; if (eventRec[eventLoc-1].eventdat2 > 0) { enemy[temp].enemycycle = eventRec[eventLoc-1].eventdat2; enemy[temp].animin = enemy[temp].enemycycle; } else { enemy[temp].enemycycle = 0; } if (eventRec[eventLoc-1].eventdat > 0) enemy[temp].ani = eventRec[eventLoc-1].eventdat; if (eventRec[eventLoc-1].eventdat3 == 1) { enemy[temp].animax = enemy[temp].ani; } else if (eventRec[eventLoc-1].eventdat3 == 2) { enemy[temp].aniactive = 2; enemy[temp].animax = enemy[temp].ani; enemy[temp].aniwhenfire = 2; } } } break; case 25: /* Enemy Global Damage change */ for (temp = 0; temp < 100; temp++) { if (eventRec[eventLoc-1].eventdat4 == 0 || enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) { if (galagaMode) enemy[temp].armorleft = roundf(eventRec[eventLoc-1].eventdat * (difficultyLevel / 2)); else enemy[temp].armorleft = eventRec[eventLoc-1].eventdat; } } break; case 26: smallEnemyAdjust = eventRec[eventLoc-1].eventdat; break; case 27: /* Enemy Global AccelRev */ if (eventRec[eventLoc-1].eventdat3 > 79 && eventRec[eventLoc-1].eventdat3 < 90) eventRec[eventLoc-1].eventdat4 = newPL[eventRec[eventLoc-1].eventdat3 - 80]; for (temp = 0; temp < 100; temp++) { if (eventRec[eventLoc-1].eventdat4 == 0 || enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) { if (eventRec[eventLoc-1].eventdat != -99) enemy[temp].exrev = eventRec[eventLoc-1].eventdat; if (eventRec[eventLoc-1].eventdat2 != -99) enemy[temp].eyrev = eventRec[eventLoc-1].eventdat2; if (eventRec[eventLoc-1].eventdat3 != 0 && eventRec[eventLoc-1].eventdat3 < 17) enemy[temp].filter = eventRec[eventLoc-1].eventdat3; } } break; case 28: topEnemyOver = false; break; case 29: topEnemyOver = true; break; case 30: map1YDelay = 1; map1YDelayMax = 1; map2YDelay = 1; map2YDelayMax = 1; backMove = eventRec[eventLoc-1].eventdat; backMove2 = eventRec[eventLoc-1].eventdat2; explodeMove = backMove2; backMove3 = eventRec[eventLoc-1].eventdat3; break; case 31: /* Enemy Fire Override */ for (temp = 0; temp < 100; temp++) { if (eventRec[eventLoc-1].eventdat4 == 99 || enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) { enemy[temp].freq[1-1] = eventRec[eventLoc-1].eventdat ; enemy[temp].freq[2-1] = eventRec[eventLoc-1].eventdat2; enemy[temp].freq[3-1] = eventRec[eventLoc-1].eventdat3; for (temp2 = 0; temp2 < 3; temp2++) { enemy[temp].eshotwait[temp2] = 1; } if (enemy[temp].launchtype > 0) { enemy[temp].launchfreq = eventRec[eventLoc-1].eventdat5; enemy[temp].launchwait = 1; } } } break; case 32: // create enemy JE_createNewEventEnemy(0, 50, 0); if (b > 0) enemy[b-1].ey = 190; break; case 33: /* Enemy From other Enemies */ if (!((eventRec[eventLoc-1].eventdat == 512 || eventRec[eventLoc-1].eventdat == 513) && (twoPlayerMode || onePlayerAction || superTyrian))) { if (superArcadeMode != SA_NONE) { if (eventRec[eventLoc-1].eventdat == 534) eventRec[eventLoc-1].eventdat = 827; } else if (!superTyrian) { const Uint8 lives = *player[0].lives; if (eventRec[eventLoc-1].eventdat == 533 && (lives == 11 || (mt_rand() % 15) < lives)) { // enemy will drop random special weapon eventRec[eventLoc-1].eventdat = 829 + (mt_rand() % 6); } } if (eventRec[eventLoc-1].eventdat == 534 && superTyrian) eventRec[eventLoc-1].eventdat = 828 + superTyrianSpecials[mt_rand() % 4]; for (temp = 0; temp < 100; temp++) { if (enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) enemy[temp].enemydie = eventRec[eventLoc-1].eventdat; } } break; case 34: /* Start Music Fade */ if (firstGameOver) { musicFade = true; tempVolume = tyrMusicVolume; } break; case 35: /* Play new song */ if (firstGameOver) { play_song(eventRec[eventLoc-1].eventdat - 1); set_volume(tyrMusicVolume, fxVolume); } musicFade = false; break; case 36: readyToEndLevel = true; break; case 37: levelEnemyFrequency = eventRec[eventLoc-1].eventdat; break; case 38: curLoc = eventRec[eventLoc-1].eventdat; int new_event_loc = 1; for (tempW = 0; tempW < maxEvent; tempW++) { if (eventRec[tempW].eventtime <= curLoc) new_event_loc = tempW+1 - 1; } eventLoc = new_event_loc; break; case 39: /* Enemy Global Linknum Change */ for (temp = 0; temp < 100; temp++) { if (enemy[temp].linknum == eventRec[eventLoc-1].eventdat) enemy[temp].linknum = eventRec[eventLoc-1].eventdat2; } break; case 40: /* Enemy Continual Damage */ enemyContinualDamage = true; break; case 41: if (eventRec[eventLoc-1].eventdat == 0) { memset(enemyAvail, 1, sizeof(enemyAvail)); } else { for (x = 0; x <= 24; x++) enemyAvail[x] = 1; } break; case 42: background3over = 2; break; case 43: background2over = eventRec[eventLoc-1].eventdat; break; case 44: filterActive = (eventRec[eventLoc-1].eventdat > 0); filterFade = (eventRec[eventLoc-1].eventdat == 2); levelFilter = eventRec[eventLoc-1].eventdat2; levelBrightness = eventRec[eventLoc-1].eventdat3; levelFilterNew = eventRec[eventLoc-1].eventdat4; levelBrightnessChg = eventRec[eventLoc-1].eventdat5; filterFadeStart = (eventRec[eventLoc-1].eventdat6 == 0); break; case 45: /* arcade-only enemy from other enemies */ if (!superTyrian) { const Uint8 lives = *player[0].lives; if (eventRec[eventLoc-1].eventdat == 533 && (lives == 11 || (mt_rand() % 15) < lives)) { eventRec[eventLoc-1].eventdat = 829 + (mt_rand() % 6); } if (twoPlayerMode || onePlayerAction) { for (temp = 0; temp < 100; temp++) { if (enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) enemy[temp].enemydie = eventRec[eventLoc-1].eventdat; } } } break; case 46: // change difficulty if (eventRec[eventLoc-1].eventdat3 != 0) damageRate = eventRec[eventLoc-1].eventdat3; if (eventRec[eventLoc-1].eventdat2 == 0 || twoPlayerMode || onePlayerAction) { difficultyLevel += eventRec[eventLoc-1].eventdat; if (difficultyLevel < DIFFICULTY_EASY) difficultyLevel = DIFFICULTY_EASY; if (difficultyLevel > DIFFICULTY_10) difficultyLevel = DIFFICULTY_10; } break; case 47: /* Enemy Global AccelRev */ for (temp = 0; temp < 100; temp++) { if (eventRec[eventLoc-1].eventdat4 == 0 || enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) enemy[temp].armorleft = eventRec[eventLoc-1].eventdat; } break; case 48: /* Background 2 Cannot be Transparent */ background2notTransparent = true; break; case 49: case 50: case 51: case 52: tempDat2 = eventRec[eventLoc-1].eventdat; eventRec[eventLoc-1].eventdat = 0; tempDat = eventRec[eventLoc-1].eventdat3; eventRec[eventLoc-1].eventdat3 = 0; tempDat3 = eventRec[eventLoc-1].eventdat6; eventRec[eventLoc-1].eventdat6 = 0; enemyDat[0].armor = tempDat3; enemyDat[0].egraphic[1-1] = tempDat2; switch (eventRec[eventLoc-1].eventtype - 48) { case 1: temp = 25; break; case 2: temp = 0; break; case 3: temp = 50; break; case 4: temp = 75; break; } JE_createNewEventEnemy(0, temp, tempDat); eventRec[eventLoc-1].eventdat = tempDat2; eventRec[eventLoc-1].eventdat3 = tempDat; eventRec[eventLoc-1].eventdat6 = tempDat3; break; case 53: forceEvents = (eventRec[eventLoc-1].eventdat != 99); break; case 54: JE_eventJump(eventRec[eventLoc-1].eventdat); break; case 55: /* Enemy Global AccelRev */ if (eventRec[eventLoc-1].eventdat3 > 79 && eventRec[eventLoc-1].eventdat3 < 90) eventRec[eventLoc-1].eventdat4 = newPL[eventRec[eventLoc-1].eventdat3 - 80]; for (temp = 0; temp < 100; temp++) { if (eventRec[eventLoc-1].eventdat4 == 0 || enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) { if (eventRec[eventLoc-1].eventdat != -99) enemy[temp].xaccel = eventRec[eventLoc-1].eventdat; if (eventRec[eventLoc-1].eventdat2 != -99) enemy[temp].yaccel = eventRec[eventLoc-1].eventdat2; } } break; case 56: /* Ground2 Bottom */ JE_createNewEventEnemy(0, 75, 0); if (b > 0) enemy[b-1].ey = 190; break; case 57: superEnemy254Jump = eventRec[eventLoc-1].eventdat; break; case 60: /*Assign Special Enemy*/ for (temp = 0; temp < 100; temp++) { if (enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) { enemy[temp].special = true; enemy[temp].flagnum = eventRec[eventLoc-1].eventdat; enemy[temp].setto = (eventRec[eventLoc-1].eventdat2 == 1); } } break; case 61: // if specific flag set to specific value, skip events if (globalFlags[eventRec[eventLoc-1].eventdat-1] == eventRec[eventLoc-1].eventdat2) eventLoc += eventRec[eventLoc-1].eventdat3; break; case 62: /*Play sound effect*/ soundQueue[3] = eventRec[eventLoc-1].eventdat; break; case 63: // skip events if not in 2-player mode if (!twoPlayerMode && !onePlayerAction) eventLoc += eventRec[eventLoc-1].eventdat; break; case 64: if (!(eventRec[eventLoc-1].eventdat == 6 && twoPlayerMode && difficultyLevel > DIFFICULTY_NORMAL)) { smoothies[eventRec[eventLoc-1].eventdat-1] = eventRec[eventLoc-1].eventdat2; temp = eventRec[eventLoc-1].eventdat; if (temp == 5) temp = 3; smoothie_data[temp-1] = eventRec[eventLoc-1].eventdat3; } break; case 65: background3x1 = (eventRec[eventLoc-1].eventdat == 0); break; case 66: /*If not on this difficulty level or higher then...*/ if (initialDifficulty <= eventRec[eventLoc-1].eventdat) eventLoc += eventRec[eventLoc-1].eventdat2; break; case 67: levelTimer = (eventRec[eventLoc-1].eventdat == 1); levelTimerCountdown = eventRec[eventLoc-1].eventdat3 * 100; levelTimerJumpTo = eventRec[eventLoc-1].eventdat2; break; case 68: randomExplosions = (eventRec[eventLoc-1].eventdat == 1); break; case 69: for (uint i = 0; i < COUNTOF(player); ++i) player[i].invulnerable_ticks = eventRec[eventLoc-1].eventdat; break; case 70: if (eventRec[eventLoc-1].eventdat2 == 0) { /*1-10*/ bool found = false; for (temp = 1; temp <= 19; temp++) found = found || JE_searchFor(temp, NULL); if (!found) JE_eventJump(eventRec[eventLoc-1].eventdat); } else if (!JE_searchFor(eventRec[eventLoc-1].eventdat2, NULL) && (eventRec[eventLoc-1].eventdat3 == 0 || !JE_searchFor(eventRec[eventLoc-1].eventdat3, NULL)) && (eventRec[eventLoc-1].eventdat4 == 0 || !JE_searchFor(eventRec[eventLoc-1].eventdat4, NULL))) { JE_eventJump(eventRec[eventLoc-1].eventdat); } break; case 71: if (((((intptr_t)mapYPos - (intptr_t)&megaData1.mainmap) / sizeof(JE_byte *)) * 2) <= (unsigned)eventRec[eventLoc-1].eventdat2) JE_eventJump(eventRec[eventLoc-1].eventdat); break; case 72: background3x1b = (eventRec[eventLoc-1].eventdat == 1); break; case 73: skyEnemyOverAll = (eventRec[eventLoc-1].eventdat == 1); break; case 74: /* Enemy Global BounceParams */ for (temp = 0; temp < 100; temp++) { if (eventRec[eventLoc-1].eventdat4 == 0 || enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) { if (eventRec[eventLoc-1].eventdat5 != -99) enemy[temp].xminbounce = eventRec[eventLoc-1].eventdat5; if (eventRec[eventLoc-1].eventdat6 != -99) enemy[temp].yminbounce = eventRec[eventLoc-1].eventdat6; if (eventRec[eventLoc-1].eventdat != -99) enemy[temp].xmaxbounce = eventRec[eventLoc-1].eventdat; if (eventRec[eventLoc-1].eventdat2 != -99) enemy[temp].ymaxbounce = eventRec[eventLoc-1].eventdat2; } } break; case 75:; bool temp_no_clue = false; // TODO: figure out what this is doing for (temp = 0; temp < 100; temp++) { if (enemyAvail[temp] == 0 && enemy[temp].eyc == 0 && enemy[temp].linknum >= eventRec[eventLoc-1].eventdat && enemy[temp].linknum <= eventRec[eventLoc-1].eventdat2) { temp_no_clue = true; } } if (temp_no_clue) { JE_byte enemy_i; do { temp = (mt_rand() % (eventRec[eventLoc-1].eventdat2 + 1 - eventRec[eventLoc-1].eventdat)) + eventRec[eventLoc-1].eventdat; } while (!(JE_searchFor(temp, &enemy_i) && enemy[enemy_i].eyc == 0)); newPL[eventRec[eventLoc-1].eventdat3 - 80] = temp; } else { newPL[eventRec[eventLoc-1].eventdat3 - 80] = 255; if (eventRec[eventLoc-1].eventdat4 > 0) { /*Skip*/ curLoc = eventRec[eventLoc-1 + eventRec[eventLoc-1].eventdat4].eventtime - 1; eventLoc += eventRec[eventLoc-1].eventdat4 - 1; } } break; case 76: returnActive = true; break; case 77: mapYPos = &megaData1.mainmap[0][0]; mapYPos += eventRec[eventLoc-1].eventdat / 2; if (eventRec[eventLoc-1].eventdat2 > 0) { mapY2Pos = &megaData2.mainmap[0][0]; mapY2Pos += eventRec[eventLoc-1].eventdat2 / 2; } else { mapY2Pos = &megaData2.mainmap[0][0]; mapY2Pos += eventRec[eventLoc-1].eventdat / 2; } break; case 78: if (galagaShotFreq < 10) galagaShotFreq++; break; case 79: boss_bar[0].link_num = eventRec[eventLoc-1].eventdat; boss_bar[1].link_num = eventRec[eventLoc-1].eventdat2; break; case 80: // skip events if in 2-player mode if (twoPlayerMode) eventLoc += eventRec[eventLoc-1].eventdat; break; case 81: /*WRAP2*/ BKwrap2 = &megaData2.mainmap[0][0]; BKwrap2 += eventRec[eventLoc-1].eventdat / 2; BKwrap2to = &megaData2.mainmap[0][0]; BKwrap2to += eventRec[eventLoc-1].eventdat2 / 2; break; case 82: /*Give SPECIAL WEAPON*/ player[0].items.special = eventRec[eventLoc-1].eventdat; shotMultiPos[SHOT_SPECIAL] = 0; shotRepeat[SHOT_SPECIAL] = 0; shotMultiPos[SHOT_SPECIAL2] = 0; shotRepeat[SHOT_SPECIAL2] = 0; break; default: fprintf(stderr, "warning: ignoring unknown event %d\n", eventRec[eventLoc-1].eventtype); break; } eventLoc++; } void JE_whoa(void) { unsigned int i, j, color, offset, timer; unsigned int screenSize, topBorder, bottomBorder; Uint8 * TempScreen1, * TempScreen2, * TempScreenSwap; /* 'whoa' gets us that nifty screen fade used when you type in * 'engage'. We need two temporary screen buffers (char arrays can * work too, but these screens already exist) for our effect. * This could probably be a lot more efficient (there's probably a * way to get vgascreen as one of the temp buffers), but it's only called * once so don't worry about it. */ TempScreen1 = game_screen->pixels; TempScreen2 = VGAScreen2->pixels; screenSize = VGAScreenSeg->h * VGAScreenSeg->pitch; topBorder = VGAScreenSeg->pitch * 4; /* Seems an arbitrary number of lines */ bottomBorder = VGAScreenSeg->pitch * 7; /* Okay, one disadvantage to using other screens as temp buffers: they * need to be the right size. I doubt they'll ever be anything but 320x200, * but just in case, these asserts will clue in whoever stumbles across * the problem. You can fix it with the stack or malloc. */ assert((unsigned)VGAScreen2->h * VGAScreen2->pitch >= screenSize && (unsigned)game_screen->h * game_screen->pitch >= screenSize); /* Clear the top and bottom borders. We don't want to process * them and we don't want to draw them. */ memset((Uint8 *)VGAScreenSeg->pixels, 0, topBorder); memset((Uint8 *)VGAScreenSeg->pixels + screenSize - bottomBorder, 0, bottomBorder); /* Copy our test subject to one of the temporary buffers. Blank the other */ memset(TempScreen1, 0, screenSize); memcpy(TempScreen2, VGAScreenSeg->pixels, VGAScreenSeg->h * VGAScreenSeg->pitch); service_SDL_events(true); timer = 300; /* About 300 rounds is enough to make the screen mostly black */ do { setDelay(1); /* This gets us our 'whoa' effect with pixel bleeding magic. * I'm willing to bet the guy who originally wrote the asm was goofing * around on acid and thought this looked good enough to use. */ for (i = screenSize - bottomBorder, j = topBorder / 2; i > 0; i--, j++) { offset = j + i/8192 - 4; color = (TempScreen2[offset ] * 12 + TempScreen1[offset-VGAScreenSeg->pitch] + TempScreen1[offset-1 ] + TempScreen1[offset+1 ] + TempScreen1[offset+VGAScreenSeg->pitch]) / 16; TempScreen1[j] = color; } /* Now copy that mess to the buffer. */ memcpy((Uint8 *)VGAScreenSeg->pixels + topBorder, TempScreen1 + topBorder, screenSize - bottomBorder); JE_showVGA(); timer--; wait_delay(); /* Flip the buffer. */ TempScreenSwap = TempScreen1; TempScreen1 = TempScreen2; TempScreen2 = TempScreenSwap; } while (!(timer == 0 || JE_anyButton())); levelWarningLines = 4; } static void JE_barX(JE_word x1, JE_word y1, JE_word x2, JE_word y2, JE_byte col) { fill_rectangle_xy(VGAScreen, x1, y1, x2, y1, col + 1); fill_rectangle_xy(VGAScreen, x1, y1 + 1, x2, y2 - 1, col ); fill_rectangle_xy(VGAScreen, x1, y2, x2, y2, col - 1); } void draw_boss_bar(void) { for (unsigned int b = 0; b < COUNTOF(boss_bar); b++) { if (boss_bar[b].link_num == 0) continue; unsigned int armor = 256; // higher than armor max for (unsigned int e = 0; e < COUNTOF(enemy); e++) // find most damaged { if (enemyAvail[e] != 1 && enemy[e].linknum == boss_bar[b].link_num) if (enemy[e].armorleft < armor) armor = enemy[e].armorleft; } if (armor > 255 || armor == 0) // boss dead? boss_bar[b].link_num = 0; else boss_bar[b].armor = (armor == 255) ? 254 : armor; // 255 would make the bar too long } unsigned int bars = (boss_bar[0].link_num != 0 ? 1 : 0) + (boss_bar[1].link_num != 0 ? 1 : 0); // if only one bar left, make it the first one if (bars == 1 && boss_bar[0].link_num == 0) { memcpy(&boss_bar[0], &boss_bar[1], sizeof(boss_bar_t)); boss_bar[1].link_num = 0; } for (unsigned int b = 0; b < bars; b++) { unsigned int x = (bars == 2) ? ((b == 0) ? 125 : 185) : ((levelTimer) ? 250 : 155); // level timer and boss bar would overlap JE_barX(x - 25, 7, x + 25, 12, 115); JE_barX(x - (boss_bar[b].armor / 10), 7, x + (boss_bar[b].armor + 5) / 10, 12, 118 + boss_bar[b].color); if (boss_bar[b].color > 0) boss_bar[b].color--; } } opentyrian-2.1.20221123/src/tyrian2.h000066400000000000000000000036671432005211200167710ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TYRIAN2_H #define TYRIAN2_H #include "opentyr.h" #include "varz.h" #include "helptext.h" void intro_logos(void); typedef struct { Uint8 link_num; Uint8 armor; Uint8 color; } boss_bar_t; extern boss_bar_t boss_bar[2]; extern char tempStr[31]; extern JE_byte itemAvail[9][10], itemAvailMax[9]; void JE_createNewEventEnemy(JE_byte enemytypeofs, JE_word enemyoffset, Sint16 uniqueShapeTableI); void JE_doNetwork(void); uint JE_makeEnemy(struct JE_SingleEnemyType *enemy, Uint16 eDatI, Sint16 uniqueShapeTableI); void JE_eventJump(JE_word jump); void JE_whoa(void); Sint16 JE_newEnemy(int enemyOffset, Uint16 eDatI, Sint16 uniqueShapeTableI); void JE_drawEnemy(int enemyOffset); void JE_starShowVGA(void); void JE_main(void); void JE_loadMap(void); #ifdef WITH_NETWORK void networkStartScreen(void); #endif bool titleScreen(void); bool newGame(void); bool newSuperArcadeGame(unsigned int i); void newSuperTyrianGame(void); void JE_readTextSync(void); void JE_displayText(void); bool JE_searchFor(JE_byte PLType, JE_byte* out_index); void JE_eventSystem(void); void draw_boss_bar(void); #endif /* TYRIAN2_H */ opentyrian-2.1.20221123/src/varz.c000066400000000000000000000740631432005211200163540ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "varz.h" #include "config.h" #include "editship.h" #include "episodes.h" #include "joystick.h" #include "lds_play.h" #include "loudness.h" #include "mainint.h" #include "mouse.h" #include "mtrand.h" #include "network.h" #include "nortsong.h" #include "nortvars.h" #include "opentyr.h" #include "shots.h" #include "sprite.h" #include "vga256d.h" #include "video.h" JE_integer tempDat, tempDat2, tempDat3; const JE_byte SANextShip[SA + 2] /* [0..SA + 1] */ = { 3, 9, 6, 2, 5, 1, 4, 3, 7 }; // 0 -> 3 -> 2 -> 6 -> 4 -> 5 -> 1 -> 9 -> 7 const JE_word SASpecialWeapon[SA] /* [1..SA] */ = { 7, 8, 9, 10, 11, 12, 13 }; const JE_word SASpecialWeaponB[SA] /* [1..SA] */ = {37, 6, 15, 40, 16, 14, 41 }; const JE_byte SAShip[SA] /* [1..SA] */ = { 3, 1, 5, 10, 2, 11, 12 }; const JE_word SAWeapon[SA][5] /* [1..SA, 1..5] */ = { /* R Bl Bk G P */ { 9, 31, 32, 33, 34 }, /* Stealth Ship */ { 19, 8, 22, 41, 34 }, /* StormWind */ { 27, 5, 20, 42, 31 }, /* Techno */ { 15, 3, 28, 22, 12 }, /* Enemy */ { 23, 35, 25, 14, 6 }, /* Weird */ { 2, 5, 21, 4, 7 }, /* Unknown */ { 40, 38, 37, 41, 36 } /* NortShip Z */ }; const JE_byte specialArcadeWeapon[PORT_NUM] /* [1..Portnum] */ = { 17,17,18,0,0,0,10,0,0,0,0,0,44,0,10,0,19,0,0,-0,0,0,0,0,0,0, -0,0,0,0,45,0,0,0,0,0,0,0,0,0,0,0 }; const JE_byte optionSelect[16][3][2] /* [0..15, 1..3, 1..2] */ = { /* MAIN OPT FRONT */ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { { 1, 1},{16,16},{30,30} }, /*Single Shot*/ { { 2, 2},{29,29},{29,20} }, /*Dual Shot*/ { { 3, 3},{21,21},{12, 0} }, /*Charge Cannon*/ { { 4, 4},{18,18},{16,23} }, /*Vulcan*/ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { { 6, 6},{29,16},{ 0,22} }, /*Super Missile*/ { { 7, 7},{19,19},{19,28} }, /*Atom Bomb*/ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { {10,10},{21,21},{21,27} }, /*Mini Missile*/ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { {13,13},{17,17},{13,26} }, /*MicroBomb*/ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { {15,15},{15,16},{15,16} } /*Post-It*/ }; const JE_word PGR[21] /* [1..21] */ = { 4, 1,2,3, 41-21,57-21,73-21,89-21,105-21, 121-21,137-21,153-21, 151,151,151,151,73-21,73-21,1,2,4 /*151,151,151*/ }; const JE_byte PAni[21] /* [1..21] */ = {1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1}; const JE_word linkGunWeapons[38] /* [1..38] */ = { 0,0,0,0,0,0,0,0,444,445,446,447,0,448,449,0,0,0,0,0,450,451,0,506,0,564, 445,446,447,448,449,445,446,447,448,449,450,451 }; const JE_word chargeGunWeapons[38] /* [1..38] */ = { 0,0,0,0,0,0,0,0,476,458,464,482,0,488,470,0,0,0,0,0,494,500,0,528,0,558, 458,458,458,458,458,458,458,458,458,458,458,458 }; const JE_byte randomEnemyLaunchSounds[3] /* [1..3] */ = {13,6,26}; /* YKS: Twiddle cheat sheet: * 1: UP * 2: DOWN * 3: LEFT * 4: RIGHT * 5: UP+FIRE * 6: DOWN+FIRE * 7: LEFT+FIRE * 8: RIGHT+FIRE * 9: Release all keys (directions and fire) */ const JE_byte keyboardCombos[26][8] /* [1..26, 1..8] */ = { { 2, 1, 2, 5, 137, 0, 0, 0}, /*Invulnerability*/ { 4, 3, 2, 5, 138, 0, 0, 0}, /*Atom Bomb*/ { 3, 4, 6, 139, 0, 0, 0, 0}, /*Seeker Bombs*/ { 2, 5, 142, 0, 0, 0, 0, 0}, /*Ice Blast*/ { 6, 2, 6, 143, 0, 0, 0, 0}, /*Auto Repair*/ { 6, 7, 5, 8, 6, 7, 5, 112 }, /*Spin Wave*/ { 7, 8, 101, 0, 0, 0, 0, 0}, /*Repulsor*/ { 1, 7, 6, 146, 0, 0, 0, 0}, /*Protron Field*/ { 8, 6, 7, 1, 120, 0, 0, 0}, /*Minefield*/ { 3, 6, 8, 5, 121, 0, 0, 0}, /*Post-It Blast*/ { 1, 2, 7, 8, 119, 0, 0, 0}, /*Drone Ship - TBC*/ { 3, 4, 3, 6, 123, 0, 0, 0}, /*Repair Player 2*/ { 6, 7, 5, 8, 124, 0, 0, 0}, /*Super Bomb - TBC*/ { 1, 6, 125, 0, 0, 0, 0, 0}, /*Hot Dog*/ { 9, 5, 126, 0, 0, 0, 0, 0}, /*Lightning UP */ { 1, 7, 127, 0, 0, 0, 0, 0}, /*Lightning UP+LEFT */ { 1, 8, 128, 0, 0, 0, 0, 0}, /*Lightning UP+RIGHT*/ { 9, 7, 129, 0, 0, 0, 0, 0}, /*Lightning LEFT */ { 9, 8, 130, 0, 0, 0, 0, 0}, /*Lightning RIGHT*/ { 4, 2, 3, 5, 131, 0, 0, 0}, /*Warfly */ { 3, 1, 2, 8, 132, 0, 0, 0}, /*FrontBlaster */ { 2, 4, 5, 133, 0, 0, 0, 0}, /*Gerund */ { 3, 4, 2, 8, 134, 0, 0, 0}, /*FireBomb */ { 1, 4, 6, 135, 0, 0, 0, 0}, /*Indigo */ { 1, 3, 6, 137, 0, 0, 0, 0}, /*Invulnerability [easier] */ { 1, 4, 3, 4, 7, 136, 0, 0} /*D-Media Protron Drone */ }; const JE_byte shipCombosB[21] /* [1..21] */ = {15,16,17,18,19,20,21,22,23,24, 7, 8, 5,25,14, 4, 6, 3, 9, 2,26}; /*!! SUPER Tyrian !!*/ const JE_byte superTyrianSpecials[4] /* [1..4] */ = {1,2,4,5}; const JE_byte shipCombos[14][3] /* [0..12, 1..3] */ = { { 5, 4, 7}, /*2nd Player ship*/ { 1, 2, 0}, /*USP Talon*/ {14, 4, 0}, /*Super Carrot*/ { 4, 5, 0}, /*Gencore Phoenix*/ { 6, 5, 0}, /*Gencore Maelstrom*/ { 7, 8, 0}, /*MicroCorp Stalker*/ { 7, 9, 0}, /*MicroCorp Stalker-B*/ {10, 3, 5}, /*Prototype Stalker-C*/ { 5, 8, 9}, /*Stalker*/ { 1, 3, 0}, /*USP Fang*/ { 7,16,17}, /*U-Ship*/ { 2,11,12}, /*1st Player ship*/ { 3, 8,10}, /*Nort ship*/ { 0, 0, 0} // Dummy entry added for Stalker 21.126 }; /*Street-Fighter Commands*/ JE_byte SFCurrentCode[2][21]; /* [1..2, 1..21] */ JE_byte SFExecuted[2]; /* [1..2] */ /*Special General Data*/ JE_byte lvlFileNum; JE_word maxEvent, eventLoc; /*JE_word maxenemies;*/ JE_word tempBackMove, explodeMove; /*Speed of background movement*/ JE_byte levelEnd; JE_word levelEndFxWait; JE_shortint levelEndWarp; JE_boolean endLevel, reallyEndLevel, waitToEndLevel, playerEndLevel, normalBonusLevelCurrent, bonusLevelCurrent, smallEnemyAdjust, readyToEndLevel, quitRequested; JE_byte newPL[10]; /* [0..9] */ /*Eventsys event 75 parameter*/ JE_word returnLoc; JE_boolean returnActive; JE_word galagaShotFreq; JE_longint galagaLife; JE_boolean debug = false; /*Debug Mode*/ Uint32 debugTime, lastDebugTime; JE_longint debugHistCount; JE_real debugHist; JE_word curLoc; /*Current Pixel location of background 1*/ JE_boolean firstGameOver, gameLoaded, enemyStillExploding; /* Destruction Ratio */ JE_word totalEnemy; JE_word enemyKilled; /* Shape/Map Data - All in one Segment! */ struct JE_MegaDataType1 megaData1; struct JE_MegaDataType2 megaData2; struct JE_MegaDataType3 megaData3; /* Secret Level Display */ JE_byte flash; JE_shortint flashChange; JE_byte displayTime; /* Demo Stuff */ bool play_demo = false, record_demo = false, stopped_demo = false; Uint8 demo_num = 0; FILE *demo_file = NULL; Uint8 demo_keys; Uint16 demo_keys_wait; /* Sound Effects Queue */ JE_byte soundQueue[8]; /* [0..7] */ /*Level Event Data*/ JE_boolean enemyContinualDamage; JE_boolean enemiesActive; JE_boolean forceEvents; JE_boolean stopBackgrounds; JE_byte stopBackgroundNum; JE_byte damageRate; /*Rate at which a player takes damage*/ JE_boolean background3x1; /*Background 3 enemies use Background 1 X offset*/ JE_boolean background3x1b; /*Background 3 enemies moved 8 pixels left*/ JE_boolean levelTimer; JE_word levelTimerCountdown; JE_word levelTimerJumpTo; JE_boolean randomExplosions; JE_boolean editShip1, editShip2; JE_boolean globalFlags[10]; /* [1..10] */ JE_byte levelSong; /* DESTRUCT game */ JE_boolean loadDestruct; /* MapView Data */ JE_word mapOrigin, mapPNum; JE_byte mapPlanet[5], mapSection[5]; /* [1..5] */ /* Interface Constants */ JE_boolean moveTyrianLogoUp; JE_boolean skipStarShowVGA; /*EnemyData*/ JE_MultiEnemyType enemy; JE_EnemyAvailType enemyAvail; /* values: 0: used, 1: free, 2: secret pick-up */ JE_word enemyOffset; JE_word enemyOnScreen; JE_word superEnemy254Jump; /*EnemyShotData*/ JE_boolean fireButtonHeld; JE_boolean enemyShotAvail[ENEMY_SHOT_MAX]; /* [1..Enemyshotmax] */ EnemyShotType enemyShot[ENEMY_SHOT_MAX]; /* [1..Enemyshotmax] */ /* Player Shot Data */ JE_byte zinglonDuration; JE_byte astralDuration; JE_word flareDuration; JE_boolean flareStart; JE_shortint flareColChg; JE_byte specialWait; JE_byte nextSpecialWait; JE_boolean spraySpecial; JE_byte doIced; JE_boolean infiniteShot; /*PlayerData*/ JE_boolean allPlayersGone; /*Both players dead and finished exploding*/ const uint shadowYDist = 10; JE_real optionSatelliteRotate; JE_integer optionAttachmentMove; JE_boolean optionAttachmentLinked, optionAttachmentReturn; JE_byte chargeWait, chargeLevel, chargeMax, chargeGr, chargeGrWait; JE_word neat; /*ExplosionData*/ explosion_type explosions[MAX_EXPLOSIONS]; /* [1..ExplosionMax] */ JE_integer explosionFollowAmountX, explosionFollowAmountY; /*Repeating Explosions*/ rep_explosion_type rep_explosions[MAX_REPEATING_EXPLOSIONS]; /* [1..20] */ /*SuperPixels*/ superpixel_type superpixels[MAX_SUPERPIXELS]; /* [0..MaxSP] */ unsigned int last_superpixel; /*Temporary Numbers*/ JE_byte temp, temp2, temp3; JE_word tempX, tempY; JE_word tempW; JE_boolean doNotSaveBackup; JE_word x, y; JE_integer b; JE_byte **BKwrap1to, **BKwrap2to, **BKwrap3to, **BKwrap1, **BKwrap2, **BKwrap3; JE_shortint specialWeaponFilter, specialWeaponFreq; JE_word specialWeaponWpn; JE_boolean linkToPlayer; JE_word shipGr, shipGr2; Sprite2_array *shipGrPtr, *shipGr2ptr; void JE_getShipInfo(void) { JE_boolean extraShip, extraShip2; shipGrPtr = &spriteSheet9; shipGr2ptr = &spriteSheet9; powerAdd = powerSys[player[0].items.generator].power; extraShip = player[0].items.ship > 90; if (extraShip) { JE_byte base = (player[0].items.ship - 91) * 15; shipGr = JE_SGr(player[0].items.ship - 90, &shipGrPtr); player[0].armor = extraShips[base + 7]; } else { shipGr = ships[player[0].items.ship].shipgraphic; player[0].armor = ships[player[0].items.ship].dmg; } extraShip2 = player[1].items.ship > 90; if (extraShip2) { JE_byte base2 = (player[1].items.ship - 91) * 15; shipGr2 = JE_SGr(player[1].items.ship - 90, &shipGr2ptr); player[1].armor = extraShips[base2 + 7]; /* bug? */ } else { shipGr2 = 0; player[1].armor = 10; } for (uint i = 0; i < COUNTOF(player); ++i) { player[i].initial_armor = player[i].armor; uint temp = ((i == 0 && extraShip) || (i == 1 && extraShip2)) ? 2 : ships[player[i].items.ship].ani; if (temp == 0) { player[i].shot_hit_area_x = 12; player[i].shot_hit_area_y = 10; } else { player[i].shot_hit_area_x = 11; player[i].shot_hit_area_y = 14; } } } JE_word JE_SGr(JE_word ship, Sprite2_array **ptr) { const JE_word GR[15] /* [1..15] */ = {233, 157, 195, 271, 81, 0, 119, 5, 43, 81, 119, 157, 195, 233, 271}; JE_word tempW = extraShips[(ship - 1) * 15]; if (tempW > 7) *ptr = extraShapes; return GR[tempW-1]; } void JE_drawOptions(void) { SDL_Surface *temp_surface = VGAScreen; VGAScreen = VGAScreenSeg; Player *this_player = &player[twoPlayerMode ? 1 : 0]; for (uint i = 0; i < COUNTOF(this_player->sidekick); ++i) { JE_OptionType *this_option = &options[this_player->items.sidekick[i]]; this_player->sidekick[i].ammo = this_player->sidekick[i].ammo_max = this_option->ammo; this_player->sidekick[i].ammo_refill_ticks = this_player->sidekick[i].ammo_refill_ticks_max = (105 - this_player->sidekick[i].ammo) * 4; this_player->sidekick[i].style = this_option->tr; this_player->sidekick[i].animation_enabled = (this_option->option == 1); this_player->sidekick[i].animation_frame = 0; this_player->sidekick[i].charge = 0; this_player->sidekick[i].charge_ticks = 20; // draw initial sidekick HUD const int y = hud_sidekick_y[twoPlayerMode ? 1 : 0][i]; fill_rectangle_xy(VGAScreenSeg, 284, y, 284 + 28, y + 15, 0); if (this_option->icongr > 0) blit_sprite(VGAScreenSeg, 284, y, OPTION_SHAPES, this_option->icongr - 1); // sidekick HUD icon draw_segmented_gauge(VGAScreenSeg, 284, y + 13, 112, 2, 2, MAX(1, this_player->sidekick[i].ammo_max / 10), this_player->sidekick[i].ammo); } VGAScreen = temp_surface; JE_drawOptionLevel(); } void JE_drawOptionLevel(void) { if (twoPlayerMode) { for (temp = 1; temp <= 3; temp++) { fill_rectangle_xy(VGAScreenSeg, 268, 127 + (temp - 1) * 6, 269, 127 + 3 + (temp - 1) * 6, 193 + ((player[1].items.sidekick_level - 100) == temp) * 11); } } } void JE_tyrianHalt(JE_byte code) { deinit_audio(); deinit_video(); deinit_joysticks(); /* TODO: NETWORK */ free_main_shape_tables(); free_sprite2s(&shopSpriteSheet); free_sprite2s(&explosionSpriteSheet); free_sprite2s(&destructSpriteSheet); for (int i = 0; i < SOUND_COUNT; i++) { free(soundSamples[i]); } if (code != 9) { /* TODO? JE_drawANSI("exitmsg.bin"); JE_gotoXY(1,22);*/ JE_saveConfiguration(); } /* endkeyboard; */ if (code == 9) { /* OutputString('call=file0002.EXE' + #0'); TODO? */ } if (code == 5) { code = 0; } if (trentWin) { printf("\n" "\n" "\n" "\n" "Sleep well, Trent, you deserve the rest.\n" "You now have permission to borrow my ship on your next mission.\n" "\n" "Also, you might want to try out the YESXMAS parameter.\n" " Type: File0001 YESXMAS\n" "\n" "You'll need the 2.1 patch, though!\n" "\n"); } SDL_Quit(); exit(code); } void JE_specialComplete(JE_byte playerNum, JE_byte specialType) { nextSpecialWait = 0; switch (special[specialType].stype) { /*Weapon*/ case 1: if (playerNum == 1) b = player_shot_create(0, SHOT_SPECIAL2, player[0].x, player[0].y, mouseX, mouseY, special[specialType].wpn, playerNum); else b = player_shot_create(0, SHOT_SPECIAL2, player[1].x, player[1].y, mouseX, mouseY, special[specialType].wpn, playerNum); shotRepeat[SHOT_SPECIAL] = shotRepeat[SHOT_SPECIAL2]; break; /*Repulsor*/ case 2: for (temp = 0; temp < ENEMY_SHOT_MAX; temp++) { if (!enemyShotAvail[temp]) { if (player[0].x > enemyShot[temp].sx) enemyShot[temp].sxm--; else if (player[0].x < enemyShot[temp].sx) enemyShot[temp].sxm++; if (player[0].y > enemyShot[temp].sy) enemyShot[temp].sym--; else if (player[0].y < enemyShot[temp].sy) enemyShot[temp].sym++; } } break; /*Zinglon Blast*/ case 3: zinglonDuration = 50; shotRepeat[SHOT_SPECIAL] = 100; soundQueue[7] = S_SOUL_OF_ZINGLON; break; /*Attractor*/ case 4: for (temp = 0; temp < 100; temp++) { if (enemyAvail[temp] != 1 && enemy[temp].scoreitem && enemy[temp].evalue != 0) { if (player[0].x > enemy[temp].ex) enemy[temp].exc++; else if (player[0].x < enemy[temp].ex) enemy[temp].exc--; if (player[0].y > enemy[temp].ey) enemy[temp].eyc++; else if (player[0].y < enemy[temp].ey) enemy[temp].eyc--; } } break; /*Flare*/ case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 16: if (flareDuration == 0) flareStart = true; specialWeaponWpn = special[specialType].wpn; linkToPlayer = false; spraySpecial = false; switch (special[specialType].stype) { case 5: specialWeaponFilter = 7; specialWeaponFreq = 2; flareDuration = 50; break; case 6: specialWeaponFilter = 1; specialWeaponFreq = 7; flareDuration = 200 + 25 * player[0].items.weapon[FRONT_WEAPON].power; break; case 7: specialWeaponFilter = 3; specialWeaponFreq = 3; flareDuration = 50 + 10 * player[0].items.weapon[FRONT_WEAPON].power; zinglonDuration = 50; shotRepeat[SHOT_SPECIAL] = 100; soundQueue[7] = S_SOUL_OF_ZINGLON; break; case 8: specialWeaponFilter = -99; specialWeaponFreq = 7; flareDuration = 10 + player[0].items.weapon[FRONT_WEAPON].power; break; case 9: specialWeaponFilter = -99; specialWeaponFreq = 8; flareDuration = 8 + 2 * player[0].items.weapon[FRONT_WEAPON].power; linkToPlayer = true; nextSpecialWait = special[specialType].pwr; break; case 10: specialWeaponFilter = -99; specialWeaponFreq = 8; flareDuration = 14 + 4 * player[0].items.weapon[FRONT_WEAPON].power; linkToPlayer = true; break; case 11: specialWeaponFilter = -99; specialWeaponFreq = special[specialType].pwr; flareDuration = 10 + 10 * player[0].items.weapon[FRONT_WEAPON].power; astralDuration = 20 + 10 * player[0].items.weapon[FRONT_WEAPON].power; break; case 16: specialWeaponFilter = -99; specialWeaponFreq = 8; flareDuration = temp2 * 16 + 8; linkToPlayer = true; spraySpecial = true; break; } break; case 12: player[playerNum-1].invulnerable_ticks = temp2 * 10; if (superArcadeMode > 0 && superArcadeMode <= SA) { shotRepeat[SHOT_SPECIAL] = 250; b = player_shot_create(0, SHOT_SPECIAL2, player[0].x, player[0].y, mouseX, mouseY, 707, 1); player[0].invulnerable_ticks = 100; } break; case 13: player[0].armor += temp2 / 4 + 1; soundQueue[3] = S_POWERUP; break; case 14: player[1].armor += temp2 / 4 + 1; soundQueue[3] = S_POWERUP; break; case 17: // spawn left or right sidekick soundQueue[3] = S_POWERUP; if (player[0].items.sidekick[LEFT_SIDEKICK] == special[specialType].wpn) { player[0].items.sidekick[RIGHT_SIDEKICK] = special[specialType].wpn; shotMultiPos[RIGHT_SIDEKICK] = 0; } else { player[0].items.sidekick[LEFT_SIDEKICK] = special[specialType].wpn; shotMultiPos[LEFT_SIDEKICK] = 0; } JE_drawOptions(); break; case 18: // spawn right sidekick player[0].items.sidekick[RIGHT_SIDEKICK] = special[specialType].wpn; JE_drawOptions(); soundQueue[4] = S_POWERUP; shotMultiPos[RIGHT_SIDEKICK] = 0; break; } } void JE_doSpecialShot(JE_byte playerNum, uint *armor, uint *shield) { if (player[0].items.special > 0) { if (shotRepeat[SHOT_SPECIAL] == 0 && specialWait == 0 && flareDuration < 2 && zinglonDuration < 2) blit_sprite2(VGAScreen, 47, 4, spriteSheet9, 94); else blit_sprite2(VGAScreen, 47, 4, spriteSheet9, 93); } if (shotRepeat[SHOT_SPECIAL] > 0) { --shotRepeat[SHOT_SPECIAL]; } if (specialWait > 0) { specialWait--; } temp = SFExecuted[playerNum-1]; if (temp > 0 && shotRepeat[SHOT_SPECIAL] == 0 && flareDuration == 0) { temp2 = special[temp].pwr; bool can_afford = true; if (temp2 > 0) { if (temp2 < 98) // costs some shield { if (*shield >= temp2) *shield -= temp2; else can_afford = false; } else if (temp2 == 98) // costs all shield { if (*shield < 4) can_afford = false; temp2 = *shield; *shield = 0; } else if (temp2 == 99) // costs half shield { temp2 = *shield / 2; *shield = temp2; } else // costs some armor { temp2 -= 100; if (*armor > temp2) *armor -= temp2; else can_afford = false; } } shotMultiPos[SHOT_SPECIAL] = 0; shotMultiPos[SHOT_SPECIAL2] = 0; if (can_afford) JE_specialComplete(playerNum, temp); SFExecuted[playerNum-1] = 0; JE_wipeShieldArmorBars(); VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ JE_drawShield(); JE_drawArmor(); VGAScreen = game_screen; /* side-effect of game_screen */ } if (playerNum == 1 && player[0].items.special > 0) { /*Main Begin*/ if (superArcadeMode > 0 && (button[2-1] || button[3-1])) { fireButtonHeld = false; } if (!button[1-1] && !(superArcadeMode != SA_NONE && (button[2-1] || button[3-1]))) { fireButtonHeld = false; } else if (shotRepeat[SHOT_SPECIAL] == 0 && !fireButtonHeld && !(flareDuration > 0) && specialWait == 0) { fireButtonHeld = true; JE_specialComplete(playerNum, player[0].items.special); } } /*Main End*/ if (astralDuration > 0) astralDuration--; shotAvail[MAX_PWEAPON-1] = 0; if (flareDuration > 1) { if (specialWeaponFilter != -99) { if (levelFilter == -99 && levelBrightness == -99) { filterActive = false; } if (!filterActive) { levelFilter = specialWeaponFilter; if (levelFilter == 7) { levelBrightness = 0; } filterActive = true; } if (mt_rand() % 2 == 0) flareColChg = -1; else flareColChg = 1; if (levelFilter == 7) { if (levelBrightness < -6) { flareColChg = 1; } if (levelBrightness > 6) { flareColChg = -1; } levelBrightness += flareColChg; } } if ((signed)(mt_rand() % 6) < specialWeaponFreq) { b = MAX_PWEAPON; if (linkToPlayer) { if (shotRepeat[SHOT_SPECIAL] == 0) { b = player_shot_create(0, SHOT_SPECIAL, player[0].x, player[0].y, mouseX, mouseY, specialWeaponWpn, playerNum); } } else { b = player_shot_create(0, SHOT_SPECIAL, mt_rand() % 280, mt_rand() % 180, mouseX, mouseY, specialWeaponWpn, playerNum); } if (spraySpecial && b != MAX_PWEAPON) { playerShotData[b].shotXM = (mt_rand() % 5) - 2; playerShotData[b].shotYM = (mt_rand() % 5) - 2; if (playerShotData[b].shotYM == 0) { playerShotData[b].shotYM++; } } } flareDuration--; if (flareDuration == 1) { specialWait = nextSpecialWait; } } else if (flareStart) { flareStart = false; shotRepeat[SHOT_SPECIAL] = linkToPlayer ? 15 : 200; flareDuration = 0; if (levelFilter == specialWeaponFilter) { levelFilter = -99; levelBrightness = -99; filterActive = false; } } if (zinglonDuration > 1) { temp = 25 - abs(zinglonDuration - 25); JE_barBright(VGAScreen, player[0].x + 7 - temp, 0, player[0].x + 7 + temp, 184); JE_barBright(VGAScreen, player[0].x + 7 - temp - 2, 0, player[0].x + 7 + temp + 2, 184); zinglonDuration--; if (zinglonDuration % 5 == 0) { shotAvail[MAX_PWEAPON-1] = 1; } } } void JE_setupExplosion(signed int x, signed int y, signed int delta_y, unsigned int type, bool fixed_position, bool follow_player) { const struct { JE_word sprite; JE_byte ttl; } explosion_data[53] /* [1..53] */ = { { 144, 7 }, { 120, 12 }, { 190, 12 }, { 209, 12 }, { 152, 12 }, { 171, 12 }, { 133, 7 }, /*White Smoke*/ { 1, 12 }, { 20, 12 }, { 39, 12 }, { 58, 12 }, { 110, 3 }, { 76, 7 }, { 91, 3 }, /*15*/ { 227, 3 }, { 230, 3 }, { 233, 3 }, { 252, 3 }, { 246, 3 }, /*20*/ { 249, 3 }, { 265, 3 }, { 268, 3 }, { 271, 3 }, { 236, 3 }, /*25*/ { 239, 3 }, { 242, 3 }, { 261, 3 }, { 274, 3 }, { 277, 3 }, /*30*/ { 280, 3 }, { 299, 3 }, { 284, 3 }, { 287, 3 }, { 290, 3 }, /*35*/ { 293, 3 }, { 165, 8 }, /*Coin Values*/ { 184, 8 }, { 203, 8 }, { 222, 8 }, { 168, 8 }, { 187, 8 }, { 206, 8 }, { 225, 10 }, { 169, 10 }, { 188, 10 }, { 207, 20 }, { 226, 14 }, { 170, 14 }, { 189, 14 }, { 208, 14 }, { 246, 14 }, { 227, 14 }, { 265, 14 } }; if (y > -16 && y < 190) { for (int i = 0; i < MAX_EXPLOSIONS; i++) { if (explosions[i].ttl == 0) { explosions[i].x = x; explosions[i].y = y; if (type == 6) { explosions[i].y += 12; explosions[i].x += 2; } else if (type == 98) { type = 6; } explosions[i].sprite = explosion_data[type].sprite; explosions[i].ttl = explosion_data[type].ttl; explosions[i].follow_player = follow_player; explosions[i].fixed_position = fixed_position; explosions[i].delta_x = 0; explosions[i].delta_y = delta_y; break; } } } } void JE_setupExplosionLarge(JE_boolean enemyGround, JE_byte exploNum, JE_integer x, JE_integer y) { if (y >= 0) { if (enemyGround) { JE_setupExplosion(x - 6, y - 14, 0, 2, false, false); JE_setupExplosion(x + 6, y - 14, 0, 4, false, false); JE_setupExplosion(x - 6, y, 0, 3, false, false); JE_setupExplosion(x + 6, y, 0, 5, false, false); } else { JE_setupExplosion(x - 6, y - 14, 0, 7, false, false); JE_setupExplosion(x + 6, y - 14, 0, 9, false, false); JE_setupExplosion(x - 6, y, 0, 8, false, false); JE_setupExplosion(x + 6, y, 0, 10, false, false); } bool big; if (exploNum > 10) { exploNum -= 10; big = true; } else { big = false; } if (exploNum) { for (int i = 0; i < MAX_REPEATING_EXPLOSIONS; i++) { if (rep_explosions[i].ttl == 0) { rep_explosions[i].ttl = exploNum; rep_explosions[i].delay = 2; rep_explosions[i].x = x; rep_explosions[i].y = y; rep_explosions[i].big = big; break; } } } } } void JE_wipeShieldArmorBars(void) { if (!twoPlayerMode || galagaMode) { fill_rectangle_xy(VGAScreenSeg, 270, 137, 278, 194 - player[0].shield * 2, 0); } else { fill_rectangle_xy(VGAScreenSeg, 270, 60 - 44, 278, 60, 0); fill_rectangle_xy(VGAScreenSeg, 270, 194 - 44, 278, 194, 0); } if (!twoPlayerMode || galagaMode) { fill_rectangle_xy(VGAScreenSeg, 307, 137, 315, 194 - player[0].armor * 2, 0); } else { fill_rectangle_xy(VGAScreenSeg, 307, 60 - 44, 315, 60, 0); fill_rectangle_xy(VGAScreenSeg, 307, 194 - 44, 315, 194, 0); } } JE_byte JE_playerDamage(JE_byte temp, Player *this_player) { int playerDamage = 0; soundQueue[7] = S_SHIELD_HIT; /* Player Damage Routines */ if (this_player->shield < temp) { playerDamage = temp; temp -= this_player->shield; this_player->shield = 0; if (temp > 0) { /*Through Shields - Now Armor */ if (this_player->armor < temp) { temp -= this_player->armor; this_player->armor = 0; if (this_player->is_alive && !youAreCheating) { levelTimer = false; this_player->is_alive = false; this_player->exploding_ticks = 60; levelEnd = 40; tempVolume = tyrMusicVolume; soundQueue[1] = S_EXPLOSION_22; } } else { this_player->armor -= temp; soundQueue[7] = S_HULL_HIT; } } } else { this_player->shield -= temp; JE_setupExplosion(this_player->x - 17, this_player->y - 12, 0, 14, false, !twoPlayerMode); JE_setupExplosion(this_player->x - 5 , this_player->y - 12, 0, 15, false, !twoPlayerMode); JE_setupExplosion(this_player->x + 7 , this_player->y - 12, 0, 16, false, !twoPlayerMode); JE_setupExplosion(this_player->x + 19, this_player->y - 12, 0, 17, false, !twoPlayerMode); JE_setupExplosion(this_player->x - 17, this_player->y + 2, 0, 18, false, !twoPlayerMode); JE_setupExplosion(this_player->x + 19, this_player->y + 2, 0, 19, false, !twoPlayerMode); JE_setupExplosion(this_player->x - 17, this_player->y + 16, 0, 20, false, !twoPlayerMode); JE_setupExplosion(this_player->x - 5 , this_player->y + 16, 0, 21, false, !twoPlayerMode); JE_setupExplosion(this_player->x + 7 , this_player->y + 16, 0, 22, false, !twoPlayerMode); } JE_wipeShieldArmorBars(); VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ JE_drawShield(); JE_drawArmor(); VGAScreen = game_screen; /* side-effect of game_screen */ return playerDamage; } JE_word JE_portConfigs(void) { const uint player_index = twoPlayerMode ? 1 : 0; return tempW = weaponPort[player[player_index].items.weapon[REAR_WEAPON].id].opnum; } void JE_drawShield(void) { if (twoPlayerMode && !galagaMode) { for (uint i = 0; i < COUNTOF(player); ++i) JE_dBar3(VGAScreen, 270, 60 + 134 * i, roundf(player[i].shield * 0.8f), 144); } else { JE_dBar3(VGAScreen, 270, 194, player[0].shield, 144); if (player[0].shield != player[0].shield_max) { const uint y = 193 - (player[0].shield_max * 2); JE_rectangle(VGAScreen, 270, y, 278, y, 68); /* SEGa000 */ } } } void JE_drawArmor(void) { for (uint i = 0; i < COUNTOF(player); ++i) if (player[i].armor > 28) player[i].armor = 28; if (twoPlayerMode && !galagaMode) { for (uint i = 0; i < COUNTOF(player); ++i) JE_dBar3(VGAScreen, 307, 60 + 134 * i, roundf(player[i].armor * 0.8f), 224); } else { JE_dBar3(VGAScreen, 307, 194, player[0].armor, 224); } } void JE_doSP(JE_word x, JE_word y, JE_word num, JE_byte explowidth, JE_byte color) /* superpixels */ { for (temp = 0; temp < num; temp++) { JE_real tempr = mt_rand_lt1() * (2 * M_PI); signed int tempy = roundf(cosf(tempr) * mt_rand_1() * explowidth); signed int tempx = roundf(sinf(tempr) * mt_rand_1() * explowidth); if (++last_superpixel >= MAX_SUPERPIXELS) last_superpixel = 0; superpixels[last_superpixel].x = tempx + x; superpixels[last_superpixel].y = tempy + y; superpixels[last_superpixel].delta_x = tempx; superpixels[last_superpixel].delta_y = tempy + 1; superpixels[last_superpixel].color = color; superpixels[last_superpixel].z = 15; } } void JE_drawSP(void) { for (int i = MAX_SUPERPIXELS; i--; ) { if (superpixels[i].z) { superpixels[i].x += superpixels[i].delta_x; superpixels[i].y += superpixels[i].delta_y; if (superpixels[i].x < (unsigned)VGAScreen->w && superpixels[i].y < (unsigned)VGAScreen->h) { Uint8 *s = (Uint8 *)VGAScreen->pixels; /* screen pointer, 8-bit specific */ s += superpixels[i].y * VGAScreen->pitch; s += superpixels[i].x; *s = (((*s & 0x0f) + superpixels[i].z) >> 1) + superpixels[i].color; if (superpixels[i].x > 0) *(s - 1) = (((*(s - 1) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color; if (superpixels[i].x < VGAScreen->w - 1u) *(s + 1) = (((*(s + 1) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color; if (superpixels[i].y > 0) *(s - VGAScreen->pitch) = (((*(s - VGAScreen->pitch) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color; if (superpixels[i].y < VGAScreen->h - 1u) *(s + VGAScreen->pitch) = (((*(s + VGAScreen->pitch) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color; } superpixels[i].z--; } } } opentyrian-2.1.20221123/src/varz.h000066400000000000000000000233531432005211200163550ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef VARZ_H #define VARZ_H #include "episodes.h" #include "opentyr.h" #include "player.h" #include "sprite.h" #include #define SA 7 enum { SA_NONE = 0, SA_NORTSHIPZ = 7, // only used for code entry SA_DESTRUCT = 8, SA_ENGAGE = 9, // only used in pItems[P_SUPERARCADE] SA_SUPERTYRIAN = 254, SA_ARCADE = 255 }; #define ENEMY_SHOT_MAX 60 /* 60*/ #define CURRENT_KEY_SPEED 1 /*Keyboard/Joystick movement rate*/ #define MAX_EXPLOSIONS 200 #define MAX_REPEATING_EXPLOSIONS 20 #define MAX_SUPERPIXELS 101 struct JE_SingleEnemyType { JE_byte fillbyte; JE_integer ex, ey; /* POSITION */ JE_shortint exc, eyc; /* CURRENT SPEED */ JE_shortint exca, eyca; /* RANDOM ACCELERATION */ JE_shortint excc, eycc; /* FIXED ACCELERATION WAITTIME */ JE_shortint exccw, eyccw; JE_byte armorleft; JE_byte eshotwait[3], eshotmultipos[3]; /* [1..3] */ JE_byte enemycycle; JE_byte ani; JE_word egr[20]; /* [1..20] */ JE_byte size; JE_byte linknum; JE_byte aniactive; JE_byte animax; JE_byte aniwhenfire; Sprite2_array *sprite2s; JE_shortint exrev, eyrev; JE_integer exccadd, eyccadd; JE_byte exccwmax, eyccwmax; void *enemydatofs; JE_boolean edamaged; JE_word enemytype; JE_byte animin; JE_word edgr; JE_shortint edlevel; JE_shortint edani; JE_byte fill1; JE_byte filter; JE_integer evalue; JE_integer fixedmovey; JE_byte freq[3]; /* [1..3] */ JE_byte launchwait; JE_word launchtype; JE_byte launchfreq; JE_byte xaccel; JE_byte yaccel; JE_byte tur[3]; /* [1..3] */ JE_word enemydie; /* Enemy created when this one dies */ JE_boolean enemyground; JE_byte explonum; JE_word mapoffset; JE_boolean scoreitem; JE_boolean special; JE_byte flagnum; JE_boolean setto; JE_byte iced; /*Duration*/ JE_byte launchspecial; JE_integer xminbounce; JE_integer xmaxbounce; JE_integer yminbounce; JE_integer ymaxbounce; JE_byte fill[3]; /* [1..3] */ }; typedef struct JE_SingleEnemyType JE_MultiEnemyType[100]; /* [1..100] */ typedef JE_byte JE_DanCShape[24 * 28]; /* [1..(24*28) div 2] OF WORD */ typedef JE_char JE_CharString[256]; /* [1..256] */ typedef JE_byte JE_Map1Buffer[24 * 28 * 13 * 4]; /* [1..24*28*13*4] */ typedef JE_byte *JE_MapType[300][14]; /* [1..300, 1..14] */ typedef JE_byte *JE_MapType2[600][14]; /* [1..600, 1..14] */ typedef JE_byte *JE_MapType3[600][15]; /* [1..600, 1..15] */ struct JE_EventRecType { JE_word eventtime; JE_byte eventtype; JE_integer eventdat, eventdat2; JE_shortint eventdat3, eventdat5, eventdat6; JE_byte eventdat4; }; struct JE_MegaDataType1 { JE_MapType mainmap; struct { JE_DanCShape sh; } shapes[72]; /* [0..71] */ JE_byte tempdat1; /*JE_DanCShape filler;*/ }; struct JE_MegaDataType2 { JE_MapType2 mainmap; struct { JE_byte nothing[3]; /* [1..3] */ JE_byte fill; JE_DanCShape sh; } shapes[71]; /* [0..70] */ JE_byte tempdat2; }; struct JE_MegaDataType3 { JE_MapType3 mainmap; struct { JE_byte nothing[3]; /* [1..3] */ JE_byte fill; JE_DanCShape sh; } shapes[70]; /* [0..69] */ JE_byte tempdat3; }; typedef JE_byte JE_EnemyAvailType[100]; /* [1..100] */ typedef struct { JE_integer sx, sy; JE_integer sxm, sym; JE_shortint sxc, syc; JE_byte tx, ty; JE_word sgr; JE_byte sdmg; JE_byte duration; JE_word animate; JE_word animax; JE_byte fill[12]; } EnemyShotType; typedef struct { unsigned int ttl; signed int x, y; signed int delta_x, delta_y; bool fixed_position; bool follow_player; unsigned int sprite; } explosion_type; typedef struct { unsigned int delay; unsigned int ttl; unsigned int x, y; bool big; } rep_explosion_type; typedef struct { unsigned int x, y, z; signed int delta_x, delta_y; Uint8 color; } superpixel_type; extern JE_integer tempDat, tempDat2, tempDat3; extern const JE_byte SANextShip[SA + 2]; extern const JE_word SASpecialWeapon[SA]; extern const JE_word SASpecialWeaponB[SA]; extern const JE_byte SAShip[SA]; extern const JE_word SAWeapon[SA][5]; extern const JE_byte specialArcadeWeapon[PORT_NUM]; extern const JE_byte optionSelect[16][3][2]; extern const JE_word PGR[21]; extern const JE_byte PAni[21]; extern const JE_word linkGunWeapons[38]; extern const JE_word chargeGunWeapons[38]; extern const JE_byte randomEnemyLaunchSounds[3]; extern const JE_byte keyboardCombos[26][8]; extern const JE_byte shipCombosB[21]; extern const JE_byte superTyrianSpecials[4]; extern const JE_byte shipCombos[14][3]; extern JE_byte SFCurrentCode[2][21]; extern JE_byte SFExecuted[2]; extern JE_byte lvlFileNum; extern JE_word maxEvent, eventLoc; extern JE_word tempBackMove, explodeMove; extern JE_byte levelEnd; extern JE_word levelEndFxWait; extern JE_shortint levelEndWarp; extern JE_boolean endLevel, reallyEndLevel, waitToEndLevel, playerEndLevel, normalBonusLevelCurrent, bonusLevelCurrent, smallEnemyAdjust, readyToEndLevel, quitRequested; extern JE_byte newPL[10]; extern JE_word returnLoc; extern JE_boolean returnActive; extern JE_word galagaShotFreq; extern JE_longint galagaLife; extern JE_boolean debug; extern Uint32 debugTime, lastDebugTime; extern JE_longint debugHistCount; extern JE_real debugHist; extern JE_word curLoc; extern JE_boolean firstGameOver, gameLoaded, enemyStillExploding; extern JE_word totalEnemy; extern JE_word enemyKilled; extern struct JE_MegaDataType1 megaData1; extern struct JE_MegaDataType2 megaData2; extern struct JE_MegaDataType3 megaData3; extern JE_byte flash; extern JE_shortint flashChange; extern JE_byte displayTime; extern bool play_demo, record_demo, stopped_demo; extern Uint8 demo_num; extern FILE *demo_file; extern Uint8 demo_keys; extern Uint16 demo_keys_wait; extern JE_byte soundQueue[8]; extern JE_boolean enemyContinualDamage; extern JE_boolean enemiesActive; extern JE_boolean forceEvents; extern JE_boolean stopBackgrounds; extern JE_byte stopBackgroundNum; extern JE_byte damageRate; extern JE_boolean background3x1; extern JE_boolean background3x1b; extern JE_boolean levelTimer; extern JE_word levelTimerCountdown; extern JE_word levelTimerJumpTo; extern JE_boolean randomExplosions; extern JE_boolean editShip1, editShip2; extern JE_boolean globalFlags[10]; extern JE_byte levelSong; extern JE_boolean loadDestruct; extern JE_word mapOrigin, mapPNum; extern JE_byte mapPlanet[5], mapSection[5]; extern JE_boolean moveTyrianLogoUp; extern JE_boolean skipStarShowVGA; extern JE_MultiEnemyType enemy; extern JE_EnemyAvailType enemyAvail; extern JE_word enemyOffset; extern JE_word enemyOnScreen; extern JE_word superEnemy254Jump; extern explosion_type explosions[MAX_EXPLOSIONS]; extern JE_integer explosionFollowAmountX, explosionFollowAmountY; extern JE_boolean fireButtonHeld; extern JE_boolean enemyShotAvail[ENEMY_SHOT_MAX]; extern EnemyShotType enemyShot[ENEMY_SHOT_MAX]; extern JE_byte zinglonDuration; extern JE_byte astralDuration; extern JE_word flareDuration; extern JE_boolean flareStart; extern JE_shortint flareColChg; extern JE_byte specialWait; extern JE_byte nextSpecialWait; extern JE_boolean spraySpecial; extern JE_byte doIced; extern JE_boolean infiniteShot; extern JE_boolean allPlayersGone; extern const uint shadowYDist; extern JE_real optionSatelliteRotate; extern JE_integer optionAttachmentMove; extern JE_boolean optionAttachmentLinked, optionAttachmentReturn; extern JE_byte chargeWait, chargeLevel, chargeMax, chargeGr, chargeGrWait; extern JE_word neat; extern rep_explosion_type rep_explosions[MAX_REPEATING_EXPLOSIONS]; extern superpixel_type superpixels[MAX_SUPERPIXELS]; extern unsigned int last_superpixel; extern JE_byte temp, temp2, temp3; extern JE_word tempX, tempY; extern JE_word tempW; extern JE_boolean doNotSaveBackup; extern JE_word x, y; extern JE_integer b; extern JE_byte **BKwrap1to, **BKwrap2to, **BKwrap3to, **BKwrap1, **BKwrap2, **BKwrap3; extern JE_shortint specialWeaponFilter, specialWeaponFreq; extern JE_word specialWeaponWpn; extern JE_boolean linkToPlayer; extern JE_word shipGr, shipGr2; extern Sprite2_array *shipGrPtr, *shipGr2ptr; static const int hud_sidekick_y[2][2] = { { 64, 82 }, // one player HUD { 108, 126 }, // two player HUD }; void JE_getShipInfo(void); JE_word JE_SGr(JE_word ship, Sprite2_array **ptr); void JE_drawOptions(void); void JE_tyrianHalt(JE_byte code); /* This ends the game */ void JE_specialComplete(JE_byte playernum, JE_byte specialType); void JE_doSpecialShot(JE_byte playernum, uint *armor, uint *shield); void JE_wipeShieldArmorBars(void); JE_byte JE_playerDamage(JE_byte temp, Player *); void JE_setupExplosion(signed int x, signed int y, signed int delta_y, unsigned int type, bool fixed_position, bool follow_player); void JE_setupExplosionLarge(JE_boolean enemyground, JE_byte explonum, JE_integer x, JE_integer y); void JE_drawShield(void); void JE_drawArmor(void); JE_word JE_portConfigs(void); /*SuperPixels*/ void JE_doSP(JE_word x, JE_word y, JE_word num, JE_byte explowidth, JE_byte color); void JE_drawSP(void); void JE_drawOptionLevel(void); #endif /* VARZ_H */ opentyrian-2.1.20221123/src/vga256d.c000066400000000000000000000103411432005211200165350ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "vga256d.h" #include "config.h" // For fullscreen stuff #include "keyboard.h" #include "opentyr.h" #include "palette.h" #include "video.h" #include "SDL.h" #include #include #include #include #include void JE_pix(SDL_Surface *surface, int x, int y, JE_byte c) { /* Bad things happen if we don't clip */ if (x < surface->pitch && y < surface->h) { Uint8 *vga = surface->pixels; vga[y * surface->pitch + x] = c; } } void JE_pix3(SDL_Surface *surface, int x, int y, JE_byte c) { /* Originally implemented as several direct accesses */ JE_pix(surface, x, y, c); JE_pix(surface, x - 1, y, c); JE_pix(surface, x + 1, y, c); JE_pix(surface, x, y - 1, c); JE_pix(surface, x, y + 1, c); } void JE_rectangle(SDL_Surface *surface, int a, int b, int c, int d, int e) /* x1, y1, x2, y2, color */ { if (a < surface->pitch && b < surface->h && c < surface->pitch && d < surface->h) { Uint8 *vga = surface->pixels; int i; /* Top line */ memset(&vga[b * surface->pitch + a], e, c - a + 1); /* Bottom line */ memset(&vga[d * surface->pitch + a], e, c - a + 1); /* Left line */ for (i = (b + 1) * surface->pitch + a; i < (d * surface->pitch + a); i += surface->pitch) { vga[i] = e; } /* Right line */ for (i = (b + 1) * surface->pitch + c; i < (d * surface->pitch + c); i += surface->pitch) { vga[i] = e; } } else { printf("!!! WARNING: Rectangle clipped: %d %d %d %d %d\n", a, b, c, d, e); } } void fill_rectangle_xy(SDL_Surface *surface, int x, int y, int x2, int y2, Uint8 color) { SDL_Rect rect = { x, y, x2 - x + 1, y2 - y + 1 }; SDL_FillRect(surface, &rect, color); } void JE_barShade(SDL_Surface *surface, int a, int b, int c, int d) /* x1, y1, x2, y2 */ { if (a < surface->pitch && b < surface->h && c < surface->pitch && d < surface->h) { Uint8 *vga = surface->pixels; int i, j, width; width = c - a + 1; for (i = b * surface->pitch + a; i <= d * surface->pitch + a; i += surface->pitch) { for (j = 0; j < width; j++) { vga[i + j] = ((vga[i + j] & 0x0F) >> 1) | (vga[i + j] & 0xF0); } } } else { printf("!!! WARNING: Darker Rectangle clipped: %d %d %d %d\n", a,b,c,d); } } void JE_barBright(SDL_Surface *surface, int a, int b, int c, int d) /* x1, y1, x2, y2 */ { if (a < surface->pitch && b < surface->h && c < surface->pitch && d < surface->h) { Uint8 *vga = surface->pixels; int i, j, width; width = c-a+1; for (i = b * surface->pitch + a; i <= d * surface->pitch + a; i += surface->pitch) { for (j = 0; j < width; j++) { JE_byte al, ah; al = ah = vga[i + j]; ah &= 0xF0; al = (al & 0x0F) + 2; if (al > 0x0F) { al = 0x0F; } vga[i + j] = al + ah; } } } else { printf("!!! WARNING: Brighter Rectangle clipped: %d %d %d %d\n", a,b,c,d); } } void draw_segmented_gauge(SDL_Surface *surface, int x, int y, Uint8 color, uint segment_width, uint segment_height, uint segment_value, uint value) { assert(segment_width > 0 && segment_height > 0); const uint segments = value / segment_value, partial_segment = value % segment_value; for (uint i = 0; i < segments; ++i) { fill_rectangle_wh(surface, x, y, segment_width, segment_height, color + 12); x += segment_width + 1; } if (partial_segment > 0) fill_rectangle_wh(surface, x, y, segment_width, segment_height, color + (12 * partial_segment / segment_value)); } opentyrian-2.1.20221123/src/vga256d.h000066400000000000000000000032321432005211200165430ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef VGA256D_H #define VGA256D_H #include "opentyr.h" #include "SDL.h" void JE_pix(SDL_Surface *surface, int x, int y, JE_byte c); void JE_pix3(SDL_Surface *surface, int x, int y, JE_byte c); void JE_rectangle(SDL_Surface *surface, int a, int b, int c, int d, int e); void fill_rectangle_xy(SDL_Surface *, int x, int y, int x2, int y2, Uint8 color); void JE_barShade(SDL_Surface *surface, int a, int b, int c, int d); void JE_barBright(SDL_Surface *surface, int a, int b, int c, int d); static inline void fill_rectangle_wh(SDL_Surface *surface, int x, int y, uint w, uint h, Uint8 color) { SDL_Rect rect = { x, y, w, h }; SDL_FillRect(surface, &rect, color); } void draw_segmented_gauge(SDL_Surface *surface, int x, int y, Uint8 color, uint segment_width, uint segment_height, uint segment_value, uint value); #endif /* VGA256D_H */ opentyrian-2.1.20221123/src/vga_palette.c000066400000000000000000000106221432005211200176540ustar00rootroot00000000000000#include "vga_palette.h" // only used for the jukebox Palette vga_palette = { { 0, 0, 0}, { 0, 0, 168}, { 0, 168, 0}, { 0, 168, 168}, {168, 0, 0}, {168, 0, 168}, {168, 84, 0}, {168, 168, 168}, { 84, 84, 84}, { 84, 84, 252}, { 84, 252, 84}, { 84, 252, 252}, {252, 84, 84}, {252, 84, 252}, {252, 252, 84}, {252, 252, 252}, { 0, 0, 0}, { 20, 20, 20}, { 32, 32, 32}, { 44, 44, 44}, { 56, 56, 56}, { 68, 68, 68}, { 80, 80, 80}, { 96, 96, 96}, {112, 112, 112}, {128, 128, 128}, {144, 144, 144}, {160, 160, 160}, {180, 180, 180}, {200, 200, 200}, {224, 224, 224}, {252, 252, 252}, { 0, 0, 252}, { 64, 0, 252}, {124, 0, 252}, {188, 0, 252}, {252, 0, 252}, {252, 0, 188}, {252, 0, 124}, {252, 0, 64}, {252, 0, 0}, {252, 64, 0}, {252, 124, 0}, {252, 188, 0}, {252, 252, 0}, {188, 252, 0}, {124, 252, 0}, { 64, 252, 0}, { 0, 252, 0}, { 0, 252, 64}, { 0, 252, 124}, { 0, 252, 188}, { 0, 252, 252}, { 0, 188, 252}, { 0, 124, 252}, { 0, 64, 252}, {124, 124, 252}, {156, 124, 252}, {188, 124, 252}, {220, 124, 252}, {252, 124, 252}, {252, 124, 220}, {252, 124, 188}, {252, 124, 156}, {252, 124, 124}, {252, 156, 124}, {252, 188, 124}, {252, 220, 124}, {252, 252, 124}, {220, 252, 124}, {188, 252, 124}, {156, 252, 124}, {124, 252, 124}, {124, 252, 156}, {124, 252, 188}, {124, 252, 220}, {124, 252, 252}, {124, 220, 252}, {124, 188, 252}, {124, 156, 252}, {180, 180, 252}, {196, 180, 252}, {216, 180, 252}, {232, 180, 252}, {252, 180, 252}, {252, 180, 232}, {252, 180, 216}, {252, 180, 196}, {252, 180, 180}, {252, 196, 180}, {252, 216, 180}, {252, 232, 180}, {252, 252, 180}, {232, 252, 180}, {216, 252, 180}, {196, 252, 180}, {180, 252, 180}, {180, 252, 196}, {180, 252, 216}, {180, 252, 232}, {180, 252, 252}, {180, 232, 252}, {180, 216, 252}, {180, 196, 252}, { 0, 0, 112}, { 28, 0, 112}, { 56, 0, 112}, { 84, 0, 112}, {112, 0, 112}, {112, 0, 84}, {112, 0, 56}, {112, 0, 28}, {112, 0, 0}, {112, 28, 0}, {112, 56, 0}, {112, 84, 0}, {112, 112, 0}, { 84, 112, 0}, { 56, 112, 0}, { 28, 112, 0}, { 0, 112, 0}, { 0, 112, 28}, { 0, 112, 56}, { 0, 112, 84}, { 0, 112, 112}, { 0, 84, 112}, { 0, 56, 112}, { 0, 28, 112}, { 56, 56, 112}, { 68, 56, 112}, { 84, 56, 112}, { 96, 56, 112}, {112, 56, 112}, {112, 56, 96}, {112, 56, 84}, {112, 56, 68}, {112, 56, 56}, {112, 68, 56}, {112, 84, 56}, {112, 96, 56}, {112, 112, 56}, { 96, 112, 56}, { 84, 112, 56}, { 68, 112, 56}, { 56, 112, 56}, { 56, 112, 68}, { 56, 112, 84}, { 56, 112, 96}, { 56, 112, 112}, { 56, 96, 112}, { 56, 84, 112}, { 56, 68, 112}, { 80, 80, 112}, { 88, 80, 112}, { 96, 80, 112}, {104, 80, 112}, {112, 80, 112}, {112, 80, 104}, {112, 80, 96}, {112, 80, 88}, {112, 80, 80}, {112, 88, 80}, {112, 96, 80}, {112, 104, 80}, {112, 112, 80}, {104, 112, 80}, { 96, 112, 80}, { 88, 112, 80}, { 80, 112, 80}, { 80, 112, 88}, { 80, 112, 96}, { 80, 112, 104}, { 80, 112, 112}, { 80, 104, 112}, { 80, 96, 112}, { 80, 88, 112}, { 0, 0, 64}, { 16, 0, 64}, { 32, 0, 64}, { 48, 0, 64}, { 64, 0, 64}, { 64, 0, 48}, { 64, 0, 32}, { 64, 0, 16}, { 64, 0, 0}, { 64, 16, 0}, { 64, 32, 0}, { 64, 48, 0}, { 64, 64, 0}, { 48, 64, 0}, { 32, 64, 0}, { 16, 64, 0}, { 0, 64, 0}, { 0, 64, 16}, { 0, 64, 32}, { 0, 64, 48}, { 0, 64, 64}, { 0, 48, 64}, { 0, 32, 64}, { 0, 16, 64}, { 32, 32, 64}, { 40, 32, 64}, { 48, 32, 64}, { 56, 32, 64}, { 64, 32, 64}, { 64, 32, 56}, { 64, 32, 48}, { 64, 32, 40}, { 64, 32, 32}, { 64, 40, 32}, { 64, 48, 32}, { 64, 56, 32}, { 64, 64, 32}, { 56, 64, 32}, { 48, 64, 32}, { 40, 64, 32}, { 32, 64, 32}, { 32, 64, 40}, { 32, 64, 48}, { 32, 64, 56}, { 32, 64, 64}, { 32, 56, 64}, { 32, 48, 64}, { 32, 40, 64}, { 44, 44, 64}, { 48, 44, 64}, { 52, 44, 64}, { 60, 44, 64}, { 64, 44, 64}, { 64, 44, 60}, { 64, 44, 52}, { 64, 44, 48}, { 64, 44, 44}, { 64, 48, 44}, { 64, 52, 44}, { 64, 60, 44}, { 64, 64, 44}, { 60, 64, 44}, { 52, 64, 44}, { 48, 64, 44}, { 44, 64, 44}, { 44, 64, 48}, { 44, 64, 52}, { 44, 64, 60}, { 44, 64, 64}, { 44, 60, 64}, { 44, 52, 64}, { 44, 48, 64}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0} }; opentyrian-2.1.20221123/src/vga_palette.h000066400000000000000000000001701432005211200176560ustar00rootroot00000000000000#ifndef VGA_PALETTE_H #define VGA_PALETTE_H #include "palette.h" extern Palette vga_palette; #endif // VGA_PALETTE_H opentyrian-2.1.20221123/src/video.c000066400000000000000000000251051432005211200164710ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "video.h" #include "keyboard.h" #include "opentyr.h" #include "palette.h" #include "video_scale.h" #include #include #include #include #include const char *const scaling_mode_names[ScalingMode_MAX] = { "Center", "Integer", "Fit 8:5", "Fit 4:3", }; int fullscreen_display; ScalingMode scaling_mode = SCALE_INTEGER; static SDL_Rect last_output_rect = { 0, 0, vga_width, vga_height }; SDL_Surface *VGAScreen, *VGAScreenSeg; SDL_Surface *VGAScreen2; SDL_Surface *game_screen; SDL_Window *main_window = NULL; static SDL_Renderer *main_window_renderer = NULL; SDL_PixelFormat *main_window_tex_format = NULL; static SDL_Texture *main_window_texture = NULL; static ScalerFunction scaler_function; static void init_renderer(void); static void deinit_renderer(void); static void init_texture(void); static void deinit_texture(void); static int window_get_display_index(void); static void window_center_in_display(int display_index); static void calc_dst_render_rect(SDL_Surface *src_surface, SDL_Rect *dst_rect); static void scale_and_flip(SDL_Surface *); void init_video(void) { if (SDL_WasInit(SDL_INIT_VIDEO)) return; if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) { fprintf(stderr, "error: failed to initialize SDL video: %s\n", SDL_GetError()); exit(1); } // Create the software surfaces that the game renders to. These are all 320x200x8 regardless // of the window size or monitor resolution. VGAScreen = VGAScreenSeg = SDL_CreateRGBSurface(0, vga_width, vga_height, 8, 0, 0, 0, 0); VGAScreen2 = SDL_CreateRGBSurface(0, vga_width, vga_height, 8, 0, 0, 0, 0); game_screen = SDL_CreateRGBSurface(0, vga_width, vga_height, 8, 0, 0, 0, 0); // The game code writes to surface->pixels directly without locking, so make sure that we // indeed created software surfaces that support this. assert(!SDL_MUSTLOCK(VGAScreen)); assert(!SDL_MUSTLOCK(VGAScreen2)); assert(!SDL_MUSTLOCK(game_screen)); JE_clr256(VGAScreen); // Create the window with a temporary initial size, hidden until we set up the // scaler and find the true window size main_window = SDL_CreateWindow("OpenTyrian", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, vga_width, vga_height, SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN); if (main_window == NULL) { fprintf(stderr, "error: failed to create window: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } reinit_fullscreen(fullscreen_display); init_renderer(); init_texture(); init_scaler(scaler); SDL_ShowWindow(main_window); SDL_SetRenderDrawColor(main_window_renderer, 0, 0, 0, 255); SDL_RenderClear(main_window_renderer); SDL_RenderPresent(main_window_renderer); } void deinit_video(void) { deinit_texture(); deinit_renderer(); SDL_DestroyWindow(main_window); SDL_FreeSurface(VGAScreenSeg); SDL_FreeSurface(VGAScreen2); SDL_FreeSurface(game_screen); SDL_QuitSubSystem(SDL_INIT_VIDEO); } static void init_renderer(void) { main_window_renderer = SDL_CreateRenderer(main_window, -1, 0); if (main_window_renderer == NULL) { fprintf(stderr, "error: failed to create renderer: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } } static void deinit_renderer(void) { if (main_window_renderer != NULL) { SDL_DestroyRenderer(main_window_renderer); main_window_renderer = NULL; } } static void init_texture(void) { assert(main_window_renderer != NULL); int bpp = 32; // TODOSDL2 Uint32 format = bpp == 32 ? SDL_PIXELFORMAT_RGB888 : SDL_PIXELFORMAT_RGB565; int scaler_w = scalers[scaler].width; int scaler_h = scalers[scaler].height; main_window_tex_format = SDL_AllocFormat(format); main_window_texture = SDL_CreateTexture(main_window_renderer, format, SDL_TEXTUREACCESS_STREAMING, scaler_w, scaler_h); if (main_window_texture == NULL) { fprintf(stderr, "error: failed to create scaler texture %dx%dx%s: %s\n", scaler_w, scaler_h, SDL_GetPixelFormatName(format), SDL_GetError()); exit(EXIT_FAILURE); } } static void deinit_texture(void) { if (main_window_texture != NULL) { SDL_DestroyTexture(main_window_texture); main_window_texture = NULL; } if (main_window_tex_format != NULL) { SDL_FreeFormat(main_window_tex_format); main_window_tex_format = NULL; } } static int window_get_display_index(void) { return SDL_GetWindowDisplayIndex(main_window); } static void window_center_in_display(int display_index) { int win_w, win_h; SDL_GetWindowSize(main_window, &win_w, &win_h); SDL_Rect bounds; SDL_GetDisplayBounds(display_index, &bounds); SDL_SetWindowPosition(main_window, bounds.x + (bounds.w - win_w) / 2, bounds.y + (bounds.h - win_h) / 2); } void reinit_fullscreen(int new_display) { fullscreen_display = new_display; if (fullscreen_display >= SDL_GetNumVideoDisplays()) { fullscreen_display = 0; } SDL_SetWindowFullscreen(main_window, SDL_FALSE); SDL_SetWindowSize(main_window, scalers[scaler].width, scalers[scaler].height); if (fullscreen_display == -1) { window_center_in_display(window_get_display_index()); } else { window_center_in_display(fullscreen_display); if (SDL_SetWindowFullscreen(main_window, SDL_WINDOW_FULLSCREEN_DESKTOP) != 0) { reinit_fullscreen(-1); return; } } } void video_on_win_resize(void) { int w, h; int scaler_w, scaler_h; // Tell video to reinit if the window was manually resized by the user. // Also enforce a minimum size on the window. SDL_GetWindowSize(main_window, &w, &h); scaler_w = scalers[scaler].width; scaler_h = scalers[scaler].height; if (w < scaler_w || h < scaler_h) { w = w < scaler_w ? scaler_w : w; h = h < scaler_h ? scaler_h : h; SDL_SetWindowSize(main_window, w, h); } } void toggle_fullscreen(void) { if (fullscreen_display != -1) reinit_fullscreen(-1); else reinit_fullscreen(SDL_GetWindowDisplayIndex(main_window)); } bool init_scaler(unsigned int new_scaler) { int w = scalers[new_scaler].width, h = scalers[new_scaler].height; int bpp = main_window_tex_format->BitsPerPixel; // TODOSDL2 scaler = new_scaler; deinit_texture(); init_texture(); if (fullscreen_display == -1) { // Changing scalers, when not in fullscreen mode, forces the window // to resize to exactly match the scaler's output dimensions. SDL_SetWindowSize(main_window, w, h); window_center_in_display(window_get_display_index()); } switch (bpp) { case 32: scaler_function = scalers[scaler].scaler32; break; case 16: scaler_function = scalers[scaler].scaler16; break; default: scaler_function = NULL; break; } if (scaler_function == NULL) { assert(false); return false; } return true; } bool set_scaling_mode_by_name(const char *name) { for (int i = 0; i < ScalingMode_MAX; ++i) { if (strcmp(name, scaling_mode_names[i]) == 0) { scaling_mode = i; return true; } } return false; } void JE_clr256(SDL_Surface *screen) { SDL_FillRect(screen, NULL, 0); } void JE_showVGA(void) { scale_and_flip(VGAScreen); } static void calc_dst_render_rect(SDL_Surface *const src_surface, SDL_Rect *const dst_rect) { // Decides how the logical output texture (after software scaling applied) will fit // in the window. int win_w, win_h; SDL_GetWindowSize(main_window, &win_w, &win_h); int maxh_width, maxw_height; switch (scaling_mode) { case SCALE_CENTER: SDL_QueryTexture(main_window_texture, NULL, NULL, &dst_rect->w, &dst_rect->h); break; case SCALE_INTEGER: dst_rect->w = src_surface->w; dst_rect->h = src_surface->h; while (dst_rect->w + src_surface->w <= win_w && dst_rect->h + src_surface->h <= win_h) { dst_rect->w += src_surface->w; dst_rect->h += src_surface->h; } break; case SCALE_ASPECT_8_5: maxh_width = win_h * (8.f / 5.f); maxw_height = win_w * (5.f / 8.f); if (maxh_width > win_w) { dst_rect->w = win_w; dst_rect->h = maxw_height; } else { dst_rect->w = maxh_width; dst_rect->h = win_h; } break; case SCALE_ASPECT_4_3: maxh_width = win_h * (4.f / 3.f); maxw_height = win_w * (3.f / 4.f); if (maxh_width > win_w) { dst_rect->w = win_w; dst_rect->h = maxw_height; } else { dst_rect->w = maxh_width; dst_rect->h = win_h; } break; case ScalingMode_MAX: assert(false); break; } dst_rect->x = (win_w - dst_rect->w) / 2; dst_rect->y = (win_h - dst_rect->h) / 2; } static void scale_and_flip(SDL_Surface *src_surface) { assert(src_surface->format->BitsPerPixel == 8); // Do software scaling assert(scaler_function != NULL); scaler_function(src_surface, main_window_texture); SDL_Rect dst_rect; calc_dst_render_rect(src_surface, &dst_rect); // Clear the window and blit the output texture to it SDL_SetRenderDrawColor(main_window_renderer, 0, 0, 0, 255); SDL_RenderClear(main_window_renderer); SDL_RenderCopy(main_window_renderer, main_window_texture, NULL, &dst_rect); SDL_RenderPresent(main_window_renderer); // Save output rect to be used by mouse functions last_output_rect = dst_rect; } /** Maps a specified point in game screen coordinates to window coordinates. */ void mapScreenPointToWindow(Sint32 *const inout_x, Sint32 *const inout_y) { *inout_x = (2 * *inout_x + 1) * last_output_rect.w / (2 * VGAScreen->w) + last_output_rect.x; *inout_y = (2 * *inout_y + 1) * last_output_rect.h / (2 * VGAScreen->h) + last_output_rect.y; } /** Maps a specified point in window coordinates to game screen coordinates. */ void mapWindowPointToScreen(Sint32 *const inout_x, Sint32 *const inout_y) { *inout_x = (2 * (*inout_x - last_output_rect.x) + 1) * VGAScreen->w / (2 * last_output_rect.w); *inout_y = (2 * (*inout_y - last_output_rect.y) + 1) * VGAScreen->h / (2 * last_output_rect.h); } /** Scales a distance in window coordinates to game screen coordinates. */ void scaleWindowDistanceToScreen(Sint32 *const inout_x, Sint32 *const inout_y) { *inout_x = (2 * *inout_x + 1) * VGAScreen->w / (2 * last_output_rect.w); *inout_y = (2 * *inout_y + 1) * VGAScreen->h / (2 * last_output_rect.h); } opentyrian-2.1.20221123/src/video.h000066400000000000000000000036221432005211200164760ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef VIDEO_H #define VIDEO_H #include "opentyr.h" #include "SDL.h" #define vga_width 320 #define vga_height 200 typedef enum { SCALE_CENTER, SCALE_INTEGER, SCALE_ASPECT_8_5, SCALE_ASPECT_4_3, ScalingMode_MAX } ScalingMode; extern const char *const scaling_mode_names[ScalingMode_MAX]; extern int fullscreen_display; // -1 means windowed extern ScalingMode scaling_mode; extern SDL_Surface *VGAScreen, *VGAScreenSeg; extern SDL_Surface *game_screen; extern SDL_Surface *VGAScreen2; extern SDL_Window *main_window; extern SDL_PixelFormat *main_window_tex_format; void init_video(void); void video_on_win_resize(void); void reinit_fullscreen(int new_display); void toggle_fullscreen(void); bool init_scaler(unsigned int new_scaler); bool set_scaling_mode_by_name(const char *name); void deinit_video(void); void JE_clr256(SDL_Surface *); void JE_showVGA(void); void mapScreenPointToWindow(Sint32 *inout_x, Sint32 *inout_y); void mapWindowPointToScreen(Sint32 *inout_x, Sint32 *inout_y); void scaleWindowDistanceToScreen(Sint32 *inout_x, Sint32 *inout_y); #endif /* VIDEO_H */ opentyrian-2.1.20221123/src/video_scale.c000066400000000000000000000272631432005211200176470ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2010 The OpenTyrian Development Team * * Scale2x, Scale3x * Copyright (C) 2001, 2002, 2003, 2004 Andrea Mazzoleni * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT 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. */ #include "video_scale.h" #include "palette.h" #include "video.h" #include #include static void nn_32(SDL_Surface *src_surface, SDL_Texture *dst_texture); static void nn_16(SDL_Surface *src_surface, SDL_Texture *dst_texture); static void scale2x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture); static void scale2x_16(SDL_Surface *src_surface, SDL_Texture *dst_texture); static void scale3x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture); static void scale3x_16(SDL_Surface *src_surface, SDL_Texture *dst_texture); void hq2x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture); void hq3x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture); void hq4x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture); uint scaler; const struct Scalers scalers[] = { { 1 * vga_width, 1 * vga_height, nn_16, nn_32, "None" }, { 2 * vga_width, 2 * vga_height, nn_16, nn_32, "2x" }, { 2 * vga_width, 2 * vga_height, scale2x_16, scale2x_32, "Scale2x" }, { 2 * vga_width, 2 * vga_height, NULL, hq2x_32, "hq2x" }, { 3 * vga_width, 3 * vga_height, nn_16, nn_32, "3x" }, { 3 * vga_width, 3 * vga_height, scale3x_16, scale3x_32, "Scale3x" }, { 3 * vga_width, 3 * vga_height, NULL, hq3x_32, "hq3x" }, { 4 * vga_width, 4 * vga_height, nn_16, nn_32, "4x" }, { 4 * vga_width, 4 * vga_height, NULL, hq4x_32, "hq4x" }, }; const uint scalers_count = COUNTOF(scalers); void set_scaler_by_name(const char *name) { for (uint i = 0; i < scalers_count; ++i) { if (strcmp(name, scalers[i].name) == 0) { scaler = i; break; } } } void nn_32(SDL_Surface *src_surface, SDL_Texture *dst_texture) { Uint8 *src = src_surface->pixels, *src_temp; Uint8 *dst, *dst_temp; int src_pitch = src_surface->pitch; int dst_pitch; const int dst_Bpp = 4; // dst_surface->format->BytesPerPixel int dst_width, dst_height; SDL_QueryTexture(dst_texture, NULL, NULL, &dst_width, &dst_height); const int height = vga_height, // src_surface->h width = vga_width, // src_surface->w scale = dst_width / width; assert(scale == dst_height / height); void* tmp_ptr; SDL_LockTexture(dst_texture, NULL, &tmp_ptr, &dst_pitch); dst = tmp_ptr; for (int y = height; y > 0; y--) { src_temp = src; dst_temp = dst; for (int x = width; x > 0; x--) { for (int z = scale; z > 0; z--) { *(Uint32 *)dst = rgb_palette[*src]; dst += dst_Bpp; } src++; } src = src_temp + src_pitch; dst = dst_temp + dst_pitch; for (int z = scale; z > 1; z--) { memcpy(dst, dst_temp, dst_width * dst_Bpp); dst += dst_pitch; } } SDL_UnlockTexture(dst_texture); } void nn_16(SDL_Surface *src_surface, SDL_Texture *dst_texture) { Uint8 *src = src_surface->pixels, *src_temp; Uint8 *dst, *dst_temp; int src_pitch = src_surface->pitch; int dst_pitch; const int dst_Bpp = 2; // dst_surface->format->BytesPerPixel int dst_width, dst_height; SDL_QueryTexture(dst_texture, NULL, NULL, &dst_width, &dst_height); const int height = vga_height, // src_surface->h width = vga_width, // src_surface->w scale = dst_width / width; assert(scale == dst_height / height); void* tmp_ptr; SDL_LockTexture(dst_texture, NULL, &tmp_ptr, &dst_pitch); dst = tmp_ptr; for (int y = height; y > 0; y--) { src_temp = src; dst_temp = dst; for (int x = width; x > 0; x--) { for (int z = scale; z > 0; z--) { *(Uint16 *)dst = rgb_palette[*src]; dst += dst_Bpp; } src++; } src = src_temp + src_pitch; dst = dst_temp + dst_pitch; for (int z = scale; z > 1; z--) { memcpy(dst, dst_temp, dst_width * dst_Bpp); dst += dst_pitch; } } SDL_UnlockTexture(dst_texture); } void scale2x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture) { Uint8 *src = src_surface->pixels, *src_temp; Uint8 *dst, *dst_temp; int src_pitch = src_surface->pitch; int dst_pitch; const int dst_Bpp = 4, // dst_surface->format->BytesPerPixel height = vga_height, // src_surface->h width = vga_width; // src_surface->w void* tmp_ptr; SDL_LockTexture(dst_texture, NULL, &tmp_ptr, &dst_pitch); dst = tmp_ptr; int prevline, nextline; Uint32 E0, E1, E2, E3, B, D, E, F, H; for (int y = 0; y < height; y++) { src_temp = src; dst_temp = dst; prevline = (y > 0) ? -src_pitch : 0; nextline = (y < height - 1) ? src_pitch : 0; for (int x = 0; x < width; x++) { B = rgb_palette[*(src + prevline)]; D = rgb_palette[*(x > 0 ? src - 1 : src)]; E = rgb_palette[*src]; F = rgb_palette[*(x < width - 1 ? src + 1 : src)]; H = rgb_palette[*(src + nextline)]; if (B != H && D != F) { E0 = D == B ? D : E; E1 = B == F ? F : E; E2 = D == H ? D : E; E3 = H == F ? F : E; } else { E0 = E1 = E2 = E3 = E; } *(Uint32 *)dst = E0; *(Uint32 *)(dst + dst_Bpp) = E1; *(Uint32 *)(dst + dst_pitch) = E2; *(Uint32 *)(dst + dst_pitch + dst_Bpp) = E3; src++; dst += 2 * dst_Bpp; } src = src_temp + src_pitch; dst = dst_temp + 2 * dst_pitch; } SDL_UnlockTexture(dst_texture); } void scale2x_16(SDL_Surface *src_surface, SDL_Texture *dst_texture) { Uint8 *src = src_surface->pixels, *src_temp; Uint8 *dst, *dst_temp; int src_pitch = src_surface->pitch; int dst_pitch; const int dst_Bpp = 2, // dst_surface->format->BytesPerPixel height = vga_height, // src_surface->h width = vga_width; // src_surface->w void* tmp_ptr; SDL_LockTexture(dst_texture, NULL, &tmp_ptr, &dst_pitch); dst = tmp_ptr; int prevline, nextline; Uint16 E0, E1, E2, E3, B, D, E, F, H; for (int y = 0; y < height; y++) { src_temp = src; dst_temp = dst; prevline = (y > 0) ? -src_pitch : 0; nextline = (y < height - 1) ? src_pitch : 0; for (int x = 0; x < width; x++) { B = rgb_palette[*(src + prevline)]; D = rgb_palette[*(x > 0 ? src - 1 : src)]; E = rgb_palette[*src]; F = rgb_palette[*(x < width - 1 ? src + 1 : src)]; H = rgb_palette[*(src + nextline)]; if (B != H && D != F) { E0 = D == B ? D : E; E1 = B == F ? F : E; E2 = D == H ? D : E; E3 = H == F ? F : E; } else { E0 = E1 = E2 = E3 = E; } *(Uint16 *)dst = E0; *(Uint16 *)(dst + dst_Bpp) = E1; *(Uint16 *)(dst + dst_pitch) = E2; *(Uint16 *)(dst + dst_pitch + dst_Bpp) = E3; src++; dst += 2 * dst_Bpp; } src = src_temp + src_pitch; dst = dst_temp + 2 * dst_pitch; } SDL_UnlockTexture(dst_texture); } void scale3x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture) { Uint8 *src = src_surface->pixels, *src_temp; Uint8 *dst, *dst_temp; int src_pitch = src_surface->pitch; int dst_pitch; const int dst_Bpp = 4, // dst_surface->format->BytesPerPixel height = vga_height, // src_surface->h width = vga_width; // src_surface->w void* tmp_ptr; SDL_LockTexture(dst_texture, NULL, &tmp_ptr, &dst_pitch); dst = tmp_ptr; int prevline, nextline; Uint32 E0, E1, E2, E3, E4, E5, E6, E7, E8, A, B, C, D, E, F, G, H, I; for (int y = 0; y < height; y++) { src_temp = src; dst_temp = dst; prevline = (y > 0) ? -src_pitch : 0; nextline = (y < height - 1) ? src_pitch : 0; for (int x = 0; x < width; x++) { A = rgb_palette[*(src + prevline - (x > 0 ? 1 : 0))]; B = rgb_palette[*(src + prevline)]; C = rgb_palette[*(src + prevline + (x < width - 1 ? 1 : 0))]; D = rgb_palette[*(src - (x > 0 ? 1 : 0))]; E = rgb_palette[*src]; F = rgb_palette[*(src + (x < width - 1 ? 1 : 0))]; G = rgb_palette[*(src + nextline - (x > 0 ? 1 : 0))]; H = rgb_palette[*(src + nextline)]; I = rgb_palette[*(src + nextline + (x < width - 1 ? 1 : 0))]; if (B != H && D != F) { E0 = D == B ? D : E; E1 = (D == B && E != C) || (B == F && E != A) ? B : E; E2 = B == F ? F : E; E3 = (D == B && E != G) || (D == H && E != A) ? D : E; E4 = E; E5 = (B == F && E != I) || (H == F && E != C) ? F : E; E6 = D == H ? D : E; E7 = (D == H && E != I) || (H == F && E != G) ? H : E; E8 = H == F ? F : E; } else { E0 = E1 = E2 = E3 = E4 = E5 = E6 = E7 = E8 = E; } *(Uint32 *)dst = E0; *(Uint32 *)(dst + dst_Bpp) = E1; *(Uint32 *)(dst + 2 * dst_Bpp) = E2; *(Uint32 *)(dst + dst_pitch) = E3; *(Uint32 *)(dst + dst_pitch + dst_Bpp) = E4; *(Uint32 *)(dst + dst_pitch + 2 * dst_Bpp) = E5; *(Uint32 *)(dst + 2 * dst_pitch) = E6; *(Uint32 *)(dst + 2 * dst_pitch + dst_Bpp) = E7; *(Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp) = E8; src++; dst += 3 * dst_Bpp; } src = src_temp + src_pitch; dst = dst_temp + 3 * dst_pitch; } SDL_UnlockTexture(dst_texture); } void scale3x_16(SDL_Surface *src_surface, SDL_Texture *dst_texture) { Uint8 *src = src_surface->pixels, *src_temp; Uint8 *dst, *dst_temp; int src_pitch = src_surface->pitch; int dst_pitch; const int dst_Bpp = 2, // dst_surface->format->BytesPerPixel height = vga_height, // src_surface->h width = vga_width; // src_surface->w void* tmp_ptr; SDL_LockTexture(dst_texture, NULL, &tmp_ptr, &dst_pitch); dst = tmp_ptr; int prevline, nextline; Uint16 E0, E1, E2, E3, E4, E5, E6, E7, E8, A, B, C, D, E, F, G, H, I; for (int y = 0; y < height; y++) { src_temp = src; dst_temp = dst; prevline = (y > 0) ? -src_pitch : 0; nextline = (y < height - 1) ? src_pitch : 0; for (int x = 0; x < width; x++) { A = rgb_palette[*(src + prevline - (x > 0 ? 1 : 0))]; B = rgb_palette[*(src + prevline)]; C = rgb_palette[*(src + prevline + (x < width - 1 ? 1 : 0))]; D = rgb_palette[*(src - (x > 0 ? 1 : 0))]; E = rgb_palette[*src]; F = rgb_palette[*(src + (x < width - 1 ? 1 : 0))]; G = rgb_palette[*(src + nextline - (x > 0 ? 1 : 0))]; H = rgb_palette[*(src + nextline)]; I = rgb_palette[*(src + nextline + (x < width - 1 ? 1 : 0))]; if (B != H && D != F) { E0 = D == B ? D : E; E1 = (D == B && E != C) || (B == F && E != A) ? B : E; E2 = B == F ? F : E; E3 = (D == B && E != G) || (D == H && E != A) ? D : E; E4 = E; E5 = (B == F && E != I) || (H == F && E != C) ? F : E; E6 = D == H ? D : E; E7 = (D == H && E != I) || (H == F && E != G) ? H : E; E8 = H == F ? F : E; } else { E0 = E1 = E2 = E3 = E4 = E5 = E6 = E7 = E8 = E; } *(Uint16 *)dst = E0; *(Uint16 *)(dst + dst_Bpp) = E1; *(Uint16 *)(dst + 2 * dst_Bpp) = E2; *(Uint16 *)(dst + dst_pitch) = E3; *(Uint16 *)(dst + dst_pitch + dst_Bpp) = E4; *(Uint16 *)(dst + dst_pitch + 2 * dst_Bpp) = E5; *(Uint16 *)(dst + 2 * dst_pitch) = E6; *(Uint16 *)(dst + 2 * dst_pitch + dst_Bpp) = E7; *(Uint16 *)(dst + 2 * dst_pitch + 2 * dst_Bpp) = E8; src++; dst += 3 * dst_Bpp; } src = src_temp + src_pitch; dst = dst_temp + 3 * dst_pitch; } SDL_UnlockTexture(dst_texture); } opentyrian-2.1.20221123/src/video_scale.h000066400000000000000000000023521432005211200176440ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2010 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef VIDEO_SCALE_H #define VIDEO_SCALE_H #include "opentyr.h" #include "SDL.h" typedef void (*ScalerFunction)(SDL_Surface *src, SDL_Texture *dst); struct Scalers { int width, height; ScalerFunction scaler16, scaler32; const char *name; }; extern uint scaler; extern const struct Scalers scalers[]; extern const uint scalers_count; void set_scaler_by_name(const char *name); #endif /* VIDEO_SCALE_H */ opentyrian-2.1.20221123/src/video_scale_hqNx.c000066400000000000000000005520311432005211200206410ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2010 The OpenTyrian Development Team * * hq2x, hq3x, hq4x * Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT 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. */ #include "palette.h" #include "video.h" #include void interp1(Uint32 *pc, Uint32 c1, Uint32 c2); void interp2(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3); void interp3(Uint32 *pc, Uint32 c1, Uint32 c2); void interp4(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3); void interp5(Uint32 *pc, Uint32 c1, Uint32 c2); void interp6(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3); void interp7(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3); void interp8(Uint32 *pc, Uint32 c1, Uint32 c2); void interp9(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3); void interp10(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3); bool diff(unsigned int w1, unsigned int w2); void hq2x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture); void hq3x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture); void hq4x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture); static int YUV1, YUV2; const int Ymask = 0x00FF0000; const int Umask = 0x0000FF00; const int Vmask = 0x000000FF; const int trY = 0x00300000; const int trU = 0x00000700; const int trV = 0x00000006; inline void interp1(Uint32 *pc, Uint32 c1, Uint32 c2) { *pc = (c1*3+c2) >> 2; } inline void interp2(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3) { *pc = (c1*2+c2+c3) >> 2; } inline void interp3(Uint32 *pc, Uint32 c1, Uint32 c2) { //*((int*)pc) = (c1*7+c2)/8; *((int*)pc) = ((((c1 & 0x00FF00)*7 + (c2 & 0x00FF00) ) & 0x0007F800) + (((c1 & 0xFF00FF)*7 + (c2 & 0xFF00FF) ) & 0x07F807F8)) >> 3; } inline void interp4(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3) { //*((int*)pc) = (c1*2+(c2+c3)*7)/16; *((int*)pc) = ((((c1 & 0x00FF00)*2 + ((c2 & 0x00FF00) + (c3 & 0x00FF00))*7 ) & 0x000FF000) + (((c1 & 0xFF00FF)*2 + ((c2 & 0xFF00FF) + (c3 & 0xFF00FF))*7 ) & 0x0FF00FF0)) >> 4; } inline void interp5(Uint32 *pc, Uint32 c1, Uint32 c2) { *pc = (c1+c2) >> 1; } inline void interp6(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3) { //*pc = (c1*5+c2*2+c3)/8; *pc = ((((c1 & 0x00FF00)*5 + (c2 & 0x00FF00)*2 + (c3 & 0x00FF00) ) & 0x0007F800) + (((c1 & 0xFF00FF)*5 + (c2 & 0xFF00FF)*2 + (c3 & 0xFF00FF) ) & 0x07F807F8)) >> 3; } inline void interp7(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3) { //*pc = (c1*6+c2+c3)/8; *pc = ((((c1 & 0x00FF00)*6 + (c2 & 0x00FF00) + (c3 & 0x00FF00) ) & 0x0007F800) + (((c1 & 0xFF00FF)*6 + (c2 & 0xFF00FF) + (c3 & 0xFF00FF) ) & 0x07F807F8)) >> 3; } inline void interp8(Uint32 *pc, Uint32 c1, Uint32 c2) { //*pc = (c1*5+c2*3)/8; *pc = ((((c1 & 0x00FF00)*5 + (c2 & 0x00FF00)*3 ) & 0x0007F800) + (((c1 & 0xFF00FF)*5 + (c2 & 0xFF00FF)*3 ) & 0x07F807F8)) >> 3; } inline void interp9(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3) { //*pc = (c1*2+(c2+c3)*3)/8; *pc = ((((c1 & 0x00FF00)*2 + ((c2 & 0x00FF00) + (c3 & 0x00FF00))*3 ) & 0x0007F800) + (((c1 & 0xFF00FF)*2 + ((c2 & 0xFF00FF) + (c3 & 0xFF00FF))*3 ) & 0x07F807F8)) >> 3; } inline void interp10(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3) { //*pc = (c1*14+c2+c3)/16; *pc = ((((c1 & 0x00FF00)*14 + (c2 & 0x00FF00) + (c3 & 0x00FF00) ) & 0x000FF000) + (((c1 & 0xFF00FF)*14 + (c2 & 0xFF00FF) + (c3 & 0xFF00FF) ) & 0x0FF00FF0)) >> 4; } inline bool diff(unsigned int w1, unsigned int w2) { Uint32 YUV1 = yuv_palette[w1]; Uint32 YUV2 = yuv_palette[w2]; return ( ( abs((int)(YUV1 & Ymask) - (int)(YUV2 & Ymask)) > trY ) || ( abs((int)(YUV1 & Umask) - (int)(YUV2 & Umask)) > trU ) || ( abs((int)(YUV1 & Vmask) - (int)(YUV2 & Vmask)) > trV ) ); } #define PIXEL00_0 *(Uint32 *)dst = c[5]; #define PIXEL00_10 interp1((Uint32 *)dst, c[5], c[1]); #define PIXEL00_11 interp1((Uint32 *)dst, c[5], c[4]); #define PIXEL00_12 interp1((Uint32 *)dst, c[5], c[2]); #define PIXEL00_20 interp2((Uint32 *)dst, c[5], c[4], c[2]); #define PIXEL00_21 interp2((Uint32 *)dst, c[5], c[1], c[2]); #define PIXEL00_22 interp2((Uint32 *)dst, c[5], c[1], c[4]); #define PIXEL00_60 interp6((Uint32 *)dst, c[5], c[2], c[4]); #define PIXEL00_61 interp6((Uint32 *)dst, c[5], c[4], c[2]); #define PIXEL00_70 interp7((Uint32 *)dst, c[5], c[4], c[2]); #define PIXEL00_90 interp9((Uint32 *)dst, c[5], c[4], c[2]); #define PIXEL00_100 interp10((Uint32 *)dst, c[5], c[4], c[2]); #define PIXEL01_0 *(Uint32 *)(dst + dst_Bpp) = c[5]; #define PIXEL01_10 interp1((Uint32 *)(dst + dst_Bpp), c[5], c[3]); #define PIXEL01_11 interp1((Uint32 *)(dst + dst_Bpp), c[5], c[2]); #define PIXEL01_12 interp1((Uint32 *)(dst + dst_Bpp), c[5], c[6]); #define PIXEL01_20 interp2((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[6]); #define PIXEL01_21 interp2((Uint32 *)(dst + dst_Bpp), c[5], c[3], c[6]); #define PIXEL01_22 interp2((Uint32 *)(dst + dst_Bpp), c[5], c[3], c[2]); #define PIXEL01_60 interp6((Uint32 *)(dst + dst_Bpp), c[5], c[6], c[2]); #define PIXEL01_61 interp6((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[6]); #define PIXEL01_70 interp7((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[6]); #define PIXEL01_90 interp9((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[6]); #define PIXEL01_100 interp10((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[6]); #define PIXEL10_0 *(Uint32 *)(dst + dst_pitch) = c[5]; #define PIXEL10_10 interp1((Uint32 *)(dst + dst_pitch), c[5], c[7]); #define PIXEL10_11 interp1((Uint32 *)(dst + dst_pitch), c[5], c[8]); #define PIXEL10_12 interp1((Uint32 *)(dst + dst_pitch), c[5], c[4]); #define PIXEL10_20 interp2((Uint32 *)(dst + dst_pitch), c[5], c[8], c[4]); #define PIXEL10_21 interp2((Uint32 *)(dst + dst_pitch), c[5], c[7], c[4]); #define PIXEL10_22 interp2((Uint32 *)(dst + dst_pitch), c[5], c[7], c[8]); #define PIXEL10_60 interp6((Uint32 *)(dst + dst_pitch), c[5], c[4], c[8]); #define PIXEL10_61 interp6((Uint32 *)(dst + dst_pitch), c[5], c[8], c[4]); #define PIXEL10_70 interp7((Uint32 *)(dst + dst_pitch), c[5], c[8], c[4]); #define PIXEL10_90 interp9((Uint32 *)(dst + dst_pitch), c[5], c[8], c[4]); #define PIXEL10_100 interp10((Uint32 *)(dst + dst_pitch), c[5], c[8], c[4]); #define PIXEL11_0 *(Uint32 *)(dst + dst_pitch + dst_Bpp) = c[5]; #define PIXEL11_10 interp1((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[9]); #define PIXEL11_11 interp1((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[6]); #define PIXEL11_12 interp1((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[8]); #define PIXEL11_20 interp2((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[6], c[8]); #define PIXEL11_21 interp2((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[9], c[8]); #define PIXEL11_22 interp2((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[9], c[6]); #define PIXEL11_60 interp6((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[8], c[6]); #define PIXEL11_61 interp6((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[6], c[8]); #define PIXEL11_70 interp7((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[6], c[8]); #define PIXEL11_90 interp9((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[6], c[8]); #define PIXEL11_100 interp10((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[6], c[8]); void hq2x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture) { Uint8 *src = src_surface->pixels, *src_temp; Uint8 *dst, *dst_temp; int src_pitch = src_surface->pitch; int dst_pitch; const int dst_Bpp = 4, // dst_surface->format->BytesPerPixel height = vga_height, // src_surface->h width = vga_width; // src_surface->w void* tmp_ptr; SDL_LockTexture(dst_texture, NULL, &tmp_ptr, &dst_pitch); dst = tmp_ptr; int prevline, nextline; Uint32 w[10]; Uint32 c[10]; // +----+----+----+ // | | | | // | w1 | w2 | w3 | // +----+----+----+ // | | | | // | w4 | w5 | w6 | // +----+----+----+ // | | | | // | w7 | w8 | w9 | // +----+----+----+ for (int j = 0; j < height; j++) { src_temp = src; dst_temp = dst; prevline = (j > 0) ? -width : 0; nextline = (j < height - 1) ? width : 0; for (int i = 0; i < width; i++) { w[2] = *(src + prevline); w[5] = *src; w[8] = *(src + nextline); if (i > 0) { w[1] = *(src + prevline - 1); w[4] = *(src - 1); w[7] = *(src + nextline - 1); } else { w[1] = w[2]; w[4] = w[5]; w[7] = w[8]; } if (i < width - 1) { w[3] = *(src + prevline + 1); w[6] = *(src + 1); w[9] = *(src + nextline + 1); } else { w[3] = w[2]; w[6] = w[5]; w[9] = w[8]; } int pattern = 0; int flag = 1; YUV1 = yuv_palette[w[5]]; for (int k=1; k<=9; k++) { if (k==5) continue; if (w[k] != w[5]) { YUV2 = yuv_palette[w[k]]; if ( ( abs((YUV1 & Ymask) - (YUV2 & Ymask)) > trY ) || ( abs((YUV1 & Umask) - (YUV2 & Umask)) > trU ) || ( abs((YUV1 & Vmask) - (YUV2 & Vmask)) > trV ) ) pattern |= flag; } flag <<= 1; } for (int k=1; k<=9; k++) c[k] = rgb_palette[w[k]] & 0xfcfcfcfc; // hq2x has a nasty inability to accept more than 6 bits for each component switch (pattern) { case 0: case 1: case 4: case 32: case 128: case 5: case 132: case 160: case 33: case 129: case 36: case 133: case 164: case 161: case 37: case 165: { PIXEL00_20 PIXEL01_20 PIXEL10_20 PIXEL11_20 break; } case 2: case 34: case 130: case 162: { PIXEL00_22 PIXEL01_21 PIXEL10_20 PIXEL11_20 break; } case 16: case 17: case 48: case 49: { PIXEL00_20 PIXEL01_22 PIXEL10_20 PIXEL11_21 break; } case 64: case 65: case 68: case 69: { PIXEL00_20 PIXEL01_20 PIXEL10_21 PIXEL11_22 break; } case 8: case 12: case 136: case 140: { PIXEL00_21 PIXEL01_20 PIXEL10_22 PIXEL11_20 break; } case 3: case 35: case 131: case 163: { PIXEL00_11 PIXEL01_21 PIXEL10_20 PIXEL11_20 break; } case 6: case 38: case 134: case 166: { PIXEL00_22 PIXEL01_12 PIXEL10_20 PIXEL11_20 break; } case 20: case 21: case 52: case 53: { PIXEL00_20 PIXEL01_11 PIXEL10_20 PIXEL11_21 break; } case 144: case 145: case 176: case 177: { PIXEL00_20 PIXEL01_22 PIXEL10_20 PIXEL11_12 break; } case 192: case 193: case 196: case 197: { PIXEL00_20 PIXEL01_20 PIXEL10_21 PIXEL11_11 break; } case 96: case 97: case 100: case 101: { PIXEL00_20 PIXEL01_20 PIXEL10_12 PIXEL11_22 break; } case 40: case 44: case 168: case 172: { PIXEL00_21 PIXEL01_20 PIXEL10_11 PIXEL11_20 break; } case 9: case 13: case 137: case 141: { PIXEL00_12 PIXEL01_20 PIXEL10_22 PIXEL11_20 break; } case 18: case 50: { PIXEL00_22 if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_20 } PIXEL10_20 PIXEL11_21 break; } case 80: case 81: { PIXEL00_20 PIXEL01_22 PIXEL10_21 if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_20 } break; } case 72: case 76: { PIXEL00_21 PIXEL01_20 if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_20 } PIXEL11_22 break; } case 10: case 138: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_20 } PIXEL01_21 PIXEL10_22 PIXEL11_20 break; } case 66: { PIXEL00_22 PIXEL01_21 PIXEL10_21 PIXEL11_22 break; } case 24: { PIXEL00_21 PIXEL01_22 PIXEL10_22 PIXEL11_21 break; } case 7: case 39: case 135: { PIXEL00_11 PIXEL01_12 PIXEL10_20 PIXEL11_20 break; } case 148: case 149: case 180: { PIXEL00_20 PIXEL01_11 PIXEL10_20 PIXEL11_12 break; } case 224: case 228: case 225: { PIXEL00_20 PIXEL01_20 PIXEL10_12 PIXEL11_11 break; } case 41: case 169: case 45: { PIXEL00_12 PIXEL01_20 PIXEL10_11 PIXEL11_20 break; } case 22: case 54: { PIXEL00_22 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_20 PIXEL11_21 break; } case 208: case 209: { PIXEL00_20 PIXEL01_22 PIXEL10_21 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 104: case 108: { PIXEL00_21 PIXEL01_20 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } PIXEL11_22 break; } case 11: case 139: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } PIXEL01_21 PIXEL10_22 PIXEL11_20 break; } case 19: case 51: { if (diff(w[2], w[6])) { PIXEL00_11 PIXEL01_10 } else { PIXEL00_60 PIXEL01_90 } PIXEL10_20 PIXEL11_21 break; } case 146: case 178: { PIXEL00_22 if (diff(w[2], w[6])) { PIXEL01_10 PIXEL11_12 } else { PIXEL01_90 PIXEL11_61 } PIXEL10_20 break; } case 84: case 85: { PIXEL00_20 if (diff(w[6], w[8])) { PIXEL01_11 PIXEL11_10 } else { PIXEL01_60 PIXEL11_90 } PIXEL10_21 break; } case 112: case 113: { PIXEL00_20 PIXEL01_22 if (diff(w[6], w[8])) { PIXEL10_12 PIXEL11_10 } else { PIXEL10_61 PIXEL11_90 } break; } case 200: case 204: { PIXEL00_21 PIXEL01_20 if (diff(w[8], w[4])) { PIXEL10_10 PIXEL11_11 } else { PIXEL10_90 PIXEL11_60 } break; } case 73: case 77: { if (diff(w[8], w[4])) { PIXEL00_12 PIXEL10_10 } else { PIXEL00_61 PIXEL10_90 } PIXEL01_20 PIXEL11_22 break; } case 42: case 170: { if (diff(w[4], w[2])) { PIXEL00_10 PIXEL10_11 } else { PIXEL00_90 PIXEL10_60 } PIXEL01_21 PIXEL11_20 break; } case 14: case 142: { if (diff(w[4], w[2])) { PIXEL00_10 PIXEL01_12 } else { PIXEL00_90 PIXEL01_61 } PIXEL10_22 PIXEL11_20 break; } case 67: { PIXEL00_11 PIXEL01_21 PIXEL10_21 PIXEL11_22 break; } case 70: { PIXEL00_22 PIXEL01_12 PIXEL10_21 PIXEL11_22 break; } case 28: { PIXEL00_21 PIXEL01_11 PIXEL10_22 PIXEL11_21 break; } case 152: { PIXEL00_21 PIXEL01_22 PIXEL10_22 PIXEL11_12 break; } case 194: { PIXEL00_22 PIXEL01_21 PIXEL10_21 PIXEL11_11 break; } case 98: { PIXEL00_22 PIXEL01_21 PIXEL10_12 PIXEL11_22 break; } case 56: { PIXEL00_21 PIXEL01_22 PIXEL10_11 PIXEL11_21 break; } case 25: { PIXEL00_12 PIXEL01_22 PIXEL10_22 PIXEL11_21 break; } case 26: case 31: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_22 PIXEL11_21 break; } case 82: case 214: { PIXEL00_22 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_21 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 88: case 248: { PIXEL00_21 PIXEL01_22 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 74: case 107: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } PIXEL01_21 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } PIXEL11_22 break; } case 27: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } PIXEL01_10 PIXEL10_22 PIXEL11_21 break; } case 86: { PIXEL00_22 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_21 PIXEL11_10 break; } case 216: { PIXEL00_21 PIXEL01_22 PIXEL10_10 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 106: { PIXEL00_10 PIXEL01_21 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } PIXEL11_22 break; } case 30: { PIXEL00_10 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_22 PIXEL11_21 break; } case 210: { PIXEL00_22 PIXEL01_10 PIXEL10_21 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 120: { PIXEL00_21 PIXEL01_22 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } PIXEL11_10 break; } case 75: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } PIXEL01_21 PIXEL10_10 PIXEL11_22 break; } case 29: { PIXEL00_12 PIXEL01_11 PIXEL10_22 PIXEL11_21 break; } case 198: { PIXEL00_22 PIXEL01_12 PIXEL10_21 PIXEL11_11 break; } case 184: { PIXEL00_21 PIXEL01_22 PIXEL10_11 PIXEL11_12 break; } case 99: { PIXEL00_11 PIXEL01_21 PIXEL10_12 PIXEL11_22 break; } case 57: { PIXEL00_12 PIXEL01_22 PIXEL10_11 PIXEL11_21 break; } case 71: { PIXEL00_11 PIXEL01_12 PIXEL10_21 PIXEL11_22 break; } case 156: { PIXEL00_21 PIXEL01_11 PIXEL10_22 PIXEL11_12 break; } case 226: { PIXEL00_22 PIXEL01_21 PIXEL10_12 PIXEL11_11 break; } case 60: { PIXEL00_21 PIXEL01_11 PIXEL10_11 PIXEL11_21 break; } case 195: { PIXEL00_11 PIXEL01_21 PIXEL10_21 PIXEL11_11 break; } case 102: { PIXEL00_22 PIXEL01_12 PIXEL10_12 PIXEL11_22 break; } case 153: { PIXEL00_12 PIXEL01_22 PIXEL10_22 PIXEL11_12 break; } case 58: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } PIXEL10_11 PIXEL11_21 break; } case 83: { PIXEL00_11 if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } PIXEL10_21 if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 92: { PIXEL00_21 PIXEL01_11 if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 202: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } PIXEL01_21 if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } PIXEL11_11 break; } case 78: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } PIXEL01_12 if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } PIXEL11_22 break; } case 154: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } PIXEL10_22 PIXEL11_12 break; } case 114: { PIXEL00_22 if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } PIXEL10_12 if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 89: { PIXEL00_12 PIXEL01_22 if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 90: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 55: case 23: { if (diff(w[2], w[6])) { PIXEL00_11 PIXEL01_0 } else { PIXEL00_60 PIXEL01_90 } PIXEL10_20 PIXEL11_21 break; } case 182: case 150: { PIXEL00_22 if (diff(w[2], w[6])) { PIXEL01_0 PIXEL11_12 } else { PIXEL01_90 PIXEL11_61 } PIXEL10_20 break; } case 213: case 212: { PIXEL00_20 if (diff(w[6], w[8])) { PIXEL01_11 PIXEL11_0 } else { PIXEL01_60 PIXEL11_90 } PIXEL10_21 break; } case 241: case 240: { PIXEL00_20 PIXEL01_22 if (diff(w[6], w[8])) { PIXEL10_12 PIXEL11_0 } else { PIXEL10_61 PIXEL11_90 } break; } case 236: case 232: { PIXEL00_21 PIXEL01_20 if (diff(w[8], w[4])) { PIXEL10_0 PIXEL11_11 } else { PIXEL10_90 PIXEL11_60 } break; } case 109: case 105: { if (diff(w[8], w[4])) { PIXEL00_12 PIXEL10_0 } else { PIXEL00_61 PIXEL10_90 } PIXEL01_20 PIXEL11_22 break; } case 171: case 43: { if (diff(w[4], w[2])) { PIXEL00_0 PIXEL10_11 } else { PIXEL00_90 PIXEL10_60 } PIXEL01_21 PIXEL11_20 break; } case 143: case 15: { if (diff(w[4], w[2])) { PIXEL00_0 PIXEL01_12 } else { PIXEL00_90 PIXEL01_61 } PIXEL10_22 PIXEL11_20 break; } case 124: { PIXEL00_21 PIXEL01_11 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } PIXEL11_10 break; } case 203: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } PIXEL01_21 PIXEL10_10 PIXEL11_11 break; } case 62: { PIXEL00_10 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_11 PIXEL11_21 break; } case 211: { PIXEL00_11 PIXEL01_10 PIXEL10_21 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 118: { PIXEL00_22 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_12 PIXEL11_10 break; } case 217: { PIXEL00_12 PIXEL01_22 PIXEL10_10 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 110: { PIXEL00_10 PIXEL01_12 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } PIXEL11_22 break; } case 155: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } PIXEL01_10 PIXEL10_22 PIXEL11_12 break; } case 188: { PIXEL00_21 PIXEL01_11 PIXEL10_11 PIXEL11_12 break; } case 185: { PIXEL00_12 PIXEL01_22 PIXEL10_11 PIXEL11_12 break; } case 61: { PIXEL00_12 PIXEL01_11 PIXEL10_11 PIXEL11_21 break; } case 157: { PIXEL00_12 PIXEL01_11 PIXEL10_22 PIXEL11_12 break; } case 103: { PIXEL00_11 PIXEL01_12 PIXEL10_12 PIXEL11_22 break; } case 227: { PIXEL00_11 PIXEL01_21 PIXEL10_12 PIXEL11_11 break; } case 230: { PIXEL00_22 PIXEL01_12 PIXEL10_12 PIXEL11_11 break; } case 199: { PIXEL00_11 PIXEL01_12 PIXEL10_21 PIXEL11_11 break; } case 220: { PIXEL00_21 PIXEL01_11 if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 158: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_22 PIXEL11_12 break; } case 234: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } PIXEL01_21 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } PIXEL11_11 break; } case 242: { PIXEL00_22 if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } PIXEL10_12 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 59: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } PIXEL10_11 PIXEL11_21 break; } case 121: { PIXEL00_12 PIXEL01_22 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 87: { PIXEL00_11 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_21 if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 79: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } PIXEL01_12 if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } PIXEL11_22 break; } case 122: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 94: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 218: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 91: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 229: { PIXEL00_20 PIXEL01_20 PIXEL10_12 PIXEL11_11 break; } case 167: { PIXEL00_11 PIXEL01_12 PIXEL10_20 PIXEL11_20 break; } case 173: { PIXEL00_12 PIXEL01_20 PIXEL10_11 PIXEL11_20 break; } case 181: { PIXEL00_20 PIXEL01_11 PIXEL10_20 PIXEL11_12 break; } case 186: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } PIXEL10_11 PIXEL11_12 break; } case 115: { PIXEL00_11 if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } PIXEL10_12 if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 93: { PIXEL00_12 PIXEL01_11 if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 206: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } PIXEL01_12 if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } PIXEL11_11 break; } case 205: case 201: { PIXEL00_12 PIXEL01_20 if (diff(w[8], w[4])) { PIXEL10_10 } else { PIXEL10_70 } PIXEL11_11 break; } case 174: case 46: { if (diff(w[4], w[2])) { PIXEL00_10 } else { PIXEL00_70 } PIXEL01_12 PIXEL10_11 PIXEL11_20 break; } case 179: case 147: { PIXEL00_11 if (diff(w[2], w[6])) { PIXEL01_10 } else { PIXEL01_70 } PIXEL10_20 PIXEL11_12 break; } case 117: case 116: { PIXEL00_20 PIXEL01_11 PIXEL10_12 if (diff(w[6], w[8])) { PIXEL11_10 } else { PIXEL11_70 } break; } case 189: { PIXEL00_12 PIXEL01_11 PIXEL10_11 PIXEL11_12 break; } case 231: { PIXEL00_11 PIXEL01_12 PIXEL10_12 PIXEL11_11 break; } case 126: { PIXEL00_10 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } PIXEL11_10 break; } case 219: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } PIXEL01_10 PIXEL10_10 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 125: { if (diff(w[8], w[4])) { PIXEL00_12 PIXEL10_0 } else { PIXEL00_61 PIXEL10_90 } PIXEL01_11 PIXEL11_10 break; } case 221: { PIXEL00_12 if (diff(w[6], w[8])) { PIXEL01_11 PIXEL11_0 } else { PIXEL01_60 PIXEL11_90 } PIXEL10_10 break; } case 207: { if (diff(w[4], w[2])) { PIXEL00_0 PIXEL01_12 } else { PIXEL00_90 PIXEL01_61 } PIXEL10_10 PIXEL11_11 break; } case 238: { PIXEL00_10 PIXEL01_12 if (diff(w[8], w[4])) { PIXEL10_0 PIXEL11_11 } else { PIXEL10_90 PIXEL11_60 } break; } case 190: { PIXEL00_10 if (diff(w[2], w[6])) { PIXEL01_0 PIXEL11_12 } else { PIXEL01_90 PIXEL11_61 } PIXEL10_11 break; } case 187: { if (diff(w[4], w[2])) { PIXEL00_0 PIXEL10_11 } else { PIXEL00_90 PIXEL10_60 } PIXEL01_10 PIXEL11_12 break; } case 243: { PIXEL00_11 PIXEL01_10 if (diff(w[6], w[8])) { PIXEL10_12 PIXEL11_0 } else { PIXEL10_61 PIXEL11_90 } break; } case 119: { if (diff(w[2], w[6])) { PIXEL00_11 PIXEL01_0 } else { PIXEL00_60 PIXEL01_90 } PIXEL10_12 PIXEL11_10 break; } case 237: case 233: { PIXEL00_12 PIXEL01_20 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_100 } PIXEL11_11 break; } case 175: case 47: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_100 } PIXEL01_12 PIXEL10_11 PIXEL11_20 break; } case 183: case 151: { PIXEL00_11 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_100 } PIXEL10_20 PIXEL11_12 break; } case 245: case 244: { PIXEL00_20 PIXEL01_11 PIXEL10_12 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_100 } break; } case 250: { PIXEL00_10 PIXEL01_10 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 123: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } PIXEL01_10 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } PIXEL11_10 break; } case 95: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_10 PIXEL11_10 break; } case 222: { PIXEL00_10 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_10 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 252: { PIXEL00_21 PIXEL01_11 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_100 } break; } case 249: { PIXEL00_12 PIXEL01_22 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_100 } if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 235: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } PIXEL01_21 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_100 } PIXEL11_11 break; } case 111: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_100 } PIXEL01_12 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } PIXEL11_22 break; } case 63: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_100 } if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_11 PIXEL11_21 break; } case 159: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_100 } PIXEL10_22 PIXEL11_12 break; } case 215: { PIXEL00_11 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_100 } PIXEL10_21 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 246: { PIXEL00_22 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } PIXEL10_12 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_100 } break; } case 254: { PIXEL00_10 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_100 } break; } case 253: { PIXEL00_12 PIXEL01_11 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_100 } if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_100 } break; } case 251: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } PIXEL01_10 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_100 } if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 239: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_100 } PIXEL01_12 if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_100 } PIXEL11_11 break; } case 127: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_100 } if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_20 } if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_20 } PIXEL11_10 break; } case 191: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_100 } if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_100 } PIXEL10_11 PIXEL11_12 break; } case 223: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_20 } if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_100 } PIXEL10_10 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_20 } break; } case 247: { PIXEL00_11 if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_100 } PIXEL10_12 if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_100 } break; } case 255: { if (diff(w[4], w[2])) { PIXEL00_0 } else { PIXEL00_100 } if (diff(w[2], w[6])) { PIXEL01_0 } else { PIXEL01_100 } if (diff(w[8], w[4])) { PIXEL10_0 } else { PIXEL10_100 } if (diff(w[6], w[8])) { PIXEL11_0 } else { PIXEL11_100 } break; } } src++; dst += 2 * dst_Bpp; } src = src_temp + src_pitch; dst = dst_temp + 2 * dst_pitch; } SDL_UnlockTexture(dst_texture); } #define PIXEL00_1M interp1((Uint32 *)dst, c[5], c[1]); #define PIXEL00_1U interp1((Uint32 *)dst, c[5], c[2]); #define PIXEL00_1L interp1((Uint32 *)dst, c[5], c[4]); #define PIXEL00_2 interp2((Uint32 *)dst, c[5], c[4], c[2]); #define PIXEL00_4 interp4((Uint32 *)dst, c[5], c[4], c[2]); #define PIXEL00_5 interp5((Uint32 *)dst, c[4], c[2]); #define PIXEL00_C *(Uint32 *)dst = c[5]; #define PIXEL01_1 interp1((Uint32 *)(dst + dst_Bpp), c[5], c[2]); #define PIXEL01_3 interp3((Uint32 *)(dst + dst_Bpp), c[5], c[2]); #define PIXEL01_6 interp1((Uint32 *)(dst + dst_Bpp), c[2], c[5]); #define PIXEL01_C *(Uint32 *)(dst + dst_Bpp) = c[5]; #define PIXEL02_1M interp1((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[3]); #define PIXEL02_1U interp1((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2]); #define PIXEL02_1R interp1((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[6]); #define PIXEL02_2 interp2((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2], c[6]); #define PIXEL02_4 interp4((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2], c[6]); #define PIXEL02_5 interp5((Uint32 *)(dst + 2 * dst_Bpp), c[2], c[6]); #define PIXEL02_C *(Uint32 *)(dst + 2 * dst_Bpp) = c[5]; #define PIXEL10_1 interp1((Uint32 *)(dst + dst_pitch), c[5], c[4]); #define PIXEL10_3 interp3((Uint32 *)(dst + dst_pitch), c[5], c[4]); #define PIXEL10_6 interp1((Uint32 *)(dst + dst_pitch), c[4], c[5]); #define PIXEL10_C *(Uint32 *)(dst + dst_pitch) = c[5]; #define PIXEL11 *(Uint32 *)(dst + dst_pitch + dst_Bpp) = c[5]; #define PIXEL12_1 interp1((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[5], c[6]); #define PIXEL12_3 interp3((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[5], c[6]); #define PIXEL12_6 interp1((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[6], c[5]); #define PIXEL12_C *(Uint32 *)(dst + dst_pitch + 2 * dst_Bpp) = c[5]; #define PIXEL20_1M interp1((Uint32 *)(dst + 2 * dst_pitch), c[5], c[7]); #define PIXEL20_1D interp1((Uint32 *)(dst + 2 * dst_pitch), c[5], c[8]); #define PIXEL20_1L interp1((Uint32 *)(dst + 2 * dst_pitch), c[5], c[4]); #define PIXEL20_2 interp2((Uint32 *)(dst + 2 * dst_pitch), c[5], c[8], c[4]); #define PIXEL20_4 interp4((Uint32 *)(dst + 2 * dst_pitch), c[5], c[8], c[4]); #define PIXEL20_5 interp5((Uint32 *)(dst + 2 * dst_pitch), c[8], c[4]); #define PIXEL20_C *(Uint32 *)(dst + 2 * dst_pitch) = c[5]; #define PIXEL21_1 interp1((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[5], c[8]); #define PIXEL21_3 interp3((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[5], c[8]); #define PIXEL21_6 interp1((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[8], c[5]); #define PIXEL21_C *(Uint32 *)(dst + 2 * dst_pitch + dst_Bpp) = c[5]; #define PIXEL22_1M interp1((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[9]); #define PIXEL22_1D interp1((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[8]); #define PIXEL22_1R interp1((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[6]); #define PIXEL22_2 interp2((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[6], c[8]); #define PIXEL22_4 interp4((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[6], c[8]); #define PIXEL22_5 interp5((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[6], c[8]); #define PIXEL22_C *(Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp) = c[5]; void hq3x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture) { Uint8 *src = src_surface->pixels, *src_temp; Uint8 *dst, *dst_temp; int src_pitch = src_surface->pitch; int dst_pitch; const int dst_Bpp = 4, // dst_surface->format->BytesPerPixel height = vga_height, // src_surface->h width = vga_width; // src_surface->w void* tmp_ptr; SDL_LockTexture(dst_texture, NULL, &tmp_ptr, &dst_pitch); dst = tmp_ptr; int prevline, nextline; Uint32 w[10]; Uint32 c[10]; // +----+----+----+ // | | | | // | w1 | w2 | w3 | // +----+----+----+ // | | | | // | w4 | w5 | w6 | // +----+----+----+ // | | | | // | w7 | w8 | w9 | // +----+----+----+ for (int j = 0; j < height; j++) { src_temp = src; dst_temp = dst; prevline = (j > 0) ? -width : 0; nextline = (j < height - 1) ? width : 0; for (int i = 0; i < width; i++) { w[2] = *(src + prevline); w[5] = *src; w[8] = *(src + nextline); if (i>0) { w[1] = *(src + prevline - 1); w[4] = *(src - 1); w[7] = *(src + nextline - 1); } else { w[1] = w[2]; w[4] = w[5]; w[7] = w[8]; } if (i < width - 1) { w[3] = *(src + prevline + 1); w[6] = *(src + 1); w[9] = *(src + nextline + 1); } else { w[3] = w[2]; w[6] = w[5]; w[9] = w[8]; } int pattern = 0; int flag = 1; YUV1 = yuv_palette[w[5]]; for (int k=1; k<=9; k++) { if (k==5) continue; if (w[k] != w[5]) { YUV2 = yuv_palette[w[k]]; if ( ( abs((YUV1 & Ymask) - (YUV2 & Ymask)) > trY ) || ( abs((YUV1 & Umask) - (YUV2 & Umask)) > trU ) || ( abs((YUV1 & Vmask) - (YUV2 & Vmask)) > trV ) ) pattern |= flag; } flag <<= 1; } for (int k=1; k<=9; k++) c[k] = rgb_palette[w[k]] & 0xfcfcfcfc; // hq3x has a nasty inability to accept more than 6 bits for each component switch (pattern) { case 0: case 1: case 4: case 32: case 128: case 5: case 132: case 160: case 33: case 129: case 36: case 133: case 164: case 161: case 37: case 165: { PIXEL00_2 PIXEL01_1 PIXEL02_2 PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_2 PIXEL21_1 PIXEL22_2 break; } case 2: case 34: case 130: case 162: { PIXEL00_1M PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_2 PIXEL21_1 PIXEL22_2 break; } case 16: case 17: case 48: case 49: { PIXEL00_2 PIXEL01_1 PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_2 PIXEL21_1 PIXEL22_1M break; } case 64: case 65: case 68: case 69: { PIXEL00_2 PIXEL01_1 PIXEL02_2 PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1M break; } case 8: case 12: case 136: case 140: { PIXEL00_1M PIXEL01_1 PIXEL02_2 PIXEL10_C PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_1 PIXEL22_2 break; } case 3: case 35: case 131: case 163: { PIXEL00_1L PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_2 PIXEL21_1 PIXEL22_2 break; } case 6: case 38: case 134: case 166: { PIXEL00_1M PIXEL01_C PIXEL02_1R PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_2 PIXEL21_1 PIXEL22_2 break; } case 20: case 21: case 52: case 53: { PIXEL00_2 PIXEL01_1 PIXEL02_1U PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_2 PIXEL21_1 PIXEL22_1M break; } case 144: case 145: case 176: case 177: { PIXEL00_2 PIXEL01_1 PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_2 PIXEL21_1 PIXEL22_1D break; } case 192: case 193: case 196: case 197: { PIXEL00_2 PIXEL01_1 PIXEL02_2 PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1R break; } case 96: case 97: case 100: case 101: { PIXEL00_2 PIXEL01_1 PIXEL02_2 PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1L PIXEL21_C PIXEL22_1M break; } case 40: case 44: case 168: case 172: { PIXEL00_1M PIXEL01_1 PIXEL02_2 PIXEL10_C PIXEL11 PIXEL12_1 PIXEL20_1D PIXEL21_1 PIXEL22_2 break; } case 9: case 13: case 137: case 141: { PIXEL00_1U PIXEL01_1 PIXEL02_2 PIXEL10_C PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_1 PIXEL22_2 break; } case 18: case 50: { PIXEL00_1M if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_1M PIXEL12_C } else { PIXEL01_3 PIXEL02_4 PIXEL12_3 } PIXEL10_1 PIXEL11 PIXEL20_2 PIXEL21_1 PIXEL22_1M break; } case 80: case 81: { PIXEL00_2 PIXEL01_1 PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL20_1M if (diff(w[6], w[8])) { PIXEL12_C PIXEL21_C PIXEL22_1M } else { PIXEL12_3 PIXEL21_3 PIXEL22_4 } break; } case 72: case 76: { PIXEL00_1M PIXEL01_1 PIXEL02_2 PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_1M PIXEL21_C } else { PIXEL10_3 PIXEL20_4 PIXEL21_3 } PIXEL22_1M break; } case 10: case 138: { if (diff(w[4], w[2])) { PIXEL00_1M PIXEL01_C PIXEL10_C } else { PIXEL00_4 PIXEL01_3 PIXEL10_3 } PIXEL02_1M PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_1 PIXEL22_2 break; } case 66: { PIXEL00_1M PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1M break; } case 24: { PIXEL00_1M PIXEL01_1 PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_1 PIXEL22_1M break; } case 7: case 39: case 135: { PIXEL00_1L PIXEL01_C PIXEL02_1R PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_2 PIXEL21_1 PIXEL22_2 break; } case 148: case 149: case 180: { PIXEL00_2 PIXEL01_1 PIXEL02_1U PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_2 PIXEL21_1 PIXEL22_1D break; } case 224: case 228: case 225: { PIXEL00_2 PIXEL01_1 PIXEL02_2 PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1L PIXEL21_C PIXEL22_1R break; } case 41: case 169: case 45: { PIXEL00_1U PIXEL01_1 PIXEL02_2 PIXEL10_C PIXEL11 PIXEL12_1 PIXEL20_1D PIXEL21_1 PIXEL22_2 break; } case 22: case 54: { PIXEL00_1M if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C PIXEL12_C } else { PIXEL01_3 PIXEL02_4 PIXEL12_3 } PIXEL10_1 PIXEL11 PIXEL20_2 PIXEL21_1 PIXEL22_1M break; } case 208: case 209: { PIXEL00_2 PIXEL01_1 PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL20_1M if (diff(w[6], w[8])) { PIXEL12_C PIXEL21_C PIXEL22_C } else { PIXEL12_3 PIXEL21_3 PIXEL22_4 } break; } case 104: case 108: { PIXEL00_1M PIXEL01_1 PIXEL02_2 PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C PIXEL21_C } else { PIXEL10_3 PIXEL20_4 PIXEL21_3 } PIXEL22_1M break; } case 11: case 139: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL10_C } else { PIXEL00_4 PIXEL01_3 PIXEL10_3 } PIXEL02_1M PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_1 PIXEL22_2 break; } case 19: case 51: { if (diff(w[2], w[6])) { PIXEL00_1L PIXEL01_C PIXEL02_1M PIXEL12_C } else { PIXEL00_2 PIXEL01_6 PIXEL02_5 PIXEL12_1 } PIXEL10_1 PIXEL11 PIXEL20_2 PIXEL21_1 PIXEL22_1M break; } case 146: case 178: { if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_1M PIXEL12_C PIXEL22_1D } else { PIXEL01_1 PIXEL02_5 PIXEL12_6 PIXEL22_2 } PIXEL00_1M PIXEL10_1 PIXEL11 PIXEL20_2 PIXEL21_1 break; } case 84: case 85: { if (diff(w[6], w[8])) { PIXEL02_1U PIXEL12_C PIXEL21_C PIXEL22_1M } else { PIXEL02_2 PIXEL12_6 PIXEL21_1 PIXEL22_5 } PIXEL00_2 PIXEL01_1 PIXEL10_1 PIXEL11 PIXEL20_1M break; } case 112: case 113: { if (diff(w[6], w[8])) { PIXEL12_C PIXEL20_1L PIXEL21_C PIXEL22_1M } else { PIXEL12_1 PIXEL20_2 PIXEL21_6 PIXEL22_5 } PIXEL00_2 PIXEL01_1 PIXEL02_1M PIXEL10_1 PIXEL11 break; } case 200: case 204: { if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_1M PIXEL21_C PIXEL22_1R } else { PIXEL10_1 PIXEL20_5 PIXEL21_6 PIXEL22_2 } PIXEL00_1M PIXEL01_1 PIXEL02_2 PIXEL11 PIXEL12_1 break; } case 73: case 77: { if (diff(w[8], w[4])) { PIXEL00_1U PIXEL10_C PIXEL20_1M PIXEL21_C } else { PIXEL00_2 PIXEL10_6 PIXEL20_5 PIXEL21_1 } PIXEL01_1 PIXEL02_2 PIXEL11 PIXEL12_1 PIXEL22_1M break; } case 42: case 170: { if (diff(w[4], w[2])) { PIXEL00_1M PIXEL01_C PIXEL10_C PIXEL20_1D } else { PIXEL00_5 PIXEL01_1 PIXEL10_6 PIXEL20_2 } PIXEL02_1M PIXEL11 PIXEL12_1 PIXEL21_1 PIXEL22_2 break; } case 14: case 142: { if (diff(w[4], w[2])) { PIXEL00_1M PIXEL01_C PIXEL02_1R PIXEL10_C } else { PIXEL00_5 PIXEL01_6 PIXEL02_2 PIXEL10_1 } PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_1 PIXEL22_2 break; } case 67: { PIXEL00_1L PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1M break; } case 70: { PIXEL00_1M PIXEL01_C PIXEL02_1R PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1M break; } case 28: { PIXEL00_1M PIXEL01_1 PIXEL02_1U PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_1 PIXEL22_1M break; } case 152: { PIXEL00_1M PIXEL01_1 PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_1 PIXEL22_1D break; } case 194: { PIXEL00_1M PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1R break; } case 98: { PIXEL00_1M PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1L PIXEL21_C PIXEL22_1M break; } case 56: { PIXEL00_1M PIXEL01_1 PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1D PIXEL21_1 PIXEL22_1M break; } case 25: { PIXEL00_1U PIXEL01_1 PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_1 PIXEL22_1M break; } case 26: case 31: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL10_C } else { PIXEL00_4 PIXEL10_3 } PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_C PIXEL12_C } else { PIXEL02_4 PIXEL12_3 } PIXEL11 PIXEL20_1M PIXEL21_1 PIXEL22_1M break; } case 82: case 214: { PIXEL00_1M if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C } else { PIXEL01_3 PIXEL02_4 } PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_1M if (diff(w[6], w[8])) { PIXEL21_C PIXEL22_C } else { PIXEL21_3 PIXEL22_4 } break; } case 88: case 248: { PIXEL00_1M PIXEL01_1 PIXEL02_1M PIXEL11 if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C } else { PIXEL10_3 PIXEL20_4 } PIXEL21_C if (diff(w[6], w[8])) { PIXEL12_C PIXEL22_C } else { PIXEL12_3 PIXEL22_4 } break; } case 74: case 107: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C } else { PIXEL00_4 PIXEL01_3 } PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL20_C PIXEL21_C } else { PIXEL20_4 PIXEL21_3 } PIXEL22_1M break; } case 27: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL10_C } else { PIXEL00_4 PIXEL01_3 PIXEL10_3 } PIXEL02_1M PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_1 PIXEL22_1M break; } case 86: { PIXEL00_1M if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C PIXEL12_C } else { PIXEL01_3 PIXEL02_4 PIXEL12_3 } PIXEL10_1 PIXEL11 PIXEL20_1M PIXEL21_C PIXEL22_1M break; } case 216: { PIXEL00_1M PIXEL01_1 PIXEL02_1M PIXEL10_C PIXEL11 PIXEL20_1M if (diff(w[6], w[8])) { PIXEL12_C PIXEL21_C PIXEL22_C } else { PIXEL12_3 PIXEL21_3 PIXEL22_4 } break; } case 106: { PIXEL00_1M PIXEL01_C PIXEL02_1M PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C PIXEL21_C } else { PIXEL10_3 PIXEL20_4 PIXEL21_3 } PIXEL22_1M break; } case 30: { PIXEL00_1M if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C PIXEL12_C } else { PIXEL01_3 PIXEL02_4 PIXEL12_3 } PIXEL10_C PIXEL11 PIXEL20_1M PIXEL21_1 PIXEL22_1M break; } case 210: { PIXEL00_1M PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL20_1M if (diff(w[6], w[8])) { PIXEL12_C PIXEL21_C PIXEL22_C } else { PIXEL12_3 PIXEL21_3 PIXEL22_4 } break; } case 120: { PIXEL00_1M PIXEL01_1 PIXEL02_1M PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C PIXEL21_C } else { PIXEL10_3 PIXEL20_4 PIXEL21_3 } PIXEL22_1M break; } case 75: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL10_C } else { PIXEL00_4 PIXEL01_3 PIXEL10_3 } PIXEL02_1M PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1M break; } case 29: { PIXEL00_1U PIXEL01_1 PIXEL02_1U PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_1 PIXEL22_1M break; } case 198: { PIXEL00_1M PIXEL01_C PIXEL02_1R PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1R break; } case 184: { PIXEL00_1M PIXEL01_1 PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1D PIXEL21_1 PIXEL22_1D break; } case 99: { PIXEL00_1L PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1L PIXEL21_C PIXEL22_1M break; } case 57: { PIXEL00_1U PIXEL01_1 PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1D PIXEL21_1 PIXEL22_1M break; } case 71: { PIXEL00_1L PIXEL01_C PIXEL02_1R PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1M break; } case 156: { PIXEL00_1M PIXEL01_1 PIXEL02_1U PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_1 PIXEL22_1D break; } case 226: { PIXEL00_1M PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1L PIXEL21_C PIXEL22_1R break; } case 60: { PIXEL00_1M PIXEL01_1 PIXEL02_1U PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1D PIXEL21_1 PIXEL22_1M break; } case 195: { PIXEL00_1L PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1R break; } case 102: { PIXEL00_1M PIXEL01_C PIXEL02_1R PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1L PIXEL21_C PIXEL22_1M break; } case 153: { PIXEL00_1U PIXEL01_1 PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_1 PIXEL22_1D break; } case 58: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1D PIXEL21_1 PIXEL22_1M break; } case 83: { PIXEL00_1L PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 92: { PIXEL00_1M PIXEL01_1 PIXEL02_1U PIXEL10_C PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 202: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } PIXEL01_C PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } PIXEL21_C PIXEL22_1R break; } case 78: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } PIXEL01_C PIXEL02_1R PIXEL10_C PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } PIXEL21_C PIXEL22_1M break; } case 154: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_1 PIXEL22_1D break; } case 114: { PIXEL00_1M PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_1L PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 89: { PIXEL00_1U PIXEL01_1 PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 90: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL10_C PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 55: case 23: { if (diff(w[2], w[6])) { PIXEL00_1L PIXEL01_C PIXEL02_C PIXEL12_C } else { PIXEL00_2 PIXEL01_6 PIXEL02_5 PIXEL12_1 } PIXEL10_1 PIXEL11 PIXEL20_2 PIXEL21_1 PIXEL22_1M break; } case 182: case 150: { if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C PIXEL12_C PIXEL22_1D } else { PIXEL01_1 PIXEL02_5 PIXEL12_6 PIXEL22_2 } PIXEL00_1M PIXEL10_1 PIXEL11 PIXEL20_2 PIXEL21_1 break; } case 213: case 212: { if (diff(w[6], w[8])) { PIXEL02_1U PIXEL12_C PIXEL21_C PIXEL22_C } else { PIXEL02_2 PIXEL12_6 PIXEL21_1 PIXEL22_5 } PIXEL00_2 PIXEL01_1 PIXEL10_1 PIXEL11 PIXEL20_1M break; } case 241: case 240: { if (diff(w[6], w[8])) { PIXEL12_C PIXEL20_1L PIXEL21_C PIXEL22_C } else { PIXEL12_1 PIXEL20_2 PIXEL21_6 PIXEL22_5 } PIXEL00_2 PIXEL01_1 PIXEL02_1M PIXEL10_1 PIXEL11 break; } case 236: case 232: { if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C PIXEL21_C PIXEL22_1R } else { PIXEL10_1 PIXEL20_5 PIXEL21_6 PIXEL22_2 } PIXEL00_1M PIXEL01_1 PIXEL02_2 PIXEL11 PIXEL12_1 break; } case 109: case 105: { if (diff(w[8], w[4])) { PIXEL00_1U PIXEL10_C PIXEL20_C PIXEL21_C } else { PIXEL00_2 PIXEL10_6 PIXEL20_5 PIXEL21_1 } PIXEL01_1 PIXEL02_2 PIXEL11 PIXEL12_1 PIXEL22_1M break; } case 171: case 43: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL10_C PIXEL20_1D } else { PIXEL00_5 PIXEL01_1 PIXEL10_6 PIXEL20_2 } PIXEL02_1M PIXEL11 PIXEL12_1 PIXEL21_1 PIXEL22_2 break; } case 143: case 15: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL02_1R PIXEL10_C } else { PIXEL00_5 PIXEL01_6 PIXEL02_2 PIXEL10_1 } PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_1 PIXEL22_2 break; } case 124: { PIXEL00_1M PIXEL01_1 PIXEL02_1U PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C PIXEL21_C } else { PIXEL10_3 PIXEL20_4 PIXEL21_3 } PIXEL22_1M break; } case 203: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL10_C } else { PIXEL00_4 PIXEL01_3 PIXEL10_3 } PIXEL02_1M PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1R break; } case 62: { PIXEL00_1M if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C PIXEL12_C } else { PIXEL01_3 PIXEL02_4 PIXEL12_3 } PIXEL10_C PIXEL11 PIXEL20_1D PIXEL21_1 PIXEL22_1M break; } case 211: { PIXEL00_1L PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL20_1M if (diff(w[6], w[8])) { PIXEL12_C PIXEL21_C PIXEL22_C } else { PIXEL12_3 PIXEL21_3 PIXEL22_4 } break; } case 118: { PIXEL00_1M if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C PIXEL12_C } else { PIXEL01_3 PIXEL02_4 PIXEL12_3 } PIXEL10_1 PIXEL11 PIXEL20_1L PIXEL21_C PIXEL22_1M break; } case 217: { PIXEL00_1U PIXEL01_1 PIXEL02_1M PIXEL10_C PIXEL11 PIXEL20_1M if (diff(w[6], w[8])) { PIXEL12_C PIXEL21_C PIXEL22_C } else { PIXEL12_3 PIXEL21_3 PIXEL22_4 } break; } case 110: { PIXEL00_1M PIXEL01_C PIXEL02_1R PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C PIXEL21_C } else { PIXEL10_3 PIXEL20_4 PIXEL21_3 } PIXEL22_1M break; } case 155: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL10_C } else { PIXEL00_4 PIXEL01_3 PIXEL10_3 } PIXEL02_1M PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_1 PIXEL22_1D break; } case 188: { PIXEL00_1M PIXEL01_1 PIXEL02_1U PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1D PIXEL21_1 PIXEL22_1D break; } case 185: { PIXEL00_1U PIXEL01_1 PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1D PIXEL21_1 PIXEL22_1D break; } case 61: { PIXEL00_1U PIXEL01_1 PIXEL02_1U PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1D PIXEL21_1 PIXEL22_1M break; } case 157: { PIXEL00_1U PIXEL01_1 PIXEL02_1U PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_1 PIXEL22_1D break; } case 103: { PIXEL00_1L PIXEL01_C PIXEL02_1R PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1L PIXEL21_C PIXEL22_1M break; } case 227: { PIXEL00_1L PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1L PIXEL21_C PIXEL22_1R break; } case 230: { PIXEL00_1M PIXEL01_C PIXEL02_1R PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1L PIXEL21_C PIXEL22_1R break; } case 199: { PIXEL00_1L PIXEL01_C PIXEL02_1R PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1R break; } case 220: { PIXEL00_1M PIXEL01_1 PIXEL02_1U PIXEL10_C PIXEL11 if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } if (diff(w[6], w[8])) { PIXEL12_C PIXEL21_C PIXEL22_C } else { PIXEL12_3 PIXEL21_3 PIXEL22_4 } break; } case 158: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C PIXEL12_C } else { PIXEL01_3 PIXEL02_4 PIXEL12_3 } PIXEL10_C PIXEL11 PIXEL20_1M PIXEL21_1 PIXEL22_1D break; } case 234: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } PIXEL01_C PIXEL02_1M PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C PIXEL21_C } else { PIXEL10_3 PIXEL20_4 PIXEL21_3 } PIXEL22_1R break; } case 242: { PIXEL00_1M PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL10_1 PIXEL11 PIXEL20_1L if (diff(w[6], w[8])) { PIXEL12_C PIXEL21_C PIXEL22_C } else { PIXEL12_3 PIXEL21_3 PIXEL22_4 } break; } case 59: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL10_C } else { PIXEL00_4 PIXEL01_3 PIXEL10_3 } if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL11 PIXEL12_C PIXEL20_1D PIXEL21_1 PIXEL22_1M break; } case 121: { PIXEL00_1U PIXEL01_1 PIXEL02_1M PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C PIXEL21_C } else { PIXEL10_3 PIXEL20_4 PIXEL21_3 } if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 87: { PIXEL00_1L if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C PIXEL12_C } else { PIXEL01_3 PIXEL02_4 PIXEL12_3 } PIXEL10_1 PIXEL11 PIXEL20_1M PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 79: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL10_C } else { PIXEL00_4 PIXEL01_3 PIXEL10_3 } PIXEL02_1R PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } PIXEL21_C PIXEL22_1M break; } case 122: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C PIXEL21_C } else { PIXEL10_3 PIXEL20_4 PIXEL21_3 } if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 94: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C PIXEL12_C } else { PIXEL01_3 PIXEL02_4 PIXEL12_3 } PIXEL10_C PIXEL11 if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 218: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL10_C PIXEL11 if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } if (diff(w[6], w[8])) { PIXEL12_C PIXEL21_C PIXEL22_C } else { PIXEL12_3 PIXEL21_3 PIXEL22_4 } break; } case 91: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL10_C } else { PIXEL00_4 PIXEL01_3 PIXEL10_3 } if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 229: { PIXEL00_2 PIXEL01_1 PIXEL02_2 PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1L PIXEL21_C PIXEL22_1R break; } case 167: { PIXEL00_1L PIXEL01_C PIXEL02_1R PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_2 PIXEL21_1 PIXEL22_2 break; } case 173: { PIXEL00_1U PIXEL01_1 PIXEL02_2 PIXEL10_C PIXEL11 PIXEL12_1 PIXEL20_1D PIXEL21_1 PIXEL22_2 break; } case 181: { PIXEL00_2 PIXEL01_1 PIXEL02_1U PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_2 PIXEL21_1 PIXEL22_1D break; } case 186: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1D PIXEL21_1 PIXEL22_1D break; } case 115: { PIXEL00_1L PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_1L PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 93: { PIXEL00_1U PIXEL01_1 PIXEL02_1U PIXEL10_C PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 206: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } PIXEL01_C PIXEL02_1R PIXEL10_C PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } PIXEL21_C PIXEL22_1R break; } case 205: case 201: { PIXEL00_1U PIXEL01_1 PIXEL02_2 PIXEL10_C PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL20_1M } else { PIXEL20_2 } PIXEL21_C PIXEL22_1R break; } case 174: case 46: { if (diff(w[4], w[2])) { PIXEL00_1M } else { PIXEL00_2 } PIXEL01_C PIXEL02_1R PIXEL10_C PIXEL11 PIXEL12_1 PIXEL20_1D PIXEL21_1 PIXEL22_2 break; } case 179: case 147: { PIXEL00_1L PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_1M } else { PIXEL02_2 } PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_2 PIXEL21_1 PIXEL22_1D break; } case 117: case 116: { PIXEL00_2 PIXEL01_1 PIXEL02_1U PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_1L PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_1M } else { PIXEL22_2 } break; } case 189: { PIXEL00_1U PIXEL01_1 PIXEL02_1U PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1D PIXEL21_1 PIXEL22_1D break; } case 231: { PIXEL00_1L PIXEL01_C PIXEL02_1R PIXEL10_1 PIXEL11 PIXEL12_1 PIXEL20_1L PIXEL21_C PIXEL22_1R break; } case 126: { PIXEL00_1M if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C PIXEL12_C } else { PIXEL01_3 PIXEL02_4 PIXEL12_3 } PIXEL11 if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C PIXEL21_C } else { PIXEL10_3 PIXEL20_4 PIXEL21_3 } PIXEL22_1M break; } case 219: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL10_C } else { PIXEL00_4 PIXEL01_3 PIXEL10_3 } PIXEL02_1M PIXEL11 PIXEL20_1M if (diff(w[6], w[8])) { PIXEL12_C PIXEL21_C PIXEL22_C } else { PIXEL12_3 PIXEL21_3 PIXEL22_4 } break; } case 125: { if (diff(w[8], w[4])) { PIXEL00_1U PIXEL10_C PIXEL20_C PIXEL21_C } else { PIXEL00_2 PIXEL10_6 PIXEL20_5 PIXEL21_1 } PIXEL01_1 PIXEL02_1U PIXEL11 PIXEL12_C PIXEL22_1M break; } case 221: { if (diff(w[6], w[8])) { PIXEL02_1U PIXEL12_C PIXEL21_C PIXEL22_C } else { PIXEL02_2 PIXEL12_6 PIXEL21_1 PIXEL22_5 } PIXEL00_1U PIXEL01_1 PIXEL10_C PIXEL11 PIXEL20_1M break; } case 207: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL02_1R PIXEL10_C } else { PIXEL00_5 PIXEL01_6 PIXEL02_2 PIXEL10_1 } PIXEL11 PIXEL12_1 PIXEL20_1M PIXEL21_C PIXEL22_1R break; } case 238: { if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C PIXEL21_C PIXEL22_1R } else { PIXEL10_1 PIXEL20_5 PIXEL21_6 PIXEL22_2 } PIXEL00_1M PIXEL01_C PIXEL02_1R PIXEL11 PIXEL12_1 break; } case 190: { if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C PIXEL12_C PIXEL22_1D } else { PIXEL01_1 PIXEL02_5 PIXEL12_6 PIXEL22_2 } PIXEL00_1M PIXEL10_C PIXEL11 PIXEL20_1D PIXEL21_1 break; } case 187: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL10_C PIXEL20_1D } else { PIXEL00_5 PIXEL01_1 PIXEL10_6 PIXEL20_2 } PIXEL02_1M PIXEL11 PIXEL12_C PIXEL21_1 PIXEL22_1D break; } case 243: { if (diff(w[6], w[8])) { PIXEL12_C PIXEL20_1L PIXEL21_C PIXEL22_C } else { PIXEL12_1 PIXEL20_2 PIXEL21_6 PIXEL22_5 } PIXEL00_1L PIXEL01_C PIXEL02_1M PIXEL10_1 PIXEL11 break; } case 119: { if (diff(w[2], w[6])) { PIXEL00_1L PIXEL01_C PIXEL02_C PIXEL12_C } else { PIXEL00_2 PIXEL01_6 PIXEL02_5 PIXEL12_1 } PIXEL10_1 PIXEL11 PIXEL20_1L PIXEL21_C PIXEL22_1M break; } case 237: case 233: { PIXEL00_1U PIXEL01_1 PIXEL02_2 PIXEL10_C PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL20_C } else { PIXEL20_2 } PIXEL21_C PIXEL22_1R break; } case 175: case 47: { if (diff(w[4], w[2])) { PIXEL00_C } else { PIXEL00_2 } PIXEL01_C PIXEL02_1R PIXEL10_C PIXEL11 PIXEL12_1 PIXEL20_1D PIXEL21_1 PIXEL22_2 break; } case 183: case 151: { PIXEL00_1L PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_C } else { PIXEL02_2 } PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_2 PIXEL21_1 PIXEL22_1D break; } case 245: case 244: { PIXEL00_2 PIXEL01_1 PIXEL02_1U PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_1L PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_C } else { PIXEL22_2 } break; } case 250: { PIXEL00_1M PIXEL01_C PIXEL02_1M PIXEL11 if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C } else { PIXEL10_3 PIXEL20_4 } PIXEL21_C if (diff(w[6], w[8])) { PIXEL12_C PIXEL22_C } else { PIXEL12_3 PIXEL22_4 } break; } case 123: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C } else { PIXEL00_4 PIXEL01_3 } PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL20_C PIXEL21_C } else { PIXEL20_4 PIXEL21_3 } PIXEL22_1M break; } case 95: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL10_C } else { PIXEL00_4 PIXEL10_3 } PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_C PIXEL12_C } else { PIXEL02_4 PIXEL12_3 } PIXEL11 PIXEL20_1M PIXEL21_C PIXEL22_1M break; } case 222: { PIXEL00_1M if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C } else { PIXEL01_3 PIXEL02_4 } PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1M if (diff(w[6], w[8])) { PIXEL21_C PIXEL22_C } else { PIXEL21_3 PIXEL22_4 } break; } case 252: { PIXEL00_1M PIXEL01_1 PIXEL02_1U PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C } else { PIXEL10_3 PIXEL20_4 } PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_C } else { PIXEL22_2 } break; } case 249: { PIXEL00_1U PIXEL01_1 PIXEL02_1M PIXEL10_C PIXEL11 if (diff(w[8], w[4])) { PIXEL20_C } else { PIXEL20_2 } PIXEL21_C if (diff(w[6], w[8])) { PIXEL12_C PIXEL22_C } else { PIXEL12_3 PIXEL22_4 } break; } case 235: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C } else { PIXEL00_4 PIXEL01_3 } PIXEL02_1M PIXEL10_C PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL20_C } else { PIXEL20_2 } PIXEL21_C PIXEL22_1R break; } case 111: { if (diff(w[4], w[2])) { PIXEL00_C } else { PIXEL00_2 } PIXEL01_C PIXEL02_1R PIXEL10_C PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL20_C PIXEL21_C } else { PIXEL20_4 PIXEL21_3 } PIXEL22_1M break; } case 63: { if (diff(w[4], w[2])) { PIXEL00_C } else { PIXEL00_2 } PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_C PIXEL12_C } else { PIXEL02_4 PIXEL12_3 } PIXEL10_C PIXEL11 PIXEL20_1D PIXEL21_1 PIXEL22_1M break; } case 159: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL10_C } else { PIXEL00_4 PIXEL10_3 } PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_C } else { PIXEL02_2 } PIXEL11 PIXEL12_C PIXEL20_1M PIXEL21_1 PIXEL22_1D break; } case 215: { PIXEL00_1L PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_C } else { PIXEL02_2 } PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_1M if (diff(w[6], w[8])) { PIXEL21_C PIXEL22_C } else { PIXEL21_3 PIXEL22_4 } break; } case 246: { PIXEL00_1M if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C } else { PIXEL01_3 PIXEL02_4 } PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_1L PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_C } else { PIXEL22_2 } break; } case 254: { PIXEL00_1M if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C } else { PIXEL01_3 PIXEL02_4 } PIXEL11 if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C } else { PIXEL10_3 PIXEL20_4 } if (diff(w[6], w[8])) { PIXEL12_C PIXEL21_C PIXEL22_C } else { PIXEL12_3 PIXEL21_3 PIXEL22_2 } break; } case 253: { PIXEL00_1U PIXEL01_1 PIXEL02_1U PIXEL10_C PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL20_C } else { PIXEL20_2 } PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_C } else { PIXEL22_2 } break; } case 251: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C } else { PIXEL00_4 PIXEL01_3 } PIXEL02_1M PIXEL11 if (diff(w[8], w[4])) { PIXEL10_C PIXEL20_C PIXEL21_C } else { PIXEL10_3 PIXEL20_2 PIXEL21_3 } if (diff(w[6], w[8])) { PIXEL12_C PIXEL22_C } else { PIXEL12_3 PIXEL22_4 } break; } case 239: { if (diff(w[4], w[2])) { PIXEL00_C } else { PIXEL00_2 } PIXEL01_C PIXEL02_1R PIXEL10_C PIXEL11 PIXEL12_1 if (diff(w[8], w[4])) { PIXEL20_C } else { PIXEL20_2 } PIXEL21_C PIXEL22_1R break; } case 127: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL01_C PIXEL10_C } else { PIXEL00_2 PIXEL01_3 PIXEL10_3 } if (diff(w[2], w[6])) { PIXEL02_C PIXEL12_C } else { PIXEL02_4 PIXEL12_3 } PIXEL11 if (diff(w[8], w[4])) { PIXEL20_C PIXEL21_C } else { PIXEL20_4 PIXEL21_3 } PIXEL22_1M break; } case 191: { if (diff(w[4], w[2])) { PIXEL00_C } else { PIXEL00_2 } PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_C } else { PIXEL02_2 } PIXEL10_C PIXEL11 PIXEL12_C PIXEL20_1D PIXEL21_1 PIXEL22_1D break; } case 223: { if (diff(w[4], w[2])) { PIXEL00_C PIXEL10_C } else { PIXEL00_4 PIXEL10_3 } if (diff(w[2], w[6])) { PIXEL01_C PIXEL02_C PIXEL12_C } else { PIXEL01_3 PIXEL02_2 PIXEL12_3 } PIXEL11 PIXEL20_1M if (diff(w[6], w[8])) { PIXEL21_C PIXEL22_C } else { PIXEL21_3 PIXEL22_4 } break; } case 247: { PIXEL00_1L PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_C } else { PIXEL02_2 } PIXEL10_1 PIXEL11 PIXEL12_C PIXEL20_1L PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_C } else { PIXEL22_2 } break; } case 255: { if (diff(w[4], w[2])) { PIXEL00_C } else { PIXEL00_2 } PIXEL01_C if (diff(w[2], w[6])) { PIXEL02_C } else { PIXEL02_2 } PIXEL10_C PIXEL11 PIXEL12_C if (diff(w[8], w[4])) { PIXEL20_C } else { PIXEL20_2 } PIXEL21_C if (diff(w[6], w[8])) { PIXEL22_C } else { PIXEL22_2 } break; } } src++; dst += 3 * dst_Bpp; } src = src_temp + src_pitch; dst = dst_temp + 3 * dst_pitch; } SDL_UnlockTexture(dst_texture); } #define PIXEL4_00_0 *(Uint32 *)(dst) = c[5]; #define PIXEL4_00_11 interp1((Uint32 *)(dst), c[5], c[4]); #define PIXEL4_00_12 interp1((Uint32 *)(dst), c[5], c[2]); #define PIXEL4_00_20 interp2((Uint32 *)(dst), c[5], c[2], c[4]); #define PIXEL4_00_50 interp5((Uint32 *)(dst), c[2], c[4]); #define PIXEL4_00_80 interp8((Uint32 *)(dst), c[5], c[1]); #define PIXEL4_00_81 interp8((Uint32 *)(dst), c[5], c[4]); #define PIXEL4_00_82 interp8((Uint32 *)(dst), c[5], c[2]); #define PIXEL4_01_0 *(Uint32 *)(dst + dst_Bpp) = c[5]; #define PIXEL4_01_10 interp1((Uint32 *)(dst + dst_Bpp), c[5], c[1]); #define PIXEL4_01_12 interp1((Uint32 *)(dst + dst_Bpp), c[5], c[2]); #define PIXEL4_01_14 interp1((Uint32 *)(dst + dst_Bpp), c[2], c[5]); #define PIXEL4_01_21 interp2((Uint32 *)(dst + dst_Bpp), c[2], c[5], c[4]); #define PIXEL4_01_31 interp3((Uint32 *)(dst + dst_Bpp), c[5], c[4]); #define PIXEL4_01_50 interp5((Uint32 *)(dst + dst_Bpp), c[2], c[5]); #define PIXEL4_01_60 interp6((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[4]); #define PIXEL4_01_61 interp6((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[1]); #define PIXEL4_01_82 interp8((Uint32 *)(dst + dst_Bpp), c[5], c[2]); #define PIXEL4_01_83 interp8((Uint32 *)(dst + dst_Bpp), c[2], c[4]); #define PIXEL4_02_0 *(Uint32 *)(dst + 2 * dst_Bpp) = c[5]; #define PIXEL4_02_10 interp1((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[3]); #define PIXEL4_02_11 interp1((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2]); #define PIXEL4_02_13 interp1((Uint32 *)(dst + 2 * dst_Bpp), c[2], c[5]); #define PIXEL4_02_21 interp2((Uint32 *)(dst + 2 * dst_Bpp), c[2], c[5], c[6]); #define PIXEL4_02_32 interp3((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[6]); #define PIXEL4_02_50 interp5((Uint32 *)(dst + 2 * dst_Bpp), c[2], c[5]); #define PIXEL4_02_60 interp6((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2], c[6]); #define PIXEL4_02_61 interp6((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2], c[3]); #define PIXEL4_02_81 interp8((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2]); #define PIXEL4_02_83 interp8((Uint32 *)(dst + 2 * dst_Bpp), c[2], c[6]); #define PIXEL4_03_0 *(Uint32 *)(dst + 3 * dst_Bpp) = c[5]; #define PIXEL4_03_11 interp1((Uint32 *)(dst + 3 * dst_Bpp), c[5], c[2]); #define PIXEL4_03_12 interp1((Uint32 *)(dst + 3 * dst_Bpp), c[5], c[6]); #define PIXEL4_03_20 interp2((Uint32 *)(dst + 3 * dst_Bpp), c[5], c[2], c[6]); #define PIXEL4_03_50 interp5((Uint32 *)(dst + 3 * dst_Bpp), c[2], c[6]); #define PIXEL4_03_80 interp8((Uint32 *)(dst + 3 * dst_Bpp), c[5], c[3]); #define PIXEL4_03_81 interp8((Uint32 *)(dst + 3 * dst_Bpp), c[5], c[2]); #define PIXEL4_03_82 interp8((Uint32 *)(dst + 3 * dst_Bpp), c[5], c[6]); #define PIXEL4_10_0 *(Uint32 *)(dst + dst_pitch) = c[5]; #define PIXEL4_10_10 interp1((Uint32 *)(dst + dst_pitch ), c[5], c[1]); #define PIXEL4_10_11 interp1((Uint32 *)(dst + dst_pitch ), c[5], c[4]); #define PIXEL4_10_13 interp1((Uint32 *)(dst + dst_pitch ), c[4], c[5]); #define PIXEL4_10_21 interp2((Uint32 *)(dst + dst_pitch ), c[4], c[5], c[2]); #define PIXEL4_10_32 interp3((Uint32 *)(dst + dst_pitch ), c[5], c[2]); #define PIXEL4_10_50 interp5((Uint32 *)(dst + dst_pitch ), c[4], c[5]); #define PIXEL4_10_60 interp6((Uint32 *)(dst + dst_pitch ), c[5], c[4], c[2]); #define PIXEL4_10_61 interp6((Uint32 *)(dst + dst_pitch ), c[5], c[4], c[1]); #define PIXEL4_10_81 interp8((Uint32 *)(dst + dst_pitch ), c[5], c[4]); #define PIXEL4_10_83 interp8((Uint32 *)(dst + dst_pitch ), c[4], c[2]); #define PIXEL4_11_0 *(Uint32 *)(dst + dst_pitch + dst_Bpp) = c[5]; #define PIXEL4_11_30 interp3((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[1]); #define PIXEL4_11_31 interp3((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[4]); #define PIXEL4_11_32 interp3((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[2]); #define PIXEL4_11_70 interp7((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[4], c[2]); #define PIXEL4_12_0 *(Uint32 *)(dst + dst_pitch + 2 * dst_Bpp) = c[5]; #define PIXEL4_12_30 interp3((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[5], c[3]); #define PIXEL4_12_31 interp3((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[5], c[2]); #define PIXEL4_12_32 interp3((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[5], c[6]); #define PIXEL4_12_70 interp7((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[5], c[6], c[2]); #define PIXEL4_13_0 *(Uint32 *)(dst + dst_pitch + 3 * dst_Bpp) = c[5]; #define PIXEL4_13_10 interp1((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[5], c[3]); #define PIXEL4_13_12 interp1((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[5], c[6]); #define PIXEL4_13_14 interp1((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[6], c[5]); #define PIXEL4_13_21 interp2((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[6], c[5], c[2]); #define PIXEL4_13_31 interp3((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[5], c[2]); #define PIXEL4_13_50 interp5((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[6], c[5]); #define PIXEL4_13_60 interp6((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[5], c[6], c[2]); #define PIXEL4_13_61 interp6((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[5], c[6], c[3]); #define PIXEL4_13_82 interp8((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[5], c[6]); #define PIXEL4_13_83 interp8((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[6], c[2]); #define PIXEL4_20_0 *(Uint32 *)(dst + 2 * dst_pitch) = c[5]; #define PIXEL4_20_10 interp1((Uint32 *)(dst + 2 * dst_pitch ), c[5], c[7]); #define PIXEL4_20_12 interp1((Uint32 *)(dst + 2 * dst_pitch ), c[5], c[4]); #define PIXEL4_20_14 interp1((Uint32 *)(dst + 2 * dst_pitch ), c[4], c[5]); #define PIXEL4_20_21 interp2((Uint32 *)(dst + 2 * dst_pitch ), c[4], c[5], c[8]); #define PIXEL4_20_31 interp3((Uint32 *)(dst + 2 * dst_pitch ), c[5], c[8]); #define PIXEL4_20_50 interp5((Uint32 *)(dst + 2 * dst_pitch ), c[4], c[5]); #define PIXEL4_20_60 interp6((Uint32 *)(dst + 2 * dst_pitch ), c[5], c[4], c[8]); #define PIXEL4_20_61 interp6((Uint32 *)(dst + 2 * dst_pitch ), c[5], c[4], c[7]); #define PIXEL4_20_82 interp8((Uint32 *)(dst + 2 * dst_pitch ), c[5], c[4]); #define PIXEL4_20_83 interp8((Uint32 *)(dst + 2 * dst_pitch ), c[4], c[8]); #define PIXEL4_21_0 *(Uint32 *)(dst + 2 * dst_pitch + dst_Bpp) = c[5]; #define PIXEL4_21_30 interp3((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[5], c[7]); #define PIXEL4_21_31 interp3((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[5], c[8]); #define PIXEL4_21_32 interp3((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[5], c[4]); #define PIXEL4_21_70 interp7((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[5], c[4], c[8]); #define PIXEL4_22_0 *(Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp) = c[5]; #define PIXEL4_22_30 interp3((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[9]); #define PIXEL4_22_31 interp3((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[6]); #define PIXEL4_22_32 interp3((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[8]); #define PIXEL4_22_70 interp7((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[6], c[8]); #define PIXEL4_23_0 *(Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp) = c[5]; #define PIXEL4_23_10 interp1((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[5], c[9]); #define PIXEL4_23_11 interp1((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[5], c[6]); #define PIXEL4_23_13 interp1((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[6], c[5]); #define PIXEL4_23_21 interp2((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[6], c[5], c[8]); #define PIXEL4_23_32 interp3((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[5], c[8]); #define PIXEL4_23_50 interp5((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[6], c[5]); #define PIXEL4_23_60 interp6((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[5], c[6], c[8]); #define PIXEL4_23_61 interp6((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[5], c[6], c[9]); #define PIXEL4_23_81 interp8((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[5], c[6]); #define PIXEL4_23_83 interp8((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[6], c[8]); #define PIXEL4_30_0 *(Uint32 *)(dst + 3 * dst_pitch) = c[5]; #define PIXEL4_30_11 interp1((Uint32 *)(dst + 3 * dst_pitch ), c[5], c[8]); #define PIXEL4_30_12 interp1((Uint32 *)(dst + 3 * dst_pitch ), c[5], c[4]); #define PIXEL4_30_20 interp2((Uint32 *)(dst + 3 * dst_pitch ), c[5], c[8], c[4]); #define PIXEL4_30_50 interp5((Uint32 *)(dst + 3 * dst_pitch ), c[8], c[4]); #define PIXEL4_30_80 interp8((Uint32 *)(dst + 3 * dst_pitch ), c[5], c[7]); #define PIXEL4_30_81 interp8((Uint32 *)(dst + 3 * dst_pitch ), c[5], c[8]); #define PIXEL4_30_82 interp8((Uint32 *)(dst + 3 * dst_pitch ), c[5], c[4]); #define PIXEL4_31_0 *(Uint32 *)(dst + 3 * dst_pitch + dst_Bpp) = c[5]; #define PIXEL4_31_10 interp1((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[5], c[7]); #define PIXEL4_31_11 interp1((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[5], c[8]); #define PIXEL4_31_13 interp1((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[8], c[5]); #define PIXEL4_31_21 interp2((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[8], c[5], c[4]); #define PIXEL4_31_32 interp3((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[5], c[4]); #define PIXEL4_31_50 interp5((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[8], c[5]); #define PIXEL4_31_60 interp6((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[5], c[8], c[4]); #define PIXEL4_31_61 interp6((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[5], c[8], c[7]); #define PIXEL4_31_81 interp8((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[5], c[8]); #define PIXEL4_31_83 interp8((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[8], c[4]); #define PIXEL4_32_0 *(Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp) = c[5]; #define PIXEL4_32_10 interp1((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[5], c[9]); #define PIXEL4_32_12 interp1((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[5], c[8]); #define PIXEL4_32_14 interp1((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[8], c[5]); #define PIXEL4_32_21 interp2((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[8], c[5], c[6]); #define PIXEL4_32_31 interp3((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[5], c[6]); #define PIXEL4_32_50 interp5((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[8], c[5]); #define PIXEL4_32_60 interp6((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[5], c[8], c[6]); #define PIXEL4_32_61 interp6((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[5], c[8], c[9]); #define PIXEL4_32_82 interp8((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[5], c[8]); #define PIXEL4_32_83 interp8((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[8], c[6]); #define PIXEL4_33_0 *(Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp) = c[5]; #define PIXEL4_33_11 interp1((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[5], c[6]); #define PIXEL4_33_12 interp1((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[5], c[8]); #define PIXEL4_33_20 interp2((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[5], c[8], c[6]); #define PIXEL4_33_50 interp5((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[8], c[6]); #define PIXEL4_33_80 interp8((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[5], c[9]); #define PIXEL4_33_81 interp8((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[5], c[6]); #define PIXEL4_33_82 interp8((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[5], c[8]); void hq4x_32(SDL_Surface *src_surface, SDL_Texture *dst_texture) { Uint8 *src = src_surface->pixels, *src_temp; Uint8 *dst, *dst_temp; int src_pitch = src_surface->pitch; int dst_pitch; const int dst_Bpp = 4, // dst_surface->format->BytesPerPixel height = vga_height, // src_surface->h width = vga_width; // src_surface->w void* tmp_ptr; SDL_LockTexture(dst_texture, NULL, &tmp_ptr, &dst_pitch); dst = tmp_ptr; int prevline, nextline; Uint32 w[10]; Uint32 c[10]; // +----+----+----+ // | | | | // | w1 | w2 | w3 | // +----+----+----+ // | | | | // | w4 | w5 | w6 | // +----+----+----+ // | | | | // | w7 | w8 | w9 | // +----+----+----+ for (int j = 0; j < height; j++) { src_temp = src; dst_temp = dst; prevline = (j > 0) ? -width : 0; nextline = (j < height - 1) ? width : 0; for (int i = 0; i < width; i++) { w[2] = *(src + prevline); w[5] = *src; w[8] = *(src + nextline); if (i>0) { w[1] = *(src + prevline - 1); w[4] = *(src - 1); w[7] = *(src + nextline - 1); } else { w[1] = w[2]; w[4] = w[5]; w[7] = w[8]; } if (i < width - 1) { w[3] = *(src + prevline + 1); w[6] = *(src + 1); w[9] = *(src + nextline + 1); } else { w[3] = w[2]; w[6] = w[5]; w[9] = w[8]; } int pattern = 0; int flag = 1; YUV1 = yuv_palette[w[5]]; for (int k=1; k<=9; k++) { if (k==5) continue; if (w[k] != w[5]) { YUV2 = yuv_palette[w[k]]; if ( ( abs((YUV1 & Ymask) - (YUV2 & Ymask)) > trY ) || ( abs((YUV1 & Umask) - (YUV2 & Umask)) > trU ) || ( abs((YUV1 & Vmask) - (YUV2 & Vmask)) > trV ) ) pattern |= flag; } flag <<= 1; } for (int k=1; k<=9; k++) c[k] = rgb_palette[w[k]] & 0xfcfcfcfc; // hq4x has a nasty inability to accept more than 6 bits for each component switch (pattern) { case 0: case 1: case 4: case 32: case 128: case 5: case 132: case 160: case 33: case 129: case 36: case 133: case 164: case 161: case 37: case 165: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_60 PIXEL4_33_20 break; } case 2: case 34: case 130: case 162: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_60 PIXEL4_33_20 break; } case 16: case 17: case 48: case 49: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_61 PIXEL4_33_80 break; } case 64: case 65: case 68: case 69: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 8: case 12: case 136: case 140: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_60 PIXEL4_33_20 break; } case 3: case 35: case 131: case 163: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_60 PIXEL4_33_20 break; } case 6: case 38: case 134: case 166: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_60 PIXEL4_33_20 break; } case 20: case 21: case 52: case 53: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_61 PIXEL4_33_80 break; } case 144: case 145: case 176: case 177: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_82 PIXEL4_33_82 break; } case 192: case 193: case 196: case 197: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_31 PIXEL4_33_81 break; } case 96: case 97: case 100: case 101: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_10 PIXEL4_33_80 break; } case 40: case 44: case 168: case 172: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_60 PIXEL4_33_20 break; } case 9: case 13: case 137: case 141: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_60 PIXEL4_33_20 break; } case 18: case 50: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_12_0 PIXEL4_13_50 } PIXEL4_10_61 PIXEL4_11_30 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_61 PIXEL4_33_80 break; } case 80: case 81: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_61 PIXEL4_21_30 if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } PIXEL4_30_80 PIXEL4_31_10 break; } case 72: case 76: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_70 PIXEL4_13_60 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_50 PIXEL4_21_0 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_22_30 PIXEL4_23_61 PIXEL4_32_10 PIXEL4_33_80 break; } case 10: case 138: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 PIXEL4_11_0 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_60 PIXEL4_33_20 break; } case 66: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 24: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_61 PIXEL4_33_80 break; } case 7: case 39: case 135: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_60 PIXEL4_33_20 break; } case 148: case 149: case 180: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_82 PIXEL4_33_82 break; } case 224: case 228: case 225: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_31 PIXEL4_33_81 break; } case 41: case 169: case 45: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_60 PIXEL4_33_20 break; } case 22: case 54: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_0 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_61 PIXEL4_33_80 break; } case 208: case 209: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } PIXEL4_30_80 PIXEL4_31_10 break; } case 104: case 108: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_70 PIXEL4_13_60 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_32_10 PIXEL4_33_80 break; } case 11: case 139: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_11_0 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_60 PIXEL4_33_20 break; } case 19: case 51: { if (diff(w[2], w[6])) { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_00_12 PIXEL4_01_14 PIXEL4_02_83 PIXEL4_03_50 PIXEL4_12_70 PIXEL4_13_21 } PIXEL4_10_81 PIXEL4_11_31 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_61 PIXEL4_33_80 break; } case 146: case 178: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_23_32 PIXEL4_33_82 } else { PIXEL4_02_21 PIXEL4_03_50 PIXEL4_12_70 PIXEL4_13_83 PIXEL4_23_13 PIXEL4_33_11 } PIXEL4_10_61 PIXEL4_11_30 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_32 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_82 break; } case 84: case 85: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_81 if (diff(w[6], w[8])) { PIXEL4_03_81 PIXEL4_13_31 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_03_12 PIXEL4_13_14 PIXEL4_22_70 PIXEL4_23_83 PIXEL4_32_21 PIXEL4_33_50 } PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_31 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 break; } case 112: case 113: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_82 PIXEL4_21_32 if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_70 PIXEL4_23_21 PIXEL4_30_11 PIXEL4_31_13 PIXEL4_32_83 PIXEL4_33_50 } break; } case 200: case 204: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_70 PIXEL4_13_60 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_31 PIXEL4_33_81 } else { PIXEL4_20_21 PIXEL4_21_70 PIXEL4_30_50 PIXEL4_31_83 PIXEL4_32_14 PIXEL4_33_12 } PIXEL4_22_31 PIXEL4_23_81 break; } case 73: case 77: { if (diff(w[8], w[4])) { PIXEL4_00_82 PIXEL4_10_32 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_00_11 PIXEL4_10_13 PIXEL4_20_83 PIXEL4_21_70 PIXEL4_30_50 PIXEL4_31_21 } PIXEL4_01_82 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_11_32 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_32_10 PIXEL4_33_80 break; } case 42: case 170: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_20_31 PIXEL4_30_81 } else { PIXEL4_00_50 PIXEL4_01_21 PIXEL4_10_83 PIXEL4_11_70 PIXEL4_20_14 PIXEL4_30_12 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_21_31 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_31_81 PIXEL4_32_60 PIXEL4_33_20 break; } case 14: case 142: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_50 PIXEL4_01_83 PIXEL4_02_13 PIXEL4_03_11 PIXEL4_10_21 PIXEL4_11_70 } PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_60 PIXEL4_33_20 break; } case 67: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 70: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 28: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_61 PIXEL4_33_80 break; } case 152: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_82 PIXEL4_33_82 break; } case 194: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_31 PIXEL4_33_81 break; } case 98: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_10 PIXEL4_33_80 break; } case 56: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_61 PIXEL4_33_80 break; } case 25: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_61 PIXEL4_33_80 break; } case 26: case 31: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_11_0 PIXEL4_12_0 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_61 PIXEL4_33_80 break; } case 82: case 214: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_0 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } PIXEL4_30_80 PIXEL4_31_10 break; } case 88: case 248: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_10 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } break; } case 74: case 107: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_11_0 PIXEL4_12_30 PIXEL4_13_61 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_32_10 PIXEL4_33_80 break; } case 27: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_11_0 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_61 PIXEL4_33_80 break; } case 86: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_0 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 216: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } PIXEL4_30_80 PIXEL4_31_10 break; } case 106: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_61 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_32_10 PIXEL4_33_80 break; } case 30: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_0 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_61 PIXEL4_33_80 break; } case 210: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } PIXEL4_30_80 PIXEL4_31_10 break; } case 120: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_10 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 75: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_11_0 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 29: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_61 PIXEL4_33_80 break; } case 198: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_31 PIXEL4_33_81 break; } case 184: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_82 PIXEL4_33_82 break; } case 99: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_10 PIXEL4_33_80 break; } case 57: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_61 PIXEL4_33_80 break; } case 71: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 156: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_82 PIXEL4_33_82 break; } case 226: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_31 PIXEL4_33_81 break; } case 60: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_61 PIXEL4_33_80 break; } case 195: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_31 PIXEL4_33_81 break; } case 102: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_10 PIXEL4_33_80 break; } case 153: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_82 PIXEL4_33_82 break; } case 58: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_61 PIXEL4_33_80 break; } case 83: { PIXEL4_00_81 PIXEL4_01_31 if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } PIXEL4_10_81 PIXEL4_11_31 PIXEL4_20_61 PIXEL4_21_30 if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } PIXEL4_30_80 PIXEL4_31_10 break; } case 92: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_31 PIXEL4_13_31 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } break; } case 202: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_61 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } PIXEL4_22_31 PIXEL4_23_81 PIXEL4_32_31 PIXEL4_33_81 break; } case 78: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } PIXEL4_02_32 PIXEL4_03_82 PIXEL4_12_32 PIXEL4_13_82 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } PIXEL4_22_30 PIXEL4_23_61 PIXEL4_32_10 PIXEL4_33_80 break; } case 154: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_82 PIXEL4_33_82 break; } case 114: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } PIXEL4_10_61 PIXEL4_11_30 PIXEL4_20_82 PIXEL4_21_32 if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } PIXEL4_30_82 PIXEL4_31_32 break; } case 89: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_30 PIXEL4_13_10 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } break; } case 90: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } break; } case 55: case 23: { if (diff(w[2], w[6])) { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_0 PIXEL4_03_0 PIXEL4_12_0 PIXEL4_13_0 } else { PIXEL4_00_12 PIXEL4_01_14 PIXEL4_02_83 PIXEL4_03_50 PIXEL4_12_70 PIXEL4_13_21 } PIXEL4_10_81 PIXEL4_11_31 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_61 PIXEL4_33_80 break; } case 182: case 150: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_12_0 PIXEL4_13_0 PIXEL4_23_32 PIXEL4_33_82 } else { PIXEL4_02_21 PIXEL4_03_50 PIXEL4_12_70 PIXEL4_13_83 PIXEL4_23_13 PIXEL4_33_11 } PIXEL4_10_61 PIXEL4_11_30 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_32 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_82 break; } case 213: case 212: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_81 if (diff(w[6], w[8])) { PIXEL4_03_81 PIXEL4_13_31 PIXEL4_22_0 PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_03_12 PIXEL4_13_14 PIXEL4_22_70 PIXEL4_23_83 PIXEL4_32_21 PIXEL4_33_50 } PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_31 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 break; } case 241: case 240: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_82 PIXEL4_21_32 if (diff(w[6], w[8])) { PIXEL4_22_0 PIXEL4_23_0 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_22_70 PIXEL4_23_21 PIXEL4_30_11 PIXEL4_31_13 PIXEL4_32_83 PIXEL4_33_50 } break; } case 236: case 232: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_70 PIXEL4_13_60 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_21_0 PIXEL4_30_0 PIXEL4_31_0 PIXEL4_32_31 PIXEL4_33_81 } else { PIXEL4_20_21 PIXEL4_21_70 PIXEL4_30_50 PIXEL4_31_83 PIXEL4_32_14 PIXEL4_33_12 } PIXEL4_22_31 PIXEL4_23_81 break; } case 109: case 105: { if (diff(w[8], w[4])) { PIXEL4_00_82 PIXEL4_10_32 PIXEL4_20_0 PIXEL4_21_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_00_11 PIXEL4_10_13 PIXEL4_20_83 PIXEL4_21_70 PIXEL4_30_50 PIXEL4_31_21 } PIXEL4_01_82 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_11_32 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_32_10 PIXEL4_33_80 break; } case 171: case 43: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 PIXEL4_11_0 PIXEL4_20_31 PIXEL4_30_81 } else { PIXEL4_00_50 PIXEL4_01_21 PIXEL4_10_83 PIXEL4_11_70 PIXEL4_20_14 PIXEL4_30_12 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_21_31 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_31_81 PIXEL4_32_60 PIXEL4_33_20 break; } case 143: case 15: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_0 PIXEL4_11_0 } else { PIXEL4_00_50 PIXEL4_01_83 PIXEL4_02_13 PIXEL4_03_11 PIXEL4_10_21 PIXEL4_11_70 } PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_60 PIXEL4_33_20 break; } case 124: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_31 PIXEL4_13_31 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 203: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_11_0 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_31 PIXEL4_33_81 break; } case 62: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_0 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_61 PIXEL4_33_80 break; } case 211: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } PIXEL4_30_80 PIXEL4_31_10 break; } case 118: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_0 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_10 PIXEL4_33_80 break; } case 217: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } PIXEL4_30_80 PIXEL4_31_10 break; } case 110: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_32 PIXEL4_13_82 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_32_10 PIXEL4_33_80 break; } case 155: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_11_0 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_82 PIXEL4_33_82 break; } case 188: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_82 PIXEL4_33_82 break; } case 185: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_82 PIXEL4_33_82 break; } case 61: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_61 PIXEL4_33_80 break; } case 157: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_82 PIXEL4_33_82 break; } case 103: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_10 PIXEL4_33_80 break; } case 227: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_31 PIXEL4_33_81 break; } case 230: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_31 PIXEL4_33_81 break; } case 199: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_31 PIXEL4_33_81 break; } case 220: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_31 PIXEL4_13_31 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } break; } case 158: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_12_0 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_82 PIXEL4_33_82 break; } case 234: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_61 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_32_31 PIXEL4_33_81 break; } case 242: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } PIXEL4_10_61 PIXEL4_11_30 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } PIXEL4_30_82 PIXEL4_31_32 break; } case 59: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } PIXEL4_11_0 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_61 PIXEL4_33_80 break; } case 121: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_30 PIXEL4_13_10 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } break; } case 87: { PIXEL4_00_81 PIXEL4_01_31 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_0 PIXEL4_20_61 PIXEL4_21_30 if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } PIXEL4_30_80 PIXEL4_31_10 break; } case 79: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_32 PIXEL4_03_82 PIXEL4_11_0 PIXEL4_12_32 PIXEL4_13_82 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } PIXEL4_22_30 PIXEL4_23_61 PIXEL4_32_10 PIXEL4_33_80 break; } case 122: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } break; } case 94: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_12_0 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } break; } case 218: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } break; } case 91: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } PIXEL4_11_0 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } break; } case 229: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_31 PIXEL4_33_81 break; } case 167: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_60 PIXEL4_33_20 break; } case 173: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_60 PIXEL4_33_20 break; } case 181: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_82 PIXEL4_33_82 break; } case 186: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_82 PIXEL4_33_82 break; } case 115: { PIXEL4_00_81 PIXEL4_01_31 if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } PIXEL4_10_81 PIXEL4_11_31 PIXEL4_20_82 PIXEL4_21_32 if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } PIXEL4_30_82 PIXEL4_31_32 break; } case 93: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_31 PIXEL4_13_31 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } break; } case 206: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } PIXEL4_02_32 PIXEL4_03_82 PIXEL4_12_32 PIXEL4_13_82 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } PIXEL4_22_31 PIXEL4_23_81 PIXEL4_32_31 PIXEL4_33_81 break; } case 205: case 201: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_70 PIXEL4_13_60 if (diff(w[8], w[4])) { PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 } else { PIXEL4_20_12 PIXEL4_21_0 PIXEL4_30_20 PIXEL4_31_11 } PIXEL4_22_31 PIXEL4_23_81 PIXEL4_32_31 PIXEL4_33_81 break; } case 174: case 46: { if (diff(w[4], w[2])) { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_10_10 PIXEL4_11_30 } else { PIXEL4_00_20 PIXEL4_01_12 PIXEL4_10_11 PIXEL4_11_0 } PIXEL4_02_32 PIXEL4_03_82 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_60 PIXEL4_33_20 break; } case 179: case 147: { PIXEL4_00_81 PIXEL4_01_31 if (diff(w[2], w[6])) { PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 } else { PIXEL4_02_11 PIXEL4_03_20 PIXEL4_12_0 PIXEL4_13_12 } PIXEL4_10_81 PIXEL4_11_31 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_82 PIXEL4_33_82 break; } case 117: case 116: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_82 PIXEL4_21_32 if (diff(w[6], w[8])) { PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 } else { PIXEL4_22_0 PIXEL4_23_11 PIXEL4_32_12 PIXEL4_33_20 } PIXEL4_30_82 PIXEL4_31_32 break; } case 189: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_82 PIXEL4_33_82 break; } case 231: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_31 PIXEL4_33_81 break; } case 126: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_0 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 219: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_11_0 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } PIXEL4_30_80 PIXEL4_31_10 break; } case 125: { if (diff(w[8], w[4])) { PIXEL4_00_82 PIXEL4_10_32 PIXEL4_20_0 PIXEL4_21_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_00_11 PIXEL4_10_13 PIXEL4_20_83 PIXEL4_21_70 PIXEL4_30_50 PIXEL4_31_21 } PIXEL4_01_82 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_11_32 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 221: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_81 if (diff(w[6], w[8])) { PIXEL4_03_81 PIXEL4_13_31 PIXEL4_22_0 PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_03_12 PIXEL4_13_14 PIXEL4_22_70 PIXEL4_23_83 PIXEL4_32_21 PIXEL4_33_50 } PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_31 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_30_80 PIXEL4_31_10 break; } case 207: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_0 PIXEL4_11_0 } else { PIXEL4_00_50 PIXEL4_01_83 PIXEL4_02_13 PIXEL4_03_11 PIXEL4_10_21 PIXEL4_11_70 } PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_31 PIXEL4_23_81 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_31 PIXEL4_33_81 break; } case 238: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_32 PIXEL4_13_82 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_21_0 PIXEL4_30_0 PIXEL4_31_0 PIXEL4_32_31 PIXEL4_33_81 } else { PIXEL4_20_21 PIXEL4_21_70 PIXEL4_30_50 PIXEL4_31_83 PIXEL4_32_14 PIXEL4_33_12 } PIXEL4_22_31 PIXEL4_23_81 break; } case 190: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_12_0 PIXEL4_13_0 PIXEL4_23_32 PIXEL4_33_82 } else { PIXEL4_02_21 PIXEL4_03_50 PIXEL4_12_70 PIXEL4_13_83 PIXEL4_23_13 PIXEL4_33_11 } PIXEL4_10_10 PIXEL4_11_30 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_32 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_82 break; } case 187: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 PIXEL4_11_0 PIXEL4_20_31 PIXEL4_30_81 } else { PIXEL4_00_50 PIXEL4_01_21 PIXEL4_10_83 PIXEL4_11_70 PIXEL4_20_14 PIXEL4_30_12 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_21_31 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_31_81 PIXEL4_32_82 PIXEL4_33_82 break; } case 243: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_82 PIXEL4_21_32 if (diff(w[6], w[8])) { PIXEL4_22_0 PIXEL4_23_0 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_22_70 PIXEL4_23_21 PIXEL4_30_11 PIXEL4_31_13 PIXEL4_32_83 PIXEL4_33_50 } break; } case 119: { if (diff(w[2], w[6])) { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_0 PIXEL4_03_0 PIXEL4_12_0 PIXEL4_13_0 } else { PIXEL4_00_12 PIXEL4_01_14 PIXEL4_02_83 PIXEL4_03_50 PIXEL4_12_70 PIXEL4_13_21 } PIXEL4_10_81 PIXEL4_11_31 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_10 PIXEL4_33_80 break; } case 237: case 233: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_60 PIXEL4_03_20 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_70 PIXEL4_13_60 PIXEL4_20_0 PIXEL4_21_0 PIXEL4_22_31 PIXEL4_23_81 if (diff(w[8], w[4])) { PIXEL4_30_0 } else { PIXEL4_30_20 } PIXEL4_31_0 PIXEL4_32_31 PIXEL4_33_81 break; } case 175: case 47: { if (diff(w[4], w[2])) { PIXEL4_00_0 } else { PIXEL4_00_20 } PIXEL4_01_0 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_0 PIXEL4_11_0 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_70 PIXEL4_23_60 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_60 PIXEL4_33_20 break; } case 183: case 151: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_0 if (diff(w[2], w[6])) { PIXEL4_03_0 } else { PIXEL4_03_20 } PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_0 PIXEL4_13_0 PIXEL4_20_60 PIXEL4_21_70 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_20 PIXEL4_31_60 PIXEL4_32_82 PIXEL4_33_82 break; } case 245: case 244: { PIXEL4_00_20 PIXEL4_01_60 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_60 PIXEL4_11_70 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_0 PIXEL4_23_0 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_0 if (diff(w[6], w[8])) { PIXEL4_33_0 } else { PIXEL4_33_20 } break; } case 250: { PIXEL4_00_80 PIXEL4_01_10 PIXEL4_02_10 PIXEL4_03_80 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_30 PIXEL4_13_10 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } break; } case 123: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_11_0 PIXEL4_12_30 PIXEL4_13_10 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 95: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_11_0 PIXEL4_12_0 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_80 PIXEL4_31_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 222: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_0 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } PIXEL4_30_80 PIXEL4_31_10 break; } case 252: { PIXEL4_00_80 PIXEL4_01_61 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_31 PIXEL4_13_31 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_0 PIXEL4_23_0 PIXEL4_32_0 if (diff(w[6], w[8])) { PIXEL4_33_0 } else { PIXEL4_33_20 } break; } case 249: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_61 PIXEL4_03_80 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_0 PIXEL4_21_0 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } if (diff(w[8], w[4])) { PIXEL4_30_0 } else { PIXEL4_30_20 } PIXEL4_31_0 break; } case 235: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_11_0 PIXEL4_12_30 PIXEL4_13_61 PIXEL4_20_0 PIXEL4_21_0 PIXEL4_22_31 PIXEL4_23_81 if (diff(w[8], w[4])) { PIXEL4_30_0 } else { PIXEL4_30_20 } PIXEL4_31_0 PIXEL4_32_31 PIXEL4_33_81 break; } case 111: { if (diff(w[4], w[2])) { PIXEL4_00_0 } else { PIXEL4_00_20 } PIXEL4_01_0 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_0 PIXEL4_11_0 PIXEL4_12_32 PIXEL4_13_82 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_30 PIXEL4_23_61 PIXEL4_32_10 PIXEL4_33_80 break; } case 63: { if (diff(w[4], w[2])) { PIXEL4_00_0 } else { PIXEL4_00_20 } PIXEL4_01_0 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_0 PIXEL4_11_0 PIXEL4_12_0 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_61 PIXEL4_33_80 break; } case 159: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_0 if (diff(w[2], w[6])) { PIXEL4_03_0 } else { PIXEL4_03_20 } PIXEL4_11_0 PIXEL4_12_0 PIXEL4_13_0 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_80 PIXEL4_31_61 PIXEL4_32_82 PIXEL4_33_82 break; } case 215: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_0 if (diff(w[2], w[6])) { PIXEL4_03_0 } else { PIXEL4_03_20 } PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_0 PIXEL4_13_0 PIXEL4_20_61 PIXEL4_21_30 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } PIXEL4_30_80 PIXEL4_31_10 break; } case 246: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_61 PIXEL4_11_30 PIXEL4_12_0 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_0 PIXEL4_23_0 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_0 if (diff(w[6], w[8])) { PIXEL4_33_0 } else { PIXEL4_33_20 } break; } case 254: { PIXEL4_00_80 PIXEL4_01_10 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_10 PIXEL4_11_30 PIXEL4_12_0 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_0 PIXEL4_23_0 PIXEL4_32_0 if (diff(w[6], w[8])) { PIXEL4_33_0 } else { PIXEL4_33_20 } break; } case 253: { PIXEL4_00_82 PIXEL4_01_82 PIXEL4_02_81 PIXEL4_03_81 PIXEL4_10_32 PIXEL4_11_32 PIXEL4_12_31 PIXEL4_13_31 PIXEL4_20_0 PIXEL4_21_0 PIXEL4_22_0 PIXEL4_23_0 if (diff(w[8], w[4])) { PIXEL4_30_0 } else { PIXEL4_30_20 } PIXEL4_31_0 PIXEL4_32_0 if (diff(w[6], w[8])) { PIXEL4_33_0 } else { PIXEL4_33_20 } break; } case 251: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_10 PIXEL4_03_80 PIXEL4_11_0 PIXEL4_12_30 PIXEL4_13_10 PIXEL4_20_0 PIXEL4_21_0 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } if (diff(w[8], w[4])) { PIXEL4_30_0 } else { PIXEL4_30_20 } PIXEL4_31_0 break; } case 239: { if (diff(w[4], w[2])) { PIXEL4_00_0 } else { PIXEL4_00_20 } PIXEL4_01_0 PIXEL4_02_32 PIXEL4_03_82 PIXEL4_10_0 PIXEL4_11_0 PIXEL4_12_32 PIXEL4_13_82 PIXEL4_20_0 PIXEL4_21_0 PIXEL4_22_31 PIXEL4_23_81 if (diff(w[8], w[4])) { PIXEL4_30_0 } else { PIXEL4_30_20 } PIXEL4_31_0 PIXEL4_32_31 PIXEL4_33_81 break; } case 127: { if (diff(w[4], w[2])) { PIXEL4_00_0 } else { PIXEL4_00_20 } PIXEL4_01_0 if (diff(w[2], w[6])) { PIXEL4_02_0 PIXEL4_03_0 PIXEL4_13_0 } else { PIXEL4_02_50 PIXEL4_03_50 PIXEL4_13_50 } PIXEL4_10_0 PIXEL4_11_0 PIXEL4_12_0 if (diff(w[8], w[4])) { PIXEL4_20_0 PIXEL4_30_0 PIXEL4_31_0 } else { PIXEL4_20_50 PIXEL4_30_50 PIXEL4_31_50 } PIXEL4_21_0 PIXEL4_22_30 PIXEL4_23_10 PIXEL4_32_10 PIXEL4_33_80 break; } case 191: { if (diff(w[4], w[2])) { PIXEL4_00_0 } else { PIXEL4_00_20 } PIXEL4_01_0 PIXEL4_02_0 if (diff(w[2], w[6])) { PIXEL4_03_0 } else { PIXEL4_03_20 } PIXEL4_10_0 PIXEL4_11_0 PIXEL4_12_0 PIXEL4_13_0 PIXEL4_20_31 PIXEL4_21_31 PIXEL4_22_32 PIXEL4_23_32 PIXEL4_30_81 PIXEL4_31_81 PIXEL4_32_82 PIXEL4_33_82 break; } case 223: { if (diff(w[4], w[2])) { PIXEL4_00_0 PIXEL4_01_0 PIXEL4_10_0 } else { PIXEL4_00_50 PIXEL4_01_50 PIXEL4_10_50 } PIXEL4_02_0 if (diff(w[2], w[6])) { PIXEL4_03_0 } else { PIXEL4_03_20 } PIXEL4_11_0 PIXEL4_12_0 PIXEL4_13_0 PIXEL4_20_10 PIXEL4_21_30 PIXEL4_22_0 if (diff(w[6], w[8])) { PIXEL4_23_0 PIXEL4_32_0 PIXEL4_33_0 } else { PIXEL4_23_50 PIXEL4_32_50 PIXEL4_33_50 } PIXEL4_30_80 PIXEL4_31_10 break; } case 247: { PIXEL4_00_81 PIXEL4_01_31 PIXEL4_02_0 if (diff(w[2], w[6])) { PIXEL4_03_0 } else { PIXEL4_03_20 } PIXEL4_10_81 PIXEL4_11_31 PIXEL4_12_0 PIXEL4_13_0 PIXEL4_20_82 PIXEL4_21_32 PIXEL4_22_0 PIXEL4_23_0 PIXEL4_30_82 PIXEL4_31_32 PIXEL4_32_0 if (diff(w[6], w[8])) { PIXEL4_33_0 } else { PIXEL4_33_20 } break; } case 255: { if (diff(w[4], w[2])) { PIXEL4_00_0 } else { PIXEL4_00_20 } PIXEL4_01_0 PIXEL4_02_0 if (diff(w[2], w[6])) { PIXEL4_03_0 } else { PIXEL4_03_20 } PIXEL4_10_0 PIXEL4_11_0 PIXEL4_12_0 PIXEL4_13_0 PIXEL4_20_0 PIXEL4_21_0 PIXEL4_22_0 PIXEL4_23_0 if (diff(w[8], w[4])) { PIXEL4_30_0 } else { PIXEL4_30_20 } PIXEL4_31_0 PIXEL4_32_0 if (diff(w[6], w[8])) { PIXEL4_33_0 } else { PIXEL4_33_20 } break; } } src++; dst += 4 * dst_Bpp; } src = src_temp + src_pitch; dst = dst_temp + 4 * dst_pitch; } SDL_UnlockTexture(dst_texture); } opentyrian-2.1.20221123/src/xmas.c000066400000000000000000000123351432005211200163340ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "xmas.h" #include "font.h" #include "joystick.h" #include "keyboard.h" #include "mouse.h" #include "palette.h" #include "sprite.h" #include "vga256d.h" #include "video.h" #include #include bool xmas = false; bool xmas_time(void) { time_t now = time(NULL); return localtime(&now)->tm_mon == 11; } bool xmas_prompt(void) { static const char *const prompt[] = { "Christmas has been detected.", "Activate Christmas?", }; static const char *const choices[] = { "Yes", "No", }; if (shopSpriteSheet.data == NULL) JE_loadCompShapes(&shopSpriteSheet, '1'); // need mouse pointer sprites bool restart = true; struct { Sint16 x; Sint16 y; Uint8 dyAcc; Uint8 dyAdd; } flakes[80]; static const Uint8 dyDen = 128; const int xCenter = 320 / 2; const int yPrompt = 85; const int dyPrompt = 15; const int wChoice = 40; const int yChoice = 120; const int hChoice = 13; size_t selectedIndex = 0; for (; ; ) { bool mouseMoved = false; do { if (restart) { for (size_t i = 0; i < COUNTOF(flakes); ++i) { flakes[i].y = 200 + rand() % 200; flakes[i].dyAdd = dyDen / 2 + i * dyDen / COUNTOF(flakes); } } for (size_t i = 0; i < COUNTOF(flakes); ++i) { if (flakes[i].y >= 200) { flakes[i].x = rand() % 320; flakes[i].y = 200 - 14 - flakes[i].y; flakes[i].dyAcc = flakes[i].dyAdd; } int temp = rand() & 0xF; if ((temp & 0xE) == 0) flakes[i].x += (temp & 1) * 2 - 1; Uint16 dyNum = flakes[i].dyAcc + flakes[i].dyAdd; Uint8 dy = dyNum / dyDen; flakes[i].dyAcc = dyNum % dyDen; flakes[i].y += dy; } fill_rectangle_wh(VGAScreen, 0, 0, 320, 200, 0x8F); // Draw background snowflakes. for (size_t i = 0; i < COUNTOF(flakes) * 2 / 3; ++i) blit_sprite2_blend(VGAScreen, flakes[i].x, flakes[i].y, spriteSheet8, 225); // Draw prompt. for (uint i = 0; i < COUNTOF(prompt); ++i) draw_font_hv_full_shadow(VGAScreen, xCenter, yPrompt + dyPrompt * i, prompt[i], normal_font, centered, (i % 2) ? 2 : 4, -2, true, 1); // Draw choices. for (size_t i = 0; i < COUNTOF(choices); ++i) { const int x = xCenter - wChoice / 2 + wChoice * (int)i; const bool selected = (selectedIndex == i); draw_font_hv_full_shadow(VGAScreen, x, yChoice, choices[i], normal_font, centered, 15, selected ? -2 : -4, true, 1); } // Draw foreground snowflakes. for (size_t i = COUNTOF(flakes) * 2 / 3; i < COUNTOF(flakes); ++i) blit_sprite2_blend(VGAScreen, flakes[i].x, flakes[i].y, spriteSheet8, 226); if (restart) { mouseCursor = MOUSE_POINTER_NORMAL; fade_palette(palettes[0], 10, 0, 255); restart = false; } service_SDL_events(true); JE_mouseStart(); JE_showVGA(); JE_mouseReplace(); SDL_Delay(16); Uint16 oldMouseX = mouse_x; Uint16 oldMouseY = mouse_y; push_joysticks_as_keyboard(); service_SDL_events(false); mouseMoved = mouse_x != oldMouseX || mouse_y != oldMouseY; } while (!(newkey || newmouse || mouseMoved)); // Handle interaction. bool action = false; bool cancel = false; if (mouseMoved || newmouse) { // Find choice that was hovered or clicked. if (mouse_y >= yChoice && mouse_y < yChoice + hChoice) { for (size_t i = 0; i < COUNTOF(choices); ++i) { const int xChoice = xCenter - wChoice + wChoice * (int)i; if (mouse_x >= xChoice && mouse_x < xChoice + wChoice) { selectedIndex = i; if (newmouse && lastmouse_but == SDL_BUTTON_LEFT && lastmouse_x >= xChoice && lastmouse_x < xChoice + wChoice && lastmouse_y >= yChoice && lastmouse_y < yChoice + hChoice) { action = true; } break; } } } } if (newmouse) { if (lastmouse_but == SDL_BUTTON_RIGHT) { cancel = true; } } else if (newkey) { switch (lastkey_scan) { case SDL_SCANCODE_LEFT: { selectedIndex = selectedIndex == 0 ? COUNTOF(choices) - 1 : selectedIndex - 1; break; } case SDL_SCANCODE_RIGHT: { selectedIndex = selectedIndex == COUNTOF(choices) - 1 ? 0 : selectedIndex + 1; break; } case SDL_SCANCODE_SPACE: case SDL_SCANCODE_RETURN: { action = true; break; } case SDL_SCANCODE_ESCAPE: { cancel = true; break; } default: break; } } if (action || cancel) { fade_black(10); return !cancel && selectedIndex == 0; } } } opentyrian-2.1.20221123/src/xmas.h000066400000000000000000000017271432005211200163440ustar00rootroot00000000000000/* * OpenTyrian: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef XMAS_H #define XMAS_H #include extern bool xmas; bool xmas_time(void); bool xmas_prompt(void); #endif /* XMAS_H */ opentyrian-2.1.20221123/visualc/000077500000000000000000000000001432005211200160735ustar00rootroot00000000000000opentyrian-2.1.20221123/visualc/opentyrian.props.template000066400000000000000000000005031432005211200231600ustar00rootroot00000000000000 true "C:\\TYRIAN" opentyrian-2.1.20221123/visualc/opentyrian.sln000066400000000000000000000032261432005211200210040ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B079EE8B-7D9E-4965-B32B-4BDF407B449F}" ProjectSection(SolutionItems) = preProject ..\.editorconfig = ..\.editorconfig EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opentyrian", "opentyrian.vcxproj", "{F0FF290F-887C-4E70-8BF0-F5F45ADA8432}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {F0FF290F-887C-4E70-8BF0-F5F45ADA8432}.Debug|x64.ActiveCfg = Debug|x64 {F0FF290F-887C-4E70-8BF0-F5F45ADA8432}.Debug|x64.Build.0 = Debug|x64 {F0FF290F-887C-4E70-8BF0-F5F45ADA8432}.Debug|x86.ActiveCfg = Debug|Win32 {F0FF290F-887C-4E70-8BF0-F5F45ADA8432}.Debug|x86.Build.0 = Debug|Win32 {F0FF290F-887C-4E70-8BF0-F5F45ADA8432}.Release|x64.ActiveCfg = Release|x64 {F0FF290F-887C-4E70-8BF0-F5F45ADA8432}.Release|x64.Build.0 = Release|x64 {F0FF290F-887C-4E70-8BF0-F5F45ADA8432}.Release|x86.ActiveCfg = Release|Win32 {F0FF290F-887C-4E70-8BF0-F5F45ADA8432}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FB836A37-52A8-44D9-89F6-1D3DBA4A3B3D} EndGlobalSection EndGlobal opentyrian-2.1.20221123/visualc/opentyrian.vcxproj000066400000000000000000000277271432005211200217170ustar00rootroot00000000000000 Debug Win32 Release Win32 Debug x64 Release x64 15.0 {F0FF290F-887C-4E70-8BF0-F5F45ADA8432} $(SolutionDir)..\ $(SolutionDir)..\obj\$(Platform)\$(Configuration)\ $(ProjectName)-$(Platform)-$(Configuration) Application v142 Unicode false false true true false CompileAsC stdc17 $(SDL2BaseDir)\include;%(AdditionalIncludeDirectories) $(SDL2netBaseDir)\include;%(AdditionalIncludeDirectories) EnableAllWarnings true TARGET_WIN32;_CRT_SECURE_NO_WARNINGS;_WINDOWS;%(PreprocessorDefinitions) OPENTYRIAN_VERSION="$(OPENTYRIAN_VERSION)";%(PreprocessorDefinitions) WITH_NETWORK;%(PreprocessorDefinitions) TYRIAN_DIR=$(TYRIAN_DIR);%(PreprocessorDefinitions) 4018;4061;4204;4210;4242;4244;4245;4267;4305;4456;4457;4459;4668;4710;4711;4738;4820;5045;26451 SDL2.lib;SDL2main.lib;%(AdditionalDependencies) SDL2_net.lib;%(AdditionalDependencies) true Windows true Disabled _DEBUG;%(PreprocessorDefinitions) MaxSpeed true NDEBUG;%(PreprocessorDefinitions) true true true $(SDL2BaseDir)\lib\x86;%(AdditionalLibraryDirectories) $(SDL2netBaseDir)\lib\x86;%(AdditionalLibraryDirectories) $(SDL2BaseDir)\lib\x64;%(AdditionalLibraryDirectories) $(SDL2netBaseDir)\lib\x64;%(AdditionalLibraryDirectories) CopyPropsTemplates;EnsureSDLExists;$(BuildDependsOn) opentyrian-2.1.20221123/visualc/resources.rc000066400000000000000000000000251432005211200204300ustar00rootroot000000000000001 ICON "tyrian.ico" opentyrian-2.1.20221123/visualc/sdl_paths.props.template000066400000000000000000000005601432005211200227540ustar00rootroot00000000000000 $(SolutionDir)..\..\SDL2 $(SolutionDir)..\..\SDL2_net opentyrian-2.1.20221123/visualc/tyrian.ico000066400000000000000000002414461432005211200201100ustar00rootroot00000000000000 (F00 %n  . h>(  DDD8DX8DX8DX(8L8DX 444DDDDDDDDD444((( 4448DX8DXTh|DTl8DX8DX8DXDTlDTl8DX8DX(8L((( ((((8LDTlTh|DTl8DX8DX PPPTh|DTl8DX8DX(8L((( (((8DXTh|DTl8DX8DX8DX PPPDTlTh|DTl8DX8DX444 444DTlTh|DTl8DX8DX(8L 8DXDTlTh|DTl8DX8DX444 444Th|Th|DTl8DX8DX(8L PPPTh|Th|DTl8DX8DX444 444Th|Th|DTl8DX8DX(8L 8DXTh|Th|DTl8DX8DX444 (8LTh|Th|DTl8DX8DX(8L 8DXTh|Th|DTl8DX(8L((( (8LDTlTh|DTl8DX8DX(8L DTlTh|Th|DTl8DX(8L((( 8DXTh|DTl8DX8DX8DX DTlTh|Th|8DX8DX444 (8LTh|Th|8DX8DX8DX444 8DXTh|Th|DTl8DX(8L((( DTlTh|DTl8DX8DXDDD DTlTh|Th|8DX8DX444 444Th|hlhDTl8DX8DX444 PPPTh|Th|8DX8DX(8L((( 8DXTh|Dp8DX8DX8DXTh|Th|8DX8DX8DX444DTlTh|Dp8DX8DX8DX Th|Th|DTl8DX8DX(8LTh|Th|T8DX8DX8DX  Th|Th|Th|8DX8DX(8LTh|Th|x8DX8DX8DX  0 0 0DDDD  Th|Th|Dp8DX8DX8DXTh|Th|x8DX444444 ((((((DDDPPPPPPDDD((( 0 0 0 0TTTT  (((DDDPPPPPPDDD(((((( DDDDDDT8DX8DX8DXTh|Th|x8DX444PPP 444DDDhlhhlh\\\PPP ,@,@,@,@ ,| ,| ,| ,| 0 0 PPP\\\hlhhlhDDD444 hlhPPPTh|8DX8DXTh|DpDTl444PPP444DDDhlhxxxhlhDDD (((Q )  (,1GXkAPe*/8I &/;GXn7CW!* &.8L^s:H\$'+ !qEVkE222///mQSQQQQ{131~lplGMU)-3:;:qrqDDD&&&~hFFF\\\.0.4%%%TTTXYY8SkGS9O8LZXXXAAA""".0.4\\\FFFh***~JJJMMM222;<;TVTSSS678'''sss666888TTTQQQRSXHSGNPPPKKKJJJ///666sss'''579SSSIII222S356/03;;;1AM---XXX$$$4(((222222XXX[[[WWWZZZ444///!!!$$$4XXX--->T;;;/03356SS:BL-6e>?CFZg"2<"-6 tNON```GGGt 1111575Wm>?C-6e:BLSSINUDM~Wnz@i-;,MN E NNNBBB E NM,(16Jk}CjDM~INUS ;;;[\[nonXo{S6Qa(((b$=N2C$$8G3Fb7776QaSDknon[\[;;; '''666EFE]]]]uNzD`pXZX;;;BJODo2Si $;$.""""'''$$$#!';$.N_6`x;<B\BN2Q6S&,V #=MB\B\(Pl7]t=LVNONbbbS\4=p888%%%9:9WWWXYXAAAMMMWjtLxHtO{Nz,ES I-D2k,1\ )AP8R8QC]-Vq-K_HLO```z|zWWW9:9%%%RRR[\[...&&&***!&(.9LuIuQ}0IW000%,H,9l6BFR13@@AO>J.U)F">---!8F7R6P6M ,$%),111565[\[RRR  Y%%%+++,7=R~Jv6Zq 444+,-##%29oMY#.T6CmFS#,:&'' (((=R8Q9Q"+1())Y  a666IIILLL9GOItW?\m112333$$$333EJu!/(48>m>>>121!#&=T"Jf9Q",4LLL===!!!a ) #(-1K#3?)< +- v!a0D Hd.Wr$ G,u'49=X>>>&3<)< +%7#3 a'P6L@ZB]%Lf1[vCm)1...!)^%e > 0!@? *twtGHG---222   ;$g")U456$/8 +"/&8+P ''%M5JC^ Hd Hd.WrjYHfx.BO/@IXbg6=B.3I!Hc@jYcqhrDL(4E ;OX_d3U6L+?+?+?'5EEE8@.:#@/3V8D1=29U5Xo/Ys5ay"D[,9'n  /JKJ:::+29$5'IaLw_lv,G]CVcCVc , '.*6/ !(n,:4L/E+?*=#;)5/;#'I":#/v"0w5Xl8d|)Lc,;+!!!JKJ:::0;D0DE_5]wZgq1YqWlv^u~>]p,TpT\a16<)<) "((+ /@8O0E*=%E%1|#=$.4.F_6[|0Un1A!$!!!!QQQ@@@===6DN+NeElanx+I^=Tc=Wg2J[+Pkluy5=E,> &*/###+++!$&0B7M,@'9#0=G_4N_"4---EEE:?B:CI?HOFFF-/1PVYz{zXcj]mwasPmlrt2RfX^b./0*-/GGG:::111'''4"*6J;R!*2GT@Uc!%G'N %%%OPOLLL*LLLPPPOOO2:S)7dTVV@@@222 %'/!)='8EdddUVU???777!!!N''''!'G(>O*7+AN6HR'''"...*+-%2<#:N)6G)7H(7J(8L.EV$'*&6J$=S)7H$2@678'*/$+526:'/6#/<0C:Uk+9Q!F_-Og0RjP[aQRQDDD@A@'''"$:J!0:!4B`9N ...WXW445579:<>:;=78:/6@:;=BCB")4Vn0Xs:c}.UpJsrrIs.Up0Le!3G6K`$8:;HJL;N[PSURUWUWXSTSYYYprp999$;K#09`!. 7G'3 000klk233467:;=:;<99908AQQQZZZ#,6@^ngNxXxvLx0Zt0Ld*8$+!'momklkBT_SSSSUWRUWORTZ[[vxv??? '3!7G%%%2C$0...kmk*-0(8$1@ .=9996HSRRR121444uR\a`lqar{g0Lb0DO;AE000uEFEqrqSeoTVT,J_'Lf*KcOW]vxv58;#$02C%-&/>-;!3=(8CGGG000000!.9!7I>>>&+8 \AAAQQQadhwUZ]QQQAAA \---87?C\\\4Sg4JXMMM\\\:CI'7&0,:/>%-& #%/"2<08=AAAeee666IJI!/9 ,6N!!!CDCmpm_a_???!!!'''111N4EP;KTfgfUVUxyx8?D!*%-$,#b333kkkDDD555^  ! NLMLHHH999...N ''' ///^MMM_a_~~:;<b///FFF###n --- ;;;nSSS===???x8`0 ``A p0````@? p>|x 0`?( @  #(.08D%+5gg*,0-3<!' ,29>Ma*4B6=H@Nb'+2  (0;ETg(2A !!!;GU@Nb!%+"5?Na4=NOP123232FGF=AD???NNN(((AJJJPPPMRzMPiLLL???(((ANNN???8@EAAA---48?6;SAF_ejz>e}"'(/j$-h A333+++A!*h ,j!&)Ggy]x_e>AF121IJI]mtLxFJK4448Vi&BU"%%%)ET,GX898NSSLxOjzIJI121*07`LXmowYZYdgd[``Hr/NaA!.X#3W$/%-D,N&KA=S3[tSXZcgccecLX07`**7>cWaxy[\[dheSn}Eq=f'>q6H3:nBHz)c*111nnn[][:::GQVDmLxKw 8I4E6:VCF`!6b*;z "$!F`9R!G_'H1:o>H-9c!(&+/;U5J%)-.-GFFFb###$ '((DEEBHLHpLq.24(((34:'J$@8:A!!!"!E_9P38;BBB (6!9L#/3CP=IONxAdx,@K$)Q]"##+60QfAZ29>>>>.02 %*8*Qjd(/>@C7HS&9JKu:f~+Nc8&5N+HZ:e~;`x"A7HS;AH"2A .# &nB[1[uQ|#+17P_MRUJRV>NX'8A$1R&6q@CP,6< !1&$5n%2\=VD^+SmCn',!%=K 1%B )011AAA,-- %T"+D+5 ( #5$5%\/1(((!CZC^ Hd4^xg5Wm%EZ*="3&Nh?NZ6>i~6Bi)047*>,</V!D^$Ib %6&':,@&7!"5!)0d1:uSTS+PgD_+Sn?hA[k/DPQ[`48?(;(QkQ_v~IPo&?QW]`-A,;&0+EU);G'AS2F)<,@*=?BE6>xF5$(+CGK1=I#BZ-'0Y#5C&=N+6NFGF26:2>F4:B.6B@FL!&*0Pg2Tm.MbVo|Uo},Lc,AV0F[=IR7N]EU`UZ]YYYYZY---+7N#8F+3-1A"P\]\,0329@7:>18>ddd777V!/9xTsesf5]w-?P%xGHGV~~HT[MTZDT^OW\ghg$&(P"1A+3-,8;,;3?*27klk+-/$.7-;E.59)))`===MMM|a|GJL777'''@@@`IPTCXe5JXPUXnon+7(4-;,8;'*!2=u,02\\\???.59 *3t """DMNMz{zY[Y==="""D--- 4CLtGOT\]\hih&-&0u &*:LLL///  333HIHXYX+++:??GG0 ?@p80 8?(  ,2;10:G!%,^#$'^2:F'+31% 9EU#,8E3=IE8CR 7BML=KZ...C'''A . '''A...C?L[(.8L8AL/28e79Wf1@J!9HJ@ !@"9GJ+;DD[gUdt79t"/8#>R8:;4G{Dm.4>&5V9Y3DT6GU-HZ /V2;E*){15 4@C]Bl':L!;!6Kaf-3@'$C"9N )'9#,2519v;Pa'Le7TgGQV%0;]y[i}>Qd7EQ",)=K5E-A#0?+3n0Ee'CT#-/5667?EWiuNcqB]p6?G')+111&1/-?*D,?L;;;R+045=F-;F/DU@O\=N])8G(1@P[JLMNONR!1>-:O.;^LOP,1608?HHHnAQXhQl}-6>`a`nHT\FRZMSV)5^-:O0;'EFF368)2 ###_`_IJI###3AK MQSIMN$.'AA AA1AAAAAAAAA@AA!A