diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f0bf8293..9d7da1644 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,11 @@ list( APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ) include( CreateLaunchers ) include( FindPackageHandleStandardArgs ) +# Produce a warning if XP support will be missing. +if( MSVC14 AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v140_xp" ) + message( WARNING "This project supports Windows XP (including XP x64), but you must set the optional toolset to v140_xp manually to have it in your build! Use -T \"v140_xp\" from the command prompt." ) +endif() + # Support cross compiling option( FORCE_CROSSCOMPILE "Turn on cross compiling." NO ) if( FORCE_CROSSCOMPILE ) diff --git a/docs/licenses/README.TXT b/docs/licenses/README.TXT new file mode 100644 index 000000000..f60796436 --- /dev/null +++ b/docs/licenses/README.TXT @@ -0,0 +1,27 @@ +The original Doom source code was released by id Software under the +Doom Source Code License. See doomlic.txt. + +Parts of the renderer use code from the BUILD engine by Ken Silverman. +See buildlic.txt. + +The majority of original code uses a BSD-like lincese. See bsd.txt. + +This software is based in part on the work of the Independent JPEG Group. + +This software uses the 'zlib' general purpose compression library by +Jean-loup Gailly and Mark Adler. + +This software uses the gdtoa package, see gdtoa.txt. + +This software uses the snes_spc library, which is covered by the GNU Lesser +General Public License. See lgpl.txt. + +This software uses the "Dynamic Universal Music Bibliotheque" library for +MOD music playback. See dumb.txt for original license. The version used, +however, has been heavily modified from its original form and is the same +version used by the foobar2000 component foo_dumb as of mid-2008, found at +http://kode54.foobar2000.org/. + +This software uses the OPL emulator from MAME 0.95. Playback of MUS files +on the OPL emulation is accomplished with some help from Vladimir Arnost's +MUS File Player Library, with fixes to make it more accurate. diff --git a/docs/licenses/bsd.txt b/docs/licenses/bsd.txt new file mode 100644 index 000000000..aa48716a9 --- /dev/null +++ b/docs/licenses/bsd.txt @@ -0,0 +1,27 @@ +**--------------------------------------------------------------------------- +** Copyright 1998-2009 Randy Heit, Christoph Oelckers, et al. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- \ No newline at end of file diff --git a/docs/licenses/buildlic.txt b/docs/licenses/buildlic.txt new file mode 100644 index 000000000..a0cec1251 --- /dev/null +++ b/docs/licenses/buildlic.txt @@ -0,0 +1,71 @@ +BUILD SOURCE CODE LICENSE TERMS: 06/20/2000 + +[1] I give you permission to make modifications to my Build source and + distribute it, BUT: + +[2] Any derivative works based on my Build source may be distributed ONLY + through the INTERNET. + +[3] Distribution of any derivative works MUST be done completely FREE of + charge - no commercial exploitation whatsoever. + +[4] Anything you distribute which uses a part of my Build Engine source + code MUST include: + + [A] The following message somewhere in the archive: + + // "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman + // Ken Silverman's official web site: "http://www.advsys.net/ken" + // See the included license file "BUILDLIC.TXT" for license info. + + [B] This text file "BUILDLIC.TXT" along with it. + + [C] Any source files that you modify must include this message as well: + + // This file has been modified from Ken Silverman's original release + +[5] The use of the Build Engine for commercial purposes will require an + appropriate license arrangement with me. Contact information is + on my web site. + +[6] I take no responsibility for damage to your system. + +[7] Technical support: Before contacting me with questions, please read + and do ALL of the following! + + [A] Look though ALL of my text files. There are 7 of them (including this + one). I like to think that I wrote them for a reason. You will find + many of your answers in the history section of BUILD.TXT and + BUILD2.TXT (they're located inside SRC.ZIP). + + [B] If that doesn't satisfy you, then try going to: + + "http://www.advsys.net/ken/buildsrc" + + where I will maintain a Build Source Code FAQ (or perhaps I might + just provide a link to a good FAQ). + + [C] I am willing to respond to questions, but ONLY if they come at a rate + that I can handle. + + PLEASE TRY TO AVOID ASKING DUPLICATE QUESTIONS! + + As my line of defense, I will post my current policy about + answering Build source questions (right below the E-mail address + on my web site.) You can check there to see if I'm getting + overloaded with questions or not. + + If I'm too busy, it might say something like this: + + I'm too busy to answer Build source questions right now. + Sorry, but don't expect a reply from me any time soon. + + If I'm open for Build source questions, please state your question + clearly and don't include any unsolicited attachments unless + they're really small (like less than 50k). Assume that I have + a 28.8k modem. Also, don't leave out important details just + to make your question appear shorter - making me guess what + you're asking doesn't save me time! + +---------------------------------------------------------------------------- +-Ken S. (official web site: http://www.advsys.net/ken) diff --git a/docs/licenses/cephes.txt b/docs/licenses/cephes.txt new file mode 100644 index 000000000..cb4e3f9d6 --- /dev/null +++ b/docs/licenses/cephes.txt @@ -0,0 +1,10 @@ + Some software in this archive may be from the book _Methods and +Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +International, 1989) or from the Cephes Mathematical Library, a +commercial product. In either event, it is copyrighted by the author. +What you see here may be used freely but it comes with no support or +guarantee. + + Stephen L. Moshier + moshier@na-net.ornl.gov + \ No newline at end of file diff --git a/docs/licenses/doomlic.txt b/docs/licenses/doomlic.txt new file mode 100644 index 000000000..2b2252ee2 --- /dev/null +++ b/docs/licenses/doomlic.txt @@ -0,0 +1,112 @@ + + + LIMITED USE SOFTWARE LICENSE AGREEMENT + + This Limited Use Software License Agreement (the "Agreement") +is a legal agreement between you, the end-user, and Id Software, Inc. +("ID"). By downloading or purchasing the software material, which +includes source code (the "Source Code"), artwork data, music and +software tools (collectively, the "Software"), you are agreeing to +be bound by the terms of this Agreement. If you do not agree to the +terms of this Agreement, promptly destroy the Software you may have +downloaded or copied. + +ID SOFTWARE LICENSE + +1. Grant of License. ID grants to you the right to use the +Software. You have no ownership or proprietary rights in or to the +Software, or the Trademark. For purposes of this section, "use" means +loading the Software into RAM, as well as installation on a hard disk +or other storage device. The Software, together with any archive copy +thereof, shall be destroyed when no longer used in accordance with +this Agreement, or when the right to use the Software is terminated. +You agree that the Software will not be shipped, transferred or +exported into any country in violation of the U.S. Export +Administration Act (or any other law governing such matters) and that +you will not utilize, in any other manner, the Software in violation +of any applicable law. + +2. Permitted Uses. For educational purposes only, you, the +end-user, may use portions of the Source Code, such as particular +routines, to develop your own software, but may not duplicate the +Source Code, except as noted in paragraph 4. The limited right +referenced in the preceding sentence is hereinafter referred to as +"Educational Use." By so exercising the Educational Use right you +shall not obtain any ownership, copyright, proprietary or other +interest in or to the Source Code, or any portion of the Source +Code. You may dispose of your own software in your sole discretion. +With the exception of the Educational Use right, you may not +otherwise use the Software, or an portion of the Software, which +includes the Source Code, for commercial gain. + +3. Prohibited Uses: Under no circumstances shall you, the +end-user, be permitted, allowed or authorized to commercially exploit +the Software. Neither you nor anyone at your direction shall do any +of the following acts with regard to the Software, or any portion +thereof: + + Rent; + + Sell; + + Lease; + + Offer on a pay-per-play basis; + + Distribute for money or any other consideration; or + + In any other manner and through any medium whatsoever +commercially exploit or use for any commercial purpose. + +Notwithstanding the foregoing prohibitions, you may commercially +exploit the software you develop by exercising the Educational Use +right, referenced in paragraph 2. hereinabove. + +4. Copyright. The Software and all copyrights related thereto +(including all characters and other images generated by the Software +or depicted in the Software) are owned by ID and is protected by +United States copyright laws and international treaty provisions. +Id shall retain exclusive ownership and copyright in and to the +Software and all portions of the Software and you shall have no +ownership or other proprietary interest in such materials. You must +treat the Software like any other copyrighted material. You may not +otherwise reproduce, copy or disclose to others, in whole or in any +part, the Software. You may not copy the written materials +accompanying the Software. You agree to use your best efforts to +see that any user of the Software licensed hereunder complies with +this Agreement. + +5. NO WARRANTIES. ID DISCLAIMS ALL WARRANTIES, BOTH EXPRESS +IMPLIED, INCLUDING BUT NOT LIMITED TO, IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE WITH RESPECT +TO THE SOFTWARE. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL +RIGHTS. YOU MAY HAVE OTHER RIGHTS WHICH VARY FROM JURISDICTION TO +JURISDICTION. ID DOES NOT WARRANT THAT THE OPERATION OF THE SOFTWARE +WILL BE UNINTERRUPTED, ERROR FREE OR MEET YOUR SPECIFIC REQUIREMENTS. +THE WARRANTY SET FORTH ABOVE IS IN LIEU OF ALL OTHER EXPRESS +WARRANTIES WHETHER ORAL OR WRITTEN. THE AGENTS, EMPLOYEES, +DISTRIBUTORS, AND DEALERS OF ID ARE NOT AUTHORIZED TO MAKE +MODIFICATIONS TO THIS WARRANTY, OR ADDITIONAL WARRANTIES ON BEHALF +OF ID. + + Exclusive Remedies. The Software is being offered to you +free of any charge. You agree that you have no remedy against ID, its +affiliates, contractors, suppliers, and agents for loss or damage +caused by any defect or failure in the Software regardless of the form +of action, whether in contract, tort, includinegligence, strict +liability or otherwise, with regard to the Software. This Agreement +shall be construed in accordance with and governed by the laws of the +State of Texas. Copyright and other proprietary matters will be +governed by United States laws and international treaties. IN ANY +CASE, ID SHALL NOT BE LIABLE FOR LOSS OF DATA, LOSS OF PROFITS, LOST +SAVINGS, SPECIAL, INCIDENTAL, CONSEQUENTIAL, INDIRECT OR OTHER +SIMILAR DAMAGES ARISING FROM BREACH OF WARRANTY, BREACH OF CONTRACT, +NEGLIGENCE, OR OTHER LEGAL THEORY EVEN IF ID OR ITS AGENT HAS BEEN +ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY ANY +OTHER PARTY. Some jurisdictions do not allow the exclusion or +limitation of incidental or consequential damages, so the above +limitation or exclusion may not apply to you. + + + + diff --git a/docs/licenses/dumb.txt b/docs/licenses/dumb.txt new file mode 100644 index 000000000..231bfa7f1 --- /dev/null +++ b/docs/licenses/dumb.txt @@ -0,0 +1,54 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * licence.txt - Conditions for use of DUMB. / / \ \ + * | < / \_ + * If you do not agree to these terms, please | \/ /\ / + * do not use DUMB. \_ / > / + * | \ / / + * Information in [brackets] is provided to aid | ' / + * interpretation of the licence. \__/ + */ + + +Dynamic Universal Music Bibliotheque + +Copyright (C) 2001-2003 Ben Davis, Robert J Ohannessian and Julien Cugniere + +This software is provided 'as-is', without any express or implied warranty. +In no event shall the authors be held liable for any damages arising from the +use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a + product, you are requested to acknowledge its use in the product + documentation, along with details on where to get an unmodified version of + this software, but this is not a strict requirement. + + [Note that the above point asks for a link to DUMB, not just a mention. + Googling for DUMB doesn't help much! The URL is "http://dumb.sf.net/".] + + [The only reason why the link is not strictly required is that such a + requirement prevents DUMB from being used in projects with certain other + licences, notably the GPL. See http://www.gnu.org/philosophy/bsd.html .] + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed from or altered in any source distribution. + +4. If you are using the Program in someone else's bedroom at any Monday + 3:05 PM, you are not allowed to modify the Program for ten minutes. [This + clause provided by Inphernic; every licence should contain at least one + clause, the reasoning behind which is far from obvious.] diff --git a/docs/licenses/gdtoa.txt b/docs/licenses/gdtoa.txt new file mode 100644 index 000000000..13c0d4140 --- /dev/null +++ b/docs/licenses/gdtoa.txt @@ -0,0 +1,27 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ diff --git a/docs/licenses/lgpl.txt b/docs/licenses/lgpl.txt new file mode 100644 index 000000000..5ab7695ab --- /dev/null +++ b/docs/licenses/lgpl.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 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. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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 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 + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/output_sdl/CMakeLists.txt b/output_sdl/CMakeLists.txt index de42f6be6..323d94149 100644 --- a/output_sdl/CMakeLists.txt +++ b/output_sdl/CMakeLists.txt @@ -1,11 +1,17 @@ cmake_minimum_required( VERSION 2.8.7 ) if( NOT NO_FMOD AND FMOD_INCLUDE_DIR ) - add_library( output_sdl MODULE output_sdl.c ) include_directories( ${FMOD_INCLUDE_DIR} ${SDL2_INCLUDE_DIR} ) - target_link_libraries( output_sdl ${SDL2_LIBRARY} ) + check_library_exists(${FMOD_LIBRARY} "FMOD_System_GetDriverCaps" "fmod.h" FMOD_IS_EX) - FILE( WRITE ${CMAKE_CURRENT_BINARY_DIR}/link-make "if [ ! -e ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so ]; then ln -sf output_sdl/liboutput_sdl.so ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so; fi" ) - add_custom_command( TARGET output_sdl POST_BUILD - COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/link-make - COMMAND /bin/sh -c ${CMAKE_CURRENT_BINARY_DIR}/link-make ) + # Only usable with FMOD Ex + if( FMOD_IS_EX ) + include_directories( ${FMOD_INCLUDE_DIR} ${SDL2_INCLUDE_DIR} ) + add_library( output_sdl MODULE output_sdl.c ) + target_link_libraries( output_sdl ${SDL2_LIBRARY} ) + + FILE( WRITE ${CMAKE_CURRENT_BINARY_DIR}/link-make "if [ ! -e ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so ]; then ln -sf output_sdl/liboutput_sdl.so ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so; fi" ) + add_custom_command( TARGET output_sdl POST_BUILD + COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/link-make + COMMAND /bin/sh -c ${CMAKE_CURRENT_BINARY_DIR}/link-make ) + endif() endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fdb668b01..fb54e2077 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -262,6 +262,10 @@ if( NOT NO_FMOD ) if( FMOD_INCLUDE_DIR ) message( STATUS "FMOD include files found at ${FMOD_INCLUDE_DIR}" ) include_directories( "${FMOD_INCLUDE_DIR}" ) + + if( EXISTS "${FMOD_INCLUDE_DIR}/fmod_common.h" ) + set( FMOD_STUDIO YES ) + endif() else() message( STATUS "Could not find FMOD include files" ) set( NO_FMOD ON ) @@ -484,8 +488,10 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) set( CMAKE_C_FLAGS "-Wno-unused-result ${CMAKE_C_FLAGS}" ) set( CMAKE_CXX_FLAGS "-Wno-unused-result ${CMAKE_CXX_FLAGS}" ) endif() - if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set( CMAKE_CXX_FLAGS "-Wno-inconsistent-missing-override ${CMAKE_CXX_FLAGS}" ) + if( CMAKE_CXX_COMPILER_ID STREQUAL "Clang" ) + if( APPLE OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "3.6" ) + set( CMAKE_CXX_FLAGS "-Wno-inconsistent-missing-override ${CMAKE_CXX_FLAGS}" ) + endif() endif() set( CMAKE_C_FLAGS "-Wall -Wextra -Wno-unused -Wno-unused-parameter -Wno-missing-field-initializers -ffp-contract=off ${CMAKE_C_FLAGS}" ) set( CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unused -Wno-unused-parameter -Wno-missing-field-initializers -ffp-contract=off ${CMAKE_CXX_FLAGS}" ) @@ -633,6 +639,7 @@ set( PLAT_WIN32_SOURCES win32/i_main.cpp win32/i_movie.cpp win32/i_system.cpp + win32/i_specialpaths.cpp win32/st_start.cpp win32/win32video.cpp ) set( PLAT_POSIX_SOURCES @@ -650,8 +657,11 @@ set( PLAT_SDL_SOURCES posix/sdl/i_timer.cpp posix/sdl/sdlvideo.cpp posix/sdl/st_start.cpp ) +set( PLAT_UNIX_SOURCES + posix/unix/i_specialpaths.cpp ) set( PLAT_OSX_SOURCES posix/osx/iwadpicker_cocoa.mm + posix/osx/i_specialpaths.mm posix/osx/zdoom.icns ) set( PLAT_COCOA_SOURCES posix/cocoa/critsec.cpp @@ -668,7 +678,7 @@ set( PLAT_COCOA_SOURCES if( WIN32 ) set( SYSTEM_SOURCES_DIR win32 ) set( SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ) - set( OTHER_SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ${PLAT_UNIX_SOURCES} ) if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) # CMake is not set up to compile and link rc files with GCC. :( @@ -683,12 +693,12 @@ elseif( APPLE ) if( OSX_COCOA_BACKEND ) set( SYSTEM_SOURCES_DIR posix posix/cocoa ) set( SYSTEM_SOURCES ${PLAT_COCOA_SOURCES} ) - set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_SDL_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_UNIX_SOURCES} ) else() set( SYSTEM_SOURCES_DIR posix posix/sdl ) set( SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ) set( PLAT_OSX_SOURCES ${PLAT_OSX_SOURCES} posix/sdl/i_system.mm ) - set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} ${PLAT_UNIX_SOURCES} ) endif() set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_POSIX_SOURCES} ${PLAT_OSX_SOURCES} "${FMOD_LIBRARY}" ) @@ -698,7 +708,7 @@ elseif( APPLE ) set_source_files_properties( posix/osx/iwadpicker_cocoa.mm PROPERTIES COMPILE_FLAGS -fobjc-exceptions ) else() set( SYSTEM_SOURCES_DIR posix posix/sdl ) - set( SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ) + set( SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_UNIX_SOURCES} ) set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ) endif() @@ -1246,7 +1256,6 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE ${FASTMATH_SOURCES} ${PCH_SOURCES} x86.cpp - m_specialpaths.cpp strnatcmp.c zstring.cpp math/asin.c @@ -1326,7 +1335,16 @@ endif() if( MSVC ) option( ZDOOM_GENERATE_MAPFILE "Generate .map file for debugging." OFF ) - set( LINKERSTUFF "/MANIFEST:NO /DELAYLOAD:\"fmodex${X64}.dll\"" ) + set( LINKERSTUFF "/MANIFEST:NO" ) + + if( NOT NO_FMOD ) + if( FMOD_STUDIO ) + set( LINKERSTUFF "${LINKERSTUFF} /DELAYLOAD:\"fmod${X64}.dll\"" ) + else() + set( LINKERSTUFF "${LINKERSTUFF} /DELAYLOAD:\"fmodex${X64}.dll\"" ) + endif() + endif() + if( ZDOOM_GENERATE_MAPFILE ) set( LINKERSTUFF "${LINKERSTUFF} /MAP" ) endif() @@ -1366,17 +1384,23 @@ if( APPLE ) MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/posix/osx/zdoom-info.plist" ) if( NOT NO_FMOD ) + if( FMOD_STUDIO ) + set( FMOD_DYLIB libfmod.dylib ) + else() + set( FMOD_DYLIB libfmodex.dylib ) + endif() + # Fix fmod link so that it can be found in the app bundle. find_program( OTOOL otool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) find_program( INSTALL_NAME_TOOL install_name_tool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) execute_process( COMMAND "${OTOOL}" -L "${FMOD_LIBRARY}" - COMMAND grep "libfmodex.dylib (compat" + COMMAND grep "${FMOD_DYLIB} (compat" COMMAND head -n1 COMMAND awk "{print $1}" OUTPUT_VARIABLE FMOD_LINK OUTPUT_STRIP_TRAILING_WHITESPACE ) add_custom_command( TARGET zdoom POST_BUILD - COMMAND "${INSTALL_NAME_TOOL}" -change "${FMOD_LINK}" @executable_path/../Frameworks/libfmodex.dylib "$" + COMMAND "${INSTALL_NAME_TOOL}" -change "${FMOD_LINK}" @executable_path/../Frameworks/${FMOD_DYLIB} "$" COMMENT "Relinking FMOD Ex" ) endif() endif() @@ -1410,6 +1434,7 @@ source_group("Resource Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r source_group("POSIX Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/.+") source_group("Cocoa Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/cocoa/.+") source_group("OS X Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/osx/.+") +source_group("Unix Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/unix/.+") source_group("SDL Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/sdl/.+") source_group("SFMT" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sfmt/.+") source_group("Shared Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_shared/.+") diff --git a/src/actionspecials.h b/src/actionspecials.h index 4ab222b2c..9b9c38cb9 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -42,7 +42,7 @@ DEFINE_SPECIAL(Ceiling_LowerByValue, 40, 3, 5, 5) DEFINE_SPECIAL(Ceiling_RaiseByValue, 41, 3, 4, 4) DEFINE_SPECIAL(Ceiling_CrushAndRaise, 42, 3, 4, 4) DEFINE_SPECIAL(Ceiling_LowerAndCrush, 43, 3, 4, 4) -DEFINE_SPECIAL(Ceiling_CrushStop, 44, 1, 1, 1) +DEFINE_SPECIAL(Ceiling_CrushStop, 44, 1, 2, 2) DEFINE_SPECIAL(Ceiling_CrushRaiseAndStay, 45, 3, 4, 4) DEFINE_SPECIAL(Floor_CrushStop, 46, 1, 1, 1) DEFINE_SPECIAL(Ceiling_MoveToValue, 47, 3, 5, 5) @@ -59,7 +59,7 @@ DEFINE_SPECIAL(Sector_SetPortal, 57, -1, -1, 5) DEFINE_SPECIAL(Sector_CopyScroller, 58, -1, -1, 2) DEFINE_SPECIAL(Polyobj_OR_MoveToSpot, 59, 3, 3, 3) DEFINE_SPECIAL(Plat_PerpetualRaise, 60, 3, 3, 3) -DEFINE_SPECIAL(Plat_Stop, 61, 1, 1, 1) +DEFINE_SPECIAL(Plat_Stop, 61, 1, 2, 2) DEFINE_SPECIAL(Plat_DownWaitUpStay, 62, 3, 3, 3) DEFINE_SPECIAL(Plat_DownByValue, 63, 4, 4, 4) DEFINE_SPECIAL(Plat_UpWaitDownStay, 64, 3, 3, 3) diff --git a/src/actor.h b/src/actor.h index 154d14289..d804b0995 100644 --- a/src/actor.h +++ b/src/actor.h @@ -111,6 +111,8 @@ struct FPortalGroupArray; // Any questions? // + + // --- mobj.flags --- enum ActorFlag { @@ -283,7 +285,7 @@ enum ActorFlag4 enum ActorFlag5 { MF5_DONTDRAIN = 0x00000001, // cannot be drained health from. - /* FREE SLOT 0x00000002*/ + MF5_GETOWNER = 0x00000002, MF5_NODROPOFF = 0x00000004, // cannot drop off under any circumstances. MF5_NOFORWARDFALL = 0x00000008, // Does not make any actor fall forward by being damaged by this MF5_COUNTSECRET = 0x00000010, // From Doom 64: actor acts like a secret @@ -1014,7 +1016,9 @@ public: SDWORD tics; // state tic counter FState *state; - VMFunction *Damage; // For missiles and monster railgun + //VMFunction *Damage; // For missiles and monster railgun + int DamageVal; + VMFunction *DamageFunc; int projectileKickback; ActorFlags flags; ActorFlags2 flags2; // Heretic flags @@ -1057,6 +1061,7 @@ public: int skillrespawncount; int TIDtoHate; // TID of things to hate (0 if none) FNameNoInit Species; // For monster families + TObjPtr alternative; // (Un)Morphed actors stored here. Those with the MF_UNMORPHED flag are the originals. TObjPtr tracer; // Thing being chased/attacked for tracers TObjPtr master; // Thing which spawned this one (prevents mutual attacks) double Floorclip; // value to use for floor clipping @@ -1200,6 +1205,23 @@ public: FState *GetRaiseState(); void Revive(); + void SetDamage(int dmg) + { + DamageVal = dmg; + DamageFunc = nullptr; + } + + bool IsZeroDamage() const + { + return DamageVal == 0 && DamageFunc == nullptr; + } + + void RestoreDamage() + { + DamageVal = GetDefault()->DamageVal; + DamageFunc = GetDefault()->DamageFunc; + } + FState *FindState (FName label) const { return GetClass()->FindState(1, &label); diff --git a/src/b_game.cpp b/src/b_game.cpp index 9681aa7a8..9d6419787 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -494,7 +494,7 @@ bool FCajunMaster::LoadBots () tmp = M_GetCajunPath(BOTFILENAME); if (tmp.IsEmpty()) { - DPrintf ("No " BOTFILENAME ", so no bots\n"); + DPrintf (DMSG_ERROR, "No " BOTFILENAME ", so no bots\n"); return false; } try diff --git a/src/b_think.cpp b/src/b_think.cpp index 0ff75722a..5a98b5c1a 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -259,7 +259,7 @@ void DBot::ThinkForMove (ticcmd_t *cmd) r = pr_botmove(); if (r < 128) { - TThinkerIterator it (STAT_INVENTORY, bglobal.firstthing); + TThinkerIterator it (MAX_STATNUM+1, bglobal.firstthing); AInventory *item = it.Next(); if (item != NULL || (item = it.Next()) != NULL) diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index d72fc79a2..03a7495e4 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -923,10 +923,21 @@ static bool IsActorACountItem(AActor *mo) return mo->IsKindOf(RUNTIME_CLASS(AInventory)) && mo->flags&MF_SPECIAL && mo->flags&MF_COUNTITEM; } -static void PrintFilteredActorList(const ActorTypeChecker IsActorType, const char *FilterName) +// [SP] for all actors +static bool IsActor(AActor *mo) +{ + if (mo->IsKindOf(RUNTIME_CLASS(AInventory))) + return static_cast(mo)->Owner == NULL; // [SP] Exclude inventory-owned items + else + return true; +} + +// [SP] modified - now allows showing count only, new arg must be passed. Also now still counts regardless, if lists are printed. +static void PrintFilteredActorList(const ActorTypeChecker IsActorType, const char *FilterName, bool countOnly) { AActor *mo; const PClass *FilterClass = NULL; + int counter = 0; if (FilterName != NULL) { @@ -943,10 +954,32 @@ static void PrintFilteredActorList(const ActorTypeChecker IsActorType, const cha { if ((FilterClass == NULL || mo->IsA(FilterClass)) && IsActorType(mo)) { - Printf ("%s at (%f,%f,%f)\n", - mo->GetClass()->TypeName.GetChars(), mo->X(), mo->Y(), mo->Z()); + counter++; + if (!countOnly) + Printf ("%s at (%f,%f,%f)\n", + mo->GetClass()->TypeName.GetChars(), mo->X(), mo->Y(), mo->Z()); } } + Printf("%i match(s) found.\n", counter); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- +CCMD(actorlist) // [SP] print all actors (this can get quite big?) +{ + if (CheckCheatmode ()) return; + + PrintFilteredActorList(IsActor, argv.argc() > 1 ? argv[1] : NULL, false); +} + +CCMD(actornum) // [SP] count all actors +{ + if (CheckCheatmode ()) return; + + PrintFilteredActorList(IsActor, argv.argc() > 1 ? argv[1] : NULL, true); } //----------------------------------------------------------------------------- @@ -958,7 +991,14 @@ CCMD(monster) { if (CheckCheatmode ()) return; - PrintFilteredActorList(IsActorAMonster, argv.argc() > 1 ? argv[1] : NULL); + PrintFilteredActorList(IsActorAMonster, argv.argc() > 1 ? argv[1] : NULL, false); +} + +CCMD(monsternum) // [SP] count monsters +{ + if (CheckCheatmode ()) return; + + PrintFilteredActorList(IsActorAMonster, argv.argc() > 1 ? argv[1] : NULL, true); } //----------------------------------------------------------------------------- @@ -970,7 +1010,14 @@ CCMD(items) { if (CheckCheatmode ()) return; - PrintFilteredActorList(IsActorAnItem, argv.argc() > 1 ? argv[1] : NULL); + PrintFilteredActorList(IsActorAnItem, argv.argc() > 1 ? argv[1] : NULL, false); +} + +CCMD(itemsnum) // [SP] # of any items +{ + if (CheckCheatmode ()) return; + + PrintFilteredActorList(IsActorAnItem, argv.argc() > 1 ? argv[1] : NULL, true); } //----------------------------------------------------------------------------- @@ -982,7 +1029,14 @@ CCMD(countitems) { if (CheckCheatmode ()) return; - PrintFilteredActorList(IsActorACountItem, argv.argc() > 1 ? argv[1] : NULL); + PrintFilteredActorList(IsActorACountItem, argv.argc() > 1 ? argv[1] : NULL, false); +} + +CCMD(countitemsnum) // [SP] # of counted items +{ + if (CheckCheatmode ()) return; + + PrintFilteredActorList(IsActorACountItem, argv.argc() > 1 ? argv[1] : NULL, true); } //----------------------------------------------------------------------------- diff --git a/src/c_console.cpp b/src/c_console.cpp index 9a9274aa1..2d164c478 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -159,12 +159,37 @@ static int HistSize; CVAR (Float, con_notifytime, 3.f, CVAR_ARCHIVE) CVAR (Bool, con_centernotify, false, CVAR_ARCHIVE) -CUSTOM_CVAR (Int, con_scaletext, 0, CVAR_ARCHIVE) // Scale notify text at high resolutions? +CUSTOM_CVAR (Int, con_scaletext, 1, CVAR_ARCHIVE) // Scale notify text at high resolutions? { if (self < 0) self = 0; if (self > 3) self = 3; } +CUSTOM_CVAR(Int, con_scale, 0, CVAR_ARCHIVE) +{ + if (self < 0) self = 0; +} + +int active_con_scale() +{ + if (con_scale == 0) + return uiscale; + else + return con_scale; +} + +int active_con_scaletext() +{ + switch (con_scaletext) + { + default: + case 0: return 1; + case 1: return uiscale; + case 2: return 2; + case 3: return 4; + } +} + CUSTOM_CVAR(Float, con_alpha, 0.75f, CVAR_ARCHIVE) { if (self < 0.f) self = 0.f; @@ -493,13 +518,13 @@ void C_AddNotifyString (int printlevel, const char *source) return; } - switch (con_scaletext) + if (active_con_scaletext() == 0) { - default: - case 0: width = DisplayWidth; break; - case 1: width = DisplayWidth / CleanXfac; break; - case 2: width = DisplayWidth / 2; break; - case 3: width = DisplayWidth / 4; break; + width = DisplayWidth / CleanXfac; + } + else + { + width = DisplayWidth / active_con_scaletext(); } if (addtype == APPENDLINE && NotifyStrings[NUMNOTIFIES-1].PrintLevel == printlevel) @@ -612,12 +637,12 @@ int Printf (const char *format, ...) return count; } -int DPrintf (const char *format, ...) +int DPrintf (int level, const char *format, ...) { va_list argptr; int count; - if (developer) + if (developer >= level) { va_start (argptr, format); count = VPrintf (PRINT_HIGH, format, argptr); @@ -721,7 +746,7 @@ static void C_DrawNotifyText () canskip = true; lineadv = SmallFont->GetHeight (); - if (con_scaletext == 1) + if (active_con_scaletext() == 0) { lineadv *= CleanYfac; } @@ -755,7 +780,7 @@ static void C_DrawNotifyText () else color = PrintColors[NotifyStrings[i].PrintLevel]; - if (con_scaletext == 1) + if (active_con_scaletext() == 0) { if (!center) screen->DrawText (SmallFont, color, 0, line, NotifyStrings[i].Text, @@ -766,7 +791,7 @@ static void C_DrawNotifyText () line, NotifyStrings[i].Text, DTA_CleanNoMove, true, DTA_AlphaF, alpha, TAG_DONE); } - else if (con_scaletext == 0) + else if (active_con_scaletext() == 1) { if (!center) screen->DrawText (SmallFont, color, 0, line, NotifyStrings[i].Text, @@ -777,37 +802,20 @@ static void C_DrawNotifyText () line, NotifyStrings[i].Text, DTA_AlphaF, alpha, TAG_DONE); } - else if (con_scaletext == 3) - { - if (!center) - screen->DrawText (SmallFont, color, 0, line, NotifyStrings[i].Text, - DTA_VirtualWidth, screen->GetWidth() / 4, - DTA_VirtualHeight, screen->GetHeight() / 4, - DTA_KeepRatio, true, - DTA_AlphaF, alpha, TAG_DONE); - else - screen->DrawText (SmallFont, color, (screen->GetWidth() / 4 - - SmallFont->StringWidth (NotifyStrings[i].Text))/4, - line, NotifyStrings[i].Text, - DTA_VirtualWidth, screen->GetWidth() / 4, - DTA_VirtualHeight, screen->GetHeight() / 4, - DTA_KeepRatio, true, - DTA_AlphaF, alpha, TAG_DONE); - } else { if (!center) screen->DrawText (SmallFont, color, 0, line, NotifyStrings[i].Text, - DTA_VirtualWidth, screen->GetWidth() / 2, - DTA_VirtualHeight, screen->GetHeight() / 2, + DTA_VirtualWidth, screen->GetWidth() / active_con_scaletext(), + DTA_VirtualHeight, screen->GetHeight() / active_con_scaletext(), DTA_KeepRatio, true, DTA_AlphaF, alpha, TAG_DONE); else - screen->DrawText (SmallFont, color, (screen->GetWidth() / 2 - - SmallFont->StringWidth (NotifyStrings[i].Text))/2, + screen->DrawText (SmallFont, color, (screen->GetWidth() - + SmallFont->StringWidth (NotifyStrings[i].Text) * active_con_scaletext()) / 2 / active_con_scaletext(), line, NotifyStrings[i].Text, - DTA_VirtualWidth, screen->GetWidth() / 2, - DTA_VirtualHeight, screen->GetHeight() / 2, + DTA_VirtualWidth, screen->GetWidth() / active_con_scaletext(), + DTA_VirtualHeight, screen->GetHeight() / active_con_scaletext(), DTA_KeepRatio, true, DTA_AlphaF, alpha, TAG_DONE); } @@ -851,9 +859,13 @@ void C_DrawConsole (bool hw2d) static int oldbottom = 0; int lines, left, offset; + int textScale = active_con_scale(); + if (textScale == 0) + textScale = CleanXfac; + left = LEFTMARGIN; - lines = (ConBottom-ConFont->GetHeight()*2)/ConFont->GetHeight(); - if (-ConFont->GetHeight() + lines*ConFont->GetHeight() > ConBottom - ConFont->GetHeight()*7/2) + lines = (ConBottom/textScale-ConFont->GetHeight()*2)/ConFont->GetHeight(); + if (-ConFont->GetHeight() + lines*ConFont->GetHeight() > ConBottom/textScale - ConFont->GetHeight()*7/2) { offset = -ConFont->GetHeight()/2; lines--; @@ -899,16 +911,26 @@ void C_DrawConsole (bool hw2d) if (ConBottom >= 12) { - screen->DrawText (ConFont, CR_ORANGE, SCREENWIDTH - 8 - - ConFont->StringWidth (GetVersionString()), - ConBottom - ConFont->GetHeight() - 4, - GetVersionString(), TAG_DONE); + if (textScale == 1) + screen->DrawText (ConFont, CR_ORANGE, SCREENWIDTH - 8 - + ConFont->StringWidth (GetVersionString()), + ConBottom / textScale - ConFont->GetHeight() - 4, + GetVersionString(), TAG_DONE); + else + screen->DrawText(ConFont, CR_ORANGE, SCREENWIDTH / textScale - 8 - + ConFont->StringWidth(GetVersionString()), + ConBottom / textScale - ConFont->GetHeight() - 4, + GetVersionString(), + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); + if (TickerMax) { char tickstr[256]; - const int tickerY = ConBottom - ConFont->GetHeight() - 4; + const int tickerY = ConBottom / textScale - ConFont->GetHeight() - 4; size_t i; - int tickend = ConCols - SCREENWIDTH / 90 - 6; + int tickend = ConCols / textScale - SCREENWIDTH / textScale / 90 - 6; int tickbegin = 0; if (TickerLabel) @@ -931,11 +953,23 @@ void C_DrawConsole (bool hw2d) { tickstr[tickend+3] = 0; } - screen->DrawText (ConFont, CR_BROWN, LEFTMARGIN, tickerY, tickstr, TAG_DONE); + if (textScale == 1) + screen->DrawText (ConFont, CR_BROWN, LEFTMARGIN, tickerY, tickstr, TAG_DONE); + else + screen->DrawText (ConFont, CR_BROWN, LEFTMARGIN, tickerY, tickstr, + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); // Draw the marker i = LEFTMARGIN+5+tickbegin*8 + Scale (TickerAt, (SDWORD)(tickend - tickbegin)*8, TickerMax); - screen->DrawChar (ConFont, CR_ORANGE, (int)i, tickerY, 0x13, TAG_DONE); + if (textScale == 1) + screen->DrawChar (ConFont, CR_ORANGE, (int)i, tickerY, 0x13, TAG_DONE); + else + screen->DrawChar(ConFont, CR_ORANGE, (int)i, tickerY, 0x13, + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); TickerVisible = true; } @@ -971,18 +1005,28 @@ void C_DrawConsole (bool hw2d) if (lines > 0) { // No more enqueuing because adding new text to the console won't touch the actual print data. - conbuffer->FormatText(ConFont, ConWidth); + conbuffer->FormatText(ConFont, ConWidth / textScale); unsigned int consolelines = conbuffer->GetFormattedLineCount(); FBrokenLines **blines = conbuffer->GetLines(); FBrokenLines **printline = blines + consolelines - 1 - RowAdjust; - int bottomline = ConBottom - ConFont->GetHeight()*2 - 4; + int bottomline = ConBottom / textScale - ConFont->GetHeight()*2 - 4; ConsoleDrawing = true; for(FBrokenLines **p = printline; p >= blines && lines > 0; p--, lines--) { - screen->DrawText(ConFont, CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(), (*p)->Text, TAG_DONE); + if (textScale == 1) + { + screen->DrawText(ConFont, CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(), (*p)->Text, TAG_DONE); + } + else + { + screen->DrawText(ConFont, CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(), (*p)->Text, + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); + } } ConsoleDrawing = false; @@ -997,21 +1041,52 @@ void C_DrawConsole (bool hw2d) FString command((char *)&CmdLine[2+CmdLine[259]]); int cursorpos = CmdLine[1] - CmdLine[259]; - screen->DrawChar (ConFont, CR_ORANGE, left, bottomline, '\x1c', TAG_DONE); - screen->DrawText (ConFont, CR_ORANGE, left + ConFont->GetCharWidth(0x1c), bottomline, - command, TAG_DONE); - - if (cursoron) + if (textScale == 1) { - screen->DrawChar (ConFont, CR_YELLOW, left + ConFont->GetCharWidth(0x1c) + cursorpos * ConFont->GetCharWidth(0xb), - bottomline, '\xb', TAG_DONE); + screen->DrawChar(ConFont, CR_ORANGE, left, bottomline, '\x1c', TAG_DONE); + screen->DrawText(ConFont, CR_ORANGE, left + ConFont->GetCharWidth(0x1c), bottomline, + command, TAG_DONE); + + if (cursoron) + { + screen->DrawChar(ConFont, CR_YELLOW, left + ConFont->GetCharWidth(0x1c) + cursorpos * ConFont->GetCharWidth(0xb), + bottomline, '\xb', TAG_DONE); + } + } + else + { + screen->DrawChar(ConFont, CR_ORANGE, left, bottomline, '\x1c', + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); + + screen->DrawText(ConFont, CR_ORANGE, left + ConFont->GetCharWidth(0x1c), bottomline, + command, + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); + + if (cursoron) + { + screen->DrawChar(ConFont, CR_YELLOW, left + ConFont->GetCharWidth(0x1c) + cursorpos * ConFont->GetCharWidth(0xb), + bottomline, '\xb', + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); + } } } if (RowAdjust && ConBottom >= ConFont->GetHeight()*7/2) { // Indicate that the view has been scrolled up (10) // and if we can scroll no further (12) - screen->DrawChar (ConFont, CR_GREEN, 0, bottomline, RowAdjust == conbuffer->GetFormattedLineCount() ? 12 : 10, TAG_DONE); + if (textScale == 1) + screen->DrawChar (ConFont, CR_GREEN, 0, bottomline, RowAdjust == conbuffer->GetFormattedLineCount() ? 12 : 10, TAG_DONE); + else + screen->DrawChar(ConFont, CR_GREEN, 0, bottomline, RowAdjust == conbuffer->GetFormattedLineCount() ? 12 : 10, + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); } } } @@ -1672,6 +1747,14 @@ void C_AddTabCommand (const char *name) void C_RemoveTabCommand (const char *name) { + if (TabCommands.Size() == 0) + { + // There are no tab commands that can be removed. + // This is important to skip construction of aname + // in case the NameManager has already been destroyed. + return; + } + FName aname(name, true); if (aname == NAME_None) diff --git a/src/compatibility.cpp b/src/compatibility.cpp index ae3b905d0..24897ac27 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -109,8 +109,8 @@ static FCompatOption Options[] = { "ignoreteleporttags", BCOMPATF_BADTELEPORTERS, SLOT_BCOMPAT }, { "rebuildnodes", BCOMPATF_REBUILDNODES, SLOT_BCOMPAT }, { "linkfrozenprops", BCOMPATF_LINKFROZENPROPS, SLOT_BCOMPAT }, - { "disablepushwindowcheck", BCOMPATF_NOWINDOWCHECK, SLOT_BCOMPAT }, { "floatbob", BCOMPATF_FLOATBOB, SLOT_BCOMPAT }, + { "noslopeid", BCOMPATF_NOSLOPEID, SLOT_BCOMPAT }, // list copied from g_mapinfo.cpp { "shorttex", COMPATF_SHORTTEX, SLOT_COMPAT }, @@ -147,6 +147,8 @@ static FCompatOption Options[] = { "soundcutoff", COMPATF2_SOUNDCUTOFF, SLOT_COMPAT2 }, { "pointonline", COMPATF2_POINTONLINE, SLOT_COMPAT2 }, { "multiexit", COMPATF2_MULTIEXIT, SLOT_COMPAT2 }, + { "teleport", COMPATF2_TELEPORT, SLOT_COMPAT2 }, + { "disablepushwindowcheck", COMPATF2_PUSHWINDOW, SLOT_COMPAT2 }, { NULL, 0, 0 } }; @@ -397,7 +399,7 @@ void CheckCompatibility(MapData *map) flags = BCompatMap.CheckKey(md5); - if (developer) + if (developer >= DMSG_NOTIFY) { Printf("MD5 = "); for (size_t j = 0; j < sizeof(md5.Bytes); ++j) diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index b053d8bd8..3c0994a3b 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -46,6 +46,8 @@ EXTERN_CVAR (Bool, sb_cooperative_enable) EXTERN_CVAR (Bool, sb_deathmatch_enable) EXTERN_CVAR (Bool, sb_teamdeathmatch_enable) +int active_con_scaletext(); + // Public data void CT_Init (); @@ -224,7 +226,7 @@ void CT_Drawer (void) int i, x, scalex, y, promptwidth; y = (viewactive || gamestate != GS_LEVEL) ? -10 : -30; - if (con_scaletext == 1) + if (active_con_scaletext() == 0) { scalex = CleanXfac; y *= CleanYfac; @@ -235,25 +237,17 @@ void CT_Drawer (void) } int screen_width, screen_height, st_y; - switch (con_scaletext) + if (active_con_scaletext() == 0) { - default: - case 0: - case 1: screen_width = SCREENWIDTH; screen_height = SCREENHEIGHT; st_y = ST_Y; - break; - case 2: - screen_width = SCREENWIDTH / 2; - screen_height = SCREENHEIGHT / 2; - st_y = ST_Y / 2; - break; - case 3: - screen_width = SCREENWIDTH / 4; - screen_height = SCREENHEIGHT / 4; - st_y = ST_Y / 4; - break; + } + else + { + screen_width = SCREENWIDTH / active_con_scaletext(); + screen_height = SCREENHEIGHT / active_con_scaletext(); + st_y = ST_Y / active_con_scaletext(); } y += ((SCREENHEIGHT == viewheight && viewactive) || gamestate != GS_LEVEL) ? screen_height : st_y; @@ -280,10 +274,10 @@ void CT_Drawer (void) // draw the prompt, text, and cursor ChatQueue[len] = SmallFont->GetCursor(); ChatQueue[len+1] = '\0'; - if (con_scaletext < 2) + if (active_con_scaletext() < 2) { - screen->DrawText (SmallFont, CR_GREEN, 0, y, prompt, DTA_CleanNoMove, *con_scaletext, TAG_DONE); - screen->DrawText (SmallFont, CR_GREY, promptwidth, y, (char *)(ChatQueue + i), DTA_CleanNoMove, *con_scaletext, TAG_DONE); + screen->DrawText (SmallFont, CR_GREEN, 0, y, prompt, DTA_CleanNoMove, active_con_scaletext() == 0, TAG_DONE); + screen->DrawText (SmallFont, CR_GREY, promptwidth, y, (char *)(ChatQueue + i), DTA_CleanNoMove, active_con_scaletext() == 0, TAG_DONE); } else { diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 29a087fac..dfa4f56f0 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -450,7 +450,7 @@ int FindStyle (const char *namestr) { if (!stricmp(StyleNames[i].Name, namestr)) return StyleNames[i].Num; } - DPrintf("Unknown render style %s\n", namestr); + DPrintf(DMSG_ERROR, "Unknown render style %s\n", namestr); return -1; } @@ -861,7 +861,7 @@ static int PatchThing (int thingy) } else { - DPrintf ("Thing %d\n", thingy); + DPrintf (DMSG_SPAMMY, "Thing %d\n", thingy); if (thingy > 0) { type = InfoNames[thingy - 1]; @@ -915,7 +915,7 @@ static int PatchThing (int thingy) } else if (linelen == 14 && stricmp (Line1, "Missile damage") == 0) { - info->Damage = CreateDamageFunction(val); + info->SetDamage(val); } else if (linelen == 5) { @@ -1086,7 +1086,7 @@ static int PatchThing (int thingy) } if (i == BitNames.Size()) { - DPrintf("Unknown bit mnemonic %s\n", strval); + DPrintf(DMSG_ERROR, "Unknown bit mnemonic %s\n", strval); } } } @@ -1242,7 +1242,7 @@ static int PatchThing (int thingy) else info->renderflags &= ~RF_INVISIBLE; } - DPrintf ("Bits: %d,%d (0x%08x,0x%08x)\n", info->flags.GetValue(), info->flags2.GetValue(), + DPrintf (DMSG_SPAMMY, "Bits: %d,%d (0x%08x,0x%08x)\n", info->flags.GetValue(), info->flags2.GetValue(), info->flags.GetValue(), info->flags2.GetValue()); } else if (stricmp (Line1, "ID #") == 0) @@ -1328,7 +1328,7 @@ static int PatchSound (int soundNum) { int result; - DPrintf ("Sound %d (no longer supported)\n", soundNum); + //DPrintf ("Sound %d (no longer supported)\n", soundNum); /* sfxinfo_t *info, dummy; int offset = 0; @@ -1385,7 +1385,7 @@ static int PatchFrame (int frameNum) info = FindState (frameNum); if (info) { - DPrintf ("Frame %d\n", frameNum); + DPrintf (DMSG_SPAMMY, "Frame %d\n", frameNum); if (frameNum == 47) { // Use original tics for S_DSGUNFLASH1 tics = 5; @@ -1487,7 +1487,7 @@ static int PatchSprite (int sprNum) if ((unsigned)sprNum < OrgSprNames.Size()) { - DPrintf ("Sprite %d\n", sprNum); + DPrintf (DMSG_SPAMMY, "Sprite %d\n", sprNum); } else { @@ -1534,7 +1534,7 @@ static int PatchAmmo (int ammoNum) if (ammoNum >= 0 && ammoNum < 4 && (unsigned)ammoNum <= AmmoNames.Size()) { - DPrintf ("Ammo %d.\n", ammoNum); + DPrintf (DMSG_SPAMMY, "Ammo %d.\n", ammoNum); ammoType = AmmoNames[ammoNum]; if (ammoType != NULL) { @@ -1617,7 +1617,7 @@ static int PatchWeapon (int weapNum) if (type != NULL) { info = (AWeapon *)GetDefaultByType (type); - DPrintf ("Weapon %d\n", weapNum); + DPrintf (DMSG_SPAMMY, "Weapon %d\n", weapNum); } } @@ -1757,7 +1757,7 @@ static int PatchPointer (int ptrNum) { if (CodePConv[ptrNum] == indexnum) break; } - DPrintf("Final ptrNum: %i\n", ptrNum); + DPrintf(DMSG_SPAMMY, "Final ptrNum: %i\n", ptrNum); } // End of hack. @@ -1765,7 +1765,7 @@ static int PatchPointer (int ptrNum) // Better to just use the size of the array rather than a hardcoded value. if (ptrNum >= 0 && (unsigned int) ptrNum < CodePConv.Size()) { - DPrintf ("Pointer %d\n", ptrNum); + DPrintf (DMSG_SPAMMY, "Pointer %d\n", ptrNum); } else { @@ -1789,7 +1789,7 @@ static int PatchPointer (int ptrNum) { SetPointer(state, Actions[index], CodePConv[ptrNum]); } - DPrintf("%s has a hacked state for pointer num %i with index %i\nLine1=%s, Line2=%s\n", + DPrintf(DMSG_SPAMMY, "%s has a hacked state for pointer num %i with index %i\nLine1=%s, Line2=%s\n", state->StaticFindStateOwner(state)->TypeName.GetChars(), ptrNum, index, Line1, Line2); } else @@ -1806,7 +1806,7 @@ static int PatchCheats (int dummy) { int result; - DPrintf ("Cheats (support removed by request)\n"); + DPrintf (DMSG_NOTIFY, "Dehacked cheats support removed by request\n"); while ((result = GetLine ()) == 1) { @@ -1836,7 +1836,7 @@ static int PatchMisc (int dummy) }; int result; - DPrintf ("Misc\n"); + DPrintf (DMSG_SPAMMY, "Misc\n"); while ((result = GetLine()) == 1) { @@ -2017,7 +2017,7 @@ static int PatchPars (int dummy) level_info_t *info; int result, par; - DPrintf ("[Pars]\n"); + DPrintf (DMSG_SPAMMY, "[Pars]\n"); while ( (result = GetLine()) ) { // Argh! .bex doesn't follow the same rules as .deh @@ -2058,7 +2058,7 @@ static int PatchPars (int dummy) } info->partime = par; - DPrintf ("Par for %s changed to %d\n", mapname, par); + DPrintf (DMSG_SPAMMY, "Par for %s changed to %d\n", mapname, par); } return result; } @@ -2067,7 +2067,7 @@ static int PatchCodePtrs (int dummy) { int result; - DPrintf ("[CodePtr]\n"); + DPrintf (DMSG_SPAMMY, "[CodePtr]\n"); while ((result = GetLine()) == 1) { @@ -2132,7 +2132,7 @@ static int PatchMusic (int dummy) { int result; - DPrintf ("[Music]\n"); + DPrintf (DMSG_SPAMMY, "[Music]\n"); while ((result = GetLine()) == 1) { @@ -2142,7 +2142,7 @@ static int PatchMusic (int dummy) keystring << "MUSIC_" << Line1; GStrings.SetString (keystring, newname); - DPrintf ("Music %s set to:\n%s\n", keystring.GetChars(), newname); + DPrintf (DMSG_SPAMMY, "Music %s set to:\n%s\n", keystring.GetChars(), newname); } return result; @@ -2198,7 +2198,7 @@ static int PatchText (int oldSize) goto donewithtext; } - DPrintf ("Searching for text:\n%s\n", oldStr); + DPrintf (DMSG_SPAMMY, "Searching for text:\n%s\n", oldStr); good = false; // Search through sprite names; they are always 4 chars @@ -2264,7 +2264,7 @@ static int PatchText (int oldSize) if (!good) { - DPrintf (" (Unmatched)\n"); + DPrintf (DMSG_SPAMMY, " (Unmatched)\n"); } donewithtext: @@ -2284,7 +2284,7 @@ static int PatchStrings (int dummy) { int result; - DPrintf ("[Strings]\n"); + DPrintf (DMSG_SPAMMY, "[Strings]\n"); while ((result = GetLine()) == 1) { @@ -2310,7 +2310,7 @@ static int PatchStrings (int dummy) const char *ll = Line1; if (!stricmp(ll, "GOTREDSKULL")) ll = "GOTREDSKUL"; GStrings.SetString (ll, holdstring); - DPrintf ("%s set to:\n%s\n", Line1, holdstring.GetChars()); + DPrintf (DMSG_SPAMMY, "%s set to:\n%s\n", Line1, holdstring.GetChars()); } return result; @@ -2350,7 +2350,7 @@ static int DoInclude (int dummy) else { data = Line2; - DPrintf ("Including %s\n", data); + DPrintf (DMSG_SPAMMY, "Including %s\n", data); savepatchname = PatchName; savepatchfile = PatchFile; savepatchpt = PatchPt; @@ -2384,7 +2384,7 @@ static int DoInclude (int dummy) delete[] path; } - DPrintf ("Done with include\n"); + DPrintf (DMSG_SPAMMY, "Done with include\n"); PatchName = savepatchname; PatchFile = savepatchfile; PatchPt = savepatchpt; @@ -2536,7 +2536,7 @@ static bool DoDehPatch() } else { - DPrintf ("Patch does not have DeHackEd signature. Assuming .bex\n"); + DPrintf (DMSG_WARNING, "Patch does not have DeHackEd signature. Assuming .bex\n"); dversion = 19; pversion = 6; PatchPt = PatchFile; @@ -3027,7 +3027,7 @@ void FinishDehPatch () subclass->Replacement = old_replacement; } - DPrintf ("%s replaces %s\n", subclass->TypeName.GetChars(), type->TypeName.GetChars()); + DPrintf (DMSG_NOTIFY, "%s replaces %s\n", subclass->TypeName.GetChars(), type->TypeName.GetChars()); } // Now that all Dehacked patches have been processed, it's okay to free StateMap. diff --git a/src/d_main.cpp b/src/d_main.cpp index d6d53dd77..9f4568614 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -574,7 +574,7 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL) case 4: // Old ZDoom compat mode v = COMPATF_SOUNDTARGET | COMPATF_LIGHT; - w = COMPATF2_MULTIEXIT; + w = COMPATF2_MULTIEXIT | COMPATF2_TELEPORT | COMPATF2_PUSHWINDOW; break; case 5: // MBF compat mode @@ -630,6 +630,8 @@ CVAR (Flag, compat_floormove, compatflags2, COMPATF2_FLOORMOVE); CVAR (Flag, compat_soundcutoff, compatflags2, COMPATF2_SOUNDCUTOFF); CVAR (Flag, compat_pointonline, compatflags2, COMPATF2_POINTONLINE); CVAR (Flag, compat_multiexit, compatflags2, COMPATF2_MULTIEXIT); +CVAR (Flag, compat_teleport, compatflags2, COMPATF2_TELEPORT); +CVAR (Flag, compat_pushwindow, compatflags2, COMPATF2_PUSHWINDOW); //========================================================================== // diff --git a/src/d_net.cpp b/src/d_net.cpp index eadd47d96..261133867 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -231,7 +231,7 @@ static struct TicSpecial specialsize = MAX(specialsize * 2, needed + 30); - DPrintf ("Expanding special size to %zu\n", specialsize); + DPrintf (DMSG_NOTIFY, "Expanding special size to %zu\n", specialsize); for (i = 0; i < BACKUPTICS; i++) streams[i] = (BYTE *)M_Realloc (streams[i], specialsize); diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index a7f07247f..7a593c5fe 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -2134,7 +2134,7 @@ bool PArray::ReadValue(FArchive &ar, void *addr) const } if (i < ElementCount) { - DPrintf("Array on disk (%u) is bigger than in memory (%u)\n", + DPrintf(DMSG_WARNING, "Array on disk (%u) is bigger than in memory (%u)\n", count, ElementCount); for (; i < ElementCount; ++i) { @@ -2501,13 +2501,13 @@ bool PStruct::ReadFields(FArchive &ar, void *addr) const const PSymbol *sym = Symbols.FindSymbol(FName(label, true), true); if (sym == NULL) { - DPrintf("Cannot find field %s in %s\n", + DPrintf(DMSG_ERROR, "Cannot find field %s in %s\n", label, TypeName.GetChars()); SkipValue(ar); } else if (!sym->IsKindOf(RUNTIME_CLASS(PField))) { - DPrintf("Symbol %s in %s is not a field\n", + DPrintf(DMSG_ERROR, "Symbol %s in %s is not a field\n", label, TypeName.GetChars()); SkipValue(ar); } @@ -2806,7 +2806,7 @@ bool PClass::ReadValue(FArchive &ar, void *addr) const } else { - DPrintf("Unknown superclass %s of class %s\n", + DPrintf(DMSG_ERROR, "Unknown superclass %s of class %s\n", type->TypeName.GetChars(), TypeName.GetChars()); SkipValue(ar, VAL_Struct); } @@ -3243,7 +3243,7 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size) { I_Error("%s must inherit from %s but doesn't.", name.GetChars(), type->ParentClass->TypeName.GetChars()); } - DPrintf("Defining placeholder class %s\n", name.GetChars()); + DPrintf(DMSG_SPAMMY, "Defining placeholder class %s\n", name.GetChars()); notnew = true; } else @@ -3327,7 +3327,7 @@ PClass *PClass::FindClassTentative(FName name, bool fatal) return static_cast(found); } PClass *type = static_cast(GetClass()->CreateNew()); - DPrintf("Creating placeholder class %s : %s\n", name.GetChars(), TypeName.GetChars()); + DPrintf(DMSG_SPAMMY, "Creating placeholder class %s : %s\n", name.GetChars(), TypeName.GetChars()); type->TypeName = name; type->ParentClass = this; diff --git a/src/dobjtype.h b/src/dobjtype.h index 7edf8ca6a..d0ccee746 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -18,6 +18,7 @@ typedef std::pair FTypeAndOffset; #define VARF_Method (1<<1) // func has an implied self parameter #define VARF_Action (1<<2) // func has implied owner and state parameters #define VARF_Native (1<<3) // func is native code/don't auto serialize field +#define VARF_ReadOnly (1<<4) // field is read only, do not write to it // Symbol information ------------------------------------------------------- diff --git a/src/doomdef.h b/src/doomdef.h index 56b69fe7c..5a1d1f95f 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -342,6 +342,8 @@ enum : unsigned int COMPATF2_SOUNDCUTOFF = 1 << 2, // Cut off sounds when an actor vanishes instead of making it owner-less COMPATF2_POINTONLINE = 1 << 3, // Use original but buggy P_PointOnLineSide() and P_PointOnDivlineSideCompat() COMPATF2_MULTIEXIT = 1 << 4, // Level exit can be triggered multiple times (required by Daedalus's travel tubes, thanks to a faulty script) + COMPATF2_TELEPORT = 1 << 5, // Don't let indirect teleports trigger sector actions + COMPATF2_PUSHWINDOW = 1 << 6, // Disable the window check in CheckForPushSpecial() }; // Emulate old bugs for select maps. These are not exposed by a cvar @@ -355,8 +357,8 @@ enum BCOMPATF_BADPORTALS = 1 << 4, // Restores the old unstable portal behavior BCOMPATF_REBUILDNODES = 1 << 5, // Force node rebuild BCOMPATF_LINKFROZENPROPS = 1 << 6, // Clearing PROP_TOTALLYFROZEN or PROP_FROZEN also clears the other - BCOMPATF_NOWINDOWCHECK = 1 << 7, // Disable the window check in CheckForPushSpecial() BCOMPATF_FLOATBOB = 1 << 8, // Use Hexen's original method of preventing floatbobbing items from falling down + BCOMPATF_NOSLOPEID = 1 << 9, // disable line IDs on slopes. }; // phares 3/20/98: diff --git a/src/doomstat.cpp b/src/doomstat.cpp index d693aae1b..87ca50a16 100644 --- a/src/doomstat.cpp +++ b/src/doomstat.cpp @@ -39,7 +39,7 @@ FStringTable GStrings; EGameSpeed GameSpeed = SPEED_Normal; // Show developer messages if true. -CVAR (Bool, developer, false, 0) +CVAR (Int, developer, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // [RH] Feature control cvars CVAR (Bool, var_friction, true, CVAR_SERVERINFO); diff --git a/src/doomstat.h b/src/doomstat.h index cfdca5e5a..bbb323c7e 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -204,7 +204,7 @@ extern int bodyqueslot; // ---- [RH] ---- -EXTERN_CVAR (Bool, developer) +EXTERN_CVAR (Int, developer) extern bool ToggleFullscreen; diff --git a/src/doomtype.h b/src/doomtype.h index 9fca870d3..34e4e72e8 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -152,7 +152,7 @@ int Printf (int printlevel, const char *, ...) GCCPRINTF(2,3); int Printf (const char *, ...) GCCPRINTF(1,2); // [RH] Same here: -int DPrintf (const char *, ...) GCCPRINTF(1,2); +int DPrintf (int level, const char *, ...) GCCPRINTF(2,3); extern "C" int mysnprintf(char *buffer, size_t count, const char *format, ...) GCCPRINTF(3,4); extern "C" int myvsnprintf(char *buffer, size_t count, const char *format, va_list argptr) GCCFORMAT(3); @@ -165,15 +165,19 @@ enum PRINT_MEDIUM, // death messages PRINT_HIGH, // critical messages PRINT_CHAT, // chat messages - PRINT_TEAMCHAT // chat messages from a teammate + PRINT_TEAMCHAT, // chat messages from a teammate + PRINT_LOG, // only to logfile + PRINT_BOLD = 200 // What Printf_Bold used +}; + +enum +{ + DMSG_OFF, // no developer messages. + DMSG_ERROR, // general notification messages + DMSG_WARNING, // warnings + DMSG_NOTIFY, // general notification messages + DMSG_SPAMMY, // for those who want to see everything, regardless of its usefulness. }; -#define PRINT_LOW 0 // pickup messages -#define PRINT_MEDIUM 1 // death messages -#define PRINT_HIGH 2 // critical messages -#define PRINT_CHAT 3 // chat messages -#define PRINT_TEAMCHAT 4 // chat messages from a teammate -#define PRINT_LOG 5 // only to logfile -#define PRINT_BOLD 200 // What Printf_Bold used struct PalEntry { diff --git a/src/dthinker.h b/src/dthinker.h index 10a53309d..ac5adc431 100644 --- a/src/dthinker.h +++ b/src/dthinker.h @@ -77,6 +77,11 @@ public: static void RunThinkers (int statnum); static void DestroyAllThinkers (); static void DestroyMostThinkers (); + static void DestroyThinkersInList(int statnum) + { + DestroyThinkersInList(Thinkers[statnum]); + DestroyThinkersInList(FreshThinkers[statnum]); + } static void SerializeAll (FArchive &arc, bool keepPlayers); static void MarkRoots(); diff --git a/src/farchive.cpp b/src/farchive.cpp index 6697fd5b7..de3e248a4 100644 --- a/src/farchive.cpp +++ b/src/farchive.cpp @@ -354,12 +354,12 @@ void FCompressedFile::Implode () // If the data could not be compressed, store it as-is. if (r != Z_OK || outlen >= len) { - DPrintf ("cfile could not be compressed\n"); + DPrintf (DMSG_SPAMMY, "cfile could not be compressed\n"); outlen = 0; } else { - DPrintf ("cfile shrank from %lu to %lu bytes\n", len, outlen); + DPrintf (DMSG_SPAMMY, "cfile shrank from %lu to %lu bytes\n", len, outlen); } } else @@ -717,7 +717,7 @@ void FArchive::Close () { m_File->Close (); m_File = NULL; - DPrintf ("Processed %u objects\n", ArchiveToObject.Size()); + DPrintf (DMSG_SPAMMY, "Processed %u objects\n", ArchiveToObject.Size()); } } @@ -1194,6 +1194,7 @@ FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype) const PClass *type; BYTE playerNum; DWORD index; + DObject *newobj; operator<< (objHead); @@ -1255,11 +1256,11 @@ FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype) case NEW_CLS_OBJ: type = ReadClass (wanttype); // Printf ("New class: %s (%u)\n", type->Name, m_File->Tell()); - obj = type->CreateNew (); + newobj = obj = type->CreateNew (); MapObject (obj); - obj->SerializeUserVars (*this); - obj->Serialize (*this); - obj->CheckIfSerialized (); + newobj->SerializeUserVars (*this); + newobj->Serialize (*this); + newobj->CheckIfSerialized (); break; case NEW_PLYR_OBJ: diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index f02843c89..deb4907a3 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -1452,7 +1452,8 @@ void FParser::SF_SetCamera(void) newcamera->specialf1 = newcamera->Angles.Yaw.Degrees; newcamera->specialf2 = newcamera->Z(); - newcamera->SetZ(t_argc < 3 ? newcamera->Z() + 41 : floatvalue(t_argv[2])); + double z = t_argc < 3 ? newcamera->Sector->floorplane.ZatPoint(newcamera) + 41 : floatvalue(t_argv[2]); + newcamera->SetOrigin(newcamera->PosAtZ(z), false); newcamera->Angles.Yaw = angle; if (t_argc < 4) newcamera->Angles.Pitch = 0.; else newcamera->Angles.Pitch = clamp(floatvalue(t_argv[3]), -50., 50.) * (20. / 32.); diff --git a/src/g_doom/a_bossbrain.cpp b/src/g_doom/a_bossbrain.cpp index 5d10e0138..e6fd5f16d 100644 --- a/src/g_doom/a_bossbrain.cpp +++ b/src/g_doom/a_bossbrain.cpp @@ -47,7 +47,7 @@ static void BrainishExplosion (const DVector3 &pos) boom->SetState (state); } boom->effects = 0; - boom->Damage = NULL; // disables collision detection which is not wanted here + boom->SetDamage(0); // disables collision detection which is not wanted here boom->tics -= pr_brainscream() & 7; if (boom->tics < 1) boom->tics = 1; diff --git a/src/g_heretic/a_ironlich.cpp b/src/g_heretic/a_ironlich.cpp index 7f33fb57a..a88748407 100644 --- a/src/g_heretic/a_ironlich.cpp +++ b/src/g_heretic/a_ironlich.cpp @@ -115,7 +115,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LichAttack) fire->target = baseFire->target; fire->Angles.Yaw = baseFire->Angles.Yaw; fire->Vel = baseFire->Vel; - fire->Damage = NULL; + fire->SetDamage(0); fire->health = (i+1) * 2; P_CheckMissileSpawn (fire, self->radius); } @@ -205,7 +205,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LichFireGrow) self->AddZ(9.); if (self->health == 0) { - self->Damage = self->GetDefault()->Damage; + self->RestoreDamage(); self->SetState (self->FindState("NoGrow")); } return 0; diff --git a/src/g_hexen/a_magelightning.cpp b/src/g_hexen/a_magelightning.cpp index b5718f4d7..896384f4e 100644 --- a/src/g_hexen/a_magelightning.cpp +++ b/src/g_hexen/a_magelightning.cpp @@ -340,7 +340,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LastZap) { mo->SetState (mo->FindState (NAME_Death)); mo->Vel.Z = 40; - mo->Damage = NULL; + mo->SetDamage(0); } return 0; } diff --git a/src/g_level.cpp b/src/g_level.cpp index 2ce03b7da..b1d9c4959 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1295,6 +1295,11 @@ void G_FinishTravel () } bglobal.FinishTravel (); + + // make sure that, after travelling has completed, no travelling thinkers are left. + // Since this list is excluded from regular thinker cleaning, anything that may survive through here + // will endlessly multiply and severely break the following savegames or just simply crash on broken pointers. + DThinker::DestroyThinkersInList(STAT_TRAVELLING); } //========================================================================== diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 78ba9142b..ac1552b99 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1341,6 +1341,8 @@ MapFlagHandlers[] = { "compat_soundcutoff", MITYPE_COMPATFLAG, 0, COMPATF2_SOUNDCUTOFF }, { "compat_pointonline", MITYPE_COMPATFLAG, 0, COMPATF2_POINTONLINE }, { "compat_multiexit", MITYPE_COMPATFLAG, 0, COMPATF2_MULTIEXIT }, + { "compat_teleport", MITYPE_COMPATFLAG, 0, COMPATF2_TELEPORT }, + { "compat_pushwindow", MITYPE_COMPATFLAG, 0, COMPATF2_PUSHWINDOW }, { "cd_start_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end1_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end2_track", MITYPE_EATNEXT, 0, 0 }, diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index c37c1065a..94e92e725 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -825,13 +825,13 @@ void ADecal::BeginPlay () // without effectively doing anything. if (NULL == ShootDecal(tpl, this, Sector, X(), Y(), Z(), Angles.Yaw + 180, 64., true)) { - DPrintf ("Could not find a wall to stick decal to at (%f,%f)\n", X(), Y()); + DPrintf (DMSG_WARNING, "Could not find a wall to stick decal to at (%f,%f)\n", X(), Y()); } } } else { - DPrintf ("Decal actor at (%f,%f) does not have a good template\n", X(), Y()); + DPrintf (DMSG_ERROR, "Decal actor at (%f,%f) does not have a good template\n", X(), Y()); } // This actor doesn't need to stick around anymore. Destroy(); diff --git a/src/g_shared/a_fastprojectile.cpp b/src/g_shared/a_fastprojectile.cpp index 8c7d63b74..c1253f966 100644 --- a/src/g_shared/a_fastprojectile.cpp +++ b/src/g_shared/a_fastprojectile.cpp @@ -166,8 +166,14 @@ void AFastProjectile::Effect() if (trail != NULL) { AActor *act = Spawn (trail, PosAtZ(hitz), ALLOW_REPLACE); - if (act != NULL) + if (act != nullptr) { + if ((flags5 & MF5_GETOWNER) && (target != nullptr)) + act->target = target; + else + act->target = this; + + act->Angles.Pitch = Angles.Pitch; act->Angles.Yaw = Angles.Yaw; } } diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 98af9ff25..57271f768 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -38,7 +38,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp APlayerPawn *actor; actor = p->mo; - if (actor == NULL) + if (actor == nullptr) { return false; } @@ -55,7 +55,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp if ((p->mo->GetClass() == spawntype) && (p->mo->PlayerFlags & PPF_CANSUPERMORPH) && (p->morphTics < (((duration) ? duration : MORPHTICS) - TICRATE)) - && (p->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true) == NULL)) + && (p->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true) == nullptr)) { // Make a super chicken p->mo->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2)); } @@ -65,7 +65,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp { // Dead players cannot morph return false; } - if (spawntype == NULL) + if (spawntype == nullptr) { return false; } @@ -94,7 +94,8 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp } morphed->Angles.Yaw = actor->Angles.Yaw; morphed->target = actor->target; - morphed->tracer = actor; + morphed->tracer = actor->tracer; + morphed->alternative = actor; morphed->FriendPlayer = actor->FriendPlayer; morphed->DesignatedTeam = actor->DesignatedTeam; morphed->Score = actor->Score; @@ -113,7 +114,8 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp morphed->flags2 |= actor->flags2 & MF2_FLY; morphed->flags3 |= actor->flags3 & MF3_GHOST; AActor *eflash = Spawn(((enter_flash) ? enter_flash : RUNTIME_CLASS(ATeleportFog)), actor->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE); - actor->player = NULL; + actor->player = nullptr; + actor->alternative = morphed; actor->flags &= ~(MF_SOLID|MF_SHOOTABLE); actor->flags |= MF_UNMORPHED; actor->renderflags |= RF_INVISIBLE; @@ -129,7 +131,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp p->Vel.X = p->Vel.Y = 0; morphed->ObtainInventory (actor); // Remove all armor - for (item = morphed->Inventory; item != NULL; ) + for (item = morphed->Inventory; item != nullptr; ) { AInventory *next = item->Inventory; if (item->IsKindOf (RUNTIME_CLASS(AArmor))) @@ -182,7 +184,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, // because the level or game is ended while morphed, // by the time it gets executed the morphed player // pawn instance may have already been destroyed. - if (pmo == NULL || pmo->tracer == NULL) + if (pmo == nullptr || pmo->alternative == nullptr) { return false; } @@ -197,7 +199,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, return false; } - mo = barrier_cast(pmo->tracer); + mo = barrier_cast(pmo->alternative); mo->SetOrigin (pmo->Pos(), false); mo->flags |= MF_SOLID; pmo->flags &= ~MF_SOLID; @@ -208,10 +210,14 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, player->morphTics = 2*TICRATE; return false; } - pmo->player = NULL; + // No longer using tracer as morph storage. That is what 'alternative' is for. + // If the tracer has changed on the morph, change the original too. + mo->target = pmo->target; + mo->tracer = pmo->tracer; + pmo->player = nullptr; // Remove the morph power if the morph is being undone prematurely. - for (AInventory *item = pmo->Inventory, *next = NULL; item != NULL; item = next) + for (AInventory *item = pmo->Inventory, *next = nullptr; item != nullptr; item = next) { next = item->Inventory; if (item->IsKindOf(RUNTIME_CLASS(APowerMorph))) @@ -252,10 +258,10 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, player->morphTics = 0; player->MorphedPlayerClass = 0; player->MorphStyle = 0; - player->MorphExitFlash = NULL; + player->MorphExitFlash = nullptr; player->viewheight = mo->ViewHeight; AInventory *level2 = mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true); - if (level2 != NULL) + if (level2 != nullptr) { level2->Destroy (); } @@ -310,31 +316,31 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, } } - AActor *eflash = NULL; - if (exit_flash != NULL) + AActor *eflash = nullptr; + if (exit_flash != nullptr) { eflash = Spawn(exit_flash, pmo->Vec3Angle(20., mo->Angles.Yaw, TELEFOGHEIGHT), ALLOW_REPLACE); if (eflash) eflash->target = mo; } mo->SetupWeaponSlots(); // Use original class's weapon slots. beastweap = player->ReadyWeapon; - if (player->PremorphWeapon != NULL) + if (player->PremorphWeapon != nullptr) { player->PremorphWeapon->PostMorphWeapon (); } else { - player->ReadyWeapon = player->PendingWeapon = NULL; + player->ReadyWeapon = player->PendingWeapon = nullptr; } if (correctweapon) { // Better "lose morphed weapon" semantics PClassActor *morphweapon = PClass::FindActor(pmo->MorphWeapon); - if (morphweapon != NULL && morphweapon->IsDescendantOf(RUNTIME_CLASS(AWeapon))) + if (morphweapon != nullptr && morphweapon->IsDescendantOf(RUNTIME_CLASS(AWeapon))) { AWeapon *OriginalMorphWeapon = static_cast(mo->FindInventory (morphweapon)); - if ((OriginalMorphWeapon != NULL) && (OriginalMorphWeapon->GivenAsMorphWeapon)) + if ((OriginalMorphWeapon != nullptr) && (OriginalMorphWeapon->GivenAsMorphWeapon)) { // You don't get to keep your morphed weapon. - if (OriginalMorphWeapon->SisterWeapon != NULL) + if (OriginalMorphWeapon->SisterWeapon != nullptr) { OriginalMorphWeapon->SisterWeapon->Destroy (); } @@ -344,20 +350,21 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, } else // old behaviour (not really useful now) { // Assumptions made here are no longer valid - if (beastweap != NULL) + if (beastweap != nullptr) { // You don't get to keep your morphed weapon. - if (beastweap->SisterWeapon != NULL) + if (beastweap->SisterWeapon != nullptr) { beastweap->SisterWeapon->Destroy (); } beastweap->Destroy (); } } - pmo->tracer = NULL; + mo->alternative = nullptr; + pmo->alternative = nullptr; pmo->Destroy (); // Restore playerclass armor to its normal amount. AHexenArmor *hxarmor = mo->FindInventory(); - if (hxarmor != NULL) + if (hxarmor != nullptr) { hxarmor->Slots[4] = mo->GetClass()->HexenArmor[0]; } @@ -517,9 +524,9 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor (actor->player->morphTics) && (actor->player->MorphStyle & MORPH_UNDOBYDEATH) && (actor->player->mo) && - (actor->player->mo->tracer)) + (actor->player->mo->alternative)) { - AActor *realme = actor->player->mo->tracer; + AActor *realme = actor->player->mo->alternative; int realstyle = actor->player->MorphStyle; int realhealth = actor->health; if (P_UndoPlayerMorph(actor->player, actor->player, 0, !!(actor->player->MorphStyle & MORPH_UNDOBYDEATHFORCED))) diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 6ad8e6e4a..c72366201 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -572,7 +572,6 @@ bool AInventory::ShouldRespawn () void AInventory::BeginPlay () { Super::BeginPlay (); - ChangeStatNum (STAT_INVENTORY); flags |= MF_DROPPED; // [RH] Items are dropped by default } @@ -822,6 +821,7 @@ void AInventory::BecomeItem () } RemoveFromHash (); flags &= ~MF_SPECIAL; + ChangeStatNum(STAT_INVENTORY); SetState (FindState("Held")); } @@ -848,6 +848,7 @@ void AInventory::BecomePickup () } flags = (GetDefault()->flags | MF_DROPPED) & ~MF_COUNTITEM; renderflags &= ~RF_INVISIBLE; + ChangeStatNum(STAT_DEFAULT); SetState (SpawnState); } diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index 74c416468..63394e02a 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -161,7 +161,7 @@ double DEarthquake::GetModWave(double waveMultiplier) const // //========================================================================== -double DEarthquake::GetModIntensity(double intensity) const +double DEarthquake::GetModIntensity(double intensity, bool fake) const { assert(m_CountdownStart >= m_Countdown); @@ -195,7 +195,7 @@ double DEarthquake::GetModIntensity(double intensity) const } scalar = (scalar > divider) ? divider : scalar; - if (m_Flags & QF_FULLINTENSITY) + if (!fake && (m_Flags & QF_FULLINTENSITY)) { scalar *= 2; } @@ -273,64 +273,67 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger DEarthquake *quake; int count = 0; - while ( (quake = iterator.Next()) != NULL) + while ( (quake = iterator.Next()) != nullptr) { - if (quake->m_Spot != NULL) + if (quake->m_Spot != nullptr) { - double dist = quake->m_Spot->Distance2D (victim, true); + const double dist = quake->m_Spot->Distance2D(victim, true); if (dist < quake->m_TremorRadius) { - const double falloff = quake->GetFalloff(dist); - const double rfalloff = (quake->m_RollIntensity != 0) ? falloff : 0.; ++count; - double x = quake->GetModIntensity(quake->m_Intensity.X); - double y = quake->GetModIntensity(quake->m_Intensity.Y); - double z = quake->GetModIntensity(quake->m_Intensity.Z); - double r = quake->GetModIntensity(quake->m_RollIntensity); + const double falloff = quake->GetFalloff(dist); + const double r = quake->GetModIntensity(quake->m_RollIntensity); + const double strength = quake->GetModIntensity(1.0, true); + DVector3 intensity; + intensity.X = quake->GetModIntensity(quake->m_Intensity.X); + intensity.Y = quake->GetModIntensity(quake->m_Intensity.Y); + intensity.Z = quake->GetModIntensity(quake->m_Intensity.Z); if (!(quake->m_Flags & QF_WAVE)) { - jiggers.Falloff = MAX(falloff, jiggers.Falloff); - jiggers.RFalloff = MAX(rfalloff, jiggers.RFalloff); - jiggers.RollIntensity = MAX(r, jiggers.RollIntensity); + jiggers.RollIntensity = MAX(r, jiggers.RollIntensity) * falloff; + + intensity *= falloff; if (quake->m_Flags & QF_RELATIVE) { - jiggers.RelIntensity.X = MAX(x, jiggers.RelIntensity.X); - jiggers.RelIntensity.Y = MAX(y, jiggers.RelIntensity.Y); - jiggers.RelIntensity.Z = MAX(z, jiggers.RelIntensity.Z); + jiggers.RelIntensity.X = MAX(intensity.X, jiggers.RelIntensity.X); + jiggers.RelIntensity.Y = MAX(intensity.Y, jiggers.RelIntensity.Y); + jiggers.RelIntensity.Z = MAX(intensity.Z, jiggers.RelIntensity.Z); } else { - jiggers.Intensity.X = MAX(x, jiggers.Intensity.X); - jiggers.Intensity.Y = MAX(y, jiggers.Intensity.Y); - jiggers.Intensity.Z = MAX(z, jiggers.Intensity.Z); + jiggers.Intensity.X = MAX(intensity.X, jiggers.Intensity.X); + jiggers.Intensity.Y = MAX(intensity.Y, jiggers.Intensity.Y); + jiggers.Intensity.Z = MAX(intensity.Z, jiggers.Intensity.Z); } } else { - jiggers.WFalloff = MAX(falloff, jiggers.WFalloff); - jiggers.RWFalloff = MAX(rfalloff, jiggers.RWFalloff); - jiggers.RollWave = r * quake->GetModWave(quake->m_RollWave); - double mx = x * quake->GetModWave(quake->m_WaveSpeed.X); - double my = y * quake->GetModWave(quake->m_WaveSpeed.Y); - double mz = z * quake->GetModWave(quake->m_WaveSpeed.Z); + jiggers.RollWave = r * quake->GetModWave(quake->m_RollWave) * falloff * strength; + + + intensity.X *= quake->GetModWave(quake->m_WaveSpeed.X); + intensity.Y *= quake->GetModWave(quake->m_WaveSpeed.Y); + intensity.Z *= quake->GetModWave(quake->m_WaveSpeed.Z); + intensity *= strength * falloff; // [RH] This only gives effect to the last sine quake. I would // prefer if some way was found to make multiples coexist // peacefully, but just summing them together is undesirable // because they could cancel each other out depending on their // relative phases. + + // [MC] Now does so. And they stack rather well. I'm a little + // surprised at how easy it was. + + if (quake->m_Flags & QF_RELATIVE) { - jiggers.RelOffset.X = mx; - jiggers.RelOffset.Y = my; - jiggers.RelOffset.Z = mz; + jiggers.RelOffset += intensity; } else { - jiggers.Offset.X = mx; - jiggers.Offset.Y = my; - jiggers.Offset.Z = mz; + jiggers.Offset += intensity; } } } diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index f873ab0b7..029653bb1 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -153,7 +153,6 @@ struct FQuakeJiggers DVector3 RelIntensity; DVector3 Offset; DVector3 RelOffset; - double Falloff, WFalloff, RFalloff, RWFalloff; double RollIntensity, RollWave; }; @@ -180,8 +179,7 @@ public: int m_Highpoint, m_MiniCount; double m_RollIntensity, m_RollWave; - - double GetModIntensity(double intensity) const; + double GetModIntensity(double intensity, bool fake = false) const; double GetModWave(double waveMultiplier) const; double GetFalloff(double dist) const; diff --git a/src/g_shared/hudmessages.cpp b/src/g_shared/hudmessages.cpp index 9f13d3d9e..32a8d2de5 100644 --- a/src/g_shared/hudmessages.cpp +++ b/src/g_shared/hudmessages.cpp @@ -41,7 +41,8 @@ #include "doomstat.h" #include "farchive.h" -EXTERN_CVAR (Int, con_scaletext) +EXTERN_CVAR(Int, con_scaletext) +int active_con_scaletext(); IMPLEMENT_POINTY_CLASS (DHUDMessage) DECLARE_POINTER(Next) @@ -260,13 +261,10 @@ void DHUDMessage::ResetText (const char *text) } else { - switch (con_scaletext) + switch (active_con_scaletext()) { - default: - case 0: width = SCREENWIDTH; break; - case 1: width = SCREENWIDTH / CleanXfac; break; - case 2: width = SCREENWIDTH / 2; break; - case 3: width = SCREENWIDTH / 4; break; + case 0: width = SCREENWIDTH / CleanXfac; break; + default: width = SCREENWIDTH / active_con_scaletext(); break; } } @@ -332,7 +330,7 @@ void DHUDMessage::Draw (int bottom, int visibility) int screen_width = SCREENWIDTH; int screen_height = SCREENHEIGHT; - if (HUDWidth == 0 && con_scaletext==1) + if (HUDWidth == 0 && active_con_scaletext() == 0) { clean = true; xscale = CleanXfac; @@ -341,17 +339,11 @@ void DHUDMessage::Draw (int bottom, int visibility) else { xscale = yscale = 1; - if (HUDWidth==0 && con_scaletext==2) + if (HUDWidth == 0) { - screen_width/=2; - screen_height/=2; - bottom/=2; - } - else if (HUDWidth==0 && con_scaletext==3) - { - screen_width/=4; - screen_height/=4; - bottom/=4; + screen_width /= active_con_scaletext(); + screen_height /= active_con_scaletext(); + bottom /= active_con_scaletext(); } } @@ -453,7 +445,7 @@ void DHUDMessage::DoDraw (int linenum, int x, int y, bool clean, int hudheight) { if (hudheight == 0) { - if (con_scaletext <= 1) + if (active_con_scaletext() <= 1) { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_CleanNoMove, clean, @@ -461,21 +453,11 @@ void DHUDMessage::DoDraw (int linenum, int x, int y, bool clean, int hudheight) DTA_RenderStyle, Style, TAG_DONE); } - else if (con_scaletext == 3) - { - screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/4, - DTA_VirtualHeight, SCREENHEIGHT/4, - DTA_AlphaF, Alpha, - DTA_RenderStyle, Style, - DTA_KeepRatio, true, - TAG_DONE); - } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/2, - DTA_VirtualHeight, SCREENHEIGHT/2, + DTA_VirtualWidth, SCREENWIDTH / active_con_scaletext(), + DTA_VirtualHeight, SCREENHEIGHT / active_con_scaletext(), DTA_AlphaF, Alpha, DTA_RenderStyle, Style, DTA_KeepRatio, true, @@ -566,7 +548,7 @@ void DHUDMessageFadeOut::DoDraw (int linenum, int x, int y, bool clean, int hudh float trans = float(Alpha * -(Tics - FadeOutTics) / FadeOutTics); if (hudheight == 0) { - if (con_scaletext <= 1) + if (active_con_scaletext() <= 1) { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_CleanNoMove, clean, @@ -574,21 +556,11 @@ void DHUDMessageFadeOut::DoDraw (int linenum, int x, int y, bool clean, int hudh DTA_RenderStyle, Style, TAG_DONE); } - else if (con_scaletext == 3) - { - screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/4, - DTA_VirtualHeight, SCREENHEIGHT/4, - DTA_AlphaF, trans, - DTA_RenderStyle, Style, - DTA_KeepRatio, true, - TAG_DONE); - } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/2, - DTA_VirtualHeight, SCREENHEIGHT/2, + DTA_VirtualWidth, SCREENWIDTH / active_con_scaletext(), + DTA_VirtualHeight, SCREENHEIGHT / active_con_scaletext(), DTA_AlphaF, trans, DTA_RenderStyle, Style, DTA_KeepRatio, true, @@ -676,7 +648,7 @@ void DHUDMessageFadeInOut::DoDraw (int linenum, int x, int y, bool clean, int hu float trans = float(Alpha * Tics / FadeInTics); if (hudheight == 0) { - if (con_scaletext <= 1) + if (active_con_scaletext() <= 1) { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_CleanNoMove, clean, @@ -684,21 +656,11 @@ void DHUDMessageFadeInOut::DoDraw (int linenum, int x, int y, bool clean, int hu DTA_RenderStyle, Style, TAG_DONE); } - else if (con_scaletext == 3) - { - screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/4, - DTA_VirtualHeight, SCREENHEIGHT/4, - DTA_AlphaF, trans, - DTA_RenderStyle, Style, - DTA_KeepRatio, true, - TAG_DONE); - } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/2, - DTA_VirtualHeight, SCREENHEIGHT/2, + DTA_VirtualWidth, SCREENWIDTH / active_con_scaletext(), + DTA_VirtualHeight, SCREENHEIGHT / active_con_scaletext(), DTA_AlphaF, trans, DTA_RenderStyle, Style, DTA_KeepRatio, true, @@ -864,7 +826,7 @@ void DHUDMessageTypeOnFadeOut::DoDraw (int linenum, int x, int y, bool clean, in { if (hudheight == 0) { - if (con_scaletext <= 1) + if (active_con_scaletext() <= 1) { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_CleanNoMove, clean, @@ -873,22 +835,11 @@ void DHUDMessageTypeOnFadeOut::DoDraw (int linenum, int x, int y, bool clean, in DTA_RenderStyle, Style, TAG_DONE); } - else if (con_scaletext == 3) - { - screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/4, - DTA_VirtualHeight, SCREENHEIGHT/4, - DTA_KeepRatio, true, - DTA_TextLen, LineVisible, - DTA_AlphaF, Alpha, - DTA_RenderStyle, Style, - TAG_DONE); - } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/2, - DTA_VirtualHeight, SCREENHEIGHT/2, + DTA_VirtualWidth, SCREENWIDTH / active_con_scaletext(), + DTA_VirtualHeight, SCREENHEIGHT / active_con_scaletext(), DTA_KeepRatio, true, DTA_TextLen, LineVisible, DTA_AlphaF, Alpha, diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index 0136b4f25..42f28173c 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -1013,7 +1013,15 @@ public: void ScreenSizeChanged() { Super::ScreenSizeChanged(); - V_CalcCleanFacs(script->resW, script->resH, SCREENWIDTH, SCREENHEIGHT, &script->cleanX, &script->cleanY); + if (uiscale > 0) + { + script->cleanX = uiscale; + script->cleanY = uiscale; + } + else + { + V_CalcCleanFacs(script->resW, script->resH, SCREENWIDTH, SCREENHEIGHT, &script->cleanX, &script->cleanY); + } } void Draw (EHudState state) diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index b4a011510..b7827f6bd 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -1914,7 +1914,7 @@ class CommandAspectRatio : public SBarInfoCommandFlowControl { SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged); - SetTruth(ratioMap[CheckRatio(screen->GetWidth(), screen->GetHeight())] == ratio, block, statusBar); + SetTruth(ratioMap[FindRatio()] == ratio, block, statusBar); } protected: enum Ratio @@ -1931,6 +1931,37 @@ class CommandAspectRatio : public SBarInfoCommandFlowControl static Ratio ratioMap[5]; Ratio ratio; + + private: + int FindRatio() + { + float aspect = ActiveRatio(screen->GetWidth(), screen->GetHeight()); + + static std::pair ratioTypes[] = + { + { 21 / 9.0f , ASPECTRATIO_16_9 }, + { 16 / 9.0f , ASPECTRATIO_16_9 }, + { 17 / 10.0f , ASPECTRATIO_17_10 }, + { 16 / 10.0f , ASPECTRATIO_16_10 }, + { 4 / 3.0f , ASPECTRATIO_4_3 }, + { 5 / 4.0f , ASPECTRATIO_5_4 }, + { 0.0f, 0 } + }; + + int ratio = ratioTypes[0].second; + float distance = fabs(ratioTypes[0].first - aspect); + for (int i = 1; ratioTypes[i].first != 0.0f; i++) + { + float d = fabs(ratioTypes[i].first - aspect); + if (d < distance) + { + ratio = ratioTypes[i].second; + distance = d; + } + } + + return ratio; + } }; CommandAspectRatio::Ratio CommandAspectRatio::ratioMap[5] = {ASPECTRATIO_4_3,ASPECTRATIO_16_9,ASPECTRATIO_16_10,ASPECTRATIO_16_10,ASPECTRATIO_5_4}; @@ -3464,6 +3495,78 @@ class CommandIfWaterLevel : public SBarInfoNegatableFlowControl //////////////////////////////////////////////////////////////////////////////// +class CommandIfCVarInt : public SBarInfoNegatableFlowControl +{ + public: + CommandIfCVarInt(SBarInfo *script) : SBarInfoNegatableFlowControl(script), + equalcomp(false) + { + } + + void ParseNegatable(FScanner &sc, bool fullScreenOffsets) + { + if(!sc.CheckToken(TK_StringConst)) + { + sc.MustGetToken(TK_Identifier); + } + + cvarname = sc.String; + cvar = FindCVar(cvarname, nullptr); + + if (cvar != nullptr) + { + ECVarType cvartype = cvar->GetRealType(); + + if (cvartype == CVAR_Bool || cvartype == CVAR_Int) + { + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + value = sc.Number; + + if (sc.CheckToken(',')) + { + sc.MustGetToken(TK_Identifier); + + if(sc.Compare("equal")) + { + equalcomp = true; + } + } + } + else + { + sc.ScriptError("Type mismatch: console variable '%s' is not of type 'bool' or 'int'.", cvarname.GetChars()); + } + } + else + { + sc.ScriptError("Unknown console variable '%s'.", cvarname.GetChars()); + } + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged); + + bool result = false; + cvar = GetCVar(statusBar->CPlayer->mo, cvarname); + + if (cvar != nullptr) + { + int cvarvalue = cvar->GetGenericRep(CVAR_Int).Int; + result = equalcomp ? cvarvalue == value : cvarvalue >= value; + } + + SetTruth(result, block, statusBar); + } + protected: + FString cvarname; + FBaseCVar *cvar; + int value; + bool equalcomp; +}; + +//////////////////////////////////////////////////////////////////////////////// + static const char *SBarInfoCommandNames[] = { "drawimage", "drawnumber", "drawswitchableimage", @@ -3474,7 +3577,7 @@ static const char *SBarInfoCommandNames[] = "isselected", "usesammo", "usessecondaryammo", "hasweaponpiece", "inventorybarnotvisible", "weaponammo", "ininventory", "alpha", "ifhealth", - "ifinvulnerable", "ifwaterlevel", + "ifinvulnerable", "ifwaterlevel", "ifcvarint", NULL }; @@ -3488,7 +3591,7 @@ enum SBarInfoCommands SBARINFO_ISSELECTED, SBARINFO_USESAMMO, SBARINFO_USESSECONDARYAMMO, SBARINFO_HASWEAPONPIECE, SBARINFO_INVENTORYBARNOTVISIBLE, SBARINFO_WEAPONAMMO, SBARINFO_ININVENTORY, SBARINFO_ALPHA, SBARINFO_IFHEALTH, - SBARINFO_IFINVULNERABLE, SBARINFO_IFWATERLEVEL, + SBARINFO_IFINVULNERABLE, SBARINFO_IFWATERLEVEL, SBARINFO_IFCVARINT, }; SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc) @@ -3524,6 +3627,7 @@ SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc) case SBARINFO_IFHEALTH: return new CommandIfHealth(script); case SBARINFO_IFINVULNERABLE: return new CommandIfInvulnerable(script); case SBARINFO_IFWATERLEVEL: return new CommandIfWaterLevel(script); + case SBARINFO_IFCVARINT: return new CommandIfCVarInt(script); } sc.ScriptError("Unknown command '%s'.\n", sc.String); diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index c9e87d642..c4d8ec1ed 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -66,7 +66,7 @@ EXTERN_CVAR (Int, screenblocks) EXTERN_CVAR (Bool, am_showtime) EXTERN_CVAR (Bool, am_showtotaltime) -CVAR(Int,hud_althudscale, 2, CVAR_ARCHIVE) // Scale the hud to 640x400? +CVAR(Int,hud_althudscale, 4, CVAR_ARCHIVE) // Scale the hud to 640x400? CVAR(Bool,hud_althud, false, CVAR_ARCHIVE) // Enable/Disable the alternate HUD // These are intentionally not the same as in the automap! @@ -118,7 +118,7 @@ static int hudwidth, hudheight; // current width/height for HUD display static int statspace; DVector2 AM_GetPosition(); - +int active_con_scaletext(); FTextureID GetHUDIcon(PClassInventory *cls) { @@ -886,22 +886,15 @@ static void DrawCoordinates(player_t * CPlayer) } int vwidth, vheight; - switch (con_scaletext) + if (active_con_scaletext() == 0) { - default: - case 0: - vwidth = SCREENWIDTH; - vheight = SCREENWIDTH; - break; - case 1: - case 2: - vwidth = SCREENWIDTH/2; - vheight = SCREENWIDTH/2; - break; - case 3: - vwidth = SCREENWIDTH/4; - vheight = SCREENWIDTH/4; - break; + vwidth = SCREENWIDTH / 2; + vheight = SCREENHEIGHT / 2; + } + else + { + vwidth = SCREENWIDTH / active_con_scaletext(); + vheight = SCREENHEIGHT / active_con_scaletext(); } int xpos = vwidth - SmallFont->StringWidth("X: -00000")-6; @@ -1090,7 +1083,20 @@ void DrawHUD() if (hud_althudscale && SCREENWIDTH>640) { hudwidth=SCREENWIDTH/2; - if (hud_althudscale == 3) + if (hud_althudscale == 4) + { + if (uiscale == 0) + { + hudwidth = CleanWidth; + hudheight = CleanHeight; + } + else + { + hudwidth = SCREENWIDTH / uiscale; + hudheight = SCREENHEIGHT / uiscale; + } + } + else if (hud_althudscale == 3) { hudwidth = SCREENWIDTH / 4; hudheight = SCREENHEIGHT / 4; @@ -1102,13 +1108,13 @@ void DrawHUD() } else { - if (WidescreenRatio == 4) + if (AspectTallerThanWide(WidescreenRatio)) { - hudheight = hudwidth * 30 / BaseRatioSizes[WidescreenRatio][3]; // BaseRatioSizes is inverted for this mode + hudheight = hudwidth * 30 / AspectMultiplier(WidescreenRatio); // BaseRatioSizes is inverted for this mode } else { - hudheight = hudwidth * 30 / (48*48/BaseRatioSizes[WidescreenRatio][3]); + hudheight = hudwidth * 30 / (48*48/AspectMultiplier(WidescreenRatio)); } } } diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index c3b1fd262..f66ec187d 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -74,6 +74,8 @@ EXTERN_CVAR (Bool, am_showtotaltime) EXTERN_CVAR (Bool, noisedebug) EXTERN_CVAR (Int, con_scaletext) +int active_con_scaletext(); + DBaseStatusBar *StatusBar; extern int setblocks; @@ -297,14 +299,15 @@ void DBaseStatusBar::SetScaled (bool scale, bool force) { ST_X = 0; ST_Y = VirticalResolution - RelTop; - if (CheckRatio(SCREENWIDTH, SCREENHEIGHT) != 4) + float aspect = ActiveRatio(SCREENWIDTH, SCREENHEIGHT); + if (!AspectTallerThanWide(aspect)) { // Normal resolution ::ST_Y = Scale (ST_Y, SCREENHEIGHT, VirticalResolution); } else { // 5:4 resolution - ::ST_Y = Scale(ST_Y - VirticalResolution/2, SCREENHEIGHT*3, Scale(VirticalResolution, BaseRatioSizes[4][1], 200)) + SCREENHEIGHT/2 - + (SCREENHEIGHT - SCREENHEIGHT * BaseRatioSizes[4][3] / 48) / 2; + ::ST_Y = Scale(ST_Y - VirticalResolution/2, SCREENHEIGHT*3, Scale(VirticalResolution, AspectBaseHeight(aspect), 200)) + SCREENHEIGHT/2 + + (SCREENHEIGHT - SCREENHEIGHT * AspectMultiplier(aspect) / 48) / 2; } Displacement = 0; } @@ -1033,10 +1036,10 @@ void DBaseStatusBar::DrSmallNumberOuter (int val, int x, int y, bool center) con void DBaseStatusBar::RefreshBackground () const { - int x, x2, y, ratio; + int x, x2, y; - ratio = CheckRatio (SCREENWIDTH, SCREENHEIGHT); - x = (!IsRatioWidescreen(ratio) || !Scaled) ? ST_X : SCREENWIDTH*(48-BaseRatioSizes[ratio][3])/(48*2); + float ratio = ActiveRatio (SCREENWIDTH, SCREENHEIGHT); + x = (ratio < 1.5f || !Scaled) ? ST_X : SCREENWIDTH*(48-AspectMultiplier(ratio))/(48*2); y = x == ST_X && x > 0 ? ST_Y : ::ST_Y; if(!CompleteBorder) @@ -1056,8 +1059,8 @@ void DBaseStatusBar::RefreshBackground () const { if(!CompleteBorder) { - x2 = !IsRatioWidescreen(ratio) || !Scaled ? ST_X+HorizontalResolution : - SCREENWIDTH - (SCREENWIDTH*(48-BaseRatioSizes[ratio][3])+48*2-1)/(48*2); + x2 = ratio < 1.5f || !Scaled ? ST_X+HorizontalResolution : + SCREENWIDTH - (SCREENWIDTH*(48-AspectMultiplier(ratio))+48*2-1)/(48*2); } else { @@ -1240,17 +1243,17 @@ void DBaseStatusBar::Draw (EHudState state) int xpos; int y; - if (con_scaletext == 0) + if (active_con_scaletext() == 1) { vwidth = SCREENWIDTH; vheight = SCREENHEIGHT; xpos = vwidth - 80; y = ::ST_Y - height; } - else if (con_scaletext == 3) + else if (active_con_scaletext() > 1) { - vwidth = SCREENWIDTH/4; - vheight = SCREENHEIGHT/4; + vwidth = SCREENWIDTH / active_con_scaletext(); + vheight = SCREENHEIGHT / active_con_scaletext(); xpos = vwidth - SmallFont->StringWidth("X: -00000")-6; y = ::ST_Y/4 - height; } @@ -1264,9 +1267,9 @@ void DBaseStatusBar::Draw (EHudState state) if (gameinfo.gametype == GAME_Strife) { - if (con_scaletext == 0) + if (active_con_scaletext() == 1) y -= height * 4; - else if (con_scaletext == 3) + else if (active_con_scaletext() > 3) y -= height; else y -= height * 2; @@ -1400,27 +1403,15 @@ void DBaseStatusBar::DrawLog () if (CPlayer->LogText.IsNotEmpty()) { // This uses the same scaling as regular HUD messages - switch (con_scaletext) + if (active_con_scaletext() == 0) { - default: - hudwidth = SCREENWIDTH; - hudheight = SCREENHEIGHT; - break; - - case 1: hudwidth = SCREENWIDTH / CleanXfac; hudheight = SCREENHEIGHT / CleanYfac; - break; - - case 2: - hudwidth = SCREENWIDTH / 2; - hudheight = SCREENHEIGHT / 2; - break; - - case 3: - hudwidth = SCREENWIDTH / 4; - hudheight = SCREENHEIGHT / 4; - break; + } + else + { + hudwidth = SCREENWIDTH / active_con_scaletext(); + hudheight = SCREENHEIGHT / active_con_scaletext(); } int linelen = hudwidth<640? Scale(hudwidth,9,10)-40 : 560; diff --git a/src/g_strife/a_strifeweapons.cpp b/src/g_strife/a_strifeweapons.cpp index f81ca79f7..24e7fb5c7 100644 --- a/src/g_strife/a_strifeweapons.cpp +++ b/src/g_strife/a_strifeweapons.cpp @@ -552,7 +552,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_MaulerTorpedoWave) // If the torpedo hit the ceiling, it should still spawn the wave savedz = self->Z(); - if (wavedef && self->ceilingz < wavedef->Top()) + if (wavedef && self->ceilingz < self->Z() + wavedef->Height) { self->SetZ(self->ceilingz - wavedef->Height); } diff --git a/src/i_net.cpp b/src/i_net.cpp index 5957846d2..e03f50a6b 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -322,7 +322,7 @@ void PacketGet (void) // Don't show the message for disconnect notifications. if (c != 2 || TransmitBuffer[0] != PRE_FAKE || TransmitBuffer[1] != PRE_DISCONNECT) { - DPrintf("Dropped packet: Unknown host (%s:%d)\n", inet_ntoa(fromaddress.sin_addr), fromaddress.sin_port); + DPrintf(DMSG_WARNING, "Dropped packet: Unknown host (%s:%d)\n", inet_ntoa(fromaddress.sin_addr), fromaddress.sin_port); } doomcom.remotenode = -1; return; diff --git a/src/info.cpp b/src/info.cpp index 7198a5de4..6eeca1186 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -344,7 +344,7 @@ size_t PClassActor::PropagateMark() // Mark damage function if (Defaults != NULL) { - GC::Mark(((AActor *)Defaults)->Damage); + GC::Mark(((AActor *)Defaults)->DamageFunc); } // marked += ActorInfo->NumOwnedStates * sizeof(FState); diff --git a/src/menu/videomenu.cpp b/src/menu/videomenu.cpp index 1328e2f7a..b1b4d792f 100644 --- a/src/menu/videomenu.cpp +++ b/src/menu/videomenu.cpp @@ -95,7 +95,7 @@ CUSTOM_CVAR (Int, menu_screenratios, -1, CVAR_ARCHIVE) } else { - BuildModesList (SCREENWIDTH, SCREENHEIGHT, DisplayBits); + BuildModesList (screen->VideoWidth, screen->VideoHeight, DisplayBits); } } @@ -139,7 +139,7 @@ public: DVideoModeMenu() { - SetModesMenu (SCREENWIDTH, SCREENHEIGHT, DisplayBits); + SetModesMenu (screen->VideoWidth, screen->VideoHeight, DisplayBits); } bool MenuEvent(int mkey, bool fromcontroller) @@ -163,13 +163,13 @@ public: { if (!GetSelectedSize (&NewWidth, &NewHeight)) { - NewWidth = SCREENWIDTH; - NewHeight = SCREENHEIGHT; + NewWidth = screen->VideoWidth; + NewHeight = screen->VideoHeight; } else { - OldWidth = SCREENWIDTH; - OldHeight = SCREENHEIGHT; + OldWidth = screen->VideoWidth; + OldHeight = screen->VideoHeight; OldBits = DisplayBits; NewBits = BitTranslate[DummyDepthCvar]; setmodeneeded = true; @@ -297,11 +297,11 @@ void M_RestoreMode () void M_SetDefaultMode () { // Make current resolution the default - vid_defwidth = SCREENWIDTH; - vid_defheight = SCREENHEIGHT; + vid_defwidth = screen->VideoWidth; + vid_defheight = screen->VideoHeight; vid_defbits = DisplayBits; testingmode = 0; - SetModesMenu (SCREENWIDTH, SCREENHEIGHT, DisplayBits); + SetModesMenu (screen->VideoWidth, screen->VideoHeight, DisplayBits); } @@ -314,7 +314,7 @@ void M_SetDefaultMode () void M_RefreshModesList () { - BuildModesList (SCREENWIDTH, SCREENHEIGHT, DisplayBits); + BuildModesList (screen->VideoWidth, screen->VideoHeight, DisplayBits); } void M_InitVideoModesMenu () @@ -385,8 +385,8 @@ void M_SetVideoMode() { if (!GetSelectedSize (&NewWidth, &NewHeight)) { - NewWidth = SCREENWIDTH; - NewHeight = SCREENHEIGHT; + NewWidth = screen->VideoWidth; + NewHeight = screen->VideoHeight; } else { diff --git a/src/name.cpp b/src/name.cpp index 8140829c6..ecedcf0f3 100644 --- a/src/name.cpp +++ b/src/name.cpp @@ -35,6 +35,7 @@ #include #include "name.h" #include "c_dispatch.h" +#include "c_console.h" // MACROS ------------------------------------------------------------------ @@ -268,6 +269,8 @@ FName::NameManager::~NameManager() { NameBlock *block, *next; + C_ClearTabCommands(); + for (block = Blocks; block != NULL; block = next) { next = block->NextBlock; diff --git a/src/namedef.h b/src/namedef.h index cd0d5615b..e5b6c452a 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -654,6 +654,7 @@ xx(DecoCallLineSpecial) xx(DecoNameToClass) xx(DecoFindMultiNameState) xx(DecoFindSingleNameState) +xx(DecoHandleRuntimeState) xx(Damage) // basic type names diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 01953cad9..ffac7a78f 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1331,12 +1331,12 @@ static int CheckInventory (AActor *activator, const char *type, bool max) if (info == NULL) { - Printf ("ACS: I don't know what '%s' is.\n", type); + DPrintf (DMSG_ERROR, "ACS: '%s': Unknown actor class.\n", type); return 0; } else if (!info->IsDescendantOf(RUNTIME_CLASS(AInventory))) { - Printf ("ACS: '%s' is not an inventory item.\n", type); + DPrintf(DMSG_ERROR, "ACS: '%s' is not an inventory item.\n", type); return 0; } @@ -2237,7 +2237,7 @@ bool FBehavior::Init(int lumpnum, FileReader * fr, int len) } } - DPrintf ("Loaded %d scripts, %d functions\n", NumScripts, NumFunctions); + DPrintf (DMSG_NOTIFY, "Loaded %d scripts, %d functions\n", NumScripts, NumFunctions); return true; } @@ -2824,7 +2824,7 @@ void FBehavior::StaticStartTypedScripts (WORD type, AActor *activator, bool alwa "Disconnect", "Return" }; - DPrintf("Starting all scripts of type %d (%s)\n", type, + DPrintf(DMSG_NOTIFY, "Starting all scripts of type %d (%s)\n", type, type < countof(TypeNames) ? TypeNames[type] : TypeNames[SCRIPT_Lightning - 1]); for (unsigned int i = 0; i < StaticModules.Size(); ++i) { @@ -3791,7 +3791,7 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) break; case APROP_Damage: - actor->Damage = CreateDamageFunction(value); + actor->SetDamage(value); break; case APROP_Alpha: @@ -4438,8 +4438,7 @@ enum EACSFunctions ACSF_SpawnParticle, ACSF_SetMusicVolume, ACSF_CheckProximity, - // 1 more left... - + ACSF_CheckActorState, // 99 /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), -101 : PlayerIsSpectator(1), @@ -4450,6 +4449,10 @@ enum EACSFunctions -106 : KickFromGame(2), */ + ACSF_CheckClass = 200, + ACSF_DamageActor, // [arookas] + ACSF_SetActorFlag, + // ZDaemon ACSF_GetTeamScore = 19620, // (int team) ACSF_SetTeamScore, // (int team, int value) @@ -6017,6 +6020,61 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return P_Thing_CheckProximity(actor, classname, distance, count, flags, ptr); } + case ACSF_CheckActorState: + { + actor = SingleActorFromTID(args[0], activator); + const char *statename = FBehavior::StaticLookupString(args[1]); + bool exact = (argCount > 2) ? !!args[2] : false; + if (actor && statename) + { + return (actor->GetClass()->FindStateByString(statename, exact) != nullptr); + } + return false; + } + + case ACSF_CheckClass: + { + const char *clsname = FBehavior::StaticLookupString(args[0]); + return !!PClass::FindActor(clsname); + } + + case ACSF_DamageActor: // [arookas] wrapper around P_DamageMobj + { + // (target, ptr_select1, inflictor, ptr_select2, amount, damagetype) + AActor* target = COPY_AAPTR(SingleActorFromTID(args[0], activator), args[1]); + AActor* inflictor = COPY_AAPTR(SingleActorFromTID(args[2], activator), args[3]); + FName damagetype(FBehavior::StaticLookupString(args[5])); + return P_DamageMobj(target, inflictor, inflictor, args[4], damagetype); + } + + case ACSF_SetActorFlag: + { + int tid = args[0]; + FString flagname = FBehavior::StaticLookupString(args[1]); + bool flagvalue = !!args[2]; + int count = 0; // Return value; number of actors affected + if (tid == 0) + { + if (ModActorFlag(activator, flagname, flagvalue)) + { + ++count; + } + } + else + { + FActorIterator it(tid); + while ((actor = it.Next()) != nullptr) + { + // Don't log errors when affecting many actors because things might share a TID but not share the flag + if (ModActorFlag(actor, flagname, flagvalue, false)) + { + ++count; + } + } + } + return count; + } + default: break; } @@ -6198,7 +6256,7 @@ int DLevelScript::RunScript () activeBehavior = savedActiveBehavior; // fall through case PCD_TERMINATE: - DPrintf ("%s finished\n", ScriptPresentation(script).GetChars()); + DPrintf (DMSG_NOTIFY, "%s finished\n", ScriptPresentation(script).GetChars()); state = SCRIPT_PleaseRemove; break; @@ -7635,7 +7693,7 @@ scriptwait: if (activationline != NULL) { activationline->special = 0; - DPrintf("Cleared line special on line %d\n", (int)(activationline - lines)); + DPrintf(DMSG_SPAMMY, "Cleared line special on line %d\n", (int)(activationline - lines)); } break; @@ -8274,7 +8332,7 @@ scriptwait: line->args[2] = STACK(3); line->args[3] = STACK(2); line->args[4] = STACK(1); - DPrintf("Set special on line %d (id %d) to %d(%d,%d,%d,%d,%d)\n", + DPrintf(DMSG_SPAMMY, "Set special on line %d (id %d) to %d(%d,%d,%d,%d,%d)\n", linenum, STACK(7), specnum, arg0, STACK(4), STACK(3), STACK(2), STACK(1)); } sp -= 7; @@ -9652,7 +9710,7 @@ DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr PutLast(); } - DPrintf("%s started.\n", ScriptPresentation(num).GetChars()); + DPrintf(DMSG_SPAMMY, "%s started.\n", ScriptPresentation(num).GetChars()); } static void SetScriptState (int script, DLevelScript::EScriptState state) @@ -9699,12 +9757,12 @@ void P_DoDeferedScripts () case acsdefered_t::defsuspend: SetScriptState (def->script, DLevelScript::SCRIPT_Suspended); - DPrintf ("Deferred suspend of %s\n", ScriptPresentation(def->script).GetChars()); + DPrintf (DMSG_SPAMMY, "Deferred suspend of %s\n", ScriptPresentation(def->script).GetChars()); break; case acsdefered_t::defterminate: SetScriptState (def->script, DLevelScript::SCRIPT_PleaseRemove); - DPrintf ("Deferred terminate of %s\n", ScriptPresentation(def->script).GetChars()); + DPrintf (DMSG_SPAMMY, "Deferred terminate of %s\n", ScriptPresentation(def->script).GetChars()); break; } delete def; @@ -9740,7 +9798,7 @@ static void addDefered (level_info_t *i, acsdefered_t::EType type, int script, c def->playernum = -1; } i->defered = def; - DPrintf ("%s on map %s deferred\n", ScriptPresentation(script).GetChars(), i->MapName.GetChars()); + DPrintf (DMSG_SPAMMY, "%s on map %s deferred\n", ScriptPresentation(script).GetChars(), i->MapName.GetChars()); } } diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index c635d9743..888b67b6c 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -552,21 +552,31 @@ void P_ActivateInStasisCeiling (int tag) // //============================================================================ -bool EV_CeilingCrushStop (int tag) +bool EV_CeilingCrushStop (int tag, bool remove) { bool rtn = false; DCeiling *scan; TThinkerIterator iterator; - while ( (scan = iterator.Next ()) ) + scan = iterator.Next(); + while (scan != nullptr) { + DCeiling *next = iterator.Next(); if (scan->m_Tag == tag && scan->m_Direction != 0) { - SN_StopSequence (scan->m_Sector, CHAN_CEILING); - scan->m_OldDirection = scan->m_Direction; - scan->m_Direction = 0; // in-stasis; + if (!remove) + { + SN_StopSequence(scan->m_Sector, CHAN_CEILING); + scan->m_OldDirection = scan->m_Direction; + scan->m_Direction = 0; // in-stasis; + } + else + { + scan->Destroy(); + } rtn = true; } + scan = next; } return rtn; diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 64fe2cbab..545603368 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -253,7 +253,7 @@ static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool inc if ((type == 1 && !isbinary) || (type == 2 && isbinary)) { - DPrintf("Incorrect data format for %s.", Wads.GetLumpFullName(lumpnum)); + DPrintf(DMSG_ERROR, "Incorrect data format for %s.", Wads.GetLumpFullName(lumpnum)); return false; } @@ -273,7 +273,7 @@ static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool inc // is exactly 1516 bytes long. if (numnodes % 1516 != 0) { - DPrintf("Incorrect data format for %s.", Wads.GetLumpFullName(lumpnum)); + DPrintf(DMSG_ERROR, "Incorrect data format for %s.", Wads.GetLumpFullName(lumpnum)); return false; } numnodes /= 1516; @@ -283,7 +283,7 @@ static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool inc // And the teaser version has 1488-byte entries. if (numnodes % 1488 != 0) { - DPrintf("Incorrect data format for %s.", Wads.GetLumpFullName(lumpnum)); + DPrintf(DMSG_ERROR, "Incorrect data format for %s.", Wads.GetLumpFullName(lumpnum)); return false; } numnodes /= 1488; diff --git a/src/p_glnodes.cpp b/src/p_glnodes.cpp index 0fe4cd9c0..39c8f8e0d 100644 --- a/src/p_glnodes.cpp +++ b/src/p_glnodes.cpp @@ -1013,7 +1013,7 @@ bool P_CheckNodes(MapData * map, bool rebuilt, int buildtime) subsectors, numsubsectors, vertexes, numvertexes); endTime = I_FPSTime (); - DPrintf ("BSP generation took %.3f sec (%d segs)\n", (endTime - startTime) * 0.001, numsegs); + DPrintf (DMSG_NOTIFY, "BSP generation took %.3f sec (%d segs)\n", (endTime - startTime) * 0.001, numsegs); buildtime = endTime - startTime; } } @@ -1026,12 +1026,12 @@ bool P_CheckNodes(MapData * map, bool rebuilt, int buildtime) #endif if (level.maptype != MAPTYPE_BUILD && gl_cachenodes && buildtime/1000.f >= gl_cachetime) { - DPrintf("Caching nodes\n"); + DPrintf(DMSG_NOTIFY, "Caching nodes\n"); CreateCachedNodes(map); } else { - DPrintf("Not caching nodes (time = %f)\n", buildtime/1000.f); + DPrintf(DMSG_NOTIFY, "Not caching nodes (time = %f)\n", buildtime/1000.f); } } diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 9f50c9ab5..261cfb8f3 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -948,7 +948,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, bool forcedPain = false; int fakeDamage = 0; int holdDamage = 0; - const int rawdamage = damage; + const int rawdamage = damage; + const bool telefragDamage = (rawdamage >= TELEFRAG_DAMAGE); if (damage < 0) damage = 0; @@ -962,7 +963,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, forcedPain = (MustForcePain(target, inflictor)); // Spectral targets only take damage from spectral projectiles. - if (target->flags4 & MF4_SPECTRAL && damage < TELEFRAG_DAMAGE) + if (target->flags4 & MF4_SPECTRAL && !telefragDamage) { if (inflictor == NULL || !(inflictor->flags4 & MF4_SPECTRAL)) { @@ -987,7 +988,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // different here. At any rate, invulnerable is being checked before type factoring, which is then being // checked by player cheats/invul/buddha followed by monster buddha. This is inconsistent. Don't let the // original telefrag damage CHECK (rawdamage) be influenced by outside factors when looking at cheats/invul. - if ((target->flags2 & MF2_INVULNERABLE) && (rawdamage < TELEFRAG_DAMAGE) && (!(flags & DMG_FORCED))) + if ((target->flags2 & MF2_INVULNERABLE) && !telefragDamage && (!(flags & DMG_FORCED))) { // actor is invulnerable if (target->player == NULL) { @@ -1046,7 +1047,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, return -1; } - if ((rawdamage < TELEFRAG_DAMAGE) || (target->flags7 & MF7_LAXTELEFRAGDMG)) // TELEFRAG_DAMAGE may only be reduced with LAXTELEFRAGDMG or it may not guarantee its effect. + if (!telefragDamage || (target->flags7 & MF7_LAXTELEFRAGDMG)) // TELEFRAG_DAMAGE may only be reduced with LAXTELEFRAGDMG or it may not guarantee its effect. { if (player && damage > 1) { @@ -1214,19 +1215,19 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, target->IsTeammate (source)) { //Use the original damage to check for telefrag amount. Don't let the now-amplified damagetypes do it. - if (rawdamage < TELEFRAG_DAMAGE || (target->flags7 & MF7_LAXTELEFRAGDMG)) + if (!telefragDamage || (target->flags7 & MF7_LAXTELEFRAGDMG)) { // Still allow telefragging :-( damage = (int)(damage * level.teamdamage); if (damage < 0) { return damage; - } + } else if (damage == 0) { if (forcedPain) { goto dopain; - } + } else if (fakedPain) { goto fakepain; @@ -1256,7 +1257,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (!(flags & DMG_FORCED)) { // check the real player, not a voodoo doll here for invulnerability effects - if ((rawdamage < TELEFRAG_DAMAGE && ((player->mo->flags2 & MF2_INVULNERABLE) || + if ((!telefragDamage && ((player->mo->flags2 & MF2_INVULNERABLE) || (player->cheats & CF_GODMODE))) || (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NODAMAGE)) //Absolutely no hurting if NODAMAGE is involved. Same for GODMODE2. @@ -1281,7 +1282,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { player->mo->Inventory->AbsorbDamage(damage, mod, newdam); } - if ((rawdamage < TELEFRAG_DAMAGE) || (player->mo->flags7 & MF7_LAXTELEFRAGDMG)) //rawdamage is never modified. + if (!telefragDamage || (player->mo->flags7 & MF7_LAXTELEFRAGDMG)) //rawdamage is never modified. { // if we are telefragging don't let the damage value go below that magic value. Some further checks would fail otherwise. damage = newdam; @@ -1306,7 +1307,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } } - if (damage >= player->health && rawdamage < TELEFRAG_DAMAGE + if (damage >= player->health && !telefragDamage && (G_SkillProperty(SKILLP_AutoUseHealth) || deathmatch) && !player->morphTics) { // Try to use some inventory health @@ -1330,7 +1331,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // but telefragging should still do enough damage to kill the player) // Ignore players that are already dead. // [MC]Buddha2 absorbs telefrag damage, and anything else thrown their way. - if (!(flags & DMG_FORCED) && (((player->cheats & CF_BUDDHA2) || (((player->cheats & CF_BUDDHA) || (player->mo->flags7 & MF7_BUDDHA)) && (rawdamage < TELEFRAG_DAMAGE))) && (player->playerstate != PST_DEAD))) + if (!(flags & DMG_FORCED) && (((player->cheats & CF_BUDDHA2) || (((player->cheats & CF_BUDDHA) || (player->mo->flags7 & MF7_BUDDHA)) && !telefragDamage)) && (player->playerstate != PST_DEAD))) { // If this is a voodoo doll we need to handle the real player as well. player->mo->health = target->health = player->health = 1; @@ -1395,7 +1396,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (target->health <= 0) { //[MC]Buddha flag for monsters. - if (!(flags & DMG_FORCED) && ((target->flags7 & MF7_BUDDHA) && (rawdamage < TELEFRAG_DAMAGE) && ((inflictor == NULL || !(inflictor->flags7 & MF7_FOILBUDDHA)) && !(flags & DMG_FOILBUDDHA)))) + if (!(flags & DMG_FORCED) && ((target->flags7 & MF7_BUDDHA) && !telefragDamage && ((inflictor == NULL || !(inflictor->flags7 & MF7_FOILBUDDHA)) && !(flags & DMG_FOILBUDDHA)))) { //FOILBUDDHA or Telefrag damage must kill it. target->health = 1; } diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index d0a56ee87..c8c7db74d 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -686,9 +686,22 @@ FUNC(LS_Ceiling_LowerAndCrushDist) } FUNC(LS_Ceiling_CrushStop) -// Ceiling_CrushStop (tag) +// Ceiling_CrushStop (tag, remove) { - return EV_CeilingCrushStop (arg0); + bool remove; + switch (arg3) + { + case 1: + remove = false; + break; + case 2: + remove = true; + break; + default: + remove = gameinfo.gametype == GAME_Hexen; + break; + } + return EV_CeilingCrushStop (arg0, remove); } FUNC(LS_Ceiling_CrushRaiseAndStay) @@ -890,9 +903,22 @@ FUNC(LS_Plat_PerpetualRaiseLip) } FUNC(LS_Plat_Stop) -// Plat_Stop (tag) +// Plat_Stop (tag, remove?) { - EV_StopPlat (arg0); + bool remove; + switch (arg3) + { + case 1: + remove = false; + break; + case 2: + remove = true; + break; + default: + remove = gameinfo.gametype == GAME_Hexen; + break; + } + EV_StopPlat(arg0, remove); return true; } diff --git a/src/p_local.h b/src/p_local.h index ebbaeace5..d759828dc 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -392,7 +392,7 @@ enum RADF_NODAMAGE = 8, RADF_THRUSTZ = 16, }; -void P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance, +int P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance, FName damageType, int flags, int fulldamagedistance=0); void P_DelSector_List(); diff --git a/src/p_map.cpp b/src/p_map.cpp index 110ae5235..5cc9bdf72 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -434,7 +434,8 @@ bool P_TeleportMove(AActor* thing, const DVector3 &pos, bool telefrag, bool modi // If this teleport was caused by a move, P_TryMove() will handle the // sector transition messages better than we can here. - if (!(thing->flags6 & MF6_INTRYMOVE)) + // This needs to be compatibility optioned because some older maps exploited this missing feature. + if (!(thing->flags6 & MF6_INTRYMOVE) && !(i_compatflags2 & COMPATF2_TELEPORT)) { thing->CheckSectorTransition(oldsec); } @@ -1319,7 +1320,7 @@ bool PIT_CheckThing(FMultiBlockThingsIterator &it, FMultiBlockThingsIterator::Ch // [RH] What is the point of this check, again? In Hexen, it is unconditional, // but here we only do it if the missile's damage is 0. // MBF bouncer might have a non-0 damage value, but they must not deal damage on impact either. - if ((tm.thing->BounceFlags & BOUNCE_Actors) && (tm.thing->Damage == 0 || !(tm.thing->flags & MF_MISSILE))) + if ((tm.thing->BounceFlags & BOUNCE_Actors) && (tm.thing->IsZeroDamage() || !(tm.thing->flags & MF_MISSILE))) { return (tm.thing->target == thing || !(thing->flags & MF_SOLID)); } @@ -1911,7 +1912,7 @@ static void CheckForPushSpecial(line_t *line, int side, AActor *mobj, DVector2 * { if (line->special && !(mobj->flags6 & MF6_NOTRIGGER)) { - if (posforwindowcheck && !(ib_compatflags & BCOMPATF_NOWINDOWCHECK) && line->backsector != NULL) + if (posforwindowcheck && !(i_compatflags2 & COMPATF2_PUSHWINDOW) && line->backsector != NULL) { // Make sure this line actually blocks us and is not a window // or similar construct we are standing inside of. DVector3 pos = mobj->PosRelative(line); @@ -4438,6 +4439,9 @@ AActor *P_LinePickActor(AActor *t1, DAngle angle, double distance, DAngle pitch, TData.Caller = t1; TData.hitGhosts = true; + TData.MThruSpecies = false; + TData.ThruActors = false; + TData.ThruSpecies = false; if (Trace(t1->PosAtZ(shootz), t1->Sector, direction, distance, actorMask, wallMask, t1, trace, TRACE_NoSky | TRACE_PortalRestrict, CheckForActor, &TData)) @@ -5241,11 +5245,11 @@ CUSTOM_CVAR(Float, splashfactor, 1.f, CVAR_SERVERINFO) // //========================================================================== -void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bombdistance, FName bombmod, +int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bombdistance, FName bombmod, int flags, int fulldamagedistance) { if (bombdistance <= 0) - return; + return 0; fulldamagedistance = clamp(fulldamagedistance, 0, bombdistance - 1); double bombdistancefloat = 1. / (double)(bombdistance - fulldamagedistance); @@ -5260,6 +5264,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo bombsource = bombspot; } + int count = 0; while ((it.Next(&cres))) { AActor *thing = cres.thing; @@ -5355,7 +5360,12 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo int newdam = damage; if (!(flags & RADF_NODAMAGE)) + { + //[MC] Don't count actors saved by buddha if already at 1 health. + int prehealth = thing->health; newdam = P_DamageMobj(thing, bombspot, bombsource, damage, bombmod); + if (thing->health < prehealth) count++; + } else if (thing->player == NULL && (!(flags & RADF_NOIMPACTDAMAGE) && !(thing->flags7 & MF7_DONTTHRUST))) thing->flags2 |= MF2_BLASTED; @@ -5421,12 +5431,16 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo damage = int(damage * factor); if (damage > 0) { + //[MC] Don't count actors saved by buddha if already at 1 health. + int prehealth = thing->health; int newdam = P_DamageMobj(thing, bombspot, bombsource, damage, bombmod); P_TraceBleed(newdam > 0 ? newdam : damage, thing, bombspot); + if (thing->health < prehealth) count++; } } } } + return count; } //========================================================================== diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index 5fa8aa2f2..a3d4d0e9e 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -361,7 +361,7 @@ bool AActor::FixMapthingPos() if (distance < radius) { - DPrintf("%s at (%f,%f) lies on %s line %td, distance = %f\n", + DPrintf(DMSG_NOTIFY, "%s at (%f,%f) lies on %s line %td, distance = %f\n", this->GetClass()->TypeName.GetChars(), X(), Y(), ldef->Delta().X == 0 ? "vertical" : ldef->Delta().Y == 0 ? "horizontal" : "diagonal", ldef - lines, distance); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 8971efe25..d38901f1e 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -139,7 +139,8 @@ IMPLEMENT_POINTY_CLASS (AActor) DECLARE_POINTER (LastHeard) DECLARE_POINTER (master) DECLARE_POINTER (Poisoner) - DECLARE_POINTER (Damage) + DECLARE_POINTER (DamageFunc) + DECLARE_POINTER (alternative) END_POINTERS AActor::~AActor () @@ -148,73 +149,6 @@ AActor::~AActor () // Use Destroy() instead. } -//========================================================================== -// -// CalcDamageValue -// -// Given a script function, returns an integer to represent it in a -// savegame. This encoding is compatible with previous incarnations -// where damage was an integer. -// -// 0 : use null function -// 0x40000000 : use default function -// anything else : use function that returns this number -// -//========================================================================== - -static int CalcDamageValue(VMFunction *func) -{ - if (func == NULL) - { - return 0; - } - VMScriptFunction *sfunc = dyn_cast(func); - if (sfunc == NULL) - { - return 0x40000000; - } - VMOP *op = sfunc->Code; - // If the function was created by CreateDamageFunction(), extract - // the value used to create it and return that. Otherwise, return - // indicating to use the default function. - if (op->op == OP_RETI && op->a == 0) - { - return op->i16; - } - if (op->op == OP_RET && op->a == 0 && op->b == (REGT_INT | REGT_KONST)) - { - return sfunc->KonstD[op->c]; - } - return 0x40000000; -} - -//========================================================================== -// -// UncalcDamageValue -// -// Given a damage integer, returns a script function for it. -// -//========================================================================== - -static VMFunction *UncalcDamageValue(int dmg, VMFunction *def) -{ - if (dmg == 0) - { - return NULL; - } - if ((dmg & 0xC0000000) == 0x40000000) - { - return def; - } - // Does the default version return this? If so, use it. Otherwise, - // create a new function. - if (CalcDamageValue(def) == dmg) - { - return def; - } - return CreateDamageFunction(dmg); -} - //========================================================================== // // AActor :: Serialize @@ -262,18 +196,16 @@ void AActor::Serialize(FArchive &arc) << projectilepassheight << Vel << tics - << state; - if (arc.IsStoring()) + << state + << DamageVal; + if (DamageVal == 0x40000000 || DamageVal == -1) { - int dmg; - dmg = CalcDamageValue(Damage); - arc << dmg; + DamageVal = -1; + DamageFunc = GetDefault()->DamageFunc; } else { - int dmg; - arc << dmg; - Damage = UncalcDamageValue(dmg, GetDefault()->Damage); + DamageFunc = nullptr; } P_SerializeTerrain(arc, floorterrain); arc << projectileKickback @@ -389,6 +321,11 @@ void AActor::Serialize(FArchive &arc) arc << SpriteRotation; } + if (SaveVersion >= 4550) + { + arc << alternative; + } + { FString tagstr; if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag; @@ -1806,6 +1743,11 @@ double P_XYMovement (AActor *mo, DVector2 scroll) mo->Vel.X *= fac; mo->Vel.Y *= fac; } + const double VELOCITY_THRESHOLD = 5000; // don't let it move faster than this. Fixed point overflowed at 32768 but that's too much to make this safe. + if (mo->Vel.LengthSquared() >= VELOCITY_THRESHOLD*VELOCITY_THRESHOLD) + { + mo->Vel.MakeResize(VELOCITY_THRESHOLD); + } move = mo->Vel; // [RH] Carrying sectors didn't work with low speeds in BOOM. This is // because BOOM relied on the speed being fast enough to accumulate @@ -2964,8 +2906,21 @@ CCMD(utid) int AActor::GetMissileDamage (int mask, int add) { - if (Damage == NULL) + if (DamageVal >= 0) { + if (mask == 0) + { + return add * DamageVal; + } + else + { + return ((pr_missiledamage() & mask) + add) * DamageVal; + } + } + if (DamageFunc == nullptr) + { + // This should never happen + assert(false && "No damage function found"); return 0; } VMFrameStack stack; @@ -2977,22 +2932,11 @@ int AActor::GetMissileDamage (int mask, int add) results[0].IntAt(&amount); results[1].IntAt(&calculated); - if (stack.Call(Damage, ¶m, 1, results, 2) < 1) + if (stack.Call(DamageFunc, ¶m, 1, results, 2) < 1) { // No results return 0; } - if (calculated) - { - return amount; - } - else if (mask == 0) - { - return add * amount; - } - else - { - return ((pr_missiledamage() & mask) + add) * amount; - } + return amount; } void AActor::Howl () @@ -3684,7 +3628,7 @@ void AActor::Tick () // still have missiles that go straight up and down through actors without // damaging anything. // (for backwards compatibility this must check for lack of damage function, not for zero damage!) - if ((flags & MF_MISSILE) && Vel.X == 0 && Vel.Y == 0 && Damage != NULL) + if ((flags & MF_MISSILE) && Vel.X == 0 && Vel.Y == 0 && !IsZeroDamage()) { Vel.X = MinVel; } diff --git a/src/p_plats.cpp b/src/p_plats.cpp index 55ba39fe5..96fec71c1 100644 --- a/src/p_plats.cpp +++ b/src/p_plats.cpp @@ -429,15 +429,21 @@ void DPlat::Stop () m_Status = in_stasis; } -void EV_StopPlat (int tag) +void EV_StopPlat (int tag, bool remove) { DPlat *scan; TThinkerIterator iterator; - while ( (scan = iterator.Next ()) ) + scan = iterator.Next(); + while (scan != nullptr) { + DPlat *next = iterator.Next(); if (scan->m_Status != DPlat::in_stasis && scan->m_Tag == tag) - scan->Stop (); + { + if (!remove) scan->Stop(); + else scan->Destroy(); + } + scan = next; } } diff --git a/src/p_scroll.cpp b/src/p_scroll.cpp index b7ed4fc65..52be13d56 100644 --- a/src/p_scroll.cpp +++ b/src/p_scroll.cpp @@ -147,8 +147,8 @@ static void RotationComp(const sector_t *sec, int which, double dx, double dy, d } else { - double ca = an.Cos(); - double sa = an.Sin(); + double ca = -an.Cos(); + double sa = -an.Sin(); tdx = dx*ca - dy*sa; tdy = dy*ca + dx*sa; } @@ -349,8 +349,8 @@ DScroller::DScroller (double dx, double dy, const line_t *l, if (y > x) d = x, x = y, y = d; d = x / g_sin(g_atan2(y, x) + M_PI / 2); - x = (-dy * l->Delta().Y + dx * l->Delta().X) / d; - y = (-dx * l->Delta().Y - dy * l->Delta().Y) / d; + x = -(dy * l->Delta().Y + dx * l->Delta().X) / d; + y = -(dx * l->Delta().Y - dy * l->Delta().X) / d; m_Type = EScroll::sc_side; m_dx = x; diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 8a69c2de2..d8d95d277 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1689,7 +1689,7 @@ static void SetMapThingUserData(AActor *actor, unsigned udi) if (var == NULL || (var->Flags & VARF_Native) || !var->Type->IsKindOf(RUNTIME_CLASS(PBasicType))) { - DPrintf("%s is not a user variable in class %s\n", varname.GetChars(), + DPrintf(DMSG_WARNING, "%s is not a user variable in class %s\n", varname.GetChars(), actor->GetClass()->TypeName.GetChars()); } else @@ -1959,7 +1959,7 @@ void P_SetLineID (int i, line_t *ld) break; case Plane_Align: - setid = ld->args[2]; + if (!(ib_compatflags & BCOMPATF_NOSLOPEID)) setid = ld->args[2]; break; case Static_Init: @@ -2477,7 +2477,7 @@ int P_DetermineTranslucency (int lumpnum) if (newcolor2.r == 255) // if black on white results in white it's either // fully transparent or additive { - if (developer) + if (developer >= DMSG_NOTIFY) { char lumpname[9]; lumpname[8] = 0; @@ -2488,7 +2488,7 @@ int P_DetermineTranslucency (int lumpnum) return -newcolor.r; } - if (developer) + if (developer >= DMSG_NOTIFY) { char lumpname[9]; lumpname[8] = 0; @@ -3028,7 +3028,7 @@ void P_LoadBlockMap (MapData * map) Args->CheckParm("-blockmap") ) { - DPrintf ("Generating BLOCKMAP\n"); + DPrintf (DMSG_SPAMMY, "Generating BLOCKMAP\n"); P_CreateBlockMap (); } else @@ -3060,7 +3060,7 @@ void P_LoadBlockMap (MapData * map) if (!P_VerifyBlockMap(count)) { - DPrintf ("Generating BLOCKMAP\n"); + DPrintf (DMSG_SPAMMY, "Generating BLOCKMAP\n"); P_CreateBlockMap(); } @@ -3957,7 +3957,7 @@ void P_SetupLevel (const char *lumpname, int position) subsectors, numsubsectors, vertexes, numvertexes); endTime = I_FPSTime (); - DPrintf ("BSP generation took %.3f sec (%d segs)\n", (endTime - startTime) * 0.001, numsegs); + DPrintf (DMSG_NOTIFY, "BSP generation took %.3f sec (%d segs)\n", (endTime - startTime) * 0.001, numsegs); oldvertextable = builder.GetOldVertexTable(); reloop = true; } diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 67bb4de73..4c3bdb488 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -186,7 +186,7 @@ bool P_ActivateLine (line_t *line, AActor *mo, int side, int activationType, DVe line->special = 0; } // end of changed code - if (developer && buttonSuccess) + if (developer >= DMSG_SPAMMY && buttonSuccess) { Printf ("Line special %d activated on line %i\n", special, int(line - lines)); } @@ -358,7 +358,7 @@ bool P_PredictLine(line_t *line, AActor *mo, int side, int activationType) special = line->special; // end of changed code - if (developer && buttonSuccess) + if (developer >= DMSG_SPAMMY && buttonSuccess) { Printf("Line special %d predicted on line %i\n", special, int(line - lines)); } diff --git a/src/p_spec.h b/src/p_spec.h index 125b2374e..d7abd4b42 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -212,13 +212,13 @@ private: friend bool EV_DoPlat (int tag, line_t *line, EPlatType type, double height, double speed, int delay, int lip, int change); - friend void EV_StopPlat (int tag); + friend void EV_StopPlat (int tag, bool remove); friend void P_ActivateInStasis (int tag); }; bool EV_DoPlat (int tag, line_t *line, DPlat::EPlatType type, double height, double speed, int delay, int lip, int change); -void EV_StopPlat (int tag); +void EV_StopPlat (int tag, bool remove); void P_ActivateInStasis (int tag); // @@ -434,14 +434,14 @@ private: DCeiling (); friend bool P_CreateCeiling(sector_t *sec, DCeiling::ECeiling type, line_t *line, int tag, double speed, double speed2, double height, int crush, int silent, int change, DCeiling::ECrushMode hexencrush); - friend bool EV_CeilingCrushStop (int tag); + friend bool EV_CeilingCrushStop (int tag, bool remove); friend void P_ActivateInStasisCeiling (int tag); }; bool P_CreateCeiling(sector_t *sec, DCeiling::ECeiling type, line_t *line, int tag, double speed, double speed2, double height, int crush, int silent, int change, DCeiling::ECrushMode hexencrush); bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line, int tag, double speed, double speed2, double height, int crush, int silent, int change, DCeiling::ECrushMode hexencrush = DCeiling::ECrushMode::crushDoom); -bool EV_CeilingCrushStop (int tag); +bool EV_CeilingCrushStop (int tag, bool remove); void P_ActivateInStasisCeiling (int tag); diff --git a/src/p_tick.cpp b/src/p_tick.cpp index 978c2c7ef..332b7af81 100644 --- a/src/p_tick.cpp +++ b/src/p_tick.cpp @@ -42,7 +42,7 @@ extern gamestate_t wipegamestate; // // P_CheckTickerPaused // -// Returns true if the ticker should be paused. In that cause, it also +// Returns true if the ticker should be paused. In that case, it also // pauses sound effects and possibly music. If the ticker should not be // paused, then it returns false but does not unpause anything. // diff --git a/src/p_trace.cpp b/src/p_trace.cpp index 16fff3fbe..b82ac0f5a 100644 --- a/src/p_trace.cpp +++ b/src/p_trace.cpp @@ -303,9 +303,13 @@ void FTraceInfo::Setup3DFloors() { if (Check3DFloorPlane(rover, false)) { - Results->Crossed3DWater = rover; - Results->Crossed3DWaterPos = Results->HitPos; - Results->Distance = 0; + // only consider if the plane is above the actual floor. + if (rover->top.plane->ZatPoint(Results->HitPos) > bf) + { + Results->Crossed3DWater = rover; + Results->Crossed3DWaterPos = Results->HitPos; + Results->Distance = 0; + } } } @@ -766,9 +770,13 @@ bool FTraceInfo::TraceTraverse (int ptflags) { if (Check3DFloorPlane(rover, false)) { - Results->Crossed3DWater = rover; - Results->Crossed3DWaterPos = Results->HitPos; - Results->Distance = 0; + // only consider if the plane is above the actual floor. + if (rover->top.plane->ZatPoint(Results->HitPos) > CurSector->floorplane.ZatPoint(Results->HitPos)) + { + Results->Crossed3DWater = rover; + Results->Crossed3DWaterPos = Results->HitPos; + Results->Distance = 0; + } } } } diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 86116f881..c09debc51 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -147,7 +147,7 @@ extern TArray linemap; void UDMFParserBase::Skip() { - if (developer) sc.ScriptMessage("Ignoring unknown key \"%s\".", sc.String); + if (developer >= DMSG_WARNING) sc.ScriptMessage("Ignoring unknown UDMF key \"%s\".", sc.String); if(sc.CheckToken('{')) { int level = 1; @@ -309,6 +309,11 @@ void FUDMFKeys::Sort() FUDMFKey *FUDMFKeys::Find(FName key) { + if (!mSorted) + { + mSorted = true; + Sort(); + } int min = 0, max = Size()-1; while (min <= max) diff --git a/src/p_user.cpp b/src/p_user.cpp index e4b6ec6bd..b602432b3 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2441,7 +2441,7 @@ void P_PlayerThink (player_t *player) S_ChangeMusic("*"); } } - DPrintf("MUSINFO change for player %d to %d\n", (int)(player - players), player->MUSINFOactor->args[0]); + DPrintf(DMSG_NOTIFY, "MUSINFO change for player %d to %d\n", (int)(player - players), player->MUSINFOactor->args[0]); } } @@ -2828,9 +2828,9 @@ void P_PredictPlayer (player_t *player) DoLerp = (int)PredictionLast.pos.X != (int)player->mo->X() || (int)PredictionLast.pos.Y != (int)player->mo->Y(); // Aditional Debug information - if (developer && DoLerp) + if (developer >= DMSG_NOTIFY && DoLerp) { - DPrintf("Lerp! Ltic (%d) && Ptic (%d) | Lx (%f) && Px (%f) | Ly (%f) && Py (%f)\n", + DPrintf(DMSG_NOTIFY, "Lerp! Ltic (%d) && Ptic (%d) | Lx (%f) && Px (%f) | Ly (%f) && Py (%f)\n", PredictionLast.gametic, i, (PredictionLast.pos.X), (player->mo->X()), (PredictionLast.pos.Y), (player->mo->Y())); diff --git a/src/po_man.cpp b/src/po_man.cpp index 001f308f1..e449e0d67 100644 --- a/src/po_man.cpp +++ b/src/po_man.cpp @@ -1327,6 +1327,17 @@ void FPolyObj::RecalcActorFloorCeil(FBoundingBox bounds) const while ((actor = it.Next()) != NULL) { + // skip everything outside the bounding box. + if (actor->X() + actor->radius <= bounds.Left() || + actor->X() - actor->radius >= bounds.Right() || + actor->Y() + actor->radius <= bounds.Bottom() || + actor->Y() - actor->radius >= bounds.Top()) + { + continue; + } + // Todo: Be a little more thorough with what gets altered here + // because this can dislocate a lot of items that were spawned on + // the lower side of a sector boundary. P_FindFloorCeiling(actor); } } diff --git a/src/portal.cpp b/src/portal.cpp index bf919f09a..d7dad220e 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -294,7 +294,8 @@ static void SetRotation(FLinePortal *port) else { // Linked portals have no angular difference. - port->mSinRot = port->mCosRot = 0.; + port->mSinRot = 0.; + port->mCosRot = 1.; port->mAngleDiff = 0.; } } diff --git a/src/posix/osx/i_specialpaths.mm b/src/posix/osx/i_specialpaths.mm new file mode 100644 index 000000000..843fb82d6 --- /dev/null +++ b/src/posix/osx/i_specialpaths.mm @@ -0,0 +1,187 @@ +/* +** i_specialpaths.mm +** Gets special system folders where data should be stored. (macOS version) +** +**--------------------------------------------------------------------------- +** Copyright 2013-2016 Randy Heit +** Copyright 2016 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include + +#include "cmdlib.h" +#include "m_misc.h" +#include "version.h" // for GAMENAME + +//=========================================================================== +// +// M_GetCachePath macOS +// +// Returns the path for cache GL nodes. +// +//=========================================================================== + +FString M_GetCachePath(bool create) +{ + FString path; + + char pathstr[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kApplicationSupportFolderType, create ? kCreateFolder : 0, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)pathstr, PATH_MAX)) + { + path = pathstr; + } + else + { + path = progdir; + } + path += "/zdoom/cache"; + return path; +} + +//=========================================================================== +// +// M_GetAutoexecPath macOS +// +// Returns the expected location of autoexec.cfg. +// +//=========================================================================== + +FString M_GetAutoexecPath() +{ + FString path; + + char cpath[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + { + path << cpath << "/" GAME_DIR "/autoexec.cfg"; + } + return path; +} + +//=========================================================================== +// +// M_GetCajunPath macOS +// +// Returns the location of the Cajun Bot definitions. +// +//=========================================================================== + +FString M_GetCajunPath(const char *botfilename) +{ + FString path; + + // Just copies the Windows code. Should this be more Mac-specific? + path << progdir << "zcajun/" << botfilename; + if (!FileExists(path)) + { + path = ""; + } + return path; +} + +//=========================================================================== +// +// M_GetConfigPath macOS +// +// Returns the path to the config file. On Windows, this can vary for reading +// vs writing. i.e. If $PROGDIR/zdoom-.ini does not exist, it will try +// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini. +// +//=========================================================================== + +FString M_GetConfigPath(bool for_reading) +{ + char cpath[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kPreferencesFolderType, kCreateFolder, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + { + FString path; + path << cpath << "/" GAMENAMELOWERCASE ".ini"; + return path; + } + // Ungh. + return GAMENAMELOWERCASE ".ini"; +} + +//=========================================================================== +// +// M_GetScreenshotsPath macOS +// +// Returns the path to the default screenshots directory. +// +//=========================================================================== + +FString M_GetScreenshotsPath() +{ + FString path; + char cpath[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + { + path << cpath << "/" GAME_DIR "/Screenshots/"; + } + else + { + path = "~/"; + } + return path; +} + +//=========================================================================== +// +// M_GetSavegamesPath macOS +// +// Returns the path to the default save games directory. +// +//=========================================================================== + +FString M_GetSavegamesPath() +{ + FString path; + char cpath[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + { + path << cpath << "/" GAME_DIR "/Savegames/"; + } + return path; +} + diff --git a/src/posix/sdl/hardware.cpp b/src/posix/sdl/hardware.cpp index 9de4d03a4..221cca44a 100644 --- a/src/posix/sdl/hardware.cpp +++ b/src/posix/sdl/hardware.cpp @@ -243,18 +243,18 @@ void I_SetFPSLimit(int limit) } if (limit == 0) { // no limit - DPrintf("FPS timer disabled\n"); + DPrintf(DMSG_NOTIFY, "FPS timer disabled\n"); } else { FPSLimitTimerEnabled = true; if(timer_create(CLOCK_REALTIME, &FPSLimitEvent, &FPSLimitTimer) == -1) - Printf("Failed to create FPS limitter event\n"); + Printf(DMSG_WARNING, "Failed to create FPS limitter event\n"); itimerspec period = { {0, 0}, {0, 0} }; period.it_value.tv_nsec = period.it_interval.tv_nsec = 1000000000 / limit; if(timer_settime(FPSLimitTimer, 0, &period, NULL) == -1) - Printf("Failed to set FPS limitter timer\n"); - DPrintf("FPS timer set to %u ms\n", (unsigned int) period.it_interval.tv_nsec / 1000000); + Printf(DMSG_WARNING, "Failed to set FPS limitter timer\n"); + DPrintf(DMSG_NOTIFY, "FPS timer set to %u ms\n", (unsigned int) period.it_interval.tv_nsec / 1000000); } } #else diff --git a/src/posix/unix/i_specialpaths.cpp b/src/posix/unix/i_specialpaths.cpp new file mode 100644 index 000000000..5dedba057 --- /dev/null +++ b/src/posix/unix/i_specialpaths.cpp @@ -0,0 +1,200 @@ +/* +** i_specialpaths.cpp +** Gets special system folders where data should be stored. (Unix version) +** +**--------------------------------------------------------------------------- +** Copyright 2013-2016 Randy Heit +** Copyright 2016 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include +#include "i_system.h" +#include "cmdlib.h" + +#include "version.h" // for GAMENAME + + +FString GetUserFile (const char *file) +{ + FString path; + struct stat info; + + path = NicePath("~/" GAME_DIR "/"); + + if (stat (path, &info) == -1) + { + struct stat extrainfo; + + // Sanity check for ~/.config + FString configPath = NicePath("~/.config/"); + if (stat (configPath, &extrainfo) == -1) + { + if (mkdir (configPath, S_IRUSR | S_IWUSR | S_IXUSR) == -1) + { + I_FatalError ("Failed to create ~/.config directory:\n%s", strerror(errno)); + } + } + else if (!S_ISDIR(extrainfo.st_mode)) + { + I_FatalError ("~/.config must be a directory"); + } + + // This can be removed after a release or two + // Transfer the old zdoom directory to the new location + bool moved = false; + FString oldpath = NicePath("~/." GAMENAMELOWERCASE "/"); + if (stat (oldpath, &extrainfo) != -1) + { + if (rename(oldpath, path) == -1) + { + I_Error ("Failed to move old " GAMENAMELOWERCASE " directory (%s) to new location (%s).", + oldpath.GetChars(), path.GetChars()); + } + else + moved = true; + } + + if (!moved && mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) + { + I_FatalError ("Failed to create %s directory:\n%s", + path.GetChars(), strerror (errno)); + } + } + else + { + if (!S_ISDIR(info.st_mode)) + { + I_FatalError ("%s must be a directory", path.GetChars()); + } + } + path += file; + return path; +} + +//=========================================================================== +// +// M_GetCachePath Unix +// +// Returns the path for cache GL nodes. +// +//=========================================================================== + +FString M_GetCachePath(bool create) +{ + // Don't use GAME_DIR and such so that ZDoom and its child ports can + // share the node cache. + FString path = NicePath("~/.config/zdoom/cache"); + if (create) + { + CreatePath(path); + } + return path; +} + +//=========================================================================== +// +// M_GetAutoexecPath Unix +// +// Returns the expected location of autoexec.cfg. +// +//=========================================================================== + +FString M_GetAutoexecPath() +{ + return GetUserFile("autoexec.cfg"); +} + +//=========================================================================== +// +// M_GetCajunPath Unix +// +// Returns the location of the Cajun Bot definitions. +// +//=========================================================================== + +FString M_GetCajunPath(const char *botfilename) +{ + FString path; + + // Check first in ~/.config/zdoom/botfilename. + path = GetUserFile(botfilename); + if (!FileExists(path)) + { + // Then check in SHARE_DIR/botfilename. + path = SHARE_DIR; + path << botfilename; + if (!FileExists(path)) + { + path = ""; + } + } + return path; +} + +//=========================================================================== +// +// M_GetConfigPath Unix +// +// Returns the path to the config file. On Windows, this can vary for reading +// vs writing. i.e. If $PROGDIR/zdoom-.ini does not exist, it will try +// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini. +// +//=========================================================================== + +FString M_GetConfigPath(bool for_reading) +{ + return GetUserFile(GAMENAMELOWERCASE ".ini"); +} + +//=========================================================================== +// +// M_GetScreenshotsPath Unix +// +// Returns the path to the default screenshots directory. +// +//=========================================================================== + +FString M_GetScreenshotsPath() +{ + return NicePath("~/" GAME_DIR "/screenshots/"); +} + +//=========================================================================== +// +// M_GetSavegamesPath Unix +// +// Returns the path to the default save games directory. +// +//=========================================================================== + +FString M_GetSavegamesPath() +{ + return NicePath("~/" GAME_DIR); +} diff --git a/src/r_defs.h b/src/r_defs.h index c0f878664..42fa96156 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -220,6 +220,7 @@ struct FUDMFKey class FUDMFKeys : public TArray { + bool mSorted = false; public: void Sort(); FUDMFKey *Find(FName key); diff --git a/src/r_main.cpp b/src/r_main.cpp index 247a98125..5ff80b101 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -235,7 +235,7 @@ void R_SetVisibility(double vis) else r_WallVisibility = r_BaseVisibility; - r_WallVisibility = (InvZtoScale * SCREENWIDTH*BaseRatioSizes[WidescreenRatio][1] / + r_WallVisibility = (InvZtoScale * SCREENWIDTH*AspectBaseHeight(WidescreenRatio) / (viewwidth*SCREENHEIGHT*3)) * (r_WallVisibility * FocalTangent); // Prevent overflow on floors/ceilings. Note that the calculation of @@ -298,7 +298,7 @@ CCMD (r_visibility) // //========================================================================== -void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, int trueratio) +void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio) { int virtheight, virtwidth, virtwidth2, virtheight2; @@ -321,22 +321,22 @@ void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, virtwidth = virtwidth2 = fullWidth; virtheight = virtheight2 = fullHeight; - if (Is54Aspect(trueratio)) + if (AspectTallerThanWide(trueratio)) { - virtheight2 = virtheight2 * BaseRatioSizes[trueratio][3] / 48; + virtheight2 = virtheight2 * AspectMultiplier(trueratio) / 48; } else { - virtwidth2 = virtwidth2 * BaseRatioSizes[trueratio][3] / 48; + virtwidth2 = virtwidth2 * AspectMultiplier(trueratio) / 48; } - if (Is54Aspect(WidescreenRatio)) + if (AspectTallerThanWide(WidescreenRatio)) { - virtheight = virtheight * BaseRatioSizes[WidescreenRatio][3] / 48; + virtheight = virtheight * AspectMultiplier(WidescreenRatio) / 48; } else { - virtwidth = virtwidth * BaseRatioSizes[WidescreenRatio][3] / 48; + virtwidth = virtwidth * AspectMultiplier(WidescreenRatio) / 48; } BaseYaspectMul = 320.0 * virtheight2 / (r_Yaspect * virtwidth2); @@ -688,7 +688,7 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) ViewPos.X = (x1 + r * dx)*2 - x; ViewPos.Y = (y1 + r * dy)*2 - y; } - ViewAngle = pds->src->Delta().Angle() - startang; + ViewAngle = pds->src->Delta().Angle() * 2 - startang; } else { @@ -968,7 +968,7 @@ void R_RenderViewToCanvas (AActor *actor, DCanvas *canvas, RenderTarget = canvas; bRenderingToCanvas = true; - R_SetWindow (12, width, height, height); + R_SetWindow (12, width, height, height, true); viewwindowx = x; viewwindowy = y; viewactive = true; diff --git a/src/r_renderer.h b/src/r_renderer.h index 6c65fc12f..a39520b49 100644 --- a/src/r_renderer.h +++ b/src/r_renderer.h @@ -55,7 +55,7 @@ struct FRenderer virtual void ErrorCleanup () {} virtual void ClearBuffer(int color) = 0; virtual void Init() = 0; - virtual void SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, int trueratio) {} + virtual void SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio) {} virtual void SetupFrame(player_t *player) {} virtual void CopyStackedViewParameters() {} virtual void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) = 0; diff --git a/src/r_segs.cpp b/src/r_segs.cpp index 2d39a6d97..078f1d921 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -2311,7 +2311,7 @@ void R_CheckDrawSegs () firstdrawseg = drawsegs + firstofs; ds_p = drawsegs + MaxDrawSegs; MaxDrawSegs = newdrawsegs; - DPrintf ("MaxDrawSegs increased to %zu\n", MaxDrawSegs); + DPrintf (DMSG_NOTIFY, "MaxDrawSegs increased to %zu\n", MaxDrawSegs); } } @@ -2330,7 +2330,7 @@ ptrdiff_t R_NewOpening (ptrdiff_t len) maxopenings = maxopenings ? maxopenings*2 : 16384; while ((size_t)lastopening > maxopenings); openings = (short *)M_Realloc (openings, maxopenings * sizeof(*openings)); - DPrintf ("MaxOpenings increased to %zu\n", maxopenings); + DPrintf (DMSG_NOTIFY, "MaxOpenings increased to %zu\n", maxopenings); } return res; } diff --git a/src/r_swrenderer.cpp b/src/r_swrenderer.cpp index c81d2a110..034275101 100644 --- a/src/r_swrenderer.cpp +++ b/src/r_swrenderer.cpp @@ -47,7 +47,7 @@ EXTERN_CVAR(Bool, r_shadercolormaps) class FArchive; -void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, int trueratio); +void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio); void R_SetupColormap(player_t *); void R_SetupFreelook(); void R_InitRenderer(); @@ -299,7 +299,7 @@ void FSoftwareRenderer::ClearBuffer(int color) // //=========================================================================== -void FSoftwareRenderer::SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, int trueratio) +void FSoftwareRenderer::SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio) { R_SWRSetWindow(windowSize, fullWidth, fullHeight, stHeight, trueratio); } diff --git a/src/r_swrenderer.h b/src/r_swrenderer.h index 3e5fed9bc..2856d9586 100644 --- a/src/r_swrenderer.h +++ b/src/r_swrenderer.h @@ -30,7 +30,7 @@ struct FSoftwareRenderer : public FRenderer void ErrorCleanup (); void ClearBuffer(int color); void Init(); - void SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, int trueratio); + void SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio); void SetupFrame(player_t *player); void CopyStackedViewParameters(); void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov); diff --git a/src/r_things.cpp b/src/r_things.cpp index ff5ef48d2..013fc7152 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -223,7 +223,7 @@ vissprite_t *R_NewVisSprite (void) lastvissprite = &vissprites[MaxVisSprites]; firstvissprite = &vissprites[firstvisspritenum]; vissprite_p = &vissprites[prevvisspritenum]; - DPrintf ("MaxVisSprites increased to %d\n", MaxVisSprites); + DPrintf (DMSG_NOTIFY, "MaxVisSprites increased to %d\n", MaxVisSprites); // Allocate sprites from the new pile for (vissprite_t **p = vissprite_p; p < lastvissprite; ++p) @@ -832,13 +832,11 @@ void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor else { // decide which texture to use for the sprite -#ifdef RANGECHECK - if (spritenum >= (signed)sprites.Size () || spritenum < 0) + if ((unsigned)spritenum >= sprites.Size ()) { - DPrintf ("R_ProjectSprite: invalid sprite number %u\n", spritenum); + DPrintf (DMSG_ERROR, "R_ProjectSprite: invalid sprite number %u\n", spritenum); return; } -#endif spritedef_t *sprdef = &sprites[spritenum]; if (thing->frame >= sprdef->numframes) { @@ -1314,13 +1312,13 @@ void R_DrawPSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double // decide which patch to use if ((unsigned)pspr->GetSprite() >= (unsigned)sprites.Size()) { - DPrintf("R_DrawPSprite: invalid sprite number %i\n", pspr->GetSprite()); + DPrintf(DMSG_ERROR, "R_DrawPSprite: invalid sprite number %i\n", pspr->GetSprite()); return; } sprdef = &sprites[pspr->GetSprite()]; if (pspr->GetFrame() >= sprdef->numframes) { - DPrintf("R_DrawPSprite: invalid sprite frame %i : %i\n", pspr->GetSprite(), pspr->GetFrame()); + DPrintf(DMSG_ERROR, "R_DrawPSprite: invalid sprite frame %i : %i\n", pspr->GetSprite(), pspr->GetFrame()); return; } sprframe = &SpriteFrames[sprdef->spriteframes + pspr->GetFrame()]; @@ -1397,7 +1395,7 @@ void R_DrawPSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double } if (pspr->GetID() < PSP_TARGETCENTER) { // Move the weapon down for 1280x1024. - vis->texturemid -= BaseRatioSizes[WidescreenRatio][2]; + vis->texturemid -= AspectPspriteOffset(WidescreenRatio); } vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth : x2; diff --git a/src/r_utility.cpp b/src/r_utility.cpp index efc901ca5..b80f5c793 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -138,7 +138,7 @@ angle_t LocalViewAngle; int LocalViewPitch; bool LocalKeyboardTurner; -int WidescreenRatio; +float WidescreenRatio; int setblocks; int extralight; bool setsizeneeded; @@ -198,9 +198,9 @@ void R_SetViewSize (int blocks) // //========================================================================== -void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight) +void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, bool renderingToCanvas) { - int trueratio; + float trueratio; if (windowSize >= 11) { @@ -220,8 +220,15 @@ void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight) freelookviewheight = ((setblocks*fullHeight)/10)&~7; } - // If the screen is approximately 16:9 or 16:10, consider it widescreen. - WidescreenRatio = CheckRatio (fullWidth, fullHeight, &trueratio); + if (renderingToCanvas) + { + WidescreenRatio = fullWidth / (float)fullHeight; + trueratio = WidescreenRatio; + } + else + { + WidescreenRatio = ActiveRatio(fullWidth, fullHeight, &trueratio); + } DrawFSHUD = (windowSize == 11); @@ -230,13 +237,13 @@ void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight) centery = viewheight/2; centerx = viewwidth/2; - if (Is54Aspect(WidescreenRatio)) + if (AspectTallerThanWide(WidescreenRatio)) { centerxwide = centerx; } else { - centerxwide = centerx * BaseRatioSizes[WidescreenRatio][3] / 48; + centerxwide = centerx * AspectMultiplier(WidescreenRatio) / 48; } @@ -656,7 +663,7 @@ void R_AddInterpolationPoint(const DVector3a &vec) // //========================================================================== -static double QuakePower(double factor, double intensity, double offset, double falloff, double wfalloff) +static double QuakePower(double factor, double intensity, double offset) { double randumb; if (intensity == 0) @@ -667,7 +674,7 @@ static double QuakePower(double factor, double intensity, double offset, double { randumb = pr_torchflicker.GenRand_Real2() * (intensity * 2) - intensity; } - return factor * (wfalloff * offset + falloff * randumb); + return factor * (offset + randumb); } //========================================================================== @@ -797,36 +804,36 @@ void R_SetupFrame (AActor *actor) if (jiggers.RollIntensity != 0 || jiggers.RollWave != 0) { - ViewRoll += QuakePower(quakefactor, jiggers.RollIntensity, jiggers.RollWave, jiggers.RFalloff, jiggers.RWFalloff); + ViewRoll += QuakePower(quakefactor, jiggers.RollIntensity, jiggers.RollWave); } if (jiggers.RelIntensity.X != 0 || jiggers.RelOffset.X != 0) { an = camera->Angles.Yaw; - double power = QuakePower(quakefactor, jiggers.RelIntensity.X, jiggers.RelOffset.X, jiggers.Falloff, jiggers.WFalloff); + double power = QuakePower(quakefactor, jiggers.RelIntensity.X, jiggers.RelOffset.X); ViewPos += an.ToVector(power); } if (jiggers.RelIntensity.Y != 0 || jiggers.RelOffset.Y != 0) { an = camera->Angles.Yaw + 90; - double power = QuakePower(quakefactor, jiggers.RelIntensity.Y, jiggers.RelOffset.Y, jiggers.Falloff, jiggers.WFalloff); + double power = QuakePower(quakefactor, jiggers.RelIntensity.Y, jiggers.RelOffset.Y); ViewPos += an.ToVector(power); } // FIXME: Relative Z is not relative if (jiggers.RelIntensity.Z != 0 || jiggers.RelOffset.Z != 0) { - ViewPos.Z += QuakePower(quakefactor, jiggers.RelIntensity.Z, jiggers.RelOffset.Z, jiggers.Falloff, jiggers.WFalloff); + ViewPos.Z += QuakePower(quakefactor, jiggers.RelIntensity.Z, jiggers.RelOffset.Z); } if (jiggers.Intensity.X != 0 || jiggers.Offset.X != 0) { - ViewPos.X += QuakePower(quakefactor, jiggers.Intensity.X, jiggers.Offset.X, jiggers.Falloff, jiggers.WFalloff); + ViewPos.X += QuakePower(quakefactor, jiggers.Intensity.X, jiggers.Offset.X); } if (jiggers.Intensity.Y != 0 || jiggers.Offset.Y != 0) { - ViewPos.Y += QuakePower(quakefactor, jiggers.Intensity.Y, jiggers.Offset.Y, jiggers.Falloff, jiggers.WFalloff); + ViewPos.Y += QuakePower(quakefactor, jiggers.Intensity.Y, jiggers.Offset.Y); } if (jiggers.Intensity.Z != 0 || jiggers.Offset.Z != 0) { - ViewPos.Z += QuakePower(quakefactor, jiggers.Intensity.Z, jiggers.Offset.Z, jiggers.Falloff, jiggers.WFalloff); + ViewPos.Z += QuakePower(quakefactor, jiggers.Intensity.Z, jiggers.Offset.Z); } } } diff --git a/src/r_utility.h b/src/r_utility.h index 8cabfa600..09a8b5667 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -33,7 +33,7 @@ extern int validcount; extern angle_t LocalViewAngle; // [RH] Added to consoleplayer's angle extern int LocalViewPitch; // [RH] Used directly instead of consoleplayer's pitch extern bool LocalKeyboardTurner; // [RH] The local player used the keyboard to turn, so interpolate -extern int WidescreenRatio; +extern float WidescreenRatio; extern double r_TicFracF; extern DWORD r_FrameTime; @@ -95,7 +95,7 @@ void R_ExecuteSetViewSize (void); // Called by M_Responder. void R_SetViewSize (int blocks); -void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight); +void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, bool renderingToCanvas = false); extern void R_FreePastViewers (); diff --git a/src/resourcefiles/file_wad.cpp b/src/resourcefiles/file_wad.cpp index 0732d4c11..5965a5c8a 100644 --- a/src/resourcefiles/file_wad.cpp +++ b/src/resourcefiles/file_wad.cpp @@ -471,7 +471,7 @@ void FWadFile::SetNamespace(const char *startmarker, const char *endmarker, name { // We can't add this to the flats namespace but // it needs to be flagged for the texture manager. - DPrintf("Marking %s as potential flat\n", Lumps[i].Name); + DPrintf(DMSG_NOTIFY, "Marking %s as potential flat\n", Lumps[i].Name); Lumps[i].Flags |= LUMPF_MAYBEFLAT; } } @@ -517,7 +517,7 @@ void FWadFile::SetNamespace(const char *startmarker, const char *endmarker, name } // we found a marked block - DPrintf("Found %s block at (%d-%d)\n", startmarker, markers[start].index, end); + DPrintf(DMSG_NOTIFY, "Found %s block at (%d-%d)\n", startmarker, markers[start].index, end); for(int j = markers[start].index + 1; j < end; j++) { if (Lumps[j].Namespace != ns_global) @@ -534,7 +534,7 @@ void FWadFile::SetNamespace(const char *startmarker, const char *endmarker, name // ignore sprite lumps smaller than 8 bytes (the smallest possible) // in size -- this was used by some dmadds wads // as an 'empty' graphics resource - DPrintf(" Skipped empty sprite %s (lump %d)\n", Lumps[j].Name, j); + DPrintf(DMSG_WARNING, " Skipped empty sprite %s (lump %d)\n", Lumps[j].Name, j); } else { diff --git a/src/s_playlist.cpp b/src/s_playlist.cpp index ec775e275..e1b0fd5b6 100644 --- a/src/s_playlist.cpp +++ b/src/s_playlist.cpp @@ -182,7 +182,7 @@ int FPlayList::SetPosition (int position) { Position = position; } - DPrintf ("Playlist position set to %d\n", Position); + DPrintf (DMSG_NOTIFY, "Playlist position set to %d\n", Position); return Position; } @@ -197,7 +197,7 @@ int FPlayList::Advance () { Position = 0; } - DPrintf ("Playlist advanced to song %d\n", Position); + DPrintf (DMSG_NOTIFY, "Playlist advanced to song %d\n", Position); return Position; } @@ -207,7 +207,7 @@ int FPlayList::Backup () { Position = Songs.Size() - 1; } - DPrintf ("Playlist backed up to song %d\n", Position); + DPrintf (DMSG_NOTIFY, "Playlist backed up to song %d\n", Position); return Position; } diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 321cbce1b..2149f7814 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -552,7 +552,7 @@ void S_UnloadSound (sfxinfo_t *sfx) GSnd->UnloadSound(sfx->data); sfx->data.Clear(); sfx->data3d.Clear(); - DPrintf("Unloaded sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); + DPrintf(DMSG_NOTIFY, "Unloaded sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); } } @@ -1327,7 +1327,7 @@ sfxinfo_t *S_LoadSound(sfxinfo_t *sfx) { if (S_sfx[i].data.isValid() && S_sfx[i].link == sfxinfo_t::NO_LINK && S_sfx[i].lumpnum == sfx->lumpnum) { - DPrintf ("Linked %s to %s (%d)\n", sfx->name.GetChars(), S_sfx[i].name.GetChars(), i); + DPrintf (DMSG_NOTIFY, "Linked %s to %s (%d)\n", sfx->name.GetChars(), S_sfx[i].name.GetChars(), i); sfx->link = i; // This is necessary to avoid using the rolloff settings of the linked sound if its // settings are different. @@ -1336,7 +1336,7 @@ sfxinfo_t *S_LoadSound(sfxinfo_t *sfx) } } - DPrintf("Loading sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); + DPrintf(DMSG_NOTIFY, "Loading sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); int size = Wads.LumpLength(sfx->lumpnum); if (size > 0) @@ -1396,7 +1396,7 @@ static void S_LoadSound3D(sfxinfo_t *sfx) if(sfx->data3d.isValid()) return; - DPrintf("Loading monoized sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); + DPrintf(DMSG_NOTIFY, "Loading monoized sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); int size = Wads.LumpLength(sfx->lumpnum); if(size <= 0) return; diff --git a/src/sc_man.cpp b/src/sc_man.cpp index da106927e..3f5530ff3 100644 --- a/src/sc_man.cpp +++ b/src/sc_man.cpp @@ -1044,7 +1044,7 @@ void FScriptPosition::Message (int severity, const char *message, ...) const { FString composed; - if ((severity == MSG_DEBUG || severity == MSG_DEBUGLOG) && !developer) return; + if ((severity == MSG_DEBUG || severity == MSG_DEBUGLOG) && developer < DMSG_NOTIFY) return; if (severity == MSG_OPTERROR) { severity = strictdecorate ? MSG_ERROR : MSG_WARNING; diff --git a/src/sound/fmod_wrap.h b/src/sound/fmod_wrap.h index 6734ba85b..02d6a56ae 100644 --- a/src/sound/fmod_wrap.h +++ b/src/sound/fmod_wrap.h @@ -611,5 +611,14 @@ namespace FMOD } #endif + +// FMOD Ex vs FMOD Studio +#if FMOD_VERSION >= 0x00040000 && FMOD_VERSION <= 0x0004FFFF +#define FMOD_STUDIO 0 +#else +#define FMOD_STUDIO 1 +#define FMOD_SOFTWARE 0 +#endif + #endif #endif diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 1088818ce..c130b7e6a 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -122,10 +122,14 @@ CUSTOM_CVAR (Int, snd_streambuffersize, 64, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } #ifndef NO_FMOD -#if FMOD_VERSION < 0x43400 +#if !FMOD_STUDIO && FMOD_VERSION < 0x43400 #define FMOD_OPENSTATE_PLAYING FMOD_OPENSTATE_STREAMING #endif +#if !FMOD_STUDIO +#define setParameterFloat setParameter +#endif + // TYPES ------------------------------------------------------------------- struct FEnumList @@ -183,15 +187,28 @@ static const FEnumList OutputNames[] = { "WASAPI", FMOD_OUTPUTTYPE_WASAPI }, { "ASIO", FMOD_OUTPUTTYPE_ASIO }, +#if FMOD_STUDIO + //Android + + { "OPENSL", FMOD_OUTPUTTYPE_OPENSL }, + { "Android Audio Track", FMOD_OUTPUTTYPE_AUDIOTRACK }, +#endif + // Linux +#if !FMOD_STUDIO { "OSS", FMOD_OUTPUTTYPE_OSS }, +#endif { "ALSA", FMOD_OUTPUTTYPE_ALSA }, +#if !FMOD_STUDIO { "ESD", FMOD_OUTPUTTYPE_ESD }, -#if FMOD_VERSION >= 0x43400 +#endif +#if FMOD_STUDIO || FMOD_VERSION >= 0x43400 { "PulseAudio", FMOD_OUTPUTTYPE_PULSEAUDIO }, { "Pulse", FMOD_OUTPUTTYPE_PULSEAUDIO }, #endif +#if !FMOD_STUDIO { "SDL", 666 }, +#endif // Mac { "Core Audio", FMOD_OUTPUTTYPE_COREAUDIO }, @@ -207,7 +224,7 @@ static const FEnumList SpeakerModeNames[] = { "Surround", FMOD_SPEAKERMODE_SURROUND }, { "5.1", FMOD_SPEAKERMODE_5POINT1 }, { "7.1", FMOD_SPEAKERMODE_7POINT1 }, -#if FMOD_VERSION < 0x44000 +#if !FMOD_STUDIO && FMOD_VERSION < 0x44000 { "Prologic", FMOD_SPEAKERMODE_PROLOGIC }, #endif { "1", FMOD_SPEAKERMODE_MONO }, @@ -222,7 +239,7 @@ static const FEnumList ResamplerNames[] = { "NoInterp", FMOD_DSP_RESAMPLER_NOINTERP }, { "Linear", FMOD_DSP_RESAMPLER_LINEAR }, // [BL] 64-bit version of FMOD Ex 4.26 crashes with these resamplers. -#if !(defined(_M_X64) || defined(__amd64__)) || !(FMOD_VERSION >= 0x42600 && FMOD_VERSION <= 0x426FF) +#if FMOD_STUDIO || !(defined(_M_X64) || defined(__amd64__)) || !(FMOD_VERSION >= 0x42600 && FMOD_VERSION <= 0x426FF) { "Cubic", FMOD_DSP_RESAMPLER_CUBIC }, { "Spline", FMOD_DSP_RESAMPLER_SPLINE }, #endif @@ -237,11 +254,13 @@ static const FEnumList SoundFormatNames[] = { "PCM-24", FMOD_SOUND_FORMAT_PCM24 }, { "PCM-32", FMOD_SOUND_FORMAT_PCM32 }, { "PCM-Float", FMOD_SOUND_FORMAT_PCMFLOAT }, +#if FMOD_STUDIO && FMOD_VERSION < 0x10700 { "GCADPCM", FMOD_SOUND_FORMAT_GCADPCM }, { "IMAADPCM", FMOD_SOUND_FORMAT_IMAADPCM }, { "VAG", FMOD_SOUND_FORMAT_VAG }, { "XMA", FMOD_SOUND_FORMAT_XMA }, { "MPEG", FMOD_SOUND_FORMAT_MPEG }, +#endif { NULL, 0 } }; @@ -356,10 +375,13 @@ public: Stream = stream; // As this interface is for music, make it super-high priority. +#if FMOD_STUDIO + if (FMOD_OK == stream->getDefaults(&frequency, NULL)) + stream->setDefaults(frequency, 1); +#else if (FMOD_OK == stream->getDefaults(&frequency, NULL, NULL, NULL)) - { stream->setDefaults(frequency, 1, 0, 0); - } +#endif } bool Play(bool looping, float volume) @@ -371,21 +393,33 @@ public: looping = false; } Stream->setMode((looping ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF) | FMOD_SOFTWARE | FMOD_2D); +#if FMOD_STUDIO + result = Owner->Sys->playSound(Stream,0, true, &Channel); +#else result = Owner->Sys->playSound(FMOD_CHANNEL_FREE, Stream, true, &Channel); +#endif if (result != FMOD_OK) { return false; } Channel->setChannelGroup(Owner->MusicGroup); +#if FMOD_STUDIO + Channel->setMixLevelsOutput(1, 1, 1, 1, 1, 1, 1, 1); +#else Channel->setSpeakerMix(1, 1, 1, 1, 1, 1, 1, 1); +#endif Channel->setVolume(volume); // Ensure reverb is disabled. +#if FMOD_STUDIO + Channel->setReverbProperties(0,0.f); +#else FMOD_REVERB_CHANNELPROPERTIES reverb = { 0, }; if (FMOD_OK == Channel->getReverbProperties(&reverb)) { reverb.Room = -10000; Channel->setReverbProperties(&reverb); } +#endif Channel->setPaused(false); Ended = false; JustStarted = true; @@ -429,7 +463,7 @@ public: bool is; FMOD_OPENSTATE openstate = FMOD_OPENSTATE_MAX; bool starving; -#if FMOD_VERSION >= 0x43400 +#if FMOD_STUDIO || FMOD_VERSION >= 0x43400 bool diskbusy; #endif @@ -437,7 +471,7 @@ public: { return true; } -#if FMOD_VERSION < 0x43400 +#if !FMOD_STUDIO && FMOD_VERSION < 0x43400 if (FMOD_OK != Stream->getOpenState(&openstate, NULL, &starving)) #else if (FMOD_OK != Stream->getOpenState(&openstate, NULL, &starving, &diskbusy)) @@ -526,7 +560,7 @@ public: unsigned int percentbuffered; unsigned int position; bool starving; -#if FMOD_VERSION >= 0x43400 +#if FMOD_STUDIO || FMOD_VERSION >= 0x43400 bool diskbusy; #endif float volume; @@ -534,7 +568,7 @@ public: bool paused; bool isplaying; -#if FMOD_VERSION < 0x43400 +#if !FMOD_STUDIO && FMOD_VERSION < 0x43400 if (FMOD_OK == Stream->getOpenState(&openstate, &percentbuffered, &starving)) #else if (FMOD_OK == Stream->getOpenState(&openstate, &percentbuffered, &starving, &diskbusy)) @@ -710,7 +744,9 @@ bool FMODSoundRenderer::Init() } const char *wrongver = NULL; -#if FMOD_VERSION >= 0x43600 +#if FMOD_STUDIO + if (version < (FMOD_VERSION & 0xFFFF00)) +#elif FMOD_VERSION >= 0x43600 if (version < 0x43600) #else if (version < 0x42000) @@ -718,7 +754,7 @@ bool FMODSoundRenderer::Init() { wrongver = "an old"; } -#if FMOD_VERSION < 0x42700 +#if !FMOD_STUDIO && FMOD_VERSION < 0x42700 else if ((version & 0xFFFF00) > 0x42600) #else else if ((version & 0xFFFF00) > (FMOD_VERSION & 0xFFFF00)) @@ -744,7 +780,7 @@ bool FMODSoundRenderer::Init() Printf("Loaded FMOD version %x.%02x.%02x\n", version >> 16, (version >> 8) & 255, version & 255); ShowedBanner = true; } -#ifdef _WIN32 +#if defined(_WIN32) && !FMOD_STUDIO if (OSPlatform == os_WinNT4) { // The following was true as of FMOD 3. I don't know if it still @@ -781,7 +817,7 @@ bool FMODSoundRenderer::Init() } #endif -#if !defined _WIN32 && !defined __APPLE__ +#if !defined _WIN32 && !defined __APPLE__ && !FMOD_STUDIO // Try to load SDL output plugin result = Sys->setPluginPath(progdir); // Should we really look for it in the program directory? result = Sys->loadPlugin("liboutput_sdl.so", &OutputPlugin); @@ -812,7 +848,7 @@ bool FMODSoundRenderer::Init() } result = Sys->getNumDrivers(&driver); -#ifdef __unix__ +#if defined(__unix__) && !FMOD_STUDIO if (result == FMOD_OK) { // On Linux, FMOD defaults to OSS. If OSS is not present, it doesn't @@ -852,7 +888,11 @@ bool FMODSoundRenderer::Init() result = Sys->setDriver(driver); } result = Sys->getDriver(&driver); -#if FMOD_VERSION >= 0x43600 +#if FMOD_STUDIO + // We were built with an FMOD Studio that only returns the control panel frequency + result = Sys->getDriverInfo(driver, nullptr, 0, nullptr, &Driver_MinFrequency, &speakermode, nullptr); + Driver_MaxFrequency = Driver_MinFrequency; +#elif FMOD_VERSION >= 0x43600 // We were built with an FMOD that only returns the control panel frequency result = Sys->getDriverCaps(driver, &Driver_Caps, &Driver_MinFrequency, &speakermode); Driver_MaxFrequency = Driver_MinFrequency; @@ -865,7 +905,9 @@ bool FMODSoundRenderer::Init() Printf(TEXTCOLOR_BLUE"Could not ascertain driver capabilities. Some things may be weird. (Error %d)\n", result); // Fill in some default to pretend it worked. (But as long as we specify a valid driver, // can this call actually fail?) +#if !FMOD_STUDIO Driver_Caps = 0; +#endif Driver_MinFrequency = 4000; Driver_MaxFrequency = 48000; speakermode = FMOD_SPEAKERMODE_STEREO; @@ -877,11 +919,13 @@ bool FMODSoundRenderer::Init() { speakermode = FMOD_SPEAKERMODE(eval); } +#if !FMOD_STUDIO result = Sys->setSpeakerMode(speakermode); if (result != FMOD_OK) { Printf(TEXTCOLOR_BLUE"Could not set speaker mode to '%s'. (Error %d)\n", *snd_speakermode, result); } +#endif // Set software format eval = Enum_NumForName(SoundFormatNames, snd_output_format); @@ -902,7 +946,11 @@ bool FMODSoundRenderer::Init() samplerate = snd_samplerate; if (samplerate == 0 || snd_samplerate == 0) { // Creative's ASIO drivers report the only supported frequency as 0! +#if FMOD_STUDIO + if (FMOD_OK != Sys->getSoftwareFormat(&samplerate, NULL, NULL)) +#else if (FMOD_OK != Sys->getSoftwareFormat(&samplerate, NULL, NULL, NULL, NULL, NULL)) +#endif { samplerate = 48000; } @@ -911,12 +959,27 @@ bool FMODSoundRenderer::Init() { Printf(TEXTCOLOR_BLUE"Sample rate %d is unsupported. Trying %d.\n", *snd_samplerate, samplerate); } +#if FMOD_STUDIO + result = Sys->setSoftwareFormat(samplerate, speakermode, 0); +#else result = Sys->setSoftwareFormat(samplerate, format, 0, 0, resampler); +#endif if (result != FMOD_OK) { Printf(TEXTCOLOR_BLUE"Could not set mixing format. Defaults will be used. (Error %d)\n", result); } +#if FMOD_STUDIO + FMOD_ADVANCEDSETTINGS advSettings = {}; + advSettings.cbSize = sizeof advSettings; + advSettings.resamplerMethod = resampler; + result = Sys->setAdvancedSettings(&advSettings); + if (result != FMOD_OK) + { + Printf(TEXTCOLOR_BLUE"Could not set resampler method. Defaults will be used. (Error %d)\n", result); + } +#endif + // Set software channels according to snd_channels result = Sys->setSoftwareChannels(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS); if (result != FMOD_OK) @@ -924,6 +987,7 @@ bool FMODSoundRenderer::Init() Printf(TEXTCOLOR_BLUE"Failed to set the preferred number of channels. (Error %d)\n", result); } +#if !FMOD_STUDIO if (Driver_Caps & FMOD_CAPS_HARDWARE_EMULATED) { // The user has the 'Acceleration' slider set to off! // This is really bad for latency! @@ -931,7 +995,9 @@ bool FMODSoundRenderer::Init() Printf (TEXTCOLOR_BLUE"Please turn it back on if you want decent sound.\n"); result = Sys->setDSPBufferSize(1024, 10); // At 48khz, the latency between issuing an fmod command and hearing it will now be about 213ms. } - else if (snd_buffersize != 0 || snd_buffercount != 0) + else +#endif + if (snd_buffersize != 0 || snd_buffercount != 0) { int buffersize = snd_buffersize ? snd_buffersize : 1024; int buffercount = snd_buffercount ? snd_buffercount : 4; @@ -951,7 +1017,9 @@ bool FMODSoundRenderer::Init() if (snd_hrtf) { // These flags are the same thing, just with different names. -#ifdef FMOD_INIT_SOFTWARE_HRTF +#ifdef FMOD_INIT_CHANNEL_LOWPASS + initflags |= FMOD_INIT_CHANNEL_LOWPASS; +#elif defined(FMOD_INIT_SOFTWARE_HRTF) initflags |= FMOD_INIT_SOFTWARE_HRTF; #else initflags |= FMOD_INIT_HRTF_LOWPASS; @@ -959,7 +1027,11 @@ bool FMODSoundRenderer::Init() } if (snd_profile) { +#ifdef FMOD_INIT_PROFILE_ENABLE + initflags |= FMOD_INIT_PROFILE_ENABLE; +#else initflags |= FMOD_INIT_ENABLE_PROFILE; +#endif } for (;;) { @@ -970,14 +1042,23 @@ bool FMODSoundRenderer::Init() // 1. The speaker mode selected isn't supported by this soundcard. Force it to stereo. // 2. The output format is unsupported. Force it to 16-bit PCM. // 3. ??? +#if FMOD_STUDIO + result = Sys->getSoftwareFormat(nullptr, &speakermode, nullptr); +#else result = Sys->getSpeakerMode(&speakermode); +#endif if (result == FMOD_OK && speakermode != FMOD_SPEAKERMODE_STEREO && +#if FMOD_STUDIO + FMOD_OK == Sys->setSoftwareFormat(samplerate, FMOD_SPEAKERMODE_STEREO, 0)) +#else FMOD_OK == Sys->setSpeakerMode(FMOD_SPEAKERMODE_STEREO)) +#endif { Printf(TEXTCOLOR_RED" Buffer creation failed. Retrying with stereo output.\n"); continue; } +#if !FMOD_STUDIO result = Sys->getSoftwareFormat(&samplerate, &format, NULL, NULL, &resampler, NULL); if (result == FMOD_OK && format != FMOD_SOUND_FORMAT_PCM16 && @@ -986,11 +1067,21 @@ bool FMODSoundRenderer::Init() Printf(TEXTCOLOR_RED" Buffer creation failed. Retrying with PCM-16 output.\n"); continue; } +#endif } - else if (result == FMOD_ERR_NET_SOCKET_ERROR && (initflags & FMOD_INIT_ENABLE_PROFILE)) + else if (result == FMOD_ERR_NET_SOCKET_ERROR && +#ifdef FMOD_INIT_PROFILE_ENABLE + (initflags & FMOD_INIT_PROFILE_ENABLE)) +#else + (initflags & FMOD_INIT_ENABLE_PROFILE)) +#endif { Printf(TEXTCOLOR_RED" Could not create socket. Retrying without profiling.\n"); +#ifdef FMOD_INIT_PROFILE_ENABLE + initflags &= ~FMOD_INIT_PROFILE_ENABLE; +#else initflags &= ~FMOD_INIT_ENABLE_PROFILE; +#endif continue; } #ifdef _WIN32 @@ -1066,7 +1157,11 @@ bool FMODSoundRenderer::Init() { FMOD::DSP *sfx_head, *pausable_head; +#if FMOD_STUDIO + result = SfxGroup->getDSP(FMOD_CHANNELCONTROL_DSP_HEAD, &sfx_head); +#else result = SfxGroup->getDSPHead(&sfx_head); +#endif if (result == FMOD_OK) { result = sfx_head->getInput(0, &pausable_head, &SfxConnection); @@ -1103,8 +1198,8 @@ bool FMODSoundRenderer::Init() } result = WaterLP->addInput(pausable_head, NULL); WaterLP->setActive(false); - WaterLP->setParameter(FMOD_DSP_LOWPASS_CUTOFF, snd_waterlp); - WaterLP->setParameter(FMOD_DSP_LOWPASS_RESONANCE, 2); + WaterLP->setParameterFloat(FMOD_DSP_LOWPASS_CUTOFF, snd_waterlp); + WaterLP->setParameterFloat(FMOD_DSP_LOWPASS_RESONANCE, 2); if (WaterReverb != NULL) { @@ -1120,15 +1215,25 @@ bool FMODSoundRenderer::Init() // These parameters are entirely empirical and can probably // stand some improvement, but it sounds remarkably close // to the old reverb unit's output. - WaterReverb->setParameter(FMOD_DSP_SFXREVERB_LFREFERENCE, 150); - WaterReverb->setParameter(FMOD_DSP_SFXREVERB_HFREFERENCE, 10000); - WaterReverb->setParameter(FMOD_DSP_SFXREVERB_ROOM, 0); - WaterReverb->setParameter(FMOD_DSP_SFXREVERB_ROOMHF, -5000); - WaterReverb->setParameter(FMOD_DSP_SFXREVERB_DRYLEVEL, 0); - WaterReverb->setParameter(FMOD_DSP_SFXREVERB_DECAYHFRATIO, 1); - WaterReverb->setParameter(FMOD_DSP_SFXREVERB_DECAYTIME, 0.25f); - WaterReverb->setParameter(FMOD_DSP_SFXREVERB_DENSITY, 100); - WaterReverb->setParameter(FMOD_DSP_SFXREVERB_DIFFUSION, 100); +#if FMOD_STUDIO + WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_LOWSHELFFREQUENCY, 150); +#else + WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_LFREFERENCE, 150); +#endif + WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_HFREFERENCE, 10000); +#if !FMOD_STUDIO + WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_ROOM, 0); + WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_ROOMHF, -5000); +#endif + WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_DRYLEVEL, 0); +#if FMOD_STUDIO + WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_HFDECAYRATIO, 100); +#else + WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_DECAYHFRATIO, 1); +#endif + WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_DECAYTIME, 0.25f); + WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_DENSITY, 100); + WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_DIFFUSION, 100); WaterReverb->setActive(false); } } @@ -1154,7 +1259,11 @@ bool FMODSoundRenderer::Init() { FMOD::DSP *master_head; +#if FMOD_STUDIO + result = master_group->getDSP(FMOD_CHANNELCONTROL_DSP_HEAD, &master_head); +#else result = master_group->getDSPHead(&master_head); +#endif if (result == FMOD_OK) { result = master_head->getOutput(0, &ChannelGroupTargetUnit, NULL); @@ -1174,7 +1283,11 @@ bool FMODSoundRenderer::Init() } } +#if FMOD_STUDIO + if (FMOD_OK != Sys->getSoftwareFormat(&OutputRate, NULL, NULL)) +#else if (FMOD_OK != Sys->getSoftwareFormat(&OutputRate, NULL, NULL, NULL, NULL, NULL)) +#endif { OutputRate = 48000; // Guess, but this should never happen. } @@ -1258,11 +1371,8 @@ void FMODSoundRenderer::PrintStatus() { FMOD_OUTPUTTYPE output; FMOD_SPEAKERMODE speakermode; - FMOD_SOUND_FORMAT format; - FMOD_DSP_RESAMPLER resampler; int driver; int samplerate; - int numoutputchannels; unsigned int bufferlength; int numbuffers; @@ -1272,20 +1382,38 @@ void FMODSoundRenderer::PrintStatus() { Printf ("Output type: " TEXTCOLOR_GREEN "%s\n", Enum_NameForNum(OutputNames, output)); } +#if FMOD_STUDIO + if (FMOD_OK == Sys->getSoftwareFormat(&samplerate, &speakermode, nullptr)) + { + Printf ("Speaker mode: " TEXTCOLOR_GREEN "%s\n", Enum_NameForNum(SpeakerModeNames, speakermode)); + Printf (TEXTCOLOR_LIGHTBLUE "Software mixer sample rate: " TEXTCOLOR_GREEN "%d\n", samplerate); + } +#else if (FMOD_OK == Sys->getSpeakerMode(&speakermode)) { Printf ("Speaker mode: " TEXTCOLOR_GREEN "%s\n", Enum_NameForNum(SpeakerModeNames, speakermode)); } +#endif if (FMOD_OK == Sys->getDriver(&driver)) { char name[256]; +#if FMOD_STUDIO + if (FMOD_OK != Sys->getDriverInfo(driver, name, sizeof(name), nullptr, nullptr, nullptr, nullptr)) +#else if (FMOD_OK != Sys->getDriverInfo(driver, name, sizeof(name), NULL)) +#endif { strcpy(name, "Unknown"); } Printf ("Driver: " TEXTCOLOR_GREEN "%d" TEXTCOLOR_NORMAL " (" TEXTCOLOR_ORANGE "%s" TEXTCOLOR_NORMAL ")\n", driver, name); +#if !FMOD_STUDIO DumpDriverCaps(Driver_Caps, Driver_MinFrequency, Driver_MaxFrequency); +#endif } +#if !FMOD_STUDIO + FMOD_SOUND_FORMAT format; + FMOD_DSP_RESAMPLER resampler; + int numoutputchannels; if (FMOD_OK == Sys->getSoftwareFormat(&samplerate, &format, &numoutputchannels, NULL, &resampler, NULL)) { Printf (TEXTCOLOR_LIGHTBLUE "Software mixer sample rate: " TEXTCOLOR_GREEN "%d\n", samplerate); @@ -1293,6 +1421,7 @@ void FMODSoundRenderer::PrintStatus() Printf (TEXTCOLOR_LIGHTBLUE "Software mixer channels: " TEXTCOLOR_GREEN "%d\n", numoutputchannels); Printf (TEXTCOLOR_LIGHTBLUE "Software mixer resampler: " TEXTCOLOR_GREEN "%s\n", Enum_NameForNum(ResamplerNames, resampler)); } +#endif if (FMOD_OK == Sys->getDSPBufferSize(&bufferlength, &numbuffers)) { Printf (TEXTCOLOR_LIGHTBLUE "DSP buffers: " TEXTCOLOR_GREEN "%u samples x %d\n", bufferlength, numbuffers); @@ -1305,6 +1434,7 @@ void FMODSoundRenderer::PrintStatus() // //========================================================================== +#if !FMOD_STUDIO void FMODSoundRenderer::DumpDriverCaps(FMOD_CAPS caps, int minfrequency, int maxfrequency) { Printf (TEXTCOLOR_OLIVE " Min. frequency: " TEXTCOLOR_GREEN "%d\n", minfrequency); @@ -1325,6 +1455,7 @@ void FMODSoundRenderer::DumpDriverCaps(FMOD_CAPS caps, int minfrequency, int max } if (caps & FMOD_CAPS_REVERB_LIMITED) Printf(TEXTCOLOR_OLIVE " Limited reverb\n"); } +#endif //========================================================================== // @@ -1342,7 +1473,11 @@ void FMODSoundRenderer::PrintDriversList() { for (i = 0; i < numdrivers; ++i) { +#if FMOD_STUDIO + if (FMOD_OK == Sys->getDriverInfo(i, name, sizeof(name), nullptr, nullptr, nullptr, nullptr)) +#else if (FMOD_OK == Sys->getDriverInfo(i, name, sizeof(name), NULL)) +#endif { Printf("%d. %s\n", i, name); } @@ -1365,7 +1500,7 @@ FString FMODSoundRenderer::GatherStats() channels = 0; total = update = geometry = stream = dsp = 0; Sys->getChannelsPlaying(&channels); -#if FMOD_VERSION >= 0x42501 +#if FMOD_STUDIO || FMOD_VERSION >= 0x42501 // We were built with an FMOD with the geometry parameter. if (ActiveFMODVersion >= 0x42501) { // And we are running with an FMOD that includes it. @@ -1612,7 +1747,11 @@ static void SetCustomLoopPts(FMOD::Sound *sound) // //========================================================================== +#if FMOD_STUDIO +static FMOD_RESULT F_CALLBACK open_reader_callback(const char *name, unsigned int *filesize, void **handle, void *userdata) +#else static FMOD_RESULT F_CALLBACK open_reader_callback(const char *name, int unicode, unsigned int *filesize, void **handle, void **userdata) +#endif { FileReader *reader = NULL; if(sscanf(name, "_FileReader_%p", &reader) != 1) @@ -1623,7 +1762,9 @@ static FMOD_RESULT F_CALLBACK open_reader_callback(const char *name, int unicode *filesize = reader->GetLength(); *handle = reader; +#if !FMOD_STUDIO *userdata = reader; +#endif return FMOD_OK; } @@ -1667,10 +1808,17 @@ SoundStream *FMODSoundRenderer::OpenStream(FileReader *reader, int flags) FString name; InitCreateSoundExInfo(&exinfo); +#if FMOD_STUDIO + exinfo.fileuseropen = open_reader_callback; + exinfo.fileuserclose = close_reader_callback; + exinfo.fileuserread = read_reader_callback; + exinfo.fileuserseek = seek_reader_callback; +#else exinfo.useropen = open_reader_callback; exinfo.userclose = close_reader_callback; exinfo.userread = read_reader_callback; exinfo.userseek = seek_reader_callback; +#endif mode = FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM; if(flags & SoundStream::Loop) @@ -1775,7 +1923,11 @@ FISoundChannel *FMODSoundRenderer::StartSound(SoundHandle sfx, float vol, int pi FMOD::Channel *chan; float freq; +#if FMOD_STUDIO + if (FMOD_OK == ((FMOD::Sound *)sfx.data)->getDefaults(&freq, NULL)) +#else if (FMOD_OK == ((FMOD::Sound *)sfx.data)->getDefaults(&freq, NULL, NULL, NULL)) +#endif { freq = PITCH(freq, pitch); } @@ -1785,7 +1937,11 @@ FISoundChannel *FMODSoundRenderer::StartSound(SoundHandle sfx, float vol, int pi } GRolloff = NULL; // Do 2D sounds need rolloff? +#if FMOD_STUDIO + result = Sys->playSound((FMOD::Sound *)sfx.data, (flags & SNDF_NOPAUSE) ? SfxGroup : PausableSfx, true, &chan); +#else result = Sys->playSound(FMOD_CHANNEL_FREE, (FMOD::Sound *)sfx.data, true, &chan); +#endif if (FMOD_OK == result) { result = chan->getMode(&mode); @@ -1809,7 +1965,9 @@ FISoundChannel *FMODSoundRenderer::StartSound(SoundHandle sfx, float vol, int pi mode |= FMOD_LOOP_OFF; } chan->setMode(mode); +#if !FMOD_STUDIO chan->setChannelGroup((flags & SNDF_NOPAUSE) ? SfxGroup : PausableSfx); +#endif if (freq != 0) { chan->setFrequency(freq); @@ -1822,18 +1980,22 @@ FISoundChannel *FMODSoundRenderer::StartSound(SoundHandle sfx, float vol, int pi } if (flags & SNDF_NOREVERB) { +#if FMOD_STUDIO + chan->setReverbProperties(0,0.f); +#else FMOD_REVERB_CHANNELPROPERTIES reverb = { 0, }; if (FMOD_OK == chan->getReverbProperties(&reverb)) { reverb.Room = -10000; chan->setReverbProperties(&reverb); } +#endif } chan->setPaused(false); return CommonChannelSetup(chan, reuse_chan); } - //DPrintf ("Sound %s failed to play: %d\n", sfx->name.GetChars(), result); + //DPrintf (DMSG_WARNING, "Sound %s failed to play: %d\n", sfx->name.GetChars(), result); return NULL; } @@ -1852,15 +2014,26 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener * FMOD_MODE mode; FMOD::Channel *chan; float freq; - float def_freq, def_vol, def_pan; + float def_freq; +#if !FMOD_STUDIO + float def_vol, def_pan; +#endif int numchans; int def_priority; +#if FMOD_STUDIO + if (FMOD_OK == ((FMOD::Sound *)sfx.data)->getDefaults(&def_freq, &def_priority)) +#else if (FMOD_OK == ((FMOD::Sound *)sfx.data)->getDefaults(&def_freq, &def_vol, &def_pan, &def_priority)) +#endif { freq = PITCH(def_freq, pitch); // Change the sound's default priority before playing it. +#if FMOD_STUDIO + ((FMOD::Sound *)sfx.data)->setDefaults(def_freq, clamp(def_priority - priority, 1, 256)); +#else ((FMOD::Sound *)sfx.data)->setDefaults(def_freq, def_vol, def_pan, clamp(def_priority - priority, 1, 256)); +#endif } else { @@ -1876,12 +2049,20 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener * // as long as the parameters are set properly. It will first try to kick out sounds // with the same priority level but has no problem with kicking out sounds at // higher priority levels if it needs to. +#if FMOD_STUDIO + result = Sys->playSound((FMOD::Sound *)sfx.data, (flags & SNDF_NOPAUSE) ? SfxGroup : PausableSfx, true, &chan); +#else result = Sys->playSound(FMOD_CHANNEL_FREE, (FMOD::Sound *)sfx.data, true, &chan); +#endif // Then set the priority back. if (def_priority >= 0) { +#if FMOD_STUDIO + ((FMOD::Sound *)sfx.data)->setDefaults(def_freq, def_priority); +#else ((FMOD::Sound *)sfx.data)->setDefaults(def_freq, def_vol, def_pan, def_priority); +#endif } if (FMOD_OK == result) @@ -1906,7 +2087,9 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener * } mode = SetChanHeadSettings(listener, chan, pos, !!(flags & SNDF_AREA), mode); chan->setMode(mode); +#if !FMOD_STUDIO chan->setChannelGroup((flags & SNDF_NOPAUSE) ? SfxGroup : PausableSfx); +#endif if (mode & FMOD_3D) { @@ -1941,12 +2124,16 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener * } if (flags & SNDF_NOREVERB) { +#if FMOD_STUDIO + chan->setReverbProperties(0,0.f); +#else FMOD_REVERB_CHANNELPROPERTIES reverb = { 0, }; if (FMOD_OK == chan->getReverbProperties(&reverb)) { reverb.Room = -10000; chan->setReverbProperties(&reverb); } +#endif } chan->setPaused(false); chan->getPriority(&def_priority); @@ -1956,7 +2143,7 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener * } GRolloff = NULL; - //DPrintf ("Sound %s failed to play: %d\n", sfx->name.GetChars(), result); + //DPrintf (DMSG_WARNING, "Sound %s failed to play: %d\n", sfx->name.GetChars(), result); return 0; } @@ -1970,7 +2157,14 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener * void FMODSoundRenderer::MarkStartTime(FISoundChannel *chan) { +#if FMOD_STUDIO + unsigned long long int dsp_time; + ((FMOD::Channel *)chan->SysChannel)->getDSPClock(&dsp_time,NULL); + chan->StartTime.Lo = dsp_time & 0xFFFFFFFF; + chan->StartTime.Hi = dsp_time >> 32; +#else Sys->getDSPClock(&chan->StartTime.Hi, &chan->StartTime.Lo); +#endif } //========================================================================== @@ -1990,7 +2184,14 @@ bool FMODSoundRenderer::HandleChannelDelay(FMOD::Channel *chan, FISoundChannel * { // Sound is being restarted, so seek it to the position // it would be in now if it had never been evicted. QWORD_UNION nowtime; +#if FMOD_STUDIO + unsigned long long int delay; + chan->getDelay(&delay,NULL,NULL); + nowtime.Lo = delay & 0xFFFFFFFF; + nowtime.Hi = delay >> 32; +#else chan->getDelay(FMOD_DELAYTYPE_DSPCLOCK_START, &nowtime.Hi, &nowtime.Lo); +#endif // If abstime is set, the sound is being restored, and // the channel's start time is actually its seek position. @@ -2078,14 +2279,24 @@ FMOD_MODE FMODSoundRenderer::SetChanHeadSettings(SoundListener *listener, FMOD:: { // Beyond interp_range: Normal 3D panning. level = 1; } +#if FMOD_STUDIO + if (chan->get3DLevel(&old_level) == FMOD_OK && old_level != level) + { // Only set it if it's different. + chan->set3DLevel(level); +#else if (chan->get3DPanLevel(&old_level) == FMOD_OK && old_level != level) { // Only set it if it's different. chan->set3DPanLevel(level); +#endif if (level < 1) { // Let the noise come from all speakers, not just the front ones. // A centered 3D sound does not play at full volume, so neither should the 2D-panned one. // This is sqrt(0.5), which is the result for a centered equal power panning. +#if FMOD_STUDIO + chan->setMixLevelsOutput(0.70711f,0.70711f,0.70711f,0.70711f,0.70711f,0.70711f,0.70711f,0.70711f); +#else chan->setSpeakerMix(0.70711f,0.70711f,0.70711f,0.70711f,0.70711f,0.70711f,0.70711f,0.70711f); +#endif } } return oldmode; @@ -2119,7 +2330,14 @@ FISoundChannel *FMODSoundRenderer::CommonChannelSetup(FMOD::Channel *chan, FISou else { schan = S_GetChannel(chan); +#if FMOD_STUDIO + unsigned long long int time; + chan->getDelay(&time,NULL,NULL); + schan->StartTime.Lo = time & 0xFFFFFFFF; + schan->StartTime.Hi = time >> 32; +#else chan->getDelay(FMOD_DELAYTYPE_DSPCLOCK_START, &schan->StartTime.Hi, &schan->StartTime.Lo); +#endif } chan->setUserData(schan); chan->setCallback(ChannelCallback); @@ -2349,7 +2567,7 @@ void FMODSoundRenderer::UpdateListener(SoundListener *listener) } if (env != PrevEnvironment || env->Modified) { - DPrintf ("Reverb Environment %s\n", env->Name); + DPrintf (DMSG_NOTIFY, "Reverb Environment %s\n", env->Name); const_cast(env)->Modified = false; SetSystemReverbProperties(&env->Properties); PrevEnvironment = env; @@ -2369,7 +2587,7 @@ void FMODSoundRenderer::UpdateListener(SoundListener *listener) if (LastWaterLP != snd_waterlp) { LastWaterLP = snd_waterlp; - WaterLP->setParameter(FMOD_DSP_LOWPASS_CUTOFF, snd_waterlp); + WaterLP->setParameterFloat(FMOD_DSP_LOWPASS_CUTOFF, snd_waterlp); } WaterLP->setActive(true); if (WaterReverb != NULL && snd_waterreverb) @@ -2481,7 +2699,14 @@ void FMODSoundRenderer::Sync(bool sync) if (sync) { Sys->lockDSP(); +#if FMOD_STUDIO + unsigned long long int clock; + SfxGroup->getDSPClock(&clock,NULL); + DSPClock.Lo = clock & 0xFFFFFFFF; + DSPClock.Hi = clock >> 32; +#else Sys->getDSPClock(&DSPClock.Hi, &DSPClock.Lo); +#endif } else { @@ -2499,7 +2724,14 @@ void FMODSoundRenderer::UpdateSounds() { // Any sounds played between now and the next call to this function // will start exactly one tic from now. +#if FMOD_STUDIO + unsigned long long int clock; + SfxGroup->getDSPClock(&clock,NULL); + DSPClock.Lo = clock & 0xFFFFFFFF; + DSPClock.Hi = clock >> 32; +#else Sys->getDSPClock(&DSPClock.Hi, &DSPClock.Lo); +#endif DSPClock.AsOne += OutputRate / TICRATE; Sys->update(); } @@ -2527,6 +2759,16 @@ std::pair FMODSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int l exinfo.defaultfrequency = frequency; switch (bits) { +#if FMOD_STUDIO + case -8: + // Need to convert sample data from signed to unsigned. + for (int i = 0; i < length; i++) + { + sfxdata[i] ^= 0x80; + } + + case 8: +#else // !FMOD_STUDIO case 8: // Need to convert sample data from unsigned to signed. for (int i = 0; i < length; ++i) @@ -2535,6 +2777,7 @@ std::pair FMODSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int l } case -8: +#endif // FMOD_STUDIO exinfo.format = FMOD_SOUND_FORMAT_PCM8; numsamples = length; break; @@ -2560,7 +2803,7 @@ std::pair FMODSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int l result = Sys->createSound((char *)sfxdata, samplemode, &exinfo, &sample); if (result != FMOD_OK) { - DPrintf("Failed to allocate sample: Error %d\n", result); + DPrintf(DMSG_ERROR, "Failed to allocate sample: Error %d\n", result); return std::make_pair(retval, true); } @@ -2598,7 +2841,7 @@ std::pair FMODSoundRenderer::LoadSound(BYTE *sfxdata, int leng result = Sys->createSound((char *)sfxdata, samplemode, &exinfo, &sample); if (result != FMOD_OK) { - DPrintf("Failed to allocate sample: Error %d\n", result); + DPrintf(DMSG_ERROR, "Failed to allocate sample: Error %d\n", result); return std::make_pair(retval, true); } SetCustomLoopPts(sample); @@ -2673,18 +2916,34 @@ unsigned int FMODSoundRenderer::GetSampleLength(SoundHandle sfx) //========================================================================== FMOD_RESULT F_CALLBACK FMODSoundRenderer::ChannelCallback +#if FMOD_STUDIO + (FMOD_CHANNELCONTROL *channel, FMOD_CHANNELCONTROL_TYPE controltype, FMOD_CHANNELCONTROL_CALLBACK_TYPE type, void *data1, void *data2) +#else (FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *data1, void *data2) +#endif { +#if FMOD_STUDIO + FMOD::ChannelControl *chan = (FMOD::ChannelControl *)channel; +#else FMOD::Channel *chan = (FMOD::Channel *)channel; +#endif FISoundChannel *schan; if (chan->getUserData((void **)&schan) == FMOD_OK && schan != NULL) { +#if FMOD_STUDIO + if (type == FMOD_CHANNELCONTROL_CALLBACK_END) +#else if (type == FMOD_CHANNEL_CALLBACKTYPE_END) +#endif { S_ChannelEnded(schan); } +#if FMOD_STUDIO + else if (type == FMOD_CHANNELCONTROL_CALLBACK_VIRTUALVOICE) +#else else if (type == FMOD_CHANNEL_CALLBACKTYPE_VIRTUALVOICE) +#endif { S_ChannelVirtualChanged(schan, data1 != 0); } @@ -2701,9 +2960,17 @@ FMOD_RESULT F_CALLBACK FMODSoundRenderer::ChannelCallback // //========================================================================== +#if FMOD_STUDIO +float F_CALLBACK FMODSoundRenderer::RolloffCallback(FMOD_CHANNELCONTROL *channel, float distance) +#else float F_CALLBACK FMODSoundRenderer::RolloffCallback(FMOD_CHANNEL *channel, float distance) +#endif { +#if FMOD_STUDIO + FMOD::ChannelControl *chan = (FMOD::ChannelControl *)channel; +#else FMOD::Channel *chan = (FMOD::Channel *)channel; +#endif FISoundChannel *schan; if (GRolloff != NULL) @@ -2742,7 +3009,11 @@ void FMODSoundRenderer::DrawWaveDebug(int mode) const spk *labels; int labelcount; +#if FMOD_STUDIO + if (FMOD_OK != Sys->getSoftwareFormat(NULL, NULL, &numoutchans)) +#else if (FMOD_OK != Sys->getSoftwareFormat(NULL, NULL, &numoutchans, NULL, NULL, NULL)) +#endif { return; } @@ -2866,12 +3137,14 @@ int FMODSoundRenderer::DrawChannelGroupWaveData(FMOD::ChannelGroup *group, float int drawn = 0; int x = 16; +#if !FMOD_STUDIO while (FMOD_OK == group->getWaveData(wavearray, width, drawn)) { drawn++; DrawWave(wavearray, x, y, width, height); x += (width + 16) << int(skip); } +#endif if (drawn) { y += height + 16; @@ -2893,12 +3166,14 @@ int FMODSoundRenderer::DrawSystemWaveData(float *wavearray, int width, int heigh int drawn = 0; int x = 16; +#if !FMOD_STUDIO while (FMOD_OK == Sys->getWaveData(wavearray, width, drawn)) { drawn++; DrawWave(wavearray, x, y, width, height); x += (width + 16) << int(skip); } +#endif if (drawn) { y += height + 16; @@ -2970,12 +3245,15 @@ int FMODSoundRenderer::DrawChannelGroupSpectrum(FMOD::ChannelGroup *group, float { x += width + 16; } + // TODO: FMOD Studio: Grab from DSP +#if !FMOD_STUDIO while (FMOD_OK == group->getSpectrum(spectrumarray, SPECTRUM_SIZE, drawn, FMOD_DSP_FFT_WINDOW_TRIANGLE)) { drawn++; DrawSpectrum(spectrumarray, x, y, width, height); x += (width + 16) << int(skip); } +#endif if (drawn) { y += height + 16; @@ -3001,12 +3279,15 @@ int FMODSoundRenderer::DrawSystemSpectrum(float *spectrumarray, int width, int h { x += width + 16; } + // TODO: FMOD Studio: Grab from DSP +#if !FMOD_STUDIO while (FMOD_OK == Sys->getSpectrum(spectrumarray, SPECTRUM_SIZE, drawn, FMOD_DSP_FFT_WINDOW_TRIANGLE)) { drawn++; DrawSpectrum(spectrumarray, x, y, width, height); x += (width + 16) << int(skip); } +#endif if (drawn) { y += height + 16; @@ -3121,7 +3402,7 @@ short *FMODSoundRenderer::DecodeSample(int outlen, const void *coded, int sizeby void FMODSoundRenderer::InitCreateSoundExInfo(FMOD_CREATESOUNDEXINFO *exinfo) const { memset(exinfo, 0, sizeof(*exinfo)); -#if FMOD_VERSION >= 0x42600 && FMOD_VERSION < 0x43800 +#if !FMOD_STUDIO && FMOD_VERSION >= 0x42600 && FMOD_VERSION < 0x43800 if (ActiveFMODVersion < 0x42600) { // This parameter was added for 4.26.00, and trying to pass it to older @@ -3145,13 +3426,32 @@ void FMODSoundRenderer::InitCreateSoundExInfo(FMOD_CREATESOUNDEXINFO *exinfo) co FMOD_RESULT FMODSoundRenderer::SetSystemReverbProperties(const REVERB_PROPERTIES *props) { -#if FMOD_VERSION < 0x43600 +#if !FMOD_STUDIO && FMOD_VERSION < 0x43600 return Sys->setReverbProperties((const FMOD_REVERB_PROPERTIES *)props); #else // The reverb format changed when hardware mixing support was dropped, because // all EAX-only properties were removed from the structure. FMOD_REVERB_PROPERTIES fr; +#if FMOD_STUDIO + const float LateEarlyRatio = powf(10.f, (props->Reverb - props->Reflections)/2000.f); + const float EarlyAndLatePower = powf(10.f, props->Reflections/1000.f) + powf(10, props->Reverb/1000.f); + const float HFGain = powf(10.f, props->RoomHF/2000.f); + fr.DecayTime = props->DecayTime*1000.f; + fr.EarlyDelay = props->ReflectionsDelay*1000.f; + fr.LateDelay = props->ReverbDelay*1000.f; + fr.HFReference = props->HFReference; + fr.HFDecayRatio = clamp(props->DecayHFRatio*100.f, 0.f, 100.f); + fr.Diffusion = props->Diffusion; + fr.Density = props->Density; + fr.LowShelfFrequency = props->DecayLFRatio; + fr.LowShelfGain = clamp(props->RoomLF/100.f, -48.f, 12.f); + fr.HighCut = clamp(props->RoomLF < 0 ? props->HFReference/sqrtf((1.f-HFGain)/HFGain) : 20000.f, 20.f, 20000.f); + fr.EarlyLateMix = props->Reflections > -10000.f ? LateEarlyRatio/(LateEarlyRatio + 1)*100.f : 100.f; + fr.WetLevel = clamp(10*log10f(EarlyAndLatePower)+props->Room/100.f, -80.f, 20.f); + + return Sys->setReverbProperties(0, &fr); +#else fr.Instance = props->Instance; fr.Environment = props->Environment; fr.EnvDiffusion = props->EnvDiffusion; @@ -3175,6 +3475,7 @@ FMOD_RESULT FMODSoundRenderer::SetSystemReverbProperties(const REVERB_PROPERTIES return Sys->setReverbProperties(&fr); #endif +#endif } #endif // NO_FMOD diff --git a/src/sound/fmodsound.h b/src/sound/fmodsound.h index 33b1f4bf7..e85a993c5 100644 --- a/src/sound/fmodsound.h +++ b/src/sound/fmodsound.h @@ -76,8 +76,13 @@ private: QWORD_UNION DSPClock; int OutputRate; +#if FMOD_STUDIO + static FMOD_RESULT F_CALLBACK ChannelCallback(FMOD_CHANNELCONTROL *channel, FMOD_CHANNELCONTROL_TYPE controltype, FMOD_CHANNELCONTROL_CALLBACK_TYPE type, void *data1, void *data2); + static float F_CALLBACK RolloffCallback(FMOD_CHANNELCONTROL *channel, float distance); +#else static FMOD_RESULT F_CALLBACK ChannelCallback(FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *data1, void *data2); static float F_CALLBACK RolloffCallback(FMOD_CHANNEL *channel, float distance); +#endif bool HandleChannelDelay(FMOD::Channel *chan, FISoundChannel *reuse_chan, int flags, float freq) const; FISoundChannel *CommonChannelSetup(FMOD::Channel *chan, FISoundChannel *reuse_chan) const; @@ -89,7 +94,9 @@ private: bool Init (); void Shutdown (); +#if !FMOD_STUDIO void DumpDriverCaps(FMOD_CAPS caps, int minfrequency, int maxfrequency); +#endif int DrawChannelGroupOutput(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, int mode); int DrawSystemOutput(float *wavearray, int width, int height, int y, int mode); @@ -121,7 +128,9 @@ private: // Just for snd_status display int Driver_MinFrequency; int Driver_MaxFrequency; +#if !FMOD_STUDIO FMOD_CAPS Driver_Caps; +#endif friend class FMODStreamCapsule; }; diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index f19e080c3..67f46b266 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -417,7 +417,7 @@ short *SoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, decoder->getInfo(&srate, &chans, &type); if(chans != ChannelConfig_Mono || type != SampleType_Int16) { - DPrintf("Sample is not 16-bit mono\n"); + DPrintf(DMSG_WARNING, "Sample is not 16-bit mono\n"); delete decoder; return samples; } @@ -553,7 +553,7 @@ std::pair SoundRenderer::LoadSoundVoc(BYTE *sfxdata, int lengt break; default: // Unknown block type okay = false; - DPrintf ("Unknown VOC block type %i\n", blocktype); + DPrintf (DMSG_ERROR, "Unknown VOC block type %i\n", blocktype); break; } // Move to next block diff --git a/src/sound/music_fluidsynth_mididevice.cpp b/src/sound/music_fluidsynth_mididevice.cpp index 0427ca438..b3f7b7d45 100644 --- a/src/sound/music_fluidsynth_mididevice.cpp +++ b/src/sound/music_fluidsynth_mididevice.cpp @@ -498,12 +498,12 @@ int FluidSynthMIDIDevice::LoadPatchSets(const char *patches) } if (FLUID_FAILED != fluid_synth_sfload(FluidSynth, path, count == 0)) { - DPrintf("Loaded patch set %s.\n", tok); + DPrintf(DMSG_NOTIFY, "Loaded patch set %s.\n", tok); count++; } else { - DPrintf("Failed to load patch set %s.\n", tok); + DPrintf(DMSG_ERROR, "Failed to load patch set %s.\n", tok); } tok = strtok(NULL, delim); } diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index 30468839d..ee9d322a4 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -376,7 +376,7 @@ bool TimidityPPMIDIDevice::LaunchTimidity () return false; } - DPrintf ("cmd: \x1cG%s\n", CommandLine.GetChars()); + DPrintf (DMSG_NOTIFY, "cmd: \x1cG%s\n", CommandLine.GetChars()); #ifdef _WIN32 STARTUPINFO startup = { sizeof(startup), }; diff --git a/src/sound/music_xmi_midiout.cpp b/src/sound/music_xmi_midiout.cpp index 50b95a776..a9cbcd2c6 100644 --- a/src/sound/music_xmi_midiout.cpp +++ b/src/sound/music_xmi_midiout.cpp @@ -143,7 +143,7 @@ XMISong::XMISong (FileReader &reader, EMidiDevice type, const char *args) memset(Songs, 0, sizeof(*Songs) * NumSongs); FindXMIDforms(MusHeader, SongLen, Songs); CurrSong = Songs; - DPrintf("XMI song count: %d\n", NumSongs); + DPrintf(DMSG_SPAMMY, "XMI song count: %d\n", NumSongs); } //========================================================================== diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index 6dd204c86..21370d082 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -747,8 +747,8 @@ OpenALSoundRenderer::OpenALSoundRenderer() ALCint major=0, minor=0; alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); - DPrintf(" ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); - DPrintf(" ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); + DPrintf(DMSG_SPAMMY, " ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); + DPrintf(DMSG_SPAMMY, " ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); TArray attribs; if(*snd_samplerate > 0) @@ -778,10 +778,10 @@ OpenALSoundRenderer::OpenALSoundRenderer() } attribs.Clear(); - DPrintf(" Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); - DPrintf(" Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); - DPrintf(" Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); - DPrintf(" Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); + DPrintf(DMSG_SPAMMY, " Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); + DPrintf(DMSG_SPAMMY, " Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); + DPrintf(DMSG_SPAMMY, " Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); + DPrintf(DMSG_SPAMMY, " Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); ALC.EXT_EFX = !!alcIsExtensionPresent(Device, "ALC_EXT_EFX"); ALC.EXT_disconnect = !!alcIsExtensionPresent(Device, "ALC_EXT_disconnect"); @@ -864,7 +864,7 @@ OpenALSoundRenderer::OpenALSoundRenderer() return; } FreeSfx = Sources; - DPrintf(" Allocated " TEXTCOLOR_BLUE"%u" TEXTCOLOR_NORMAL" sources\n", Sources.Size()); + DPrintf(DMSG_NOTIFY, " Allocated " TEXTCOLOR_BLUE"%u" TEXTCOLOR_NORMAL" sources\n", Sources.Size()); WasInWater = false; if(*snd_efx && ALC.EXT_EFX) @@ -913,10 +913,10 @@ OpenALSoundRenderer::OpenALSoundRenderer() { alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); if(alGetError() == AL_NO_ERROR) - DPrintf(" EAX Reverb found\n"); + DPrintf(DMSG_SPAMMY, " EAX Reverb found\n"); alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); if(alGetError() == AL_NO_ERROR) - DPrintf(" Standard Reverb found\n"); + DPrintf(DMSG_SPAMMY, " Standard Reverb found\n"); alDeleteEffects(1, &envReverb); getALError(); @@ -929,7 +929,7 @@ OpenALSoundRenderer::OpenALSoundRenderer() alFilteri(EnvFilters[0], AL_FILTER_TYPE, AL_FILTER_LOWPASS); alFilteri(EnvFilters[1], AL_FILTER_TYPE, AL_FILTER_LOWPASS); if(getALError() == AL_NO_ERROR) - DPrintf(" Lowpass found\n"); + DPrintf(DMSG_SPAMMY, " Lowpass found\n"); else { alDeleteFilters(2, EnvFilters); @@ -1194,7 +1194,7 @@ std::pair OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int loopend = length / (channels*bits/8); ALint loops[2] = { loopstart, loopend }; - DPrintf("Setting loop points %d -> %d\n", loops[0], loops[1]); + DPrintf(DMSG_NOTIFY, "Setting loop points %d -> %d\n", loops[0], loops[1]); alBufferiv(buffer, AL_LOOP_POINTS_SOFT, loops); getALError(); } @@ -1202,7 +1202,7 @@ std::pair OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int { static bool warned = false; if(!warned) - Printf("Loop points not supported!\n"); + Printf(DMSG_WARNING, "Loop points not supported!\n"); warned = true; } @@ -1867,7 +1867,7 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) if(env != PrevEnvironment || env->Modified) { PrevEnvironment = env; - DPrintf("Reverb Environment %s\n", env->Name); + DPrintf(DMSG_NOTIFY, "Reverb Environment %s\n", env->Name); if(EnvSlot != 0) LoadReverb(env); diff --git a/src/thingdef/olddecorations.cpp b/src/thingdef/olddecorations.cpp index 5fa994262..3ea0c4788 100644 --- a/src/thingdef/olddecorations.cpp +++ b/src/thingdef/olddecorations.cpp @@ -496,8 +496,7 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults, else if (def == DEF_Projectile && sc.Compare ("Damage")) { sc.MustGetNumber (); - FxDamageValue *x = new FxDamageValue(new FxConstant(sc.Number, sc), false); - defaults->Damage = (VMFunction *)(uintptr_t)(ActorDamageFuncs.Push(x) + 1); + defaults->SetDamage(sc.Number); } else if (def == DEF_Projectile && sc.Compare ("DamageType")) { diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index 04ac21890..635637149 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -270,23 +270,28 @@ static void FinishThingdef() for (i = 0; i < StateTempCalls.Size(); ++i) { FStateTempCall *tcall = StateTempCalls[i]; - VMFunction *func; + VMFunction *func = nullptr; assert(tcall->Code != NULL); - // Can we call this function directly without wrapping it in an - // anonymous function? e.g. Are we passing any parameters to it? - func = tcall->Code->GetDirectFunction(); - if (func == NULL) - { - FCompileContext ctx(tcall->ActorClass); - tcall->Code = tcall->Code->Resolve(ctx); + // We don't know the return type in advance for anonymous functions. + FCompileContext ctx(tcall->ActorClass, nullptr); + tcall->Code = tcall->Code->Resolve(ctx); + tcall->Proto = ctx.ReturnProto; - // Make sure resolving it didn't obliterate it. - if (tcall->Code != NULL) + // Make sure resolving it didn't obliterate it. + if (tcall->Code != nullptr) + { + // Can we call this function directly without wrapping it in an + // anonymous function? e.g. Are we passing any parameters to it? + func = tcall->Code->GetDirectFunction(); + + if (func == nullptr) { VMFunctionBuilder buildit; + assert(tcall->Proto != nullptr); + // Allocate registers used to pass parameters in. // self, stateowner, state (all are pointers) buildit.Registers[REGT_POINTER].Get(3); @@ -300,15 +305,7 @@ static void FinishThingdef() // Generate prototype for this anonymous function TArray args(3); SetImplicitArgs(&args, NULL, tcall->ActorClass, VARF_Method | VARF_Action); - if (tcall->Proto != NULL) - { - sfunc->Proto = NewPrototype(tcall->Proto->ReturnTypes, args); - } - else - { - TArray norets(0); - sfunc->Proto = NewPrototype(norets, args); - } + sfunc->Proto = NewPrototype(tcall->Proto->ReturnTypes, args); func = sfunc; @@ -321,11 +318,9 @@ static void FinishThingdef() codesize += sfunc->CodeSize; } } - } - if (tcall->Code != NULL) - { + delete tcall->Code; - tcall->Code = NULL; + tcall->Code = nullptr; for (int k = 0; k < tcall->NumStates; ++k) { tcall->ActorClass->OwnedStates[tcall->FirstState + k].SetAction(func); @@ -348,17 +343,17 @@ static void FinishThingdef() if (!def) { - Printf("No ActorInfo defined for class '%s'\n", ti->TypeName.GetChars()); + Printf(TEXTCOLOR_RED "No ActorInfo defined for class '%s'\n", ti->TypeName.GetChars()); errorcount++; continue; } - if (def->Damage != NULL) + if (def->DamageFunc != nullptr) { - FxDamageValue *dmg = (FxDamageValue *)ActorDamageFuncs[(uintptr_t)def->Damage - 1]; + FxDamageValue *dmg = (FxDamageValue *)ActorDamageFuncs[(uintptr_t)def->DamageFunc - 1]; VMScriptFunction *sfunc; sfunc = dmg->GetFunction(); - if (sfunc == NULL) + if (sfunc == nullptr) { FCompileContext ctx(ti); dmg = static_cast(dmg->Resolve(ctx)); @@ -370,15 +365,15 @@ static void FinishThingdef() dmg->Emit(&buildit); sfunc = buildit.MakeFunction(); sfunc->NumArgs = 1; - sfunc->Proto = NULL; ///FIXME: Need a proper prototype here + sfunc->Proto = nullptr; ///FIXME: Need a proper prototype here // Save this function in case this damage value was reused // (which happens quite easily with inheritance). dmg->SetFunction(sfunc); } } - def->Damage = sfunc; + def->DamageFunc = sfunc; - if (dump != NULL && sfunc != NULL) + if (dump != nullptr && sfunc != nullptr) { char label[64]; int labellen = mysnprintf(label, countof(label), "Function %s.Damage", diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 8ad28f098..3a419b112 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -30,6 +30,7 @@ void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int in bool CheckDeprecatedFlags(const AActor *actor, PClassActor *info, int index); const char *GetFlagName(unsigned int flagnum, int flagoffset); void ModActorFlag(AActor *actor, FFlagDef *fd, bool set); +bool ModActorFlag(AActor *actor, FString &flagname, bool set, bool printerror = true); INTBOOL CheckActorFlag(const AActor *actor, FFlagDef *fd); INTBOOL CheckActorFlag(const AActor *owner, const char *flagname, bool printerror = true); @@ -190,10 +191,9 @@ AFuncDesc *FindFunction(const char * string); void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag); void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray &out_params, PFunction *afd, FString statestring, FStateDefinitions *statedef); -FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, PPrototype *&proto, bool &endswithret); +FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &endswithret); class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag); FName CheckCastKludges(FName in); -void AddImplicitReturn(class FxSequence *code, const PPrototype *proto, FScanner &sc); void SetImplicitArgs(TArray *args, TArray *argflags, PClassActor *cls, DWORD funcflags); PFunction *FindGlobalActionFunction(const char *name); @@ -356,6 +356,7 @@ int MatchString (const char *in, const char **strings); #define ACTION_RETURN_STATE(v) do { FState *state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state, ATAG_STATE); return 1; } return 0; } while(0) +#define ACTION_RETURN_FLOAT(v) do { double u = v; if (numret > 0) { assert(ret != nullptr); ret->SetFloat(u); return 1; } return 0; } while(0) #define ACTION_RETURN_INT(v) do { int u = v; if (numret > 0) { assert(ret != NULL); ret->SetInt(u); return 1; } return 0; } while(0) #define ACTION_RETURN_BOOL(v) ACTION_RETURN_INT(v) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 6904a05d4..21551364b 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -76,6 +76,7 @@ #include "d_player.h" #include "p_maputl.h" #include "p_spec.h" +#include "templates.h" #include "math/cmath.h" AActor *SingleActorFromTID(int tid, AActor *defactor); @@ -230,6 +231,38 @@ DEFINE_ACTION_FUNCTION(AActor, CheckClass) return 0; } +//========================================================================== +// +// CheckClass +// +// NON-ACTION function to calculate missile damage for the given actor +// +//========================================================================== + +DEFINE_ACTION_FUNCTION(AActor, GetMissileDamage) +{ + if (numret > 0) + { + assert(ret != NULL); + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT(mask); + PARAM_INT(add) + PARAM_INT_OPT(pick_pointer) { pick_pointer = AAPTR_DEFAULT; } + + self = COPY_AAPTR(self, pick_pointer); + if (self == NULL) + { + ret->SetInt(0); + } + else + { + ret->SetInt(self->GetMissileDamage(mask, add)); + } + return 1; + } + return 0; +} + //========================================================================== // // IsPointerEqual @@ -1373,8 +1406,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfArmorType) enum { - XF_HURTSOURCE = 1, - XF_NOTMISSILE = 4, + XF_HURTSOURCE = 1, + XF_NOTMISSILE = 4, + XF_NOACTORTYPE = 1 << 3, }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) @@ -1388,6 +1422,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) PARAM_INT_OPT (nails) { nails = 0; } PARAM_INT_OPT (naildamage) { naildamage = 10; } PARAM_CLASS_OPT (pufftype, AActor) { pufftype = PClass::FindActor(NAME_BulletPuff); } + PARAM_NAME_OPT (damagetype) { damagetype = NAME_None; } if (damage < 0) // get parameters from metadata { @@ -1414,13 +1449,22 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) } } - P_RadiusAttack (self, self->target, damage, distance, self->DamageType, flags, fulldmgdistance); + if (!(flags & XF_NOACTORTYPE) && damagetype == NAME_None) + { + damagetype = self->DamageType; + } + + int pflags = 0; + if (flags & XF_HURTSOURCE) pflags |= RADF_HURTSOURCE; + if (flags & XF_NOTMISSILE) pflags |= RADF_SOURCEISSPOT; + + int count = P_RadiusAttack (self, self->target, damage, distance, damagetype, pflags, fulldmgdistance); P_CheckSplash(self, distance); if (alert && self->target != NULL && self->target->player != NULL) { P_NoiseAlert(self->target, self); } - return 0; + ACTION_RETURN_INT(count); } //========================================================================== @@ -4494,7 +4538,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) else { target = viewport; viewport = self; } } - if (fov > 0 && (fov < 360.)) + fov = MIN(fov, 360.); + + if (fov > 0) { DAngle an = absangle(viewport->AngleTo(target), viewport->Angles.Yaw); @@ -4671,90 +4717,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) PARAM_STRING (flagname); PARAM_BOOL (value); - const char *dot = strchr(flagname, '.'); - FFlagDef *fd; - PClassActor *cls = self->GetClass(); - - if (dot != NULL) - { - FString part1(flagname.GetChars(), dot - flagname); - fd = FindFlag(cls, part1, dot + 1); - } - else - { - fd = FindFlag(cls, flagname, NULL); - } - - if (fd != NULL) - { - bool kill_before, kill_after; - INTBOOL item_before, item_after; - INTBOOL secret_before, secret_after; - - kill_before = self->CountsAsKill(); - item_before = self->flags & MF_COUNTITEM; - secret_before = self->flags5 & MF5_COUNTSECRET; - - if (fd->structoffset == -1) - { - HandleDeprecatedFlags(self, cls, value, fd->flagbit); - } - else - { - ActorFlags *flagp = (ActorFlags*) (((char*)self) + fd->structoffset); - - // If these 2 flags get changed we need to update the blockmap and sector links. - bool linkchange = flagp == &self->flags && (fd->flagbit == MF_NOBLOCKMAP || fd->flagbit == MF_NOSECTOR); - - if (linkchange) self->UnlinkFromWorld(); - ModActorFlag(self, fd, value); - if (linkchange) self->LinkToWorld(); - } - kill_after = self->CountsAsKill(); - item_after = self->flags & MF_COUNTITEM; - secret_after = self->flags5 & MF5_COUNTSECRET; - // Was this monster previously worth a kill but no longer is? - // Or vice versa? - if (kill_before != kill_after) - { - if (kill_after) - { // It counts as a kill now. - level.total_monsters++; - } - else - { // It no longer counts as a kill. - level.total_monsters--; - } - } - // same for items - if (item_before != item_after) - { - if (item_after) - { // It counts as an item now. - level.total_items++; - } - else - { // It no longer counts as an item - level.total_items--; - } - } - // and secretd - if (secret_before != secret_after) - { - if (secret_after) - { // It counts as an secret now. - level.total_secrets++; - } - else - { // It no longer counts as an secret - level.total_secrets--; - } - } - } - else - { - Printf("Unknown flag '%s' in '%s'\n", flagname.GetChars(), cls->TypeName.GetChars()); - } + ModActorFlag(self, flagname, value); return 0; } @@ -5888,24 +5851,63 @@ enum RadiusGiveFlags RGF_OBJECTS | RGF_VOODOO | RGF_CORPSES | + RGF_KILLED | RGF_MISSILES | RGF_ITEMS, }; static bool DoRadiusGive(AActor *self, AActor *thing, PClassActor *item, int amount, double distance, int flags, PClassActor *filter, FName species, double mindist) { - // [MC] We only want to make an exception for missiles here. Nothing else. - bool missilePass = !!((flags & RGF_MISSILES) && thing->flags & MF_MISSILE); + + bool doPass = false; + // Always allow self to give, no matter what other flags are specified. Otherwise, not at all. if (thing == self) { if (!(flags & RGF_GIVESELF)) return false; + doPass = true; } else if (thing->flags & MF_MISSILE) { - if (!missilePass) + if (!(flags & RGF_MISSILES)) return false; + doPass = true; } + else if (((flags & RGF_ITEMS) && thing->IsKindOf(RUNTIME_CLASS(AInventory))) || + ((flags & RGF_CORPSES) && thing->flags & MF_CORPSE) || + ((flags & RGF_KILLED) && thing->flags6 & MF6_KILLED)) + { + doPass = true; + } + else if ((flags & (RGF_MONSTERS | RGF_OBJECTS | RGF_PLAYERS | RGF_VOODOO))) + { + // Make sure it's alive as we're not looking for corpses or killed here. + if (!doPass && thing->health > 0) + { + if (thing->player != nullptr) + { + if (((flags & RGF_PLAYERS) && (thing->player->mo == thing)) || + ((flags & RGF_VOODOO) && (thing->player->mo != thing))) + { + doPass = true; + } + } + else + { + if (((flags & RGF_MONSTERS) && (thing->flags3 & MF3_ISMONSTER)) || + ((flags & RGF_OBJECTS) && (!(thing->flags3 & MF3_ISMONSTER)) && + (thing->flags & MF_SHOOTABLE || thing->flags6 & MF6_VULNERABLE))) + { + doPass = true; + } + } + } + } + + // Nothing matched up so don't bother with the rest. + if (!doPass) + return false; + //[MC] Check for a filter, species, and the related exfilter/expecies/either flag(s). bool filterpass = DoCheckClass(thing, filter, !!(flags & RGF_EXFILTER)), speciespass = DoCheckSpecies(thing, species, !!(flags & RGF_EXSPECIES)); @@ -5916,13 +5918,14 @@ static bool DoRadiusGive(AActor *self, AActor *thing, PClassActor *item, int amo return false; } - //Check for target, master, and tracer flagging. - bool targetPass = true; - bool masterPass = true; - bool tracerPass = true; - bool ptrPass = false; if ((thing != self) && (flags & (RGF_NOTARGET | RGF_NOMASTER | RGF_NOTRACER))) { + //Check for target, master, and tracer flagging. + bool targetPass = true; + bool masterPass = true; + bool tracerPass = true; + bool ptrPass = false; + if ((thing == self->target) && (flags & RGF_NOTARGET)) targetPass = false; if ((thing == self->master) && (flags & RGF_NOMASTER)) @@ -5937,35 +5940,8 @@ static bool DoRadiusGive(AActor *self, AActor *thing, PClassActor *item, int amo return false; } - //Next, actor flag checking. - bool selfPass = !!((flags & RGF_GIVESELF) && thing == self); - bool corpsePass = !!((flags & RGF_CORPSES) && thing->flags & MF_CORPSE); - bool killedPass = !!((flags & RGF_KILLED) && thing->flags6 & MF6_KILLED); - bool monsterPass = !!((flags & RGF_MONSTERS) && thing->flags3 & MF3_ISMONSTER); - bool objectPass = !!((flags & RGF_OBJECTS) && (thing->player == NULL) && (!(thing->flags3 & MF3_ISMONSTER)) - && ((thing->flags & MF_SHOOTABLE) || (thing->flags6 & MF6_VULNERABLE))); - bool playerPass = !!((flags & RGF_PLAYERS) && (thing->player != NULL) && (thing->player->mo == thing)); - bool voodooPass = !!((flags & RGF_VOODOO) && (thing->player != NULL) && (thing->player->mo != thing)); - //Self calls priority over the rest of this. - if (!selfPass) + if (doPass) { - //If it's specifically a monster/object/player/voodoo... Can be either or... - if (monsterPass || objectPass || playerPass || voodooPass) - { - //...and is dead, without desire to give to the dead... - if (((thing->health <= 0) && !(corpsePass || killedPass))) - { - //Skip! - return false; - } - } - } - - bool itemPass = !!((flags & RGF_ITEMS) && thing->IsKindOf(RUNTIME_CLASS(AInventory))); - - if (selfPass || monsterPass || corpsePass || killedPass || itemPass || objectPass || missilePass || playerPass || voodooPass) - { - DVector3 diff = self->Vec3To(thing); diff.Z += thing->Height *0.5; if (flags & RGF_CUBE) @@ -7247,12 +7223,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FaceMovementDirection) { current -= anglelimit + offset; } - else // huh??? - { - current = angle + 180. + offset; - } mobj->SetAngle(current, !!(flags & FMDF_INTERPOLATE)); } + else + mobj->SetAngle(angle + offset, !!(flags & FMDF_INTERPOLATE)); } else mobj->SetAngle(angle + offset, !!(flags & FMDF_INTERPOLATE)); diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 3b114fbee..8248634a3 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -182,6 +182,7 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(MF4, BOSSDEATH, AActor, flags4), DEFINE_FLAG(MF5, DONTDRAIN, AActor, flags5), + DEFINE_FLAG(MF5, GETOWNER, AActor, flags5), DEFINE_FLAG(MF5, NODROPOFF, AActor, flags5), DEFINE_FLAG(MF5, NOFORWARDFALL, AActor, flags5), DEFINE_FLAG(MF5, COUNTSECRET, AActor, flags5), @@ -631,38 +632,39 @@ void InitThingdef() // Define some member variables we feel like exposing to the user PSymbolTable &symt = RUNTIME_CLASS(AActor)->Symbols; PType *array5 = NewArray(TypeSInt32, 5); - symt.AddSymbol(new PField(NAME_Alpha, TypeFloat64, VARF_Native, myoffsetof(AActor,Alpha))); - symt.AddSymbol(new PField(NAME_Angle, TypeFloat64, VARF_Native, myoffsetof(AActor,Angles.Yaw))); - symt.AddSymbol(new PField(NAME_Args, array5, VARF_Native, myoffsetof(AActor,args))); - symt.AddSymbol(new PField(NAME_CeilingZ, TypeFloat64, VARF_Native, myoffsetof(AActor,ceilingz))); - symt.AddSymbol(new PField(NAME_FloorZ, TypeFloat64, VARF_Native, myoffsetof(AActor,floorz))); - symt.AddSymbol(new PField(NAME_Health, TypeSInt32, VARF_Native, myoffsetof(AActor,health))); - symt.AddSymbol(new PField(NAME_Mass, TypeSInt32, VARF_Native, myoffsetof(AActor,Mass))); - symt.AddSymbol(new PField(NAME_Pitch, TypeFloat64, VARF_Native, myoffsetof(AActor,Angles.Pitch))); - symt.AddSymbol(new PField(NAME_Roll, TypeFloat64, VARF_Native, myoffsetof(AActor,Angles.Roll))); - symt.AddSymbol(new PField(NAME_Special, TypeSInt32, VARF_Native, myoffsetof(AActor,special))); - symt.AddSymbol(new PField(NAME_TID, TypeSInt32, VARF_Native, myoffsetof(AActor,tid))); - symt.AddSymbol(new PField(NAME_TIDtoHate, TypeSInt32, VARF_Native, myoffsetof(AActor,TIDtoHate))); - symt.AddSymbol(new PField(NAME_WaterLevel, TypeSInt32, VARF_Native, myoffsetof(AActor,waterlevel))); - symt.AddSymbol(new PField(NAME_X, TypeFloat64, VARF_Native, myoffsetof(AActor,__Pos.X))); // must remain read-only! - symt.AddSymbol(new PField(NAME_Y, TypeFloat64, VARF_Native, myoffsetof(AActor,__Pos.Y))); // must remain read-only! - symt.AddSymbol(new PField(NAME_Z, TypeFloat64, VARF_Native, myoffsetof(AActor,__Pos.Z))); // must remain read-only! - symt.AddSymbol(new PField(NAME_VelX, TypeFloat64, VARF_Native, myoffsetof(AActor,Vel.X))); - symt.AddSymbol(new PField(NAME_VelY, TypeFloat64, VARF_Native, myoffsetof(AActor, Vel.Y))); - symt.AddSymbol(new PField(NAME_VelZ, TypeFloat64, VARF_Native, myoffsetof(AActor, Vel.Z))); - symt.AddSymbol(new PField(NAME_MomX, TypeFloat64, VARF_Native, myoffsetof(AActor, Vel.X))); - symt.AddSymbol(new PField(NAME_MomY, TypeFloat64, VARF_Native, myoffsetof(AActor, Vel.Y))); - symt.AddSymbol(new PField(NAME_MomZ, TypeFloat64, VARF_Native, myoffsetof(AActor, Vel.Z))); - symt.AddSymbol(new PField(NAME_ScaleX, TypeFloat64, VARF_Native, myoffsetof(AActor, Scale.X))); - symt.AddSymbol(new PField(NAME_ScaleY, TypeFloat64, VARF_Native, myoffsetof(AActor, Scale.Y))); - symt.AddSymbol(new PField(NAME_Score, TypeSInt32, VARF_Native, myoffsetof(AActor,Score))); - symt.AddSymbol(new PField(NAME_Accuracy, TypeSInt32, VARF_Native, myoffsetof(AActor,accuracy))); - symt.AddSymbol(new PField(NAME_Stamina, TypeSInt32, VARF_Native, myoffsetof(AActor,stamina))); - symt.AddSymbol(new PField(NAME_Height, TypeFloat64, VARF_Native, myoffsetof(AActor,Height))); - symt.AddSymbol(new PField(NAME_Radius, TypeFloat64, VARF_Native, myoffsetof(AActor,radius))); - symt.AddSymbol(new PField(NAME_ReactionTime,TypeSInt32, VARF_Native, myoffsetof(AActor,reactiontime))); - symt.AddSymbol(new PField(NAME_MeleeRange, TypeFloat64, VARF_Native, myoffsetof(AActor,meleerange))); - symt.AddSymbol(new PField(NAME_Speed, TypeFloat64, VARF_Native, myoffsetof(AActor,Speed))); - symt.AddSymbol(new PField(NAME_Threshold, TypeSInt32, VARF_Native, myoffsetof(AActor,threshold))); - symt.AddSymbol(new PField(NAME_DefThreshold,TypeSInt32, VARF_Native, myoffsetof(AActor,DefThreshold))); + symt.AddSymbol(new PField(NAME_Alpha, TypeFloat64, VARF_Native, myoffsetof(AActor, Alpha))); + symt.AddSymbol(new PField(NAME_Angle, TypeFloat64, VARF_Native, myoffsetof(AActor, Angles.Yaw))); + symt.AddSymbol(new PField(NAME_Args, array5, VARF_Native, myoffsetof(AActor, args))); + symt.AddSymbol(new PField(NAME_CeilingZ, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, ceilingz))); + symt.AddSymbol(new PField(NAME_FloorZ, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, floorz))); + symt.AddSymbol(new PField(NAME_Health, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, health))); + symt.AddSymbol(new PField(NAME_Mass, TypeSInt32, VARF_Native, myoffsetof(AActor, Mass))); + symt.AddSymbol(new PField(NAME_Pitch, TypeFloat64, VARF_Native, myoffsetof(AActor, Angles.Pitch))); + symt.AddSymbol(new PField(NAME_Roll, TypeFloat64, VARF_Native, myoffsetof(AActor, Angles.Roll))); + symt.AddSymbol(new PField(NAME_Special, TypeSInt32, VARF_Native, myoffsetof(AActor, special))); + symt.AddSymbol(new PField(NAME_TID, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, tid))); + symt.AddSymbol(new PField(NAME_TIDtoHate, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, TIDtoHate))); + symt.AddSymbol(new PField(NAME_WaterLevel, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, waterlevel))); + symt.AddSymbol(new PField(NAME_X, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, __Pos.X))); // must remain read-only! + symt.AddSymbol(new PField(NAME_Y, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, __Pos.Y))); // must remain read-only! + symt.AddSymbol(new PField(NAME_Z, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, __Pos.Z))); // must remain read-only! + symt.AddSymbol(new PField(NAME_VelX, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.X))); + symt.AddSymbol(new PField(NAME_VelY, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.Y))); + symt.AddSymbol(new PField(NAME_VelZ, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.Z))); + symt.AddSymbol(new PField(NAME_MomX, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.X))); + symt.AddSymbol(new PField(NAME_MomY, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.Y))); + symt.AddSymbol(new PField(NAME_MomZ, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.Z))); + symt.AddSymbol(new PField(NAME_ScaleX, TypeFloat64, VARF_Native, myoffsetof(AActor, Scale.X))); + symt.AddSymbol(new PField(NAME_ScaleY, TypeFloat64, VARF_Native, myoffsetof(AActor, Scale.Y))); + symt.AddSymbol(new PField(NAME_Score, TypeSInt32, VARF_Native, myoffsetof(AActor, Score))); + symt.AddSymbol(new PField(NAME_Accuracy, TypeSInt32, VARF_Native, myoffsetof(AActor, accuracy))); + symt.AddSymbol(new PField(NAME_Stamina, TypeSInt32, VARF_Native, myoffsetof(AActor, stamina))); + symt.AddSymbol(new PField(NAME_Height, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Height))); + symt.AddSymbol(new PField(NAME_Radius, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, radius))); + symt.AddSymbol(new PField(NAME_ReactionTime, TypeSInt32, VARF_Native, myoffsetof(AActor, reactiontime))); + symt.AddSymbol(new PField(NAME_MeleeRange, TypeFloat64, VARF_Native, myoffsetof(AActor, meleerange))); + symt.AddSymbol(new PField(NAME_Speed, TypeFloat64, VARF_Native, myoffsetof(AActor, Speed))); + symt.AddSymbol(new PField(NAME_Threshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, threshold))); + symt.AddSymbol(new PField(NAME_DefThreshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, DefThreshold))); + symt.AddSymbol(new PField(NAME_Damage, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, DamageVal))); } diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 6a756d9b5..cad6198f0 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -63,6 +63,7 @@ static FxExpression *ParseClamp(FScanner &sc, PClassActor *cls); // // ParseExpression // [GRB] Parses an expression and stores it into Expression array +// It's worth mentioning that this is using C++ operator precedence // static FxExpression *ParseExpressionM (FScanner &sc, PClassActor *cls); @@ -95,18 +96,83 @@ FxExpression *ParseExpression (FScanner &sc, PClassActor *cls, bool mustresolve) static FxExpression *ParseExpressionM (FScanner &sc, PClassActor *cls) { - FxExpression *condition = ParseExpressionL (sc, cls); + FxExpression *base = ParseExpressionL (sc, cls); if (sc.CheckToken('?')) { FxExpression *truex = ParseExpressionM (sc, cls); sc.MustGetToken(':'); FxExpression *falsex = ParseExpressionM (sc, cls); - return new FxConditional(condition, truex, falsex); + return new FxConditional(base, truex, falsex); + } + else if (sc.CheckToken('=')) + { + FxExpression *right = ParseExpressionM(sc, cls); + return new FxAssign(base, right); } else { - return condition; + FxBinary *exp; + FxAssignSelf *left = new FxAssignSelf(sc); + + sc.GetToken(); + switch (sc.TokenType) + { + case TK_AddEq: + exp = new FxAddSub('+', left, nullptr); + break; + + case TK_SubEq: + exp = new FxAddSub('-', left, nullptr); + break; + + case TK_MulEq: + exp = new FxMulDiv('*', left, nullptr); + break; + + case TK_DivEq: + exp = new FxMulDiv('/', left, nullptr); + break; + + case TK_ModEq: + exp = new FxMulDiv('%', left, nullptr); + break; + + case TK_LShiftEq: + exp = new FxBinaryInt(TK_LShift, left, nullptr); + break; + + case TK_RShiftEq: + exp = new FxBinaryInt(TK_RShift, left, nullptr); + break; + + case TK_URShiftEq: + exp = new FxBinaryInt(TK_URShift, left, nullptr); + break; + + case TK_AndEq: + exp = new FxBinaryInt('&', left, nullptr); + break; + + case TK_XorEq: + exp = new FxBinaryInt('^', left, nullptr); + break; + + case TK_OrEq: + exp = new FxBinaryInt('|', left, nullptr); + break; + + default: + sc.UnGet(); + delete left; + return base; + } + + exp->right = ParseExpressionM(sc, cls); + + FxAssign *ret = new FxAssign(base, exp); + left->Assignment = ret; + return ret; } } @@ -243,7 +309,8 @@ static FxExpression *ParseExpressionC (FScanner &sc, PClassActor *cls) static FxExpression *ParseExpressionB (FScanner &sc, PClassActor *cls) { sc.GetToken(); - switch(sc.TokenType) + int token = sc.TokenType; + switch(token) { case '~': return new FxUnaryNotBitwise(ParseExpressionA (sc, cls)); @@ -257,6 +324,10 @@ static FxExpression *ParseExpressionB (FScanner &sc, PClassActor *cls) case '+': return new FxPlusSign(ParseExpressionA (sc, cls)); + case TK_Incr: + case TK_Decr: + return new FxPreIncrDecr(ParseExpressionA(sc, cls), token); + default: sc.UnGet(); return ParseExpressionA (sc, cls); @@ -312,6 +383,14 @@ static FxExpression *ParseExpressionA (FScanner &sc, PClassActor *cls) sc.MustGetToken(']'); base_expr = new FxArrayElement(base_expr, index); } + else if (sc.CheckToken(TK_Incr)) + { + return new FxPostIncrDecr(base_expr, TK_Incr); + } + else if (sc.CheckToken(TK_Decr)) + { + return new FxPostIncrDecr(base_expr, TK_Decr); + } else break; } @@ -357,6 +436,49 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) // a cheap way to get them working when people use "name" instead of 'name'. return new FxConstant(FName(sc.String), scpos); } + else if (sc.CheckToken(TK_Bool)) + { + sc.MustGetToken('('); + FxExpression *exp = ParseExpressionM(sc, cls); + sc.MustGetToken(')'); + return new FxBoolCast(exp); + } + else if (sc.CheckToken(TK_Int)) + { + sc.MustGetToken('('); + FxExpression *exp = ParseExpressionM(sc, cls); + sc.MustGetToken(')'); + return new FxIntCast(exp); + } + else if (sc.CheckToken(TK_Float)) + { + sc.MustGetToken('('); + FxExpression *exp = ParseExpressionM(sc, cls); + sc.MustGetToken(')'); + return new FxFloatCast(exp); + } + else if (sc.CheckToken(TK_State)) + { + sc.MustGetToken('('); + FxExpression *exp; + if (sc.CheckToken(TK_StringConst)) + { + if (sc.String[0] == 0 || sc.Compare("None")) + { + exp = new FxConstant((FState*)nullptr, sc); + } + else + { + exp = new FxMultiNameState(sc.String, sc); + } + } + else + { + exp = new FxRuntimeStateIndex(ParseExpressionM(sc, cls)); + } + sc.MustGetToken(')'); + return exp; + } else if (sc.CheckToken(TK_Identifier)) { FName identifier = FName(sc.String); @@ -374,6 +496,29 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) case NAME_Random2: return ParseRandom2(sc, cls); default: + if (cls != nullptr) + { + func = dyn_cast(cls->Symbols.FindSymbol(identifier, true)); + + // There is an action function ACS_NamedExecuteWithResult which must be ignored here for this to work. + if (func != nullptr && identifier != NAME_ACS_NamedExecuteWithResult) + { + args = new FArgumentList; + if (sc.CheckToken('(')) + { + sc.UnGet(); + ParseFunctionParameters(sc, cls, *args, func, "", nullptr); + } + if (args->Size() == 0) + { + delete args; + args = nullptr; + } + + return new FxVMFunctionCall(func, args, sc); + } + } + break; } if (sc.CheckToken('(')) @@ -392,17 +537,9 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) return ParseAtan2(sc, identifier, cls); default: args = new FArgumentList; - func = (cls == nullptr) ? nullptr : dyn_cast(cls->Symbols.FindSymbol(identifier, true)); try { - // There is an action function ACS_NamedExecuteWithResult which must be ignored here for this to work. - if (func != NULL && identifier != NAME_ACS_NamedExecuteWithResult) - { - sc.UnGet(); - ParseFunctionParameters(sc, cls, *args, func, "", NULL); - return new FxVMFunctionCall(func, args, sc); - } - else if (!sc.CheckToken(')')) + if (!sc.CheckToken(')')) { do { @@ -420,7 +557,7 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) } break; } - } + } else { return new FxIdentifier(identifier, sc); diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index b878e50f1..5fe8f252f 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -62,14 +62,16 @@ class FxJumpStatement; struct FCompileContext { TArray Jumps; + PPrototype *ReturnProto; PClassActor *Class; - FCompileContext(PClassActor *cls = nullptr); + FCompileContext(PClassActor *cls = nullptr, PPrototype *ret = nullptr); PSymbol *FindInClass(FName identifier); PSymbol *FindGlobal(FName identifier); void HandleJumps(int token, FxExpression *handler); + void CheckReturn(PPrototype *proto, FScriptPosition &pos); }; //========================================================================== @@ -170,14 +172,14 @@ struct ExpVal struct ExpEmit { - ExpEmit() : RegNum(0), RegType(REGT_NIL), Konst(false), Fixed(false) {} - ExpEmit(int reg, int type) : RegNum(reg), RegType(type), Konst(false), Fixed(false) {} - ExpEmit(int reg, int type, bool konst) : RegNum(reg), RegType(type), Konst(konst), Fixed(false) {} + ExpEmit() : RegNum(0), RegType(REGT_NIL), Konst(false), Fixed(false), Final(false) {} + ExpEmit(int reg, int type) : RegNum(reg), RegType(type), Konst(false), Fixed(false), Final(false) {} + ExpEmit(int reg, int type, bool konst) : RegNum(reg), RegType(type), Konst(konst), Fixed(false), Final(false) {} ExpEmit(VMFunctionBuilder *build, int type); void Free(VMFunctionBuilder *build); void Reuse(VMFunctionBuilder *build); - BYTE RegNum, RegType, Konst:1, Fixed:1; + BYTE RegNum, RegType, Konst:1, Fixed:1, Final:1; }; //========================================================================== @@ -201,7 +203,8 @@ public: virtual FxExpression *Resolve(FCompileContext &ctx); virtual bool isConstant() const; - virtual void RequestAddress(); + virtual bool RequestAddress(); + virtual PPrototype *ReturnProto(); virtual VMFunction *GetDirectFunction(); bool IsNumeric() const { return ValueType != TypeName && (ValueType->GetRegType() == REGT_INT || ValueType->GetRegType() == REGT_FLOAT); } bool IsPointer() const { return ValueType->GetRegType() == REGT_POINTER; } @@ -469,6 +472,84 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// FxPreIncrDecr +// +//========================================================================== + +class FxPreIncrDecr : public FxExpression +{ + int Token; + FxExpression *Base; + bool AddressRequested; + bool AddressWritable; + +public: + FxPreIncrDecr(FxExpression *base, int token); + ~FxPreIncrDecr(); + FxExpression *Resolve(FCompileContext&); + bool RequestAddress(); + ExpEmit Emit(VMFunctionBuilder *build); +}; + +//========================================================================== +// +// FxPostIncrDecr +// +//========================================================================== + +class FxPostIncrDecr : public FxExpression +{ + int Token; + FxExpression *Base; + +public: + FxPostIncrDecr(FxExpression *base, int token); + ~FxPostIncrDecr(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + +//========================================================================== +// +// FxAssign +// +//========================================================================== + +class FxAssign : public FxExpression +{ + FxExpression *Base; + FxExpression *Right; + bool AddressRequested; + bool AddressWritable; + +public: + FxAssign(FxExpression *base, FxExpression *right); + ~FxAssign(); + FxExpression *Resolve(FCompileContext&); + bool RequestAddress(); + ExpEmit Emit(VMFunctionBuilder *build); + + ExpEmit Address; +}; + +//========================================================================== +// +// FxAssignSelf +// +//========================================================================== + +class FxAssignSelf : public FxExpression +{ +public: + FxAssign *Assignment; + + FxAssignSelf(const FScriptPosition &pos); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // FxBinary @@ -670,6 +751,7 @@ public: class FxRandom : public FxExpression { protected: + bool EmitTail; FRandom *rng; FxExpression *min, *max; @@ -678,7 +760,7 @@ public: FxRandom(FRandom *, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos); ~FxRandom(); FxExpression *Resolve(FCompileContext&); - + PPrototype *ReturnProto(); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -724,6 +806,7 @@ public: class FxRandom2 : public FxExpression { + bool EmitTail; FRandom * rng; FxExpression *mask; @@ -732,7 +815,7 @@ public: FxRandom2(FRandom *, FxExpression *m, const FScriptPosition &pos); ~FxRandom2(); FxExpression *Resolve(FCompileContext&); - + PPrototype *ReturnProto(); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -753,7 +836,7 @@ public: FxClassMember(FxExpression*, PField*, const FScriptPosition&); ~FxClassMember(); FxExpression *Resolve(FCompileContext&); - void RequestAddress(); + bool RequestAddress(); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -771,20 +854,6 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; -//========================================================================== -// -// FxDamage -// -//========================================================================== - -class FxDamage : public FxExpression -{ -public: - FxDamage(const FScriptPosition&); - FxExpression *Resolve(FCompileContext&); - ExpEmit Emit(VMFunctionBuilder *build); -}; - //========================================================================== // // FxArrayElement @@ -796,12 +865,13 @@ class FxArrayElement : public FxExpression public: FxExpression *Array; FxExpression *index; - //bool AddressRequested; + bool AddressRequested; + bool AddressWritable; FxArrayElement(FxExpression*, FxExpression*); ~FxArrayElement(); FxExpression *Resolve(FCompileContext&); - //void RequestAddress(); + bool RequestAddress(); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -836,8 +906,9 @@ public: class FxActionSpecialCall : public FxExpression { - FxExpression *Self; int Special; + bool EmitTail; + FxExpression *Self; FArgumentList *ArgList; public: @@ -845,6 +916,7 @@ public: FxActionSpecialCall(FxExpression *self, int special, FArgumentList *args, const FScriptPosition &pos); ~FxActionSpecialCall(); FxExpression *Resolve(FCompileContext&); + PPrototype *ReturnProto(); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -875,6 +947,7 @@ public: class FxVMFunctionCall : public FxExpression { + bool EmitTail; PFunction *Function; FArgumentList *ArgList; @@ -882,13 +955,10 @@ public: FxVMFunctionCall(PFunction *func, FArgumentList *args, const FScriptPosition &pos); ~FxVMFunctionCall(); FxExpression *Resolve(FCompileContext&); + PPrototype *ReturnProto(); + VMFunction *GetDirectFunction(); ExpEmit Emit(VMFunctionBuilder *build); - ExpEmit Emit(VMFunctionBuilder *build, bool tailcall); bool CheckEmitCast(VMFunctionBuilder *build, bool returnit, ExpEmit ®); - unsigned GetArgCount() const { return ArgList == NULL ? 0 : ArgList->Size(); } - PFunction *GetFunction() const { return Function; } - VMFunction *GetVMFunction() const { return Function->Variants[0].Implementation; } - bool IsDirectFunction(); }; //========================================================================== @@ -1010,10 +1080,10 @@ public: class FxReturnStatement : public FxExpression { - FxVMFunctionCall *Call; + FxExpression *Value; public: - FxReturnStatement(FxVMFunctionCall *call, const FScriptPosition &pos); + FxReturnStatement(FxExpression *value, const FScriptPosition &pos); ~FxReturnStatement(); FxExpression *Resolve(FCompileContext&); ExpEmit Emit(VMFunctionBuilder *build); @@ -1058,6 +1128,26 @@ public: FxExpression *Resolve(FCompileContext&); }; +//========================================================================== +// +// Same as above except for expressions which means it will have to be +// evaluated at runtime +// +//========================================================================== + +class FxRuntimeStateIndex : public FxExpression +{ + bool EmitTail; + FxExpression *Index; + +public: + FxRuntimeStateIndex(FxExpression *index); + ~FxRuntimeStateIndex(); + FxExpression *Resolve(FCompileContext&); + PPrototype *ReturnProto(); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // @@ -1084,12 +1174,11 @@ public: class FxDamageValue : public FxExpression { FxExpression *val; - bool Calculated; VMScriptFunction *MyFunction; public: - FxDamageValue(FxExpression *v, bool calc); + FxDamageValue(FxExpression *v); ~FxDamageValue(); FxExpression *Resolve(FCompileContext&); diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index b57618575..ce603f261 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -91,7 +91,7 @@ static const FLOP FxFlops[] = // //========================================================================== -FCompileContext::FCompileContext(PClassActor *cls) : Class(cls) +FCompileContext::FCompileContext(PClassActor *cls, PPrototype *ret) : Class(cls), ReturnProto(ret) { } @@ -118,6 +118,50 @@ void FCompileContext::HandleJumps(int token, FxExpression *handler) } } +void FCompileContext::CheckReturn(PPrototype *proto, FScriptPosition &pos) +{ + assert(proto != nullptr); + bool fail = false; + + if (ReturnProto == nullptr) + { + ReturnProto = proto; + return; + } + + // A prototype that defines fewer return types can be compatible with + // one that defines more if the shorter one matches the initial types + // for the longer one. + if (ReturnProto->ReturnTypes.Size() < proto->ReturnTypes.Size()) + { // Make proto the shorter one to avoid code duplication below. + swapvalues(proto, ReturnProto); + } + // If one prototype returns nothing, they both must. + if (proto->ReturnTypes.Size() == 0) + { + if (ReturnProto->ReturnTypes.Size() != 0) + { + fail = true; + } + } + else + { + for (unsigned i = 0; i < proto->ReturnTypes.Size(); i++) + { + if (ReturnProto->ReturnTypes[i] != proto->ReturnTypes[i]) + { // Incompatible + fail = true; + break; + } + } + } + + if (fail) + { + pos.Message(MSG_ERROR, "All return expressions must deduce to the same type"); + } +} + //========================================================================== // // ExpEmit @@ -125,7 +169,7 @@ void FCompileContext::HandleJumps(int token, FxExpression *handler) //========================================================================== ExpEmit::ExpEmit(VMFunctionBuilder *build, int type) -: RegNum(build->Registers[type].Get(1)), RegType(type), Konst(false), Fixed(false) +: RegNum(build->Registers[type].Get(1)), RegType(type), Konst(false), Fixed(false), Final(false) { } @@ -218,13 +262,34 @@ FxExpression *FxExpression::Resolve(FCompileContext &ctx) //========================================================================== // -// +// Returns true if we can write to the address. // //========================================================================== -void FxExpression::RequestAddress() +bool FxExpression::RequestAddress() { ScriptPosition.Message(MSG_ERROR, "invalid dereference\n"); + return false; +} + +//========================================================================== +// +// Called by return statements. +// +//========================================================================== + +PPrototype *FxExpression::ReturnProto() +{ + assert(ValueType != nullptr); + + TArray ret(0); + TArray none(0); + if (ValueType != TypeVoid) + { + ret.Push(ValueType); + } + + return NewPrototype(ret, none); } //========================================================================== @@ -887,6 +952,296 @@ ExpEmit FxUnaryNotBoolean::Emit(VMFunctionBuilder *build) return from; } +//========================================================================== +// +// FxPreIncrDecr +// +//========================================================================== + +FxPreIncrDecr::FxPreIncrDecr(FxExpression *base, int token) +: FxExpression(base->ScriptPosition), Base(base), Token(token) +{ + AddressRequested = false; + AddressWritable = false; +} + +FxPreIncrDecr::~FxPreIncrDecr() +{ + SAFE_DELETE(Base); +} + +bool FxPreIncrDecr::RequestAddress() +{ + AddressRequested = true; + return AddressWritable; +} + +FxExpression *FxPreIncrDecr::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Base, ctx); + + ValueType = Base->ValueType; + + if (!Base->IsNumeric()) + { + ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); + delete this; + return nullptr; + } + else if (Base->ValueType == TypeBool) + { + ScriptPosition.Message(MSG_ERROR, "%s is not allowed on type bool", FScanner::TokenName(Token).GetChars()); + delete this; + return nullptr; + } + if (!(AddressWritable = Base->RequestAddress())) + { + ScriptPosition.Message(MSG_ERROR, "Expression must be a modifiable value"); + delete this; + return nullptr; + } + + return this; +} + +ExpEmit FxPreIncrDecr::Emit(VMFunctionBuilder *build) +{ + assert(Token == TK_Incr || Token == TK_Decr); + assert(ValueType == Base->ValueType && IsNumeric()); + + int zero = build->GetConstantInt(0); + int regtype = ValueType->GetRegType(); + ExpEmit pointer = Base->Emit(build); + + ExpEmit value(build, regtype); + build->Emit(ValueType->GetLoadOp(), value.RegNum, pointer.RegNum, zero); + + if (regtype == REGT_INT) + { + build->Emit((Token == TK_Incr) ? OP_ADD_RK : OP_SUB_RK, value.RegNum, value.RegNum, build->GetConstantInt(1)); + } + else + { + build->Emit((Token == TK_Incr) ? OP_ADDF_RK : OP_SUBF_RK, value.RegNum, value.RegNum, build->GetConstantFloat(1.)); + } + + build->Emit(ValueType->GetStoreOp(), pointer.RegNum, value.RegNum, zero); + + if (AddressRequested) + { + value.Free(build); + return pointer; + } + + pointer.Free(build); + return value; +} + +//========================================================================== +// +// FxPostIncrDecr +// +//========================================================================== + +FxPostIncrDecr::FxPostIncrDecr(FxExpression *base, int token) +: FxExpression(base->ScriptPosition), Base(base), Token(token) +{ +} + +FxPostIncrDecr::~FxPostIncrDecr() +{ + SAFE_DELETE(Base); +} + +FxExpression *FxPostIncrDecr::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Base, ctx); + + ValueType = Base->ValueType; + + if (!Base->IsNumeric()) + { + ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); + delete this; + return nullptr; + } + else if (Base->ValueType == TypeBool) + { + ScriptPosition.Message(MSG_ERROR, "%s is not allowed on type bool", FScanner::TokenName(Token).GetChars()); + delete this; + return nullptr; + } + if (!Base->RequestAddress()) + { + ScriptPosition.Message(MSG_ERROR, "Expression must be a modifiable value"); + delete this; + return nullptr; + } + + return this; +} + +ExpEmit FxPostIncrDecr::Emit(VMFunctionBuilder *build) +{ + assert(Token == TK_Incr || Token == TK_Decr); + assert(ValueType == Base->ValueType && IsNumeric()); + + int zero = build->GetConstantInt(0); + int regtype = ValueType->GetRegType(); + ExpEmit pointer = Base->Emit(build); + + ExpEmit out(build, regtype); + build->Emit(ValueType->GetLoadOp(), out.RegNum, pointer.RegNum, zero); + + ExpEmit assign(build, regtype); + if (regtype == REGT_INT) + { + build->Emit((Token == TK_Incr) ? OP_ADD_RK : OP_SUB_RK, assign.RegNum, out.RegNum, build->GetConstantInt(1)); + } + else + { + build->Emit((Token == TK_Incr) ? OP_ADDF_RK : OP_SUBF_RK, assign.RegNum, out.RegNum, build->GetConstantFloat(1.)); + } + + build->Emit(ValueType->GetStoreOp(), pointer.RegNum, assign.RegNum, zero); + + pointer.Free(build); + assign.Free(build); + return out; +} + +//========================================================================== +// +// FxAssign +// +//========================================================================== + +FxAssign::FxAssign(FxExpression *base, FxExpression *right) +: FxExpression(base->ScriptPosition), Base(base), Right(right) +{ + AddressRequested = false; + AddressWritable = false; +} + +FxAssign::~FxAssign() +{ + SAFE_DELETE(Base); + SAFE_DELETE(Right); +} + +bool FxAssign::RequestAddress() +{ + AddressRequested = true; + return AddressWritable; +} + +FxExpression *FxAssign::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Base, ctx); + + ValueType = Base->ValueType; + + SAFE_RESOLVE(Right, ctx); + + if (!Base->IsNumeric() || !Right->IsNumeric()) + { + ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); + delete this; + return nullptr; + } + if (!(AddressWritable = Base->RequestAddress())) + { + ScriptPosition.Message(MSG_ERROR, "Expression must be a modifiable value"); + delete this; + return nullptr; + } + + if (Right->ValueType != ValueType) + { + if (ValueType == TypeBool) + { + Right = new FxBoolCast(Right); + } + else if (ValueType->GetRegType() == REGT_INT) + { + Right = new FxIntCast(Right); + } + else + { + Right = new FxFloatCast(Right); + } + SAFE_RESOLVE(Right, ctx); + } + + return this; +} + +ExpEmit FxAssign::Emit(VMFunctionBuilder *build) +{ + assert(ValueType == Base->ValueType && IsNumeric()); + assert(ValueType->GetRegType() == Right->ValueType->GetRegType()); + + ExpEmit pointer = Base->Emit(build); + Address = pointer; + + ExpEmit result = Right->Emit(build); + + if (result.Konst) + { + ExpEmit temp(build, result.RegType); + build->Emit(result.RegType == REGT_FLOAT ? OP_LKF : OP_LK, temp.RegNum, result.RegNum); + result.Free(build); + result = temp; + } + + build->Emit(ValueType->GetStoreOp(), pointer.RegNum, result.RegNum, build->GetConstantInt(0)); + + if (AddressRequested) + { + result.Free(build); + return pointer; + } + + pointer.Free(build); + return result; +} + +//========================================================================== +// +// FxAssignSelf +// +//========================================================================== + +FxAssignSelf::FxAssignSelf(const FScriptPosition &pos) +: FxExpression(pos) +{ + Assignment = nullptr; +} + +FxExpression *FxAssignSelf::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + + // This should never happen if FxAssignSelf is used correctly + assert(Assignment != nullptr); + + ValueType = Assignment->ValueType; + + return this; +} + +ExpEmit FxAssignSelf::Emit(VMFunctionBuilder *build) +{ + assert(ValueType = Assignment->ValueType); + ExpEmit pointer = Assignment->Address; // FxAssign should have already emitted it + ExpEmit out(build, ValueType->GetRegType()); + build->Emit(ValueType->GetLoadOp(), out.RegNum, pointer.RegNum, build->GetConstantInt(0)); + return out; +} + //========================================================================== // // @@ -2360,6 +2715,7 @@ ExpEmit FxMinMax::Emit(VMFunctionBuilder *build) FxRandom::FxRandom(FRandom * r, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos) : FxExpression(pos) { + EmitTail = false; if (mi != NULL && ma != NULL) { min = new FxIntCast(mi); @@ -2388,6 +2744,18 @@ FxRandom::~FxRandom() // //========================================================================== +PPrototype *FxRandom::ReturnProto() +{ + EmitTail = true; + return FxExpression::ReturnProto(); +} + +//========================================================================== +// +// +// +//========================================================================== + FxExpression *FxRandom::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); @@ -2415,12 +2783,12 @@ int DecoRandom(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, FRandom *rng = reinterpret_cast(param[0].a); if (numparam == 1) { - ret->SetInt((*rng)()); + ACTION_RETURN_INT((*rng)()); } else if (numparam == 2) { int maskval = param[1].i; - ret->SetInt(rng->Random2(maskval)); + ACTION_RETURN_INT(rng->Random2(maskval)); } else if (numparam == 3) { @@ -2429,9 +2797,11 @@ int DecoRandom(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, { swapvalues(max, min); } - ret->SetInt((*rng)(max - min + 1) + min); + ACTION_RETURN_INT((*rng)(max - min + 1) + min); } - return 1; + + // Shouldn't happen + return 0; } ExpEmit FxRandom::Emit(VMFunctionBuilder *build) @@ -2444,17 +2814,27 @@ ExpEmit FxRandom::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != NULL); callfunc = ((PSymbolVMFunction *)sym)->Function; + int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); if (min != NULL && max != NULL) { EmitParameter(build, min, ScriptPosition); EmitParameter(build, max, ScriptPosition); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); + build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); } else { - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1); + build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1); } + + if (EmitTail) + { + ExpEmit call; + call.Final = true; + return call; + } + ExpEmit out(build, REGT_INT); build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); return out; @@ -2661,13 +3041,12 @@ int DecoFRandom(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret { swapvalues(max, min); } - ret->SetFloat(frandom * (max - min) + min); + ACTION_RETURN_FLOAT(frandom * (max - min) + min); } else { - ret->SetFloat(frandom); + ACTION_RETURN_FLOAT(frandom); } - return 1; } ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) @@ -2680,17 +3059,27 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != NULL); callfunc = ((PSymbolVMFunction *)sym)->Function; + int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); if (min != NULL && max != NULL) { EmitParameter(build, min, ScriptPosition); EmitParameter(build, max, ScriptPosition); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); + build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); } else { - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1); + build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1); } + + if (EmitTail) + { + ExpEmit call; + call.Final = true; + return call; + } + ExpEmit out(build, REGT_FLOAT); build->Emit(OP_RESULT, 0, REGT_FLOAT, out.RegNum); return out; @@ -2705,6 +3094,7 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) FxRandom2::FxRandom2(FRandom *r, FxExpression *m, const FScriptPosition &pos) : FxExpression(pos) { + EmitTail = false; rng = r; if (m) mask = new FxIntCast(m); else mask = new FxConstant(-1, pos); @@ -2728,6 +3118,18 @@ FxRandom2::~FxRandom2() // //========================================================================== +PPrototype *FxRandom2::ReturnProto() +{ + EmitTail = true; + return FxExpression::ReturnProto(); +} + +//========================================================================== +// +// +// +//========================================================================== + FxExpression *FxRandom2::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); @@ -2751,9 +3153,19 @@ ExpEmit FxRandom2::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != NULL); callfunc = ((PSymbolVMFunction *)sym)->Function; + int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); EmitParameter(build, mask, ScriptPosition); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2, 1); + build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2, 1); + + if (EmitTail) + { + ExpEmit call; + call.Final = true; + return call; + } + ExpEmit out(build, REGT_INT); build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); return out; @@ -2806,11 +3218,6 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) ScriptPosition.Message(MSG_ERROR, "Invalid member identifier '%s'\n", Identifier.GetChars()); } } - // the damage property needs special handling - else if (Identifier == NAME_Damage) - { - newex = new FxDamage(ScriptPosition); - } // now check the global identifiers. else if ((sym = ctx.FindGlobal(Identifier)) != NULL) { @@ -2904,65 +3311,6 @@ ExpEmit FxSelf::Emit(VMFunctionBuilder *build) } -//========================================================================== -// -// -// -//========================================================================== - -FxDamage::FxDamage(const FScriptPosition &pos) -: FxExpression(pos) -{ -} - -//========================================================================== -// -// FxDamage :: Resolve -// -//========================================================================== - -FxExpression *FxDamage::Resolve(FCompileContext& ctx) -{ - CHECKRESOLVED(); - ValueType = TypeSInt32; - return this; -} - -//========================================================================== -// -// FxDamage :: Emit -// -// Call this actor's damage function, if it has one -// -//========================================================================== - -ExpEmit FxDamage::Emit(VMFunctionBuilder *build) -{ - ExpEmit dmgval(build, REGT_INT); - - // Get damage function - ExpEmit dmgfunc(build, REGT_POINTER); - build->Emit(OP_LO, dmgfunc.RegNum, 0/*self*/, build->GetConstantInt(myoffsetof(AActor, Damage))); - - // If it's non-null... - build->Emit(OP_EQA_K, 1, dmgfunc.RegNum, build->GetConstantAddress(nullptr, ATAG_GENERIC)); - size_t nulljump = build->Emit(OP_JMP, 0); - - // ...call it - build->Emit(OP_PARAM, 0, REGT_POINTER, 0/*self*/); - build->Emit(OP_CALL, dmgfunc.RegNum, 1, 1); - build->Emit(OP_RESULT, 0, REGT_INT, dmgval.RegNum); - size_t notnulljump = build->Emit(OP_JMP, 0); - - // Otherwise, use 0 - build->BackpatchToHere(nulljump); - build->EmitLoadInt(dmgval.RegNum, 0); - build->BackpatchToHere(notnulljump); - - return dmgval; -} - - //========================================================================== // // @@ -2995,9 +3343,10 @@ FxClassMember::~FxClassMember() // //========================================================================== -void FxClassMember::RequestAddress() +bool FxClassMember::RequestAddress() { AddressRequested = true; + return !!(~membervar->Flags & VARF_ReadOnly); } //========================================================================== @@ -3027,6 +3376,17 @@ ExpEmit FxClassMember::Emit(VMFunctionBuilder *build) ExpEmit obj = classx->Emit(build); assert(obj.RegType == REGT_POINTER); + if (obj.Konst) + { + // If the situation where we are dereferencing a constant + // pointer is common, then it would probably be worthwhile + // to add new opcodes for those. But as of right now, I + // don't expect it to be a particularly common case. + ExpEmit newobj(build, REGT_POINTER); + build->Emit(OP_LKP, newobj.RegNum, obj.RegNum); + obj = newobj; + } + if (AddressRequested) { if (membervar->Offset == 0) @@ -3040,20 +3400,8 @@ ExpEmit FxClassMember::Emit(VMFunctionBuilder *build) } int offsetreg = build->GetConstantInt((int)membervar->Offset); - ExpEmit loc, tmp; + ExpEmit loc(build, membervar->Type->GetRegType()); - if (obj.Konst) - { - // If the situation where we are dereferencing a constant - // pointer is common, then it would probably be worthwhile - // to add new opcodes for those. But as of right now, I - // don't expect it to be a particularly common case. - ExpEmit newobj(build, REGT_POINTER); - build->Emit(OP_LKP, newobj.RegNum, obj.RegNum); - obj = newobj; - } - - loc = ExpEmit(build, membervar->Type->GetRegType()); build->Emit(membervar->Type->GetLoadOp(), loc.RegNum, obj.RegNum, offsetreg); obj.Free(build); return loc; @@ -3071,7 +3419,8 @@ FxArrayElement::FxArrayElement(FxExpression *base, FxExpression *_index) { Array=base; index = _index; - //AddressRequested = false; + AddressRequested = false; + AddressWritable = false; } //========================================================================== @@ -3092,12 +3441,11 @@ FxArrayElement::~FxArrayElement() // //========================================================================== -/* -void FxArrayElement::RequestAddress() +bool FxArrayElement::RequestAddress() { AddressRequested = true; + return AddressWritable; } -*/ //========================================================================== // @@ -3136,6 +3484,16 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) delete this; return NULL; } + if (index->isConstant()) + { + unsigned indexval = static_cast(index)->GetValue().GetInt(); + if (indexval >= arraytype->ElementCount) + { + ScriptPosition.Message(MSG_ERROR, "Array index out of bounds"); + delete this; + return NULL; + } + } ValueType = arraytype->ElementType; if (ValueType->GetRegType() != REGT_INT && ValueType->GetRegType() != REGT_FLOAT) @@ -3145,7 +3503,7 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) delete this; return NULL; } - Array->RequestAddress(); + AddressWritable = Array->RequestAddress(); return this; } @@ -3159,25 +3517,33 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) { ExpEmit start = Array->Emit(build); PArray *const arraytype = static_cast(Array->ValueType); - PType *const elementtype = arraytype->ElementType; - ExpEmit dest(build, elementtype->GetRegType()); + ExpEmit dest(build, arraytype->ElementType->GetRegType()); if (start.Konst) { ExpEmit tmpstart(build, REGT_POINTER); build->Emit(OP_LKP, tmpstart.RegNum, start.RegNum); + start.Free(build); start = tmpstart; } if (index->isConstant()) { unsigned indexval = static_cast(index)->GetValue().GetInt(); - if (indexval >= arraytype->ElementCount) - { - I_Error("Array index out of bounds"); - } + assert(indexval < arraytype->ElementCount && "Array index out of bounds"); indexval *= arraytype->ElementSize; - build->Emit(arraytype->ElementType->GetLoadOp(), dest.RegNum, - start.RegNum, build->GetConstantInt(indexval)); + + if (AddressRequested) + { + if (indexval != 0) + { + build->Emit(OP_ADDA_RK, start.RegNum, start.RegNum, build->GetConstantInt(indexval)); + } + } + else + { + build->Emit(arraytype->ElementType->GetLoadOp(), dest.RegNum, + start.RegNum, build->GetConstantInt(indexval)); + } } else { @@ -3193,10 +3559,24 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) { build->Emit(OP_SLL_RI, indexv.RegNum, indexv.RegNum, shiftbits); } - build->Emit(arraytype->ElementType->GetLoadOp() + 1, // added 1 to use the *_R version that - dest.RegNum, start.RegNum, indexv.RegNum); // takes the offset from a register + + if (AddressRequested) + { + build->Emit(OP_ADDA_RR, start.RegNum, start.RegNum, indexv.RegNum); + } + else + { + build->Emit(arraytype->ElementType->GetLoadOp() + 1, // added 1 to use the *_R version that + dest.RegNum, start.RegNum, indexv.RegNum); // takes the offset from a register + } indexv.Free(build); } + if (AddressRequested) + { + dest.Free(build); + return start; + } + start.Free(build); return dest; } @@ -3307,6 +3687,7 @@ FxActionSpecialCall::FxActionSpecialCall(FxExpression *self, int special, FArgum Self = self; Special = special; ArgList = args; + EmitTail = false; } //========================================================================== @@ -3327,6 +3708,18 @@ FxActionSpecialCall::~FxActionSpecialCall() // //========================================================================== +PPrototype *FxActionSpecialCall::ReturnProto() +{ + EmitTail = true; + return FxExpression::ReturnProto(); +} + +//========================================================================== +// +// +// +//========================================================================== + FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); @@ -3379,7 +3772,6 @@ FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx) int DecoCallLineSpecial(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret) { assert(numparam > 2 && numparam < 8); - assert(numret == 1); assert(param[0].Type == REGT_INT); assert(param[1].Type == REGT_POINTER); int v[5] = { 0 }; @@ -3388,8 +3780,7 @@ int DecoCallLineSpecial(VMFrameStack *stack, VMValue *param, int numparam, VMRet { v[i - 2] = param[i].i; } - ret->SetInt(P_ExecuteSpecial(param[0].i, NULL, reinterpret_cast(param[1].a), false, v[0], v[1], v[2], v[3], v[4])); - return 1; + ACTION_RETURN_INT(P_ExecuteSpecial(param[0].i, NULL, reinterpret_cast(param[1].a), false, v[0], v[1], v[2], v[3], v[4])); } ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) @@ -3434,6 +3825,14 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != NULL); callfunc = ((PSymbolVMFunction *)sym)->Function; + if (EmitTail) + { + build->Emit(OP_TAIL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2 + i, 0); + ExpEmit call; + call.Final = true; + return call; + } + ExpEmit dest(build, REGT_INT); build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2 + i, 1); build->Emit(OP_RESULT, 0, REGT_INT, dest.RegNum); @@ -3451,6 +3850,7 @@ FxVMFunctionCall::FxVMFunctionCall(PFunction *func, FArgumentList *args, const F { Function = func; ArgList = args; + EmitTail = false; } //========================================================================== @@ -3464,6 +3864,38 @@ FxVMFunctionCall::~FxVMFunctionCall() SAFE_DELETE(ArgList); } +//========================================================================== +// +// +// +//========================================================================== + +PPrototype *FxVMFunctionCall::ReturnProto() +{ + EmitTail = true; + return Function->Variants[0].Implementation->Proto; +} + +//========================================================================== +// +// +// +//========================================================================== + +VMFunction *FxVMFunctionCall::GetDirectFunction() +{ + // If this return statement calls a function with no arguments, + // then it can be a "direct" function. That is, the DECORATE + // definition can call that function directly without wrapping + // it inside VM code. + if (EmitTail && (ArgList ? ArgList->Size() : 0) == 0 && (Function->Flags & VARF_Action)) + { + return Function->Variants[0].Implementation; + } + + return nullptr; +} + //========================================================================== // // FxVMFunctionCall :: Resolve @@ -3493,6 +3925,11 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) { ValueType = rets[0]; } + else + { + ValueType = TypeVoid; + } + return this; } @@ -3505,19 +3942,14 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) //========================================================================== ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) -{ - return Emit(build, false); -} - -ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build, bool tailcall) { assert(build->Registers[REGT_POINTER].GetMostUsed() >= 3); - int count = GetArgCount(); + int count = (ArgList ? ArgList->Size() : 0); if (count == 1) { ExpEmit reg; - if (CheckEmitCast(build, tailcall, reg)) + if (CheckEmitCast(build, EmitTail, reg)) { return reg; } @@ -3546,10 +3978,12 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build, bool tailcall) VMFunction *vmfunc = Function->Variants[0].Implementation; int funcaddr = build->GetConstantAddress(vmfunc, ATAG_OBJECT); // Emit the call - if (tailcall) + if (EmitTail) { // Tail call build->Emit(OP_TAIL_K, funcaddr, count, 0); - return ExpEmit(); + ExpEmit call; + call.Final = true; + return call; } else if (vmfunc->Proto->ReturnTypes.Size() > 0) { // Call, expecting one result @@ -3597,6 +4031,7 @@ bool FxVMFunctionCall::CheckEmitCast(VMFunctionBuilder *build, bool returnit, Ex where.Free(build); } reg = ExpEmit(); + reg.Final = true; } else { @@ -4224,54 +4659,78 @@ ExpEmit FxJumpStatement::Emit(VMFunctionBuilder *build) // //========================================================================== -FxReturnStatement::FxReturnStatement(FxVMFunctionCall *call, const FScriptPosition &pos) -: FxExpression(pos), Call(call) +FxReturnStatement::FxReturnStatement(FxExpression *value, const FScriptPosition &pos) +: FxExpression(pos), Value(value) { + ValueType = TypeVoid; } FxReturnStatement::~FxReturnStatement() { - SAFE_DELETE(Call); + SAFE_DELETE(Value); } FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); - if (Call != NULL) + SAFE_RESOLVE_OPT(Value, ctx); + + PPrototype *retproto; + if (Value == nullptr) { - Call = static_cast(Call->Resolve(ctx)); - ABORT(Call); + TArray none(0); + retproto = NewPrototype(none, none); } + else + { + retproto = Value->ReturnProto(); + } + + ctx.CheckReturn(retproto, ScriptPosition); + return this; } ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build) { - // If we return nothing, use a regular RET opcode. If we return - // something, use TAIL to call the function. Our return type - // should be compatible with the called function's return type. - if (Call == NULL) + ExpEmit out(0, REGT_NIL); + + // If we return nothing, use a regular RET opcode. + // Otherwise just return the value we're given. + if (Value == nullptr) { build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0); } else { - Call->Emit(build, true); + out = Value->Emit(build); + + // Check if it is a function call that simplified itself + // into a tail call in which case we don't emit anything. + if (!out.Final) + { + if (Value->ValueType == TypeVoid) + { // Nothing is returned. + build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0); + } + else + { + build->Emit(OP_RET, RET_FINAL, out.RegType | (out.Konst ? REGT_KONST : 0), out.RegNum); + } + } } - return ExpEmit(); + + out.Final = true; + return out; } VMFunction *FxReturnStatement::GetDirectFunction() { - // If this return statement calls a function with no arguments, - // then it can be a "direct" function. That is, the DECORATE - // definition can call that function directly without wrapping - // it inside VM code. - if (Call != NULL && Call->GetArgCount() == 0 && (Call->GetFunction()->Flags & VARF_Action)) + if (Value != nullptr) { - return Call->GetVMFunction(); + return Value->GetDirectFunction(); } - return NULL; + return nullptr; } //========================================================================== @@ -4428,7 +4887,103 @@ FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx) delete this; return x; } + +//========================================================================== +// +// +// +//========================================================================== + +FxRuntimeStateIndex::FxRuntimeStateIndex(FxExpression *index) +: FxExpression(index->ScriptPosition), Index(index) +{ + EmitTail = false; + ValueType = TypeState; +} + +FxRuntimeStateIndex::~FxRuntimeStateIndex() +{ + SAFE_DELETE(Index); +} + +PPrototype *FxRuntimeStateIndex::ReturnProto() +{ + EmitTail = true; + return FxExpression::ReturnProto(); +} + +FxExpression *FxRuntimeStateIndex::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Index, ctx); + + if (!Index->IsNumeric()) + { + ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); + delete this; + return nullptr; + } + else if (Index->ValueType->GetRegType() != REGT_INT) + { // Float. + Index = new FxIntCast(Index); + SAFE_RESOLVE(Index, ctx); + } + + return this; +} + +static int DecoHandleRuntimeState(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret) +{ + PARAM_PROLOGUE; + PARAM_OBJECT(stateowner, AActor); + PARAM_POINTER(stateinfo, FStateParamInfo); + PARAM_INT(index); + + if (index == 0 || !stateowner->GetClass()->OwnsState(stateinfo->mCallingState + index)) + { + // Null is returned if the location was invalid which means that no jump will be performed + // if used as return value + // 0 always meant the same thing so we handle it here for compatibility + ACTION_RETURN_STATE(nullptr); + } + else + { + ACTION_RETURN_STATE(stateinfo->mCallingState + index); + } +} + +ExpEmit FxRuntimeStateIndex::Emit(VMFunctionBuilder *build) +{ + assert(build->Registers[REGT_POINTER].GetMostUsed() >= 3); + + ExpEmit out(build, REGT_POINTER); + + build->Emit(OP_PARAM, 0, REGT_POINTER, 1); // stateowner + build->Emit(OP_PARAM, 0, REGT_POINTER, 2); // stateinfo + ExpEmit id = Index->Emit(build); + build->Emit(OP_PARAM, 0, REGT_INT | (id.Konst ? REGT_KONST : 0), id.RegNum); // index + + VMFunction *callfunc; + PSymbol *sym; + sym = FindDecorateBuiltinFunction(NAME_DecoHandleRuntimeState, DecoHandleRuntimeState); + assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); + assert(((PSymbolVMFunction *)sym)->Function != nullptr); + callfunc = ((PSymbolVMFunction *)sym)->Function; + + if (EmitTail) + { + build->Emit(OP_TAIL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); + out.Final = true; + } + else + { + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); + build->Emit(OP_RESULT, 0, REGT_POINTER, out.RegNum); + } + + return out; +} //========================================================================== // @@ -4606,18 +5161,12 @@ ExpEmit FxMultiNameState::Emit(VMFunctionBuilder *build) // //========================================================================== -FxDamageValue::FxDamageValue(FxExpression *v, bool calc) +FxDamageValue::FxDamageValue(FxExpression *v) : FxExpression(v->ScriptPosition) { val = v; ValueType = TypeVoid; - Calculated = calc; MyFunction = NULL; - - if (!calc) - { - assert(v->isConstant() && "Non-calculated damage must be constant"); - } } FxDamageValue::~FxDamageValue() @@ -4653,7 +5202,7 @@ ExpEmit FxDamageValue::Emit(VMFunctionBuilder *build) assert(emitval.RegType == REGT_INT); build->Emit(OP_RET, 0, REGT_INT | (emitval.Konst ? REGT_KONST : 0), emitval.RegNum); } - build->Emit(OP_RETI, 1 | RET_FINAL, Calculated); + build->Emit(OP_RETI, 1 | RET_FINAL, true); return ExpEmit(); } diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 566643b8e..963165ab1 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -138,23 +138,30 @@ FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool c else if (type == TypeState) { // This forces quotation marks around the state name. - sc.MustGetToken(TK_StringConst); - if (sc.String[0] == 0 || sc.Compare("None")) + if (sc.CheckToken(TK_StringConst)) { - x = new FxConstant((FState*)NULL, sc); - } - else if (sc.Compare("*")) - { - if (constant) + if (sc.String[0] == 0 || sc.Compare("None")) { - x = new FxConstant((FState*)(intptr_t)-1, sc); + x = new FxConstant((FState*)NULL, sc); + } + else if (sc.Compare("*")) + { + if (constant) + { + x = new FxConstant((FState*)(intptr_t)-1, sc); + } + else sc.ScriptError("Invalid state name '*'"); + } + else + { + x = new FxMultiNameState(sc.String, sc); } - else sc.ScriptError("Invalid state name '*'"); } - else + else if (!constant) { - x = new FxMultiNameState(sc.String, sc); + x = new FxRuntimeStateIndex(ParseExpression(sc, cls)); } + else sc.MustGetToken(TK_StringConst); // This is for the error. } else if (type->GetClass() == RUNTIME_CLASS(PClassPointer)) { // Actor name @@ -858,19 +865,21 @@ static bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defau if (sc.CheckString ("(")) { - x = new FxDamageValue(new FxIntCast(ParseExpression(sc, bag.Info)), true); + conv.i = -1; + params.Push(conv); + x = new FxDamageValue(new FxIntCast(ParseExpression(sc, bag.Info))); sc.MustGetStringName(")"); + conv.exp = x; + params.Push(conv); + } else { sc.MustGetNumber(); - if (sc.Number != 0) - { - x = new FxDamageValue(new FxConstant(sc.Number, bag.ScriptPosition), false); - } + conv.i = sc.Number; + params.Push(conv); + conv.exp = nullptr; } - conv.exp = x; - params.Push(conv); } break; diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 372f444cd..52194435b 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -162,6 +162,71 @@ void ModActorFlag(AActor *actor, FFlagDef *fd, bool set) #endif } +//========================================================================== +// +// Finds a flag by name and sets or clears it +// +// Returns true if the flag was found for the actor; else returns false +// +//========================================================================== + +bool ModActorFlag(AActor *actor, FString &flagname, bool set, bool printerror) +{ + bool found = false; + + if (actor != NULL) + { + const char *dot = strchr(flagname, '.'); + FFlagDef *fd; + PClassActor *cls = actor->GetClass(); + + if (dot != NULL) + { + FString part1(flagname.GetChars(), dot - flagname); + fd = FindFlag(cls, part1, dot + 1); + } + else + { + fd = FindFlag(cls, flagname, NULL); + } + + if (fd != NULL) + { + found = true; + + if (actor->CountsAsKill() && actor->health > 0) --level.total_monsters; + if (actor->flags & MF_COUNTITEM) --level.total_items; + if (actor->flags5 & MF5_COUNTSECRET) --level.total_secrets; + + if (fd->structoffset == -1) + { + HandleDeprecatedFlags(actor, cls, set, fd->flagbit); + } + else + { + ActorFlags *flagp = (ActorFlags*)(((char*)actor) + fd->structoffset); + + // If these 2 flags get changed we need to update the blockmap and sector links. + bool linkchange = flagp == &actor->flags && (fd->flagbit == MF_NOBLOCKMAP || fd->flagbit == MF_NOSECTOR); + + if (linkchange) actor->UnlinkFromWorld(); + ModActorFlag(actor, fd, set); + if (linkchange) actor->LinkToWorld(); + } + + if (actor->CountsAsKill() && actor->health > 0) ++level.total_monsters; + if (actor->flags & MF_COUNTITEM) ++level.total_items; + if (actor->flags5 & MF5_COUNTSECRET) ++level.total_secrets; + } + else if (printerror) + { + DPrintf(DMSG_ERROR, "ACS/DECORATE: '%s' is not a flag in '%s'\n", flagname.GetChars(), cls->TypeName.GetChars()); + } + } + + return found; +} + //========================================================================== // // Returns whether an actor flag is true or not. @@ -571,7 +636,8 @@ DEFINE_PROPERTY(threshold, I, Actor) //========================================================================== DEFINE_PROPERTY(damage, X, Actor) { - PROP_EXP_PARM(id, 0); + PROP_INT_PARM(dmgval, 0); + PROP_EXP_PARM(id, 1); // Damage can either be a single number, in which case it is subject // to the original damage calculation rules. Or, it can be an expression @@ -581,13 +647,15 @@ DEFINE_PROPERTY(damage, X, Actor) // Store this expression here for now. It will be converted to a function // later once all actors have been processed. - if (id == NULL) + defaults->DamageVal = dmgval; + + if (id == nullptr) { - defaults->Damage = NULL; + defaults->DamageFunc = nullptr; } else { - defaults->Damage = (VMFunction *)(uintptr_t)(ActorDamageFuncs.Push(id) + 1); + defaults->DamageFunc = (VMFunction *)(uintptr_t)(ActorDamageFuncs.Push(id) + 1); } } diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index ecc62bf29..707de3045 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -315,10 +315,10 @@ do_stop: } bool hasfinalret; - tcall->Code = ParseActions(sc, state, statestring, bag, tcall->Proto, hasfinalret); - if (!hasfinalret) + tcall->Code = ParseActions(sc, state, statestring, bag, hasfinalret); + if (!hasfinalret && tcall->Code != nullptr) { - AddImplicitReturn(static_cast(tcall->Code), tcall->Proto, sc); + static_cast(tcall->Code)->Add(new FxReturnStatement(nullptr, sc)); } goto endofstate; } @@ -351,125 +351,37 @@ endofstate: sc.SetEscape(true); // re-enable escape sequences } -//========================================================================== -// -// AddImplicitReturn -// -// Adds an implied return; statement to the end of a code sequence. -// -//========================================================================== - -void AddImplicitReturn(FxSequence *code, const PPrototype *proto, FScanner &sc) -{ - if (code == NULL) - { - return; - } - if (proto == NULL || proto->ReturnTypes.Size() == 0) - { // Returns nothing. Good. We can safely add an implied return. - code->Add(new FxReturnStatement(NULL, sc)); - } - else - { // Something was returned earlier in the sequence. Make it an error - // instead of adding an implicit one. - sc.ScriptError("Not all paths return a value"); - } -} - -//========================================================================== -// -// ReturnCheck -// -// If proto1 is NULL, returns proto2. If proto2 is NULL, returns proto1. -// If neither is null, checks if both prototypes define the same return -// types. If not, an error is flagged. -// -//========================================================================== - -static PPrototype *ReturnCheck(PPrototype *proto1, PPrototype *proto2, FScanner &sc) -{ - if (proto1 == NULL) - { - return proto2; - } - if (proto2 == NULL) - { - return proto1; - } - // A prototype that defines fewer return types can be compatible with - // one that defines more if the shorter one matches the initial types - // for the longer one. - if (proto2->ReturnTypes.Size() < proto1->ReturnTypes.Size()) - { // Make proto1 the shorter one to avoid code duplication below. - swapvalues(proto1, proto2); - } - // If one prototype returns nothing, they both must. - if (proto1->ReturnTypes.Size() == 0) - { - if (proto2->ReturnTypes.Size() == 0) - { - return proto1; - } - proto1 = NULL; - } - else - { - for (unsigned i = 0; i < proto1->ReturnTypes.Size(); ++i) - { - if (proto1->ReturnTypes[i] != proto2->ReturnTypes[i]) - { // Incompatible - proto1 = NULL; - break; - } - } - } - if (proto1 == NULL) - { - sc.ScriptError("Return types are incompatible"); - } - return proto1; -} - //========================================================================== // // ParseActions // -// If this action block contains any return statements, the prototype for -// one of them will be returned. This is used for deducing the return type -// of anonymous functions. All called functions passed to return must have -// matching return types. -// //========================================================================== -static FxExpression *ParseIf(FScanner &sc, FState state, FString statestring, Baggage &bag, - PPrototype *&retproto, bool &lastwasret) +static FxExpression *ParseIf(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &lastwasret) { FxExpression *add, *cond; - FxExpression *true_part, *false_part = NULL; - PPrototype *true_proto, *false_proto = NULL; + FxExpression *true_part, *false_part = nullptr; bool true_ret, false_ret = false; sc.MustGetStringName("("); cond = ParseExpression(sc, bag.Info); sc.MustGetStringName(")"); sc.MustGetStringName("{"); // braces are mandatory - true_part = ParseActions(sc, state, statestring, bag, true_proto, true_ret); + true_part = ParseActions(sc, state, statestring, bag, true_ret); sc.MustGetString(); if (sc.Compare("else")) { if (sc.CheckString("if")) { - false_part = ParseIf(sc, state, statestring, bag, false_proto, false_ret); + false_part = ParseIf(sc, state, statestring, bag, false_ret); } else { sc.MustGetStringName("{"); // braces are still mandatory - false_part = ParseActions(sc, state, statestring, bag, false_proto, false_ret); + false_part = ParseActions(sc, state, statestring, bag, false_ret); sc.MustGetString(); } } add = new FxIfStatement(cond, true_part, false_part, sc); - retproto = ReturnCheck(retproto, true_proto, sc); - retproto = ReturnCheck(retproto, false_proto, sc); // If one side does not end with a return, we don't consider the if statement // to end with a return. If the else case is missing, it can never be considered // as ending with a return. @@ -480,11 +392,9 @@ static FxExpression *ParseIf(FScanner &sc, FState state, FString statestring, Ba return add; } -static FxExpression *ParseWhile(FScanner &sc, FState state, FString statestring, Baggage &bag, - PPrototype *&retproto, bool &lastwasret) +static FxExpression *ParseWhile(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &lastwasret) { FxExpression *cond, *code; - PPrototype *proto; bool ret; sc.MustGetStringName("("); @@ -492,24 +402,21 @@ static FxExpression *ParseWhile(FScanner &sc, FState state, FString statestring, sc.MustGetStringName(")"); sc.MustGetStringName("{"); // Enforce braces like for if statements. - code = ParseActions(sc, state, statestring, bag, proto, ret); + code = ParseActions(sc, state, statestring, bag, ret); sc.MustGetString(); - retproto = ReturnCheck(retproto, proto, sc); lastwasret = false; // A while loop always jumps back. return new FxWhileLoop(cond, code, sc); } -static FxExpression *ParseDoWhile(FScanner &sc, FState state, FString statestring, Baggage &bag, - PPrototype *&retproto, bool &lastwasret) +static FxExpression *ParseDoWhile(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &lastwasret) { FxExpression *cond, *code; - PPrototype *proto; bool ret; sc.MustGetStringName("{"); // Enforce braces like for if statements. - code = ParseActions(sc, state, statestring, bag, proto, ret); + code = ParseActions(sc, state, statestring, bag, ret); sc.MustGetStringName("while"); sc.MustGetStringName("("); @@ -518,20 +425,17 @@ static FxExpression *ParseDoWhile(FScanner &sc, FState state, FString statestrin sc.MustGetStringName(";"); sc.MustGetString(); - retproto = ReturnCheck(retproto, proto, sc); lastwasret = false; return new FxDoWhileLoop(cond, code, sc); } -static FxExpression *ParseFor(FScanner &sc, FState state, FString statestring, Baggage &bag, - PPrototype *&retproto, bool &lastwasret) +static FxExpression *ParseFor(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &lastwasret) { FxExpression *init = nullptr; FxExpression *cond = nullptr; FxExpression *iter = nullptr; FxExpression *code = nullptr; - PPrototype *proto; bool ret; // Parse the statements. @@ -539,7 +443,8 @@ static FxExpression *ParseFor(FScanner &sc, FState state, FString statestring, B sc.MustGetString(); if (!sc.Compare(";")) { - init = ParseAction(sc, state, statestring, bag); // That's all DECORATE can handle for now. + sc.UnGet(); + init = ParseExpression(sc, bag.Info); sc.MustGetStringName(";"); } sc.MustGetString(); @@ -552,30 +457,28 @@ static FxExpression *ParseFor(FScanner &sc, FState state, FString statestring, B sc.MustGetString(); if (!sc.Compare(")")) { - iter = ParseAction(sc, state, statestring, bag); + sc.UnGet(); + iter = ParseExpression(sc, bag.Info); sc.MustGetStringName(")"); } // Now parse the loop's content. sc.MustGetStringName("{"); // Enforce braces like for if statements. - code = ParseActions(sc, state, statestring, bag, proto, ret); + code = ParseActions(sc, state, statestring, bag, ret); sc.MustGetString(); - retproto = ReturnCheck(retproto, proto, sc); lastwasret = false; return new FxForLoop(init, cond, iter, code, sc); } -FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, - PPrototype *&retproto, bool &endswithret) +FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &endswithret) { // If it's not a '{', then it should be a single action. // Otherwise, it's a sequence of actions. if (!sc.Compare("{")) { FxVMFunctionCall *call = ParseAction(sc, state, statestring, bag); - retproto = call->GetVMFunction()->Proto; endswithret = true; return new FxReturnStatement(call, sc); } @@ -583,7 +486,6 @@ FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Bagg const FScriptPosition pos(sc); FxSequence *seq = NULL; - PPrototype *proto = NULL; bool lastwasret = false; sc.MustGetString(); @@ -593,38 +495,31 @@ FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Bagg lastwasret = false; if (sc.Compare("if")) { // Handle an if statement - add = ParseIf(sc, state, statestring, bag, proto, lastwasret); + add = ParseIf(sc, state, statestring, bag, lastwasret); } else if (sc.Compare("while")) { // Handle a while loop - add = ParseWhile(sc, state, statestring, bag, proto, lastwasret); + add = ParseWhile(sc, state, statestring, bag, lastwasret); } else if (sc.Compare("do")) { // Handle a do-while loop - add = ParseDoWhile(sc, state, statestring, bag, proto, lastwasret); + add = ParseDoWhile(sc, state, statestring, bag, lastwasret); } else if (sc.Compare("for")) { // Handle a for loop - add = ParseFor(sc, state, statestring, bag, proto, lastwasret); + add = ParseFor(sc, state, statestring, bag, lastwasret); } else if (sc.Compare("return")) { // Handle a return statement lastwasret = true; - FxVMFunctionCall *retexp = NULL; - PPrototype *retproto; + FxExpression *retexp = nullptr; sc.MustGetString(); if (!sc.Compare(";")) { - retexp = ParseAction(sc, state, statestring, bag); + sc.UnGet(); + retexp = ParseExpression(sc, bag.Info); sc.MustGetStringName(";"); - retproto = retexp->GetVMFunction()->Proto; } - else - { // Returning nothing; we still need a prototype for that. - TArray notypes(0); - retproto = NewPrototype(notypes, notypes); - } - proto = ReturnCheck(proto, retproto, sc); sc.MustGetString(); add = new FxReturnStatement(retexp, sc); } @@ -641,8 +536,9 @@ FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Bagg sc.MustGetString(); } else - { // Handle a regular action function call - add = ParseAction(sc, state, statestring, bag); + { // Handle a regular expression + sc.UnGet(); + add = ParseExpression(sc, bag.Info); sc.MustGetStringName(";"); sc.MustGetString(); } @@ -657,7 +553,6 @@ FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Bagg } } endswithret = lastwasret; - retproto = proto; return seq; } diff --git a/src/v_draw.cpp b/src/v_draw.cpp index 0a039e6ff..37ced09d5 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -63,6 +63,14 @@ #include "colormatcher.h" #include "r_data/colormaps.h" +CUSTOM_CVAR(Int, uiscale, 2, CVAR_ARCHIVE | CVAR_NOINITCALL) +{ + if (StatusBar != NULL) + { + StatusBar->ScreenSizeChanged(); + } +} + // [RH] Stretch values to make a 320x200 image best fit the screen // without using fractional steppings int CleanXfac, CleanYfac; @@ -76,7 +84,7 @@ int CleanXfac_1, CleanYfac_1, CleanWidth_1, CleanHeight_1; // FillSimplePoly uses this extern "C" short spanend[MAXHEIGHT]; -CVAR (Bool, hud_scale, false, CVAR_ARCHIVE); +CVAR (Bool, hud_scale, true, CVAR_ARCHIVE); // For routines that take RGB colors, cache the previous lookup in case there // are several repetitions with the same color. @@ -862,37 +870,37 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, double x, double y, DWORD tag void DCanvas::VirtualToRealCoords(double &x, double &y, double &w, double &h, double vwidth, double vheight, bool vbottom, bool handleaspect) const { - int myratio = handleaspect ? CheckRatio (Width, Height) : 0; + float myratio = handleaspect ? ActiveRatio (Width, Height) : (4.0f / 3.0f); // if 21:9 AR, map to 16:9 for all callers. // this allows for black bars and stops the stretching of fullscreen images - if (myratio == 6) { - myratio = 2; + if (myratio > 1.7f) { + myratio = 16.0f / 9.0f; } double right = x + w; double bottom = y + h; - if (myratio != 0 && myratio != 4) + if (myratio > 1.334f) { // The target surface is either 16:9 or 16:10, so expand the // specified virtual size to avoid undesired stretching of the // image. Does not handle non-4:3 virtual sizes. I'll worry about // those if somebody expresses a desire to use them. - x = (x - vwidth * 0.5) * Width * 960 / (vwidth * BaseRatioSizes[myratio][0]) + Width * 0.5; - w = (right - vwidth * 0.5) * Width * 960 / (vwidth * BaseRatioSizes[myratio][0]) + Width * 0.5 - x; + x = (x - vwidth * 0.5) * Width * 960 / (vwidth * AspectBaseWidth(myratio)) + Width * 0.5; + w = (right - vwidth * 0.5) * Width * 960 / (vwidth * AspectBaseWidth(myratio)) + Width * 0.5 - x; } else { x = x * Width / vwidth; w = right * Width / vwidth - x; } - if (myratio == 4) + if (AspectTallerThanWide(myratio)) { // The target surface is 5:4 - y = (y - vheight * 0.5) * Height * 600 / (vheight * BaseRatioSizes[myratio][1]) + Height * 0.5; - h = (bottom - vheight * 0.5) * Height * 600 / (vheight * BaseRatioSizes[myratio][1]) + Height * 0.5 - y; + y = (y - vheight * 0.5) * Height * 600 / (vheight * AspectBaseHeight(myratio)) + Height * 0.5; + h = (bottom - vheight * 0.5) * Height * 600 / (vheight * AspectBaseHeight(myratio)) + Height * 0.5 - y; if (vbottom) { - y += (Height - Height * BaseRatioSizes[myratio][3] / 48.0) * 0.5; + y += (Height - Height * AspectMultiplier(myratio) / 48.0) * 0.5; } } else @@ -936,30 +944,30 @@ void DCanvas::VirtualToRealCoordsInt(int &x, int &y, int &w, int &h, void DCanvas::FillBorder (FTexture *img) { - int myratio = CheckRatio (Width, Height); + float myratio = ActiveRatio (Width, Height); // if 21:9 AR, fill borders akin to 16:9, since all fullscreen // images are being drawn to that scale. - if (myratio == 6) { - myratio = 2; + if (myratio > 1.7f) { + myratio = 16 / 9.0f; } - if (myratio == 0) + if (myratio >= 1.3f && myratio <= 1.4f) { // This is a 4:3 display, so no border to show return; } int bordtop, bordbottom, bordleft, bordright, bord; - if (Is54Aspect(myratio)) + if (AspectTallerThanWide(myratio)) { // Screen is taller than it is wide bordleft = bordright = 0; - bord = Height - Height * BaseRatioSizes[myratio][3] / 48; + bord = Height - Height * AspectMultiplier(myratio) / 48; bordtop = bord / 2; bordbottom = bord - bordtop; } else { // Screen is wider than it is tall bordtop = bordbottom = 0; - bord = Width - Width * BaseRatioSizes[myratio][3] / 48; + bord = Width - Width * AspectMultiplier(myratio) / 48; bordleft = bord / 2; bordright = bord - bordleft; } diff --git a/src/v_video.cpp b/src/v_video.cpp index bcd49f920..e95303ca7 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -65,6 +65,8 @@ #include "menu/menu.h" #include "r_data/voxels.h" +int active_con_scale(); + FRenderer *Renderer; IMPLEMENT_ABSTRACT_CLASS (DCanvas) @@ -761,6 +763,21 @@ void DCanvas::CalcGamma (float gamma, BYTE gammalookup[256]) DSimpleCanvas::DSimpleCanvas (int width, int height, bool bgra) : DCanvas (width, height, bgra) { + MemBuffer = nullptr; + Resize(width, height); +} + +void DSimpleCanvas::Resize(int width, int height) +{ + Width = width; + Height = height; + + if (MemBuffer != NULL) + { + delete[] MemBuffer; + MemBuffer = NULL; + } + // Making the pitch a power of 2 is very bad for performance // Try to maximize the number of cache lines that can be filled // for each column drawing operation by making the pitch slightly @@ -796,7 +813,7 @@ DSimpleCanvas::DSimpleCanvas (int width, int height, bool bgra) Pitch = width + MAX(0, CPU.DataL1LineSize - 8); } } - int bytes_per_pixel = bgra ? 4 : 1; + int bytes_per_pixel = Bgra ? 4 : 1; MemBuffer = new BYTE[Pitch * height * bytes_per_pixel]; memset (MemBuffer, 0, Pitch * height * bytes_per_pixel); } @@ -872,6 +889,9 @@ DFrameBuffer::DFrameBuffer (int width, int height, bool bgra) { LastMS = LastSec = FrameCount = LastCount = LastTic = 0; Accel2D = false; + + VideoWidth = width; + VideoHeight = height; } //========================================================================== @@ -959,10 +979,20 @@ void DFrameBuffer::DrawRateStuff () int chars; int rate_x; + int textScale = active_con_scale(); + if (textScale == 0) + textScale = CleanXfac; + chars = mysnprintf (fpsbuff, countof(fpsbuff), "%2u ms (%3u fps)", howlong, LastCount); - rate_x = Width - ConFont->StringWidth(&fpsbuff[0]); - Clear (rate_x, 0, Width, ConFont->GetHeight(), GPalette.BlackIndex, 0); - DrawText (ConFont, CR_WHITE, rate_x, 0, (char *)&fpsbuff[0], TAG_DONE); + rate_x = Width / textScale - ConFont->StringWidth(&fpsbuff[0]); + Clear (rate_x * textScale, 0, Width, ConFont->GetHeight() * textScale, GPalette.BlackIndex, 0); + if (textScale == 1) + DrawText (ConFont, CR_WHITE, rate_x, 0, (char *)&fpsbuff[0], TAG_DONE); + else + DrawText (ConFont, CR_WHITE, rate_x, 0, (char *)&fpsbuff[0], + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); DWORD thisSec = ms/1000; if (LastSec < thisSec) @@ -1020,11 +1050,12 @@ void DFrameBuffer::DrawRateStuff () // Drawing it as a texture does and continues to show how // well the PalTex shader is working. static FPaletteTester palette; + int size = screen->GetHeight() < 800 ? 16 * 7 : 16 * 7 * 2; palette.SetTranslation(vid_showpalette); DrawTexture(&palette, 0, 0, - DTA_DestWidth, 16*7, - DTA_DestHeight, 16*7, + DTA_DestWidth, size, + DTA_DestHeight, size, DTA_Masked, false, TAG_DONE); } @@ -1350,7 +1381,6 @@ CCMD(clean) bool V_DoModeSetup (int width, int height, int bits) { DFrameBuffer *buff = I_SetMode (width, height, screen); - int cx1, cx2; if (buff == NULL) { @@ -1365,6 +1395,17 @@ bool V_DoModeSetup (int width, int height, int bits) // if D3DFB is being used for the display. FFont::StaticPreloadFonts(); + DisplayBits = bits; + V_UpdateModeSize(width, height); + + M_RefreshModesList (); + + return true; +} + +void V_UpdateModeSize (int width, int height) +{ + int cx1, cx2; V_CalcCleanFacs(320, 200, width, height, &CleanXfac, &CleanYfac, &cx1, &cx2); CleanWidth = width / CleanXfac; @@ -1405,32 +1446,38 @@ bool V_DoModeSetup (int width, int height, int bits) DisplayWidth = width; DisplayHeight = height; - DisplayBits = bits; R_OldBlend = ~0; Renderer->OnModeSet(); - - M_RefreshModesList (); +} - return true; +void V_OutputResized (int width, int height) +{ + V_UpdateModeSize(width, height); + setsizeneeded = true; + if (StatusBar != NULL) + { + StatusBar->ScreenSizeChanged(); + } + C_NewModeAdjust(); } void V_CalcCleanFacs (int designwidth, int designheight, int realwidth, int realheight, int *cleanx, int *cleany, int *_cx1, int *_cx2) { - int ratio; + float ratio; int cwidth; int cheight; int cx1, cy1, cx2, cy2; - ratio = CheckRatio(realwidth, realheight); - if (Is54Aspect(ratio)) + ratio = ActiveRatio(realwidth, realheight); + if (AspectTallerThanWide(ratio)) { cwidth = realwidth; - cheight = realheight * BaseRatioSizes[ratio][3] / 48; + cheight = realheight * AspectMultiplier(ratio) / 48; } else { - cwidth = realwidth * BaseRatioSizes[ratio][3] / 48; + cwidth = realwidth * AspectMultiplier(ratio) / 48; cheight = realheight; } // Use whichever pair of cwidth/cheight or width/height that produces less difference @@ -1664,20 +1711,10 @@ CUSTOM_CVAR (Int, vid_aspect, 0, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) } } -// Tries to guess the physical dimensions of the screen based on the -// screen's pixel dimensions. Can return: -// 0: 4:3 -// 1: 16:9 -// 2: 16:10 -// 3: 17:10 -// 4: 5:4 -// 5: 17:10 (redundant) -// 6: 21:9 -int CheckRatio (int width, int height, int *trueratio) +// Helper for ActiveRatio and CheckRatio. Returns the forced ratio type, or -1 if none. +int ActiveFakeRatio(int width, int height) { int fakeratio = -1; - int ratio; - if ((vid_aspect >= 1) && (vid_aspect <= 6)) { // [SP] User wants to force aspect ratio; let them. @@ -1689,7 +1726,7 @@ int CheckRatio (int width, int height, int *trueratio) else if (fakeratio == 5) { fakeratio = 3; - } + } } if (vid_nowidescreen) { @@ -1699,74 +1736,112 @@ int CheckRatio (int width, int height, int *trueratio) } else { - fakeratio = (height * 5/4 == width) ? 4 : 0; + fakeratio = (height * 5 / 4 == width) ? 4 : 0; } } - // If the size is approximately 16:9, consider it so. - if (abs (height * 16/9 - width) < 10) - { - ratio = 1; - } - // Consider 17:10 as well. - else if (abs (height * 17/10 - width) < 10) - { - ratio = 3; - } - // 16:10 has more variance in the pixel dimensions. Grr. - else if (abs (height * 16/10 - width) < 60) - { - // 320x200 and 640x400 are always 4:3, not 16:10 - if ((width == 320 && height == 200) || (width == 640 && height == 400)) - { - ratio = 0; - } - else - { - ratio = 2; - } - } - // Unless vid_tft is set, 1280x1024 is 4:3, not 5:4. - else if (height * 5/4 == width && vid_tft) - { - ratio = 4; - } - // test for 21:9 (actually 64:27, 21:9 is a semi-accurate ratio used in marketing) - else if (abs (height * 64/27 - width) < 30) - { - ratio = 6; - } - // Assume anything else is 4:3. (Which is probably wrong these days...) - else - { - ratio = 0; - } - - if (trueratio != NULL) - { - *trueratio = ratio; - } - return (fakeratio >= 0) ? fakeratio : ratio; + return fakeratio; } -// First column: Base width -// Second column: Base height (used for wall visibility multiplier) -// Third column: Psprite offset (needed for "tallscreen" modes) -// Fourth column: Width or height multiplier - -// For widescreen aspect ratio x:y ... -// base_width = 240 * x / y -// multiplier = 320 / base_width -// base_height = 200 * multiplier -const int BaseRatioSizes[7][4] = +// Active screen ratio based on cvars and size +float ActiveRatio(int width, int height, float *trueratio) { - { 960, 600, 0, 48 }, // 4:3 320, 200, multiplied by three - { 1280, 450, 0, 48*3/4 }, // 16:9 426.6667, 150, multiplied by three - { 1152, 500, 0, 48*5/6 }, // 16:10 386, 166.6667, multiplied by three - { 1224, 471, 0, 48*40/51 }, // 17:10 408, 156.8627, multiplied by three - { 960, 640, (int)(6.5*FRACUNIT), 48*15/16 }, // 5:4 320, 213.3333, multiplied by three - { 1224, 471, 0, 48*40/51 }, // 17:10 408, 156.8627, multiplied by three (REDUNDANT) - { 1707, 338, 0, 48*9/16 } // 21:9 568.8889, 337.5, multiplied by three -}; + static float forcedRatioTypes[] = + { + 4 / 3.0f, + 16 / 9.0f, + 16 / 10.0f, + 17 / 10.0f, + 5 / 4.0f, + 17 / 10.0f, + 21 / 9.0f + }; + + float ratio = width / (float)height; + int fakeratio = ActiveFakeRatio(width, height); + + if (trueratio) + *trueratio = ratio; + return (fakeratio != -1) ? forcedRatioTypes[fakeratio] : ratio; +} + +// Tries to guess the physical dimensions of the screen based on the +// screen's pixel dimensions. Can return: +// 0: 4:3 +// 1: 16:9 +// 2: 16:10 +// 3: 17:10 +// 4: 5:4 +// 5: 17:10 (redundant, never returned) +// 6: 21:9 +int CheckRatio (int width, int height, int *trueratio) +{ + float aspect = width / (float)height; + + static std::pair ratioTypes[] = + { + { 21 / 9.0f , 6 }, + { 16 / 9.0f , 1 }, + { 17 / 10.0f , 3 }, + { 16 / 10.0f , 2 }, + { 4 / 3.0f , 0 }, + { 5 / 4.0f , 4 }, + { 0.0f, 0 } + }; + + int ratio = ratioTypes[0].second; + float distance = fabs(ratioTypes[0].first - aspect); + for (int i = 1; ratioTypes[i].first != 0.0f; i++) + { + float d = fabs(ratioTypes[i].first - aspect); + if (d < distance) + { + ratio = ratioTypes[i].second; + distance = d; + } + } + + int fakeratio = ActiveFakeRatio(width, height); + if (fakeratio == -1) + fakeratio = ratio; + + if (trueratio) + *trueratio = ratio; + return fakeratio; +} + +int AspectBaseWidth(float aspect) +{ + return (int)round(240.0f * aspect * 3.0f); +} + +int AspectBaseHeight(float aspect) +{ + if (!AspectTallerThanWide(aspect)) + return (int)round(200.0f * (320.0f / (AspectBaseWidth(aspect) / 3.0f)) * 3.0f); + else + return (int)round((200.0f * (4.0f / 3.0f)) / aspect * 3.0f); +} + +double AspectPspriteOffset(float aspect) +{ + if (!AspectTallerThanWide(aspect)) + return 0.0; + else + return ((4.0 / 3.0) / aspect - 1.0) * 97.5; +} + +int AspectMultiplier(float aspect) +{ + if (!AspectTallerThanWide(aspect)) + return (int)round(320.0f / (AspectBaseWidth(aspect) / 3.0f) * 48.0f); + else + return (int)round(200.0f / (AspectBaseHeight(aspect) / 3.0f) * 48.0f); +} + +bool AspectTallerThanWide(float aspect) +{ + return aspect < 1.333f; +} void IVideo::DumpAdapters () { diff --git a/src/v_video.h b/src/v_video.h index 526d2a562..9769ce06c 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -47,6 +47,8 @@ extern int CleanWidth_1, CleanHeight_1, CleanXfac_1, CleanYfac_1; extern int DisplayWidth, DisplayHeight, DisplayBits; bool V_DoModeSetup (int width, int height, int bits); +void V_UpdateModeSize (int width, int height); +void V_OutputResized (int width, int height); void V_CalcCleanFacs (int designwidth, int designheight, int realwidth, int realheight, int *cleanx, int *cleany, int *cx1=NULL, int *cx2=NULL); class FTexture; @@ -302,6 +304,8 @@ public: void Unlock (); protected: + void Resize(int width, int height); + BYTE *MemBuffer; DSimpleCanvas() {} @@ -420,6 +424,10 @@ public: virtual bool Is8BitMode() = 0; #endif + // The original size of the framebuffer as selected in the video menu. + int VideoWidth = 0; + int VideoHeight = 0; + protected: void DrawRateStuff (); void CopyFromBuff (BYTE *src, int srcPitch, int width, int height, BYTE *dest); @@ -515,14 +523,17 @@ extern "C" void ASM_PatchPitch (void); int CheckRatio (int width, int height, int *trueratio=NULL); static inline int CheckRatio (double width, double height) { return CheckRatio(int(width), int(height)); } -extern const int BaseRatioSizes[7][4]; +inline bool IsRatioWidescreen(int ratio) { return (ratio & 3) != 0; } -inline bool IsRatioWidescreen(int ratio) { - return (ratio & 3)!=0; -} +float ActiveRatio (int width, int height, float *trueratio = NULL); +static inline double ActiveRatio (double width, double height) { return ActiveRatio(int(width), int(height)); } -inline bool Is54Aspect(int ratio) { - return ratio == 4; -} +int AspectBaseWidth(float aspect); +int AspectBaseHeight(float aspect); +double AspectPspriteOffset(float aspect); +int AspectMultiplier(float aspect); +bool AspectTallerThanWide(float aspect); + +EXTERN_CVAR(Int, uiscale); #endif // __V_VIDEO_H__ diff --git a/src/version.h b/src/version.h index cffa25bdd..57dacaa00 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4549 +#define SAVEVER 4550 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp index fd84e3bbb..46895028c 100644 --- a/src/win32/fb_d3d9.cpp +++ b/src/win32/fb_d3d9.cpp @@ -1222,6 +1222,24 @@ void D3DFB::Flip() CurrRenderTexture ^= RenderTextureToggle; TempRenderTexture = RenderTexture[CurrRenderTexture]; } + + if (Windowed) + { + RECT box; + GetClientRect(Window, &box); + if (box.right > 0 && box.right > 0 && (Width != box.right || Height != box.bottom)) + { + Resize(box.right, box.bottom); + + TrueHeight = Height; + PixelDoubling = 0; + LBOffsetI = 0; + LBOffset = 0.0f; + Reset(); + + V_OutputResized(Width, Height); + } + } } //========================================================================== diff --git a/src/win32/i_input.cpp b/src/win32/i_input.cpp index 2b0659c83..1e6390392 100644 --- a/src/win32/i_input.cpp +++ b/src/win32/i_input.cpp @@ -537,10 +537,18 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) if (screen && !VidResizing) { LPMINMAXINFO mmi = (LPMINMAXINFO)lParam; - RECT rect = { 0, 0, screen->GetWidth(), screen->GetHeight() }; - AdjustWindowRectEx(&rect, WS_VISIBLE|WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW); - mmi->ptMinTrackSize.x = rect.right - rect.left; - mmi->ptMinTrackSize.y = rect.bottom - rect.top; + if (screen->IsFullscreen()) + { + RECT rect = { 0, 0, screen->GetWidth(), screen->GetHeight() }; + AdjustWindowRectEx(&rect, WS_VISIBLE | WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW); + mmi->ptMinTrackSize.x = rect.right - rect.left; + mmi->ptMinTrackSize.y = rect.bottom - rect.top; + } + else + { + mmi->ptMinTrackSize.x = 320; + mmi->ptMinTrackSize.y = 200; + } return 0; } break; diff --git a/src/m_specialpaths.cpp b/src/win32/i_specialpaths.cpp similarity index 50% rename from src/m_specialpaths.cpp rename to src/win32/i_specialpaths.cpp index abfb5db8f..ed8dc2ee6 100644 --- a/src/m_specialpaths.cpp +++ b/src/win32/i_specialpaths.cpp @@ -1,27 +1,46 @@ -#ifdef __APPLE__ -#include -#endif +/* +** i_specialpaths.cpp +** Gets special system folders where data should be stored. (Windows version) +** +**--------------------------------------------------------------------------- +** Copyright 2013-2016 Randy Heit +** Copyright 2016 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ -#ifdef _WIN32 #include #include #include #define USE_WINDOWS_DWORD -#endif #include "cmdlib.h" #include "m_misc.h" - -#if !defined(__APPLE__) && !defined(_WIN32) -#include -#include -#include "i_system.h" -#endif - #include "version.h" // for GAMENAME - -#if defined(_WIN32) - #include "i_system.h" typedef HRESULT (WINAPI *GKFP)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR *); @@ -323,314 +342,3 @@ FString M_GetSavegamesPath() } return path; } - -#elif defined(__APPLE__) - -//=========================================================================== -// -// M_GetCachePath Mac OS X -// -// Returns the path for cache GL nodes. -// -//=========================================================================== - -FString M_GetCachePath(bool create) -{ - FString path; - - char pathstr[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kApplicationSupportFolderType, create ? kCreateFolder : 0, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)pathstr, PATH_MAX)) - { - path = pathstr; - } - else - { - path = progdir; - } - path += "/zdoom/cache"; - return path; -} - -//=========================================================================== -// -// M_GetAutoexecPath Mac OS X -// -// Returns the expected location of autoexec.cfg. -// -//=========================================================================== - -FString M_GetAutoexecPath() -{ - FString path; - - char cpath[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) - { - path << cpath << "/" GAME_DIR "/autoexec.cfg"; - } - return path; -} - -//=========================================================================== -// -// M_GetCajunPath Mac OS X -// -// Returns the location of the Cajun Bot definitions. -// -//=========================================================================== - -FString M_GetCajunPath(const char *botfilename) -{ - FString path; - - // Just copies the Windows code. Should this be more Mac-specific? - path << progdir << "zcajun/" << botfilename; - if (!FileExists(path)) - { - path = ""; - } - return path; -} - -//=========================================================================== -// -// M_GetConfigPath Mac OS X -// -// Returns the path to the config file. On Windows, this can vary for reading -// vs writing. i.e. If $PROGDIR/zdoom-.ini does not exist, it will try -// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini. -// -//=========================================================================== - -FString M_GetConfigPath(bool for_reading) -{ - char cpath[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kPreferencesFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) - { - FString path; - path << cpath << "/" GAMENAMELOWERCASE ".ini"; - return path; - } - // Ungh. - return GAMENAMELOWERCASE ".ini"; -} - -//=========================================================================== -// -// M_GetScreenshotsPath Mac OS X -// -// Returns the path to the default screenshots directory. -// -//=========================================================================== - -FString M_GetScreenshotsPath() -{ - FString path; - char cpath[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) - { - path << cpath << "/" GAME_DIR "/Screenshots/"; - } - else - { - path = "~/"; - } - return path; -} - -//=========================================================================== -// -// M_GetSavegamesPath Mac OS X -// -// Returns the path to the default save games directory. -// -//=========================================================================== - -FString M_GetSavegamesPath() -{ - FString path; - char cpath[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) - { - path << cpath << "/" GAME_DIR "/Savegames/"; - } - return path; -} - -#else // Linux, et al. - - -FString GetUserFile (const char *file) -{ - FString path; - struct stat info; - - path = NicePath("~/" GAME_DIR "/"); - - if (stat (path, &info) == -1) - { - struct stat extrainfo; - - // Sanity check for ~/.config - FString configPath = NicePath("~/.config/"); - if (stat (configPath, &extrainfo) == -1) - { - if (mkdir (configPath, S_IRUSR | S_IWUSR | S_IXUSR) == -1) - { - I_FatalError ("Failed to create ~/.config directory:\n%s", strerror(errno)); - } - } - else if (!S_ISDIR(extrainfo.st_mode)) - { - I_FatalError ("~/.config must be a directory"); - } - - // This can be removed after a release or two - // Transfer the old zdoom directory to the new location - bool moved = false; - FString oldpath = NicePath("~/." GAMENAMELOWERCASE "/"); - if (stat (oldpath, &extrainfo) != -1) - { - if (rename(oldpath, path) == -1) - { - I_Error ("Failed to move old " GAMENAMELOWERCASE " directory (%s) to new location (%s).", - oldpath.GetChars(), path.GetChars()); - } - else - moved = true; - } - - if (!moved && mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) - { - I_FatalError ("Failed to create %s directory:\n%s", - path.GetChars(), strerror (errno)); - } - } - else - { - if (!S_ISDIR(info.st_mode)) - { - I_FatalError ("%s must be a directory", path.GetChars()); - } - } - path += file; - return path; -} - -//=========================================================================== -// -// M_GetCachePath Unix -// -// Returns the path for cache GL nodes. -// -//=========================================================================== - -FString M_GetCachePath(bool create) -{ - // Don't use GAME_DIR and such so that ZDoom and its child ports can - // share the node cache. - FString path = NicePath("~/.config/zdoom/cache"); - if (create) - { - CreatePath(path); - } - return path; -} - -//=========================================================================== -// -// M_GetAutoexecPath Unix -// -// Returns the expected location of autoexec.cfg. -// -//=========================================================================== - -FString M_GetAutoexecPath() -{ - return GetUserFile("autoexec.cfg"); -} - -//=========================================================================== -// -// M_GetCajunPath Unix -// -// Returns the location of the Cajun Bot definitions. -// -//=========================================================================== - -FString M_GetCajunPath(const char *botfilename) -{ - FString path; - - // Check first in ~/.config/zdoom/botfilename. - path = GetUserFile(botfilename); - if (!FileExists(path)) - { - // Then check in SHARE_DIR/botfilename. - path = SHARE_DIR; - path << botfilename; - if (!FileExists(path)) - { - path = ""; - } - } - return path; -} - -//=========================================================================== -// -// M_GetConfigPath Unix -// -// Returns the path to the config file. On Windows, this can vary for reading -// vs writing. i.e. If $PROGDIR/zdoom-.ini does not exist, it will try -// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini. -// -//=========================================================================== - -FString M_GetConfigPath(bool for_reading) -{ - return GetUserFile(GAMENAMELOWERCASE ".ini"); -} - -//=========================================================================== -// -// M_GetScreenshotsPath Unix -// -// Returns the path to the default screenshots directory. -// -//=========================================================================== - -FString M_GetScreenshotsPath() -{ - return NicePath("~/" GAME_DIR "/screenshots/"); -} - -//=========================================================================== -// -// M_GetSavegamesPath Unix -// -// Returns the path to the default save games directory. -// -//=========================================================================== - -FString M_GetSavegamesPath() -{ - return NicePath("~/" GAME_DIR); -} - -#endif diff --git a/src/win32/win32video.cpp b/src/win32/win32video.cpp index 74b10ef07..0d1fe0cff 100644 --- a/src/win32/win32video.cpp +++ b/src/win32/win32video.cpp @@ -795,7 +795,7 @@ void I_SetFPSLimit(int limit) CloseHandle(FPSLimitEvent); FPSLimitEvent = NULL; } - DPrintf("FPS timer disabled\n"); + DPrintf(DMSG_NOTIFY, "FPS timer disabled\n"); } else { @@ -804,7 +804,7 @@ void I_SetFPSLimit(int limit) FPSLimitEvent = CreateEvent(NULL, FALSE, TRUE, NULL); if (FPSLimitEvent == NULL) { // Could not create event, so cannot use timer. - Printf("Failed to create FPS limitter event\n"); + Printf(DMSG_WARNING, "Failed to create FPS limitter event\n"); return; } } @@ -819,7 +819,7 @@ void I_SetFPSLimit(int limit) Printf("Failed to create FPS limitter timer\n"); return; } - DPrintf("FPS timer set to %u ms\n", period); + DPrintf(DMSG_NOTIFY, "FPS timer set to %u ms\n", period); } } diff --git a/src/zscript/vm.h b/src/zscript/vm.h index 52b354028..0d45ad90a 100644 --- a/src/zscript/vm.h +++ b/src/zscript/vm.h @@ -156,6 +156,15 @@ enum ATAG_RNG, // pointer to FRandom }; +enum EVMAbortException +{ + X_READ_NIL, + X_WRITE_NIL, + X_TOO_MANY_TRIES, + X_ARRAY_OUT_OF_BOUNDS, + X_DIVISION_BY_ZERO, +}; + class VMFunction : public DObject { DECLARE_ABSTRACT_CLASS(VMFunction, DObject); diff --git a/src/zscript/vmexec.cpp b/src/zscript/vmexec.cpp index 070aeb30a..454f02d03 100644 --- a/src/zscript/vmexec.cpp +++ b/src/zscript/vmexec.cpp @@ -44,7 +44,7 @@ #define ASSERTKA(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstA) #define ASSERTKS(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstS) -#define THROW(x) +#define THROW(x) throw(EVMAbortException(x)) #define CMPJMP(test) \ if ((test) == (a & CMP_CHECK)) { \ @@ -54,14 +54,6 @@ pc += 1; \ } -enum -{ - X_READ_NIL, - X_WRITE_NIL, - X_TOO_MANY_TRIES, - X_ARRAY_OUT_OF_BOUNDS -}; - #define GETADDR(a,o,x) \ if (a == NULL) { THROW(x); } \ ptr = (VM_SBYTE *)a + o diff --git a/src/zscript/vmexec.h b/src/zscript/vmexec.h index 8232340a0..42a374030 100644 --- a/src/zscript/vmexec.h +++ b/src/zscript/vmexec.h @@ -786,27 +786,51 @@ begin: OP(DIV_RR): ASSERTD(a); ASSERTD(B); ASSERTD(C); + if (reg.d[C] == 0) + { + THROW(X_DIVISION_BY_ZERO); + } reg.d[a] = reg.d[B] / reg.d[C]; NEXTOP; OP(DIV_RK): ASSERTD(a); ASSERTD(B); ASSERTKD(C); + if (konstd[C] == 0) + { + THROW(X_DIVISION_BY_ZERO); + } reg.d[a] = reg.d[B] / konstd[C]; NEXTOP; OP(DIV_KR): ASSERTD(a); ASSERTKD(B); ASSERTD(C); + if (reg.d[C] == 0) + { + THROW(X_DIVISION_BY_ZERO); + } reg.d[a] = konstd[B] / reg.d[C]; NEXTOP; OP(MOD_RR): ASSERTD(a); ASSERTD(B); ASSERTD(C); + if (reg.d[C] == 0) + { + THROW(X_DIVISION_BY_ZERO); + } reg.d[a] = reg.d[B] % reg.d[C]; NEXTOP; OP(MOD_RK): ASSERTD(a); ASSERTD(B); ASSERTKD(C); + if (konstd[C] == 0) + { + THROW(X_DIVISION_BY_ZERO); + } reg.d[a] = reg.d[B] % konstd[C]; NEXTOP; OP(MOD_KR): ASSERTD(a); ASSERTKD(B); ASSERTD(C); + if (reg.d[C] == 0) + { + THROW(X_DIVISION_BY_ZERO); + } reg.d[a] = konstd[B] % reg.d[C]; NEXTOP; @@ -981,14 +1005,26 @@ begin: OP(DIVF_RR): ASSERTF(a); ASSERTF(B); ASSERTF(C); + if (reg.f[C] == 0.) + { + THROW(X_DIVISION_BY_ZERO); + } reg.f[a] = reg.f[B] / reg.f[C]; NEXTOP; OP(DIVF_RK): ASSERTF(a); ASSERTF(B); ASSERTKF(C); + if (konstf[C] == 0.) + { + THROW(X_DIVISION_BY_ZERO); + } reg.f[a] = reg.f[B] / konstf[C]; NEXTOP; OP(DIVF_KR): ASSERTF(a); ASSERTKF(B); ASSERTF(C); + if (reg.f[C] == 0.) + { + THROW(X_DIVISION_BY_ZERO); + } reg.f[a] = konstf[B] / reg.f[C]; NEXTOP; @@ -996,6 +1032,10 @@ begin: ASSERTF(a); ASSERTF(B); ASSERTF(C); fb = reg.f[B]; fc = reg.f[C]; Do_MODF: + if (fc == 0.) + { + THROW(X_DIVISION_BY_ZERO); + } reg.f[a] = luai_nummod(fb, fc); NEXTOP; OP(MODF_RK): diff --git a/src/zscript/vmframe.cpp b/src/zscript/vmframe.cpp index c7d9cb87d..29da0c5b9 100644 --- a/src/zscript/vmframe.cpp +++ b/src/zscript/vmframe.cpp @@ -407,6 +407,44 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur } throw; } + catch (EVMAbortException exception) + { + if (allocated) + { + PopFrame(); + } + if (trap != nullptr) + { + *trap = nullptr; + } + + Printf("VM execution aborted: "); + switch (exception) + { + case X_READ_NIL: + Printf("tried to read from address zero."); + break; + + case X_WRITE_NIL: + Printf("tried to write to address zero."); + break; + + case X_TOO_MANY_TRIES: + Printf("too many try-catch blocks."); + break; + + case X_ARRAY_OUT_OF_BOUNDS: + Printf("array access out of bounds."); + break; + + case X_DIVISION_BY_ZERO: + Printf("division by zero."); + break; + } + Printf("\n"); + + return -1; + } catch (...) { if (allocated) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index fa8932050..b8b805219 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -53,6 +53,7 @@ ACTOR Actor native //: Thinker native int CountProximity(class classname, float distance, int flags = 0, int ptr = AAPTR_DEFAULT); native float GetSpriteAngle(int ptr = AAPTR_DEFAULT); native float GetSpriteRotation(int ptr = AAPTR_DEFAULT); + native int GetMissileDamage(int mask, int add, int ptr = AAPTR_DEFAULT); // Action functions // Meh, MBF redundant functions. Only for DeHackEd support. @@ -253,7 +254,7 @@ ACTOR Actor native //: Thinker action native A_Blast(int flags = 0, float strength = 255, float radius = 255, float speed = 20, class blasteffect = "BlastEffect", sound blastsound = "BlastRadius"); action native A_RadiusThrust(int force = 128, int distance = -1, int flags = RTF_AFFECTSOURCE, int fullthrustdistance = 0); action native A_RadiusDamageSelf(int damage = 128, float distance = 128, int flags = 0, class flashtype = "None"); - action native A_Explode(int damage = -1, int distance = -1, int flags = XF_HURTSOURCE, bool alert = false, int fulldamagedistance = 0, int nails = 0, int naildamage = 10, class pufftype = "BulletPuff"); + action native int A_Explode(int damage = -1, int distance = -1, int flags = XF_HURTSOURCE, bool alert = false, int fulldamagedistance = 0, int nails = 0, int naildamage = 10, class pufftype = "BulletPuff", name damagetype = "none"); action native A_Stop(); action native A_Respawn(int flags = 1); action native A_BarrelDestroy(); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 30afd1064..2a6f1c483 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -185,6 +185,7 @@ const int MSF_DontHurt = 2; // Flags for A_Explode const int XF_HURTSOURCE = 1; const int XF_NOTMISSILE = 4; +const int XF_EXPLICITDAMAGETYPE = 1 << 3; // Flags for A_RadiusThrust const int RTF_AFFECTSOURCE = 1; diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index a2c3794ae..d964cbf75 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -397,6 +397,7 @@ D62DCA9EC226DE49108D5DD9271F7631 // Cheogsh 2 map04 E89CCC7E155F1032F693359CC219BE6C // hexen.wad map30 B9DFF13207EACAC675C71D82624D0007 // XtheaterIII map01 +6941BDC2F80C0FEBE34EFA23D5FB72B7 // sonic.wad map10 { DisablePushWindowCheck } @@ -430,6 +431,22 @@ C98F79709BD7E0E4C19026AB9575EC6F // cc-cod.zip:codlev.wad map07 maskedmidtex } +7B82B12A6990E09553B12FDB4E3824A0 // hti.wad map01 +{ + teleport +} + +8570AA0D6737C0A19DB66767764F157F // sonic.wad map04 +{ + noslopeid +} + +05AA32F1D2220A462DCDA245ED22B94B // sonic.wad map09 +{ + polyobj +} + + D7F6E9F08C39A17026349A04F8C0B0BE // Return to Hadron, e1m9 19D03FFC875589E21EDBB7AB74EF4AEF // Return to Hadron, e1m9, 2016.01.03 update { diff --git a/wadsrc/static/language.eng b/wadsrc/static/language.eng index ed0f95a9a..ca27bf80a 100644 --- a/wadsrc/static/language.eng +++ b/wadsrc/static/language.eng @@ -108,5 +108,3 @@ CMPTMNU_SECTORSOUNDS = "Sector sounds use centre as source"; OPTVAL_MAPDEFINEDCOLORSONLY = "Map defined colours only"; C_GRAY = "\ccgrey"; C_DARKGRAY = "\cudark grey"; -DSPLYMNU_MOVEBOB = "View bob amount while moving"; -DSPLYMNU_STILLBOB = "View bob amount while not moving"; diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 81363e099..91459673a 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1804,11 +1804,14 @@ DSPLYMNU_QUAKEINTENSITY = "Earthquake shake intensity"; DSPLYMNU_NOMONSTERINTERPOLATION = "Interpolate monster movement"; DSPLYMNU_MENUDIM = "Menu dim"; DSPLYMNU_DIMCOLOR = "Dim color"; +DSPLYMNU_MOVEBOB = "View bob amount while moving"; +DSPLYMNU_STILLBOB = "View bob amount while not moving"; // HUD Options HUDMNU_TITLE = "HUD Options"; HUDMNU_ALTHUD = "Alternative HUD"; HUDMNU_MESSAGE = "Message Options"; +HUDMNU_UISCALE = "User interface scale"; HUDMNU_CROSSHAIR = "Default Crosshair"; HUDMNU_FORCECROSSHAIR = "Force default crosshair"; HUDMNU_GROWCROSSHAIR = "Grow crosshair when picking up items"; @@ -1948,6 +1951,7 @@ MSGMNU_SHOWMESSAGES = "Show messages"; MSGMNU_SHOWOBITUARIES = "Show obituaries"; MSGMNU_SHOWSECRETS = "Show secret notifications"; MSGMNU_SCALETEXT = "Scale text in high res"; +MSGMNU_SCALECONSOLE = "Scale console"; MSGMNU_MESSAGELEVEL = "Minimum message level"; MSGMNU_CENTERMESSAGES = "Center messages"; MSGMNU_MESSAGECOLORS = "Message Colors"; @@ -1959,6 +1963,7 @@ MSGMNU_TEAMMESSAGES = "Team Messages"; MSGMNU_CENTEREDMESSAGES = "Centered Messages"; MSGMNU_SCREENSHOTMESSAGES = "Screenshot messages"; MSGMNU_LONGSAVEMESSAGES = "Detailed save messages"; +MSGMNU_DEVELOPER = "Developer message mode"; // Scoreboard Options SCRBRDMNU_TITLE = "SCOREBOARD OPTIONS"; @@ -2073,6 +2078,8 @@ CMPTMNU_SILENTINSTANTFLOORS = "Inst. moving floors are not silent"; CMPTMNU_SECTORSOUNDS = "Sector sounds use center as source"; CMPTMNU_SOUNDCUTOFF = "Sounds stop when actor vanishes"; CMPTMNU_SOUNDTARGET = "Use original sound target handling"; +CMPTMNU_TELEPORT = "Scripted teleports don't trigger sector actions"; +CMPTMNU_PUSHWINDOW = "Non-blocking lines can be pushed"; // Sound Options SNDMNU_TITLE = "SOUND OPTIONS"; @@ -2236,6 +2243,7 @@ OPTVAL_ANIMATED = "Animated"; OPTVAL_ROTATED = "Rotated"; OPTVAL_MAPDEFINEDCOLORSONLY = "Map defined colors only"; OPTVAL_DOUBLE = "Double"; +OPTVAL_TRIPLE = "Triple"; OPTVAL_QUADRUPLE = "Quadruple"; OPTVAL_ITEMPICKUP = "Item Pickup"; OPTVAL_OBITUARIES = "Obituaries"; @@ -2287,6 +2295,10 @@ OPTVAL_SINC = "Sinc"; OPTVAL_NOTEONOFFONLY = "Note on/off only"; OPTVAL_FULLRAMPING = "Full ramping"; OPTVAL_ALLUNACKNOWLEDGED = "All unacknowledged"; +OPTVAL_ERRORS = "Errors"; +OPTVAL_WARNINGS = "Warnings"; +OPTVAL_NOTIFICATIONS = "Notifications"; +OPTVAL_EVERYTHING = "Everything"; // Colors C_BRICK = "\cabrick"; C_TAN = "\cbtan"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index d44a99d5a..0202a40e6 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -759,6 +759,8 @@ OptionMenu "HUDOptions" Submenu "$HUDMNU_ALTHUD", "AltHudOptions" Submenu "$HUDMNU_MESSAGE", "MessageOptions" StaticText " " + Slider "$HUDMNU_UISCALE", "uiscale", 0.0, 8.0, 1.0, 0 + StaticText " " Option "$HUDMNU_CROSSHAIR", "crosshair", "Crosshairs" Option "$HUDMNU_FORCECROSSHAIR", "crosshairforce", "OnOff" Option "$HUDMNU_GROWCROSSHAIR", "crosshairgrow", "OnOff" @@ -793,6 +795,7 @@ OptionValue "AMCoordinates" OptionValue "AltHUDScale" { 0, "$OPTVAL_OFF" + 4, "$OPTVAL_ON" 1, "$OPTVAL_SCALETO640X400" 2, "$OPTVAL_PIXELDOUBLE" 3, "$OPTVAL_PIXELQUADRUPLE" @@ -1115,6 +1118,15 @@ OptionValue ScaleValues 3, "$OPTVAL_QUADRUPLE" } +OptionValue ConsoleScaleValues +{ + 1, "$OPTVAL_OFF" + 0, "$OPTVAL_ON" + 2, "$OPTVAL_DOUBLE" + 3, "$OPTVAL_TRIPLE" + 4, "$OPTVAL_QUADRUPLE" +} + OptionValue MessageLevels { 0.0, "$OPTVAL_ITEMPICKUP" @@ -1122,6 +1134,15 @@ OptionValue MessageLevels 2.0, "$OPTVAL_CRITICALMESSAGES" } +OptionValue DevMessageLevels +{ + 0, "$OPTVAL_OFF" + 1, "$OPTVAL_ERRORS" + 2, "$OPTVAL_WARNINGS" + 3, "$OPTVAL_NOTIFICATIONS" + 4, "$OPTVAL_EVERYTHING" +} + OptionMenu MessageOptions { Title "$MSGMNU_TITLE" @@ -1129,7 +1150,9 @@ OptionMenu MessageOptions Option "$MSGMNU_SHOWOBITUARIES", "show_obituaries", "OnOff" Option "$MSGMNU_SHOWSECRETS", "cl_showsecretmessage", "OnOff" Option "$MSGMNU_SCALETEXT", "con_scaletext", "ScaleValues" + Option "$MSGMNU_SCALECONSOLE", "con_scale", "ConsoleScaleValues" Option "$MSGMNU_MESSAGELEVEL", "msg", "MessageLevels" + Option "$MSGMNU_DEVELOPER", "developer", "DevMessageLevels" Option "$MSGMNU_CENTERMESSAGES", "con_centernotify", "OnOff" StaticText " " StaticText "$MSGMNU_MESSAGECOLORS", 1 @@ -1324,6 +1347,8 @@ OptionMenu "CompatibilityOptions" Option "$CMPTMNU_FLOORMOVE", "compat_floormove", "YesNo" Option "$CMPTMNU_POINTONLINE", "compat_pointonline", "YesNo" Option "$CMPTMNU_MULTIEXIT", "compat_multiexit", "YesNo" + Option "$CMPTMNU_TELEPORT", "compat_teleport", "YesNo" + Option "$CMPTMNU_PUSHWINDOW", "compat_pushwindow", "YesNo" StaticText " " StaticText "$CMPTMNU_PHYSICSBEHAVIOR",1