diff options
author | Ian C <ianc@noddybox.co.uk> | 2011-06-09 13:46:28 +0000 |
---|---|---|
committer | Ian C <ianc@noddybox.co.uk> | 2011-06-09 13:46:28 +0000 |
commit | a9022b5972dc49d86f617a27940fafe9c4d0e7e7 (patch) | |
tree | 61405aa4ade91ed1057f863ddf118ceb38e14f8e |
Initial import of (very old) vidoom sources.
-rw-r--r-- | LICENSE | 341 | ||||
-rw-r--r-- | Makefile | 334 | ||||
-rw-r--r-- | README | 29 | ||||
-rw-r--r-- | base.ini | 154 | ||||
-rw-r--r-- | config.h | 109 | ||||
-rw-r--r-- | debug.c | 231 | ||||
-rw-r--r-- | debug.h | 66 | ||||
-rw-r--r-- | djgpp/file.c | 118 | ||||
-rw-r--r-- | djgpp/gfx.c | 559 | ||||
-rw-r--r-- | djgpp/install | 31 | ||||
-rw-r--r-- | djgpp/install.c | 119 | ||||
-rw-r--r-- | djgpp/main.c | 34 | ||||
-rw-r--r-- | djgpp/mem.c | 262 | ||||
-rw-r--r-- | djgpp/platgui.c | 4235 | ||||
-rw-r--r-- | djgpp/runcmd.c | 143 | ||||
-rw-r--r-- | djgpp/vstring.c | 48 | ||||
-rw-r--r-- | doc/bugs.htm | 40 | ||||
-rw-r--r-- | doc/building.htm | 88 | ||||
-rw-r--r-- | doc/changelog.htm | 64 | ||||
-rw-r--r-- | doc/djgpp.htm | 58 | ||||
-rwxr-xr-x | doc/ed_ex1.gif | bin | 0 -> 530 bytes | |||
-rwxr-xr-x | doc/ed_ex2.gif | bin | 0 -> 596 bytes | |||
-rwxr-xr-x | doc/ed_ex3.gif | bin | 0 -> 702 bytes | |||
-rwxr-xr-x | doc/ed_l_hex.gif | bin | 0 -> 2499 bytes | |||
-rwxr-xr-x | doc/ed_line.gif | bin | 0 -> 15339 bytes | |||
-rwxr-xr-x | doc/ed_lninf.gif | bin | 0 -> 2275 bytes | |||
-rwxr-xr-x | doc/ed_merge.gif | bin | 0 -> 19280 bytes | |||
-rwxr-xr-x | doc/ed_multi.gif | bin | 0 -> 19790 bytes | |||
-rwxr-xr-x | doc/ed_sect.gif | bin | 0 -> 17537 bytes | |||
-rwxr-xr-x | doc/ed_step.gif | bin | 0 -> 15527 bytes | |||
-rwxr-xr-x | doc/ed_t_hx1.gif | bin | 0 -> 2383 bytes | |||
-rwxr-xr-x | doc/ed_t_hx2.gif | bin | 0 -> 2177 bytes | |||
-rwxr-xr-x | doc/ed_thing.gif | bin | 0 -> 17621 bytes | |||
-rwxr-xr-x | doc/ed_vert.gif | bin | 0 -> 13192 bytes | |||
-rwxr-xr-x | doc/editing.htm | 1603 | ||||
-rw-r--r-- | doc/glossary.htm | 152 | ||||
-rw-r--r-- | doc/index.htm | 45 | ||||
-rw-r--r-- | doc/license.htm | 360 | ||||
-rw-r--r-- | doc/mainmenu.htm | 177 | ||||
-rw-r--r-- | doc/overview.htm | 1164 | ||||
-rw-r--r-- | doc/porting.htm | 1452 | ||||
-rw-r--r-- | doc/sys.htm | 24 | ||||
-rw-r--r-- | doc/thanks.htm | 91 | ||||
-rw-r--r-- | doom.cfg | 571 | ||||
-rw-r--r-- | doom2.cfg | 77 | ||||
-rw-r--r-- | edit.c | 751 | ||||
-rw-r--r-- | edit.h | 70 | ||||
-rw-r--r-- | edit3d.c | 474 | ||||
-rw-r--r-- | editcord.c | 225 | ||||
-rw-r--r-- | editcrse.c | 397 | ||||
-rw-r--r-- | editdraw.c | 349 | ||||
-rw-r--r-- | editevnt.c | 1079 | ||||
-rw-r--r-- | editgui.c | 519 | ||||
-rw-r--r-- | editilst.c | 106 | ||||
-rw-r--r-- | editline.c | 3812 | ||||
-rw-r--r-- | editlump.c | 330 | ||||
-rw-r--r-- | editmrg.c | 564 | ||||
-rw-r--r-- | editmult.c | 708 | ||||
-rw-r--r-- | editsect.c | 1735 | ||||
-rw-r--r-- | editsel.c | 133 | ||||
-rw-r--r-- | editsrot.c | 76 | ||||
-rw-r--r-- | editthng.c | 1126 | ||||
-rw-r--r-- | editvar.c | 492 | ||||
-rw-r--r-- | editvar.h | 701 | ||||
-rw-r--r-- | editvert.c | 834 | ||||
-rw-r--r-- | file.h | 58 | ||||
-rw-r--r-- | flags.c | 203 | ||||
-rw-r--r-- | flags.h | 66 | ||||
-rw-r--r-- | gdb.ini | 1 | ||||
-rw-r--r-- | genlines.c | 460 | ||||
-rw-r--r-- | genlines.h | 86 | ||||
-rw-r--r-- | gensect.c | 423 | ||||
-rw-r--r-- | gensect.h | 80 | ||||
-rw-r--r-- | gfx.h | 319 | ||||
-rw-r--r-- | gfxtest.c | 862 | ||||
-rw-r--r-- | globals.c | 1375 | ||||
-rw-r--r-- | globals.h | 232 | ||||
-rw-r--r-- | gui.c | 337 | ||||
-rw-r--r-- | gui.h | 84 | ||||
-rw-r--r-- | ini.c | 532 | ||||
-rw-r--r-- | ini.h | 106 | ||||
-rw-r--r-- | linedefs.c | 316 | ||||
-rw-r--r-- | linedefs.h | 84 | ||||
-rw-r--r-- | linux/file.c | 103 | ||||
-rw-r--r-- | linux/gfx.c | 1013 | ||||
-rw-r--r-- | linux/main.c | 33 | ||||
-rw-r--r-- | linux/mem.c | 107 | ||||
-rw-r--r-- | linux/platgui.c | 3703 | ||||
-rw-r--r-- | linux/runcmd.c | 82 | ||||
-rw-r--r-- | linux/vstring.c | 48 | ||||
-rw-r--r-- | list.c | 288 | ||||
-rw-r--r-- | list.h | 104 | ||||
-rw-r--r-- | make/cygwin-xfree.cfg | 41 | ||||
-rw-r--r-- | make/djgpp.cfg | 41 | ||||
-rw-r--r-- | make/linux.cfg | 41 | ||||
-rw-r--r-- | map.c | 162 | ||||
-rw-r--r-- | map.h | 86 | ||||
-rw-r--r-- | mem.h | 79 | ||||
-rw-r--r-- | names.c | 256 | ||||
-rw-r--r-- | names.h | 43 | ||||
-rw-r--r-- | platgui.h | 258 | ||||
-rw-r--r-- | runcmd.h | 57 | ||||
-rw-r--r-- | sectors.c | 319 | ||||
-rw-r--r-- | sectors.h | 95 | ||||
-rw-r--r-- | specials.c | 319 | ||||
-rw-r--r-- | specials.h | 74 | ||||
-rw-r--r-- | texture.c | 592 | ||||
-rw-r--r-- | texture.h | 69 | ||||
-rw-r--r-- | things.c | 288 | ||||
-rw-r--r-- | things.h | 72 | ||||
-rw-r--r-- | todo | 4 | ||||
-rw-r--r-- | util.c | 253 | ||||
-rw-r--r-- | util.h | 132 | ||||
-rw-r--r-- | vidoom.c | 892 | ||||
-rw-r--r-- | vidoom.h | 44 | ||||
-rw-r--r-- | vidoom.ini | 186 | ||||
-rw-r--r-- | vstring.h | 48 | ||||
-rw-r--r-- | wad.c | 1144 | ||||
-rw-r--r-- | wad.h | 294 | ||||
-rw-r--r-- | waddir.c | 76 | ||||
-rwxr-xr-x | wadtest | bin | 0 -> 703835 bytes | |||
-rw-r--r-- | wadtest.c | 79 | ||||
-rw-r--r-- | zdoom.cfg | 719 |
123 files changed, 45128 insertions, 0 deletions
@@ -0,0 +1,341 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c048fe1 --- /dev/null +++ b/Makefile @@ -0,0 +1,334 @@ +# viDOOM - level editor for DOOM +# +# Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# ------------------------------------------------------------------------- +# + +# +# START OF CONFIGURATION PART +# +# 1. The platform to compile for. Currently supported ones are: +# djgpp (Currently unmaintained) +# linux +# cygwin-xfree (Same as linux) +# +MAKEPLAT=djgpp + + +# 2. Set to the directory where you want to install viDOOM. Note this is not +# used in the default make - a 'make install' must be issued to activate +# this. +# +INSTALLDIR=C:/viDOOM + + +# 3. The directory seperator for this machine. This is unquoted (unlike the +# seperator in the config file) as this is for the makefiles own use. +# +MKDS=/ + + +# +# END OF CONFIGURATION PART +# + + + +# Shouldn't be any need to edit past here +# +include make$(MKDS)$(MAKEPLAT).cfg + + +# Uncomment to build debug version +# +DEBUG=$(DEFINEFLAG)VIDOOM_DEBUG + + +CFLAGS= $(INCFLAG). $(EXTRACF) $(DEFINEFLAG)TRACEFORM='$(TRACEFORM)' \ + $(DEBUG) $(DEFINEFLAG)PLATFORM=$(PLATFORM) \ + $(DEFINEFLAG)DIRSEP='$(DIRSEP)' + +VIDOOM= vidoom +WADDIR= waddir +GFXTEST= gfxtest +WADTEST= wadtest + +COMMON_OBJ= $(PLATFORM)$(MKDS)main$(OBJ_EXT) \ + $(PLATFORM)$(MKDS)file$(OBJ_EXT) \ + $(PLATFORM)$(MKDS)gfx$(OBJ_EXT) \ + $(PLATFORM)$(MKDS)mem$(OBJ_EXT) \ + $(PLATFORM)$(MKDS)platgui$(OBJ_EXT) \ + $(PLATFORM)$(MKDS)runcmd$(OBJ_EXT) \ + $(PLATFORM)$(MKDS)vstring$(OBJ_EXT) \ + ini$(OBJ_EXT) wad$(OBJ_EXT) util$(OBJ_EXT) \ + list$(OBJ_EXT) map$(OBJ_EXT) gui$(OBJ_EXT) debug$(OBJ_EXT) \ + globals$(OBJ_EXT) texture$(OBJ_EXT) things$(OBJ_EXT) \ + linedefs$(OBJ_EXT) sectors$(OBJ_EXT) edit$(OBJ_EXT) \ + editcord$(OBJ_EXT) editdraw$(OBJ_EXT) editgui$(OBJ_EXT) \ + editevnt$(OBJ_EXT) editline$(OBJ_EXT) editmult$(OBJ_EXT) \ + editsect$(OBJ_EXT) editsel$(OBJ_EXT) editthng$(OBJ_EXT) \ + editvar$(OBJ_EXT) editvert$(OBJ_EXT) editsrot$(OBJ_EXT) \ + editcrse$(OBJ_EXT) editilst$(OBJ_EXT) editmrg$(OBJ_EXT) \ + edit3d$(OBJ_EXT) genlines$(OBJ_EXT) gensect$(OBJ_EXT) \ + names$(OBJ_EXT) editlump$(OBJ_EXT) specials$(OBJ_EXT) \ + flags$(OBJ_EXT) + +ALL_HEADERS= vidoom.h config.h file.h gfx.h mem.h map.h runcmd.h \ + ini.h wad.h list.h edit.h platgui.h gui.h debug.h globals.h \ + texture.h things.h linedefs.h editvar.h genlines.h gensect.h \ + names.h specials.h flags.h + +VIDOOM_OBJ= vidoom$(OBJ_EXT) $(COMMON_OBJ) +WADDIR_OBJ= waddir$(OBJ_EXT) $(COMMON_OBJ) +GFXTEST_OBJ= gfxtest$(OBJ_EXT) $(COMMON_OBJ) +WADTEST_OBJ= $(WADTEST)$(OBJ_EXT) $(COMMON_OBJ) + + + +$(VIDOOM)$(EXE_EXT):$(VIDOOM_OBJ) + $(LD) $(EXTRALF) $(EXEFLAG) $(VIDOOM)$(EXE_EXT) \ + $(VIDOOM_OBJ) $(LIBS) $(MATHLIB) + +$(WADDIR)$(EXE_EXT):$(WADDIR_OBJ) + $(LD) $(EXTRALF) $(EXEFLAG) $(WADDIR)$(EXE_EXT) \ + $(WADDIR_OBJ) $(LIBS) $(MATHLIB) + +$(GFXTEST)$(EXE_EXT):$(GFXTEST_OBJ) + $(LD) $(EXTRALF) $(EXEFLAG) $(GFXTEST)$(EXE_EXT) $(GFXTEST_OBJ) \ + $(LIBS) $(MATHLIB) + +$(WADTEST)$(EXE_EXT):$(WADTEST_OBJ) + $(LD) $(EXTRALF) $(EXEFLAG) $(WADTEST)$(EXE_EXT) $(WADTEST_OBJ) \ + $(LIBS) $(MATHLIB) + + +all: $(VIDOOM)$(EXE_EXT) $(WADDIR)$(EXE_EXT) \ + $(GFXTEST)$(EXE_EXT) $(WADTEST)$(EXE_EXT) + + +$(PLATFORM)$(MKDS)main$(OBJ_EXT): $(PLATFORM)$(MKDS)main.c $(ALL_HEADERS) + cd $(PLATFORM); $(CC) $(INCFLAG).. $(CFLAGS) $(OBJFLAG) main.c ; cd .. + +$(PLATFORM)$(MKDS)file$(OBJ_EXT): $(PLATFORM)$(MKDS)file.c $(ALL_HEADERS) + cd $(PLATFORM); $(CC) $(INCFLAG).. $(CFLAGS) $(OBJFLAG) file.c ; cd .. + +$(PLATFORM)$(MKDS)gfx$(OBJ_EXT): $(PLATFORM)$(MKDS)gfx.c $(ALL_HEADERS) + cd $(PLATFORM); $(CC) $(INCFLAG).. $(CFLAGS) $(OBJFLAG) gfx.c ; cd .. + +$(PLATFORM)$(MKDS)mem$(OBJ_EXT): $(PLATFORM)$(MKDS)mem.c $(ALL_HEADERS) + cd $(PLATFORM); $(CC) $(INCFLAG).. $(CFLAGS) $(OBJFLAG) mem.c ; cd .. + +$(PLATFORM)$(MKDS)platgui$(OBJ_EXT): $(PLATFORM)$(MKDS)platgui.c $(ALL_HEADERS) + cd $(PLATFORM); $(CC) $(INCFLAG).. $(CFLAGS) $(OBJFLAG) platgui.c ; cd .. + +$(PLATFORM)$(MKDS)runcmd$(OBJ_EXT): $(PLATFORM)$(MKDS)runcmd.c $(ALL_HEADERS) + cd $(PLATFORM); $(CC) $(INCFLAG).. $(CFLAGS) $(OBJFLAG) runcmd.c ; cd .. + +$(PLATFORM)$(MKDS)vstring$(OBJ_EXT): $(PLATFORM)$(MKDS)vstring.c $(ALL_HEADERS) + cd $(PLATFORM); $(CC) $(INCFLAG).. $(CFLAGS) $(OBJFLAG) vstring.c ; cd .. + + +debug$(OBJ_EXT): debug.c config.h debug.h vstring.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +edit$(OBJ_EXT): edit.c config.h debug.h vstring.h globals.h edit.h wad.h map.h \ + list.h editvar.h things.h gfx.h platgui.h linedefs.h gui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +edit3d$(OBJ_EXT): edit3d.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h gui.h \ + mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editcord$(OBJ_EXT): editcord.c config.h debug.h vstring.h globals.h texture.h \ + gfx.h platgui.h editvar.h things.h linedefs.h wad.h map.h \ + list.h gui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editcrse$(OBJ_EXT): editcrse.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h \ + gui.h mem.h sectors.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editdraw$(OBJ_EXT): editdraw.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h \ + gui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editevnt$(OBJ_EXT): editevnt.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h \ + gui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editgui$(OBJ_EXT): editgui.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h \ + gui.h mem.h texture.h sectors.h specials.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editilst$(OBJ_EXT): editilst.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h \ + gui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editline$(OBJ_EXT): editline.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h \ + gui.h mem.h sectors.h texture.h genlines.h specials.h \ + flags.h util.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editlump$(OBJ_EXT): editlump.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h \ + gui.h mem.h runcmd.h file.h util.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editmrg$(OBJ_EXT): editmrg.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h \ + gui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editmult$(OBJ_EXT): editmult.c config.h debug.h vstring.h globals.h util.h \ + editvar.h things.h gfx.h platgui.h linedefs.h wad.h map.h \ + list.h gui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editsect$(OBJ_EXT): editsect.c config.h debug.h vstring.h globals.h sectors.h \ + wad.h map.h list.h gfx.h platgui.h editvar.h things.h \ + linedefs.h gui.h mem.h gensect.h util.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editsel$(OBJ_EXT): editsel.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h \ + gui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editsrot$(OBJ_EXT): editsrot.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h \ + gui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editthng$(OBJ_EXT): editthng.c config.h debug.h vstring.h globals.h specials.h \ + flags.h util.h editvar.h things.h gfx.h platgui.h \ + linedefs.h wad.h map.h list.h gui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editvar$(OBJ_EXT): editvar.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h \ + gui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +editvert$(OBJ_EXT): editvert.c config.h debug.h vstring.h globals.h editvar.h \ + things.h gfx.h platgui.h linedefs.h wad.h map.h list.h \ + gui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +flags$(OBJ_EXT): flags.c config.h debug.h vstring.h globals.h platgui.h gfx.h \ + flags.h mem.h list.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +genlines$(OBJ_EXT): genlines.c config.h debug.h vstring.h globals.h platgui.h \ + gfx.h genlines.h mem.h map.h linedefs.h wad.h list.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +gensect$(OBJ_EXT): gensect.c config.h debug.h vstring.h globals.h platgui.h \ + gfx.h gensect.h mem.h map.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +globals$(OBJ_EXT): globals.c config.h debug.h vstring.h globals.h ini.h gfx.h \ + mem.h texture.h platgui.h things.h linedefs.h wad.h map.h \ + list.h sectors.h genlines.h gensect.h specials.h flags.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +gui$(OBJ_EXT): gui.c config.h debug.h vstring.h globals.h gui.h gfx.h \ + platgui.h mem.h ini.h names.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +ini$(OBJ_EXT): ini.c config.h debug.h vstring.h ini.h mem.h file.h gfx.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +linedefs$(OBJ_EXT): linedefs.c config.h debug.h vstring.h globals.h platgui.h \ + gfx.h linedefs.h wad.h map.h list.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +list$(OBJ_EXT): list.c config.h debug.h vstring.h list.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +map$(OBJ_EXT): map.c config.h debug.h vstring.h map.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +names$(OBJ_EXT): names.c config.h debug.h vstring.h names.h globals.h file.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +sectors$(OBJ_EXT): sectors.c config.h debug.h vstring.h globals.h sectors.h \ + wad.h map.h list.h gfx.h platgui.h mem.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +specials$(OBJ_EXT): specials.c config.h debug.h vstring.h globals.h platgui.h \ + gfx.h gui.h specials.h mem.h list.h map.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +texture$(OBJ_EXT): texture.c config.h debug.h vstring.h globals.h texture.h \ + gfx.h platgui.h wad.h map.h list.h mem.h gui.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +things$(OBJ_EXT): things.c config.h debug.h vstring.h globals.h platgui.h \ + gfx.h things.h mem.h list.h map.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +util$(OBJ_EXT): util.c config.h debug.h vstring.h mem.h util.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + +wad$(OBJ_EXT): wad.c config.h debug.h vstring.h globals.h wad.h map.h list.h \ + gfx.h mem.h file.h util.h + $(CC) $(CFLAGS) $(OBJFLAG) $< + + +$(VIDOOM)$(OBJ_EXT): $(VIDOOM).c $(ALL_HEADERS) + $(CC) $(CFLAGS) $(OBJFLAG) $(VIDOOM).c + +$(WADDIR)$(OBJ_EXT): $(WADDIR).c $(ALL_HEADERS) + $(CC) $(CFLAGS) $(OBJFLAG) $(WADDIR).c + +$(GFXTEST)(OBJ_EXT): $(GFXTEST).c $(ALL_HEADERS) + $(CC) $(CFLAGS) $(OBJFLAG) $(GFXTEST).c + +$(WADTEST)(OBJ_EXT): $(WADTEST).c $(ALL_HEADERS) + $(CC) $(CFLAGS) $(OBJFLAG) $(WADTEST).c + + +# Rule for installation +# +install: $(VIDOOM)$(EXE_EXT) FORCE + cd $(PLATFORM) ; $(MAKEINSTALL) + +FORCE: + +# Rule for cleaning up +# +clean: + $(RMCMD) $(COMMON_OBJ) + $(RMCMD) vidoom$(OBJ_EXT) + $(RMCMD) waddir$(OBJ_EXT) + $(RMCMD) gfxtest$(OBJ_EXT) + $(RMCMD) vidoom$(EXE_EXT) + $(RMCMD) waddir$(EXE_EXT) + $(RMCMD) gfxtest$(EXE_EXT) + +# +# $Id: Makefile,v 1.48 2002/09/05 20:00:47 ianc Exp ianc $ +# +# END OF FILE @@ -0,0 +1,29 @@ + viDOOM v0.03 + + +viDOOM is released as Free Software. Please see LICENSE for conditions. + + + +To build viDOOM: + +1. Edit makfile. Follow the instructions for configuration at the top + of the makefile. + +2. Type 'make'. + + + +To install viDOOM: + +1. Type 'make install'. + +2. In the directory into which viDOOM has been installed, edit vidoom.ini + to set the paths to various IWAD files. + See doc/overview.htm for details. + + + +-------------------------------------------------------------------------- + +$Id: README,v 1.5 2000/09/24 22:50:28 dosuser Exp dosuser $ diff --git a/base.ini b/base.ini new file mode 100644 index 0000000..1cabe0f --- /dev/null +++ b/base.ini @@ -0,0 +1,154 @@ +# viDOOM - level editor for DOOM +# +# Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# +# viDOOM INI file +# +# $Id: base.ini,v 1.43 2002/06/08 22:29:05 ianc Exp ianc $ +# + +[Game] +game=doom +ask=yes + +[Editor] +ask_middle_on_2sided=no +auto_block_linedefs=yes +bright=1.00 +clear_on_menu=no +clear_on_move=no +default_ceiling_height=256 +default_edit_mode=sector +default_floor_height=0 +default_light_level=200 +default_scale=5 +grid=yes +grid_lock=yes +grid_size=64 +hover_select=add +insert_select=add +left_click_move=yes +linedef_select=2 +merge_linedef=ask +new_2sided_select=ask +sector_move=all +show_full_linedef_info=no +tag_highlight=yes +vertex_radius=8 +width=640 +height=480 + +[Check LINEDEF] +assume_yes=no +check_1side_lower=yes +check_1side_middle=yes +check_1side_upper=yes +check_2side_lower=yes +check_2side_middle=yes +check_2side_same_sector=yes +check_2side_upper=yes + +[viDOOM] +auto_loadmap=MAP01,E1M1 +initial_empty_map=no +load_flats=yes +load_sprites=yes +load_textures=yes +map_clear_warning=yes +map_exit_warning=yes +overwrite_warning=yes +show_titlepic=yes +sort_flat_names=yes +sort_texture_names=yes + +[Node Builder] +always_view_output=yes +command=bsp.exe +ignore=struct +in_before_out=yes +infile=% +outfile=-o % +use=no + +[ACS] +always_view_output=yes +command=acc.exe +dir=c:\acc +script=vidoom.acs +object=vidoom.o +args=%S %O + + +[GUI] +high=0xd2d2d2 +mid=0xb4b4b4 +low=0x8c8c8c +text=0xffffff +shadow=0x000000 +bold=0x000000 + + +[Doom] +iwad=c:\doom\doom.wad +pwad_dir=c:\wads\ +preload= +level_style=doom +vidoom_config=doom.cfg + +[Ultimate Doom] +iwad=c:\doom\ultdoom.wad +pwad_dir=c:\wads\ +preload= +level_style=ultimate doom +vidoom_config=doom.cfg + +[Doom 2] +iwad=c:\doom\doom2.wad +pwad_dir=c:\wads\ +preload= +level_style=doom 2 +vidoom_config=doom2.cfg + +[TNT:Evilution] +iwad=c:\doom\tnt.wad +pwad_dir=c:\wads\ +preload= +level_style=doom 2 +vidoom_config=doom2.cfg + +[Plutonia Experiment] +iwad=c:\doom\plutonia.wad +pwad_dir=c:\wads\ +preload= +level_style=doom 2 +vidoom_config=doom2.cfg + +[ZDoom] +iwad=c:\doom\doom2.wad +mapinfo_lump=yes +create_hexen=ask +pwad_dir=c:\wads\ +preload= +level_style=doom 2 +vidoom_config=doom2.cfg + + +# Platform dependent info +# +[linux] +edit= diff --git a/config.h b/config.h new file mode 100644 index 0000000..c1c4669 --- /dev/null +++ b/config.h @@ -0,0 +1,109 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Global config and macros + + $Id$ + +*/ + +#ifndef VIDOOM_CONFIG_H + +#define VIDOOM_CONFIG_H + +/* Usual constants +*/ +#include <stdlib.h> +#include <limits.h> +#include <string.h> + + +/* Global viDOOM includes +*/ +#include "debug.h" +#include "vstring.h" + + +/* Version +*/ +#define VIDOOMVER "0.03" +#define VIDOOMRELEASE "(Unsupported development version)" + + +/* Basic, global machine types +*/ +typedef unsigned char Byte; +typedef unsigned short Word; +typedef unsigned short UShort; +typedef signed short Short; +typedef unsigned long Long; + + +/* True/False +*/ +#ifndef FALSE +# define FALSE 0 +#endif + +#ifndef TRUE +# define TRUE 1 +#endif + + +/* MIN,MAX,ABS and SGN +*/ +#ifndef MIN +# define MIN(a,b) ((a)<(b) ? (a):(b)) +#endif + +#ifndef MAX +# define MAX(a,b) ((a)>(b) ? (a):(b)) +#endif + +#ifndef ABS +# define ABS(a) ((a)<0 ? -(a):(a)) +#endif + +#ifndef SGN +# define SGN(a) ((a)<0 ? -1:(a)>0 ? 1:0) +#endif + + +/* String equalities +*/ +#define STREQ(a,b) (!StrCaseCmp((a),(b))) +#define STRNEQ(a,b) (!StrNCaseCmp((a),(b),strlen(a))) + + +/* String to integer +*/ +#define ATOI(s) ((int)strtol((s),NULL,0)) + + +/* Max path length +*/ +#ifndef PATH_MAX +# define PATH_MAX 1024 +#endif + + +#endif @@ -0,0 +1,231 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Debug output + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" + +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> + +#include "debug.h" + +/* Set this true if you want to use stderr for debug output +*/ +static int use_stderr=TRUE; + +static FILE *trace_fp=NULL; + +void VIDOOM_Debug(char *fmt,...) +{ + static char last[512]=""; + static char this[512]=""; + static int rep=0; + int f; + va_list va; + + if (!trace_fp) + { + if (use_stderr) + trace_fp=stderr; + else + { + trace_fp=fopen("trace.out","w"); + setbuf(trace_fp,NULL); + } + } + + if (trace_fp) + { + va_start(va,fmt); + vsprintf(this,fmt,va); + va_end(va); + + if (strcmp(last,this)==0) + { + rep++; + if (rep==3) + fprintf(trace_fp,"DEBUG: *** Repeating...\n"); + } + else + { + if (rep) + { + if (rep>2) + fprintf(trace_fp,"DEBUG: *** Repeated %d times\n",rep); + else + for(f=0;f<rep;f++) + fprintf(trace_fp,"DEBUG: %s",last); + rep=0; + } + + fprintf(trace_fp,"DEBUG: %s",this); + strcpy(last,this); + } + } +} + + +void VIDOOM_Trace(char *fmt,...) +{ +#ifdef VIDOOM_DEBUG + static int ok=TRUE; + static int check=FALSE; + static char last[512]=""; + static char this[512]=""; + static int rep=0; + int f; + va_list va; + + if (!ok) + return; + + if (!check) + { + check=TRUE; + + if ((getenv("VIDOOM_TRACE"))&&(!strcmp(getenv("VIDOOM_TRACE"),"Y"))) + ok=TRUE; + else + { + ok=FALSE; + return; + } + } + + if (!trace_fp) + { + if (use_stderr) + trace_fp=stderr; + else + { + trace_fp=fopen("trace.out","w"); + setbuf(trace_fp,NULL); + } + } + + if (trace_fp) + { + va_start(va,fmt); + vsprintf(this,fmt,va); + va_end(va); + + if (strcmp(last,this)==0) + { + rep++; + if (rep==3) + fprintf(trace_fp,"*** Repeating...\n"); + else if ((rep%10)==0) + fprintf(trace_fp,"*** Still repeating - %d times\n",rep); + } + else + { + if (rep) + { + if (rep>2) + fprintf(trace_fp,"*** Repeated %d times\n",rep); + else + for(f=0;f<rep;f++) + fprintf(trace_fp,"%s",last); + rep=0; + } + + fprintf(trace_fp,"%s",this); + strcpy(last,this); + } + } +#endif +} + + +char *Binstr(int val, int width) +{ + static char s[48]; + char *p; + + p=s; + width--; + while(width>=0) + { + if (val&(1<<width)) + *p++='1'; + else + *p++='0'; + + width--; + } + + *p=0; + + return(s); +} + + +char *MemStr(char *addr,int size) +{ + static char s[1024]; + int i; + + i=0; + + while(size) + { + if (isprint(*addr)) + s[i++]=*addr; + else if (*addr=='\0') + s[i++]='@'; + else if (*addr=='\n') + { + strcpy(s+i,"\\n"); + i+=2; + } + else if (*addr=='\r') + { + strcpy(s+i,"\\r"); + i+=2; + } + else + { + sprintf(s+i,"\\%3.3o",(unsigned int)*addr); + i+=4; + } + + if (i>(sizeof(s)-10)) + { + sprintf(s+i,"<...>"); + return(s); + } + + size--; + addr++; + } + + s[i]=0; + return(s); +} + + +/* END OF FILE */ @@ -0,0 +1,66 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Debug output + + $Id$ + +*/ + +#ifndef VIDOOM_DEBUG_H + +#define VIDOOM_DEBUG_H + +#include <stdio.h> + +#ifdef VIDOOM_DEBUG +# define Debug(x) VIDOOM_Debug x +#else +# define Debug(x) +#endif + +/* Macro for function tracing. Use as: + void func(void) + { + int a; + + VIDOOM_TRACE; + ... + } +*/ +#define VIDOOM_TRACE VIDOOM_Trace(TRACEFORM) + +void VIDOOM_Debug(char *fmt,...); +void VIDOOM_Trace(char *fmt,...); + +/* Utils for debug users. Converts val to a n-character binary string +*/ +char *Binstr(int val,int n); + +/* Prints out memory as a string +*/ +char *MemStr(char *addr,int size); + +#endif + + +/* END OF FILE */ diff --git a/djgpp/file.c b/djgpp/file.c new file mode 100644 index 0000000..5f5d49f --- /dev/null +++ b/djgpp/file.c @@ -0,0 +1,118 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + File system interfaces + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include <unistd.h> +#include <string.h> + + +/* ---------------------------------------- PRIVATE FUNCS +*/ +static void ChangeDirsep(char *p) +{ + while(*p) + { + if (*p=='\\') + *p='/'; + + p++; + } +} + + +/* ---------------------------------------- EXPORTED FUNCS +*/ +char *Pwd(void) +{ + static char s[PATH_MAX]; + + getcwd(s,PATH_MAX); + + return(s); +} + +void Cd(char *path) +{ + chdir(path); +} + +char *Dirname(char *path) +{ + static char s[PATH_MAX]; + char *p; + + strcpy(s,path); + + p=s+strlen(s)-1; + + while((*p)&&(p>s)) + if ((*p=='/')||(*p=='\\')) + *(p+1)=0; + else + p--; + + return(s); +} + +char *Basename(char *path) +{ + static char s[PATH_MAX]; + char *p; + + strcpy(s,path); + + p=s+strlen(s)-1; + + while(p>s) + if ((*p=='/')||(*p=='\\')) + return(p+1); + else + p--; + + return(s); +} + +int FileExists(char *path) +{ + return(__file_exists(path)); +} + +int FilenamesEqual(char *path1, char *path2) +{ + char p1[PATH_MAX],p2[PATH_MAX]; + + strcpy(p1,path1); + strcpy(p2,path2); + + ChangeDirsep(p1); + ChangeDirsep(p2); + + return(!strcasecmp(p1,p2)); +} + + +/* END OF FILE */ diff --git a/djgpp/gfx.c b/djgpp/gfx.c new file mode 100644 index 0000000..b93f6ea --- /dev/null +++ b/djgpp/gfx.c @@ -0,0 +1,559 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Graphics functions + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" + +#include <stdio.h> +#include <allegro.h> +#include <stdarg.h> +#include <ctype.h> +#include <time.h> +#include "gfx.h" +#include "gui.h" +#include "debug.h" + + +/* ---------------------------------------- VARS +*/ +#define COLDEP 16 +#define ACOL(c) (makecol_depth(COLDEP,((c)&0xff0000)>>16, \ + ((c)&0xff00)>>8, \ + ((c)&0xff))) + +static int init=FALSE; + +static BITMAP *bm; +static int width; +static int height; + +static int mbuttons; + +static int dirty_min_x; +static int dirty_max_x; +static int dirty_min_y; +static int dirty_max_y; + +#define DIRTY(x,y) do { \ + dirty_min_x=MIN(x,dirty_min_x); \ + dirty_min_y=MIN(y,dirty_min_y); \ + dirty_max_x=MAX((x)+1,dirty_max_x); \ + dirty_max_y=MAX((y)+1,dirty_max_y); \ + } while(0) + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ + +static void GetKey(GFXKey *key) +{ + static struct + { + int alleg; + int code; + } keym[]= { + {KEY_F1,GFX_F1}, + {KEY_F2,GFX_F2}, + {KEY_F3,GFX_F3}, + {KEY_F4,GFX_F4}, + {KEY_F5,GFX_F5}, + {KEY_F6,GFX_F6}, + {KEY_F7,GFX_F7}, + {KEY_F8,GFX_F8}, + {KEY_F9,GFX_F9}, + {KEY_F10,GFX_F10}, + {KEY_F11,GFX_F11}, + {KEY_F12,GFX_F12}, + {KEY_ESC,GFX_ESC}, + {KEY_INSERT,GFX_INSERT}, + {KEY_HOME,GFX_HOME}, + {KEY_PGUP,GFX_PGUP}, + {KEY_DEL,GFX_DELETE}, + {KEY_END,GFX_END}, + {KEY_PGDN,GFX_PGDN}, + {KEY_UP,GFX_UP}, + {KEY_DOWN,GFX_DOWN}, + {KEY_LEFT,GFX_LEFT}, + {KEY_RIGHT,GFX_RIGHT}, + {KEY_ENTER,GFX_ENTER}, + {KEY_BACKSPACE,GFX_BACKSPACE}, + {KEY_TAB,GFX_TAB}, + {-1,-1}, + }; + int f; + int k; + + while(TRUE) + { + k=readkey(); + + key->shift=key_shifts&KB_SHIFT_FLAG; + key->ctrl=key_shifts&KB_CTRL_FLAG; + key->alt=key_shifts&KB_ALT_FLAG; + + f=0; + while(keym[f].code!=-1) + { + if (keym[f].alleg==k>>8) + { + key->ascii=0; + key->code=keym[f].code; + return; + } + + f++; + } + + if (isprint(k&0xff)) + { + key->ascii=(k&0xff); + key->code=GFX_ASCII; + return; + } + } +} + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ +void GFX_init(void) +{ + if (!init) + { + init=TRUE; + allegro_init(); + + install_keyboard(); + + install_timer(); + + if ((mbuttons=install_mouse())==-1) + GFX_exit(EXIT_FAILURE,"Failed to install mouse handler\n"); + + set_color_depth(COLDEP); + } +} + + +void GFX_close(void) +{ + if (init) + { + allegro_exit(); + init=FALSE; + } +} + + +void GFX_open(int w,int h) +{ + height=h; + width=w; + + if (set_gfx_mode(GFX_AUTODETECT,w,h,0,0)<0) + GFX_exit(EXIT_FAILURE,"Couldn't open screen of %dx%d\n",w,h); + + if (!(bm=create_bitmap(w,h))) + GFX_exit(EXIT_FAILURE,"Couldn't create bitmap of %dx%d\n",w,h); + + show_mouse(screen); +} + + +void GFX_SETTEXT(void) +{ + if (set_gfx_mode(GFX_TEXT,80,25,0,0)<0) + GFX_exit(EXIT_FAILURE,"Couldn't open text screen\n"); + + show_mouse(NULL); + remove_mouse(); + remove_timer(); + remove_keyboard(); +} + + +void GFX_SETGRAPHICS(void) +{ + if (set_gfx_mode(GFX_AUTODETECT,width,height,0,0)<0) + GFX_exit(EXIT_FAILURE,"Couldn't re-open graphics screen\n"); + + install_keyboard(); + install_timer(); + install_mouse(); + show_mouse(screen); +} + + +void GFX_clear(int col) +{ + clear_to_color(bm,ACOL(col)); + dirty_min_x=0; + dirty_max_x=width; + dirty_min_y=0; + dirty_max_y=width; +} + + +void GFX_redraw(void) +{ + if (dirty_max_x<dirty_min_x) + return; + + dirty_min_x=MAX(0,dirty_min_x); + dirty_min_y=MAX(0,dirty_min_y); + dirty_max_x=MIN(width,dirty_max_x); + dirty_max_y=MIN(height,dirty_max_y); + + show_mouse(NULL); + + blit(bm,screen,dirty_min_x,dirty_min_y, + dirty_min_x,dirty_min_y, + dirty_max_x-dirty_min_x+1,dirty_max_y-dirty_min_y+1); + + show_mouse(screen); + + dirty_max_x=0; + dirty_min_x=width-1; + dirty_max_y=0; + dirty_min_y=width-1; +} + + +void GFX_FORCE_REDRAW(void) +{ + show_mouse(NULL); + blit(bm,screen,0,0,0,0,width,height); + show_mouse(screen); + dirty_max_x=0; + dirty_min_x=width-1; + dirty_max_y=0; + dirty_min_y=width-1; +} + + +void GFX_line(int x1,int y1,int x2,int y2,int col) +{ + DIRTY(x1,y1); + DIRTY(x2,y2); + line(bm,x1,y1,x2,y2,ACOL(col)); +} + + +void GFX_plot(int x,int y,int col) +{ + DIRTY(x,y); + putpixel(bm,x,y,ACOL(col)); +} + + +void GFX_circle(int x,int y,int r,int col) +{ + DIRTY(x-r-1,y-r-1); + DIRTY(x+r+1,y+r+1); + circle(bm,x,y,r,ACOL(col)); +} + + +void GFX_fcircle(int x,int y,int r,int col) +{ + DIRTY(x-r-1,y-r-1); + DIRTY(x+r+1,y+r+1); + circlefill(bm,x,y,r,ACOL(col)); +} + + +void GFX_rect(int x,int y,int w,int h,int col) +{ + DIRTY(x,y); + DIRTY(x+w,y+h); + + w-=SGN(w); + h-=SGN(h); + + rect(bm,x,y,x+w,y+h,ACOL(col)); +} + + +void GFX_frect(int x,int y,int w,int h,int col) +{ + DIRTY(x,y); + DIRTY(x+w,y+h); + + w-=SGN(w); + h-=SGN(h); + + rectfill(bm,x,y,x+w,y+h,ACOL(col)); +} + + +void GFX_set_XOR_mode(void) +{ + drawing_mode(DRAW_MODE_XOR,NULL,0,0); +} + + +void GFX_clear_XOR_mode(void) +{ + drawing_mode(DRAW_MODE_SOLID,NULL,0,0); +} + + +void GFX_print(int x,int y,int col,char *fmt,...) +{ + char s[512]; + va_list va; + + va_start(va,fmt); + vsprintf(s,fmt,va); + va_end(va); + + DIRTY(x,y); + DIRTY(x+text_length(font,s),y+text_height(font)); + + text_mode(-1); + textout(bm,font,s,x,y,ACOL(col)); +} + + +int GFX_fh(void) +{ + return(text_height(font)); +} + + +int GFX_fw(void) +{ + return(text_length(font," ")); +} + + +int GFX_mouse_buttons(void) +{ + return(mbuttons); +} + + +int GFX_mouse(int *x,int *y) +{ + if (x) + *x=mouse_x; + + if (y) + *y=mouse_y; + + return(mouse_b); +} + + +void GFX_waitkey(GFXKey *key) +{ + GFXKey dummy; + + while(!keypressed()); + if (key) + GetKey(key); + else + GetKey(&dummy); +} + + +int GFX_key(GFXKey *key) +{ + if(!keypressed()) + return(FALSE); + else + { + GetKey(key); + return(TRUE); + } +} + + +void GFX_bounce(void) +{ + while((keypressed())||(mouse_b)) + if (keypressed()) + readkey(); +} + + +void GFX_await_input(GFXEvent *ev) +{ + static time_t last_time=0; + static int mb; + + /* Assume that a gap of 1-2 seconds means that this is a new await + input loop + */ + if (time(NULL)-last_time>1) + mb=0; + + while (TRUE) + { + if (keypressed()) + { + ev->type=GFX_KEY_EVENT; + GetKey(&ev->key); + return; + } + + if (mouse_b!=mb) + { + ev->mouse.shift=key_shifts&KB_SHIFT_FLAG; + ev->mouse.ctrl=key_shifts&KB_CTRL_FLAG; + ev->mouse.alt=key_shifts&KB_ALT_FLAG; + ev->mouse.x=mouse_x; + ev->mouse.y=mouse_y; + ev->mouse.b=mb=mouse_b; + ev->type=GFX_MOUSE_EVENT; + return; + } + } +} + + +void GFX_await_input_full(GFXEvent *ev) +{ + static time_t last_time=0; + static int mx,my,mb; + + /* Assume that a gap of 1-2 seconds means that this is a new await + input loop + */ + if (time(NULL)-last_time>1) + { + mx=-1; + my=-1; + mb=0; + } + + while (TRUE) + { + if (keypressed()) + { + ev->type=GFX_KEY_EVENT; + GetKey(&ev->key); + last_time=time(NULL); + return; + } + + if ((mouse_b!=mb)||(mouse_x!=mx)||(mouse_y!=my)) + { + ev->mouse.shift=key_shifts&KB_SHIFT_FLAG; + ev->mouse.ctrl=key_shifts&KB_CTRL_FLAG; + ev->mouse.alt=key_shifts&KB_ALT_FLAG; + ev->mouse.x=mx=mouse_x; + ev->mouse.y=my=mouse_y; + ev->mouse.b=mb=mouse_b; + ev->type=GFX_MOUSE_EVENT; + last_time=time(NULL); + return; + } + } +} + + +void GFX_exit(int code,char *fmt,...) +{ + va_list va; + + if (init) + allegro_exit(); + + va_start(va,fmt); + vfprintf(stderr,fmt,va); + va_end(va); + + exit(code); +} + + +GFX_IMAGE GFX_create_image(GFX_BITMAP *bm) +{ + BITMAP *b; + RLE_SPRITE *ret; + int x,y; + + if (!(b=create_bitmap(bm->w,bm->h))) + return(NULL); + + clear(b); + + for(x=0;x<bm->w;x++) + for(y=0;y<bm->h;y++) + putpixel(b,x,y,ACOL(bm->pal[*(bm->data+(y*bm->w)+(x))])); + + ret=get_rle_sprite(b); + destroy_bitmap(b); + + return((GFX_IMAGE)ret); +} + + +void GFX_destroy_image(GFX_IMAGE img) +{ + destroy_rle_sprite(img); +} + + +void GFX_draw_image(GFX_IMAGE i, int x, int y) +{ + RLE_SPRITE *s; + + s=i; + draw_rle_sprite(bm,s,x,y); + DIRTY(x,y); + DIRTY(x+s->w,y+s->h); +} + + +void GFX_fill_screen(GFX_IMAGE i) +{ + BITMAP *b; + RLE_SPRITE *s; + + s=i; + b=create_bitmap(s->w,s->h); + draw_rle_sprite(b,s,0,0); + + stretch_blit(b,bm,0,0,s->w,s->h,0,0,width,height); + destroy_bitmap(b); + + DIRTY(0,0); + DIRTY(width,height); +} + + +void GFX_save_screen(char *path) +{ + BITMAP *sub; + + sub=create_sub_bitmap(screen,0,0,width,height); + save_bmp(path,sub,NULL); + destroy_bitmap(sub); +} + + +/* END OF FILE */ diff --git a/djgpp/install b/djgpp/install new file mode 100644 index 0000000..754914f --- /dev/null +++ b/djgpp/install @@ -0,0 +1,31 @@ +# viDOOM - level editor for DOOM +# +# Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# ------------------------------------------------------------------------- +# +# Install Makefile for DJGPP +# +# $Id: install,v 1.1 2000/07/19 14:05:45 dosuser Exp dosuser $ +# + +install: FORCE + $(CC) -o install.exe install.c + install.exe $(INSTALLDIR) + del install.exe + +FORCE: diff --git a/djgpp/install.c b/djgpp/install.c new file mode 100644 index 0000000..2639319 --- /dev/null +++ b/djgpp/install.c @@ -0,0 +1,119 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Install program for DOS/DJGPP version + +*/ +static const char rcs_id[]="$Id$"; + +#include <stdio.h> +#include <unistd.h> +#include <stdarg.h> + +/* This is done as an executable just because we can then ensure that the + slashes are the right way around +*/ +void Cmd(char *fmt, ...) +{ + char cmd[1024]; + va_list va; + + va_start(va,fmt); + vsprintf(cmd,fmt,va); + va_end(va); + + printf("*** %s\n",cmd); + strcat(cmd," > temp.$$$"); + system(cmd); + system("del temp.$$$"); +} + + +int main(int argc, char *argv[]) +{ + char path[1024]; + char *p,*pwd; + + if (argc!=2) + { + fprintf(stderr,"usage: install dir\n"); + exit(1); + } + + pwd=getcwd(NULL,1024); + + strcpy(path,argv[1]); + + if (path[strlen(path)-1]=='\\') + path[strlen(path)-1]=0; + + p=path; + + while(*p) + { + if (*p=='/') + *p='\\'; + + p++; + } + + if (chdir("..")) + exit(1); + + /* Do viDOOM + */ + mkdir(path); + + printf("===== Installing viDOOM to %s =====\n",path); + + Cmd("del %s\\vidoom.exe",path); + Cmd("del %s\\*.cfg",path); + Cmd("del %s\\vidoom.ini",path); + Cmd("copy vidoom.exe %s",path); + Cmd("copy LICENSE %s",path); + Cmd("copy *.cfg %s",path); + Cmd("copy base.ini %s",path); + Cmd("ren %s\\base.ini vidoom.ini",path); + + /* Do docs + */ + strcat(path,"\\doc"); + mkdir(path); + + Cmd("del %s\\*.htm",path); + Cmd("del %s\\*.png",path); + Cmd("copy doc\\*.htm %s",path); + Cmd("copy doc\\*.png %s",path); + + + /* Finish + */ + if (chdir(pwd)) + exit(1); + + free(pwd); + + return(0); +} + + +/* END OF FILE */ diff --git a/djgpp/main.c b/djgpp/main.c new file mode 100644 index 0000000..28a1e04 --- /dev/null +++ b/djgpp/main.c @@ -0,0 +1,34 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Startup code + +*/ +static const char rcs_id[]="$Id$"; + +#include "vidoom.h" + + +int main(int argc,char *argv[]) +{ + return(viDOOM(argc,argv)); +} diff --git a/djgpp/mem.c b/djgpp/mem.c new file mode 100644 index 0000000..fd7774a --- /dev/null +++ b/djgpp/mem.c @@ -0,0 +1,262 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Memory allocation code + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" + +#include <stdio.h> +#include <string.h> + +#include "mem.h" +#include "gfx.h" +#include "file.h" + +#include "debug.h" + +/* Set MEMSTAT to the following levels : + + 0 - Nodebug information + 1 - Fatal debug information (still allocated block on failure, + general release errors) + 2 - All debug information + + Regardless of the above no debug is generated if DEBUG is not set +*/ +#ifndef DEBUG +#define MEMSTAT 0 +#else +#define MEMSTAT 1 +#endif + +typedef struct memmap + { + char file[64]; + int line; + void *addr; + struct memmap *next; + struct memmap *prev; + } MemMap; + +static MemMap *mmap=NULL; + + +static void DumpMemList(char *p) +{ + MemMap *m; + +# if MEMSTAT == 0 + return; +# endif + + Debug(("**** %s ****\n",p)); + Debug(("Still waiting to be freed:\n")); + + if ((m=mmap)) + while(m) + { + Debug(("\t%p allocated to %s:%d\n",m->addr,m->file,m->line)); + m=m->next; + } + else + Debug(("\tNONE\n")); + +} + + +static void AddMM(char *mode,char *file, int line, void *addr) +{ + MemMap *n; + +# if MEMSTAT == 0 + return; +# endif + + n=malloc(sizeof(MemMap)); + strcpy(n->file,Basename(file)); + n->line=line; + n->addr=addr; + + if (!mmap) + { + mmap=n; + n->next=NULL; + n->prev=NULL; + } + else + { + mmap->prev=n; + n->next=mmap; + n->prev=NULL; + mmap=n; + } + +# if MEMSTAT == 2 + Debug(("%s: Memory %p allocated to %s:%d\n",mode,addr,file,line)); +# endif +} + + +static void RmMM(char *mode, char *file, int line, void *addr) +{ + MemMap *n; + int del; + +# if MEMSTAT == 0 + return; +# endif + + del=0; + n=mmap; + + while(n) + if (n->addr==addr) + { +# if MEMSTAT == 2 + Debug(("%s: Address %p freed by %s:%d\n",mode,addr,file,line)); +# endif + + if (n->prev) + n->prev->next=n->next; + else + mmap=n->next; + + if (n->next) + n->next->prev=n->prev; + + free(n); + n=NULL; + del=1; + } + else + n=n->next; + + if (!del) + Debug(("%s: ***** Address %p not found - freed by %s:%d\n", + mode,addr,file,line)); +} + + +void *FGrab(char *fn, int line, int len) +{ + char *ptr; + + if (len==0) + len=1; + + if (!(ptr=malloc(len))) + { + DumpMemList("GRAB FAILED"); + GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n" + "%s:%d Grab(%d)\n",fn,line,len); + } + + memset(ptr,0,len); + + AddMM("Grab",fn,line,ptr); + + return(ptr); +} + + +void *FReGrab(char *fn, int line, void *ptr, int len) +{ + if (len==0) + len=1; + + if (ptr) + RmMM("ReGrab(RELEASE)",fn,line,ptr); + + if (!(ptr=realloc(ptr,len))) + { + DumpMemList("REGRAB FAILED"); + GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n" + "%s:%d ReGrab(%d)\n",fn,line,len); + } + + AddMM("ReGrab(ACQUIRE)",fn,line,ptr); + + return(ptr); +} + + +void FRelease(char *fn, int line, void *p) +{ + RmMM("Release",fn,line,p); + free(p); +} + + +void *FCopy(char *fn, int line, void *p,int len) +{ + void *ptr; + + if (len==0) + ptr=malloc(1); + else + ptr=malloc(len); + + if (!ptr) + { + DumpMemList("COPY FAILED"); + GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n" + "%s:%d Copy(%p,%d)\n",fn,line,p,len); + } + + if (len) + memcpy(ptr,p,len); + + AddMM("Copy",fn,line,ptr); + + return(ptr); +} + + +char *FStrdup(char *fn, int line, char *p) +{ + char n_fn[PATH_MAX]; + char *ptr; + + strcpy(n_fn,fn); + strcat(n_fn," [STRDUP]"); + + if (p) + { + if (!(ptr=malloc(strlen(p)+1))) + { + DumpMemList("STRDUP FAILED"); + GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n" + "%s:%d Stdup(%s)\n",fn,line,p); + } + + strcpy(ptr,p); + AddMM("Strdup",fn,line,ptr); + return(ptr); + } + else + return(NULL); +} + +/* END OF FILE */ diff --git a/djgpp/platgui.c b/djgpp/platgui.c new file mode 100644 index 0000000..3f50964 --- /dev/null +++ b/djgpp/platgui.c @@ -0,0 +1,4235 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Platform specific GUI type routines + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" + +#include <stdio.h> +#include <allegro.h> +#include <string.h> + +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <dir.h> +#include <limits.h> +#include <ctype.h> +#include <fcntl.h> + +#include "platgui.h" +#include "gfx.h" +#include "gui.h" +#include "mem.h" +#include "file.h" +#include "util.h" + +#include "debug.h" + +#define COLDEP 16 +#define ACOL(c) (makecol_depth(COLDEP,((c)&0xff0000)>>16, \ + ((c)&0xff00)>>8, \ + ((c)&0xff))) + +#define DIALOG_STRLEN 16 + +#define SMALL_BEVEL 1 /* Bevels on radio and flag boxes */ +#define BEVEL 2 /* All other bevels */ +#define TICK 2 + +#define PL_COL 0xb0b0ff /* Col for active picklist */ + +#define BUFF_LEN 1024 + +/* ---------------------------------------- VARS +*/ +static int SCRW,SCRH; +static int FW,FH; + +/* Vars for GUI_yesno_all() +*/ +static int all_pressed=FALSE; +static int all_result=FALSE; + +/* Global picklist data and the picklist dialog +*/ +#define PL_BORDER 0 +#define PL_TITLE 1 +#define PL_PICKLIST 2 +#define PL_OK 3 +#define PL_CANCEL 4 +#define PL_END 5 + +static DIALOG picklist[PL_END+1]; +static int pick_no; +static char **pick_data; + + +/* Global image picklist data and the image picklist dialog +*/ +#define IPL_BORDER 0 +#define IPL_TITLE 1 +#define IPL_PICKLIST 2 +#define IPL_IMG_BORDER 3 +#define IPL_IMAGE 4 +#define IPL_OK 5 +#define IPL_CANCEL 6 +#define IPL_END 7 + +static DIALOG img_picklist[IPL_END+1]; +static int img_pick_no; +static PLAT_IMG_PICKLIST + *img_pick_data; + + +/* Global data and the file viewer dialog +*/ +#define FV_BORDER 0 +#define FV_TITLE 1 +#define FV_TEXTBOX 2 +#define FV_TEXT 3 +#define FV_VERT_SCROLL 4 +#define FV_HORIZ_SCROLL 5 +#define FV_OK 6 +#define FV_END 7 + +#define FV_SCROLLWIDTH 10 +#define FV_WIDTH 75 +#define FV_HEIGHT 40 + +static DIALOG fv_dialog[FV_END+1]; + +typedef struct + { + double one; + int size; + int pos; + } ScrollbarData; + + +/* Global data for the text editor dialog +*/ +#define TE_BORDER 0 +#define TE_TITLE 1 +#define TE_TEXTBOX 2 +#define TE_TEXT 3 +#define TE_OK 4 +#define TE_CANCEL 5 +#define TE_END 6 + +#define TE_WIDTH 75 +#define TE_CHUNK 256 +#define TE_HEIGHT 40 + +static DIALOG te_dialog[TE_END+1]; + +typedef struct + { + char t[TE_HEIGHT][TE_WIDTH+1]; + int top; + int x,y; + int col; + int lines; + int start; + } TextEditData; + +typedef struct + { + int len; + char *p; + } TextEditLine; + + +/* Types for the menu +*/ +typedef struct + { + int x; + int y; + int w; + int h; + } BoundBox; + +#define MENU_OK 0 +#define MENU_CANCEL_ESC 1 +#define MENU_CANCEL_CLICK 2 +#define MENU_CHILD_RETURN 3 + +typedef struct + { + int ret; + int mode; + } MenuControl; + + +static int MouseInBox(BoundBox *box,int no); + +/* 'Hidden' interfaces to gfx.c +*/ +extern void GFX_FORCE_REDRAW(void); +extern void GFX_SETTEXT(void); +extern void GFX_SETGRAPHICS(void); + + +/* ---------------------------------------- FILE VEIWER SUPPORT FUNCS +*/ +static char *ConvertFileText(char *p) +{ + static char s[BUFF_LEN+1]; + int f; + + f=0; + + while((*p)&&(f<BUFF_LEN)) + { + if (isprint(*p)) + s[f++]=*p++; + else if (*p == '\t') + { + if ((f%8)==0) + s[f++]=' '; + + while(((f%8)!=0)&&(f<BUFF_LEN)) + s[f++]=' '; + + p++; + } + else + p++; + } + + s[f]=0; + + return(s); +} + + +/* ---------------------------------------- GUI OBJECT PROCS SUPPORT FUNCS +*/ +static void Rect3D(int x,int y,int w,int h, int invert,int bevel) +{ + int f; + int mid,lo,hi; + + mid=ACOL(GUI_MID); + + if (invert) + { + lo=ACOL(GUI_HI); + hi=ACOL(GUI_LO); + } + else + { + hi=ACOL(GUI_HI); + lo=ACOL(GUI_LO); + } + + rectfill(screen,x,y,x+w-1,y+h-1,mid); + + for(f=0;f<bevel;f++) + { + vline(screen,x+f,y+f,y+h-1-f,hi); + vline(screen,x+w-1-f,y+f,y+h-1-f,lo); + + hline(screen,x+f,y+f,x+w-1-f,hi); + hline(screen,x+f,y+h-1-f,x+w-1-f,lo); + } +} + + +static void DrawTick(int x,int y,int w,int h) +{ + int ox,oy,x2,y2,x3,y3; + int f; + + x2=x+(w/4); + y2=y+h-1; + + x3=x2; + y3=y2; + + while(x3<(x+w-1)) + { + x3++; + y3--; + } + + ox=x; + oy=y; + + x=x2; + y=y2; + + while(x>ox) + { + x--; + y--; + } + + for(f=0;f<TICK;f++) + { + line(screen,x,y,x2,y2,ACOL(BLACK)); + line(screen,x2,y2,x3,y3,ACOL(BLACK)); + + y--; + y2--; + y3--; + } +} + + +/* This code is based on _draw_scrollable_frame() from guiproc.c in Allegro +*/ +static void DrawScrollableFrame(DIALOG *d, int listsize, int offset, + int height, int fg_color, int bg) +{ + BITMAP *pattern; + int i, len; + int xx, yy; + + /* draw frame + */ + rect(screen, d->x, d->y, d->x+d->w, d->y+d->h, fg_color); + + /* possibly draw scrollbar + */ + if (listsize > height) + { + vline(screen, d->x+d->w-12, d->y+1, d->y+d->h-1, fg_color); + + /* create and draw the scrollbar + */ + pattern=create_bitmap(2,2); + + /* + putpixel(pattern,0,0,d->fg); + putpixel(pattern,1,1,d->fg); + putpixel(pattern,1,0,d->bg); + putpixel(pattern,0,1,d->bg); + */ + putpixel(pattern,0,0,ACOL(GUI_MID)); + putpixel(pattern,1,1,ACOL(GUI_MID)); + putpixel(pattern,1,0,ACOL(GUI_LO)); + putpixel(pattern,0,1,ACOL(GUI_LO)); + + i = ((d->h-4) * height + listsize/2) / listsize; + xx = d->x+d->w-11; + yy = d->y+2; + + if (offset > 0) + { + len = (((d->h-4) * offset) + listsize/2) / listsize; + drawing_mode(DRAW_MODE_COPY_PATTERN, pattern, 0, 0); + rectfill(screen, xx, yy, xx+10, yy+len-1, ACOL(GUI_MID)); + solid_mode(); + yy += len; + } + + if (yy+i < d->y+d->h-2) + { + Rect3D(xx, yy, 11, i, FALSE, BEVEL); + yy += i; + drawing_mode(DRAW_MODE_COPY_PATTERN, pattern, 0, 0); + rectfill(screen, xx, yy, xx+10, d->y+d->h-2, ACOL(GUI_MID)); + solid_mode(); + } + else + Rect3D(xx, yy, 11, i, FALSE, BEVEL); + + destroy_bitmap(pattern); + } +} + + +/* This code is copied from _draw_listbox() from guiproc.c in Allegro, so we + can force it to call DrawScrollableFrame() +*/ +static void DrawListbox(DIALOG *d) +{ + typedef char *(*getfuncptr)(int, int *); + int height, listsize, i, len, bar, x, y, w; + int fg_color, fg, bg; + char *sel = d->dp2; + char *s; + char store; + + (*(getfuncptr)d->dp)(-1, &listsize); + height = (d->h-3) / text_height(font); + bar = (listsize > height); + w = (bar ? d->w-14 : d->w-2); + + fg_color = (d->flags & D_DISABLED) ? gui_mg_color : d->fg; + + /* draw box contents + */ + for (i=0; i<height; i++) + { + if (d->d2+i < listsize) + { + if (d->d2+i == d->d1) + { + fg = d->bg; + bg = fg_color; + } + else if ((sel) && (sel[d->d2+i])) + { + fg = d->bg; + bg = gui_mg_color; + } + else + { + fg = fg_color; + bg = d->bg; + } + + s = (*(getfuncptr)d->dp)(i+d->d2, NULL); + x = d->x + 2; + y = d->y + 2 + i*text_height(font); + text_mode(bg); + rectfill(screen, x, y, x+7, y+text_height(font)-1, bg); + x += 8; + len = strlen(s); + store = 0; + while (text_length(font, s) >= d->w - (bar ? 22 : 10)) + { + s[len] = store; + len--; + store = s[len]; + s[len] = 0; + } + textout(screen, font, s, x, y, fg); + x += text_length(font, s); + s[len] = store; + if (x <= d->x+w) + rectfill(screen, x, y, d->x+w, y+text_height(font)-1, bg); + } + else + rectfill(screen, d->x+2, d->y+2+i*text_height(font), + d->x+w, d->y+1+(i+1)*text_height(font), d->bg); + } + + if (d->y+2+i*text_height(font) <= d->y+d->h-2) + rectfill(screen, d->x+2, d->y+2+i*text_height(font), + d->x+w, d->y+d->h-2, d->bg); + + /* draw frame, maybe with scrollbar + */ + DrawScrollableFrame(d, listsize, d->d2, height, fg_color, d->bg); +} + + +/* ---------------------------------------- GUI OBJECT PROCS + + Note that many of the functions are based on the source available + in guiproc.c in Allegro +*/ +static int d_3d_box(int msg, DIALOG *d, int c) +{ + if (msg==MSG_DRAW) + Rect3D(d->x,d->y,d->w,d->h,FALSE,BEVEL); + + return(D_O_K); +} + + +static int d_inv_3d_box(int msg, DIALOG *d, int c) +{ + if (msg==MSG_DRAW) + Rect3D(d->x,d->y,d->w,d->h,TRUE,BEVEL); + + return(D_O_K); +} + + +static int d_img_bmap_proc(int msg, DIALOG *d, int c) +{ + RLE_SPRITE *bm; + + if (msg==MSG_DRAW) + { + rectfill(screen,d->x,d->y,d->x+d->w-1,d->y+d->h-1,ACOL(BLACK)); + + if ((bm=d->dp)) + draw_rle_sprite(screen,bm,d->x,d->y); + } + + return(D_O_K); +} + + +static int d_img_list_proc(int msg, DIALOG *d, int c) +{ + static int my_d_list_proc(int msg, DIALOG *d, int c); + int d1; + int ret; + + d1=img_picklist[IPL_PICKLIST].d1; + + ret=my_d_list_proc(msg,d,c); + + if (d1!=img_picklist[IPL_PICKLIST].d1) + { + img_picklist[IPL_IMAGE].dp= + img_pick_data[img_picklist[IPL_PICKLIST].d1].img; + + d_img_bmap_proc(MSG_DRAW,&img_picklist[IPL_IMAGE],0); + } + + return(ret); +} + + +int my_d_check_proc(int msg, DIALOG *d, int c) +{ + int x; + int fg; + + if (msg==MSG_DRAW) + { + fg=ACOL(WHITE); + text_mode(d->bg); + + gui_textout(screen, d->dp, d->x, + d->y+(d->h-(text_height(font)-gui_font_baseline))/2, + fg, FALSE); + + x=d->x+d->w-d->h; + + Rect3D(x,d->y,d->h,d->h,TRUE,SMALL_BEVEL); + rectfill(screen,x+SMALL_BEVEL,d->y+SMALL_BEVEL, + x+d->h-SMALL_BEVEL*2,d->y+d->h-SMALL_BEVEL*2, + ACOL(WHITE)); + + if (d->flags & D_SELECTED) + if (d->d1) + rectfill(screen,x+SMALL_BEVEL+2,d->y+SMALL_BEVEL+2, + x+d->h-SMALL_BEVEL*2-2, + d->y+d->h-SMALL_BEVEL*2-2, + ACOL(BLACK)); + else + DrawTick(x+SMALL_BEVEL,d->y+SMALL_BEVEL, + d->h-SMALL_BEVEL*2,d->h-SMALL_BEVEL*2); + + return D_O_K; + } + + /* Group zero is done as a check box, not a radio box + */ + if (d->d1) + return d_radio_proc(msg, d, c); + else + return d_button_proc(msg, d, c); +} + + +int my_d_radio_proc(int msg, DIALOG *d, int c) +{ + int x,fg; + + switch(msg) + { + case MSG_DRAW: + fg=(d->flags & D_DISABLED) ? gui_mg_color : d->fg; + text_mode(d->bg); + gui_textout + (screen, + d->dp,d->x+d->h+text_height(font), + d->y+(d->h-(text_height(font)-gui_font_baseline))/2,fg,FALSE); + + x = d->x; + + Rect3D(x,d->y,d->h,d->h,TRUE,SMALL_BEVEL); + rectfill(screen,x+SMALL_BEVEL,d->y+SMALL_BEVEL, + x+d->h-SMALL_BEVEL*2,d->y+d->h-SMALL_BEVEL*2, + ACOL(WHITE)); + + if (d->flags & D_SELECTED) + rectfill(screen,x+SMALL_BEVEL+2,d->y+SMALL_BEVEL+2, + x+d->h-SMALL_BEVEL*2-2, + d->y+d->h-SMALL_BEVEL*2-2, + ACOL(BLACK)); + + break; + + default: + return(d_radio_proc(msg,d,c)); + break; + } + + return(D_O_K); +} + + +static int my_d_list_proc(int msg, DIALOG *d, int c) +{ + switch(msg) + { + case MSG_DRAW: + DrawListbox(d); + break; + + default: + return(d_list_proc(msg,d,c)); + break; + } + + return (D_O_K); +} + + +static int my_d_button_proc(int msg, DIALOG *d, int c) +{ + int inv; + + switch(msg) + { + case MSG_DRAW: + if (d->flags&D_GOTFOCUS) + rect(screen,d->x-1,d->y-1,d->x+d->w,d->y+d->h,ACOL(BLACK)); + else + rect(screen,d->x-1,d->y-1,d->x+d->w,d->y+d->h,ACOL(GUI_MID)); + + if (d->flags&D_SELECTED) + inv=TRUE; + else + inv=FALSE; + + Rect3D(d->x,d->y,d->w,d->h,inv,BEVEL); + text_mode(-1); + gui_textout(screen,d->dp,d->x+(d->w/2),d->y+d->h/2-FH/2,d->fg,TRUE); + + break; + + default: + return(d_button_proc(msg,d,c)); + break; + } + + return (D_O_K); +} + + +static int d_ro_textbox(int msg, DIALOG *d, int c) +{ + char **p; + int x,y; + int f; + + p=d->dp; + + if(msg==MSG_DRAW) + { + text_mode(-1); + + y=d->d2; + x=d->d1; + + vsync(); + + rectfill(screen,d->x,d->y,d->x+d->w-1,d->y+d->h-1,ACOL(WHITE)); + + for(f=0;(f<FV_HEIGHT)&&(p[y]);f++,y++) + if (strlen(p[y])>x) + textprintf(screen,font,d->x,d->y+(f*FH),ACOL(BLACK), + "%-.*s",FV_WIDTH,p[y]+x); + + } + + return (D_O_K); +} + + +static int d_vert_scrollbar(int msg, DIALOG *d, int c) +{ + ScrollbarData *sd; + int *ip; + DIALOG *dp; + int last_y; + + /* Special case for 'zero' length scrollbars - just ignore interactions + and draw a full length scrollbar + */ + if (d->d2==0) + { + if (msg==MSG_DRAW) + { + rect(screen,d->x,d->y,d->x+d->w,d->y+d->h,ACOL(BLACK)); + Rect3D(d->x+1,d->y+1,d->w-1,d->h-1,FALSE,BEVEL); + } + + return(D_O_K); + } + + switch(msg) + { + case MSG_START: + /* Calc box size, etc + */ + sd=Grab(sizeof(*sd)); + d->dp3=sd; + + sd->one=(double)d->h/(double)(d->d2+d->d1); + sd->size=(int)(sd->one*d->d1); + sd->pos=0; + ip=d->dp; + *ip=0; + + break; + + case MSG_END: + Release(d->dp3); + break; + + case MSG_DRAW: + { + BITMAP *pattern; + + sd=d->dp3; + + /* draw frame + */ + rect(screen,d->x,d->y,d->x+d->w,d->y+d->h,ACOL(BLACK)); + + /* create and draw the scrollbar + */ + pattern=create_bitmap(2,2); + + putpixel(pattern,0,0,ACOL(GUI_MID)); + putpixel(pattern,1,1,ACOL(GUI_MID)); + putpixel(pattern,1,0,ACOL(GUI_LO)); + putpixel(pattern,0,1,ACOL(GUI_LO)); + + drawing_mode(DRAW_MODE_COPY_PATTERN, pattern, 0, 0); + rectfill(screen,d->x+1,d->y+1,d->x+d->w-1,d->y+d->h-2,0); + solid_mode(); + Rect3D(d->x+1,d->y+1+sd->pos,d->w-1,sd->size,FALSE,BEVEL); + + destroy_bitmap(pattern); + + break; + } + + case MSG_WANTFOCUS: + return(D_WANTFOCUS); + break; + + case MSG_CLICK: + sd=d->dp3; + ip=d->dp; + dp=d->dp2; + + if (mouse_y<(d->y+sd->pos)) + { + while((mouse_b)&&(mouse_y<(d->y+sd->pos))) + if (*ip) + { + (*ip)--; + + sd->pos=(int)((*ip)*sd->one); + + show_mouse(NULL); + SEND_MESSAGE(d,MSG_DRAW,0); + SEND_MESSAGE(dp,MSG_DRAW,0); + show_mouse(screen); + } + } + else if (mouse_y>(d->y+sd->pos+sd->size)) + { + while((mouse_b)&&(mouse_y>(d->y+sd->pos+sd->size))) + if ((*ip)<d->d2) + { + (*ip)++; + + sd->pos=(int)((*ip)*sd->one); + + show_mouse(NULL); + SEND_MESSAGE(d,MSG_DRAW,0); + SEND_MESSAGE(dp,MSG_DRAW,0); + show_mouse(screen); + } + } + else + { + last_y=mouse_y; + + /* I'm sure there's a better way of doing this, but I couldn't + get the sums to work out... + */ + while(mouse_b) + { + int my; + int diff; + int np; + + my=mouse_y; + diff=my-last_y; + np=sd->pos+diff; + + if ((diff<0)&&((*ip)>0)) + { + while((sd->pos>np)&&(*ip)) + { + (*ip)--; + sd->pos=(int)((*ip)*sd->one); + } + + show_mouse(NULL); + SEND_MESSAGE(d,MSG_DRAW,0); + SEND_MESSAGE(dp,MSG_DRAW,0); + show_mouse(screen); + } + else if ((diff>0)&&((*ip)<d->d2)) + { + while((sd->pos<np)&&((*ip)<d->d2)) + { + (*ip)++; + sd->pos=(int)((*ip)*sd->one); + } + + if (sd->pos==(d->h-sd->size)) + *ip=d->d2; + + show_mouse(NULL); + SEND_MESSAGE(d,MSG_DRAW,0); + SEND_MESSAGE(dp,MSG_DRAW,0); + show_mouse(screen); + } + + last_y=my; + } + } + + break; + + default: + break; + } + + return(D_O_K); +} + + +static int d_horiz_scrollbar(int msg, DIALOG *d, int c) +{ + ScrollbarData *sd; + int *ip; + DIALOG *dp; + int last_x; + + /* Special case for 'zero' length scrollbars - just ignore interactions + and draw a full length scrollbar + */ + if (d->d2==0) + { + if (msg==MSG_DRAW) + { + rect(screen,d->x,d->y,d->x+d->w,d->y+d->h,ACOL(BLACK)); + Rect3D(d->x+1,d->y+1,d->w-1,d->h-1,FALSE,BEVEL); + } + + return(D_O_K); + } + + switch(msg) + { + case MSG_START: + /* Calc box size, etc + */ + sd=Grab(sizeof(*sd)); + d->dp3=sd; + + sd->one=(double)d->w/(double)(d->d2+d->d1); + sd->size=(int)(sd->one*d->d1); + sd->pos=0; + ip=d->dp; + *ip=0; + + break; + + case MSG_END: + Release(d->dp3); + break; + + case MSG_DRAW: + { + BITMAP *pattern; + + sd=d->dp3; + + /* draw frame + */ + rect(screen,d->x,d->y,d->x+d->w,d->y+d->h,ACOL(BLACK)); + + /* create and draw the scrollbar + */ + pattern=create_bitmap(2,2); + + putpixel(pattern,0,0,ACOL(GUI_MID)); + putpixel(pattern,1,1,ACOL(GUI_MID)); + putpixel(pattern,1,0,ACOL(GUI_LO)); + putpixel(pattern,0,1,ACOL(GUI_LO)); + + drawing_mode(DRAW_MODE_COPY_PATTERN, pattern, 0, 0); + rectfill(screen,d->x+1,d->y+1,d->x+d->w-1,d->y+d->h-2,0); + solid_mode(); + Rect3D(d->x+1+sd->pos,d->y+1,sd->size,d->h-1,FALSE,BEVEL); + + destroy_bitmap(pattern); + + break; + } + + case MSG_WANTFOCUS: + return(D_WANTFOCUS); + break; + + case MSG_CLICK: + sd=d->dp3; + ip=d->dp; + dp=d->dp2; + + if (mouse_x<(d->x+sd->pos)) + { + while((mouse_b)&&(mouse_x<(d->x+sd->pos))) + if (*ip) + { + (*ip)--; + + sd->pos=(int)((*ip)*sd->one); + + show_mouse(NULL); + SEND_MESSAGE(d,MSG_DRAW,0); + SEND_MESSAGE(dp,MSG_DRAW,0); + show_mouse(screen); + } + } + else if (mouse_x>(d->x+sd->pos+sd->size)) + { + while((mouse_b)&&(mouse_x>(d->x+sd->pos+sd->size))) + if ((*ip)<d->d2) + { + (*ip)++; + + sd->pos=(int)((*ip)*sd->one); + + show_mouse(NULL); + SEND_MESSAGE(d,MSG_DRAW,0); + SEND_MESSAGE(dp,MSG_DRAW,0); + show_mouse(screen); + } + } + else + { + last_x=mouse_x; + + /* I'm sure there's a better way of doing this, but I couldn't + get the sums to work out... + */ + while(mouse_b) + { + int mx; + int diff; + int np; + + mx=mouse_x; + diff=mx-last_x; + np=sd->pos+diff; + + if ((diff<0)&&((*ip)>0)) + { + while((sd->pos>np)&&(*ip)) + { + (*ip)--; + sd->pos=(int)((*ip)*sd->one); + } + + show_mouse(NULL); + SEND_MESSAGE(d,MSG_DRAW,0); + SEND_MESSAGE(dp,MSG_DRAW,0); + show_mouse(screen); + } + else if ((diff>0)&&((*ip)<d->d2)) + { + while((sd->pos<np)&&((*ip)<d->d2)) + { + (*ip)++; + sd->pos=(int)((*ip)*sd->one); + } + + if (sd->pos==(d->h-sd->size)) + *ip=d->d2; + + show_mouse(NULL); + SEND_MESSAGE(d,MSG_DRAW,0); + SEND_MESSAGE(dp,MSG_DRAW,0); + show_mouse(screen); + } + + last_x=mx; + } + } + + break; + + default: + break; + } + + return(D_O_K); +} + + +static int d_textedit(int msg, DIALOG *d, int c) +{ + static char out[2]=" "; + char *tmp; + TextEditData *te; + TextEditLine *p; + int y,sx,sy; + int f; + + p=d->dp; + + switch(msg) + { + case MSG_START: + /* Memory for text buffer + */ + te=Grab(sizeof(*te)); + d->dp3=te; + te->start=TRUE; + te->top=0; + te->x=0; + te->y=0; + te->col=0; + + te->lines=0; + + while(p[te->lines].p) + te->lines++; + + for(sx=0;sx<TE_WIDTH+1;sx++) + for(sy=0;sy<TE_HEIGHT;sy++) + te->t[sy][sx]=0; + break; + + case MSG_END: + Release(d->dp3); + break; + + case MSG_WANTFOCUS: + return(D_WANTFOCUS); + break; + + case MSG_LOSTFOCUS: + return(D_WANTFOCUS); + break; + + case MSG_DRAW: + te=d->dp3; + + if (te->start) + { + rectfill(screen,d->x,d->y,d->x+d->w-1,d->y+d->h-1,ACOL(WHITE)); + te->start=FALSE; + } + + text_mode(ACOL(WHITE)); + + y=te->top; + + rectfill(screen,d->x+d->w-FW,d->y, + d->x+d->w-1,d->y+d->h-1,ACOL(WHITE)); + + for(sy=0;sy<TE_HEIGHT;sy++,y++) + if (y<te->lines) + { + for(sx=0;sx<TE_WIDTH;sx++) + if ((te->col+sx>=p[y].len)|| + (te->t[sy][sx]!=p[y].p[te->col+sx])) + { + if (te->col+sx>=p[y].len) + te->t[sy][sx]=0; + else + te->t[sy][sx]=p[y].p[te->col+sx]; + + if (isprint(te->t[sy][sx])) + out[0]=te->t[sy][sx]; + else + out[0]=' '; + + textout(screen,font,out, + d->x+(sx*FW),d->y+(sy*FH),ACOL(BLACK)); + } + } + else + { + memset(te->t[sy],0,TE_WIDTH+1); + rectfill(screen,d->x,d->y+(sy*FH), + d->x+d->w-1,d->y+(sy*FH)+FH-1,ACOL(WHITE)); + } + + text_mode(ACOL(BLACK)); + out[0]=isprint(te->t[te->y-te->top][te->x-te->col]) ? + te->t[te->y-te->top][te->x-te->col] : ' '; + textout(screen,font,out, + d->x+((te->x-te->col)*FW), + d->y+((te->y-te->top)*FH),ACOL(WHITE)); + te->t[te->y-te->top][te->x-te->col]=-1; + + break; + + case MSG_CLICK: + te=d->dp3; + + te->y=(mouse_y-d->y)/FH; + te->x=te->col+(mouse_x-d->x)/FW; + + if (te->y>=te->lines) + te->y=te->lines-1; + + if (te->x>p[te->y].len) + te->x=p[te->y].len; + + show_mouse(NULL); + SEND_MESSAGE(d,MSG_DRAW,0); + show_mouse(screen); + break; + + case MSG_CHAR: + te=d->dp3; + + /* Pressing shift and space seems to generate odd values + */ + if ((c>>8)==KEY_SPACE) + c=(c&0xff00)|' '; + + switch(c>>8) + { + case KEY_HOME: + te->x=0; + te->col=0; + break; + + case KEY_END: + te->x=p[te->y].len; + te->col=MAX(0,te->x-TE_WIDTH); + break; + + case KEY_LEFT: + { + int ctrl; + + ctrl=key_shifts&(KB_CTRL_FLAG|KB_SHIFT_FLAG); + + if (ctrl) + while((te->x)&&(isspace(p[te->y].p[te->x]))) + { + if (te->x) + te->x--; + else + ctrl=FALSE; + + if (te->x<te->col) + te->col--; + } + + do { + if (te->x) + te->x--; + else + ctrl=FALSE; + + if (te->x<te->col) + te->col--; + + if (isspace(p[te->y].p[te->x])) + ctrl=FALSE; + + } while(ctrl); + + break; + } + + case KEY_RIGHT: + { + int ctrl; + + ctrl=key_shifts&(KB_CTRL_FLAG|KB_SHIFT_FLAG); + + if (ctrl) + while((te->x<p[te->y].len)&& + (isspace(p[te->y].p[te->x]))) + { + te->x++; + + if ((te->x-TE_WIDTH)>te->col) + te->col++; + } + + do { + if (te->x<p[te->y].len) + { + te->x++; + + if ((te->x-TE_WIDTH)>te->col) + te->col++; + } + else + ctrl=FALSE; + + if (isspace(p[te->y].p[te->x])) + ctrl=FALSE; + + } while(ctrl); + + break; + } + + case KEY_UP: + if (te->y) + { + te->y--; + + if (te->y<te->top) + te->top--; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-TE_WIDTH); + } + } + break; + + case KEY_PGUP: + if (te->y) + { + te->y-=TE_HEIGHT; + + if (te->y<0) + te->y=0; + + if (te->y<te->top) + te->top=te->y; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-TE_WIDTH); + } + } + break; + + case KEY_DOWN: + if (p[te->y+1].p) + { + te->y++; + + if (te->y>=te->top+TE_HEIGHT) + te->top=te->y-TE_HEIGHT+1; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-TE_WIDTH); + } + } + break; + + case KEY_PGDN: + te->y+=TE_HEIGHT; + + if (te->y>=te->lines) + te->y=te->lines-1; + + if (te->y>=te->top+TE_HEIGHT) + te->top=te->y-TE_HEIGHT+1; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-TE_WIDTH); + } + + break; + + case KEY_DEL: + if (p[te->y].p[te->x]) + { + for(f=te->x;f<p[te->y].len;f++) + p[te->y].p[f]=p[te->y].p[f+1]; + + p[te->y].len--; + p[te->y].p[p[te->y].len]=0; + } + else if (p[te->y+1].p) + { + p[te->y].len+=p[te->y+1].len; + + p[te->y].p=ReGrab(p[te->y].p, + (p[te->y].len/TE_CHUNK+1)*TE_CHUNK+1); + + strcat(p[te->y].p,p[te->y+1].p); + + Release(p[te->y+1].p); + + for(f=te->y+1;f<te->lines;f++) + p[f]=p[f+1]; + + te->lines--; + + p=d->dp=ReGrab(d->dp, + sizeof(TextEditLine)*(te->lines+1)); + p[te->lines].p=NULL; + } + break; + + case KEY_BACKSPACE: + if (te->x) + { + for(f=te->x;f<p[te->y].len;f++) + p[te->y].p[f-1]=p[te->y].p[f]; + + p[te->y].len--; + te->x--; + p[te->y].p[p[te->y].len]=0; + + if (te->x<te->col) + te->col--; + } + else if (te->y) + { + te->x=p[te->y-1].len; + te->col=MAX(0,te->x-TE_WIDTH); + + p[te->y-1].len+=p[te->y].len; + + p[te->y-1].p=ReGrab(p[te->y-1].p, + (p[te->y-1].len/TE_CHUNK+1)*TE_CHUNK+1); + + strcat(p[te->y-1].p,p[te->y].p); + + Release(p[te->y].p); + + for(f=te->y;f<te->lines;f++) + p[f]=p[f+1]; + + if (--te->y<te->top) + te->top--; + + te->lines--; + p=d->dp=ReGrab(d->dp, + sizeof(TextEditLine)*(te->lines+1)); + p[te->lines].p=NULL; + } + break; + + case KEY_ENTER: + te->lines++; + p=d->dp=ReGrab(d->dp,sizeof(TextEditLine)*(te->lines+1)); + + for(f=te->lines;f>te->y;f--) + p[f]=p[f-1]; + + tmp=Strdup(p[te->y].p+te->x); + tmp=ReGrab(tmp,(strlen(tmp)/TE_CHUNK+1)*TE_CHUNK+1); + + p[te->y].p[te->x]=0; + p[te->y].len=strlen(p[te->y].p); + + te->y++; + te->x=0; + te->col=0; + + p[te->y].p=tmp; + p[te->y].len=strlen(tmp); + + if (te->y>=te->top+TE_HEIGHT) + te->top=te->y-TE_HEIGHT+1; + + break; + + case KEY_TAB: + for(f=te->x%4+1;f<=4;f++) + simulate_keypress(KEY_SPACE<<8|' '); + break; + + default: + if (isprint(c&0xff)) + { + p[te->y].len++; + + if (!(p[te->y].len%TE_CHUNK)) + p[te->y].p=ReGrab(p[te->y].p, + (p[te->y].len/TE_CHUNK+1)* + TE_CHUNK+1); + + for(f=p[te->y].len;f>te->x;f--) + p[te->y].p[f]=p[te->y].p[f-1]; + + p[te->y].p[te->x]=(c&0xff); + + te->x++; + + if ((te->x-TE_WIDTH)>te->col) + te->col++; + } + break; + } + + show_mouse(NULL); + SEND_MESSAGE(d,MSG_DRAW,0); + show_mouse(screen); + return(D_USED_CHAR); + break; + } + + return (D_O_K); +} + + +static int d_dial_picklist(int msg, DIALOG *d, int c) +{ + char **text; + int lx,ly; + + text=d->dp; + + switch(msg) + { + case MSG_START: + break; + + case MSG_END: + break; + + case MSG_DRAW: + { + int bx1,by1,bx2,by2; + + bx1=d->x+d->w-d->h+1; + by1=d->y; + bx2=d->x+d->w-1; + by2=d->y+d->h-1; + + text_mode(-1); + + if (d->flags&D_GOTFOCUS) + rectfill(screen,d->x,d->y,d->x+d->w-1,d->y+d->h-1,ACOL(PL_COL)); + else + rectfill(screen,d->x,d->y,d->x+d->w-1,d->y+d->h-1,ACOL(WHITE)); + + Rect3D(bx1,by1,d->h,d->h,FALSE,BEVEL); + + textout(screen,font,text[d->d1],d->x,d->y,d->fg); + break; + } + + case MSG_WANTFOCUS: + return(D_WANTFOCUS); + break; + + case MSG_CLICK: + { + BITMAP *under; + int done; + BoundBox b; + int fg,bg; + int f; + int draw; + int cur; + int last; + int new; + int press; + int first; + + text_mode(-1); + cur=d->d1; + press=FALSE; + first=TRUE; + + done=FALSE; + b.h=d->d2*(FH+1)+1; + b.w=d->w; + b.x=d->x; + b.y=d->y; + + if ((b.x+b.w)>SCRW) + b.x=SCRW-b.w; + + if ((b.y+b.h)>SCRH) + b.y=SCRH-b.h; + + if (!(under=create_bitmap(b.w+1,b.h+1))) + GFX_exit(EXIT_FAILURE,"No more memory to create save under\n"); + + show_mouse(NULL); + blit(screen,under,b.x,b.y,0,0,b.w+1,b.h+1); + show_mouse(screen); + + show_mouse(NULL); + rectfill(screen,b.x,b.y,b.x+b.w,b.y+b.h,ACOL(WHITE)); + rect(screen,b.x,b.y,b.x+b.w,b.y+b.h,ACOL(BLACK)); + + for(f=0;f<d->d2;f++) + { + if (f==cur) + { + fg=ACOL(WHITE); + bg=ACOL(BLACK); + } + else + { + fg=ACOL(BLACK); + bg=ACOL(WHITE); + } + + rectfill(screen,b.x+1,b.y+1+(FH+1)*f, + b.x+b.w-1,b.y+b.h-1,bg); + textout(screen,font,text[f],b.x+1,b.y+1+(FH+1)*f,fg); + } + + show_mouse(screen); + + draw=FALSE; + last=-1; + lx=-1; + ly=-1; + + while(!done) + { + if (draw) + { + show_mouse(NULL); + + for(f=0;f<d->d2;f++) + { + if (f==cur) + { + fg=ACOL(WHITE); + bg=ACOL(BLACK); + + rectfill(screen,b.x+1,b.y+1+(FH+1)*f, + b.x+b.w-1,b.y+(FH+1)*(f+1),bg); + textout(screen,font,text[f], + b.x+1,b.y+1+(FH+1)*f,fg); + } + else if (f==last) + { + fg=ACOL(BLACK); + bg=ACOL(WHITE); + + rectfill(screen,b.x+1,b.y+1+(FH+1)*f, + b.x+b.w-1,b.y+(FH+1)*(f+1),bg); + textout(screen,font,text[f], + b.x+1,b.y+1+(FH+1)*f,fg); + } + } + + show_mouse(screen); + draw=FALSE; + } + + if (first) + { + while(mouse_b); + first=FALSE; + } + + if (keypressed()) + { + int k; + + k=readkey(); + + switch(k>>8) + { + case KEY_UP: + draw=TRUE; + + if (cur==-1) + cur=d->d2-1; + else + if (--cur<0) + cur+=d->d2; + break; + + case KEY_DOWN: + draw=TRUE; + + if (cur==-1) + cur=0; + else + cur=(cur+1)%d->d2; + break; + + case KEY_ESC: + cur=-1; + done=TRUE; + break; + + case KEY_ENTER: + done=TRUE; + break; + + default: + break; + } + } + else + { + if ((mouse_b)&&(!press)) + press=TRUE; + + if ((!mouse_b)&&(press)) + { + done=TRUE; + lx=-1; + ly=-1; + } + + last=cur; + + if ((lx!=mouse_x)||(ly!=mouse_y)) + { + lx=mouse_x; + ly=mouse_y; + + if (MouseInBox(&b,1)!=-1) + { + new=(mouse_y-b.y)/(FH+1); + + if (new>=d->d2) + new=d->d2-1; + + if (new!=cur) + { + cur=new; + draw=TRUE; + } + } + else + { + if (cur!=-1) + draw=TRUE; + + cur=-1; + } + } + } + } + + if (cur!=-1) + d->d1=cur; + + show_mouse(NULL); + blit(under,screen,0,0,b.x,b.y,b.w+1,b.h+1); + SEND_MESSAGE(d,MSG_DRAW,0); + show_mouse(screen); + + break; + } + + case MSG_CHAR: + switch(c>>8) + { + case KEY_ENTER: + SEND_MESSAGE(d,MSG_CLICK,0); + break; + + default: + return(D_O_K); + break; + } + + show_mouse(NULL); + SEND_MESSAGE(d,MSG_DRAW,0); + show_mouse(screen); + return(D_USED_CHAR); + break; + + default: + break; + } + + return(D_O_K); +} + + +static int d_title_proc(int msg, DIALOG *d, int c) +{ + char *t; + int x,y; + int l; + + if (msg==MSG_DRAW) + { + t=d->dp; + l=strlen(t); + + x=d->x-l*(FW/2); + y=d->y; + + text_mode(d->bg); + textout(screen,font,t,x,y,d->fg); + + line(screen,active_dialog[0].x+BEVEL+1,d->y+FH+1, + active_dialog[0].x+active_dialog[0].w-BEVEL-1, + d->y+FH+1,ACOL(GUI_HI)); + + line(screen,active_dialog[0].x+BEVEL+1,d->y+FH+2, + active_dialog[0].x+active_dialog[0].w-BEVEL-1, + d->y+FH+2,ACOL(GUI_LO)); + } + + return(D_O_K); +} + + +static int d_my_ctext_proc(int msg, DIALOG *d, int c) +{ + char *t; + int x,y; + int l; + + if (msg==MSG_DRAW) + { + t=d->dp; + l=strlen(t); + + x=d->x-l*(FW/2); + y=d->y; + + text_mode(d->bg); + textout(screen,font,t,x,y,d->fg); + } + + return(D_O_K); +} + + +static int d_my_text_proc(int msg, DIALOG *d, int c) +{ + if (msg==MSG_DRAW) + { + text_mode(d->bg); + textout(screen,font,d->dp,d->x,d->y,d->fg); + } + + return(D_O_K); +} + + +/* ---------------------------------------- FILE SELECTION CODE + + This code is a copy of the file selector code provided by Shawn Hargreaves + in his Allegro library (original filename fsel.c). The only changes are + to the file_selector[] DIALOG structure and some of the fs_*_proc() + callbacks to use my 3D buttons, etc. + + If this code causes problems, blame *ME*, _not_ Shawn! +*/ + + +#ifndef _USE_LFN +#define _USE_LFN 0 +#endif + +static int fs_edit_proc(int, DIALOG *, int ); +static int fs_flist_proc(int, DIALOG *, int ); +static int fs_dlist_proc(int, DIALOG *, int ); +static char *fs_flist_getter(int, int *); +static char *fs_dlist_getter(int, int *); + + +#define FLIST_SIZE 2048 + +typedef struct FLIST +{ + char dir[80]; + int size; + char *name[FLIST_SIZE]; +} FLIST; + +static FLIST *flist = NULL; + +static char *fext = NULL; + + + +static DIALOG file_selector[] = +{ + /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) */ + { d_3d_box, 0, 0, 304, 160, 0, 0, 0, 0, 0, 0, NULL }, + { d_inv_3d_box, 14, 26, 276, 12, 0, 0, 0, 0, 0, 0, NULL }, + { d_my_ctext_proc, 152, 8, 1, 1, 0, 0, 0, 0, 0, 0, NULL }, + { my_d_button_proc, 208, 107, 80, 16, 0, 0, 0, D_EXIT, 0, 0, "OK" }, + { my_d_button_proc, 208, 129, 80, 16, 0, 0, 27, D_EXIT, 0, 0, "Cancel" }, + { fs_edit_proc, 16, 28, 272, 8, 0, 0, 0, 0, 79, 0, NULL }, + { fs_flist_proc, 16, 46, 176, 99, 0, 0, 0, D_EXIT, 0, 0, fs_flist_getter }, + { fs_dlist_proc, 208, 46, 80, 51, 0, 0, 0, D_EXIT, 0, 0, fs_dlist_getter }, + { NULL } +}; + +#define FS_MESSAGE 2 +#define FS_OK 3 +#define FS_CANCEL 4 +#define FS_EDIT 5 +#define FS_FILES 6 +#define FS_DISKS 7 + + + +/* fs_dlist_getter: + * Listbox data getter routine for the file selector disk list. + */ +static char *fs_dlist_getter(int index, int *list_size) +{ + static char d[] = "A:\\"; + + if (index < 0) { + if (list_size) + *list_size = 26; + return NULL; + } + + d[0] = 'A' + index; + return d; +} + + + +/* fs_dlist_proc: + * Dialog procedure for the file selector disk list. + */ +static int fs_dlist_proc(int msg, DIALOG *d, int c) +{ + int ret; + char *s = file_selector[FS_EDIT].dp; + + if (msg == MSG_START) + d->d1 = d->d2 = 0; + + ret = my_d_list_proc(msg, d, c); + + if (ret == D_CLOSE) { + *(s++) = 'A' + d->d1; + *(s++) = ':'; + *(s++) = '\\'; + *s = 0; + show_mouse(NULL); + SEND_MESSAGE(file_selector+FS_FILES, MSG_START, 0); + SEND_MESSAGE(file_selector+FS_FILES, MSG_DRAW, 0); + SEND_MESSAGE(file_selector+FS_EDIT, MSG_START, 0); + SEND_MESSAGE(file_selector+FS_EDIT, MSG_DRAW, 0); + show_mouse(screen); + return D_O_K; + } + + return ret; +} + + + +/* fs_edit_proc: + * Dialog procedure for the file selector editable string. + */ +static int fs_edit_proc(int msg, DIALOG *d, int c) +{ + char *s = d->dp; + int ch; + int attr; + int x; + char b[80]; + + if (msg == MSG_START) { + if (s[0]) { + _fixpath(s, b); + errno = 0; + + x = s[strlen(s)-1]; + if ((x=='/') || (x=='\\')) + put_backslash(b); + + for (x=0; b[x]; x++) { + if (b[x] == '/') + s[x] = '\\'; + else + s[x] = toupper(b[x]); + } + s[x] = 0; + } + } + + if (msg == MSG_KEY) { + if (*s) + ch = s[strlen(s)-1]; + else + ch = 0; + if (ch == ':') + put_backslash(s); + else { + if ((ch != '/') && (ch != '\\')) { + if (file_exists(s, FA_RDONLY | FA_HIDDEN | FA_DIREC, &attr)) { + if (attr & FA_DIREC) + put_backslash(s); + else + return D_CLOSE; + } + else + return D_CLOSE; + } + } + show_mouse(NULL); + SEND_MESSAGE(file_selector+FS_FILES, MSG_START, 0); + SEND_MESSAGE(file_selector+FS_FILES, MSG_DRAW, 0); + SEND_MESSAGE(d, MSG_START, 0); + SEND_MESSAGE(d, MSG_DRAW, 0); + show_mouse(screen); + return D_O_K; + } + + if (msg==MSG_CHAR) { + ch = c & 0xff; + if ((ch >= 'a') && (ch <= 'z')) + c = (c & 0xffffff00L) | (ch - 'a' + 'A'); + else if (ch == '/') + c = (c & 0xffffff00L) | '\\'; + else if (_USE_LFN) { + if ((ch > 127) || ((ch < 32) && (ch != 8) && (ch != 0))) + return D_O_K; + } + else { + if ((ch != '\\') && (ch != '_') && (ch != ':') && (ch != '.') && + ((ch < 'A') || (ch > 'Z')) && ((ch < '0') || (ch > '9')) && + (ch != 8) && (ch != 127) && (ch != 0)) + return D_O_K; + } + } + + return d_edit_proc(msg, d, c); +} + + + +/* fs_flist_putter: + * Callback routine for for_each_file() to fill the file selector listbox. + */ +static void fs_flist_putter(char *str, int attrib) +{ + int c, c2; + char *s, *ext, *tok; + char tmp[80]; + static char ext_tokens[] = " ,;"; + + s = get_filename(str); + strupr(s); + + if ((fext) && (!(attrib & FA_DIREC))) { + strcpy(tmp, fext); + ext = get_extension(s); + tok = strtok(tmp, ext_tokens); + while (tok) { + if (stricmp(ext, tok) == 0) + break; + tok = strtok(NULL, ext_tokens); + } + if (!tok) + return; + } + + if ((flist->size < FLIST_SIZE) && (strcmp(s,".") != 0)) { + for (c=0; c<flist->size; c++) { + if (flist->name[c][strlen(flist->name[c])-1]=='\\') { + if (attrib & FA_DIREC) + if (strcmp(s, flist->name[c]) < 0) + break; + } + else { + if (attrib & FA_DIREC) + break; + if (strcmp(s, flist->name[c]) < 0) + break; + } + } + for (c2=flist->size; c2>c; c2--) + flist->name[c2] = flist->name[c2-1]; + + flist->name[c] = malloc(strlen(s) + ((attrib & FA_DIREC) ? 2 : 1)); + strcpy(flist->name[c], s); + if (attrib & FA_DIREC) + put_backslash(flist->name[c]); + flist->size++; + } +} + + + +/* fs_flist_getter: + * Listbox data getter routine for the file selector list. + */ +static char *fs_flist_getter(int index, int *list_size) +{ + if (index < 0) { + if (list_size) + *list_size = flist->size; + return NULL; + } + return flist->name[index]; +} + + + +/* fs_flist_proc: + * Dialog procedure for the file selector list. + */ +static int fs_flist_proc(int msg, DIALOG *d, int c) +{ + int i, ret; + int sel = d->d1; + char *s = file_selector[FS_EDIT].dp; + static int recurse_flag = 0; + + if (msg == MSG_START) { + if (!flist) + flist = malloc(sizeof(FLIST)); + else { + for (i=0; i<flist->size; i++) + if (flist->name[i]) + free(flist->name[i]); + } + if (!flist) { + errno = ENOMEM; + return D_CLOSE; + } + flist->size = 0; + for (i=0; i<flist->size; i++) + flist->name[i] = NULL; + strcpy(flist->dir, s); + *get_filename(flist->dir) = 0; + put_backslash(flist->dir); + strcat(flist->dir,"*.*"); + for_each_file(flist->dir, FA_RDONLY | FA_DIREC | FA_ARCH, fs_flist_putter, 0); + if (errno) + alert(NULL, "Disk error", NULL, "OK", NULL, 13, 0); + *get_filename(flist->dir) = 0; + d->d1 = d->d2 = 0; + sel = 0; + } + + if (msg == MSG_END) { + if (flist) { + for (i=0; i<flist->size; i++) + if (flist->name[i]) + free(flist->name[i]); + free(flist); + } + flist = NULL; + } + + recurse_flag++; + ret = my_d_list_proc(msg,d,c); /* call the parent procedure */ + recurse_flag--; + + if (((sel != d->d1) || (ret == D_CLOSE)) && (recurse_flag == 0)) { + strcpy(s, flist->dir); + *get_filename(s) = 0; + put_backslash(s); + strcat(s, flist->name[d->d1]); + show_mouse(NULL); + SEND_MESSAGE(file_selector+FS_EDIT, MSG_START, 0); + SEND_MESSAGE(file_selector+FS_EDIT, MSG_DRAW, 0); + show_mouse(screen); + + if (ret == D_CLOSE) + return SEND_MESSAGE(file_selector+FS_EDIT, MSG_KEY, 0); + } + + return ret; +} + + + +/* my_file_select: + * Displays the Allegro file selector, with the message as caption. + * Allows the user to select a file, and stores the selection in path + * (which should have room for at least 80 characters). The files are + * filtered according to the file extensions in ext. Passing NULL + * includes all files, "PCX;BMP" includes only files with .PCX or .BMP + * extensions. Returns zero if it was closed with the Cancel button, + * non-zero if it was OK'd. + */ +int my_file_select(char *message, char *path, char *ext) +{ + int ret; + char *p; + int f; + + f=0; + while(file_selector[f].proc) + { + switch(f) + { + case FS_EDIT: + case FS_FILES: + case FS_DISKS: + file_selector[f].fg=ACOL(BLACK); + file_selector[f].bg=ACOL(WHITE); + break; + + default: + file_selector[f].fg=gui_fg_color; + file_selector[f].bg=gui_bg_color; + break; + } + + f++; + } + + file_selector[FS_MESSAGE].dp = message; + file_selector[FS_EDIT].dp = path; + fext = ext; + + if (!path[0]) { + getcwd(path, 80); + for (p=path; *p; p++) + if (*p=='/') + *p = '\\'; + else + *p = toupper(*p); + + put_backslash(path); + } + + clear_keybuf(); + + do { + } while (mouse_b); + + centre_dialog(file_selector); + ret = do_dialog(file_selector, FS_EDIT); + + if ((ret == FS_CANCEL) || (*get_filename(path) == 0)) + return FALSE; + + p = get_extension(path); + if ((*p == 0) && (ext) && (!strpbrk(ext, " ,;"))) { + *p = '.'; + strcpy(p+1, ext); + } + + return TRUE; +} + + +/* ---------------------------------------- GUI MENU SUPPORT FUNCS +*/ +static void DrawMenuItem(BoundBox *b,char *txt,int sel,PLAT_MENU *child) +{ + int h,hy; + int fg; + + fg=ACOL(GUI_TEXT); + + if (sel) + Rect3D(b->x,b->y,b->w,b->h,FALSE,SMALL_BEVEL); + else + rectfill(screen,b->x,b->y,b->x+b->w-1,b->y+b->h-1,ACOL(GUI_MID)); + + text_mode(-1); + textout(screen,font,txt,b->x+SMALL_BEVEL+2,b->y+b->h/2-FH/2,fg); + + if (child) + { + h=b->h-(SMALL_BEVEL*2)-2; + hy=b->y+SMALL_BEVEL+1; + + line(screen,b->x+b->w-SMALL_BEVEL-2-h/2,hy, + b->x+b->w-SMALL_BEVEL-2,hy+h/2,ACOL(GUI_HI)); + + line(screen,b->x+b->w-SMALL_BEVEL-2-h/2,hy, + b->x+b->w-SMALL_BEVEL-2-h/2,hy+h,ACOL(GUI_HI)); + + line(screen,b->x+b->w-SMALL_BEVEL-2-h/2,hy+h, + b->x+b->w-SMALL_BEVEL-2,hy+h/2,ACOL(GUI_LO)); + } +} + + +static int MouseInBox(BoundBox *box,int no) +{ + int f; + + for(f=0;f<no;f++) + if (((mouse_x>=box[f].x)&&(mouse_x<box[f].x+box[f].w))&& + ((mouse_y>=box[f].y)&&(mouse_y<box[f].y+box[f].h))) + return(f); + + return(-1); +} + + +/* ---------------------------------------- CALLBACK AND HANDLER FUNCTIONS +*/ +static char *picklist_cb(int index,int *size) +{ + if (index<0) + { + *size=pick_no; + return(NULL); + } + else + return(pick_data[index]); +} + + +static char *img_picklist_cb(int index,int *size) +{ + if (index<0) + { + *size=img_pick_no; + return(NULL); + } + else + return(img_pick_data[index].text); +} + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static void init_picklist_dialog(char *title,int width) +{ + static int done=FALSE; + + width+=15; + + picklist[PL_BORDER].x=SCRW/2-width/2-20; + picklist[PL_BORDER].w=width+40; + + picklist[PL_TITLE].w=width+30; + picklist[PL_TITLE].dp=title; + + picklist[PL_PICKLIST].x=SCRW/2-width/2-10; + picklist[PL_PICKLIST].w=width+20; + picklist[PL_PICKLIST].d1=0; + picklist[PL_PICKLIST].d2=0; + + picklist[PL_OK].x=picklist[PL_PICKLIST].x; + picklist[PL_OK].w=FW*8; + picklist[PL_CANCEL].x=picklist[PL_OK].x+picklist[PL_OK].w+20; + picklist[PL_CANCEL].w=picklist[PL_OK].w; + + if (done) + return; + + done=TRUE; + + /* Box + */ + picklist[PL_BORDER].proc=d_3d_box; + picklist[PL_BORDER].y=10; + picklist[PL_BORDER].h=SCRH-20; + picklist[PL_BORDER].fg=ACOL(GUI_TEXT); + picklist[PL_BORDER].bg=ACOL(GUI_MID); + picklist[PL_BORDER].key=0; + picklist[PL_BORDER].flags=0; + picklist[PL_BORDER].d1=picklist[PL_BORDER].d2=0; + picklist[PL_BORDER].dp=picklist[PL_BORDER].dp2=picklist[PL_BORDER].dp3=NULL; + + /* Title + */ + picklist[PL_TITLE].proc=d_my_ctext_proc; + picklist[PL_TITLE].x=SCRW/2; + picklist[PL_TITLE].y=20; + picklist[PL_TITLE].h=10; + picklist[PL_TITLE].fg=ACOL(GUI_TEXTBOLD); + picklist[PL_TITLE].bg=ACOL(GUI_MID); + picklist[PL_TITLE].key=0; + picklist[PL_TITLE].flags=0; + picklist[PL_TITLE].d1=picklist[PL_TITLE].d2=0; + picklist[PL_TITLE].dp2=picklist[PL_TITLE].dp3=NULL; + + /* List + */ + picklist[PL_PICKLIST].proc=my_d_list_proc; + picklist[PL_PICKLIST].y=30; + picklist[PL_PICKLIST].h=SCRH-60-FH*2; + picklist[PL_PICKLIST].fg=ACOL(BLACK); + picklist[PL_PICKLIST].bg=ACOL(WHITE); + picklist[PL_PICKLIST].key=0; + picklist[PL_PICKLIST].flags=D_EXIT; + picklist[PL_PICKLIST].dp=picklist_cb; + picklist[PL_PICKLIST].dp2=picklist[PL_PICKLIST].dp3=NULL; + + /* OK + */ + picklist[PL_OK].proc=my_d_button_proc; + picklist[PL_OK].y=picklist[PL_PICKLIST].y+picklist[PL_PICKLIST].h+7; + picklist[PL_OK].h=FH+3+BEVEL; + picklist[PL_OK].fg=ACOL(GUI_TEXT); + picklist[PL_OK].bg=ACOL(GUI_MID); + picklist[PL_OK].key=0; + picklist[PL_OK].flags=D_EXIT; + picklist[PL_OK].d1=picklist[PL_OK].d2=0; + picklist[PL_OK].dp="OK"; + picklist[PL_OK].dp2=picklist[PL_OK].dp3=NULL; + + /* Cancel + */ + picklist[PL_CANCEL].proc=my_d_button_proc; + picklist[PL_CANCEL].y=picklist[PL_OK].y; + picklist[PL_CANCEL].h=FH+3+BEVEL; + picklist[PL_CANCEL].fg=ACOL(GUI_TEXT); + picklist[PL_CANCEL].bg=ACOL(GUI_MID); + picklist[PL_CANCEL].key=0; + picklist[PL_CANCEL].flags=D_EXIT; + picklist[PL_CANCEL].d1=picklist[PL_CANCEL].d2=0; + picklist[PL_CANCEL].dp="Cancel"; + picklist[PL_CANCEL].dp2=picklist[PL_CANCEL].dp3=NULL; + + memset(&picklist[PL_END],0,sizeof(DIALOG)); + picklist[PL_END].proc=NULL; +} + + +static void init_img_picklist_dialog(char *title) +{ + static int done=FALSE; + + img_picklist[IPL_TITLE].dp=title; + + img_picklist[IPL_PICKLIST].d1=0; + img_picklist[IPL_PICKLIST].d2=0; + + img_picklist[IPL_IMAGE].dp=img_pick_data[0].img; + + if (done) + return; + + done=TRUE; + + /* Box + */ + img_picklist[IPL_BORDER].proc=d_3d_box; + img_picklist[IPL_BORDER].y=10; + img_picklist[IPL_BORDER].h=SCRH-20; + img_picklist[IPL_BORDER].x=10; + img_picklist[IPL_BORDER].w=SCRW-20; + img_picklist[IPL_BORDER].fg=ACOL(GUI_TEXT); + img_picklist[IPL_BORDER].bg=ACOL(GUI_MID); + img_picklist[IPL_BORDER].key=0; + img_picklist[IPL_BORDER].flags=0; + img_picklist[IPL_BORDER].d1=img_picklist[IPL_BORDER].d2=0; + img_picklist[IPL_BORDER].dp=img_picklist[IPL_BORDER].dp2= + img_picklist[IPL_BORDER].dp3=NULL; + + /* Title + */ + img_picklist[IPL_TITLE].proc=d_my_ctext_proc; + img_picklist[IPL_TITLE].x=SCRW/2; + img_picklist[IPL_TITLE].w=SCRW-20; + img_picklist[IPL_TITLE].y=20; + img_picklist[IPL_TITLE].h=10; + img_picklist[IPL_TITLE].fg=ACOL(GUI_TEXTBOLD); + img_picklist[IPL_TITLE].bg=ACOL(GUI_MID); + img_picklist[IPL_TITLE].key=0; + img_picklist[IPL_TITLE].flags=0; + img_picklist[IPL_TITLE].d1=img_picklist[IPL_TITLE].d2=0; + img_picklist[IPL_TITLE].dp2=img_picklist[IPL_TITLE].dp3=NULL; + + /* List + */ + img_picklist[IPL_PICKLIST].proc=d_img_list_proc; + img_picklist[IPL_PICKLIST].y=30; + img_picklist[IPL_PICKLIST].h=SCRH-60-FH*2; + img_picklist[IPL_PICKLIST].x=30; + img_picklist[IPL_PICKLIST].w=SCRW/2; + img_picklist[IPL_PICKLIST].fg=ACOL(BLACK); + img_picklist[IPL_PICKLIST].bg=ACOL(WHITE); + img_picklist[IPL_PICKLIST].key=0; + img_picklist[IPL_PICKLIST].flags=D_EXIT; + img_picklist[IPL_PICKLIST].dp=img_picklist_cb; + img_picklist[IPL_PICKLIST].dp2=img_picklist[IPL_PICKLIST].dp3=NULL; + + /* Image border + */ + img_picklist[IPL_IMG_BORDER].proc=d_inv_3d_box; + img_picklist[IPL_IMG_BORDER].y=30; + img_picklist[IPL_IMG_BORDER].h=MAX_GFXBM_H+4; + img_picklist[IPL_IMG_BORDER].x=SCRW-MAX_GFXBM_W-24; + img_picklist[IPL_IMG_BORDER].w=MAX_GFXBM_W+4; + img_picklist[IPL_IMG_BORDER].fg=ACOL(WHITE); + img_picklist[IPL_IMG_BORDER].bg=ACOL(BLACK); + img_picklist[IPL_IMG_BORDER].key=0; + img_picklist[IPL_IMG_BORDER].flags=0; + img_picklist[IPL_IMG_BORDER].dp=img_picklist[IPL_IMG_BORDER].dp2= + img_picklist[IPL_IMG_BORDER].dp3=NULL; + img_picklist[IPL_IMG_BORDER].d1=img_picklist[IPL_IMG_BORDER].d2=0; + + /* Image + */ + img_picklist[IPL_IMAGE].proc=d_img_bmap_proc; + img_picklist[IPL_IMAGE].x=SCRW-MAX_GFXBM_W-22; + img_picklist[IPL_IMAGE].y=32; + img_picklist[IPL_IMAGE].w=MAX_GFXBM_W; + img_picklist[IPL_IMAGE].h=MAX_GFXBM_H; + img_picklist[IPL_IMAGE].fg=ACOL(0); + img_picklist[IPL_IMAGE].bg=ACOL(0); + img_picklist[IPL_IMAGE].key=0; + img_picklist[IPL_IMAGE].flags=0; + img_picklist[IPL_IMAGE].dp2=img_picklist[IPL_IMAGE].dp3=NULL; + img_picklist[IPL_IMAGE].d1=img_picklist[IPL_IMAGE].d2=0; + + /* OK + */ + img_picklist[IPL_OK].proc=my_d_button_proc; + img_picklist[IPL_OK].x=img_picklist[IPL_PICKLIST].x; + img_picklist[IPL_OK].y=img_picklist[IPL_PICKLIST].y+ + img_picklist[IPL_PICKLIST].h+7; + img_picklist[IPL_OK].w=FW*8; + img_picklist[IPL_OK].h=FH+3+BEVEL; + img_picklist[IPL_OK].fg=ACOL(GUI_TEXT); + img_picklist[IPL_OK].bg=ACOL(GUI_MID); + img_picklist[IPL_OK].key=0; + img_picklist[IPL_OK].flags=D_EXIT; + img_picklist[IPL_OK].d1=img_picklist[IPL_OK].d2=0; + img_picklist[IPL_OK].dp="OK"; + img_picklist[IPL_OK].dp2=img_picklist[IPL_OK].dp3=NULL; + + /* Cancel + */ + img_picklist[IPL_CANCEL].proc=my_d_button_proc; + img_picklist[IPL_CANCEL].x=img_picklist[IPL_OK].x+img_picklist[IPL_OK].w+20; + img_picklist[IPL_CANCEL].y=img_picklist[IPL_OK].y; + img_picklist[IPL_CANCEL].w=img_picklist[IPL_OK].w; + img_picklist[IPL_CANCEL].h=FH+3+BEVEL; + img_picklist[IPL_CANCEL].fg=ACOL(GUI_TEXT); + img_picklist[IPL_CANCEL].bg=ACOL(GUI_MID); + img_picklist[IPL_CANCEL].key=0; + img_picklist[IPL_CANCEL].flags=D_EXIT; + img_picklist[IPL_CANCEL].d1=img_picklist[IPL_CANCEL].d2=0; + img_picklist[IPL_CANCEL].dp="Cancel"; + img_picklist[IPL_CANCEL].dp2=img_picklist[IPL_CANCEL].dp3=NULL; + + memset(&img_picklist[IPL_END],0,sizeof(DIALOG)); + img_picklist[IPL_END].proc=NULL; +} + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ + +void GUI_setscreen(int w,int h) +{ + gui_fg_color=ACOL(GUI_TEXT); + gui_bg_color=ACOL(GUI_MID); + + /* Disabled objects are used for bold text + */ + gui_mg_color=ACOL(GUI_TEXTBOLD); + + SCRW=w; + SCRH=h; + + FW=GFX_fw(); + FH=GFX_fh(); +} + + +int GUI_yesno(char *question) +{ + DIALOG *d; + char *t; + char *p; + int no; + char *tl[64]; /* Should be enough... */ + int ml,l; + int yl; + int f; + int h; + int x,y; + int ret; + + t=Strdup(question); + + no=0; + p=t; + + tl[no++]=p; + + while(*p) + { + if (*p=='|') + { + *p++=0; + + if (*p) + tl[no++]=p; + } + else + p++; + } + + h=(FH+1)*(no+1)+FH*4; + + ml=FW*14; + + for(f=0;f<no;f++) + { + l=(strlen(tl[f])+2)*FW; + ml=MAX(ml,l); + } + + x=SCRW/2-ml/2; + y=SCRH/2-h/2; + + d=Grab(sizeof(DIALOG)*(3+no)); + + /* Surrounding box + */ + d[0].proc=d_3d_box; + d[0].x=x; + d[0].y=y; + d[0].w=ml; + d[0].h=h; + d[0].fg=ACOL(GUI_TEXT); + d[0].bg=ACOL(GUI_MID); + d[0].key=0; + d[0].flags=0; + d[0].d1=d[0].d2=0; + d[0].dp=d[0].dp2=d[0].dp3=NULL; + + /* Text + */ + yl=d[0].y+FH; + + for(f=0;f<no;f++) + { + d[1+f].proc=d_my_ctext_proc; + d[1+f].x=SCRW/2; + d[1+f].w=ml-FW; + d[1+f].y=yl; + d[1+f].h=FH; + d[1+f].fg=ACOL(GUI_TEXT); + d[1+f].bg=ACOL(GUI_MID); + d[1+f].key=0; + d[1+f].flags=0; + d[1+f].d1=d[1+f].d2=0; + d[1+f].dp=tl[f]; + d[1+f].dp2=d[1+f].dp3=NULL; + + yl+=FH+1; + } + + /* Yes + */ + d[1+no].proc=my_d_button_proc; + d[1+no].x=x+10; + d[1+no].w=(ml/2)-20; + d[1+no].h=FH+3+BEVEL; + d[1+no].y=y+h-d[1+no].h-5-BEVEL; + d[1+no].fg=ACOL(GUI_TEXT); + d[1+no].bg=ACOL(GUI_MID); + d[1+no].key='Y'; + d[1+no].flags=D_EXIT; + d[1+no].d1=d[1+no].d2=0; + d[1+no].dp="&Yes"; + d[1+no].dp2=d[1+no].dp3=NULL; + + /* No + */ + d[2+no].proc=my_d_button_proc; + d[2+no].x=SCRW/2+10; + d[2+no].y=d[1+no].y; + d[2+no].w=(ml/2)-20; + d[2+no].h=d[1+no].h; + d[2+no].fg=ACOL(GUI_TEXT); + d[2+no].bg=ACOL(GUI_MID); + d[2+no].key='N'; + d[2+no].flags=D_EXIT; + d[2+no].d1=d[2+no].d2=0; + d[2+no].dp="&No"; + d[2+no].dp2=d[2+no].dp3=NULL; + + d[3+no].proc=NULL; + + GFX_bounce(); + + if(do_dialog(d,-1)==(1+no)) + ret=TRUE; + else + ret=FALSE; + + GFX_FORCE_REDRAW(); + + Release(d); + Release(t); + + return(ret); +} + + +void GUI_start_yesno_all(void) +{ + all_pressed=FALSE; + all_result=FALSE; +} + + +int GUI_yesno_all(char *question) +{ + DIALOG *d; + char *t; + char *p; + int no; + char *tl[64]; /* Should be enough... */ + int ml,l; + int yl; + int f; + int h; + int x,y; + int ret; + + if (all_pressed) + return(all_result); + + t=Strdup(question); + + no=0; + p=t; + + tl[no++]=p; + + while(*p) + { + if (*p=='|') + { + *p++=0; + + if (*p) + tl[no++]=p; + } + else + p++; + } + + h=(FH+1)*(no+1)+FH*5; + + ml=FW*26; + + for(f=0;f<no;f++) + { + l=(strlen(tl[f])+2)*FW; + ml=MAX(ml,l); + } + + x=SCRW/2-ml/2; + y=SCRH/2-h/2; + + d=Grab(sizeof(DIALOG)*(5+no)); + + /* Surrounding box + */ + d[0].proc=d_3d_box; + d[0].x=x; + d[0].y=y; + d[0].w=ml; + d[0].h=h; + d[0].fg=ACOL(GUI_TEXT); + d[0].bg=ACOL(GUI_MID); + d[0].key=0; + d[0].flags=0; + d[0].d1=d[0].d2=0; + d[0].dp=d[0].dp2=d[0].dp3=NULL; + + /* Text + */ + yl=d[0].y+FH; + + for(f=0;f<no;f++) + { + d[1+f].proc=d_my_ctext_proc; + d[1+f].x=SCRW/2; + d[1+f].w=ml-FW; + d[1+f].y=yl; + d[1+f].h=FH; + d[1+f].fg=ACOL(GUI_TEXT); + d[1+f].bg=ACOL(GUI_MID); + d[1+f].key=0; + d[1+f].flags=0; + d[1+f].d1=d[1+f].d2=0; + d[1+f].dp=tl[f]; + d[1+f].dp2=d[1+f].dp3=NULL; + + yl+=FH+1; + } + + /* Yes + */ + d[1+no].proc=my_d_button_proc; + d[1+no].x=x+10; + d[1+no].w=(ml/2)-20; + d[1+no].h=FH+3+BEVEL; + d[1+no].y=y+h-d[1+no].h-5-BEVEL; + d[1+no].fg=ACOL(GUI_TEXT); + d[1+no].bg=ACOL(GUI_MID); + d[1+no].key='Y'; + d[1+no].flags=D_EXIT; + d[1+no].d1=d[1+no].d2=0; + d[1+no].dp="&Yes"; + d[1+no].dp2=d[1+no].dp3=NULL; + + /* No + */ + d[2+no].proc=my_d_button_proc; + d[2+no].x=SCRW/2+10; + d[2+no].y=d[1+no].y; + d[2+no].w=(ml/2)-20; + d[2+no].h=d[1+no].h; + d[2+no].fg=ACOL(GUI_TEXT); + d[2+no].bg=ACOL(GUI_MID); + d[2+no].key='N'; + d[2+no].flags=D_EXIT; + d[2+no].d1=d[2+no].d2=0; + d[2+no].dp="&No"; + d[2+no].dp2=d[2+no].dp3=NULL; + + /* Yes to All + */ + d[3+no].proc=my_d_button_proc; + d[3+no].x=x+10; + d[3+no].w=(ml/2)-20; + d[3+no].h=FH+3+BEVEL; + d[3+no].y=y+h-d[1+no].h-5-BEVEL; + d[3+no].fg=ACOL(GUI_TEXT); + d[3+no].bg=ACOL(GUI_MID); + d[3+no].key='A'; + d[3+no].flags=D_EXIT; + d[3+no].d1=d[3+no].d2=0; + d[3+no].dp="Yes to &All"; + d[3+no].dp2=d[3+no].dp3=NULL; + + /* Yes to All + */ + d[4+no].proc=my_d_button_proc; + d[4+no].x=SCRW/2+10; + d[4+no].w=(ml/2)-20; + d[4+no].h=FH+3+BEVEL; + d[4+no].y=y+h-d[1+no].h-5-BEVEL; + d[4+no].fg=ACOL(GUI_TEXT); + d[4+no].bg=ACOL(GUI_MID); + d[4+no].key='O'; + d[4+no].flags=D_EXIT; + d[4+no].d1=d[4+no].d2=0; + d[4+no].dp="N&o to All"; + d[4+no].dp2=d[4+no].dp3=NULL; + + d[1+no].y=d[3+no].y-FH*2; + d[2+no].y=d[4+no].y-FH*2; + + d[5+no].proc=NULL; + + GFX_bounce(); + + ret=do_dialog(d,-1); + + if (ret==(1+no)) + all_result=TRUE; + else if (ret==(2+no)) + all_result=FALSE; + else if (ret==(3+no)) + { + all_result=TRUE; + all_pressed=TRUE; + } + else if (ret==(4+no)) + { + all_result=FALSE; + all_pressed=TRUE; + } + + GFX_FORCE_REDRAW(); + + Release(d); + Release(t); + + return(all_result); +} + + +void GUI_alert(char *title, char *text, char *button_text) +{ + DIALOG *d; + char *t; + char *p; + int no; + char *tl[64]; /* Should be enough... */ + int ml,l; + int yl; + int f; + int h; + int x,y; + + t=Strdup(text); + + no=0; + p=t; + + tl[no++]=p; + + while(*p) + { + if (*p=='|') + { + *p++=0; + + if (*p) + tl[no++]=p; + } + else + p++; + } + + h=(FH+1)*(no+2)+FH*4; + + ml=(strlen(button_text)+2)*FW; + + l=(strlen(title)+2)*FW; + ml=MAX(ml,l); + + for(f=0;f<no;f++) + { + l=(strlen(tl[f])+2)*FW; + ml=MAX(ml,l); + } + + x=SCRW/2-ml/2; + y=SCRH/2-h/2; + + d=Grab(sizeof(DIALOG)*(4+no)); + + /* Surrounding box + */ + d[0].proc=d_3d_box; + d[0].x=x; + d[0].y=y; + d[0].w=ml; + d[0].h=h; + d[0].fg=ACOL(GUI_TEXT); + d[0].bg=ACOL(GUI_MID); + d[0].key=0; + d[0].flags=0; + d[0].d1=d[0].d2=0; + d[0].dp=d[0].dp2=d[0].dp3=NULL; + + /* Title + */ + d[1].proc=d_title_proc; + d[1].x=SCRW/2; + d[1].w=ml-FW; + d[1].y=y+BEVEL+2; + d[1].h=FH; + d[1].fg=ACOL(GUI_TEXTBOLD); + d[1].bg=ACOL(GUI_MID); + d[1].key=0; + d[1].flags=0; + d[1].d1=d[1].d2=0; + d[1].dp=title; + d[1].dp2=d[1].dp3=NULL; + + /* Text + */ + yl=d[1].y+FH*2; + + for(f=0;f<no;f++) + { + d[2+f].proc=d_my_ctext_proc; + d[2+f].x=SCRW/2; + d[2+f].w=ml-FW; + d[2+f].y=yl; + d[2+f].h=FH; + d[2+f].fg=ACOL(GUI_TEXT); + d[2+f].bg=ACOL(GUI_MID); + d[2+f].key=0; + d[2+f].flags=0; + d[2+f].d1=d[2+f].d2=0; + d[2+f].dp=tl[f]; + d[2+f].dp2=d[2+f].dp3=NULL; + + yl+=FH+1; + } + + /* Button + */ + d[2+no].proc=my_d_button_proc; + d[2+no].w=((strlen(button_text)+1)*FW)+BEVEL*2; + d[2+no].x=SCRW/2-d[2+no].w/2; + d[2+no].h=FH+3+BEVEL; + d[2+no].y=y+h-d[2+no].h-5-BEVEL; + d[2+no].fg=ACOL(GUI_TEXT); + d[2+no].bg=ACOL(GUI_MID); + d[2+no].key='\r'; + d[2+no].flags=D_EXIT; + d[2+no].d1=d[2+no].d2=0; + d[2+no].dp=button_text; + d[2+no].dp2=d[2+no].dp3=NULL; + + d[3+no].proc=NULL; + + GFX_bounce(); + + do_dialog(d,2+no); + + GFX_FORCE_REDRAW(); + Release(d); + Release(t); +} + + +static MenuControl do_GUI_menu(char *title, int x, int y, + PLAT_MENU menu[],int defval,int is_child) +{ + MenuControl ret; + MenuControl cr; + int lx,ly; + int no; + int f; + BITMAP *under; + int cur; + int last_cur; + int cx; + int by; + int done; + int quit; + int child_done; + BoundBox *box; + BoundBox menu_box; + int do_child; + + /* Calc dimensions + */ + no=0; + menu_box.x=x; + menu_box.y=y; + menu_box.w=text_length(font,title); + + while(menu[no].text) + { + int l; + + l=text_length(font,menu[no].text)+(menu[no].child ? 12 : 0); + menu_box.w=MAX(menu_box.w,l); + no++; + } + + menu_box.h=BEVEL*2+FH+4; + menu_box.h+=(FH+SMALL_BEVEL*2+2)*no; + menu_box.h+=2; + menu_box.w+=BEVEL+SMALL_BEVEL+4; + + box=Grab(sizeof(BoundBox)*no); + + if ((menu_box.x+menu_box.w)>SCRW) + menu_box.x=SCRW-menu_box.w; + + if ((menu_box.y+menu_box.h)>SCRH) + menu_box.y=SCRH-menu_box.h; + + if ((menu_box.x<0)||(menu_box.y<0)) + { + alert("Cannot display menu:",title,"Menu is bigger than display!", + "OK",NULL,0,0); + + ret.mode=MENU_CHILD_RETURN; + ret.ret=defval; + + return(ret); + } + + cx=menu_box.x+menu_box.w/2; + + /* Save screen contents + */ + show_mouse(NULL); + + if (!(under=create_bitmap(menu_box.w+1,menu_box.h+1))) + GFX_exit(EXIT_FAILURE,"No more memory to create MENU save under\n"); + + blit(screen,under,menu_box.x,menu_box.y,0,0,menu_box.w+1,menu_box.h+1); + + /* Draw menu border and title + */ + Rect3D(menu_box.x,menu_box.y,menu_box.w,menu_box.h,FALSE,BEVEL); + text_mode(-1); + textout_centre(screen,font,title,cx,menu_box.y+BEVEL*2,ACOL(GUI_TEXTBOLD)); + + /* Draw menu items + */ + cur=0; + last_cur=-1; + + by=menu_box.y+BEVEL*2+FH+4; + + for(f=0;f<no;f++) + { + box[f].x=menu_box.x+BEVEL; + box[f].y=by; + box[f].w=menu_box.w-BEVEL*2; + box[f].h=FH+SMALL_BEVEL*2+2; + by+=box[f].h; + + DrawMenuItem(&box[f],menu[f].text,(f==cur),menu[f].child); + } + + show_mouse(screen); + GFX_bounce(); + + /* Main loop + */ + quit=FALSE; + done=FALSE; + child_done=FALSE; + lx=mouse_x; + ly=mouse_y; + do_child=FALSE; + + while((!done)&&(!quit)&&(!child_done)) + { + /* Handle input + */ + if (!do_child) + { + if (mouse_b) + { + int new; + + new=MouseInBox(box,no); + + if (new==-1) + { + if (MouseInBox(&menu_box,1)==-1) + { + quit=TRUE; + ret.mode=MENU_CANCEL_CLICK; + + while(mouse_b); + } + } + else + { + if (menu[new].child) + { + cur=new; + while(mouse_b); + do_child=TRUE; + } + else + { + done=TRUE; + cur=new; + ret.mode=MENU_OK; + while(mouse_b); + } + } + } + else if (keypressed()) + switch(readkey()>>8) + { + case KEY_ESC: + quit=TRUE; + ret.mode=MENU_CANCEL_ESC; + break; + + case KEY_ENTER: + ret.mode=MENU_OK; + done=TRUE; + break; + + case KEY_DOWN: + show_mouse(NULL); + DrawMenuItem(&box[cur],menu[cur].text,FALSE, + menu[cur].child); + + cur=(cur+1)%no; + + DrawMenuItem(&box[cur],menu[cur].text,TRUE, + menu[cur].child); + show_mouse(screen); + break; + + case KEY_UP: + show_mouse(NULL); + DrawMenuItem(&box[cur],menu[cur].text,FALSE, + menu[cur].child); + + if (cur) + cur--; + else + cur=no-1; + + DrawMenuItem(&box[cur],menu[cur].text,TRUE, + menu[cur].child); + show_mouse(screen); + break; + + case KEY_RIGHT: + if (menu[cur].child) + do_child=TRUE; + + break; + + case KEY_LEFT: + if (is_child) + { + quit=TRUE; + ret.mode=MENU_CHILD_RETURN; + } + break; + } + else if ((lx!=mouse_x)||(ly!=mouse_y)) + { + int new; + + lx=mouse_x; + ly=mouse_y; + + new=MouseInBox(box,no); + + if (new!=-1) + cur=new; + } + } + + + /* Draw menu selection if changed + */ + if (cur!=last_cur) + { + show_mouse(NULL); + + if (last_cur!=-1) + DrawMenuItem(&box[last_cur],menu[last_cur].text,FALSE, + menu[last_cur].child); + + DrawMenuItem(&box[cur],menu[cur].text,TRUE, + menu[cur].child); + show_mouse(screen); + } + + last_cur=cur; + + /* Handle child menus + */ + if (do_child) + { + do_child=FALSE; + + cr=do_GUI_menu(menu[cur].text, + box[cur].x+box[cur].w,box[cur].y, + menu[cur].child,defval,TRUE); + + switch(cr.mode) + { + case MENU_OK: + child_done=TRUE; + break; + + case MENU_CANCEL_ESC: + quit=TRUE; + ret.mode=MENU_CANCEL_ESC; + break; + + case MENU_CANCEL_CLICK: + if (MouseInBox(&menu_box,1)==-1) + { + quit=TRUE; + ret.mode=MENU_CANCEL_CLICK; + } + else + { + if ((cur=MouseInBox(box,no))==-1) + cur=0; + else if (menu[cur].child) + do_child=TRUE; + } + + break; + + case MENU_CHILD_RETURN: + break; + } + } + } + + show_mouse(NULL); + blit(under,screen,0,0,menu_box.x,menu_box.y,menu_box.w+1,menu_box.h+1); + show_mouse(screen); + destroy_bitmap(under); + Release(box); + + if (child_done) + ret=cr; + else + { + if (!quit) + ret.ret=menu[cur].client_index; + else + ret.ret=defval; + } + + return(ret); +} + + +int GUI_menu(char *title, int x, int y, PLAT_MENU menu[],int defval) +{ + return(do_GUI_menu(title,x,y,menu,defval,FALSE).ret); +} + + +char *GUI_fsel(char *title, char *default_path, char *filter) +{ + int ret; + char path[PATH_MAX+1]; + + if ((filter)&&(*filter=='.')) + filter++; + + strcpy(path,default_path); + + GFX_bounce(); + + ret=my_file_select(title,path,filter); + + GFX_FORCE_REDRAW(); + + if (ret) + return(Strdup(path)); + + return(NULL); +} + + +int GUI_picklist(char *title,char *opt[]) +{ + int ml; + int ret; + + pick_no=0; + + /* Enough room for the OK/CANCEL + */ + ml=(FW*16)+30; + ml=MAX(ml,gui_strlen(title)); + + while(opt[pick_no]) + { + if (gui_strlen(opt[pick_no])>ml) + ml=gui_strlen(opt[pick_no]); + pick_no++; + } + + pick_data=opt; + + init_picklist_dialog(title,ml); + + GFX_bounce(); + ret=do_dialog(picklist,-1); + GFX_FORCE_REDRAW(); + + switch(ret) + { + case PL_OK: + case PL_PICKLIST: + return(picklist[IPL_PICKLIST].d1); + break; + + default: + return(-1); + break; + } +} + + +int GUI_client_picklist(char *title,PLAT_PICKLIST opt[],int defval) +{ + int ml; + int ret; + char **c_opt; + int f; + + pick_no=0; + + /* Enough room for the OK/CANCEL + */ + ml=(FW*16)+30; + ml=MAX(ml,gui_strlen(title)); + + while(opt[pick_no].text) + { + if (gui_strlen(opt[pick_no].text)>ml) + ml=gui_strlen(opt[pick_no].text); + pick_no++; + } + + c_opt=Grab(sizeof(char *)*(pick_no+1)); + + for(f=0;f<pick_no;f++) + c_opt[f]=opt[f].text; + + c_opt[f]=NULL; + + pick_data=c_opt; + + init_picklist_dialog(title,ml); + + GFX_bounce(); + ret=do_dialog(picklist,-1); + GFX_FORCE_REDRAW(); + Release(c_opt); + + switch(ret) + { + case PL_OK: + case PL_PICKLIST: + return(opt[picklist[PL_PICKLIST].d1].client_index); + break; + + default: + return(defval); + break; + } +} + + +int GUI_image_picklist(char *title,PLAT_IMG_PICKLIST opt[],int defval) +{ + int ret; + + img_pick_no=0; + while(opt[img_pick_no].text) + img_pick_no++; + + img_pick_data=opt; + + init_img_picklist_dialog(title); + + GFX_bounce(); + ret=do_dialog(img_picklist,-1); + GFX_FORCE_REDRAW(); + + if (ret==-1) + return(defval); + + switch(ret) + { + case IPL_OK: + case IPL_PICKLIST: + return(opt[img_picklist[IPL_PICKLIST].d1].client_index); + break; + + default: + return(defval); + break; + } +} + + +int GUI_radio_box(char *title,PLAT_RADIO opt[],int current,int defval) +{ + int no; + DIALOG *d; + int ml,l; + int h; + int f; + int ret; + int x,y; + + no=0; + while(opt[no].text) + no++; + + d=Grab(sizeof(DIALOG)*(no+6)); + + h=(FH+5)*no; + ml=text_length(font,"CANCEL CANCEL"); + + for(f=0;f<no;f++) + if ((l=text_length(font,opt[f].text))>ml) + ml=l; + + h+=FH*4; + ml+=FW*4; + x=SCRW/2-ml/2; + y=SCRH/2-h/2; + + /* Surrounding box + */ + d[0].proc=d_3d_box; + d[0].x=x; + d[0].y=y; + d[0].w=ml; + d[0].h=h; + d[0].fg=ACOL(GUI_TEXT); + d[0].bg=ACOL(GUI_MID); + d[0].key=0; + d[0].flags=0; + d[0].d1=d[0].d2=0; + d[0].dp=d[0].dp2=d[0].dp3=NULL; + + /* Title + */ + d[1].proc=d_my_ctext_proc; + d[1].x=SCRW/2; + d[1].w=ml-20; + d[1].y=y+10; + d[1].h=10; + d[1].fg=ACOL(GUI_TEXTBOLD); + d[1].bg=ACOL(GUI_MID); + d[1].key=0; + d[1].flags=0; + d[1].d1=d[1].d2=0; + d[1].dp=title; + d[1].dp2=d[1].dp3=NULL; + + /* OK + */ + d[2].proc=my_d_button_proc; + d[2].x=x+10; + d[2].y=y+h-15; + d[2].w=(ml/2)-20; + d[2].h=10+BEVEL; + d[2].fg=ACOL(GUI_TEXT); + d[2].bg=ACOL(GUI_MID); + d[2].key=0; + d[2].flags=D_EXIT; + d[2].d1=d[2].d2=0; + d[2].dp="OK"; + d[2].dp2=d[2].dp3=NULL; + + /* CANCEL + */ + d[3].proc=my_d_button_proc; + d[3].x=SCRW/2+10; + d[3].y=y+h-15; + d[3].w=(ml/2)-20; + d[3].h=10+BEVEL; + d[3].fg=ACOL(GUI_TEXT); + d[3].bg=ACOL(GUI_MID); + d[3].key=0; + d[3].flags=D_EXIT; + d[3].d1=d[3].d2=0; + d[3].dp="Cancel"; + d[3].dp2=d[3].dp3=NULL; + + /* Buttons + */ + for(f=0;f<no;f++) + { + d[4+f].proc=my_d_radio_proc; + d[4+f].x=x+10; + d[4+f].y=y+20+f*10; + d[4+f].w=ml-20; + d[4+f].h=10; + d[4+f].fg=ACOL(GUI_TEXT); + d[4+f].bg=ACOL(GUI_MID); + d[4+f].key=0; + d[4+f].flags=(f==0 ? D_SELECTED : 0); + d[4+f].d1=d[4+f].d2=0; + d[4+f].dp=opt[f].text; + d[4+f].dp2=d[4+f].dp3=NULL; + } + + d[4+no].proc=NULL; + + for(f=0;f<no;f++) + if (opt[f].client_index==current) + { + d[4].flags=0; + d[4+f].flags=D_SELECTED; + } + + ret=defval; + + GFX_bounce(); + if(do_dialog(d,-1)==2) + for(f=0;f<no;f++) + if (d[4+f].flags&D_SELECTED) + ret=opt[f].client_index; + + Release(d); + + return(ret); +} + + +int GUI_multi_box(char *title,PLAT_MULTI p[]) +{ + int no; + DIALOG *d; + int ml,l; + int h; + int f; + int ret; + int x,y; + + no=0; + while(p[no].text) + no++; + + d=Grab(sizeof(DIALOG)*(no+6)); + + h=(FH+5)*no; + ml=text_length(font,"CANCEL CANCEL"); + + for(f=0;f<no;f++) + if ((l=text_length(font,p[f].text))>ml) + ml=l; + + h+=FH*4; + ml+=FW*4; + x=SCRW/2-ml/2; + y=SCRH/2-h/2; + + /* Surrounding box + */ + d[0].proc=d_3d_box; + d[0].x=x; + d[0].y=y; + d[0].w=ml; + d[0].h=h; + d[0].fg=ACOL(GUI_TEXT); + d[0].bg=ACOL(GUI_MID); + d[0].key=0; + d[0].flags=0; + d[0].d1=d[0].d2=0; + d[0].dp=d[0].dp2=d[0].dp3=NULL; + + /* Title + */ + d[1].proc=d_my_ctext_proc; + d[1].x=SCRW/2; + d[1].w=ml-20; + d[1].y=y+10; + d[1].h=10; + d[1].fg=ACOL(GUI_TEXTBOLD); + d[1].bg=ACOL(GUI_MID); + d[1].key=0; + d[1].flags=0; + d[1].d1=d[1].d2=0; + d[1].dp=title; + d[1].dp2=d[1].dp3=NULL; + + /* OK + */ + d[2].proc=my_d_button_proc; + d[2].x=x+10; + d[2].y=y+h-15; + d[2].w=(ml/2)-20; + d[2].h=10+BEVEL; + d[2].fg=ACOL(GUI_TEXT); + d[2].bg=ACOL(GUI_MID); + d[2].key=0; + d[2].flags=D_EXIT; + d[2].d1=d[2].d2=0; + d[2].dp="OK"; + d[2].dp2=d[2].dp3=NULL; + + /* CANCEL + */ + d[3].proc=my_d_button_proc; + d[3].x=SCRW/2+10; + d[3].y=y+h-15; + d[3].w=(ml/2)-20; + d[3].h=10+BEVEL; + d[3].fg=ACOL(GUI_TEXT); + d[3].bg=ACOL(GUI_MID); + d[3].key=0; + d[3].flags=D_EXIT; + d[3].d1=d[3].d2=0; + d[3].dp="Cancel"; + d[3].dp2=d[3].dp3=NULL; + + /* Buttons + */ + for(f=0;f<no;f++) + { + d[4+f].proc=my_d_check_proc; + d[4+f].x=x+10; + d[4+f].y=y+20+f*10; + d[4+f].w=ml-20; + d[4+f].h=10; + d[4+f].fg=ACOL(GUI_TEXT); + d[4+f].bg=ACOL(GUI_MID); + d[4+f].key=0; + + if (p[f].val) + d[4+f].flags=D_SELECTED; + else + d[4+f].flags=0; + + d[4+f].d1=p[f].group; + d[4+f].d2=0; + d[4+f].dp=p[f].text; + d[4+f].dp2=d[4+f].dp3=NULL; + } + + d[4+no].proc=NULL; + + GFX_bounce(); + + if(do_dialog(d,-1)==2) + { + for(f=0;f<no;f++) + if (d[4+f].flags&D_SELECTED) + p[f].val=TRUE; + else + p[f].val=FALSE; + + ret=TRUE; + } + else + ret=FALSE; + + GFX_FORCE_REDRAW(); + Release(d); + + return(ret); +} + + +int GUI_dialog(char *title, int no, PLAT_DIALOG pd[]) +{ + DIALOG *d; + int tl,ml,pl,l; + int h; + int f,r; + int ret; + int x,y; + char *p; + int tmp; + double d_tmp; + + d=Grab(sizeof(DIALOG)*(no*3+6)); + + h=(FH+10)*no; + tl=0; + + for(f=0;f<no;f++) + if ((pd[f].text)&&((l=text_length(font,pd[f].text))>tl)) + tl=l; + + pl=DIALOG_STRLEN*FW; + + for(f=0;f<no;f++) + if (pd[f].type==PLAT_DIAL_PICKLIST) + for(r=0;r<pd[f].data.pl.no;r++) + if ((l=text_length(font,pd[f].data.pl.text[r])+10)>pl) + pl=l; + + ml=tl+pl+(FW*4); + + if (ml<(text_length(font,title)+FW*4)) + ml=text_length(font,title)+FW*4; + + h+=FH*5; + x=SCRW/2-ml/2; + y=SCRH/2-h/2; + + /* Surrounding box + */ + d[0].proc=d_3d_box; + d[0].x=x; + d[0].y=y; + d[0].w=ml; + d[0].h=h; + d[0].fg=ACOL(GUI_TEXT); + d[0].bg=ACOL(GUI_MID); + d[0].key=0; + d[0].flags=0; + d[0].d1=d[0].d2=0; + d[0].dp=d[0].dp2=d[0].dp3=NULL; + + /* Title + */ + d[1].proc=d_my_ctext_proc; + d[1].x=SCRW/2; + d[1].w=ml-20; + d[1].y=y+3; + d[1].h=10; + d[1].fg=ACOL(GUI_TEXTBOLD); + d[1].bg=ACOL(GUI_MID); + d[1].key=0; + d[1].flags=0; + d[1].d1=d[1].d2=0; + d[1].dp=title; + d[1].dp2=d[1].dp3=NULL; + + /* OK + */ + d[2].proc=my_d_button_proc; + d[2].x=x+10; + d[2].w=(ml/2)-20; + d[2].h=FH+3+BEVEL; + d[2].y=y+h-d[2].h-5-BEVEL; + d[2].fg=ACOL(GUI_TEXT); + d[2].bg=ACOL(GUI_MID); + d[2].key=0; + d[2].flags=D_EXIT; + d[2].d1=d[2].d2=0; + d[2].dp="OK"; + d[2].dp2=d[2].dp3=NULL; + + /* CANCEL + */ + d[3].proc=my_d_button_proc; + d[3].x=SCRW/2+10; + d[3].y=d[2].y; + d[3].w=(ml/2)-20; + d[3].h=d[2].h; + d[3].fg=ACOL(GUI_TEXT); + d[3].bg=ACOL(GUI_MID); + d[3].key=0; + d[3].flags=D_EXIT; + d[3].d1=d[3].d2=0; + d[3].dp="Cancel"; + d[3].dp2=d[3].dp3=NULL; + + /* Labels and text entries + */ + for(f=0;f<no;f++) + { + if (pd[f].text) + p=pd[f].text; + else + p=""; + + d[4+(f*3)].proc=d_my_text_proc; + d[4+(f*3)].x=x+FW; + d[4+(f*3)].y=y+23+f*15; + d[4+(f*3)].w=text_length(font,p); + d[4+(f*3)].h=10; + d[4+(f*3)].fg=ACOL(GUI_TEXT); + d[4+(f*3)].bg=ACOL(GUI_MID); + d[4+(f*3)].key=0; + d[4+(f*3)].flags=0; + d[4+(f*3)].d1=0; + d[4+(f*3)].d2=0; + d[4+(f*3)].dp=p; + d[4+(f*3)].dp2=d[4+(f*3)].dp3=NULL; + + if (pd[f].type==PLAT_DIAL_PICKLIST) + { + d[5+(f*3)].proc=d_inv_3d_box; + d[5+(f*3)].x=d[4+(f*3)].x+tl+FW; + d[5+(f*3)].y=y+21+f*15; + d[5+(f*3)].h=FH+BEVEL*2; + d[5+(f*3)].fg=ACOL(GUI_LO); + d[5+(f*3)].bg=ACOL(GUI_MID); + d[5+(f*3)].key=0; + d[5+(f*3)].flags=0; + d[5+(f*3)].d1=d[5+(f*3)].d2=0; + d[5+(f*3)].dp=d[5+(f*3)].dp2=d[5+(f*3)].dp3=NULL; + + d[6+(f*3)].w=0; + + for(r=0;r<pd[f].data.pl.no;r++) + if ((l=text_length(font,pd[f].data.pl.text[r])+10)>d[6+(f*3)].w) + d[6+(f*3)].w=l; + + d[5+(f*3)].w=d[6+(f*3)].w+BEVEL*2; + + d[6+(f*3)].proc=d_dial_picklist; + d[6+(f*3)].x=d[5+(f*3)].x+BEVEL; + d[6+(f*3)].y=y+20+BEVEL+1+f*15; + d[6+(f*3)].h=FH; + d[6+(f*3)].fg=ACOL(BLACK); + d[6+(f*3)].bg=ACOL(WHITE); + d[6+(f*3)].key=0; + d[6+(f*3)].flags=0; + d[6+(f*3)].d1=pd[f].data.pl.current; + d[6+(f*3)].d2=pd[f].data.pl.no; + d[6+(f*3)].dp=pd[f].data.pl.text; + d[6+(f*3)].dp2=d[6+(f*3)].dp3=NULL; + } + else + { + if (pd[f].type==PLAT_DIAL_INTEGER) + { + tmp=pd[f].data.i; + sprintf(pd[f].data.s,"%d",tmp); + } + + if (pd[f].type==PLAT_DIAL_DOUBLE) + { + d_tmp=pd[f].data.d; + sprintf(pd[f].data.s,"%G",d_tmp); + } + + d[5+(f*3)].proc=d_inv_3d_box; + d[5+(f*3)].x=d[4+(f*3)].x+tl+FW; + d[5+(f*3)].y=y+21+f*15; + d[5+(f*3)].w=text_length(font," ")*DIALOG_STRLEN+2*BEVEL; + d[5+(f*3)].h=8+BEVEL*2; + d[5+(f*3)].fg=ACOL(GUI_LO); + d[5+(f*3)].bg=ACOL(GUI_MID); + d[5+(f*3)].key=0; + d[5+(f*3)].flags=0; + d[5+(f*3)].d1=d[5+(f*3)].d2=0; + d[5+(f*3)].dp=d[5+(f*3)].dp2=d[5+(f*3)].dp3=NULL; + + d[6+(f*3)].proc=d_edit_proc; + d[6+(f*3)].x=d[5+(f*3)].x+BEVEL; + d[6+(f*3)].y=y+20+BEVEL+1+f*15; + d[6+(f*3)].w=text_length(font," ")*DIALOG_STRLEN; + d[6+(f*3)].h=10; + /* + d[6+(f*3)].fg=ACOL(GUI_TEXT); + d[6+(f*3)].bg=ACOL(GUI_MID); + */ + d[6+(f*3)].fg=ACOL(BLACK); + d[6+(f*3)].bg=ACOL(WHITE); + d[6+(f*3)].key=0; + d[6+(f*3)].flags=0; + d[6+(f*3)].d1=PLAT_DIAL_MAXSTRLEN; + d[6+(f*3)].d2=strlen(pd[f].data.s); + d[6+(f*3)].dp=pd[f].data.s; + d[6+(f*3)].dp2=d[6+(f*3)].dp3=NULL; + } + } + + d[4+(no*3)].proc=NULL; + + GFX_bounce(); + if(do_dialog(d,-1)==2) + { + for(f=0;f<no;f++) + { + if (pd[f].type==PLAT_DIAL_INTEGER) + { + tmp=(int)strtol(pd[f].data.s,NULL,0); + pd[f].data.i=tmp; + } + else if (pd[f].type==PLAT_DIAL_DOUBLE) + { + d_tmp=strtod(pd[f].data.s,NULL); + pd[f].data.d=d_tmp; + } + else if (pd[f].type==PLAT_DIAL_PICKLIST) + pd[f].data.pl.current=d[6+(f*3)].d1; + } + + ret=TRUE; + } + else + ret=FALSE; + + GFX_FORCE_REDRAW(); + Release(d); + + return(ret); +} + + +void GUI_view_file (char *title, char *path) +{ + FILE *fp; + int no; + char s[BUFF_LEN]; + char **line; + int f; + int ml; + int bw; + int bh; + + if ((fp=fopen(path,"r"))) + { + no=0; + fgets(s,BUFF_LEN,fp); + + while(!feof(fp)) + { + no++; + fgets(s,BUFF_LEN,fp); + } + + fclose(fp); + + line=Grab(sizeof(char *)*(no+1)); + + f=0; + fp=fopen(path,"r"); + + fgets(s,BUFF_LEN,fp); + + ml=0; + + while(!feof(fp)) + { + line[f]=Strdup(ConvertFileText(s)); + ml=MAX(ml,strlen(line[f])); + f++; + fgets(s,BUFF_LEN,fp); + } + + fclose(fp); + + line[f]=NULL; + } + else + { + no=2; + ml=0; + line=Grab(sizeof(char *)*3); + line[0]=Strdup("File not found:"); + line[1]=Strdup(path); + line[2]=NULL; + } + + bw=FV_WIDTH*FW+20+FV_SCROLLWIDTH; + bh=FV_HEIGHT*FH+20+FV_SCROLLWIDTH+20+FH*2; + + /* Box + */ + fv_dialog[FV_BORDER].proc=d_3d_box; + fv_dialog[FV_BORDER].y=0; + fv_dialog[FV_BORDER].h=bh; + fv_dialog[FV_BORDER].x=0; + fv_dialog[FV_BORDER].w=bw; + fv_dialog[FV_BORDER].fg=ACOL(GUI_TEXT); + fv_dialog[FV_BORDER].bg=ACOL(GUI_MID); + fv_dialog[FV_BORDER].key=0; + fv_dialog[FV_BORDER].flags=0; + fv_dialog[FV_BORDER].d1=fv_dialog[FV_BORDER].d2=0; + fv_dialog[FV_BORDER].dp=fv_dialog[FV_BORDER].dp2= + fv_dialog[FV_BORDER].dp3=NULL; + + /* Title + */ + fv_dialog[FV_TITLE].proc=d_my_ctext_proc; + fv_dialog[FV_TITLE].x=bw/2; + fv_dialog[FV_TITLE].w=bw-20; + fv_dialog[FV_TITLE].y=BEVEL+5; + fv_dialog[FV_TITLE].h=FH; + fv_dialog[FV_TITLE].fg=ACOL(GUI_TEXTBOLD); + fv_dialog[FV_TITLE].bg=ACOL(GUI_MID); + fv_dialog[FV_TITLE].key=0; + fv_dialog[FV_TITLE].flags=0; + fv_dialog[FV_TITLE].d1=fv_dialog[FV_TITLE].d2=0; + fv_dialog[FV_TITLE].dp2=fv_dialog[FV_TITLE].dp3=NULL; + fv_dialog[FV_TITLE].dp=title; + + /* Text Box Pt 1 + */ + fv_dialog[FV_TEXTBOX].proc=d_inv_3d_box; + fv_dialog[FV_TEXTBOX].y=BEVEL+5+FH+5; + fv_dialog[FV_TEXTBOX].x=BEVEL+5; + fv_dialog[FV_TEXTBOX].fg=ACOL(GUI_TEXT); + fv_dialog[FV_TEXTBOX].bg=ACOL(GUI_MID); + fv_dialog[FV_TEXTBOX].key=0; + fv_dialog[FV_TEXTBOX].flags=0; + fv_dialog[FV_TEXTBOX].d1=fv_dialog[FV_TEXTBOX].d2=0; + fv_dialog[FV_TEXTBOX].dp=fv_dialog[FV_TEXTBOX].dp2= + fv_dialog[FV_TEXTBOX].dp3=NULL; + + /* Text + */ + fv_dialog[FV_TEXT].proc=d_ro_textbox; + fv_dialog[FV_TEXT].y=fv_dialog[FV_TEXTBOX].y+BEVEL; + fv_dialog[FV_TEXT].h=FH*FV_HEIGHT; + fv_dialog[FV_TEXT].x=fv_dialog[FV_TEXTBOX].x+BEVEL; + fv_dialog[FV_TEXT].w=FW*FV_WIDTH; + fv_dialog[FV_TEXT].fg=ACOL(BLACK); + fv_dialog[FV_TEXT].bg=ACOL(WHITE); + fv_dialog[FV_TEXT].key=0; + fv_dialog[FV_TEXT].flags=0; + fv_dialog[FV_TEXT].d1=0; + fv_dialog[FV_TEXT].d2=0; + fv_dialog[FV_TEXT].dp=line; + fv_dialog[FV_TEXT].dp2=fv_dialog[FV_TEXT].dp3=NULL; + + /* Text Box Pt 2 + */ + fv_dialog[FV_TEXTBOX].w=fv_dialog[FV_TEXT].w+BEVEL*2; + fv_dialog[FV_TEXTBOX].h=fv_dialog[FV_TEXT].h+BEVEL*2; + + /* Vertical scrollbar + */ + fv_dialog[FV_VERT_SCROLL].proc=d_vert_scrollbar; + fv_dialog[FV_VERT_SCROLL].x=fv_dialog[FV_TEXTBOX].x+ + fv_dialog[FV_TEXTBOX].w+3; + fv_dialog[FV_VERT_SCROLL].y=fv_dialog[FV_TEXTBOX].y; + fv_dialog[FV_VERT_SCROLL].w=FV_SCROLLWIDTH; + fv_dialog[FV_VERT_SCROLL].h=fv_dialog[FV_TEXTBOX].h; + fv_dialog[FV_VERT_SCROLL].fg=ACOL(GUI_TEXT); + fv_dialog[FV_VERT_SCROLL].bg=ACOL(GUI_MID); + fv_dialog[FV_VERT_SCROLL].key=0; + fv_dialog[FV_VERT_SCROLL].flags=0; + fv_dialog[FV_VERT_SCROLL].d1=FV_HEIGHT; + fv_dialog[FV_VERT_SCROLL].d2=MAX(0,no-FV_HEIGHT); + fv_dialog[FV_VERT_SCROLL].dp=&fv_dialog[FV_TEXT].d2; + fv_dialog[FV_VERT_SCROLL].dp2=&fv_dialog[FV_TEXT]; + fv_dialog[FV_VERT_SCROLL].dp3=NULL; + + /* Horizontal scrollbar + */ + fv_dialog[FV_HORIZ_SCROLL].proc=d_horiz_scrollbar; + fv_dialog[FV_HORIZ_SCROLL].x=fv_dialog[FV_TEXTBOX].x; + fv_dialog[FV_HORIZ_SCROLL].y=fv_dialog[FV_TEXTBOX].y+ + fv_dialog[FV_TEXTBOX].h+3; + fv_dialog[FV_HORIZ_SCROLL].w=fv_dialog[FV_TEXTBOX].w; + fv_dialog[FV_HORIZ_SCROLL].h=FV_SCROLLWIDTH; + fv_dialog[FV_HORIZ_SCROLL].fg=ACOL(GUI_TEXT); + fv_dialog[FV_HORIZ_SCROLL].bg=ACOL(GUI_MID); + fv_dialog[FV_HORIZ_SCROLL].key=0; + fv_dialog[FV_HORIZ_SCROLL].flags=0; + fv_dialog[FV_HORIZ_SCROLL].d1=FV_WIDTH; + fv_dialog[FV_HORIZ_SCROLL].d2=MAX(0,ml-FV_WIDTH); + fv_dialog[FV_HORIZ_SCROLL].dp=&fv_dialog[FV_TEXT].d1; + fv_dialog[FV_HORIZ_SCROLL].dp2=&fv_dialog[FV_TEXT]; + fv_dialog[FV_HORIZ_SCROLL].dp3=NULL; + + /* OK + */ + fv_dialog[FV_OK].proc=my_d_button_proc; + fv_dialog[FV_OK].x=fv_dialog[FV_TEXTBOX].x; + fv_dialog[FV_OK].y=fv_dialog[FV_HORIZ_SCROLL].y+ + fv_dialog[FV_HORIZ_SCROLL].h+5; + fv_dialog[FV_OK].w=FW*5; + fv_dialog[FV_OK].h=FH+3+BEVEL; + fv_dialog[FV_OK].fg=ACOL(GUI_TEXT); + fv_dialog[FV_OK].bg=ACOL(GUI_MID); + fv_dialog[FV_OK].key=0; + fv_dialog[FV_OK].flags=D_EXIT; + fv_dialog[FV_OK].d1=fv_dialog[FV_OK].d2=0; + fv_dialog[FV_OK].dp="OK"; + fv_dialog[FV_OK].dp2=fv_dialog[FV_OK].dp3=NULL; + + fv_dialog[FV_END].proc=NULL; + + do_dialog(fv_dialog,-1); + + for(f=0;f<no;f++) + Release(line[f]); + + Release(line); + + GFX_FORCE_REDRAW(); +} + + +char *GUI_text_edit (char *title, char *text) +{ + char *cp_text; + char *p; + TextEditLine *line; + char *ret; + int f; + int size; + int no; + int bw; + int bh; + + p=text; + + if (*(p+strlen(p)-1)=='\n') + no=0; + else + no=1; + + while(*p) + if (*p++=='\n') + no++; + + line=Grab(sizeof(TextEditLine)*(no+1)); + cp_text=Strdup(text); + p=cp_text; + + for(f=0;f<no;f++) + { + char *e; + + e=p; + + while((*e)&&(*e!='\n')) + e++; + + *e=0; + + /* We rely on the fact that Grab() zeroes data... + */ + line[f].len=strlen(p); + line[f].p=Grab((line[f].len/TE_CHUNK+1)*TE_CHUNK+1); + strcpy(line[f].p,p); + p=e+1; + } + + line[f].p=NULL; + line[f].len=0; + Release(cp_text); + + bw=(TE_WIDTH+1)*FW+20; + bh=TE_HEIGHT*FH+40+FH*2; + + /* Box + */ + te_dialog[TE_BORDER].proc=d_3d_box; + te_dialog[TE_BORDER].y=0; + te_dialog[TE_BORDER].h=bh; + te_dialog[TE_BORDER].x=0; + te_dialog[TE_BORDER].w=bw; + te_dialog[TE_BORDER].fg=ACOL(GUI_TEXT); + te_dialog[TE_BORDER].bg=ACOL(GUI_MID); + te_dialog[TE_BORDER].key=0; + te_dialog[TE_BORDER].flags=0; + te_dialog[TE_BORDER].d1=te_dialog[TE_BORDER].d2=0; + te_dialog[TE_BORDER].dp=te_dialog[TE_BORDER].dp2= + te_dialog[TE_BORDER].dp3=NULL; + + /* Title + */ + te_dialog[TE_TITLE].proc=d_my_ctext_proc; + te_dialog[TE_TITLE].x=bw/2; + te_dialog[TE_TITLE].w=bw-20; + te_dialog[TE_TITLE].y=BEVEL+5; + te_dialog[TE_TITLE].h=FH; + te_dialog[TE_TITLE].fg=ACOL(GUI_TEXTBOLD); + te_dialog[TE_TITLE].bg=ACOL(GUI_MID); + te_dialog[TE_TITLE].key=0; + te_dialog[TE_TITLE].flags=0; + te_dialog[TE_TITLE].d1=te_dialog[TE_TITLE].d2=0; + te_dialog[TE_TITLE].dp2=te_dialog[TE_TITLE].dp3=NULL; + te_dialog[TE_TITLE].dp=title; + + /* Text Box Pt 1 + */ + te_dialog[TE_TEXTBOX].proc=d_inv_3d_box; + te_dialog[TE_TEXTBOX].y=BEVEL+5+FH+5; + te_dialog[TE_TEXTBOX].x=BEVEL+5; + te_dialog[TE_TEXTBOX].fg=ACOL(GUI_TEXT); + te_dialog[TE_TEXTBOX].bg=ACOL(GUI_MID); + te_dialog[TE_TEXTBOX].key=0; + te_dialog[TE_TEXTBOX].flags=0; + te_dialog[TE_TEXTBOX].d1=te_dialog[TE_TEXTBOX].d2=0; + te_dialog[TE_TEXTBOX].dp=te_dialog[TE_TEXTBOX].dp2= + te_dialog[TE_TEXTBOX].dp3=NULL; + + /* Text + */ + te_dialog[TE_TEXT].proc=d_textedit; + te_dialog[TE_TEXT].y=te_dialog[TE_TEXTBOX].y+BEVEL; + te_dialog[TE_TEXT].h=FH*TE_HEIGHT; + te_dialog[TE_TEXT].x=te_dialog[TE_TEXTBOX].x+BEVEL; + te_dialog[TE_TEXT].w=FW*(TE_WIDTH+1); + te_dialog[TE_TEXT].fg=ACOL(BLACK); + te_dialog[TE_TEXT].bg=ACOL(WHITE); + te_dialog[TE_TEXT].key=0; + te_dialog[TE_TEXT].flags=0; + te_dialog[TE_TEXT].d1=0; + te_dialog[TE_TEXT].d2=0; + te_dialog[TE_TEXT].dp=line; + te_dialog[TE_TEXT].dp2=te_dialog[TE_TEXT].dp3=NULL; + + /* Text Box Pt 2 + */ + te_dialog[TE_TEXTBOX].w=te_dialog[TE_TEXT].w+BEVEL*2; + te_dialog[TE_TEXTBOX].h=te_dialog[TE_TEXT].h+BEVEL*2; + + /* OK + */ + te_dialog[TE_OK].proc=my_d_button_proc; + te_dialog[TE_OK].x=te_dialog[TE_TEXTBOX].x; + te_dialog[TE_OK].y=te_dialog[TE_TEXTBOX].y+te_dialog[TE_TEXTBOX].h+7; + te_dialog[TE_OK].w=FW*8; + te_dialog[TE_OK].h=FH+3+BEVEL; + te_dialog[TE_OK].fg=ACOL(GUI_TEXT); + te_dialog[TE_OK].bg=ACOL(GUI_MID); + te_dialog[TE_OK].key=0; + te_dialog[TE_OK].flags=D_EXIT; + te_dialog[TE_OK].d1=te_dialog[TE_OK].d2=0; + te_dialog[TE_OK].dp="OK"; + te_dialog[TE_OK].dp2=te_dialog[TE_OK].dp3=NULL; + + /* CANCEL + */ + te_dialog[TE_CANCEL].proc=my_d_button_proc; + te_dialog[TE_CANCEL].x=te_dialog[TE_OK].x+te_dialog[TE_OK].w+20; + te_dialog[TE_CANCEL].y=te_dialog[TE_TEXTBOX].y+te_dialog[TE_TEXTBOX].h+7; + te_dialog[TE_CANCEL].w=FW*8; + te_dialog[TE_CANCEL].h=FH+3+BEVEL; + te_dialog[TE_CANCEL].fg=ACOL(GUI_TEXT); + te_dialog[TE_CANCEL].bg=ACOL(GUI_MID); + te_dialog[TE_CANCEL].key=0; + te_dialog[TE_CANCEL].flags=D_EXIT; + te_dialog[TE_CANCEL].d1=te_dialog[TE_CANCEL].d2=0; + te_dialog[TE_CANCEL].dp="Cancel"; + te_dialog[TE_CANCEL].dp2=te_dialog[TE_CANCEL].dp3=NULL; + + te_dialog[TE_END].proc=NULL; + + no=do_dialog(te_dialog,TE_TEXT); + line=te_dialog[TE_TEXT].dp; + + if (no==TE_OK) + { + f=0; + size=0; + + while(line[f].p) + size+=line[f++].len+1; + + size++; + + ret=Grab(size); + *ret=0; + + f=0; + + while(line[f].p) + { + strcat(ret,line[f++].p); + + if (line[f].p) + strcat(ret,"\n"); + } + } + else + ret=NULL; + + f=0; + while(line[f].p) + Release(line[f++].p); + + Release(line); + + GFX_FORCE_REDRAW(); + + return(ret); +} + + +/* END OF FILE */ diff --git a/djgpp/runcmd.c b/djgpp/runcmd.c new file mode 100644 index 0000000..2148806 --- /dev/null +++ b/djgpp/runcmd.c @@ -0,0 +1,143 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Command execution interface + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "mem.h" + +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <process.h> + +#define MAX_ARGS 128 + +static void CopyArgs(char *argv[],char *new[]) +{ + char *na; + char *p; + int len; + int f; + + len=0; + f=0; + while(argv[f]) + len+=strlen(argv[f++])+1; + + len++; + + na=Grab(len); + + f=0; + while(argv[f]) + { + strcat(na,argv[f++]); + strcat(na," "); + } + + f=0; + p=strtok(na," \t"); + + while((f<MAX_ARGS-1)&&(p)) + { + new[f++]=Strdup(p); + p=strtok(NULL," \t"); + } + + new[f]=NULL; + + Release(na); +} + + +static void Write(int fd, char *p) +{ + write(fd,p,strlen(p)); +} + + +int RunCommand(char *argv[], char *path) +{ + static int old_stdout=-1; + static int old_stderr=-1; + int ret; + int f; + int fd; + char *new[MAX_ARGS]; + + if (old_stdout==-1) + if ((old_stdout=dup(STDOUT_FILENO))==-1) + return(FALSE); + + if (old_stderr==-1) + if ((old_stderr=dup(STDERR_FILENO))==-1) + return(FALSE); + + tmpnam(path); + + if ((fd=open(path,O_WRONLY|O_CREAT|O_TRUNC,0666))==-1) + return(FALSE); + + CopyArgs(argv,new); + + if (!new[0]) + { + close(fd); + return(FALSE); + } + + dup2(fd,STDOUT_FILENO); + dup2(fd,STDERR_FILENO); + + ret=spawnvp(P_WAIT,new[0],new); + + f=0; + while(new[f]) + Release(new[f++]); + + if (ret==-1) + { + Write(fd,"Exec failed:\n"); + Write(fd,sys_errlist[errno]); + Write(fd,"\n"); + } + + dup2(old_stdout,STDOUT_FILENO); + dup2(old_stderr,STDERR_FILENO); + close(fd); + + if (ret==-1) + return(FALSE); + + /* Assume that non-zero is error + */ + return(ret==0); +} + + +/* END OF FILE */ diff --git a/djgpp/vstring.c b/djgpp/vstring.c new file mode 100644 index 0000000..947ca99 --- /dev/null +++ b/djgpp/vstring.c @@ -0,0 +1,48 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Provides portable versions of necessary string routines + +*/ +static const char rcs_id[]="$Id$"; + +#include <string.h> + + +int StrCaseCmp(char *a, char *b) +{ + if (!a || !b) + return 0; + + return strcasecmp(a,b); +} + +int StrNCaseCmp(char *a, char *b, int n) +{ + if (!a || !b) + return 0; + + return strncasecmp(a,b,n); +} + + +/* END OF FILE */ diff --git a/doc/bugs.htm b/doc/bugs.htm new file mode 100644 index 0000000..92e5262 --- /dev/null +++ b/doc/bugs.htm @@ -0,0 +1,40 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<h1 align="center">Bugs</h1> + +<p>If you find any additional bugs please try and send as much +information (including a surefire way of repeating the problem if +possible) <a href="mailto:ianc@noddybox.demon.co.uk">here</a>. +Include the VERSION file from the distribution with your message. +If the problem is more likely to be platform specific (e.g. a bug +in the screen or file handling) please contact the following +people:<a name="CONTACTS"></a></p> + +<table border="1" cellpadding="10"> + <tr> + <td valign="top" nowrap><strong>DJGPP/DOS</strong></td> + <td valign="top"><a + href="mailto:ianc@noddybox.demon.co.uk">ianc@noddybox.demon.co.uk</a><br> + <strong>Note</strong>: messages with <strong>any sort</strong> + of attachment will not be accepted.</td> + </tr> +</table> + +<p>Also check the <a href="sys.htm">system dependent +documentation</a> to see if there is any bugs specific to your +system.</p> + +<hr> + +<p><a href="index.htm">Back to index</a></p> + +<p><tt>$Id$</tt></p> +</body> +</html> diff --git a/doc/building.htm b/doc/building.htm new file mode 100644 index 0000000..69814a6 --- /dev/null +++ b/doc/building.htm @@ -0,0 +1,88 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<h1 align="center">Building</h1> + +<p>This part of the documentation simply expands <u>slightly</u> +on the contents of the README in the source distribution.</p> + +<hr> + +<h2>Notes</h2> + +<p>Note that the viDOOM makefile expects the <strong>include</strong> +command to be honoured.</p> + +<hr> + +<h2>Instructions</h2> + +<h3>Step 1</h3> + +<p>Edit the makefile.</p> + +<h3>Step 2</h3> + +<p>Change the following line to the name of your platform:</p> + +<blockquote> + <p><tt>MAKEPLAT=djgpp</tt></p> +</blockquote> + +<p>The following platforms are currently supported:</p> + +<blockquote> + <p><strong>DJGPP - </strong>Protected-mode 32-bit MSDOS + (Windows 9x / MSDOS + DPMI)</p> +</blockquote> + +<h3>Step 3</h3> + +<p>If you are going to use the install script, then update the +following line:</p> + +<blockquote> + <p><tt>INSTALLDIR=C:/viDOOM</tt></p> +</blockquote> + +<p>so it points to the directory where you wish it to be +installed.</p> + +<h3>Step 4</h3> + +<p>Set the MKDS to the directory separator for this platform. +This is used so that the makefile can safely access +sub directories:</p> + +<blockquote> + <p><tt>MKDS=/</tt></p> +</blockquote> + +<h3>Step 5</h3> + +<p>If you wish to build the debug version remove the comment +(hash character - #) from the start of the following line:</p> + +<blockquote> + <p><tt>DEBUG=-DDEBUG</tt></p> +</blockquote> + +<h3>Step 6</h3> + +<p>To make viDOOM simply invoke the make program for your system. +To install viDOOM invoke the make program for your system, +telling it to build the rule <strong>install</strong>.</p> + +<hr width="99%"> + +<p><a href="index.htm">Back to the index</a></p> + +<p><tt>$Id: building.htm,v 1.5 2000/08/13 00:35:59 dosuser Exp dosuser $ </tt></p> +</body> +</html> diff --git a/doc/changelog.htm b/doc/changelog.htm new file mode 100644 index 0000000..8fd333c --- /dev/null +++ b/doc/changelog.htm @@ -0,0 +1,64 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<h1 align="center">Changelog</h1> + +<p>Below is a list of the major features added or bugs fixed in +each release:</p> + +<table border="1" cellpadding="3" width="100%"> + <tr> + <td valign="top"><b>Version</b> </td> + <td valign="top"><b>Date</b> </td> + <td valign="top"><b>Changes</b> </td> + </tr> + <tr> + <td valign="top" nowrap>0.01</td> + <td valign="top" nowrap>05/08/2000</td> + <td valign="top">Initial release</td> + </tr> + <tr> + <td valign="top" nowrap>0.02</td> + <td valign="top" nowrap>13/08/2000</td> + <td valign="top"><ol> + <li>Fixed bug in step creation</li> + <li>Added functionality to allow the selection of + overlapping objects</li> + </ol> + </td> + </tr> + <tr> + <td valign="top" nowrap>0.03</td> + <td valign="top" nowrap>Not yet completed</td> + <td valign="top"><ol> + <li>Fixed embarrassingly bad file pointer leaks.</li> + <li>Added conditional directives to config file.</li> + <li>Altered some configuration so that thing flags, + etc, can be different in HEXEN and DOOM mode.</li> + <li>Added support for generalised linedefs and sector + types.</li> + <li>Added support for ZDOOM HEXEN maps.</li> + <li>Added support for editting the MAPINFO lump for + ZDOOM levels.</li> + <li>Support for editting a SCRIPTS lump and + generating the HEXEN BEAHVIOR.</li> + <li>Fixed a small memory leak when closing a WAD + file.</li> + </ol> + </td> + </tr> +</table> + +<hr> + +<p><a href="index.htm">Back to index</a></p> + +<p><tt>$Id$</tt></p> +</body> +</html> diff --git a/doc/djgpp.htm b/doc/djgpp.htm new file mode 100644 index 0000000..1d53547 --- /dev/null +++ b/doc/djgpp.htm @@ -0,0 +1,58 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<h1 align="center">DJGPP</h1> + +<ul> + <li><a href="#GUI">GUI</a></li> + <li><a href="#BUGS">Known bugs and problems</a></li> +</ul> + +<hr> + +<h2><a name="GUI"></a>GUI</h2> + +<p>Hopefully the GUI employed is fairly self explanatory. All the +dialogs (apart from the file viewing one used to view the license +and node builder outputs - and that's read only anyway) have an <strong>OK</strong> +and <strong>Cancel</strong> button which behave as you would +expect.</p> + +<p>All the dialogs, apart from the text editor (used for editing +the MAPINFO lump in ZDoom mode) can be cancelled by pressing the +ESCAPE key. On all dialogs the TAB key will give the focus to +different objects on the dialog for keyboard control. Note that +the file viewer does not currently honour keyboard controls (ie. +you cannot use cursor keys, page up, etc to navigate the file).</p> + +<p>Note that if the yes/no question box is cancelled with the ESCAPE key, it +is taken as <b>No</b>.</p> + +<hr> + +<h2><a name="BUGS"></a>Known bugs and problems</h2> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong>External Commands</strong></td> + <td valign="top">On some PCs there has been occasions + where running external commands (e.g. the node builder) + fails for no apparent reason. If this does happen on your + system, then you must disable the command via the + VIDOOM.INI file and run it seperately outside of viDOOM.</td> + </tr> +</table> + +<hr> + +<p><a href="index.htm">Back to index</a></p> + +<p><tt>$Id$</tt></p> +</body> +</html> diff --git a/doc/ed_ex1.gif b/doc/ed_ex1.gif Binary files differnew file mode 100755 index 0000000..49f03f7 --- /dev/null +++ b/doc/ed_ex1.gif diff --git a/doc/ed_ex2.gif b/doc/ed_ex2.gif Binary files differnew file mode 100755 index 0000000..e50d72f --- /dev/null +++ b/doc/ed_ex2.gif diff --git a/doc/ed_ex3.gif b/doc/ed_ex3.gif Binary files differnew file mode 100755 index 0000000..c68be6a --- /dev/null +++ b/doc/ed_ex3.gif diff --git a/doc/ed_l_hex.gif b/doc/ed_l_hex.gif Binary files differnew file mode 100755 index 0000000..044078d --- /dev/null +++ b/doc/ed_l_hex.gif diff --git a/doc/ed_line.gif b/doc/ed_line.gif Binary files differnew file mode 100755 index 0000000..094e0d4 --- /dev/null +++ b/doc/ed_line.gif diff --git a/doc/ed_lninf.gif b/doc/ed_lninf.gif Binary files differnew file mode 100755 index 0000000..f663393 --- /dev/null +++ b/doc/ed_lninf.gif diff --git a/doc/ed_merge.gif b/doc/ed_merge.gif Binary files differnew file mode 100755 index 0000000..806d808 --- /dev/null +++ b/doc/ed_merge.gif diff --git a/doc/ed_multi.gif b/doc/ed_multi.gif Binary files differnew file mode 100755 index 0000000..207a96e --- /dev/null +++ b/doc/ed_multi.gif diff --git a/doc/ed_sect.gif b/doc/ed_sect.gif Binary files differnew file mode 100755 index 0000000..5d8f9b4 --- /dev/null +++ b/doc/ed_sect.gif diff --git a/doc/ed_step.gif b/doc/ed_step.gif Binary files differnew file mode 100755 index 0000000..58285e3 --- /dev/null +++ b/doc/ed_step.gif diff --git a/doc/ed_t_hx1.gif b/doc/ed_t_hx1.gif Binary files differnew file mode 100755 index 0000000..d6d0dee --- /dev/null +++ b/doc/ed_t_hx1.gif diff --git a/doc/ed_t_hx2.gif b/doc/ed_t_hx2.gif Binary files differnew file mode 100755 index 0000000..61ed203 --- /dev/null +++ b/doc/ed_t_hx2.gif diff --git a/doc/ed_thing.gif b/doc/ed_thing.gif Binary files differnew file mode 100755 index 0000000..af4785a --- /dev/null +++ b/doc/ed_thing.gif diff --git a/doc/ed_vert.gif b/doc/ed_vert.gif Binary files differnew file mode 100755 index 0000000..eaf5a0f --- /dev/null +++ b/doc/ed_vert.gif diff --git a/doc/editing.htm b/doc/editing.htm new file mode 100755 index 0000000..a276a86 --- /dev/null +++ b/doc/editing.htm @@ -0,0 +1,1603 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<h1 align="center">Editing</h1> + +<h2 align="left">Index</h2> + +<p align="left">This page is split up into the following +sections:</p> + +<ul> + <li><a href="#THE_BASICS">The Basics</a> </li> + <li><a href="#BASIC_MOUSE">Basic Mouse Operation</a> </li> + <li><a href="#GENERAL_KEY">General Key Commands</a> </li> + <li><a href="#SECTOR_MODE">Sector Edit Mode</a><ul> + <li><a href="#SECTOR_DISPLAY">Display</a></li> + <li><a href="#SECTOR_KEYS">Extra Keys</a></li> + <li><a href="#SECTOR_INSERT">Insertion</a></li> + <li><a href="#SECTOR_DELETE">Deletion</a></li> + <li><a href="#SECTOR_POPUP">Popup Menu</a></li> + </ul> + </li> + <li><a href="#LINEDEF_MODE">Linedef Edit Mode</a><ul> + <li><a href="#LINEDEF_DISPLAY">Display</a></li> + <li><a href="#LINEDEF_KEYS">Extra Keys</a></li> + <li><a href="#LINEDEF_INSERT">Insertion</a></li> + <li><a href="#LINEDEF_DELETE">Deletion</a></li> + <li><a href="#LINEDEF_POPUP">Popup Menu</a></li> + <li><a href="#SIDEDEF_MENU">Sidedef Menu</a></li> + <li><a href="#STAIR_CREATION">Stair creation</a></li> + </ul> + </li> + <li><a href="#THING_MODE">Thing Edit Mode</a><ul> + <li><a href="#THING_DISPLAY">Display</a></li> + <li><a href="#THING_KEYS">Extra Keys</a></li> + <li><a href="#THING_INSERT">Insertion</a></li> + <li><a href="#THING_DELETE">Deletion</a></li> + <li><a href="#THING_POPUP">Popup Menu</a></li> + </ul> + </li> + <li><a href="#VERTEX_MODE">Vertex Edit Mode</a><ul> + <li><a href="#VERTEX_DISPLAY">Display</a></li> + <li><a href="#VERTEX_KEYS">Extra Keys</a></li> + <li><a href="#VERTEX_INSERT">Insertion</a></li> + <li><a href="#VERTEX_DELETE">Deletion</a></li> + <li><a href="#VERTEX_POPUP">Popup Menu</a></li> + </ul> + </li> + <li><a href="#MULTI_MODE">Multiple Edit Mode</a><ul> + <li><a href="#MULTI_DISPLAY">Display</a></li> + <li><a href="#MULTI_OPERATION">Operation</a></li> + <li><a href="#MULTI_POPUP">Popup menu</a></li> + </ul> + </li> + <li><a href="#MERGE_MAP">Merging Maps</a></li> + <li><a href="#LUMP_EDIT">Lump Editing</a></li> +</ul> + +<hr> + +<h2 align="left">The Basics<a name="THE_BASICS"></a></h2> + +<p align="left">Once you start editing in viDOOM you should be +greeted by a screen similar to the following (no prizes for +guessing the map number):</p> + +<p align="left"><img src="ed_sect.gif" width="640" height="480"></p> + +<p>At the top of the screen is a title bar which shows the +following information, going from left to right:</p> + +<ul> + <li>The edit mode. This is either 'Sector', 'Linedef', + 'Thing' or 'Vertex'.</li> + <li>The position of the mouse pointer in the map.</li> + <li>The current scale</li> + <li>Whether movements are locked to the current grid scale.</li> + <li>The grid scale. The first number indicate the grid scale + that objects are snapped to. The second number indicates + how big the graphical grid displayed is (the blue lines + in the picture above). This second number changes + automatically as the display is zoomed in and out.</li> + <li>Below this is a line where specific edit modes can show + extra information.</li> +</ul> + +<p>Note that the information bar at the top of the screen will +also indicate what format the level you're editing is in. If the +background to the title bar is blue, this map is in the original +DOOM format. If the background is red then the map is in the +extended HEXEN format.</p> + +<p>The main central part of the screen is made up the map being +edited.</p> + +<p>Spread around the bottom of the screen are information boxes +that display details of the object the mouse pointer is currently +over. The size and information in these boxes changes with the +edit mode.</p> + +<hr> + +<h2>Basic Mouse Operation<a name="BASIC_MOUSE"></a></h2> + +<p>The mouse acts in exactly the same way, independent of the +mode. </p> + +<p>Moving the mouse over an object will change it's colour, +indicating it can be selected. Simply left click over an object +to select it. If the control key is pressed while selecting the +object the object is selected in addition to any others currently +selected.</p> + +<p>If the shift key is pressed then while the left button is held +down a selection rectangle can be dragged out. Any objects within +this box once the button is released will be selected (DOOM map +designers note - in this instance objects are selected in order +from the lowest numbered object up). If the control key is +pressed along with the shift key then the objects in the box are +selected in addition to any others currently selected.</p> + +<p>On pressing the right button a pop-up menu is displayed. If no +objects are currently selected then the menu just has the insert +option. If objects are selected when pressing the right mouse +button (please see the <a href="overview.htm#HOVER_SELECT">overview</a> +to see how the right button can also be made to select objects +the pointer is currently over) then a menu is shown with options +specific to each edit mode.</p> + +<p>If the left mouse button is pressed without the control key +and the pointer is not over any object then the current selection +is cleared.</p> + +<p>If the mouse has a middle button then this can be used to move +objects once they have been selected. This basically a short cut +for pressing the right button then selecting the 'Move' option.</p> + +<hr> + +<h2>General Key Commands<a name="GENERAL_KEY"></a></h2> + +<p>There are a number of key commands that are common to all edit +modes in viDOOM. These are as follows:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap>F1</td> + <td valign="top">General Help (key commands and mouse + usage).</td> + </tr> + <tr> + <td valign="top" nowrap>F2</td> + <td valign="top">Help specific to the current edit mode</td> + </tr> + <tr> + <td valign="top" nowrap>Escape</td> + <td valign="top">Finish Editing</td> + </tr> + <tr> + <td valign="top" nowrap>Cursor Keys</td> + <td valign="top">Move the map display</td> + </tr> + <tr> + <td valign="top" nowrap>Shift + Cursor Keys</td> + <td valign="top">Move the map display quickly</td> + </tr> + <tr> + <td valign="top" nowrap>Ctrl + Cursor Keys</td> + <td valign="top">Move the map by one pixel (i.e. by + whatever value the scale is currently set to).</td> + </tr> + <tr> + <td valign="top" nowrap>Page Up/Down</td> + <td valign="top">Zoom in/out</td> + </tr> + <tr> + <td valign="top" nowrap>Q/W</td> + <td valign="top">Alert the grid lock scale</td> + </tr> + <tr> + <td valign="top" nowrap>G</td> + <td valign="top">Switch grid lines on/off</td> + </tr> + <tr> + <td valign="top" nowrap>X</td> + <td valign="top">Switch grid snapping on/off</td> + </tr> + <tr> + <td valign="top" nowrap>Tab/Shift Tab</td> + <td valign="top">Go to next/previous edit mode</td> + </tr> + <tr> + <td valign="top" nowrap>V</td> + <td valign="top"><a href="#VERTEX_MODE">VERTEX edit mode</a></td> + </tr> + <tr> + <td valign="top" nowrap>L</td> + <td valign="top"><a href="#LINEDEF_MODE">LINEDEF edit + mode</a></td> + </tr> + <tr> + <td valign="top" nowrap>S</td> + <td valign="top"><a href="#SECTOR_MODE">SECTOR edit mode</a></td> + </tr> + <tr> + <td valign="top" nowrap>T</td> + <td valign="top"><a href="#THING_MODE">THING edit mode</a></td> + </tr> + <tr> + <td valign="top" nowrap>C</td> + <td valign="top"><a href="#MULTI_MODE">MULTI edit mode</a></td> + </tr> + <tr> + <td valign="top" nowrap>HOME</td> + <td valign="top"><a href="#LUMP_EDIT">Lump edit menu</a>. + Note that this only applies when the game type is ZDoom.</td> + </tr> + <tr> + <td valign="top" nowrap>F9</td> + <td valign="top">Deselect all objects</td> + </tr> + <tr> + <td valign="top" nowrap>Shift + F9</td> + <td valign="top">Invert current selection</td> + </tr> + <tr> + <td valign="top" nowrap>F10</td> + <td valign="top">Select all objects</td> + </tr> + <tr> + <td valign="top" nowrap>Shift + F10</td> + <td valign="top">Select all objects with a specific type + (not applicable to VERTEX objects).<p>Note that if the + map is in HEXEN format, then this will select special + actions, rather than types for LINEDEFS. Likewise, when + in THING edit mode you will be asked whether to search + for a THING's type or it's special action.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap>Ctrl + F10</td> + <td valign="top">Select one object at the current + position. This is mainly useful for THING and VERTEX + where it may not be possible to select objects that are + overlaid one another.<p>When pressed if there are more + than one object at the current position a list will be + displayed with object numbers (and perhaps some extra + identifying information). Selecting one of the objects + from this list will select that object, whereas + cancelling the list leaves the current selection as is.</p> + <p>Note that it may not be possible to see the selected + object in it's selected colours if it is not on top of + the stack of overlapping objects. </p> + <p>Also bear in mind, that though sectors can be moved so + they overlap, and these can be picked using this method, + a DOOM level should never have overlapping sectors. + Unless you want the Cyberdemon to come and take you away + in the night. </p> + </td> + </tr> + <tr> + <td valign="top" nowrap>Alt + F10</td> + <td valign="top">As above, but any selected object is + selected in addition to the objects currently selected.</td> + </tr> + <tr> + <td valign="top" nowrap>F11/Shift + F11</td> + <td valign="top">Locate the next or previous selected + object and display it in the centre of the screen.</td> + </tr> + <tr> + <td valign="top" nowrap>Delete</td> + <td valign="top">Delete the selected objects <sup><sup>Note + 1</sup></sup></td> + </tr> + <tr> + <td valign="top" nowrap>Insert</td> + <td valign="top">Insert a new object</td> + </tr> + <tr> + <td valign="top" nowrap>F3</td> + <td valign="top">Merge a map. See the <a + href="#MERGE_MAP">merge map</a> section for details.</td> + </tr> + <tr> + <td valign="top" nowrap>F4</td> + <td valign="top">Move selected objects</td> + </tr> + <tr> + <td valign="top" nowrap>F5</td> + <td valign="top">Rotate selected objects <sup><sup>Note 2</sup></sup></td> + </tr> + <tr> + <td valign="top" nowrap>[</td> + <td valign="top">Rotate selected objects 5° to the left <sup><sup>Note + 2</sup></sup></td> + </tr> + <tr> + <td valign="top" nowrap>]</td> + <td valign="top">Rotate selected objects 5° to the right + <sup><sup>Note 2</sup></sup></td> + </tr> + <tr> + <td valign="top" nowrap>F6</td> + <td valign="top">Scale selected objects <sup><sup>Note 2</sup></sup></td> + </tr> + <tr> + <td valign="top" nowrap>{</td> + <td valign="top">Scale selected objects by 90% <sup><sup>Note + 2</sup></sup></td> + </tr> + <tr> + <td valign="top" nowrap>}</td> + <td valign="top">Scale selected objects by 110% <sup><sup>Note + 2</sup></sup></td> + </tr> + <tr> + <td valign="top" nowrap>F7</td> + <td valign="top">Set tag number for this object (only + applicable to SECTOR and LINEDEF objects).</td> + </tr> + <tr> + <td valign="top" nowrap>Shift + F7</td> + <td valign="top">Select objects with a specific tag + number (only applicable to SECTOR and LINEDEF objects).</td> + </tr> + <tr> + <td valign="top" nowrap>F8</td> + <td valign="top">Locate a certain object and display in + the centre of the screen</td> + </tr> + <tr> + <td valign="top" nowrap>Shift + F8</td> + <td valign="top">Locate a certain object, display in the + centre of the screen and select it.</td> + </tr> + <tr> + <td valign="top" nowrap>R</td> + <td valign="top">Redraw the map display. Handy for + refreshing selected objects in SECTOR mode.</td> + </tr> +</table> + +<p><strong>Note 1:</strong> In viDOOM it is not permissible to +delete objects that are still in use by other objects, i.e. it is +impossible to delete a vertex that is still used as one of the +anchor points for a linedef - the linedef must be deleted first.</p> + +<p><strong>Note 2:</strong> When scaling or rotating multiple +objects the objects are rotated/scaled around the centre of the +imaginary box enclosing all the selected objects. Also grid +locking is not honoured in this mode.</p> + +<hr> + +<h2><a name="SECTOR_MODE"></a>Sector Edit Mode</h2> + +<p>For a basic description of a SECTOR see the <a +href="glossary.htm#SECTOR">glossary</a>.</p> + +<h3><a name="SECTOR_DISPLAY"></a>Display in Sector Mode</h3> + +<p>When in sector mode the display will look like this:</p> + +<p><img src="ed_sect.gif" width="640" height="480"></p> + +<p>When in this mode sectors are drawn in red, vertexes are not +shown and things are drawn as a cross just to indicate their +position.</p> + +<p>At the bottom of the screen is a box which shows the details +of the sector the pointer is currently over.</p> + +<p>Also note that if the tag highlighting mode is enabled any +linedefs that have a tag number matching the one for the sector +the pointer is over will be drawn in white. Note that the +highlighted linedef will not appear if is in a sector that's +selected and highlighted.</p> + +<h3><a name="SECTOR_KEYS"></a>Extra Keys in Sector Mode</h3> + +<p>The following extra keys can be used while in sector mode. +Keys that work on selected sectors will temporarily select the +sector the pointer is over if no sectors are selected.</p> + +<table border="1" cellpadding="3"> + <tr> + <td>H</td> + <td>Toggle the tag highlighting mode on an off. See the <a + href="#SECTOR_DISPLAY">display</a> section for an example + of what this does.</td> + </tr> + <tr> + <td valign="top" nowrap>M</td> + <td valign="top">Change sector move mode. See <a + href="overview.htm#SECTOR_MOVE">overview</a> for details.</td> + </tr> + <tr> + <td valign="top" nowrap>, (comma)</td> + <td valign="top">Decrease selected sectors floor height + by 8</td> + </tr> + <tr> + <td valign="top" nowrap>. (period)</td> + <td valign="top">Increase selected sectors floor height + by 8</td> + </tr> + <tr> + <td valign="top" nowrap><</td> + <td valign="top">Decrease selected sectors ceiling height + by 8</td> + </tr> + <tr> + <td valign="top" nowrap>></td> + <td valign="top">Increase selected sectors ceiling height + by 8</td> + </tr> + <tr> + <td valign="top" nowrap>-</td> + <td valign="top">Decrease selected sectors lighting level + by 16</td> + </tr> + <tr> + <td valign="top" nowrap>+</td> + <td valign="top">Increase selected sectors lighting level + by 16</td> + </tr> +</table> + +<h3><a name="SECTOR_INSERT"></a>Sector Insertion</h3> + +<p>When you insert a sector a popup menu is displayed where you +can select a type of sector to create.</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong>Create unbound sector</strong></td> + <td valign="top">This will create a sector that is + unbound to any linedefs/sidedefs. It will have a normal + type as defined in the <a + href="overview.htm#NORMAL_TYPES">config file</a>, a tag + of zero and a floor height, ceiling height and light + level as defined in the <a + href="overview.htm#DEFAULT_SECTOR_LIGHT_ETC">config file</a>. + The floor and ceiling flats will be set to the <a + href="overview.htm#EMPTY_TEXTURE_NAME">empty texture name</a>. + After it's creation the sector number will be displayed + onscreen.<p>Note that no further editing of the sector + can take place until some sidedefs/linedefs have been + manually bound to the sector, as the sector cannot be + visibly selected until they have.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><strong>Create polygon</strong></td> + <td valign="top">On selecting this you will be asked for + the number of sides to the polygon (3-64). After entering + this a cross hair will be displayed. Press the left mouse + button when the cross hair is over the point where you + want the centre of the sector. Then the sector will be + drawn and you can drag it into shape (defining it's size + and rotation) using the mouse. Note that when defining + the centre and size/rotation that grid snapping can be + toggled on/off and the usual zoom in/out and map + positioning cursor keys are active.<p>You will be asked + then for the following information:</p> + <ul> + <li>A <a href="overview.htm#LINEDEF_CLASSES">class</a> + and <a href="overview.htm#LINEDEF_TYPES">type</a> + of linedef (e.g. scrolling walls, normal, + switches) out of those defined in the config + file. Note that for an HEXEN format map a special + action will be asked for instead.</li> + <li>The new sectors floor height, ceiling height and + light level. If the sector is being defined + inside another sector that sectors floor, ceiling + and light level will already be in the dialog + when it appears. Otherwise the <a + href="overview.htm#DEFAULT_SECTOR_LIGHT_ETC">default</a> + values will be used.</li> + <li>A further linedef type that defines the linedef's + flags, as defined in the <a + href="overview.htm#LINEDEF_DEFAULTS">config file</a>.</li> + <li>The style in which the sector should be painted + as defined in the <a + href="overview.htm#SECTOR_STYLES">config file</a>.</li> + <li>If the sector is being defined within another + sector you will be asked if you wish make the + right sidedef point outwards from the new sector.</li> + </ul> + </td> + </tr> +</table> + +<h3><a name="SECTOR_DELETE"></a>Sector Deletion</h3> + +<p>It is not possible to directly delete a sector in viDOOM as it +religiously enforces the fact that objects cannot be deleted if +other objects are still bound to them. And once all the linedefs +bound to a sector have been deleted, the sector is no longer +selectable! However, when saving, any sectors that have no +bounding linedefs will be automatically deleted.</p> + +<h3><a name="SECTOR_POPUP"></a>Sector Popup Menu</h3> + +<p>On selecting sectors and pressing the right mouse button to +get the sector popup menu the following options will be +presented:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong>Change type</strong></td> + <td valign="top">Changes the sector type. The <a + href="overview.htm#SECTOR_CLASSES">classes</a> and their <a + href="overview.htm#SECTOR_TYPES">sector types</a> are + defined in the config file.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change generalised type</strong></td> + <td valign="top">If any generalised sector types have + been defined in the config file, this option allows you + to set them.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Set type to value</strong></td> + <td valign="top">Simply allows you to enter any number to + set the sector type too.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change floor height</strong></td> + <td valign="top">Change the height of the floor of the + selected sectors.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change ceiling height</strong></td> + <td valign="top">Change the height of the ceiling of the + selected sectors.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change light level</strong></td> + <td valign="top">Alter the light level of the sectors.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change tag</strong></td> + <td valign="top">Alter the tag value of the sectors.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change floor</strong></td> + <td valign="top">Shows a picklist of the flats from which + the floor texture can be selected.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change ceiling</strong></td> + <td valign="top">Shows a picklist of the flats from which + the ceiling texture can be selected.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Paint sector in style</strong></td> + <td valign="top">Paints the sector using the textures + described as being a style in the <a + href="overview.htm#SECTOR_STYLES">config file</a>.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Move</strong></td> + <td valign="top">Move the selected sectors.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Delete</strong></td> + <td valign="top">Delete the selected sectors. This just + always shows a reminder that empty sectors will be + deleted when the map is saved.</td> + </tr> +</table> + +<hr> + +<h2><a name="LINEDEF_MODE"></a>Linedef Edit Mode</h2> + +<p>For a basic description of a LINEDEF see the <a +href="glossary.htm#LINEDEF">glossary</a>.</p> + +<h3><a name="LINEDEF_DISPLAY"></a>Display in Linedef Mode</h3> + +<p>When in linedef mode the display will look like this:</p> + +<p><img src="ed_line.gif" width="640" height="480"></p> + +<p>When in this mode linedefs are just drawn as grey lines with a +normal indicating which side is the 'right' side of the linedef. +Vertexes are not shown and things are drawn as a cross just to +indicate their position..</p> + +<p>At the bottom of the screen are three boxes which shows the +details of the linedef the pointer is currently over, and any +associated right and left sidedef. Note that in the screenshot +above the type of linedef is show as the hex number and the <a +href="overview.htm#LINEDEF_CLASSES">class</a> of linedef. This +can be altered in the <a +href="overview.htm#SHOW_FULL_LINEDEF_INFO">config</a> file so +that the linedef type is show as as it's hex number and the name +defined for the linedef <a href="overview.htm#LINEDEF_TYPES">type</a> +in the config file if you are using a display with an higher +resolution. An example of the alterative info for the same +linedef is shown below.</p> + +<p><img src="ed_lninf.gif" width="396" height="72"></p> + +<p>Also note this if the tag highlight mode is on then if the +linedef the pointer is over has a non-zero tag number any sectors +with the same tag number are highlighted in white. E.g. in the +display above the pointer is over a linedef set to teleport, the +tag indicating which sector the teleported object will arrive in. +The sector above and to the right of the line has this tag and is +highlighted. Note that only linedefs not already selected in the +highlighted sector will be highlighted.</p> + +<p>Note that in HEXEN maps the showing of full linedef info +switch is ignored, and the full action special name shown. In +addition to this the special arguments are displayed:</p> + +<p><img src="ed_l_hex.gif" width="274" height="110"></p> + +<p>Note that in this mode the tag highlighting will be based on +any special action arguments called "tag".</p> + +<h3><a name="LINEDEF_KEYS"></a>Extra Keys in Linedef Mode</h3> + +<p>The following extra keys can be used while in linedef mode.</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap>H</td> + <td valign="top">Toggle the tag highlighting mode on an + off. See the <a href="#LINEDEF_DISPLAY">display</a> + section for an example of what this does.</td> + </tr> + <tr> + <td valign="top" nowrap>F12</td> + <td valign="top">Check the currently selected linedefs. + It checks and prompts for the following things:<ul> + <li>Removal of lower textures on 1-sided linedefs.<br> + This check can be disabled in the <a + href="overview.htm#CHECK_LINE_1SIDE_LOWER">config + file</a>.</li> + <li>Removal of upper textures on 1-sided linedefs.<br> + This check can be disabled in the <a + href="overview.htm#CHECK_LINE_1SIDE_UPPER">config + file</a>.</li> + <li>Addition of missing middle textures on 1-sided + linedefs.<br> + This check can be disabled in the <a + href="overview.htm#CHECK_LINE_1SIDE_MIDDLE">config + file</a>.</li> + <li>Removal of all textures on 2-sided linedefs where + both sides of the linedef are in the same sector.<br> + This check can be disabled in the <a + href="overview.htm#CHECK_LINE_2SIDE_SAME_SECTOR">config + file</a>.</li> + <li>Removal of middle textures on 2-sided linedefs.<br> + This check can be disabled in the <a + href="overview.htm#CHECK_LINE_2SIDE_MIDDLE">config + file</a>.</li> + <li>Addition of lower textures on 2-sided linedefs + where the floor heights are different on either + side. In this test the lower texture will be + removed if the floor heights are not different.<br> + This check can be disabled in the <a + href="overview.htm#CHECK_LINE_2SIDE_LOWER">config + file</a>.</li> + <li>Addition of upper textures on 2-sided linedefs + where the ceiling heights are different on either + side. In this test the upper texture will be + removed if the ceiling heights are not different.<br> + This check can be disabled in the <a + href="overview.htm#CHECK_LINE_2SIDE_UPPER">config + file</a>.</li> + </ul> + <p><strong>Note:</strong> When prompted whether to fix a + problem, you can either elect to say <strong>Yes</strong>, + <strong>No</strong>, <strong>Yes to All </strong>or <strong>No + to All</strong>. If you select either <strong>No to All</strong> + or <strong>Yes to All</strong> then it applies to <em>all</em> + further answers in <em>all</em> of the different tests.</p> + <p>Whenever textures are to be added viDOOM will display + the picklist to request what texture to paint, unless the + LINEDEF_CHECK_DEFAULT section in the <a + href="overview.htm#LINEDEF_CHECK_DEFAULT">config</a> file + is set up.</p> + <p><strong>Important Note:</strong> That it may not be + advisable to use this on all linedefs if there are lots + of lifts in the level. viDOOM (due to wanting to remain + completely config file driven for types) is unable to + realise that some textures that look like they shouldn't + be there will come into play when the lift is activated. + This may be changed in the future so that sidedefs that + are pointing into a sector tagged to a linedef of a + specified class are not checked.</p> + </td> + </tr> +</table> + +<h3><a name="LINEDEF_INSERT"></a>Linedef Insertion</h3> + +<p>When inserting a new linedef you will be asked for 2 vertices +to link together. You will then be asked for the linedef type +(from the list defined in the <a +href="overview.htm#LINEDEF_DEFAULTS">config file</a>) and the +middle texture for it. Note that the linedef is created bound to +sector zero. This must be manually changed if it is wrong.</p> + +<h3><a name="LINEDEF_DELETE"></a>Linedef Deletion</h3> + +<p>Linedefs can be safely deleted from the map at any time.</p> + +<h3><a name="LINEDEF_POPUP"></a>Linedef Popup Menu</h3> + +<p>On selecting things and pressing the right mouse button to get +the thing popup menu the following options will be presented:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong>Change linedef type<br> + (Doom only)</strong></td> + <td valign="top">Change the type of the selected + linedefs. On selecting this a list will appear so a class + of linedef can be selected (classes are defined in the <a + href="overview.htm#LINEDEF_CLASSES">config file</a>). + Once the class has been selected the a picklist will + appear from which a type of linedef from that class can + be selected. The types are also defined in the <a + href="overview.htm#LINEDEF_TYPES">config file</a>.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Set Generalised linedef<br> + (Doom only)</strong></td> + <td valign="top">If any generalised linedef types have + been defined in the config file, this option allows you + to set them.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Set type to value<br> + (Doom only)</strong></td> + <td valign="top">Allows the linedef type to be set to an + arbitrary value.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change special action<br> + (Hexen only)</strong></td> + <td valign="top">Change the special action of the + selected linedefs. On selecting this a list will appear + so a class of special can be selected (classes are + defined in the <a + href="overview.htm#HEXEN_ACTION_SPECIAL_CLASSES">config + file</a>). Once the class has been selected the a + picklist will appear from which a type of special action + from that class can be selected. The types are also + defined in the <a + href="overview.htm#HEXEN_ACTION_SPECIALS">config file</a>.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Set special action args<br> + (Hexen only)</strong></td> + <td valign="top">Change the arguments for the selected + linedefs. If all the linedefs are of the same special + action type then the arguments are entered once and set + for all the selected linedefs. If the special actions are + different you are asked if you want to set them all + individually.<p>If you select <strong>yes</strong> then + the argument setting dialog is displayed for each + selected linedef in turn. If <strong>no</strong> is + selected then the first selected linedef is used to work + out what args to ask for, and all selected linedefs have + their arguments set to those.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><strong>Set special action to + value<br> + (Hexen only)</strong></td> + <td valign="top">Allows the linedef special action to be + set to an arbitrary value.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Clear special action args<br> + (Hexen only)</strong></td> + <td valign="top">Clears all the special action arguments + to zero in the selected linedefs.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Set ranged special action + args<br> + (Hexen only)</strong></td> + <td valign="top">Change the arguments for the selected + linedefs, allowing the values to be modified for each + linedef. If all the linedefs are not of the same special + action type then you are asked if you want to continue.<p>If + you select <strong>yes</strong>, or all the linedefs are + of the same special action, then the arguments for the + first line are displayed and can be editted to the + initial values. After this the dialog is redisplayed for + you to enter how much the arguments should change by for + each subsequent line. Once this is entered the arguments + are applied.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change linedef flags</strong></td> + <td valign="top">Allows the flags of the selected + linedefs to be changed. On selecting this a dialog will + appear allowing any of flags (as defined by the <a + href="overview.htm#LINEDEF_FLAGS">config file</a>) to to + toggled on or off.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Swap sides</strong></td> + <td valign="top">Swaps the sides of the linedef, so that + the linedef points in the other direction. Note that on + two sided linedefs the sidedefs are re-arranged so that + the texturing and sector numbers on the sides of the + linedef are not switched.<p>viDOOM will not swap + one-sided by default. Once a one-sided linedef is + detected a question box is displayed to say whether you + want to swap one-sided linedefs (either all or just this + one) or skip them (again either all or this one).</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><strong>Split line</strong></td> + <td valign="top">Splits the selected linedefs in the + middle.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Select trail from this + linedef</strong></td> + <td valign="top">Selects a trail from the last selected + linedef, staying within the same sector as the one on the + linedefs right sidedef, until the starting point is found + again or there are no more linedefs in the same + direction.<p>Note that this behaves differently if the + starting point is a 2-sided line wholly within one sector + - only 2-sided linedefs wholly within the sector too + connected from this one are selected.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><strong>Join linedefs with steps</strong></td> + <td valign="top">Allows two selected linedefs to be + joined with steps. See the <a href="#STAIR_CREATION">Stair + creation</a> section for details.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Right Sidedef</strong></td> + <td valign="top">This will display a sub menu for + operation for the right sidedef. See the <a + href="#SIDEDEF_MENU">Sidedef menu</a> for details.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Left Sidedef</strong></td> + <td valign="top">This will display a sub menu for + operation for the left sidedef. See the <a + href="#SIDEDEF_MENU">Sidedef menu</a> for details.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change tag<br> + (Doom only)</strong></td> + <td valign="top">Change the tag number for the selected + linedefs.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Move</strong></td> + <td valign="top">Move the selected linedefs.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Delete</strong></td> + <td valign="top">Delete the selected linedefs.</td> + </tr> +</table> + +<h3><a name="SIDEDEF_MENU"></a>Sidedef Menu</h3> + +<p>On selecting the <strong>Right sidedef</strong> or <strong>Left +sidedef</strong> options above the following options can be +chosen.</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong>Change upper texture</strong></td> + <td valign="top">Alter the upper texture on the selected + sidedef.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change middle texture</strong></td> + <td valign="top">Alter the middle texture on the selected + sidedef.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change lower texture</strong></td> + <td valign="top">Alter the lower texture on the selected + sidedef.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change textures to any + value</strong></td> + <td valign="top">Displays a dialog where the texture + names can be entered manually. This is mainly of use to + set texture names to the special values ZDoom supports on + linedefs that describe fog colours and so on.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change texture offset</strong></td> + <td valign="top">Sets the texture offset for the selected + sidedef.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Adjust texture offset</strong></td> + <td valign="top">Adjusts the texture offset for the + selected sidedefs. ie. Entering '10' for X and '-10' for + Y will add 10 to all the X offsets and subtract 10 from + all the Y offsets.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change sector</strong></td> + <td valign="top">Alters the sector number the sidedef is + linked with.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Align textures</strong></td> + <td valign="top">Aligns the linedefs textures in the + selected sidedef. Note that when doing this the widest + texture out of the upper, middle and lower textures are + used to calculate the offsets. Also note that the + linedefs are aligned in the order they were selected, so + ensure you selected them in the order they appear.<p>Remember + that if doing the same set of linedefs then the order + should be reversed when doing the left sidedef over the + right sidedef.</p> + </td> + </tr> +</table> + +<h3><a name="STAIR_CREATION"></a>Stair creation</h3> + +<p>This joins the two selected linedefs with steps. Before +starting this option a couple of caveats should be noted:</p> + +<ul> + <li>Lines must be non-zero in length and not share any + vertices.</li> + <li>viDOOM assumes that the steps join up the left sidedef of + the linedefs together. This will generally be the case + anyway, and if it isn't remember to <strong>Swap sides</strong> + on the linedefs as required before starting (the sides + can be swapped back once the steps have been defined).</li> + <li>If the linedefs already have a left sidedef, this will be + deleted in preference for the new one facing onto the new + sectors.</li> + <li>Not too much error checking goes on. If you do define the + most bizarre looking steps you've never seen on an + automap before, there is a fair chance they won't work + when you play them.</li> + <li>If the steps pass through a sector, it must be the same + sector.</li> +</ul> + +<p>i.e. the following two examples are OK, L1 and L2 can be +joined with stairs:</p> + +<p><img src="ed_ex1.gif" width="204" height="97"></p> + +<p><img src="ed_ex2.gif" width="204" height="97"></p> + +<p>But the following example, where stairs would cross both +sectors S3 and S4 is not allowed:</p> + +<p><img src="ed_ex3.gif" width="204" height="97"></p> + +<p>(Actually, it would be allowed, but wouldn't look too nice +when you played the level...)</p> + +<p>Having got the excuses over with we can now go onto the +process involved with creating the steps. On selecting this +option you will be presented with a display like the following:</p> + +<p><img src="ed_step.gif" width="640" height="480"></p> + +<p>As can be seen the steps have been drawn and their are a +couple of help boxes displayed. The box in the lower left of the +screen shows the following information:</p> + +<ul> + <li>No of steps</li> + <li>The difference in floor height between each step</li> + <li>The difference in ceiling height between each step</li> + <li>The step drawing mode (<strong>Straight</strong> or <strong>Curve</strong> + mode).</li> + <li>Which side is being moved when in <strong>Curve</strong> + mode.</li> +</ul> + +<p>On the left is a box giving a quick help on the following +keys:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap>+</td> + <td valign="top">Increase the number of steps in the + staircase (maximum 128)</td> + </tr> + <tr> + <td valign="top" nowrap>- </td> + <td valign="top">Decrease the number of steps in the + staircase (minimum 1)</td> + </tr> + <tr> + <td valign="top" nowrap>C</td> + <td valign="top">Change between <strong>Straight</strong> + and <strong>Curve</strong> drawing mode. In straight mode + straight lines are drawn between the linked vertices on + the two linedefs. In curve mode a curve is defined + between the two points by moving the pointer.</td> + </tr> + <tr> + <td valign="top" nowrap>S</td> + <td valign="top">Swaps between the two walls when + defining the curve of the walls in <strong>curve</strong> + mode.</td> + </tr> + <tr> + <td valign="top" nowrap>R</td> + <td valign="top">Swap which vertices are paired up from + the two linedefs. For instance in the example above + switching the vertices would give the stairs a butterfly + shape rather than corridor seen above.<br> + Obviously, the butterfly effect is <b>not</b> what is + wanted...</td> + </tr> +</table> + +<p>After moving the stairs to the position you want you can press +ESC to cancel the process or press a mouse button to lock the +stairs in position and carry on with the creation.</p> + +<p>At this point you will be asked if the ceiling should also be +stepped. Selecting <strong>No</strong> means that the ceiling +will stay at a constant height (the highest ceiling height out of +the two linedef's sectors), otherwise the ceiling will be stepped +accordingly.</p> + +<p>Now the steps are created. Note that in the created steps the +pegging modes defined for the sector style the stairs will be +painted in is ignored and pegging modes and Y texture offsets +will be set appropriately to line up textures on the sides of the +steps.</p> + +<hr> + +<h2><a name="THING_MODE"></a>Thing Edit Mode</h2> + +<p>For a basic description of a THING see the <a +href="glossary.htm#THING">glossary</a>.</p> + +<h3><a name="THING_DISPLAY"></a>Display in Thing Mode</h3> + +<p>When in thing mode the display will look like this:</p> + +<p><img src="ed_thing.gif" width="640" height="480"></p> + +<p>When in this mode linedefs are just drawn as grey lines, +vertexes are not shown and things are drawn as a circle +(indicating their size within the game - like the Spider +Mastermind above) with an arrow indicating their direction. Note +different classes of things are drawn in different colours (these +colour to class pairing are defined in the <a +href="overview.htm#THING_CLASSES">config file</a>).</p> + +<p>At the bottom of the screen is a box which shows the details +of the thing the pointer is currently over. When editing Hexen +maps this will shows extra information for the thing ID, Z +position and special actions:</p> + +<p><img src="ed_t_hx1.gif" width="338" height="90"></p> + +<p><img src="ed_t_hx2.gif" width="273" height="90"></p> + +<h3><a name="THING_KEYS"></a>Extra Keys in Thing Mode</h3> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap>, (comma)</td> + <td valign="top">Rotate the selected things angles + anticlockwise.</td> + </tr> + <tr> + <td valign="top" nowrap>. (period)</td> + <td valign="top">Rotate the selected things angles + clockwise.</td> + </tr> + <tr> + <td valign="top" nowrap><</td> + <td valign="top">Decrement the Z co-ord. This only + applies when the map is in Hexen format.</td> + </tr> + <tr> + <td valign="top" nowrap>></td> + <td valign="top">Increment the Z co-ord. This only + applies when the map is in Hexen format.</td> + </tr> +</table> + +<h3><a name="THING_INSERT"></a>Thing Insertion</h3> + +<p>When inserting a new thing into the map, the thing will be +inserted at the current pointer position using the values (type, +angle and flags) used when a thing was last edited. e.g.</p> + +<ul> + <li>Insert a new thing.</li> + <li>Select the new thing and change it's type to be a + Cyberdemon (see the <a href="#THING_POPUP">Popup Menu</a> + for how to do this).</li> + <li>All newly inserted things will now be Cyberdemons, until + a further edit is made.</li> +</ul> + +<h3><a name="THING_DELETE"></a>Thing Deletion</h3> + +<p>Things can be safely deleted from the map at any time.</p> + +<h3><a name="THING_POPUP"></a>Thing Popup Menu</h3> + +<p>On selecting things and pressing the right mouse button to get +the thing popup menu the following options will be presented:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong>Change type</strong></td> + <td valign="top">Change the type of the selected things. + On selecting this a list will appear so a class of thing + can be selected (classes are defined in the <a + href="overview.htm#THING_CLASSES">config file</a>). Once + the class has been selected the a picklist will appear + from which a type of thing from that class can be + selected. The types are also defined in the <a + href="overview.htm#THING_TYPES">config file</a>.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Set type to value</strong></td> + <td valign="top">Allows the type of the selected things + to be set to any arbitrary value.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change special action<br> + (Hexen only)</strong></td> + <td valign="top" rowspan="5">These options select and set + the special action details for the selected things. This + operates identically to the <a href="#LINEDEF_POPUP">linedef + special action selection</a>.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Set special action to + value<br> + (Hexen only)</strong></td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change special action + args<br> + (Hexen only)</strong></td> + </tr> + <tr> + <td valign="top" nowrap><strong>Clear special action args<br> + (Hexen only)</strong></td> + </tr> + <tr> + <td valign="top" nowrap><strong>Set ranged special action + args<br> + (Hexen only)</strong></td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change flags</strong></td> + <td valign="top">Allows the flags of the selected things + to be changed. On selecting this a dialog will appear + allowing any of flags (as defined by the <a + href="overview.htm#THING_FLAGS">config file</a>) to to + toggled on or off.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change angle</strong></td> + <td valign="top">Allows the angle of the selected things + to be changed. On selecting this a dialog will appear + allowing any of 8 major compass points to be selected.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Change angle to value<br> + (Hexen only)</strong></td> + <td valign="top">Allows the angle of the selected things + to be changed to an arbitrary value. This is used to set + the angle field to a Polyobject number.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Set Z Position<br> + (Hexen only)</strong></td> + <td valign="top">Sets the Z position of the selected + things.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Set thing ID<br> + (Hexen only)</strong></td> + <td valign="top">Sets the thing ID of the selected + things.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Snap</strong></td> + <td valign="top">Snaps the selected things to the current + grid.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Move</strong></td> + <td valign="top">Move the selected things. Follow the + on-screen prompts for details on how to move the things + or cancel the operation.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Delete</strong></td> + <td valign="top">Delete the selected things.</td> + </tr> +</table> + +<hr> + +<h2><a name="VERTEX_MODE"></a>Vertex Edit Mode</h2> + +<p>For a basic description of a VERTEX see the <a +href="glossary.htm#VERTEX">glossary</a>.</p> + +<h3><a name="VERTEX_DISPLAY"></a>Display in Vertex Mode</h3> + +<p>When in vertex mode the display will look like this:</p> + +<p><img src="ed_vert.gif" width="640" height="480"></p> + +<p>When in this mode linedefs are drawn as grey arrows, the arrow +direction corresponding to direction of the linedef - i.e. the +right side of the linedef is on the right side of the arrow. +Vertices are displayed as white points with a grey selection box +around them.</p> + +<p>At the bottom of the Cyber demons is a box which shows the +details of the vertex the pointer is currently over.</p> + +<h3><a name="VERTEX_KEYS"></a>Extra Keys in Vertex Mode</h3> + +<p>The following extra keys can be used while in linedef mode.</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap>F12</td> + <td valign="top">Goes through all the vertices and delete + ones that are not bound to a linedef.</td> + </tr> +</table> + +<h3><a name="VERTEX_INSERT"></a>Vertex Insertion</h3> + +<p>Inserting a vertex simply inserts a new vertex unbound to any +linedefs at the pointer location.</p> + +<h3><a name="VERTEX_DELETE"></a>Vertex Deletion</h3> + +<p>Vertices can only be deleted if they are not bound to any +linedefs. Linedefs must be <a href="#LINEDEF_DELETE">deleted</a> +before you can delete the vertex.</p> + +<h3><a name="VERTEX_POPUP"></a>Vertex Popup Menu</h3> + +<p>On selecting vertices and pressing the right mouse button to +get the vertex popup menu the following options will be +presented:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong>Chain selected vertices</strong></td> + <td valign="top">Selecting this object will create + linedefs linking the currently selected vertices. Note + that the linedefs will be created such the the first + selected vertex is connected to the vertex selected, the + vertex selected second to the vertex selected third and + so on.<p>On selecting this option you will be first + prompted to selected a <a + href="overview.htm#LINEDEF_CLASSES">class</a> and <a + href="overview.htm#LINEDEF_TYPES">type</a> of linedef + (e.g. scrolling walls, normal, switches) out of those + defined in the config file. Following this you will be + asked for a further linedef type that defines the + linedef's flags, as defined in the <a + href="overview.htm#LINEDEF_DEFAULTS">config file</a>.</p> + <p>If the selected linedef type is one sided and the <a + href="overview.htm#ASK_MIDDLE_ON_2SIDED">config file + options</a> are set accordingly a middle texture for the + linedefs will be requested. After this you will be asked + if you wish to connect the last vertex to the first one + and the linedefs will be created and any textures aligned + on them.</p> + <p><strong>Tip:</strong> One thing this option is very + useful for is creating pillars and the such like inside + sectors. Insert new vertexes (with the <a + href="overview.htm#INSERT_SELECT">insert select</a> + option in the config file enabled) in the shape you want + in an <strong><u>anti-clockwise</u></strong> direction. + Then select this option and a one-sided linedef type and + the result will be a column in the room.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><strong>Chain selected vertices + into a sector</strong></td> + <td valign="top">Selecting this object will create + linedefs linking the currently selected vertices and bind + the linedefs to a newly created sector. Note that the + linedefs will be created such the the first selected + vertex is connected to the vertex selected, the vertex + selected second to the vertex selected third and so on. + It is important for this option to operate as described + below that the vertices are selected in a <strong><u>clockwise</u></strong> + direction.<p>You will be asked then for the following + information:</p> + <ul> + <li>A <a href="overview.htm#LINEDEF_CLASSES">class</a> + and <a href="overview.htm#LINEDEF_TYPES">type</a> + of linedef (e.g. scrolling walls, normal, + switches) out of those defined in the config + file.</li> + <li>The new sectors floor height, ceiling height and + light level.</li> + <li>A further linedef type that defines the linedef's + flags, as defined in the <a + href="overview.htm#LINEDEF_DEFAULTS">config file</a>.</li> + <li>The style in which the sector should be painted + as defined in the <a + href="overview.htm#SECTOR_STYLES">config file</a>.</li> + <li>If the sector is being defined within another + sector you will be asked if you wish make the + right sidedef point outwards from the sector. + This is useful as things like teleport linedefs + only work if you cross from the right side to the + left side of the linedef. For this part to work + as described the vertices must have been selected + in a <strong><u>clockwise</u></strong>order.</li> + </ul> + <p><strong>Tip:</strong> If you select vertices that are + bound to linedefs in another sector, once the creation of + the sector has been completed viDOOM will ask you if you + want to merge the new linedef from the new sector with + the old one.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><strong>Merge selected vertices</strong></td> + <td valign="top">The vertices selected second, third and + so on are deleted and all references to those vertices in + linedefs changed to the be the first selected vertex. + After doing this viDOOM will see if any linedefs are now + using the same vertex pair to define their position and + you can elect to merge these.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Snap</strong></td> + <td valign="top">Snaps the selected vertices to the + current grid.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Move</strong></td> + <td valign="top">Move the selected vertices. Follow the + on-screen prompts for details on how to move the vertices + or cancel the operation.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Delete</strong></td> + <td valign="top">Delete the selected vertices.</td> + </tr> +</table> + +<hr> + +<h2><a name="MULTI_MODE"></a>Multi Edit Mode</h2> + +<h2><a name="MULTI_DISPLAY"></a>Display</h2> + +<p>When in multi mode the display will look like this:</p> + +<p><img src="ed_multi.gif" width="640" height="480"></p> + +<p>When in this mode linedefs are drawn as grey arrows, the arrow +direction corresponding to direction of the linedef - i.e. the +right side of the linedef is on the right side of the arrow. +Vertices are displayed as white points with a grey selection box +around them.</p> + +<p>Things are drawn as a circle (indicating their size within the +game) with an arrow indicating their direction. Note different +classes of things are drawn in different colours (these colour to +class pairing are defined in the <a +href="overview.htm#THING_CLASSES">config file</a>)</p> + +<p>At the bottom of the vertexes is a box which shows what object +type (vertex or thing) the pointer is currently over and it's +number.</p> + +<h2><a name="MULTI_OPERATION"></a>Operation</h2> + +<p>Multi selection mode is given just as a way of manipulating +things and vertexes, allowing them to be moved, rotated and +scaled in unison. Note that linedefs and sectors are not +selectable as these are inherently moved just by moving the +vertices.</p> + +<p>All keys are honoured in multi selection mode, but keys for +insertion and deletion are ignored. Also the locate object number +keys (F8 and Shift + F8) are ignored.</p> + +<h3><a name="MULTI_POPUP"></a>Multi Edit Popup Menu</h3> + +<p>On selecting vertices and things and pressing the right mouse +button to get the multi edit popup menu the following options +will be presented:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong>Snap</strong></td> + <td valign="top">Snap the selected things and vertices to + the current grid.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Rotate</strong></td> + <td valign="top">Rotate the selected things and vertices + around their common centre.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Scale</strong></td> + <td valign="top">Rotate the selected things and vertices + around their common centre.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Move</strong></td> + <td valign="top">Moves the selected things and vertices.</td> + </tr> +</table> + +<hr> + +<h2><a name="MERGE_MAP"></a>Merging Maps</h2> + +<p>Pressing F3 in any edit mode will allow a different map to be +merged in with the currently edited map.</p> + +<p>First you will be asked if you wish to temporarily load a PWAD +and load the first map from it (note that the 'first' map means +MAP01 if editing DOOM II maps, and E1M1 if editing DOOM maps). If +you select <strong>Yes</strong> then you will be prompted for a +PWAD file to read. If you select <strong>No</strong> you will be +asked for a level to load from the currently loaded WADs.</p> + +<p><strong>Tip:</strong> The idea of this question is that you +can save PWAD files with a structure you wish to re-use in +multiple maps as the first map. Any linedefs in this map that +have a left sidedef bound to sector number -1 will have the +sector number reset to the sector in which the merged structure +has been placed. Note that if you do insert structures this way +and don't opt to have floors/ceilings automatically adjusted then +it is generally a good idea to check the sidedefs to make sure +they have all their necessary upper/lower textures.</p> + +<p>After choosing the map to merge in, a vector display +representing the linedefs from the map that can be moved around +the screen with the mouse is displayed as shown in the screen +below.</p> + +<p><img src="ed_merge.gif" width="640" height="480"></p> + +<p>On the bottom left is a box giving a quick help on the +following keys:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap>, (comma)</td> + <td valign="top">Rotate the map 1° to the left.</td> + </tr> + <tr> + <td valign="top" nowrap>. (period)</td> + <td valign="top">Rotate the map 1° to the right.</td> + </tr> + <tr> + <td valign="top" nowrap><</td> + <td valign="top">Rotate the map 10° to the left.</td> + </tr> + <tr> + <td valign="top" nowrap>></td> + <td valign="top">Rotate the map 10° to the right.</td> + </tr> +</table> + +<p>Pressing ESC will cancel the operation while pressing a mouse +button will place the map where it currently is.</p> + +<p>If the merged map is assumed to be a structure (i.e. it has +left sidedefs whose sector fields are set to -1) you are then +asked whether you wish to adjust the floor heights. If you do +select to adjust them then the sector that is in the middle of +the merged in WAD has it's floor height taken. The floors and +ceilings are adjusted in the new sectors so that the lowest floor +is at the same height as the surrounding floor, but the +difference in heights between the new sectors themselves are +maintained. After this you are asked whether to adjust the +ceiling heights. If you do the ceiling height of the same sector +is taken and the new sector's ceilings adjusted to match the +surrounding ceiling (note that ceilings will not be moved if +moving them places them lower than the floor).</p> + +<p>You will then be asked whether tags within the map should stay +as they are or be adjusted so that they are different from tags +used in the map being edited. After this the map will be inserted +and then a notice displayed showing by how much the various +objects in the merged map where adjusted.</p> + +<hr> + +<h2><a name="LUMP_EDIT"></a>Lump Editing</h2> + +<p>Pressing the HOME key provides you with the lump editing menu. +This menu is different depending on whether the map you are +editting is a DOOM format map or an HEXEN format map.</p> + +<h3>Doom</h3> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong>Edit MAPINFO</strong></td> + <td valign="top">This option only appears if the ability + to edit the MAPINFO lump is enabled in the <a + href="overview.htm#MAPINFO_LUMP">config</a> file. On + selecting this option the loaded MAPINFO lump is + displayed in the text editor for editing.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Switch to HEXEN mode</strong></td> + <td valign="top">This switches a DOOM map to an HEXEN + map.<p><strong>NOTE:</strong> Take heed of the displayed + warning. viDOOM makes no pretence at doing a proper + conversion - special actions, arguments, thing IDs and so + on are simply zeroed.</p> + </td> + </tr> +</table> + +<h3>Hexen</h3> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong>Edit MAPINFO</strong></td> + <td valign="top">This option only appears if the ability + to edit the MAPINFO lump is enabled in the <a + href="overview.htm#MAPINFO_LUMP">config</a> file. On + selecting this option the loaded MAPINFO lump is + displayed in the text editor for editing.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Edit SCRIPTS</strong></td> + <td valign="top">This displays the SCRIPTS lump from the + current map in the text editor for changing.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Create BEHAVIOR</strong></td> + <td valign="top">Creates the BEHAVIOR lump from the + SCRIPTS. The ACS compiler must be <a + href="overview.htm#ACC">configured</a> for this to work. + Depending on other <a href="overview.htm#ACS_ALWAYS_VIEW">configuration</a> + the output from the compilation will always be shown, or + just when an error occurs. Note that the current BEHAVIOR + lump is not replaced if an error occurs.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>Switch to DOOM mode</strong></td> + <td valign="top">This switches an HEXEN map to a DOOM + map.<p><strong>NOTE:</strong> Take heed of the displayed + warning. viDOOM makes no pretence at doing a proper + conversion - linedef types will be wrong and any entered + SCRIPTS will be lost.</p> + </td> + </tr> +</table> + +<hr> + +<p><a href="index.htm">Back to index</a></p> + +<p><tt>$Id$</tt></p> +</body> +</html> diff --git a/doc/glossary.htm b/doc/glossary.htm new file mode 100644 index 0000000..c0dc89f --- /dev/null +++ b/doc/glossary.htm @@ -0,0 +1,152 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<h1 align="center">Glossary</h1> + +<p>It is assumed that some knowledge of DOOM is known by the +reader, but in case here are some general terms that are used in +the documentation. </p> + +<h2>DOOM</h2> + +<p>If you're unsure what this is, you've probably come to the +wrong place by accident.</p> + +<h2><a name="WAD">WAD</a></h2> + +<p>A WAD file is the name given to the files used to define the +graphics, sound, music and maps that make up a game of DOOM. WAD +files are split into two different types, <a href="#IWAD">IWAD</a> +and <a href="#PWAD">PWAD</a> files. </p> + +<h2><a name="IWAD">IWAD</a></h2> + +<p>An IWAD file is is the 'Internal WAD' file. The IWAD file is +the main <a href="#WAD">WAD</a> file as supplied by id Software. +Note that only id Software are meant to produce IWAD files. </p> + +<h2><a name="PWAD">PWAD</a></h2> + +<p>An PWAD file is is the 'Patch WAD' file. This WAD file is read +in by the game after the IWAD file, and an entries in it replace +entries defined in the IWAD file. So for instance a PWAD file +that has an entry for the first <a href="#MAP">MAP</a> will mean +that DOOM will read the first map from the PWAD file, rather than +it's own IWAD file. </p> + +<p>Hmmmm.... Sure I could have explained that better. </p> + +<h2><a name="LUMP"></a>LUMP</h2> + +<p>A lump is an entry in a <a href="#WAD">WAD</a> file. For +instance a <a href="#MAP">MAP</a> is made up of a number of +lumps, one defining the <a href="#SECTOR">sectors</a> for the +map, another the <a href="#THING">things</a>, and so on.</p> + +<h2><a name="MAP">MAP</a></h2> + +<p>A map is taken to mean a DOOM level. Note there is two naming +conventions for maps, <b>E</b><b><i>x</i></b><b>M</b><b><i>y</i></b> +(where <i>x</i> is the episode number, and <i>y</i> the map +number in that episode) used in DOOM and Ultimate DOOM, and <b>MAP</b><b><i>nn</i></b> +(where <i>nn</i> is a number between 01 and 32) used in DOOM 2 +and Final Doom. </p> + +<p>A map is made up of a number of different elements, namely <a +href="#SECTOR">sectors</a>, <a href="#LINEDEF">linedefs</a>, <a +href="#SIDEDEF">sidedefs</a>, <a href="#THING">things</a> and <a +href="#VERTEX">vertexes</a>. Following are some (very) basic +definitions for these things, but you would be better reading the +<a href="thanks.htm#DOOMSPEC">Unofficial DOOM specs</a> for more +accurate definitions. </p> + +<p>MAPs generated in viDOOM must be built with a <a href="#BSP">Node +Builder</a> before they can be used in DOOM. </p> + +<p>Also note that viDOOM supports two different map formats. The +original one, as used in DOOM and DOOM 2, and the extended <a +href="#HEXEN">HEXEN</a> format supported by ZDoom.</p> + +<h2><a name="VERTEX">VERTEX</a></h2> + +<p>A vertex is simply an X,Y co-ordinate in the DOOM world. </p> + +<h2><a name="THING">THING</a></h2> + +<p>Things are all the different objects on the DOOM map that are +not the actual walls and doors. All the monsters, weapons, ammo, +non-wall based scenery and even your starting point and teleport +locations are all counted as THINGS in DOOM. </p> + +<h2><a name="LINEDEF">LINEDEF</a></h2> + +<p>A LINEDEF is a line that connects two <a href="#VERTEX">vertexes</a>. +A LINEDEF at it's most basic is used to make up the walls that +make up the DOOM levels. A LINEDEF must <i>always</i> have a +right <a href="#SIDEDEF">SIDEDEF</a> associated with it. 2 sided +LINEDEFS will also have a left SIDEDEF associated with it. </p> + +<p>In addition special types of LINEDEF can be used so that when +the player crosses or activates them actions happen in the DOOM +world - lighting changes, teleportation happens, doors open, +levels are exited and so on. </p> + +<h2><a name="SIDEDEF">SIDEDEF</a></h2> + +<p>A SIDEDEF is basically used to describe the <a href="#TEXTURE">textures</a> +that are painted upon a <a href="#LINEDEF">LINEDEF</a>. </p> + +<h2><a name="SECTOR">SECTOR</a></h2> + +<p>Though technically incorrect, it is simplest to picture a +SECTOR as an object enclosed by <a href="#LINEDEF">LINEDEFS</a>. +A sector is used to define the floor and ceiling heights and <a +href="#FLAT">flats</a> used to draw the floor and ceiling, in +addition to the lighting for that area. </p> + +<h2><a name="TEXTURE">TEXTURE</a></h2> + +<p>A texture is the graphics used to paint the walls in DOOM. </p> + +<h2><a name="FLAT">FLAT</a></h2> + +<p>A flat is the graphics used to paint the floors and ceiling in +DOOM. </p> + +<h2><a name="BSP">Node Builder</a></h2> + +<p>Does some of the nastier work of level building by creating +the extra information over and above the parts mentioned in <a +href="#MAP">MAPS</a> so that DOOM can actually understand the +level data enough to play it. Note that if you are to edit <a +href="#HEXEN">HEXEN</a> levels you will need a node builder that +understands the different data structures used.</p> + +<p><a href="thanks.htm#BSP">BSP</a> is perhaps the best known of +these, and the one that was used all through viDOOM's initial +development. When developing the HEXEN edit mode for ZDoom <a +href="thanks.htm#WARM">WARM</a> was used.</p> + +<h2><a name="HEXEN"></a>HEXEN</h2> + +<p>HEXEN is a game by Raven software that uses the DOOM graphics +engine. The WAD format for this is slightly different to DOOM, +having a BEHAVIOR <a href="#LUMP">lump</a> for each MAP and +different formats for <a href="#THING">THINGS</a> and <a +href="#LINEDEF">LINEDEFS</a>.</p> + +<p>ZDoom supports this format.</p> + +<hr width="99%"> + +<p><a href="index.htm">Back to index</a></p> + +<p><tt>$Id$ </tt></p> +</body> +</html> diff --git a/doc/index.htm b/doc/index.htm new file mode 100644 index 0000000..d701e9e --- /dev/null +++ b/doc/index.htm @@ -0,0 +1,45 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<h1 align="center">viDOOM 0.03</h1> + +<p align="center">"... but what I talk about is DOOM, +because in the end, DOOM is all that counts."<br> +The Dark Half - <i>Stephen King</i> </p> + +<p align="center">viDOOM is released as free software under the +GNU General License. See <a href="license.htm">licence</a> for +details. </p> + +<hr> + +<h2>Index</h2> + +<ol> + <li><a href="thanks.htm">Acknowledgements and thanks</a> </li> + <li><a href="glossary.htm">Simple glossary</a> </li> + <li><a href="overview.htm">Overview (Configuration, etc)</a> </li> + <li><a href="mainmenu.htm">Main menu</a> </li> + <li><a href="editing.htm">Editing</a></li> + <li><a href="sys.htm">System dependent documentation</a></li> + <li><a href="porting.htm">Porting</a> </li> + <li><a href="bugs.htm">Bug Reports</a></li> + <li><a href="building.htm">Building</a> (only needs to be + read for the source distribution)</li> + <li><a href="changelog.htm">Changelog</a></li> +</ol> + +<hr width="99%"> + +<p><a href="http://www.noddybox.demon.co.uk/vidoom">viDOOM home +page</a></p> + +<p><tt>$Id$</tt></p> +</body> +</html> diff --git a/doc/license.htm b/doc/license.htm new file mode 100644 index 0000000..e7ba3e2 --- /dev/null +++ b/doc/license.htm @@ -0,0 +1,360 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<pre> + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. +</pre> + + +<hr width="99%"> + +<p><a href="index.htm">Back to index</a></p> + +<p><tt>$Id: license.htm,v 1.1 2000/07/13 15:05:48 dosuser Exp dosuser $</tt></p> +</body> +</html> diff --git a/doc/mainmenu.htm b/doc/mainmenu.htm new file mode 100644 index 0000000..fb8b498 --- /dev/null +++ b/doc/mainmenu.htm @@ -0,0 +1,177 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<h1 align="center">Main Menu</h1> + +<p>Depending on the setting in <a href="overview.htm#GAME_ASK">configuration</a> +starting viDOOM you may be asked to pick the game type to edit. +Pick one of the games from the menu, or if the menu is cancelled +the <a href="overview.htm#GAME_TYPE">default game type</a> will +be used.</p> + +<p>After this you will be presented with the main menu containing +the following options.</p> + +<h3>Edit The Current Level</h3> + +<blockquote> + <p>Edit the current level. See <a href="editing.htm">editing</a> + for details.</p> +</blockquote> + +<h3>Load Level from open WADs</h3> + +<blockquote> + <p>Selects a level to load from the open IWAD and PWAD files. + Note that the level is loaded from the <strong>most recent</strong> + WAD file opened that holds a map for the selected level. If + you already have a level open for editing and the <strong>Edit + The Current Level</strong> option has been used then + confirmation will be asked before allowing you to proceed. + This warning can be disabled in the INI file (see <a + href="overview.htm#MAP_CLEAR">overview</a>).</p> +</blockquote> + +<blockquote> + <p>If you are editing for ZDOOM and the <a + href="overview.htm#MAPINFO_LUMP">mapinfo_lump</a> option has + been switched on, if there is a MAPINFO.WAD file in the <a + href="overview.htm#PWAD_DIR">PWAD directory</a> it will be + loaded in as well for editing.</p> +</blockquote> + +<h3>Save Current Level</h3> + +<blockquote> + <p>Saves the map you are currently editing into a <a + href="glossary.htm#PWAD">PWAD</a> file. This PWAD will <strong>only</strong> + contain the map, and nothing else. So if you have loaded the + map from a PWAD that contains any other information (e.g. + music, extra sounds, graphics patches) it is not a good idea + to save over the original PWAD file. If the saved PWAD + filename matches a PWAD that is already loaded, after saving + the open PWAD will be closed and then re-opened. This could + be important to note as the saved PWAD will now be at the top + of the open PWADs.</p> +</blockquote> + +<blockquote> + <p>If you are editing for ZDOOM and the <a + href="overview.htm#MAPINFO_LUMP">mapinfo_lump</a> option has + been switched on, a MAPINFO.WAD file will be generated in the + <a href="overview.htm#PWAD_DIR">PWAD directory</a>.</p> +</blockquote> + +<blockquote> + <p>Note that the saved map must be built with a <a + href="glossary.htm#BSP">node builder</a> before it can be + played in DOOM if the <a href="overview.htm#NODE_BUILDER">node + building section in the config file</a> has not been set up.</p> +</blockquote> + +<blockquote> + <p><strong>Important note: </strong>While saving viDOOM will + remove any deleted objects from the map, delete any sectors + that do no have linedefs attached to them and vertices that + are not in use. This is worth remembering as objects <em>will</em> + be renumbered if any have been deleted so that they fit + together contiguously after saving.</p> +</blockquote> + +<h3>Create New Empty Level</h3> + +<blockquote> + <p>Creates a completely empty level for editing. If you + already have a level open for editing and the <strong>Edit + The Current Level</strong> option has been used then + confirmation will be asked before allowing you to proceed. + This warning can be disabled in the INI file (see <a + href="overview.htm#MAP_CLEAR">overview</a>).</p> +</blockquote> + +<blockquote> + <p>If you are editing for ZDOOM and the <a + href="overview.htm#MAPINFO_LUMP">mapinfo_lump</a> option has + been switched on, if there is a MAPINFO.WAD file in the <a + href="overview.htm#PWAD_DIR">PWAD directory</a> it will be + loaded in as well for editing.</p> +</blockquote> + +<h3>Change Current Level Name</h3> + +<blockquote> + <p>Allows the map to be assigned to a different map number. + See the <a href="glossary.htm#MAP">glossary</a> for some + extra info on map/level numbers.</p> +</blockquote> + +<h3>Open PWAD file</h3> + +<blockquote> + <p>Allows a PWAD file to be opened.</p> +</blockquote> + +<h3>Close PWAD file</h3> + +<blockquote> + <p>Closes one of the currently open PWAD files. Note that + viDOOM will not let you unload the <a + href="glossary.htm#IWAD">IWAD</a> opened at startup.</p> +</blockquote> + +<h3>Preview Level</h3> + +<blockquote> + <p>Allows a level to be previewed without disturbing the + level currently being edited. Note that the preview is <em>very</em> + basic with linedefs shown as grey lines, vertexes as white + pixels and things as red blobs. While in the preview press F1 + for help on controlling the display.</p> +</blockquote> + +<h3>List entries in WAD directory</h3> + +<blockquote> + <p>This will list all the lumps defined in the directory of + the loaded IWAD and any loaded PWAD files. The entires will + be displayed in a picklist window.</p> +</blockquote> + +<h3>About viDOOM</h3> + +<blockquote> + <p>See who was responsible for this.</p> +</blockquote> + +<h3>View License</h3> + +<blockquote> + <p>View the license you must accept if you are to run viDOOM. + The license is repeated <a href="license.htm">here</a> in the + documentation. Don't worry, it's only the GNU public license, + and not anything nasty.</p> +</blockquote> + +<h3>Quit</h3> + +<blockquote> + <p>Exit viDOOM. If you have a level open for editing and the <strong>Edit + The Current Level</strong> option has been used then + confirmation will be asked before allowing you to proceed. + This warning can be disabled in the INI file (see <a + href="overview.htm#MAP_EXIT">overview</a>).</p> +</blockquote> + +<hr> + +<p><a href="index.htm">Back to index</a></p> + +<p><tt>$Id$</tt></p> +</body> +</html> diff --git a/doc/overview.htm b/doc/overview.htm new file mode 100644 index 0000000..aeec434 --- /dev/null +++ b/doc/overview.htm @@ -0,0 +1,1164 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<h1 align="center">viDOOM Overview</h1> + +<ul> + <li><a href="#INTRODUCTION">Introduction</a></li> + <li><a href="#WHY_BOTHER">Why bother?</a></li> + <li><a href="#SUPPORTED_SYSTEMS">Supported systems</a></li> + <li>Configuration<ul> + <li><a href="#ENVIRONMENT">Environment</a></li> + <li><a href="#VIDOOM_INI">VIDOOM.INI</a></li> + <li><a href="#CONFIG_FILE">Config file</a></li> + </ul> + </li> +</ul> + +<hr> + +<h2><a name="INTRODUCTION"></a>Introduction</h2> + +<p>viDOOM is a Free Software DOOM editor. It supports the +following <a href="http://www.idsoftware.com">id</a> produced +games: </p> + +<ul> + <li>Doom</li> + <li>The Ultimate Doom</li> + <li>Doom 2 - Hell On Earth</li> + <li>Final Doom</li> +</ul> + +<p>Note that in accordance with id software's wishes you are only +allowed to generate edited levels for the game if you have a +full, registered version of the game.</p> + +<p>viDOOM is fully configurable through config files, so it can +be expanded to accommodate the BOOM and ZDOOM extensions. Well, +once I've found out the slight difference in format of WAD file +that seems to accompany them too.</p> + +<hr> + +<h2><a name="WHY_BOTHER"></a>Why bother?</h2> + +<p>Good question. Anyone who has ever used them will know there +are a number of very good DOOM editors available (links to lots +of them can be found at <a href="http://www.doomworld.com">Doomworld</a> +and all good FTP servers). </p> + +<p>However, I always felt a bit clumsy using them, and in true +hacker fashion I decided writing one I could use would be easier +then reading the instructions for the others. Hence viDOOM.</p> + +<p>I reasonably like the way viDOOM works, so I release it on the +off chance other people may too. </p> + +<hr> + +<h2><a name="SUPPORTED_SYSTEMS"></a>Supported systems</h2> + +<p>As with most Free Software viDOOM is primarily distributed as +source code. The source is fairly ANSI C compliant, and therefore +the core of viDOOM should compile on all operating systems. The +current distribution includes the hardware dependent routines for +the following platforms: </p> + +<ul> + <li>Protected-mode 32-bit MSDOS (Windows 9x / MSDOS + DPMI)</li> +</ul> + +<hr> + +<h2><a name="ENVIRONMENT"></a>Environment</h2> + +<p>If defined in the environment, the value of <b><tt>VIDOOM_DIR</tt></b> +will be made the current directory as soon as viDOOM starts. This +is important as the following initialisation files are expected +to be in the current directory when viDOOM starts.</p> + +<p><b>Note:</b><br> +How this environment is set will differ from system. Basically +viDOOM just calls the C library <tt>getenv()</tt> function. So,. +for instance in a Unix type system this could be:</p> + +<blockquote> + <p><tt>% setenv VIDOOM_DIR $HOME/viDOOM</tt><br> + or<br> + <tt>% export VIDOOM_DIR=$HOME/viDOOM</tt></p> +</blockquote> + +<p>Whereas in DOS/DJGPP version this could be:</p> + +<blockquote> + <p><tt>C:\> set VIDOOM_DIR=C:\viDOOM</tt></p> +</blockquote> + +<hr> + +<h2><a name="VIDOOM_INI"></a>Initialisation File - VIDOOM.INI</h2> + +<p>On starting viDOOM will look for a file in the current +directory called <b>vidoom.ini</b>. This file tells viDOOM what +version of DOOM it is expected to be editing, the configuration +for that version of DOOM and the editor configuration. The +general format of the INI file is: </p> + +<p><tt>[Section]<br> +identifier=value<br> +</tt></p> + +<p>Blank lines and lines starting with a comment character (#) +are ignored. The following sections and identifiers are explained +below.</p> + +<ul> + <li><a href="#GAME"><tt>[Game]</tt></a></li> + <li><a href="#EDITOR"><tt>[Editor]</tt></a></li> + <li><a href="#viDOOM"><tt>[viDOOM]</tt></a></li> + <li><a href="#CHECK_LINEDEF"><tt>[Check Linedef]</tt></a></li> + <li><a href="#NODE_BUILDER"><tt>[Node Builder]</tt></a></li> + <li><a href="#ACC"><tt>[ACS]</tt></a></li> + <li><a href="#GUI"><tt>[GUI]</tt></a></li> + <li><a href="#GAME_NAME"><tt>[</tt><em><tt>Game Name</tt></em><tt>]</tt></a></li> +</ul> + +<p>After reading VIDOOM.INI, then a defined <a +href="#CONFIG_FILE">config file</a> is also read.</p> + +<hr size="1"> + +<h3><a name="GAME"></a>[Game]</h3> + +<p>Controls for the version of DOOM</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><a name="GAME_TYPE"></a><b><tt>game</tt></b></td> + <td valign="top">Use this to say which version of DOOM + you will be editing. The following values are allowed: <ul> + <li><b>Doom</b></li> + <li><b>Ultimate Doom</b></li> + <li><b>Doom 2</b></li> + <li><b>TNT: Evilution</b></li> + <li><b>Plutonia Experiment</b></li> + <li><b>ZDoom</b></li> + </ul> + </td> + </tr> + <tr> + <td><a name="GAME_ASK"></a><b>ask</b></td> + <td>If set to <b>yes</b> then on starting viDOOM will ask + for the game type to edit.</td> + </tr> +</table> + +<hr size="1"> + +<h3><a name="EDITOR"></a>[Editor]</h3> + +<p>Global editor configuration</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><a name="ASK_MIDDLE_ON_2SIDED"></a><b><tt>ask_middle_on_2sided</tt></b></td> + <td valign="top">When creating a new sector and the + sector has a two-sided boundary asks whether a middle + texture should be asked for. Allowable values <b>yes</b> + and <b>no</b></td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>auto_block_linedefs</tt></b></td> + <td valign="top">When linedef flags are altered and this + is set to <b>yes</b>, linedefs that were 2-sided and are + now 1-sided have the impassible flags automatically set.<br> + Likewise linedefs that were 1-sided and are now 2-sided + have the impassible flag automatically cleared.<br> + If set to <i>no</i> no action is taken when these events + happen. </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>bright</tt></b></td> + <td valign="top">Describes a gamma value applied to any + textures, flats and sprites read in from the DOOM WAD + files. Allowable values are any floating point number.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>clear_on_menu</tt></b></td> + <td valign="top">If set to <b>yes</b> then once the + pop-up menu has been used to modify the selected objects + they are automatically unselected. </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>clear_on_move</tt></b></td> + <td valign="top">If set to <b>yes</b> then once the + selected objects have been moved they are automatically + unselected. </td> + </tr> + <tr> + <td valign="top" nowrap><a + name="DEFAULT_SECTOR_LIGHT_ETC"></a><b><tt>default_light_level</tt></b><tt><br> + </tt><b><tt>default_floor_height</tt></b><tt><br> + </tt><b><tt>default_ceiling_height</tt></b></td> + <td valign="top">Describes the default light, floor and + ceiling height for created sectors. Note that if a sector + is created within another sector, the values for that + sector, rather than these defaults, are used.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>default_edit_mode</tt></b></td> + <td valign="top">Defines the default edit mode when + loading a new map into the editor. Possible values are:<ul> + <li><a href="editing.htm#SECTOR_MODE"><b>sector</b></a></li> + <li><a href="editing.htm#LINEDEF_MODE"><b>linedef</b></a></li> + <li><a href="editing.htm#THING_MODE"><b>thing</b></a></li> + <li><a href="editing.htm#VERTEX_MODE"><b>vertex</b></a></li> + <li><a href="editing.htm#MULTI_MODE"><b>multi</b></a></li> + </ul> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>default_scale</tt></b></td> + <td valign="top">The starting scale used in the editor.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>grid</tt></b></td> + <td valign="top">Use this to say whether the grid will be + shown on screen while editing. Allowable values are <b>yes</b> + and <b>no</b>. </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>grid_lock</tt></b></td> + <td valign="top">Use this to say whether inserted and + moved object in the editor will be snapped on a grid. + Allowable values are <b>yes</b> and <b>no</b>. </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>grid_size</tt></b></td> + <td valign="top">Describes the size of the grid used by + the above items. Allowable values are integer values + greater than two.</td> + </tr> + <tr> + <td valign="top" nowrap><a name="HOVER_SELECT"></a><b><tt>hover_select</tt></b></td> + <td valign="top">When editing object in a DOOM level this + alters the way that viDOOM works if the right mouse + button is used over an unselected object. The allowable + values, and their affects are: <ul> + <li><b>none</b> - nothing is done. The right mouse + button works as expected.</li> + <li><b>add</b> - the object the mouse is over is + added to the selected objects before displaying + the menu.</li> + <li><b>single</b> - the object the mouse is over is + made the sole selected object before displaying + the menu.</li> + </ul> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="INSERT_SELECT"></a><b><tt>insert_select</tt></b></td> + <td valign="top">When inserting new objects in a DOOM + level this alters the way that viDOOM selects the objects + after creation. The allowable values, and their affects + are: <ul> + <li><b>none</b> - nothing is done. The new object is + left unselected.</li> + <li><b>add</b> - the new object is added to the + selected objects.</li> + <li><b>single</b> - the new object is made the sole + selected object.</li> + </ul> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>linedef_select</tt></b></td> + <td valign="top">Defines who many pixels around a LINEDEF + the bounding box stretches when selecting a line.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>merge_linedef</tt></b></td> + <td valign="top">If you overlay vertexes on top of each + other you can merge vertexes. Once these vertexes are + merged checks are made to see whether linedefs share + these vertexes, and hence overlap. This option controls + what happens when these overlapping linedefs are found. + The possible values are: <ul> + <li><b>always</b> - always merge the overlapping + linedefs without any prompting.</li> + <li><b>ask</b> - confirms with the user before + merging linedefs or not as requested. </li> + <li><b>never</b> - never merge overlapping linedefs. </li> + </ul> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>new_2sided_select</tt></b></td> + <td valign="top">When linedefs have there flags set so + that a new left sidedef is added to it controls how the + altered linedefs are selected. Possible values are: <ul> + <li><b>select</b> - clears the currently selected + linedefs (if any) and selects the linedefs that + have new double sides.</li> + <li><b>ask</b> - confirms with the user. If the user + opts to select the linedefs, the current + selection is cleared and the altered linedefs + selected.</li> + <li><b>never</b> - never select the altered linedefs. + Note that a notice is still displayed so that you + know new left sidedefs have been created.</li> + </ul> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="SECTOR_MOVE"></a><b><tt>sector_move</tt></b></td> + <td valign="top">Defines which linedefs are actually + moved when moving a sector. Possible values are: <ul> + <li><b>all</b> - move all linedefs that border the + sector.</li> + <li><b>right</b> - move only linedefs that have this + sector to their right.</li> + <li><b>left</b> - move only linedefs that have this + sector to their left.</li> + </ul> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="SHOW_FULL_LINEDEF_INFO"></a><b><tt>show_full_linedef_info</tt></b></td> + <td valign="top">If set to <b>yes</b> then in the <a + href="editing.htm#LINEDEF_DISPLAY">linedef edit display</a> + the linedef type will be shown as the full linedef name + as defined in the <a href="#LINEDEF_TYPES">config file</a>. + If set to <b>no</b> then linedef type will be displayed + as a hex value and it's class.<p>This has no effect when + editting HEXEN format maps.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>tag_highlight</tt></b></td> + <td valign="top">If set to <b>yes</b> then the tag + highlighting mode is enabled by default in sector and + linedef modes.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>vertex_radius</tt></b></td> + <td valign="top">Describes the size of the box around a + VERTEX which is used to select the VERTEX while editing. + Recommended values are any integer greater than four.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>width</tt></b><tt><br> + </tt><b><tt>height</tt></b></td> + <td valign="top">Describes the size of the display used + by viDOOM. viDOOM expects a resolution of at least + 640x480.</td> + </tr> +</table> + +<hr size="1"> + +<h3><a href="#CONFIG_FILE" name="viDOOM"></a>[viDOOM]</h3> + +<p>Main menu configuration</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><b><tt>auto_loadmap</tt></b></td> + <td valign="top">Set this to a list of levels to load on + startup. The format is to set it to a list of level names + seperated by commas. The first level that can be loaded + successfully is used. If this is set then <strong>initial_empty_map</strong> + is ignored.<p>If empty or not used then no map is auto + loaded, e.g.</p> + <p><tt># Load the first map, whether we're editing DOOM + or DOOM2<br> + auto_loadmap=MAP01,E1M1</tt></p> + <p><tt># Don't auto load any maps<br> + auto_loadmap=</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>initial_empty_map</tt></b></td> + <td valign="top">If set to <b>yes</b> then on startup + viDOOM will have an empty map, either MAP01 or E1M1, + ready for editing. This is ignored if <strong>auto_loadmap</strong> + is set.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>load_flats</tt></b></td> + <td valign="top">If set to <b>yes</b> then the graphics + for the flats will be loaded so they can be selected + graphically. If set to <b>no</b> then flats are selected + just using their name. <br> + This is provided as the graphics reading is not very + efficient and can be slow on certain machines. </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>load_textures</tt></b></td> + <td valign="top">If set to <b>yes</b> then the graphics + for the textures will be loaded so they can be selected + graphically. If set to <b>no</b> then textures are + selected just using their name. <br> + This is provided as the graphics reading is not very + efficient and can be slow on certain machines. </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>load_sprites</tt></b></td> + <td valign="top">If set to <b>yes</b> then the graphics + for the things will be loaded so they can be selected + graphically. If set to <b>no</b> then things are selected + just using their name. <br> + This is provided as the graphics reading is not very + efficient and can be slow on certain machines. </td> + </tr> + <tr> + <td valign="top" nowrap><a name="MAP_CLEAR"></a><b><tt>map_clear_warning</tt></b></td> + <td valign="top">If set to <b>yes</b> then a warning is + displayed whenever an option is chosen that will replace + the currently edited map with another, if the editor has + been used since the level's loading/creation. </td> + </tr> + <tr> + <td valign="top" nowrap><a name="MAP_EXIT"></a><b><tt>map_exit_warning</tt></b></td> + <td valign="top">If set to <b>yes</b> then a warning is + displayed if exit is chosen and the editor option has + been used. </td> + </tr> + <tr> + <td valign="top" nowrap><a name="OVERWRITE_WARNING"></a><b><tt>overwrite_warning</tt></b></td> + <td valign="top">If set to <b>yes</b> then a warning is + displayed if you save a map over a file that already + exists. </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>show_titlepic</tt></b></td> + <td valign="top">If set to <b>yes</b> then the title + picture for the version of DOOM you are editing will be + displayed on the main title screen. </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>sort_flat_names</tt></b></td> + <td valign="top">If set to <b>yes</b> then when selecting + ceiling or floor flats they will be sorted into + alphabetical order. If set to <b>no</b> they are simply + in the order they appear in the IWAD file. </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>sort_texture_names</tt></b></td> + <td valign="top">If set to <b>yes</b> then when selecting + wall textures they will be sorted into alphabetical + order. If set to <b>no</b> they are simply in the order + they appear in the IWAD file. </td> + </tr> +</table> + +<hr size="1"> + +<h3><a name="CHECK_LINEDEF"></a><b>[Check LINEDEF]</b></h3> + +<p>This defines how the <b>Check LINEDEF</b> function in the +editor (see <a href="editing.htm#LINEDEF_KEYS">editing</a> for +details) operates. Note that part of the configuration for this +command also occurs in the <a href="#LINEDEF_CHECK_DEFAULT">config +file</a>. This is required as texture names can be different in +different versions of DOOM. The following options are definable +within the INI file:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><b><tt>assume_yes</tt></b></td> + <td valign="top">If this is set to <b>yes</b> then any + time a question would be asked to see if it's OK to + remove a texture that viDOOM considers unnecessary then + it will be removed without asking. Likewise when asking + to add missing textures the texture picklist will appear + (with the linedef number and what is to be set in the + title) without any prompting.</td> + </tr> + <tr> + <td valign="top" nowrap><a name="CHECK_LINE_1SIDE_LOWER"></a><b><tt>check_1side_lower</tt></b></td> + <td valign="top">If set to <b>yes</b> then when a sidedef + is attached to a one-sided linedef and there is a lower + texture defined viDOOM will ask if it's OK to remove it.</td> + </tr> + <tr> + <td valign="top" nowrap><a name="CHECK_LINE_1SIDE_MIDDLE"></a><b><tt>check_1side_middle</tt></b></td> + <td valign="top">If set to <b>yes</b> then when a sidedef + is attached to a one-sided linedef and there is no middle + texture defined viDOOM will ask if it's OK to define it.</td> + </tr> + <tr> + <td valign="top" nowrap><a name="CHECK_LINE_1SIDE_UPPER"></a><b><tt>check_1side_upper</tt></b></td> + <td valign="top">If set to <b>yes</b> then when a sidedef + is attached to a one-sided linedef and there is an upper + texture defined viDOOM will ask if it's OK to remove it.</td> + </tr> + <tr> + <td valign="top" nowrap><a name="CHECK_LINE_2SIDE_LOWER"></a><b><tt>check_2side_lower</tt></b></td> + <td valign="top">If set to <b>yes</b> then when a sidedef + is attached to a two-sided linedef which is between two + sectors with different floor heights and there is no + lower texture defined viDOOM will ask if it's OK to + define it.</td> + </tr> + <tr> + <td valign="top" nowrap><a name="CHECK_LINE_2SIDE_MIDDLE"></a><b><tt>check_2side_middle</tt></b></td> + <td valign="top">If set to <b>yes</b> then when a sidedef + is attached to a two-sided linedef which is between two + sectors and there is a middle texture defined viDOOM will + ask if it's OK to remove it.</td> + </tr> + <tr> + <td valign="top" nowrap><a name="CHECK_LINE_2SIDE_UPPER"></a><b><tt>check_2side_upper</tt></b></td> + <td valign="top">If set to <b>yes</b> then when a sidedef + is attached to a two-sided linedef which is between two + sectors with different ceiling heights and there is no + upper texture defined viDOOM will ask if it's OK to + define it.</td> + </tr> + <tr> + <td valign="top" nowrap><a + name="CHECK_LINE_2SIDE_SAME_SECTOR"></a><b><tt>check_2side_same_sector</tt></b></td> + <td valign="top">If set to <b>yes</b> then when a sidedef + is attached to a two-sided linedef which is wholly within + one sector and there are any textures defined viDOOM will + ask if it's OK to remove them.</td> + </tr> +</table> + +<hr size="1"> + +<h3><a name="NODE_BUILDER"></a>[Node Builder]</h3> + +<p>Node Builder configuration - used to build nodes for maps +automatically when saving.</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><b><tt>always_view_output</tt></b></td> + <td valign="top">Normally viDOOM will only show the + output from the build command if it fails for some + reason. Setting this to <b>yes</b> means any output from + the node builder will be displayed regardless.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>command</tt></b></td> + <td valign="top">Defines the command used to execute the + node builder. e.g.<p><tt>command=c:\bsp\bsp.exe</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>ignore</tt></b></td> + <td valign="top">If set, then any saved filename that + includes this string will not be passed through the node + builder. This is useful so that structures for merging + into other maps (see <a href="editing.htm#MERGE_MAP">editing</a> + for details) need not be put through the node building + process. e.g. if set to<p><tt>ignore=str</tt></p> + <p>Then saving a map named <b>MAP01.WAD</b> will be + passed through the node builder, whereas <b>WALL_STRUCT.WAD</b> + would not be.</p> + <p>Another important use for this is to leave a back door + where you can save a WAD without it being passed through + the node builder if anything goes wrong with trying to + build the map.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>infile</tt></b></td> + <td valign="top">Defines the format for the infile + parameter to the node builder. Any occurrence of the '%' + character will be replaced with the full path of the map + being saved. e.g.<p><tt>infile=%</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>outfile</tt></b></td> + <td valign="top">Defines the format for the outfile + parameter to the node builder. Any occurrence of the '%' + character will be replaced with the full path of the map + being saved. e.g.<p><tt>outfile=-o %</tt></p> + <p>Note that if your node builder does not allow the + input and output file to be the same then it is + permissible to put extra characters <b>after</b> the % + that will get tacked onto the end of the name, e.g.</p> + <p><tt>outfile=-o %_NEW</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>in_before_out</tt></b></td> + <td valign="top">Defines the order of the the arguments + to the node builder. If set to <b>yes</b> then the node + builder is invoked as:<p><tt>command infile outfile</tt></p> + <p>If set to <b>no</b> then the command is built as:</p> + <p><tt>command outfile infile</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>use</tt></b></td> + <td valign="top">If set to <b>yes</b> then the node + builder described above is used on any maps being saved. + If set to <b>no</b> then the map is saved and the nodes + must be built outside of viDOOM.</td> + </tr> +</table> + +<hr size="1"> + +<h3><a name="ACC"></a>[ACS]</h3> + +<p>ACS compiler configuration - used to build BEHAVIOR lumps for +HEXEN format maps.</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><a name="ACS_ALWAYS_VIEW"></a><b><tt>always_view_output</tt></b></td> + <td valign="top">Normally viDOOM will only show the + output from the compiler if it fails for some reason. + Setting this to <b>yes</b> means any output from the + compiler will be displayed regardless.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>command</tt></b></td> + <td valign="top">Defines the command used to execute the + acs compiler. e.g.<p><tt>command=c:\doom\acc\acc.exe</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>dir</tt></b></td> + <td valign="top">Defines a working directory that will be + used for the compilation. This will generally be the same + place that the acs header files are stored and viDOOM + will change directory to this directory before + compilation. e.g.<p><tt>dir=c:\doom\acc\</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>script</tt></b></td> + <td valign="top">viDOOM will write an acs script with + this name, into the directory defined by <strong>dir</strong> + before compilation. e.g.<p><tt>script=vidoom.acs</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>object</tt></b></td> + <td valign="top">viDOOM will assume that the acs object + created by the compiler will have this name. e.g.<p><tt>script=vidoom.o</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>args</tt></b></td> + <td valign="top">Defines the argument list to the ACC + compiler. The string <strong>%S</strong> will be replaced + by <strong>script</strong>, defined above. The string <strong>%O</strong> + will be replaced by <strong>object</strong> defined + above. e.g.<p><tt>args=%S %O</tt></p> + </td> + </tr> +</table> + +<p> </p> + +<hr size="1"> + +<h3><a name="GUI"></a>[GUI]</h3> + +<p>GUI configuration. All the values in this section are an RGB +triplet defined as an hex number, i.e. <tt>0xRRGGBB</tt>. The +following values can be set from the INI file.</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><b><tt>gui_hi</tt></b></td> + <td valign="top">The brightest colour used to draw the 3D + looking interface.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>gui_mid</tt></b></td> + <td valign="top">The medium colour used to draw the 3D + looking interface.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>gui_lo</tt></b></td> + <td valign="top">The darkest colour used to draw the 3D + looking interface.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>gui_text</tt></b></td> + <td valign="top">The colour of text.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>gui_textshadow</tt></b></td> + <td valign="top">The colour of the shadow behind text. + This is only really used by viDOOM's own portable GUI + routines. If set to the same value as <b>text</b> then no + shadows are drawn.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>gui_bold</tt></b></td> + <td valign="top">The colour of bold text (used for + titles)</td> + </tr> +</table> + +<hr size="1"> + +<h3><a name="GAME_NAME"></a>[<i>Game name</i>]</h3> + +<p>One of these sections appears for each possible setting of the +game variable in the [Game] section. These are:</p> + +<ul> + <li><tt>[Doom]</tt></li> + <li><tt>[Ultimate Doom]</tt></li> + <li><tt>[Doom 2]</tt></li> + <li><tt>[TNT:Evilution]</tt></li> + <li><tt>[Plutonia Experiment]</tt></li> + <li><tt>[ZDoom]</tt></li> +</ul> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><b><tt>iwad</tt></b></td> + <td valign="top">Defines the path to the IWAD for this + game. </td> + </tr> + <tr> + <td valign="top" nowrap><a name="PWAD_DIR"></a><b><tt>pwad_dir</tt></b></td> + <td valign="top">Defines the default load/save directory + for editing PWAD files. </td> + </tr> + <tr> + <td valign="top" nowrap><a name="LEVEL_STYLE"></a><b><tt>level_style</tt></b></td> + <td valign="top">Defines the level naming convention for + the game. Allowable values are: <ul> + <li><b>Doom</b> - allows level names E1M1 to E3M9</li> + <li><b>Ultimate Doom</b> - allows level names E1M1 to + E4M9</li> + <li><b>Doom 2</b> - allows level names MAP01 to MAP32</li> + </ul> + </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>preload</tt></b></td> + <td valign="top">Lists a number of PWAD files to preload + on startup. PWAD files are separated by ; and the full + path is expected. </td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>vidoom_config</tt></b></td> + <td valign="top">Defines the <a href="#CONFIG_FILE">config + file</a> for this version of DOOM. This defines the + values used for defining things, linedefs, sectors and so + on. <br> + viDOOM is currently supplied with <b>doom.cfg</b>, <b>doom2.cfg</b> + and <b>zdoom.cfg</b>. </td> + </tr> + <tr> + <td valign="top" nowrap><a name="MAPINFO_LUMP"></a><b><tt>mapinfo_lump</tt></b></td> + <td valign="top">This only applies to the section for <b>[ZDoom] + </b>.<p>If this is set to <i>yes</i> then on loading a + map from the loaded WAD files, the directory pointed to + by <a href="#PWAD_DIR">pwad_dir</a> is searched for <b>MAPINFO.WAD</b>. + If found then the MAPINFO lump from the MAPINFO.WAD is + read in and can be edited within the editor. If + WADINFO.WAD does not exist then an empty MAPINFO lump is + created.</p> + <p><b>NOTE:</b> On saving the level if there is anything + in the mapinfo entered in the editor the MAPINFO.WAD file + will be overwritten (or created if it does not exist). + This means that any infomation (apart from the edited + MAPINFO lump) that was in this WAD file will be lost. It + is suggested that nothing goes into MAPINFO.WAD except + for the MAPINFO lump.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="MAPINFO_LUMP"></a><b><tt>create_hexen</tt></b></td> + <td valign="top">This only applies to the section for <b>[ZDoom] + </b>.<p>If this is set to <i>yes</i> then on creating a + new map, it will be marked as being in the HEXEN format + (ie. having a BEHAVIOR lump and the HEXEN format of + THINGS and LINEDEFS). If set to <em>no</em> then the map + will be created as a DOOM map.</p> + <p>Setting to <em>ask</em> means that you will be asked + prior to creation which sort of map you want.</p> + </td> + </tr> +</table> + +<hr> + +<h2><a name="CONFIG_FILE"></a>Config file</h2> + +<p>There is two config files supplied with viDOOM, <b>doom.cfg</b> +and <b>doom2.cfg</b>. Each file is comprised of sections followed +by the data expected in each section. Each section is defined by +a line like:</p> + +<p><tt>%SECTION_NAME</tt></p> + +<p>While the data lines are on individual lines with the pipe (|) +character used to separate fields, e.g.</p> + +<p><tt>Field 1|Field 2</tt></p> + +<p>Blank lines and lines starting with a comment character (#) +are ignored. In addition to this blocks of lines can be forced to +be ignored by enclosing them with directives introduced with the +@ charcater. See the <a href="#CONFIG_DIRECTIVES">following +section</a> for more details.</p> + +<p>Note that some sections allow an edit mode to be defined, to +indicate whether this definition is for DOOM or HEXEN edit mode +(or both). The currently allowed edit modes are:</p> + +<ul> + <li>Doom</li> + <li>Hexen</li> +</ul> + +<p>If what is being defined applies to more than one mode, these +can be seperated with commas.</p> + +<p>The following sections are defined:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><b><tt>%INCLUDE_FILES</tt></b></td> + <td valign="top">Defines any other config files to + include before processing this one. Simply type the + filenames to be included on individual lines.</td> + </tr> + <tr> + <td valign="top" nowrap><a name="THING_CLASSES"></a><b><tt>%THING_CLASSES</tt></b></td> + <td valign="top">Defines the classes to group THINGs into + in the editor picklists. Each line is in the form + "Class Name|Colour". Note that colour is + defined as an hexadecimal number with the most + significant byte for RED, the middle byte for BLUE and + the least significant byte for GREEN. e.g.<p><tt>Monster|0xff0000</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="LINEDEF_CLASSES"></a><b><tt>%LINEDEF_CLASSES</tt></b></td> + <td valign="top">Defines the classes to group LINEDEF + types into in the editor picklists. Each line is a class + name.</td> + </tr> + <tr> + <td valign="top" nowrap><a name="SECTOR_CLASSES"></a><b><tt>%SECTOR_CLASSES</tt></b></td> + <td valign="top">Defines the classes to group SECTOR + types into in the editor picklists. Each line is in the + form "Edit mode|Class Name". e.g.<p><tt>Doom,Hexen|Lighting</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="THING_TYPES"></a><b><tt>%THING_TYPES</tt></b></td> + <td valign="top">Defines the names and IDs of the THINGs + supported by DOOM. Each line is in the form + "Class|Name|ID|Radius|Sprite Name". e.g.<p><tt>Monster|Former + Human|3004|20|POSSA1</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="THING_FLAGS"></a><b><tt>%THING_FLAGS</tt></b></td> + <td valign="top">Defines the flags used when setting a + THINGs flags. Each line is in the form "Edit + Mode|Group|Mask1|Mask2|Name|Single flag character". + e.g.<p><tt>Doom,Hexen|0|0x01|0x01|Skill 1 and 2|E</tt></p> + <p>The group number indicates masks that should be + mutually exclusive. Group zero means that the flag can be + toggled independently of any other. Other group numbers + mean that only one of that group should be set at a time.</p> + <p>The ways the masks work is:</p> + <blockquote> + <p>To see if a flag is set the flags are ANDed with + mask1. If the result equals mask2 then the flag is + set.</p> + <p>When defining flags all the mask1 values are ORed + together and those bits cleared from the flags. Then + for any option that has been set mask2 is ORed onto + the flags.</p> + </blockquote> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="LINEDEF_TYPES"></a><b><tt>%LINEDEF_TYPES</tt></b></td> + <td valign="top">Defines the names and IDs of the LINEDEF + types supported by DOOM. Each line is in the form + "Class|ID|Name", e.g.<p><tt>Special|48|Scrolling + wall </tt></p> + <p><b>Note:</b> Please note that the text for the LINEDEF + types in the supplied config files is taken directly from + the Unofficial Doom Specs (see the <a + href="thanks.htm#DOOMSPEC">thanks</a> page for details). + Please feel free to change them to something more useful + if you prefer.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="LINEDEF_FLAGS"></a><b><tt>%LINEDEF_FLAGS</tt></b></td> + <td valign="top">Defines the flags used when setting a + LINEDEFs flags. The format for this section is identical + to <a href="#THING_FLAGS">THING_FLAGS</a>.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>%LINEDEF_FLAGS_EXTRA</tt></b></td> + <td valign="top">Configuration so that viDOOM knows which + bits control certain functions. If more that one line + appears in this section then the last one is used. The + form of the line is "Bit number controlling + 2-sided|Bit controlling Impassible|Bit controlling lower + unpegged|Bit controlling upper unpegged". e.g.<p><tt>2|0|3|4</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="LINEDEF_DEFAULTS"></a><b><tt>%LINEDEF_DEFAULTS</tt></b></td> + <td valign="top">This section must not appear before + %LINEDEF_FLAGS_EXTRA and defines the bit patterns for + various linedef styles. These are used in the editor so + that defining a type of LINEDEF can be quickly done when + creating them. The format of each line is + "Name|Flags value", e.g.<p><tt>2-sided + wall|0x47</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="SECTOR_TYPES"></a><b><tt>%SECTOR_TYPES</tt></b></td> + <td valign="top">Defines the names and IDs for the + different sector types. Each line is in the form + "Edit Mode|Class|ID|Long name|Short name". e.g.<p><tt>Doom|Lights|1|Light + random off|Random off</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="SECTOR_STYLES"></a><b><tt>%SECTOR_STYLES</tt></b></td> + <td valign="top">Defines styles for painting sectors + quickly. The form of each line is + "Mode|Name|Upper|Middle|Lower|Floor|Ceiling". + e.g.<p><tt>0x19|Quarry|SP_ROCK1|SP_ROCK1|SP_ROCK1|RROCK09|F_SKY1</tt></p> + <p>The mode is a bit significant number where the bits + have the following meaning:</p> + <ul> + <li>Bit 0 - Paint textures on the sidedefs facing + into this sector</li> + <li>Bit 1 - Paint textures on the sidedefs facing out + of this sector</li> + <li>Bit 2 - Leave current lower/upper settings</li> + <li>Bit 3 - Set upper unpegged if an upper texture is + painted</li> + <li>Bit 4 - Set lower unpegged if a lower texture is + painted</li> + </ul> + <p>If neither bits 2, 3 or 4 are set it is assumed that + lower/upper unpegged will be cleared on painting those + textures.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="EMPTY_TEXTURE_NAME"></a><b><tt>%EMPTY_TEXTURE_NAME</tt></b></td> + <td valign="top">Simply defines the empty texture name + used by DOOM. This will generally just be the - + character, e.g.<p><tt>%EMPTY_TEXTURE_NAME<br> + -</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="NORMAL_TYPES"></a><b><tt>%NORMAL_TYPES</tt></b></td> + <td valign="top">Defines the values that define the + normal linedefs and sectors. The form of the single line + of data is "Id for normal linedef|Id for normal + sector". In all current versions of Doom this is + zero, e.g.<p><tt>%NORMAL_TYPES<br> + 0|0</tt></p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="LINEDEF_CHECK_DEFAULT"></a><b><tt>%LINEDEF_CHECK_DEFAULT</tt></b></td> + <td valign="top">Provides an optional default texture to + use when a texture is requested when checking linedefs. + If this value is defined to be the same as + EMPTY_TEXTURE_NAME then the user is prompted for a + texture to use, otherwise this texture is used instead, + e.g.<p><tt>%LINEDEF_CHECK_DEFAULT<br> + ASHWALL</tt></p> + <p>See <a href="editing.htm#LINEDEF_KEYS">editing</a> for + details on the check linedef operation.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="LINEDEF_GEN_BITMASKS"></a><b><tt>%LINEDEF_GEN_BITMASKS</tt></b></td> + <td valign="top">This defines the bitmasks used to + represent generalised LINEDEF types (these were + introduced by <a href="thanks.htm#BOOM">BOOM</a>).<p>This + section is a bit more complex than most of the others as + the format of the data is not fixed from line to line. + Each set starts with the class name for the bitmask. + Format for this line is:</p> + <p><tt>class name|no of fields</tt></p> + <p>After this follows <em>no of fields</em> repetitions + of the following:</p> + <p><tt>field name|shorthand name|value</tt></p> + <p>e.g.</p> + <p><tt>%LINEDEF_GEN_BITMASKS<br> + Speed|4<br> + Slow|Slow|0<br> + Normal|Norm|1<br> + Fast|Fast|2<br> + Turbo|Turb|3</tt></p> + <p>Remember that value will be shifted left a specified + amount when used in the <a href="#LINEDEF_GEN_TYPES">LINEDEF_GEN_TYPES</a> + section and so they should be defined relative to bit 0. + Also note that inside the editor all the values + associated with a bitmask are ORed together to create a + mask that can extract that information from a generalised + linedef. Following all this can follow another class, and + so on. Also remember that blank lines and comments are + OK, so that sections can be readably split using white + space.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="LINEDEF_GEN_TYPES"></a><b><tt>%LINEDEF_GEN_TYPES</tt></b></td> + <td valign="top">This defines the groupings of + LINEDEF_GEN_BITMASKS to make up different generalised + LINEDEF types.<p>This section is a bit more complex than + most of the others as the format of<br> + the data is not fixed from line to line. Each set starts + with the class name describing the name of this type of + generalised linedef, the number of different bit patterns + defined in the class, the low and high values that the + type occupies and a mask used when generating the value. + Format for this line is:</p> + <p><tt>class name|low value|high value|mask|no of + bit-field classes</tt></p> + <p>After this follows <em>no of bit-fields </em>repetitions + of the following:</p> + <p><tt>bit-field name|shift</tt></p> + <p>e.g.</p> + <p><tt>Locked Door|0x3800|0x3bff|0x3800|5<br> + Trigger|0<br> + Speed|3<br> + Locked door kind|5<br> + Opens with|6<br> + Number of keys|9</tt></p> + <p>The shift defines how much the bitmask is shifted to + the left to generate the actual values stored in the + linedef type field. Note that along with these bitmask + values the mask field is also ORed onto the result to + create the entire linedef type value. Following all this + can follow another class, and so on. Also remember that + blank lines and comments are OK, so that sections can be + readably split using white space.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><a name="SECTOR_GEN_BITMASKS"></a><b><tt>%SECTOR_GEN_BITMASKS</tt></b></td> + <td valign="top">This works in exactly the same way as <a + href="#LINEDEF_GEN_BITMASKS">LINEDEF_GEN_BITMASKS</a>, + but is used to define the generalised bitmasks used by a + SECTOR.</td> + </tr> + <tr> + <td valign="top" nowrap><a name="SECTOR_GEN_BITMASKS"></a><b><tt>%SECTOR_GEN_TYPES</tt></b></td> + <td valign="top">This works in exactly the same way as <a + href="#LINEDEF_GEN_TYPES">SECTOR_GEN_BITMASKS</a>, but is + used to define the generalised types used by a SECTOR.</td> + </tr> + <tr> + <td valign="top" nowrap><a + name="HEXEN_ACTION_SPECIAL_CLASSES"></a><b><tt>%HEXEN_ACTION_SPECIAL_CLASSES</tt></b></td> + <td valign="top">Defines the classes to group action + special types into in the editor picklists. Each line is + a class name.</td> + </tr> + <tr> + <td valign="top" nowrap><a name="HEXEN_ACTION_SPECIALS"></a><b><tt>%HEXEN_ACTION_SPECIALS</tt></b></td> + <td valign="top">Defines the HEXEN action specials. Each + line is of the form "Class + Name|Id|Name|arg0,arg1,arg2,arg3,arg4". Note that + not all the argument names need be filled in. e.g.<p><tt>Normal|0|No + action|<br> + Polyobject|1|Polyobj_StartLine|po,mirror,sound<br> + Lighting|116|Light_Strobe|tag,upper,lower,u-tics,l-tics</tt></p> + <p>Any arg name that is "tag" is used to work + out what sector the special is referring to. This is so + that tag highlighting (see <a href="editing.htm">editing</a> + for details) still works in HEXEN mode.</p> + <p>Note that unfortunately this does not apply to THINGs + yet.</p> + </td> + </tr> +</table> + +<hr size="1"> + +<h3><a name="CONFIG_DIRECTIVES"></a>Config file directives</h3> + +<p>In the config files blocks of lines can be included or +excluded by surrounding them by enclosing them with directives +introduced with the @ charcater, e.g.:</p> + +<p><tt>@DOOM_LEVEL_STYLE<br> +%SECTOR_STYLES<br> +<data><br> +@END DOOM_LEVEL_STYLE</tt></p> + +<p>As you can see a directive is terminated by putting the same +directive, beginning with END.</p> + +<p>The main purporse of these is to stop SECTOR_STYLES and +LINEDEF_CHECK_DEFAULT being defined inappropriately (Doom and +Doom 2 have differing texture names, etc), and also to allow one +single ZDOOM configuration file to be defined, regardless of +whether you are using the Doom or Doom 2 IWAD files with it.</p> + +<p>The following directives are currently supported:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><b><tt>@DOOM_LEVEL_STYLE</tt></b></td> + <td valign="top">Includes the lines up to the next <strong><tt>@END + DOOM_LEVEL_STYLE</tt></strong> if the <a + href="#LEVEL_STYLE">level_style</a> for this game is + defined as either <strong>Doom</strong> or <strong>Ultimate + Doom</strong>.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>@DOOM_1_LEVEL_STYLE</tt></b></td> + <td valign="top">Includes the lines up to the next <strong><tt>@END + DOOM_1_LEVEL_STYLE</tt></strong> if the <a + href="#LEVEL_STYLE">level_style</a> for this game is + defined as <strong>Doom</strong>.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>@ULT_DOOM_LEVEL_STYLE</tt></b></td> + <td valign="top">Includes the lines up to the next <strong><tt>@END + ULT_DOOM_LEVEL_STYLE</tt></strong> if the <a + href="#LEVEL_STYLE">level_style</a> for this game is + defined as <strong>Ultimate Doom</strong>.</td> + </tr> + <tr> + <td valign="top" nowrap><b><tt>@DOOM_2_LEVEL_STYLE</tt></b></td> + <td valign="top">Includes the lines up to the next <strong><tt>@END + DOOM_2_LEVEL_STYLE</tt></strong> if the <a + href="#LEVEL_STYLE">level_style</a> for this game is + defined as <strong>Doom 2</strong>.</td> + </tr> +</table> + +<hr> + +<p><a href="index.htm">Back to index</a></p> + +<p><tt>$Id$</tt></p> +</body> +</html> diff --git a/doc/porting.htm b/doc/porting.htm new file mode 100644 index 0000000..fded757 --- /dev/null +++ b/doc/porting.htm @@ -0,0 +1,1452 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<h1 align="center">Porting viDOOM</h1> + +<p>This document is provided in case anyone wishes to port viDOOM +to a new platform. Below is an indication of what OS dependent +routines must be provided and what configuration needs to be +done. It is requested that any ports are also released as Free +Software.</p> + +<ul> + <li><a href="#MAKEFILE">Makefile configuration</a></li> + <li><a href="#INI">INI File</a></li> + <li><a href="#FILES">Files</a></li> + <li><a href="#MAIN">Main entry point</a></li> + <li><a href="#GFX_H">Graphics and input</a></li> + <li><a href="#PLATGUI_H">Platform GUI</a></li> + <li><a href="#FILE_H">File interface</a></li> + <li><a href="#MEM_H">Memory allocation</a></li> + <li><a href="#RUNCMD_H">External command execution</a></li> + <li><a href="#VSTRING_H">String functions</a></li> + <li><a href="#INSTALLATION">Installation script</a></li> + <li><a href="#DOCUMENTATION">Documentation</a></li> +</ul> + +<hr> + +<h2><a name="MAKEFILE"></a>Makefile configuration</h2> + +<p><strong><u>makefile</u></strong></p> + +<p><a name="MAKEPLAT"></a>Set the variable <strong>MAKEPLAT</strong> +to the name of your platform, eg.</p> + +<p><tt>MAKEPLAT=</tt><em><tt>OS</tt></em></p> + +<p>You will then need to create a matching file the <strong>make</strong> +sub directory called <em>OS</em>.cfg. Also all the OS dependent C +source should go into a sub directory defined in the following +make config file by the <strong>PLATFORM</strong> variable. See +the <a href="#FILES">Files</a> section for an overview of these +files.</p> + +<p><a name="MAKE_CONFIG"></a><strong><u>make config file</u></strong></p> + +<p>The following values need to be set in the file <strong>make</strong><em><strong>/OS</strong></em><strong>.cfg</strong>:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong><tt>CC</tt></strong></td> + <td valign="top">Set to the name of the C compiler.</td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>LD</tt></strong></td> + <td valign="top">The name of the linker.</td> + </tr> + <tr> + <td valign="top" nowrap><a name="PLATFORMVAR"></a><strong><tt>PLATFORM</tt></strong></td> + <td valign="top">The name of the D containing the OS + dependent C sources. The idea of defineing tdefining + here, rather than using the MAKEPLAT variable defined in + the top level makefile, is so that different + configurations can share sources. i.e. An X11 port may + use the same code for all the unix platforms, but each + machine may require slightly different configuration + (library search paths for example).</td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>EXE_EXT</tt></strong></td> + <td valign="top">An extension to add to the executable + name, e.g.<br> + <tt>EXE_EXT=.EXE</tt></td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>OBJ_EXT</tt></strong></td> + <td valign="top">The extension used in this OS to denote + object files produced by the C compiler. Generally:<br> + <tt>OBJ_EXT=.o</tt></td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>LIBS</tt></strong></td> + <td valign="top">Any extra libraries to link in with + viDOOM for the OS dependent routines.</td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>EXRACF</tt></strong></td> + <td valign="top">Any extra C flags required when + compiling the sources. Use it to enable optimisation and + to include any extra include paths required by this OS.</td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>EXTRALF</tt></strong></td> + <td valign="top">Any extra flags required when linking + viDOOM. Use it to enable include any extra library paths + required by this OS.</td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>DIRSEP</tt></strong></td> + <td valign="top">The directory separator character for + this OS. The character must be included in quotes, e.g.<font + face="Courier New"><tt><br> + </tt></font><tt>DIRSEP="/"</tt></td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>MATHLIB</tt></strong></td> + <td valign="top">The options required to include the + maths library when linking.</td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>TRACEFORM</tt></strong></td> + <td valign="top">This variable is a printf format string + and the arguments to the format. This string is used to + provide a tracing function used to track bugs in the + editor. A simple, portable example is:<br> + <tt>TRACEFORM="%s:%d",__FILE__,__LINE__</tt><p>It + can just be defined to an empty string if you are not + compiling the debug version.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>EXEFLAG</tt></strong></td> + <td valign="top">The flag to provide to the linker to + generate an executable. The flag is used in a rule + something like this:<br> + <tt>$(LD) $(EXTRALF) $(EXEFLAG) vidoom$(EXE_EXT) + $(ALL+VIDOOM_OBJECTS)</tt></td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>OBJFLAG</tt></strong></td> + <td valign="top">The flag to provide to the C compiler to + generate an object file from the supplied C source. The + flag is used in a rule something like this:<br> + <tt>$(CC) $(EXTRACF) $(OBJFLAG) file.c</tt></td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>DEFINEFLAG</tt></strong></td> + <td valign="top">The flag to provide to the C compiler + with pre-processor definition from the command line. The + flag is used in a rule something like this (note no space + after the DEFINEFLAG - if there is a space between the + switch and the argument put it in this variable + definition):<br> + <tt>$(CC) $(EXTRACF) $(OBJFLAG) $(DEFINEFLAG)MACRO=value + file.c</tt></td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>INCFLAG</tt></strong></td> + <td valign="top">The flag to provide to the C compiler + with extra directories in which the pre-processor + searches for include files. The flag is used in a rule + something like this (note no space after the INCFLAG - if + there is a space between the switch and the argument put + it in this variable definition):<br> + <tt>$(CC) $(EXTRACF) $(OBJFLAG) $(INCFLAG)include_dir + file.c</tt></td> + </tr> + <tr> + <td valign="top" nowrap><strong><tt>MAKEINSTALL</tt></strong></td> + <td valign="top">The command used to execute the <strong>install</strong> + makefile as described in the <a href="#INSTALLATION">installation + script </a>section. The command must define the + INSTALLDIR variable for the makefile and invoke the first + rule in the install makefile.<p>For instance, using a + normal unix/GCC type make command, this would be:<br> + <tt>make INSTALL_DIR='$(INSTALL_DIR)' -f install</tt></p> + </td> + </tr> +</table> + +<hr> + +<h2><a name="INI"></a>INI File</h2> + +<p>If your port requires or wants configuration to be set at +tun-time from the INI file, it is best to place it a +system-dependent section called the same as the <em>OS</em> value +you set <a href="#MAKEPLAT">MAKEPLAT</a> to for this platform, +e.g.</p> + +<p><tt>[</tt><em><tt>OS</tt></em><tt>]<br> +degreelessness mode=off</tt></p> + +<hr> + +<h2><a name="FILES"></a>Files</h2> + +<p>The following files are the minimum that must be provided by +the platform:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong>make/</strong><em><strong>OS</strong></em><strong>.cfg</strong></td> + <td valign="top">The make config file. Described in the <a + href="#MAKE_CONFIG">previous section</a>.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>main.c</strong></td> + <td valign="top">This goes in the <a href="#PLATFORMVAR">platform + directory</a> and provides the <a href="#MAIN">startup + code</a> for the operating system.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>gfx.c</strong></td> + <td valign="top">This goes in the <a href="#PLATFORMVAR">platform + directory</a> and provides the <a href="#GFX_H">low level + graphics and input</a>.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>platgui.c</strong></td> + <td valign="top">This goes in the <a href="#PLATFORMVAR">platform + directory</a> and provides the <a href="#PLATGUI_H">system + dependent GUI</a>.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>file.c</strong></td> + <td valign="top">This goes in the <a href="#PLATFORMVAR">platform + directory</a> and provides the portable part of the <a + href="#FILE_H">file system interface</a>.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>mem.c</strong></td> + <td valign="top">This goes in the <a href="#PLATFORMVAR">platform + directory</a> and provides the <a href="#MEM_H">memory + handling</a>.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>runcmd.c</strong></td> + <td valign="top">This goes in the <a href="#PLATFORMVAR">platform + directory</a> and provides the method for <a + href="#RUNCMD_H">running external commands</a>.</td> + </tr> + <tr> + <td><strong>vstring.c</strong></td> + <td>This goes in the <a href="#PLATFORMVAR">platform + directory</a> and provides functions for <a + href="#VSTRING_H">string comparisons</a>.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>install</strong></td> + <td valign="top">This goes in the <a href="#PLATFORMVAR">platform + directory</a> and is the makefile invoked to <a + href="#INSTALLATION">install viDOOM</a>.</td> + </tr> +</table> + +<hr> + +<h2><a name="MAIN"></a>Main entry point</h2> + +<p><strong><u>main.c</u></strong></p> + +<p>The OS dependent code must provide it's own main (this is to +allow for various non-standard environments where main() is not +the standard entry point). The entry point must do any OS +dependent initialisations then invoke the following entry point +to start up viDOOM:</p> + +<p><tt>int viDOOM(int argv, char *argv[])</tt></p> + +<p>If the OS uses main() as an entry point the following example +could be enough:</p> + +<pre>int main(int argc,char *argv[]) +{ + return (viDOOM(argc,argv)); +}</pre> + +<hr> + +<h2><a name="GFX_H"></a>Graphics and input</h2> + +<p><strong><u>gfx.c</u></strong></p> + +<p>This provides the low-level graphics access and interfaces to +keyboard and mouse. The GFX object is expected to work on a weak, +semi-event driven basis for keyboard/mouse access. The following +are the basic assumptions about the GFX interface:</p> + +<ul> + <li>A true, or hicolor, display.</li> + <li>A Fixed font.</li> + <li>Default origin is in the top left of the display, with X + positive along and Y positive down.</li> + <li>A buffered display. The screen contents should not change + until GFX_redraw() is called. If not honoured viDOOM + should still work up to a point, but it's use of the XOR + mode may not be apparent to the user.</li> + <li>Colours are represented using an int, with 8 bits each + for the red, green and blue component. The int used to + define the colour, when viewed in hex, would look like <tt>0xRRGGBB</tt>. + e.g.<ul type="disc"> + <li><tt>0xFF0000</tt> - Red</li> + <li><tt>0x00FF00</tt> - Green</li> + <li type="disc"><tt>0x0000FF</tt> - Blue</li> + <li><tt>0xFFFFFF</tt> - White</li> + <li><tt>0x808080</tt> - 50% grey</li> + <li><tt>0x000000</tt> - Black</li> + </ul> + </li> +</ul> + +<p>The following types are defined and used by the GFX object :</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><pre>typedef void *GFX_IMAGE; </pre> + </td> + <td valign="top">This is an opaque type provided to allow + the GFX object to provide whatever is required to + reference a bitmap on the machine.</td> + </tr> + <tr> + <td valign="top" nowrap><pre>typedef struct + { + int w; + int h; + int pal[256]; + unsigned char *data; + } GFX_BITMAP; </pre> + </td> + <td valign="top">This type represents the bitmap objects + that viDOOM defines. These bitmaps are converted into + GFX_IMAGE prior to use. The fields are: <table border="0" + cellpadding="3" cellspacing="6"> + <tr> + <td valign="top" nowrap>w</td> + <td valign="top" nowrap>-</td> + <td valign="top">width of bitmap</td> + </tr> + <tr> + <td valign="top" nowrap>h</td> + <td valign="top" nowrap>-</td> + <td valign="top">height of bitmap</td> + </tr> + <tr> + <td valign="top" nowrap>pal[256]</td> + <td valign="top" nowrap>-</td> + <td valign="top">The palette used to define the + bitmap. Each bitmap pixel is an index into this + array of RGB values. Each entry is an integer, + that when represented in hex would define the RGB + triplet as 0xRRGGBB.</td> + </tr> + <tr> + <td valign="top" nowrap>*data</td> + <td valign="top" nowrap>-</td> + <td valign="top">A pointer to the data of the + bitmap. This should be accessed using pointer + arithmetic as *(data+(x)+(y*w))</td> + </tr> + </table> + </td> + </tr> + <tr> + <td valign="top" nowrap><pre>typedef struct + { + int type; + int shift; + int ctrl; + int alt; + char ascii; + int code; + } GFXKey;</pre> + </td> + <td valign="top">This defines an object for reporting key + presses. The fields are:<table border="0" cellpadding="3" + cellspacing="6"> + <tr> + <td valign="top" nowrap>type</td> + <td valign="top" nowrap>-</td> + <td valign="top">The type of event. This field is + just used to line up with the event union defined + later on. Should just hold GFX_KEY_EVENT.</td> + </tr> + <tr> + <td valign="top" nowrap>shift</td> + <td valign="top" nowrap>-</td> + <td valign="top">TRUE if the Shift key is being + pressed.</td> + </tr> + <tr> + <td valign="top" nowrap>ctrl</td> + <td valign="top" nowrap>-</td> + <td valign="top">TRUE if the Control key is being + pressed.</td> + </tr> + <tr> + <td valign="top" nowrap>alt</td> + <td valign="top" nowrap>-</td> + <td valign="top">TRUE if the Alt key is being + pressed.</td> + </tr> + <tr> + <td valign="top" nowrap>ascii</td> + <td valign="top" nowrap>-</td> + <td valign="top">The ASCII code of the character + read. If the key is not an ASCII key (e.g. a + function key) this field should be zero.</td> + </tr> + <tr> + <td valign="top" nowrap>code</td> + <td valign="top" nowrap>-</td> + <td valign="top">Holds the code for non-ASCII + keys, e.g. GFX_F1. This field should be set to + GFX_ASCII for key presses reported through the + ascii field.</td> + </tr> + </table> + </td> + </tr> + <tr> + <td valign="top" nowrap><pre>typedef struct GFXMouse + { + int type; + int shift; + int ctrl; + int alt; + int x; + int y; + int b; + } GFXMouse;</pre> + </td> + <td valign="top">This defines the type for reporting + mouse movements and button presses. The fields are:<table + border="0" cellpadding="3" cellspacing="6"> + <tr> + <td valign="top" nowrap>type</td> + <td valign="top" nowrap>-</td> + <td valign="top">The type of event. This field is + just used to line up with the event union defined + later on. Should just hold GFX_MOUSE_EVENT.</td> + </tr> + <tr> + <td valign="top" nowrap>shift</td> + <td valign="top" nowrap>-</td> + <td valign="top">TRUE if the Shift key is being + pressed.</td> + </tr> + <tr> + <td valign="top" nowrap>ctrl</td> + <td valign="top" nowrap>-</td> + <td valign="top">TRUE if the Control key is being + pressed.</td> + </tr> + <tr> + <td valign="top" nowrap>alt</td> + <td valign="top" nowrap>-</td> + <td valign="top">TRUE if the Alt key is being + pressed.</td> + </tr> + <tr> + <td valign="top" nowrap>x</td> + <td valign="top" nowrap>-</td> + <td valign="top">The X co-ordinate of the mouse, + relative to the top left of the display.</td> + </tr> + <tr> + <td valign="top" nowrap>y</td> + <td valign="top" nowrap>-</td> + <td valign="top">The Y co-ordinate of the mouse, + relative to the top left of the display.</td> + </tr> + <tr> + <td valign="top" nowrap>b</td> + <td valign="top" nowrap>-</td> + <td valign="top">The currently pressed buttons. + This should be made up of a bit mask composed + from GFX_BUTLEFT, GFX_BUTMIDDLE and GFX_BUTRIGHT.</td> + </tr> + </table> + </td> + </tr> + <tr> + <td valign="top" nowrap><pre>typedef union GFXEvent + { + int type; + GFXKey key; + GFXMouse mouse; + } GFXEvent;</pre> + </td> + <td valign="top">This defines the type for reporting + events (a combination of both mouse movements or key + presses). The fields are:<table border="0" + cellpadding="3" cellspacing="6"> + <tr> + <td valign="top" nowrap>type</td> + <td valign="top" nowrap>-</td> + <td valign="top">The type of event. This field is + just used to decide which of the other two fields + should be accessed to get the event information. + This field must be GFX_MOUSE_EVENT or + GFX_KEY_EVENT.</td> + </tr> + <tr> + <td valign="top" nowrap>key</td> + <td valign="top" nowrap>-</td> + <td valign="top">The GFXKey structure defining + the event if this is a GFX_KEY_EVENT.</td> + </tr> + <tr> + <td valign="top" nowrap>mouse</td> + <td valign="top" nowrap>-</td> + <td valign="top">The GFXMouse structure defining + the event if this is a GFX_MOUSE_EVENT.</td> + </tr> + </table> + </td> + </tr> +</table> + +<p>The following interfaces must be supplied by the GFX object:</p> + +<p><strong><tt>void GFX_init(void)</tt></strong></p> + +<blockquote> + <p>Initialises the GFX object. No other GFX interfaces are + called prior to this, with the possible (though current not + used) exception of <strong><tt>GFX_exit()</tt></strong>.</p> +</blockquote> + +<p><strong><tt>void GFX_close(void)</tt></strong></p> + +<blockquote> + <p>Called when viDOOM is terminating. Note that other (none + system dependent) processing may go on between calling this + and then invoking <strong><tt>exit()</tt></strong> or <strong><tt>return()</tt></strong>.</p> +</blockquote> + +<p><strong><tt>GFX_IMAGE GFX_create_image(GFX_BITMAP *bm)</tt></strong></p> + +<blockquote> + <p>Should create a GFX_IMAGE from the passed bitmap <em>bm</em>.</p> +</blockquote> + +<p><strong><tt>void GFX_destroy_image(GFX_IMAGE img)</tt></strong></p> + +<blockquote> + <p>Release the bitmap object pointed to by <em>img</em>.</p> +</blockquote> + +<p><strong><tt>void GFX_draw_image(GFX_IMAGE img, int x, int y)</tt></strong></p> + +<blockquote> + <p>Draws <em>img</em> with it's top-left co-ordinate + represented by <em>x,y</em>. This function should implement + any necessary clipping when drawing the bitmap.</p> +</blockquote> + +<p><strong><tt>void GFX_fill_screen(GFX_IMAGE img)</tt></strong></p> + +<blockquote> + <p>Should fill the screen with the image, scaled if + necessary. Note that this call is just used for the main menu + backdrop, so if it cannot be honoured no harm will be done.</p> +</blockquote> + +<p><a name="GFX_OPEN"></a><strong><tt>void GFX_open(int width, +int height)</tt></strong></p> + +<blockquote> + <p>Opens the display (or window or whatever) with the + specified <em>width</em> and <em>height</em>. Note that + failures in here should terminate the program.</p> +</blockquote> + +<p><strong><tt>void GFX_clear(int col)</tt></strong></p> + +<blockquote> + <p>This clears the display to the passed colour <em>col</em>.</p> +</blockquote> + +<p><strong><tt>void GFX_redraw(void)</tt></strong></p> + +<blockquote> + <p>This redraws the contents of the screen. All drawing + operations should not update the actual screen till this is + called (i.e. the display should be buffered).</p> +</blockquote> + +<p><strong><tt>void GFX_line(int x1, int y1, int x2, int y2, int +col)</tt></strong></p> + +<blockquote> + <p>Draw a line from <em>x1,y1</em> to <em>x2,y2</em> in + colour <em>col</em>.</p> +</blockquote> + +<p><strong><tt>void GFX_plot(int x, int y, int col)</tt></strong></p> + +<blockquote> + <p>Plot the point <em>x,y</em> in colour <em>col</em>.</p> +</blockquote> + +<p><strong><tt>void GFX_circle(int x, int y, int r, int col)<br> +void GFX_fcircle(int x, int y, int r, int col)</tt></strong></p> + +<blockquote> + <p>Draw a circle centred on <em>x,y</em> with a radius <em>r</em> + and in colour <em>col</em>. The <strong>fcircle</strong> + version should draw a filled circle.</p> +</blockquote> + +<p><strong><tt>void GFX_rect(int x, int y, int w, int h, int col)<br> +void GFX_frect(int x, int y, int w, int h, int col)</tt></strong></p> + +<blockquote> + <p>Draw a rectangle with one corner at <em>x,y</em> and the + other corner at <em>(x+w),(y+h)</em> in colour <em>col</em>. + Note that zero length and negative width and heights must be + allowed. The <strong>frect </strong>version should draw a + filled rectangle.</p> +</blockquote> + +<p><strong><tt>void GFX_set_XOR_mode(void)<br> +void GFX_clear_XOR_mode(void)</tt></strong></p> + +<blockquote> + <p>This should set and clear XOR mode. Normally all GFX + operations should set the pixels to the colour specified, but + when XOR mode is enabled the pixel values should be XORed + into place.</p> +</blockquote> + +<p><strong><tt>void GFX_print(int x, int y, int col, char *fmt, +...)</tt></strong></p> + +<blockquote> + <p>Print the printf style arguments (<em>fmt</em> and <em>...</em>) + with their top left corner at <em>x,y</em> in colour <em>col</em>. + Note that text should rendered transparently.</p> +</blockquote> + +<p><strong><tt>int GFX_fh(void)<br> +int GFX_fw(void)</tt></strong></p> + +<blockquote> + <p>Return the height (GFX_fh) and width (GFX_fw) of the fixed + width font used for display purposes.</p> +</blockquote> + +<p><strong><tt>int GFX_mouse_buttons(void)</tt></strong></p> + +<blockquote> + <p>Returns the number of mouse buttons. This is just used as + check on initialisation as viDOOM expects at least 2 mouse + buttons.</p> +</blockquote> + +<p><strong><tt>int GFX_mouse(int *x, int *y)</tt></strong></p> + +<blockquote> + <p>Return the current point position in <em>x</em> and <em>y</em>. + If any of the passed pointers are NULL that variable should + be ignored.</p> +</blockquote> + +<p><strong><tt>void GFX_waitkey(GFXKEy *key)</tt></strong></p> + +<blockquote> + <p>Waits for a <em>key</em> to be pressed and returns the key + press in <em>key</em>. If <em>key</em> is NULL simply wait + for a key press.</p> +</blockquote> + +<p><strong><tt>int GFX_key(GFXKey *key)</tt></strong></p> + +<blockquote> + <p>Returns TRUE if a key has been pressed and returns the + keypress in <em>key</em>. Returns FALSE if there is no + outstanding keypresses, in which case the contents of <em>key</em> + are undefined.</p> +</blockquote> + +<p><strong><tt>void GFX_bounce(void)</tt></strong></p> + +<blockquote> + <p>Waits for all keys and mouse buttons to be released. On a + real event-driven system could be ignored, or flush any + outstanding events.</p> +</blockquote> + +<p><strong><tt>void GFX_await_input(GFXEvent *ev)</tt></strong></p> + +<blockquote> + <p>Waits for either a keypress or a mouse button to be + pressed and fills in <em>ev</em> accordingly.</p> +</blockquote> + +<p><strong><tt>void GFX_await_input_full(GFXEvent *ev)</tt></strong></p> + +<blockquote> + <p>Waits for either a keypress, a mouse button to be pressed + or the mouse to be moved and fills in <em>ev</em> + accordingly.</p> +</blockquote> + +<p><strong><tt>void GFX_exit(int code, char *fmt, ...)</tt></strong></p> + +<blockquote> + <p>This call should do any necessary tidying of the display + (switching from graphics mode, closing windows, whatever) + then display the printf style arguments (<em>fmt</em> and <em>...</em>) + and the exit with the passed return <em>code</em>.</p> +</blockquote> + +<p><strong><tt>void GFX_save_screen(char *path)</tt></strong></p> + +<blockquote> + <p>This call need not be supported. It just allows screen + grabs to be captured when viDOOM is compiled with debug + information. If supported it should just save a bitmap in the + file pointed to by <em>path</em>.</p> +</blockquote> + +<hr> + +<h2><a name="PLATGUI_H"></a>Platform GUI</h2> + +<p><strong><u>platgui.c</u></strong></p> + +<p>This provides access to the platform's GUI routines.</p> + +<p>The following types are defined and used by the PLATGUI object +:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><pre>typedef struct + { + char *text; + GFX_IMAGE img; + int client_index; + } PLAT_IMG_PICKLIST;</pre> + </td> + <td valign="top">This structure is used to define a + picklist that has graphical images and client defined + values attached to them. This is used for selection of + textures, flats and sprites (things) in viDOOM.<p>The + fields in the structure are:</p> + <table border="0" cellpadding="3" cellspacing="6"> + <tr> + <td valign="top" nowrap>text</td> + <td valign="top" nowrap>-</td> + <td valign="top">This defines the text for a + picklist entry. NULL marks the end of the list of + picklist entries.</td> + </tr> + <tr> + <td valign="top" nowrap>img</td> + <td valign="top" nowrap>-</td> + <td valign="top">The GFX_IMAGE to associate with + this entry. Can be NULL to indicate show no + image.</td> + </tr> + <tr> + <td valign="top" nowrap>client_index</td> + <td valign="top" nowrap>-</td> + <td valign="top">The value returned if this item + is selected.</td> + </tr> + </table> + </td> + </tr> + <tr> + <td valign="top" nowrap><pre>typedef struct + { + char *text; + int client_index; + } PLAT_PICKLIST;</pre> + </td> + <td valign="top">This structure is used to define a + picklist that has client defined values attached to the + entries.<p>The fields in the structure are:</p> + <table border="0" cellpadding="3" cellspacing="6"> + <tr> + <td valign="top" nowrap>text</td> + <td valign="top" nowrap>-</td> + <td valign="top">This defines the text for a + picklist entry. NULL marks the end of the list of + picklist entries.</td> + </tr> + <tr> + <td valign="top" nowrap>client_index</td> + <td valign="top" nowrap>-</td> + <td valign="top">The value returned if this item + is selected.</td> + </tr> + </table> + </td> + </tr> + <tr> + <td valign="top" nowrap><pre>typedef struct PLATMENU + { + char *text; + int client_index; + struct PLATMENU *child; + } PLAT_MENU;</pre> + </td> + <td valign="top">This structure is used to define menu + entries that have client defined values attached to them.<p>The + fields in the structure are:</p> + <table border="0" cellpadding="3" cellspacing="6"> + <tr> + <td valign="top" nowrap>text</td> + <td valign="top" nowrap>-</td> + <td valign="top">This defines the text for the + menu entry. NULL marks the end of the list of + menu entries.</td> + </tr> + <tr> + <td valign="top" nowrap>client_index</td> + <td valign="top" nowrap>-</td> + <td valign="top">The value returned if this item + is selected. If the child field is not NULL, this + field is ignored.</td> + </tr> + <tr> + <td valign="top" nowrap>child</td> + <td valign="top" nowrap>-</td> + <td valign="top">If not NULL points to a further + array of PLAT_MENU objects defining a child menu.</td> + </tr> + </table> + </td> + </tr> + <tr> + <td valign="top" nowrap><pre>typedef struct + { + char *text; + int group; + int val; + } PLAT_MULTI; +</pre> + </td> + <td valign="top">This structure is used to entries for + the multi box call. A multi box is a dialog that holds a + mixture of radio buttons and check boxes.<p>The fields in + the structure are:</p> + <table border="0" cellpadding="3" cellspacing="6"> + <tr> + <td valign="top" nowrap>text</td> + <td valign="top" nowrap>-</td> + <td valign="top">This defines the text for the + multio box entry. NULL marks the end of the list + of entries.</td> + </tr> + <tr> + <td valign="top" nowrap>group</td> + <td valign="top" nowrap>-</td> + <td valign="top">The group this entry belongs to. + Group zero means that it is a check box and can + be checked/unchecked independently of other + entries. Entries in the same group should act as + radio buttons in that group.</td> + </tr> + <tr> + <td valign="top" nowrap>val</td> + <td valign="top" nowrap>-</td> + <td valign="top">The state of the check box/radio + button. TRUE means that it's set, FALSE means + it's clear. These fields are updated on exit once + the multo box is completed.</td> + </tr> + </table> + </td> + </tr> + <tr> + <td valign="top" nowrap><pre>typedef struct + { + char *text; + int client_index; + } PLAT_RADIO;</pre> + </td> + <td valign="top">This structure is used to define entries + for a radio style picklist (i.e. where only one option + can be chosen) that have client defined values attached + to them.<p>The fields in the structure are:</p> + <table border="0" cellpadding="3" cellspacing="6"> + <tr> + <td valign="top" nowrap>text</td> + <td valign="top" nowrap>-</td> + <td valign="top">This defines the text for the + radio button. NULL marks the end of the list of + radio button entries.</td> + </tr> + <tr> + <td valign="top" nowrap>client_index</td> + <td valign="top" nowrap>-</td> + <td valign="top">The value returned if this item + is selected.</td> + </tr> + </table> + </td> + </tr> + <tr> + <td valign="top" nowrap><pre>typedef struct + { + int no; + int current; + char **text; + } PLAT_DIAL_PL;</pre> + <pre>typedef struct + { + char *text; + int type; + union /* Data */ + { + int i; + char s[PLAT_DIAL_MAXSTRLEN+1]; + double d; + PLAT_DIAL_PL pl; + } data; + } PLAT_DIALOG; +</pre> + </td> + <td valign="top">These structures are used to define + entries for a simple dialog.<p>The fields in the + PLAT_DIAL_PL structure are:</p> + <table border="0" cellpadding="3" cellspacing="6"> + <tr> + <td valign="top" nowrap>no</td> + <td valign="top" nowrap>-</td> + <td valign="top">The number of elements pointed + to by text.</td> + </tr> + <tr> + <td valign="top" nowrap>current</td> + <td valign="top" nowrap>-</td> + <td valign="top">The currently selected item in + the picklist. This is updated on exit if the + dialog is accepted.</td> + </tr> + <tr> + <td valign="top" nowrap>text</td> + <td valign="top" nowrap>-</td> + <td valign="top">The text for the picklist + entries. This is treated as an array of character + pointers.</td> + </tr> + </table> + <p>The fields in the PLAT_DIALOG structure are:</p> + <table border="0" cellpadding="3" cellspacing="6"> + <tr> + <td valign="top" nowrap>text</td> + <td valign="top" nowrap>-</td> + <td valign="top">This defines the text for this + field in the dialog. NULL marks the end of the + list of dialog entries.</td> + </tr> + <tr> + <td valign="top" nowrap>type</td> + <td valign="top" nowrap>-</td> + <td valign="top">The type of field this is. + Possible values are PLAT_DIAL_STRING, + PLAT_DIAL_INTEGER, PLAT_DIAL_DOUBLE and + PLAT_DIAL_PICKLIST.</td> + </tr> + <tr> + <td valign="top" nowrap>data.i</td> + <td valign="top" nowrap>-</td> + <td valign="top">This is the field that is + displayed and updated on exit if the type is + PLAT_DIAL_INTEGER.</td> + </tr> + <tr> + <td valign="top" nowrap>data.s</td> + <td valign="top" nowrap>-</td> + <td valign="top">This is the field that is + displayed and updated on exit if the type is + PLAT_DIAL_STRING.</td> + </tr> + <tr> + <td valign="top" nowrap>data.d</td> + <td valign="top" nowrap>-</td> + <td valign="top">This is the field that is + displayed and updated on exit if the type is + PLAT_DIAL_DOUBLE.</td> + </tr> + <tr> + <td valign="top" nowrap>data.pl</td> + <td valign="top" nowrap>-</td> + <td valign="top">This is the structure that + defines how a PLAT_DIAL_PICKLIST is displayed. + The current field in this is updated if the + selected picklist value changes.</td> + </tr> + </table> + </td> + </tr> +</table> + +<p>Note that along with the types, the following predefined +values are set (these are read from the INI file). Note that they +should be considered to be unset until immediately prior to +viDOOM's call to <strong><tt>GUI_setscreen()</tt></strong>:</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><strong>GUI_HI</strong></td> + <td valign="top">The brightest colour used to draw the 3D + looking interface.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>GUI_MID</strong></td> + <td valign="top">The medium colour used to draw the 3D + looking interface.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>GUI_LO</strong></td> + <td valign="top">The darkest colour used to draw the 3D + looking interface.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>GUI_TEXT</strong></td> + <td valign="top">The colour of text.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>GUI_TEXTSHADOW</strong></td> + <td valign="top">The colour of the shadow behind text. + This is only really used by viDOOM's own portable GUI + routines.</td> + </tr> + <tr> + <td valign="top" nowrap><strong>GUI_BOLD</strong></td> + <td valign="top">The colour of bold text (used for + titles).</td> + </tr> +</table> + +<h3>Functions</h3> + +<p>The following interfaces are defined by the PLATGUI object. +Note that all these calls are assumed to not destroy screen +contents (ie. the screen should be restored after displaying the +GUI object):</p> + +<p><strong><tt>void GUI_setscreen(int width, int height)</tt></strong></p> + +<blockquote> + <p>Once the display has been opened with <a href="#GFX_OPEN"><strong>GFX_open()</strong></a> + then this is called to inform the platform's GUI routines of + the display size.</p> +</blockquote> + +<p><strong><tt>int GUI_yesno(char *question)</tt></strong></p> + +<blockquote> + <p>Display an alert with <em>question</em> in it and <em>Yes</em> + and <em>No</em> buttons. Returns TRUE if <em>Yes</em> is + pressed and FALSE if <em>No</em> is pressed.</p> +</blockquote> + +<p><strong><tt>int GUI_yesno_all(char *question)</tt></strong></p> + +<blockquote> + <p>Works like <strong>GUI_yesno()</strong>, but displays to + extra options - "Yes to All" and "No to + All". If either of these options are selected then + further calls to this function should return TRUE/FALSE + accordingly, until <strong>GUI_start_yesno__all()</strong> is + called.</p> +</blockquote> + +<p><strong><tt>int GUI_start_yesno_all(void);</tt></strong></p> + +<blockquote> + <p>Resets <strong>GUI_yesno_all()</strong>.</p> +</blockquote> + +<p><strong><tt>int GUI_alert(char *title, char *text, char +*button_text)</tt></strong></p> + +<blockquote> + <p>Display an alert with a title of <em>title</em>, + containing <em>text</em> as the message and with a single + button labelled <em>button_text</em>. Note that text is split + into multiple lines by the pipe (|) character.</p> +</blockquote> + +<p><strong><tt>int GUI_menu(char *title, int x, int y, PLAT_MENU +menu[], int defval)</tt></strong></p> + +<blockquote> + <p>Displays a menu with title <em>title</em> at position <em>x,y</em>. + The displayed items are taken from <em>menu</em>. The return + is the client_index field from the selected menu item, or + from the selected option in a child menu, or <em>defval</em> + if the menu is cancelled.</p> +</blockquote> + +<p><strong><tt>char *GUI_fsel(char *title, char *default_path, +char filter)</tt></strong></p> + +<blockquote> + <p>Allows a file to be selected. The file selector should + have <em>title</em> for it's title and start selecting from + the <em>default_path</em>. If <em>filter </em>is NULL then + all files should be displayed, otherwise only files ending in + <em>filter</em>.</p> + <p>The return is NULL if the selector is cancelled. Otherwise + a pointer is returned containing the fully qualified path of + the selected file. This pointer must be dynamically allocated + and will be freed using <a href="#FRELEASE"><strong>FRelease()</strong></a>.</p> +</blockquote> + +<p><strong><tt>int GUI_picklist(char *title, char *opts[])</tt></strong></p> + +<blockquote> + <p>Displays a picklist with title <em>title</em>. The options + are taken from the array of character pointers <em>opts</em>. + The return value is the index of the selected item in <em>opts</em> + if selected, or -1 if the picklist is cancelled.</p> +</blockquote> + +<p><strong><tt>int GUI_client_picklist(char *title, PLAT_PICKLIST +opts[], int defval)</tt></strong></p> + +<blockquote> + <p>Displays a picklist with title <em>title</em>. The text + items to display are taken from <em>opts</em>. The return is + the client_index field from the selected picklist item, or <em>defval</em> + if the picklist is cancelled.</p> +</blockquote> + +<p><strong><tt>int GUI_image_picklist(char *title, +PLAT_IMG_PICKLIST opts[], int defval)</tt></strong></p> + +<blockquote> + <p>Displays a picklist with title <em>title</em>. The text + items and associated image to display are taken from <em>opts</em>. + The return is the client_index field from the selected + picklist item, or <em>defval</em> if the picklist is + cancelled.</p> +</blockquote> + +<p><strong><tt>int GUI_radio_box(char *title, PLAT_RADIO opts[], +int current, int defval)</tt></strong></p> + +<blockquote> + <p>Displays a dialog containing radio buttons with title <em>title</em>. + The text to display is taken from <em>opts</em>. The selected + object when the the radio box is first displayed is the + option who's client_index field matches <em>current</em> (or + the first item if there is no match). The return is the + client_index field from the selected radio button, or <em>defval</em> + if the radio box is cancelled.</p> +</blockquote> + +<p><strong><tt>int GUI_multi_box(char *title, PLAT_MULTI opts[])</tt></strong></p> + +<blockquote> + <p>Display a mutli-selection radio box. The items are + described <em>opts</em>. The return is TRUE if the dialog is + accepted, otherwise FALSE.</p> +</blockquote> + +<p><strong><tt>int GUI_dialog(char *title, int no, PLAT_DIALOG +dial[])</tt></strong></p> + +<blockquote> + <p>Displays a dialog with the title <em>title</em>. The + fields for the dialog are extracted from <em>dial</em>, for + which there is expected to be <em>no</em> elements. The + return is TRUE if the dialog is accepted, or FALSE if it is + cancelled. On being cancelled the contents of the data union + within the <em>dial</em> elements is undefined.</p> +</blockquote> + +<p><strong><tt>void GUI_file_view(char *title, char *file)</tt></strong></p> + +<blockquote> + <p>Displays the contents of a text file, allowing the user to + move around and view the file. What form this takes is no + concern at all to viDOOM.</p> + <p>If is assumed this can view both DOS format (lines + terminated with CR and LF) and UNIX format text files (lines + terminated with LF).</p> +</blockquote> + +<p><strong><tt>char *GUI_text_edit(char *title, char *text)</tt></strong></p> + +<blockquote> + <p>Allows simple text editting. The form this display takes + is of noconcern to viDOOM (if applicable it would be more + than OK to start an external text editor). <em>text </em>is a + pointer to the original text, which is one long string with + line breaks denoted by the '\n' character.</p> + <p>The return is a newly allocated copy of the edited text is + the text is OKed, or NULL if the text is cancelled. In either + case, the original string pointed to by text should be as it + was.</p> +</blockquote> + +<hr> + +<h2><a name="FILE_H"></a>File interface</h2> + +<p><strong><u>file.c</u></strong></p> + +<p>This provides access to various file system functions and also +provides some filename manipulation routines. The following +interfaces should be provided:</p> + +<p><strong><tt>char *Pwd(void)</tt></strong></p> + +<blockquote> + <p>This call should return the current working directory. The + return should be static.</p> +</blockquote> + +<p><strong><tt>void Cd(char *path)</tt></strong></p> + +<blockquote> + <p>This call should change the current working directory to <em>path</em>.</p> +</blockquote> + +<p><strong><tt>char *Dirname(char *path)</tt></strong></p> + +<blockquote> + <p>This call should return the directory part of <em>path</em> + if any. The return should be static.</p> +</blockquote> + +<p><strong><tt>char *Basename(char *path)</tt></strong></p> + +<blockquote> + <p>This call should return the filename part of <em>path</em>. + The return should be static, or a pointer into the <em>path</em> + parameter.</p> +</blockquote> + +<p><strong><tt>int FileExists(char *path)</tt></strong></p> + +<blockquote> + <p>This call should return TRUE if the file pointed to by <em>path</em> + exists.</p> +</blockquote> + +<p><strong><tt>int FilenamesEqual(char *path1, char *path2)</tt></strong></p> + +<blockquote> + <p>This call should return TRUE if the file pointed to by <em>path1</em> + and <em>path2</em> are the same file. At it's most basic + (e.g. like in the DOS port) it can simply makes sure that + directory separators are in the same form and then does <strong>strcasecmp()</strong> + on the paths.</p> +</blockquote> + +<hr> + +<h2><a name="MEM_H"></a>Memory allocation</h2> + +<p><strong><u>mem.c</u></strong></p> + +<p>This provides memory allocation. While memory allocation can +generally be done portably using <tt>malloc()</tt> providing this +library just covers for any possible OS dependent twist. Also +these routines are expected to handle errors internally. In all +the interfaces <em>file</em> and <em>line</em> parameters are +included so that errors can be reported more accurately.</p> + +<p>The following interfaces should be provided:</p> + +<p><strong><tt>void *FGrab (char *file, int line, int len)</tt></strong></p> + +<blockquote> + <p>This call should allocate <em>len</em> bytes and return a + pointer to it. A <em>len</em> of zero is valid. Memory should + be initialised to zero. Failure to allocate the memory should + terminate the program.</p> +</blockquote> + +<p><strong><tt>void *FReGrab (char *file, int line, void *ptr, +int len)</tt></strong></p> + +<blockquote> + <p>This call should re-allocate the memory pointed to by <em>ptr</em> + and return a new memory area of <em>len</em> bytes. The + original data pointed to by <em>ptr</em> should be copied to + the new memory area. Failure to allocate the memory should + terminate the program.</p> +</blockquote> + +<p><strong><tt>char *FStrdup (char *file, int line, char *str)</tt></strong></p> + +<blockquote> + <p>This call should allocate enough bytes to copy the nul + terminated <em>str</em> to it. The returned pointer should + point to the new copy of <em>str</em>.<em> </em>Failure to + allocate the memory should terminate the program.</p> +</blockquote> + +<p><strong><tt>void *FCopy (char *file, int line, void *ptr, int +len)</tt></strong></p> + +<blockquote> + <p>This call should allocate <em>len</em> bytes and copy <em>len</em> + bytes from <em>ptr</em> into the new area. The newly + allocated memory should be returned. Failure to allocate the + memory should terminate the program.</p> +</blockquote> + +<p><a name="FRELEASE"></a><strong><tt>void FRelease (char *file, +int line, void *ptr)</tt></strong></p> + +<blockquote> + <p>This call should release the memory pointed to by <em>ptr</em>, + which will have been allocated by FGrab, FReGrab, FStrdup or + FCopy.</p> +</blockquote> + +<hr> + +<h2><a name="RUNCMD_H"></a>External command execution</h2> + +<p><strong><u>runcmd.c</u></strong></p> + +<p>Provides a mechanism to run an external command. The following +interfaces should be provided:</p> + +<p><strong><tt>int RunCommand(char *argv[], char *path)</tt></strong></p> + +<blockquote> + <p>Run a command. The output from the command (if there is + any) should NOT disturb the screen contents. The call should + return TRUE if the call succeeds, FALSE otherwise.</p> + <p>The <em>argv</em> list is an array of pointers to various + sections of the command and it's arguments, terminated with a + NULL pointer. Note that arguments may contain more than one + argument in each line - the actual command is described + simply by concatenating all the pointers together, eg.</p> + <p><tt>argv[0]="bsp"<br> + argv[1]="file.wad"<br> + argv[2]="-o file.wad"<br> + argv[3]=NULL</tt></p> + <p>The <em>path</em> argument is a place to copy the path to + a file where the output from the command has been stored. If + this is not supported then the empty string should be + assigned to it. viDOOM will <tt>remove()</tt> the file after + it has read it.</p> +</blockquote> + +<hr> + +<h2><a name="VSTRING_H"></a>Portable String routines</h2> + +<p><strong><u>vstring.c</u></strong></p> + +<p>Provides common string functions that are not actually part of +the ANSI standard. While these can easily be portably written, +they are provided as functions in case local implementations +supply them (which will probably more effecient):</p> + +<p><strong><tt>int StrCaseCmp(char *a, char *b)</tt></strong></p> + +<blockquote> + <p>Performs in exactly the same way as the ANSI <tt>strcmp()</tt> + function, save for the fact that the case of the strings + being compared is ignored.</p> +</blockquote> + +<p><strong><tt>int StrNCaseCmp(char *a, char *b)</tt></strong></p> + +<blockquote> + <p>Performs in exactly the same way as the ANSI <tt>strncmp()</tt> + function, save for the fact that the case of the strings + being compared is ignored.</p> +</blockquote> + +<hr> + +<h2><a name="INSTALLATION"></a>Installation script</h2> + +<p>Each platform should provide a makefile called <strong>install</strong>. +This is invoked from the top level makefile like this:</p> + +<blockquote> + <p><tt>cd $(PLATFORM) ; $(MAKEINSTALL)</tt></p> +</blockquote> + +<p>Note that the install makefile will be invoked with the <a +href="#PLATFORMVAR">PLATFORM</a> directory as the current working +directory.</p> + +<p>The following files must be copied (where $SRC represents the +source build directory and $INSTALLDIR the install directory):</p> + +<table border="1" cellpadding="3"> + <tr> + <td valign="top" nowrap><tt>$SRC/vidoom</tt></td> + <td valign="top"><tt>$INSTALLDIR/vidoom</tt><p>Note that + this file may have a system specific extension (e.g. .EXE + in DOS)</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><tt>$SRC/LICENSE</tt></td> + <td valign="top"><tt>$INSTALLDIR/LICENSE</tt><p>The GNU + GPL should be copied into the installation directory so + that binary distributions can be easily generated with + the license included and so that the LICENSE can be + viewed from viDOOM's main menu.</p> + </td> + </tr> + <tr> + <td valign="top" nowrap><tt>$SRC/base.ini</tt></td> + <td valign="top"><tt>$INSTALLDIR/vidoom.ini</tt></td> + </tr> + <tr> + <td valign="top" nowrap><tt>$SRC/*.cfg</tt></td> + <td valign="top"><tt>$INSTALLDIR/*.cfg</tt></td> + </tr> + <tr> + <td valign="top" nowrap><tt>$SRC/doc/*.htm</tt></td> + <td valign="top"><tt>$INSTALLDIR/doc/*.htm</tt></td> + </tr> + <tr> + <td valign="top" nowrap><tt>$SRC/doc/*.gif</tt></td> + <td valign="top"><tt>$INSTALLDIR/doc/*.gif</tt></td> + </tr> +</table> + +<p>Note that, obviously, any OS specific files should also be +copied.</p> + +<hr> + +<h2><a name="DOCUMENTATION"></a>Documentation</h2> + +<p>If you release a port of viDOOM to any platform please update <a +href="bugs.htm#CONTACTS">doc/bugs.htm</a> with a contact address +for problems on that platform. Also include a link to a system +specific HTML document detailing how the GUI works and any know +bugs, from <a href="sys.htm">doc/sys.htm</a>.</p> + +<p>For an example look at the <a href="djgpp.htm">DJGPP</a> +documentation. As you can see, it doesn't have to be too big.</p> + +<hr> + +<p><a href="index.htm">Back to index</a></p> + +<p><tt>$Id$</tt></p> +</body> +</html> diff --git a/doc/sys.htm b/doc/sys.htm new file mode 100644 index 0000000..a50827e --- /dev/null +++ b/doc/sys.htm @@ -0,0 +1,24 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<h1 align="center">System Dependent</h1> + +<p>The following is a list of the pages provided for each port:</p> + +<ul> + <li><a href="djgpp.htm">DJGPP</a></li> +</ul> + +<hr> + +<p><a href="index.htm">Back to index</a></p> + +<p><tt>$Id$</tt></p> +</body> +</html> diff --git a/doc/thanks.htm b/doc/thanks.htm new file mode 100644 index 0000000..edb3834 --- /dev/null +++ b/doc/thanks.htm @@ -0,0 +1,91 @@ +<html> + +<head> +<meta name="GENERATOR" content="vim"> +<title>viDOOM - Free Software DOOM editor</title> +</head> + +<body> + +<h1 align="center">Acknowledgements and Thanks</h1> + +<p>viDOOM could not have been written without help from the +following sources. Please mail me if there are any errors or I +have been misinformed on who actually did something. </p> + +<p><b><i>Doom - </i></b><a href="http://www.idsoftware.com"><b>id +Software</b></a><br> +Creators of DOOM, who thankfully wrote a terribly nice game and +then made it even nicer by making the WAD format simple and open. +</p> + +<p><a name="BOOM"></a><em><strong>BOOM </strong></em>- <a +href="http://www.teamtnt.com"><strong>Team TNT</strong></a><br> +One of the first newly extended versions of DOOM I saw after the +sources where released, and quite a revelation to see +transparencies, conveyer belts, water and all sorts making an +appearance. Still not to sure about the wind effect though :-)</p> + +<p><a name="ZDOOM"></a><em><strong>ZDoom </strong></em>- <a +href="http://zdoom.notgod.com"><strong>Randy Heit</strong></a><br> +My personal favourite out of the extended DOOMs. This took the +extensions added by BOOM, then went the whole hog and allowed +scripting and HEXEN style hub maps in DOOM. And made the editor +much harder to write :-)</p> + +<p>Version 1.17b was used as the test for levels generated by +viDOOM during it's development.</p> + +<p><a name="DOOMSPEC"></a><b><i>Unofficial Doom Specs -</i></b> <a +href="mailto:msfell@aol.com"><b>Matthew S Fell</b></a><br> +Writer and maintainer of the <a +href="ftp://ftp.cdrom.com/pub/idgames/docs/editing/dmspec16.zip">Unofficial +DOOM Specs</a>. viDOOM would have been impossible without this +marvellous tome.</p> + +<p><a name="BSP"></a><b><i>BSP -</i></b> <b>Colin Reed/ </b><a +href="mailto:killough@classicgaming.com"><b>Lee Killough</b></a><br> +Developers of the BSP node builder. Without this viDOOM is +completely useless. </p> + +<p><a name="WARM"></a><em><strong>WARM</strong></em><strong> - +Robert Frenske</strong><br> +The HEXEN compatible node builder used when developing the HEXEN +editing mode.</p> + +<p><b><i>Maths help -</i></b> <a +href="mailto:matthew@mjwilson.demon.co.uk"><b>Mathew Wilson</b></a><br> +Someone who knows much more maths than I ever will and writer of +the LinesCross() algorithm - saviour of the LINEDEF selection +code.</p> + +<p><em><strong>More maths help - </strong></em><a +href="http://www.faqs.org/faqs/graphics/algorithms-faq/"><strong>comp.graphics.algorithms +FAQ</strong></a><br> +Provided a much better 'is a point in a polygon?' than my +original one ever was...</p> + +<p><b><i>Music -</i></b> <a +href="http://www.c64audio.com/c64audio.asp"><b>C64 Audio</b></a><br> +Call me sad, call me mad, or call me a nutter who needs to get +out more and find a life, but some of the Commodore 64 CDs and +MP3's didn't half help the coding and vague stabs at +documentation along some evenings. </p> + +<p><b><i>VIM -</i></b> <a href="http://www.vim.org/"><b>Bram Moolenaar et al +</b></a><br> +VI iMproved. My favourite editor, and inspiration (well, the +original) for the name viDOOM. Now, if only all these flash IDEs +would realise that people may actually want to use something +other than their own bundled multi-coloured swap-shop editors... </p> + +<p> </p> + +<hr width="99%"> + +<p><a href="index.htm">Back to index</a></p> + +<p><tt>$Id: thanks.htm,v 1.6 2000/08/18 23:24:35 dosuser Exp +dosuser $ </tt></p> +</body> +</html> diff --git a/doom.cfg b/doom.cfg new file mode 100644 index 0000000..a43a671 --- /dev/null +++ b/doom.cfg @@ -0,0 +1,571 @@ +# viDOOM - level editor for DOOM +# +# Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# ------------------------------------------------------------------------- +# +# $Id: doom.cfg,v 1.31 2000/11/13 16:16:38 dosuser Exp dosuser $ +# +# Doom config file. The following sections may be present: +# +# %INCLUDE_FILES - include other config files +# %THING_CLASSES - classes of things (ammo, monsters, etc) +# %LINEDEF_CLASSES - classes of linedefs +# %SECTOR_CLASSES - classes of sectors +# %THING_TYPES - details types and IDs of things +# %THING_FLAGS - details flags and meaning for things +# %LINEDEF_FLAGS - details flags and meaning for linedefs +# %LINEDEF_FLAGS_EXTRA - details which flags indicate certain features +# %LINEDEF_TYPES - details types and IDs for linedefs +# %LINEDEF_DEFAULTS - default types for linedefs +# %SECTOR_STYLES - define texturing styles for linedefs +# %SECTOR_TYPES - details types and IDs of sectors +# %EMPTY_TEXTURE_NAME - name used for the empty texture +# %NORMAL_TYPES - values for normal sectors and linedefs +# %LINEDEF_CHECK_DEFAULT - optional textuire to use filling in missing +# textures when performing check linedefs +# +# +# The following sections are used to define generalised linedef and sector +# values. See zdoom.cfg for details: +# +# %LINEDEF_GEN_BITMASKS +# %LINEDEF_GEN_TYPES +# %SECTOR_GEN_BITMASKS +# %SECTOR_GEN_TYPES +# +# +# The following only need to appear in the config file for ZDoom if you are +# to be editing HEXEN format maps. See zdoom.cfg for examples: +# +# %HEXEN_ACTION_SPECIAL_CLASSES - action special classes +# %HEXEN_ACTION_SPECIALS - details action specials +# +# +# Note that some sections allow an edit mode to be defined, to indicate +# whether this definition is for DOOM or HEXEN edit mode (or both). The +# currently allowed edit modes are: +# +# Doom +# Hexen +# +# If what is being defined applies to more than one mode, these can be +# seperated with commas. +# + + +# THING_CLASSES +# +# Classes of things. Format is "class name|colour" +# +# Colour is defined as a hex number, each byte either R, G or B - 0xRRGGBB +# +# +%THING_CLASSES +Ammo|0x808000 +Dead Things|0x800000 +Health Items|0x00a000 +Other Items|0xa0a000 +Keys|0x00a0a0 +Monster|0xd01010 +Player Start|0xa000a0 +Scenery|0xa22a2a +Teleport|0x00a000 +Weapon|0xa0a000 + + +# LINEDEF_CLASSES +# +# Classes of linedefs. Format is "class name" +# +# +%LINEDEF_CLASSES +Normal +Local door +Remote door +Ceiling +Lift +Floor +Stair +Moving floor +Crushing ceiling +Exit level +Teleport +Light effect +Special + + +# SECTOR_CLASSES +# +# Classes of sectors. Format is "edit mode|class name" +# +# +%SECTOR_CLASSES +Doom|Normal +Doom|Lights +Doom|Damage +Doom|Damage/Lights +Doom|Damage/Exit +Doom|Door +Doom|Secret + + +# THING_TYPES section. Each lines is "class|name|id|radius|sprite_name" +# +# Where class is a class from the %THING_CLASSES section +# +# sprite_name can be "-" to incidate no graphics +# +%THING_TYPES +Ammo|Ammo clip|2007|20|CLIPA0 +Ammo|Shotgun shells|2008|20|SHELA0 +Ammo|A rocket|2010|20|ROCKA0 +Ammo|Cell charge|2047|20|CELLA0 +Ammo|Box of ammo|2048|20|AMMOA0 +Ammo|Box of shells|2049|20|SBOXA0 +Ammo|Box of rockets|2046|20|BROKA0 +Ammo|Big cell charge|17|20|CELPA0 +Ammo|Backpack|8|20|BPAKA0 + +Dead Things|Exploded player|10|20|PLAYW0 +Dead Things|Bloody mess|12|20|PLAYW0 +Dead Things|Pool of blood and flesh|24|20|POL5A0 +Dead Things|Dead player|15|20|PLAYW0 +Dead Things|Dead former human|18|20|POSSU0 +Dead Things|Dead sergeant|19|20|SPOSU0 +Dead Things|Dead imp|20|20|TROOU0 +Dead Things|Dead demon|21|20|SARGN0 +Dead Things|Dead cacodemon|22|20|HEADL0 +Dead Things|Dead lost soul|23|20|SKULJ0 + +Health Items|Stimpak|2011|20|STIMA0 +Health Items|Medikit|2012|20|MEDIA0 +Health Items|Health potion|2014|20|BON1A0 +Health Items|Spirt armour|2015|20|BON2A0 +Health Items|Green armour|2018|20|ARM1A0 +Health Items|Blue armour|2019|20|ARM2A0 +Health Items|Soulsphere|2013|20|SOULA0 +Health Items|Berserk|2023|20|PSTRA0 + +Other Items|Invulnerable|2022|20|PINVA0 +Other Items|Invisibility|2024|20|PINSA0 +Other Items|Radiation suit|2025|20|SUITA0 +Other Items|Map|2026|20|PMAPA0 +Other Items|Light amplification goggles|2045|20|PVISA0 + +Keys|Blue keycard|5|20|BKEYA0 +Keys|Blue skullkey|40|20|BSKUA0 +Keys|Red keycard|13|20|RKEYA0 +Keys|Red skullkey|38|20|RSKUA0 +Keys|Yellow keycard|6|20|YKEYA0 +Keys|Yellow skullkey|39|20|YSKUA0 + +Monster|Former Human|3004|20|POSSA1 +Monster|Former Sergeant|9|20|SPOSA1 +Monster|Imp|3001|20|TROOA1 +Monster|Demon|3002|30|SARGA1 +Monster|Spectre|58|30|SARGA1 +Monster|Lost soul|3006|16|SKULA1 +Monster|Cacodemon|3005|31|HEADA1 +Monster|Baron of Hell|3003|24|BOSSA1 +Monster|Spider Mastermind|7|128|SPIDA1D1 +Monster|Cyberdemon|16|40|CYBRA1 +Monster|Barrel|2035|16|BAR1A0 + +Player Start|Player 1 start|1|16|PLAYA1 +Player Start|Player 2 start|2|16|PLAYA1 +Player Start|Player 3 start|3|16|PLAYA1 +Player Start|Player 4 start|4|16|PLAYA1 +Player Start|Deatchmatch start|11|16|PLAYA1 + +Scenery|Tall techno pillar|48|16|ELECA0 +Scenery|Tall green pillar|30|16|COL1A0 +Scenery|Tall red pillar|32|16|COL3A0 +Scenery|Short green pillar|31|16|COL2A0 +Scenery|Short green pillar with beating heart|36|16|COL5A0 +Scenery|Short red pillar|33|16|COL4A0 +Scenery|Short red pillar with skull|37|16|COL6A0 +Scenery|Small brown pointy stump|47|16|SMITA0 +Scenery|Gray tree|43|16|TRE1A0 +Scenery|Large brown tree|54|32|TRE2A0 +Scenery|Floor lamp|2028|16|COLUA0 +Scenery|Candle|34|16|CANDA0 +Scenery|Candelabra|35|16|CBRAA0 +Scenery|Tall blue firestick|44|16|TBLUA0 +Scenery|Tall green firestick|45|16|TGRNA0 +Scenery|Tall red firestick|46|16|TREDA0 +Scenery|Short blue firestick|55|16|SMBTA0 +Scenery|Short green firestick|56|16|SMGTA0 +Scenery|Short red firestick|57|16|SMRTA0 +Scenery|Evil Eye|41|16|CEYEA0 +Scenery|Flaming skull rock|42|16|FSKUA0 + +Scenery|Hanging victim, twitching|49|16|GOR1A0 +Scenery|Hanging victim, twitching (let thru)|63|16|GOR1A0 +Scenery|Hanging victim, arms out|50|16|GOR2A0 +Scenery|Hanging victim, arms out (let thru)|59|16|GOR2A0 +Scenery|Hanging pair of legs|52|16|GOR4A0 +Scenery|Hanging pair of legs (let thru)|60|16|GOR4A0 +Scenery|Hanging victim, 1-legged|51|16|GOR3A0 +Scenery|Hanging victim, 1-legged (let thru)|61|16|GOR3A0 +Scenery|Hanging leg|53|16|GOR5A0 +Scenery|Hanging leg (let thru)|62|16|GOR5A0 + +Scenery|Impaled human|25|16|POL1A0 +Scenery|Twitching impaled human|26|16|POL6A0 +Scenery|Skull on a pole|27|16|POL4A0 +Scenery|Skewerd skulls|28|16|POL2A0 +Scenery|Pile of skulls and candles|29|16|POL3A0 + +Teleport|Teleport landing|14|16|TFOGA0 + +Weapon|Chainsaw|2005|20|CSAWA0 +Weapon|Shotgun|2001|20|SHOTA0 +Weapon|Chaingun|2002|20|MGUNA0 +Weapon|Rocket launcher|2003|20|LAUNA0 +Weapon|Plasma gun|2004|20|PLASA0 +Weapon|BFG 9000|2006|20|BFUGA0 + + +# THING_FLAGS section. Each line is +# +# "edit mode|group|mask1|mask2|name|single flag char" +# +# Group indicates whether any flags are mutually exclusive. Group 0 is a +# special group that indicates that any combination of flags is allowed. +# Other positive groups indicate that only one from that group number can be +# selected. +# +# The way the masks work is that the flags are ANDed with mask1. The result is +# then compared for equality with mask2. If the result matches then the flag +# is set. +# +# If later config files define an identical masks, the later definition replaces +# the earlier one. +# +%THING_FLAGS +Doom|0|0x01|0x01|Skill 1 and 2|E +Doom|0|0x02|0x02|Skill 3|M +Doom|0|0x04|0x04|Skill 4 and 5|H +Doom|0|0x08|0x08|Deaf|D +Doom|0|0x10|0x10|Deathmatch only|X + + +# LINEDEF_TYPES section. Each line is "class|id|name" +# +# Class is one of the classes from %LINEDEF_CLASSES +# +%LINEDEF_TYPES +Normal|0|Normal + +Special|48|Scrolling wall + +Local door|1|SRm door med 4 - open/close +Local door|26|SR door med 4 - open/close BLUE KEY +Local door|28|SR door med 4 - open/close RED KEY +Local door|27|SR door med 4 - open/close YELLOW KEY +Local door|31|S1 door med - - open +Local door|32|S1 door med - - open BLUE KEY +Local door|33|S1 door med - - open RED KEY +Local door|34|S1 door med - - open YELLOW KEY +Local door|46|GR door med - - open +Local door|117|SR blaze turbo 4 - open/close +Local door|118|S1 blaze turbo - - open + +Remote door|4|W1 door med 4 - open,close +Remote door|29|S1 door med 4 - open,close +Remote door|90|WR door med 4 - open,close +Remote door|63|SR door med 4 - open,close +Remote door|2|W1 door med - - open +Remote door|103|S1 door med - - open +Remote door|86|WR door med - - open +Remote door|61|SR door med - - open +Remote door|3|W1 door med - - close +Remote door|50|S1 door med - - close +Remote door|75|WR door med - - close +Remote door|42|SR door med - - close +Remote door|16|W1 door med 30 - close, then opens +Remote door|76|WR door med 30 - close, then opens +Remote door|108|W1 blaze turbo 4 - open,close +Remote door|111|WR blaze turbo 4 - open,close +Remote door|105|S1 blaze turbo 4 - open,close +Remote door|114|SR blaze turbo 4 - open,close +Remote door|109|W1 blaze turbo - - open +Remote door|112|S1 blaze turbo - - open +Remote door|106|WR blaze turbo - - open +Remote door|115|SR blaze turbo - - open +Remote door|110|W1 blaze turbo - - close +Remote door|113|S1 blaze turbo - - close +Remote door|107|WR blaze turbo - - close +Remote door|116|SR blaze turbo - - close +Remote door|133|S1 blaze turbo - - open BLUE KEY +Remote door|99|SR blaze turbo - - open BLUE KEY +Remote door|135|S1 blaze turbo - - open RED KEY +Remote door|134|SR blaze turbo - - open RED KEY +Remote door|137|S1 blaze turbo - - open YELLOW KEY +Remote door|136|SR blaze turbo - - open YELLOW KEY + +Ceiling|40|W1 mover slow - - up to HEC +Ceiling|41|S1 mover slow - - down to floor +Ceiling|43|SR mover slow - - down to floor +Ceiling|44|W1 mover slow - - down to floor + 8 +Ceiling|49|S1 mover slow - - down to floor + 8 +Ceiling|72|WR mover slow - - down to floor + 8 + +Lift|10|W1 lift fast 3 - lift +Lift|21|S1 lift fast 3 - lift +Lift|88|WRm lift fast 3 - lift +Lift|62|SR lift fast 3 - lift +Lift|121|W1 lift turbo 3 - lift +Lift|122|S1 lift turbo 3 - lift +Lift|120|WR lift turbo 3 - lift +Lift|123|SR lift turbo 3 - lift + +Floor|119|W1 mover slow - - up to nhEF +Floor|128|WR mover slow - - up to nhEF +Floor|18|S1 mover slow - - up to nhEF +Floor|69|SR mover slow - - up to nhEF +Floor|22|W1& mover slow - TX up to nhEF +Floor|95|WR& mover slow - TX up to nhEF +Floor|20|S1& mover slow - TX up to nhEF +Floor|68|SR& mover slow - TX up to nhEF +Floor|47|G1& mover slow - TX up to nhEF +Floor|5|W1 mover slow - - up to LIC +Floor|91|WR mover slow - - up to LIC +Floor|101|S1 mover slow - - up to LIC +Floor|64|SR mover slow - - up to LIC +Floor|24|G1 mover slow - - up to LIC +Floor|130|W1 mover turbo - - up to nhEF +Floor|131|S1 mover turbo - - up to nhEF +Floor|129|WR mover turbo - - up to nhEF +Floor|132|SR mover turbo - - up to nhEF +Floor|56|W1& mover slow - - up to LIC - 8, CRUSH +Floor|94|WR& mover slow - - up to LIC - 8, CRUSH +Floor|55|S1 mover slow - - up to LIC - 8, CRUSH +Floor|65|SR mover slow - - up to LIC - 8, CRUSH +Floor|58|W1 mover slow - - up 24 +Floor|92|WR mover slow - - up 24 +Floor|15|S1& mover slow - TX up 24 +Floor|66|SR& mover slow - TX up 24 +Floor|59|W1& mover slow - TXP up 24 +Floor|93|WR& mover slow - TXP up 24 +Floor|14|S1& mover slow - TX up 32 +Floor|67|SR& mover slow - TX up 32 +Floor|140|S1 mover med - - up 512 +Floor|30|W1 mover slow - - up ShortestLowerTexture +Floor|96|WR mover slow - - up ShortestLowerTexture +Floor|38|W1 mover slow - - down to LEF +Floor|23|S1 mover slow - - down to LEF +Floor|82|WR mover slow - - down to LEF +Floor|60|SR mover slow - - down to LEF +Floor|37|W1 mover slow - NXP down to LEF +Floor|84|WR mover slow - NXP down to LEF +Floor|19|W1 mover slow - - down to HEF +Floor|102|S1 mover slow - - down to HEF +Floor|83|WR mover slow - - down to HEF +Floor|45|SR mover slow - - down to HEF +Floor|36|W1 mover fast - - down to HEF + 8 +Floor|71|S1 mover fast - - down to HEF + 8 +Floor|98|WR mover fast - - down to HEF + 8 +Floor|70|SR mover fast - - down to HEF + 8 +Floor|9|S1 mover slow - NXP donut (see note 12 above) + +Stair|8|W1 mover slow - - stairs +Stair|7|S1 mover slow - - stairs +Stair|100|W1 mover turbo - - stairs (each up 16 not 8) + crush +Stair|127|S1 mover turbo - - stairs (each up 16 not 8) + crush + +Moving floor|53|W1& lift slow 3 - start moving floor +Moving floor|54|W1& - - - - stop moving floor +Moving floor|87|WR& lift slow 3 - start moving floor +Moving floor|89|WR& - - - - stop moving floor + +Crushing ceiling|6|W1& crush med 0 - start crushing, fast hurt +Crushing ceiling|25|W1& crush med 0 - start crushing, slow hurt +Crushing ceiling|73|WR& crush slow 0 - start crushing, slow hurt +Crushing ceiling|77|WR& crush med 0 - start crushing, fast hurt +Crushing ceiling|57|W1& - - - - stop crush +Crushing ceiling|74|WR& - - - - stop crush +Crushing ceiling|141|W1& none? slow 0 - start crushing, slow hurt "Silent" + +Exit level|11|S- clunk - - - End level, go to next level +Exit level|51|S- clunk - - - End level, go to secret level +Exit level|52|W- clunk - - - End level, go to next level +Exit level|124|W- clunk - - - End level, go to secret level + +Teleport|39|W1m tport - - - Teleport +Teleport|97|WRm tport - - - Teleport +Teleport|125|W1m tport - - - Teleport monsters only +Teleport|126|WRm tport - - - Teleport monsters only + +Light effect|35|W1 - - - - 0 +Light effect|104|W1 - - - - LE (light level) +Light effect|12|W1 - - - - HE (light level) +Light effect|13|W1 - - - - 255 +Light effect|79|WR - - - - 0 +Light effect|80|WR - - - - HE (light level) +Light effect|81|WR - - - - 255 +Light effect|17|W1 - - - - Light blinks (see [4-9-1] type 3) +Light effect|138|SR clunk - - - 255 +Light effect|139|SR clunk - - - 0 + + + +# LINEDEF_FLAGS. Each line is +# +# "edit mode|group|mask1|mask2|name|single flag char" +# +# See the THING_FLAGS section for details of the fields. +# +# If later config files define an identical mask1, the later definition replaces +# the earlier one. +# +%LINEDEF_FLAGS +Doom|0|0x001|0x001|Impassible|I +Doom|0|0x002|0x002|Block monsters|M +Doom|0|0x004|0x004|Two-sided|2 +Doom|0|0x008|0x008|Upper unpegged|U +Doom|0|0x010|0x010|Lower unpegged|L +Doom|0|0x020|0x020|Secret|S +Doom|0|0x040|0x040|Blocks sound|B +Doom|0|0x080|0x080|Not on map|N +Doom|0|0x100|0x100|Already on map|A + + +# LINEDEF_FLAGS_EXTRA +# +# Simply defines which of the LINEDEF flag bits are the ones that indicates +# certain features. This will probably never change anyhow, but is configurable +# in case. The format is: +# +# <bit for 2-sided-ness>|<bit for impassible>| +# <bit for upper unpegged>|<bit for lower unpegged> +# +# This section MUST appear before LINEDEF_DEFAULTS +# +%LINEDEF_FLAGS_EXTRA +2|0|3|4 + + +# LINEDEF_DEFAULTS +# +# Defaults to use for linedefs. Should appear after LINEDEF_FLAGS. Format: +# Name|Flags values +# +%LINEDEF_DEFAULTS +Empty|0x00 +Wall|0x01 +2-sided wall|0x47 +2-sided passable|0x04 +2-sided blocks monsters|0x46 + + +# SECTOR_TYPES +# +# Defines values and descriptions for the SECTOR special value +# +# Format: +# Edit mode|Class|Id|Long Name|Short Name +# +# Long name should be no longer than 60 characters, Short no longer than 30 +# Class is one of the classes from %SECTOR_CLASSES +# +%SECTOR_TYPES +Doom|Normal|0|Normal|Normal +Doom|Lights|1|Light random off|Random off +Doom|Lights|2|Blink lights 0.5 second|0.5 blink +Doom|Lights|3|Blink lights 1.0 second|1.5 blink +Doom|Damage/Lights|4|Lose -10/20% health & blink lights 0.5 sec|-10/20% & 0.5 blink +Doom|Damage|5|Lose -5/10% health|-5/10% +Doom|Damage|7|Lose -2/5% health|-2/5% +Doom|Lights|8|Oscillating light|Oscillating light +Doom|Secret|9|Secret|Secret +Doom|Door|10|Door closes after 30 seconds|30 sec door +Doom|Damage/Exit|11|-10/20% health - end level if < 11%|Damage/end level +Doom|Lights|12|Blink lights synchronised 0.5 second|0.5 blink sync +Doom|Lights|13|Blink lights synchronised 1.0 second|1.5 blink sync +Doom|Door|14|Door opens after 300 seconds|300 sec door +Doom|Damage|16|Lose -10/20% health|-10/20% +Doom|Lights|17|Light random on/off|Random on/off + + + +# SECTOR_STYLES +# +# Sector textureing styles. Note this section is not additive like the +# other sections (as different DOOMs can have different texture names). +# +# Format : +# +# Mode|Name|Upper|Middle|Lower|Floor|Ceiling +# +# Mode is made up of the following bit fields: +# +# Bit 0 - Paint textures on the sidedefs facing into this sector +# Bit 1 - Paint textures on the sidedefs facing out of this sector +# Bit 2 - Leave current lower/upper settings +# Bit 3 - Set upper unpegged if an upper texture is painted +# Bit 4 - Set lower unpegged if a lower texture is painted +# +# If none of bits 2,3 or 4 are set it is assumed that lower/upper unpegged will +# be cleared on painting those textures. +# +@DOOM_LEVEL_STYLE + +%SECTOR_STYLES +0x19|Quarry|ASHWALL|ASHWALL|ASHWALL|MFLR8_2|F_SKY1 +0x19|Fire Quarry|ROCKRED1|ROCKRED1|ROCKRED1|MFLR8_2|F_SKY1 +0x19|Computer Room|COMPTALL|COMPTALL|COMPTALL|CRATOP1|FLAT2 +0x19|Demon Room|MARBFACE|MARBFACE|MARBFACE|DEM1_6|DEM1_5 + +@END DOOM_LEVEL_STYLE + + +# EMPTY_TEXTURE_NAME +# +# The empty texture name. For DOOM this is '-'. +# +%EMPTY_TEXTURE_NAME +- + + +# NORMAL_TYPES +# +# The type values used to represent normal sectors and linedefs +# +# Format: +# Value for sectors|Value for linedefs +# +%NORMAL_TYPES +0|0 + + +# LINEDEF_CHECK_DEFAULT +# +# This controls the linedef F12 checking. Where it finds missing textures +# it will replace them with this one, rather than asking, unless this section +# is not defined or is defined to the same value as EMPTY_TEXTURE_NAME. +# +# Format: +# texture_name +# +@DOOM_LEVEL_STYLE + +%LINEDEF_CHECK_DEFAULT +ASHWALL + +@END DOOM_LEVEL_STYLE diff --git a/doom2.cfg b/doom2.cfg new file mode 100644 index 0000000..14e7e52 --- /dev/null +++ b/doom2.cfg @@ -0,0 +1,77 @@ +# viDOOM - level editor for DOOM +# +# Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# ------------------------------------------------------------------------- +# +# $Id: doom2.cfg,v 1.15 2000/09/11 08:08:36 dosuser Exp dosuser $ +# +# Doom 2 config file. See the heading of doom.cfg for details +# + +%INCLUDE_FILES +doom.cfg + +# THING_TYPES section +# +%THING_TYPES +Dead Things|Pool of blood|79|20|POB1A0 +Dead Things|Pool of blood|80|20|POB2A0 +Dead Things|Pool of brains|81|20|BRS1A0 + +Health Items|Megasphere|83|20|MEGAA0 + +Monster|Wolfenstien SS|84|20|SSWVA1 +Monster|Chaingunner|65|20|CPOSA1 +Monster|Hell Knight|69|24|BOS2A1C1 +Monster|Arachnotron|68|64|BSPIA1D1 +Monster|Pain Elemental|71|31|PAINA1 +Monster|Revenant|66|20|SKELA1D1 +Monster|Mancubus|67|48|FATTA1 +Monster|Arch Vile|64|20|VILEA1D1 +Monster|Boss Brain|88|16|BBRNA0 +Monster|Hellspawn Generator|89|16|BOSFB0 +Monster|Hellspawn Spot|87|16|FIRED0 + +Misc|Commander Keen|72|10|KEENA0 + +Scenery|Tall techno floor lamp|85|16|TLMPA0 +Scenery|Short techno floor lamp|86|16|TLP2A0 +Scenery|Burning barrel|70|16|FCANA0 +Scenery|Hanging victim, guts removed|73|16|HDB1A0 +Scenery|Hanging victim, guts and brain removed|74|16|HDB2A0 +Scenery|Hanging torso, looking down|75|16|HDB3A0 +Scenery|Hanging torso, open skull|76|16|HDB4A0 +Scenery|Hanging torso, looking up|77|16|HDB5A0 +Scenery|Hanging torso, brain removed|78|16|HDB6A0 + +Weapon|Double-barrel shotgun|82|20|SGN2A0 + + +# SECTOR_STYLES +# +%SECTOR_STYLES +0x19|Quarry|SP_ROCK1|SP_ROCK1|SP_ROCK1|RROCK09|F_SKY1 +0x19|Concrete Room|STONE|STONE|STONE|FLAT5_4|GRNLITE1 +0x19|Computer Room|COMPTALL|COMPTALL|COMPTALL|FLAT19|FLAT17 +0x19|Demon Room|MARBFACE|MARBFACE|MARBFACE|DEM1_6|DEM1_5 +0x03|Teleport|SUPPORT3|SUPPORT3|SUPPORT3|GATE3|GATE4 + +# LINEDEF_CHECK_DEFAULT +# +%LINEDEF_CHECK_DEFAULT +ASHWALL2 @@ -0,0 +1,751 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor main definitions + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include <ctype.h> + +#include "edit.h" +#include "editvar.h" + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ + +void EditSetScreen(int w,int h) +{ + VIDOOM_TRACE; + + SCRW=w; + SCRH=h; + + FH=GFX_fh(); + + vertex=MapNew(sizeof(Object)); + linedef=MapNew(sizeof(Object)); + sidedef=MapNew(sizeof(Object)); + thing=MapNew(sizeof(Object)); + sector=MapNew(sizeof(Object)); + selected=ListNew(sizeof(int)); +} + +void EditPreviewWadMap(char *name,WadMap *m) +{ + int min_x,min_y,cx,cy,scale; + int f; + Linedef *l; + Vertex *v1,*v2; + int done; + int redraw; + GFXKey key; + Thing *t; + int vertmode; + int linemode; + int things; + + VIDOOM_TRACE; + + scale=25; + cx=0; + cy=0; + + done=FALSE; + redraw=TRUE; + linemode=TRUE; + vertmode=TRUE; + things=TRUE; + + while(!done) + { + if (redraw) + { + min_x=cx-(SCRW/2)*scale; + min_y=cy-(SCRH/2)*scale; + + GFX_clear(BLACK); + + if (things) + for(f=0;f<MapSize(m->thing);f++) + { + t=MapElem(m->thing,f); + + GFX_fcircle((t->x-min_x)/scale, + (-t->y-min_y)/scale,8/scale,V_RGB(200,0,0)); + } + + if (linemode) + for(f=0;f<MapSize(m->linedef);f++) + { + l=MapElem(m->linedef,f); + v1=MapElem(m->vertex,l->from); + v2=MapElem(m->vertex,l->to); + + GFX_line((v1->x-min_x)/scale, + (-v1->y-min_y)/scale, + (v2->x-min_x)/scale, + (-v2->y-min_y)/scale,GREY(128)); + } + + if (vertmode) + for(f=0;f<MapSize(m->vertex);f++) + { + v1=MapElem(m->vertex,f); + + GFX_plot((v1->x-min_x)/scale, + (-v1->y-min_y)/scale,WHITE); + } + + GuiDrawInfoBox("Preview Map",1,1,FALSE, + "Map : %s|" + "Scale : %d|" + "X : %d|" + "Y : %d|" + "Linedefs : %s|" + "Vertexes : %s|" + "Things : %s|" + " |" + "Press F1 for help",name,scale,cx,cy, + YESNO(linemode), + YESNO(vertmode), + YESNO(things)); + + GFX_redraw(); + } + + GFX_waitkey(&key); + + redraw=TRUE; + + switch(key.code) + { + case GFX_ESC: + done=TRUE; + break; + + case GFX_DOWN: + cy+=scale*(key.shift ? 20 : 1); + break; + + case GFX_UP: + cy-=scale*(key.shift ? 20 : 1); + break; + + case GFX_RIGHT: + cx+=scale*(key.shift ? 20 : 1); + break; + + case GFX_LEFT: + cx-=scale*(key.shift ? 20 : 1); + break; + + case GFX_PGUP: + if (++scale>32) + scale=32; + break; + + case GFX_PGDN: + if (--scale==0) + scale=1; + break; + + case GFX_ASCII: + switch(toupper(key.ascii)) + { + case 'L': + linemode=!linemode; + break; + case 'V': + vertmode=!vertmode; + break; + case 'T': + things=!things; + break; + default: + break; + } + break; + + case GFX_F1: + GuiInfoBox("HELP", + "Cursor keys - move map|" + "Cursor keys + shift - move map quickly|" + "Page Down - reduce scale|" + "Page Up - increase scale|" + "L (toggle) - draw linedefs|" + "V (toggle) - draw vertex points|" + "T (toggle) - draw things|" + "ESC - quit"); + break; + + default: + redraw=FALSE; + break; + } + } +} + + +void EditLoad(WadMap *map) +{ + int f; + Vertex *v; + Linedef *l; + Sector *s; + Thing *t; + EditVert *ev; + EditLine *el; + EditSect *es; + EditThing *et; + Object o; + int min_x=0,max_x=0,min_y=0,max_y=0; + int find; + + VIDOOM_TRACE; + + scale=MAX(0,MIN(32,default_scale)); + + if (MapSize(map->vertex)) + { + min_x=99999; + min_y=99999; + max_x=-99999; + max_y=-99999; + find=TRUE; + } + else + { + find=FALSE; + ox=-scale*SCRW/2; + oy=scale*SCRH/2; + } + + vertex=MapEmpty(vertex); + linedef=MapEmpty(linedef); + thing=MapEmpty(thing); + sector=MapEmpty(sector); + sidedef=MapEmpty(sidedef); + selected=ListEmpty(selected); + + mapinfo=map->mapinfo; + behavior=map->behavior; + behavior_size=map->behavior_size; + scripts=map->scripts; + scripts_size=map->scripts_size; + hexen_mode=map->hexen; + + switch(default_edit_mode) + { + case EDIT_SECTOR: + SetEditMode(SECTOR_MODE); + break; + case EDIT_VERTEX: + SetEditMode(VERTEX_MODE); + break; + case EDIT_LINEDEF: + SetEditMode(LINEDEF_MODE); + break; + case EDIT_THING: + SetEditMode(THING_MODE); + break; + case EDIT_MULTI: + SetEditMode(MULTI_MODE); + break; + default: + SetEditMode(SECTOR_MODE); + break; + } + + /* Get vertex from WadMap + */ + for(f=0;f<MapSize(map->vertex);f++) + { + v=MapElem(map->vertex,f); + + min_x=MIN(min_x,v->x); + min_y=MIN(min_y,v->y); + max_x=MAX(max_x,v->x); + max_y=MAX(max_y,v->y); + + ev=Grab(sizeof(EditVert)); + memcpy(&ev->v,v,sizeof(Vertex)); + ev->l=ListNew(sizeof(int)); + + o.select=SELECT_NONE; + o.data=ev; + + MapAdd(vertex,f,&o); + } + + if (find) + { + if (min_x==max_y) + max_x++; + + if (min_y==max_y) + max_y++; + + ox=min_x+(max_x-min_x)/2; + oy=min_y+(max_y-min_y)/2; + + ox-=scale*SCRW/2; + oy+=scale*SCRH/2; + } + + /* Get sidedefs from WadMap + */ + for(f=0;f<MapSize(map->sidedef);f++) + { + o.select=SELECT_NONE; + o.data=Copy(MapElem(map->sidedef,f),sizeof(Sidedef)); + MapAdd(sidedef,f,&o); + } + + /* Get linedefs from WadMap, associated sidedefs, vertex pointers and calc + bounding box + */ + for(f=0;f<MapSize(map->linedef);f++) + { + l=MapElem(map->linedef,f); + el=Grab(sizeof(EditLine)); + + memcpy(&el->l,l,sizeof(Linedef)); + + el->no=f; + el->sr=GETSIDE(l->right); + + if (l->left!=-1) + el->sl=GETSIDE(l->left); + else + el->sl=NULL; + + el->v[0]=GETVERT(l->from); + el->v[1]=GETVERT(l->to); + + IntListUniqAdd(el->v[0]->l,f); + IntListUniqAdd(el->v[1]->l,f); + + LineCalcBounding(el); + + if (hexen_mode) + SetHexenLinedefTag(el); + + o.select=SELECT_NONE; + o.data=el; + + MapAdd(linedef,f,&o); + } + + /* Get sectors from WadMap + */ + for(f=0;f<MapSize(map->sector);f++) + { + s=MapElem(map->sector,f); + es=Grab(sizeof(EditSect)); + + memcpy(&es->s,s,sizeof(Sector)); + + es->no=f; + es->v=ListNew(sizeof(Short)); + es->sr=ListNew(sizeof(EditLine *)); + es->sl=ListNew(sizeof(EditLine *)); + es->all=ListNew(sizeof(EditLine *)); + + o.select=SELECT_NONE; + o.data=es; + + SectorCalcContaining(f,es); + SectorCalcBounding(es); + + MapAdd(sector,f,&o); + } + + /* Get things from WadMap + */ + for(f=0;f<MapSize(map->thing);f++) + { + t=MapElem(map->thing,f); + et=Grab(sizeof(EditThing)); + memcpy(&et->t,t,sizeof(Thing)); + o.select=SELECT_NONE; + o.data=et; + MapAdd(thing,f,&o); + } + + /* Set up multi map if default mode is MULTI mode + */ + if (default_edit_mode==EDIT_MULTI) + GenerateMultiMap(); +} + + +void EditSave(WadMap *map) +{ + int f; + Object *o; + EditVert *v; + EditThing *t; + EditSect *s; + EditLine *l; + + VIDOOM_TRACE; + + MapEmpty(map->linedef); + MapEmpty(map->sidedef); + MapEmpty(map->thing); + MapEmpty(map->vertex); + MapEmpty(map->sector); + + for(f=0;f<MapSize(vertex);f++) + { + v=GETVERT(f); + MapAdd(map->vertex,f,&(v->v)); + } + + /* Sidedefs are the only edit object stored fully in DOOM format + */ + for(f=0;f<MapSize(sidedef);f++) + { + o=MapElem(sidedef,f); + MapAdd(map->sidedef,f,o->data); + } + + for(f=0;f<MapSize(linedef);f++) + { + l=GETLINE(f); + MapAdd(map->linedef,f,&(l->l)); + } + + for(f=0;f<MapSize(thing);f++) + { + t=GETTHING(f); + MapAdd(map->thing,f,&(t->t)); + } + + for(f=0;f<MapSize(sector);f++) + { + s=GETSECT(f); + MapAdd(map->sector,f,&(s->s)); + } + + map->mapinfo=mapinfo; + map->behavior=behavior; + map->behavior_size=behavior_size; + map->scripts=scripts; + map->scripts_size=scripts_size; + map->hexen=hexen_mode; +} + + +void EditLoop(void) +{ + GFXEvent ev; + + VIDOOM_TRACE; + + GFX_bounce(); + FullRedraw(); + quit=FALSE; + + while(!quit) + { + GFX_redraw(); + GFX_await_input_full(&ev); + + new_selection=FALSE; + + switch(ev.type) + { + case GFX_KEY_EVENT: + HandleKey(ev.key); + break; + + case GFX_MOUSE_EVENT: + HandleMouse(ev.mouse); + break; + + default: + break; + } + } +} + + +void EditCompact(void) +{ + Map new_t,new_v,new_l,new_s,new_si; + int f; + int r; + int i; + Object o; + Object *oo; + EditLine *l; + EditSect *s; + EditVert *v; + Sidedef *side; + + VIDOOM_TRACE; + + ClearSelection(); + + new_t=MapNew(sizeof(Object)); + new_v=MapNew(sizeof(Object)); + new_l=MapNew(sizeof(Object)); + new_s=MapNew(sizeof(Object)); + new_si=MapNew(sizeof(Object)); + + /* Compress linedef + */ + i=0; + for(f=0;f<MapSize(linedef);f++) + { + memcpy(&o,MapElem(linedef,f),sizeof(Object)); + + if (o.data) + MapAdd(new_l,i++,&o); + } + + /* Compress things + */ + i=0; + for(f=0;f<MapSize(thing);f++) + { + memcpy(&o,MapElem(thing,f),sizeof(Object)); + + if (o.data) + MapAdd(new_t,i++,&o); + } + + /* Compress sidedef + */ + i=0; + for(f=0;f<MapSize(sidedef);f++) + if (GETSIDE(f)) + i++; + + if (i) + for(f=MapSize(sidedef)-1;f>=0;f--) + { + memcpy(&o,MapElem(sidedef,f),sizeof(Object)); + + if (o.data) + { + i--; + MapAdd(new_si,i,&o); + } + else + for(r=0;r<MapSize(new_l);r++) + { + oo=MapElem(new_l,r); + l=oo->data; + + if (l->l.left>=f) + l->l.left--; + + if (l->l.right>=f) + l->l.right--; + } + } + + /* Compress sectors + */ + i=0; + for(f=0;f<MapSize(sector);f++) + { + s=GETSECT(f); + + if ((s)&&(ListSize(s->all))) + i++; + } + + for(f=MapSize(sector)-1;f>=0;f--) + { + memcpy(&o,MapElem(sector,f),sizeof(Object)); + + s=o.data; + + if ((o.data)&&(ListSize(s->all))) + { + i--; + MapAdd(new_s,i,&o); + } + else + { + if (o.data) + Release(o.data); + + for(r=0;r<MapSize(new_si);r++) + { + side=SIDEFROM(new_si,r); + + if (side->sector>=f) + side->sector--; + } + } + } + + /* Compress vertexes (deleting unused ones first) + */ + for(f=0;f<MapSize(vertex);f++) + { + oo=MapElem(vertex,f); + + if (oo->data) + { + v=oo->data; + + if (ListSize(v->l)==0) + { + ListClear(v->l); + Release(oo->data); + oo->data=NULL; + } + } + } + + i=0; + for(f=0;f<MapSize(vertex);f++) + if (GETVERT(f)) + i++; + + if (i) + for(f=MapSize(vertex)-1;f>=0;f--) + { + memcpy(&o,MapElem(vertex,f),sizeof(Object)); + + if (o.data) + { + i--; + MapAdd(new_v,i,&o); + } + else + for(r=0;r<MapSize(new_l);r++) + { + oo=MapElem(new_l,r); + l=oo->data; + + if (l->l.from>=f) + l->l.from--; + + if (l->l.to>=f) + l->l.to--; + } + } + + for(r=0;r<MapSize(new_l);r++) + { + oo=MapElem(new_l,r); + l=oo->data; + + l->v[0]=VERTFROM(new_v,l->l.from); + l->v[1]=VERTFROM(new_v,l->l.to); + } + + /* Renumber items that remember their own number + */ + for(f=0;f<MapSize(new_s);f++) + { + oo=MapElem(new_s,f); + s=oo->data; + s->no=f; + } + + for(f=0;f<MapSize(new_l);f++) + { + oo=MapElem(new_l,f); + l=oo->data; + l->no=f; + } + + /* Save new maps + */ + MapClear(vertex); + MapClear(linedef); + MapClear(sector); + MapClear(thing); + MapClear(sidedef); + + vertex=new_v; + linedef=new_l; + sector=new_s; + thing=new_t; + sidedef=new_si; + + /* Reset current edit set + */ + switch(edit_mode) + { + case SECTOR_MODE: + map=sector; + break; + case LINEDEF_MODE: + map=linedef; + break; + case THING_MODE: + map=thing; + break; + case VERTEX_MODE: + map=vertex; + break; + } + + /* Recalc sidedef pointers in linedefs + */ + for(f=0;f<MapSize(linedef);f++) + { + l=GETLINE(f); + + if (l->l.left!=-1) + l->sl=GETSIDE(l->l.left); + else + l->sl=NULL; + + if (l->l.right!=-1) + l->sr=GETSIDE(l->l.right); + else + l->sr=NULL; + } + + /* Recalc vertex containing lists + */ + for(f=0;f<MapSize(vertex);f++) + ListEmpty(GETVERT(f)->l); + + for(f=0;f<MapSize(linedef);f++) + { + l=GETLINE(f); + IntListUniqAdd(l->v[0]->l,f); + IntListUniqAdd(l->v[1]->l,f); + } + + /* Recalc sector containing values + */ + SectorCalcContainingAll(); +} + + +/* END OF FILE */ @@ -0,0 +1,70 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor definitions + + Note that the lindefs, vertices, etc here are copied from the WadMap + loaded prior to editting. They are then reconverted back to the WadMap + for saving. + + $Id$ + +*/ + +#ifndef VIDOOM_EDIT_H +#define VIDOOM_EDIT_H + +#include "wad.h" + +/* Inform the editor of the screen size +*/ +void EditSetScreen(int w,int h); + + +/* Preview a rough map from a passed WadMap +*/ +void EditPreviewWadMap(char *name,WadMap *map); + + +/* Load in the editor data from a WadMap. Clears the current editor state. +*/ +void EditLoad(WadMap *map); + + +/* Save the editor data back into the WadMap +*/ +void EditSave(WadMap *map); + + +/* Editor loop +*/ +void EditLoop(void); + + +/* Compact the various objects, updating references as required. +*/ +void EditCompact(void); + +#endif + + +/* END OF FILE */ diff --git a/edit3d.c b/edit3d.c new file mode 100644 index 0000000..a6eb88f --- /dev/null +++ b/edit3d.c @@ -0,0 +1,474 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + _Very_ basic 3D preview + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" +#include "editvar.h" +#include "map.h" +#include "gfx.h" + +#include <math.h> +#include <ctype.h> + + +/* ---------------------------------------- LOCAL DATA +*/ +#define POLY 0 +#define SPHERE 1 + +typedef struct + { + int type; + Point p[4]; + Point c; + int r; + int z; + int col; + void *next; + } Poly; + +static int draw_things=FALSE; +static int fade=0; +static int roll; +static int pitch; +static int px,py,pz; + +static Poly *plist=NULL; + + +/* ---------------------------------------- POLY LIST FUNCTIONS +*/ +static void NewPList(void) +{ + Poly *p; + + while (plist) + { + p=plist; + plist=plist->next; + Release(p); + } +} + + +static void AddPList(Poly *p) +{ + Poly *np; + Poly *l; + + if (p->col<0x101010) + return; + + np=Copy(p,sizeof(Poly)); + + if (!plist) + { + plist=np; + np->next=NULL; + return; + } + else + { + if (np->z>=plist->z) + { + np->next=plist; + plist=np; + return; + } + + l=plist; + + while(l) + { + if (np->z<l->z) + { + if (!l->next) + { + l->next=np; + np->next=NULL; + return; + } + } + else + { + np->next=l->next; + l->next=np; + return; + } + + l=l->next; + } + } + + /* It shouldn't be possible to reach here!! + */ + Release(np); +} + + +/* ---------------------------------------- LOCAL FUNCTIONS +*/ +static void Transform (int x, int y, int z, int *rx, int *ry, int *rz) +{ + y=-y; + + *rx=x-px; + *ry=y-py; + *rz=z-pz; + + Rotate(0,0,rx,rz,roll); + Rotate(0,0,ry,rz,pitch); +} + + +static void Project (int x, int y, int z, int to_x, int to_z, Point *p) +{ + double dx,dy,sc; + + if ((z<1)&&(to_z<1)) + { + p->x=-1; + p->y=-1; + } + + if (z<1) + z=1; + + dx=(double)x; + dy=(double)y; + + sc=(double)z/400.0; + + p->x=(int)(dx/sc)+SCRW/2; + p->y=(int)(dy/sc)+SCRH/2; +} + +static int Fade(int col, int z) +{ + int r,g,b,f; + + f=z/(16/fade); + + r=(col&0xff0000)>>16; + g=(col&0xff00)>>8; + b=col&0xff; + + r=MAX(r-f,0); + g=MAX(g-f,0); + b=MAX(b-f,0); + + return(V_RGB(r,g,b)); +} + + +static void DoPoly(EditVert *v1, EditVert *v2, int low, int high) +{ + int x[4],y[4],z[4]; + int zt; + int f; + int ok; + Poly p; + + Transform(v1->v.x,low,v1->v.y,&x[0],&y[0],&z[0]); + Transform(v1->v.x,high,v1->v.y,&x[1],&y[1],&z[1]); + + Transform(v2->v.x,high,v2->v.y,&x[2],&y[2],&z[2]); + Transform(v2->v.x,low,v2->v.y,&x[3],&y[3],&z[3]); + + ok=FALSE; + + for(f=0;f<4;f++) + if (z[f]>0) + ok=TRUE; + + if (ok) + { + p.type=POLY; + + zt=0; + + p.z=999999; + + for(f=0;f<4;f++) + { + Project(x[f],y[f],z[f],x[(f+1)%4],z[(f+1)%4],&p.p[f]); + zt+=z[f]; + p.z=MIN(p.z,z[f]); + } + + if (fade) + p.col=Fade(WHITE,p.z); + else + p.col=WHITE; + + AddPList(&p); + } +} + + + +static void DoThing(EditThing *t,int tz) +{ + int x,y,z; + int r; + Poly p; + Point rp; + int col; + + Transform(t->t.x,tz,t->t.y,&x,&y,&z); + + if (z<1) + return; + + p.type=SPHERE; + + r=ThingRadius(t->t.type,&col); + + Project(x,y-r,z,x,z,&p.c); + Project(x+r,y-r,z,x+r,z,&rp); + + p.r=rp.x-p.c.x; + p.z=z; + + if (p.r<1) + p.r=1; + + if (fade) + p.col=Fade(RED,z); + else + p.col=col; + + AddPList(&p); +} + + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ +void EditPreview3D(void) +{ + int done; + int redraw; + GFXKey k; + int rd; + + px=0; + py=0; + pz=0; + roll=0; + pitch=0; + + done=FALSE; + redraw=TRUE; + rd=FALSE; + + while(!done) + { + if ((redraw)||(rd)) + { + EditThing *t; + EditLine *l; + EditSect *s,*sl; + int f; + int low,high; + int sn; + Poly *p; + + rd=FALSE; + + GFX_clear(BLACK); + + NewPList(); + + for(f=0;(f<MapSize(linedef))&&(!rd);f++) + { + if ((l=GETLINE(f))&&(s=GETSECT(l->sr->sector))) + if ((l->sl)&&(sl=GETSECT(l->sl->sector))) + { + if (s->s.floor!=sl->s.floor) + { + low=MIN(s->s.floor,sl->s.floor); + high=MAX(s->s.floor,sl->s.floor); + DoPoly(l->v[0],l->v[1],low,high); + } + + if (s->s.ceiling!=sl->s.ceiling) + { + low=MIN(s->s.ceiling,sl->s.ceiling); + high=MAX(s->s.ceiling,sl->s.ceiling); + DoPoly(l->v[0],l->v[1],low,high); + } + } + else + DoPoly(l->v[0],l->v[1],s->s.floor,s->s.ceiling); + + rd=GFX_key(&k); + } + + if (draw_things) + for(f=0;(f<MapSize(thing))&&(!rd);f++) + { + if ((t=GETTHING(f))) + { + sn=SectorHoldingPoint(t->t.x,t->t.y); + + if ((sn!=-1)&&(s=GETSECT(sn))) + high=s->s.floor; + else + high=0; + + if (hexen_mode) + high+=t->t.z; + + DoThing(t,high); + } + + rd=GFX_key(&k); + } + + p=plist; + + while((p)&&(!rd)) + { + switch(p->type) + { + case POLY: + for(f=0;f<4;f++) + GFX_line(p->p[f].x,p->p[f].y, + p->p[(f+1)%4].x,p->p[(f+1)%4].y,p->col); + break; + + case SPHERE: + GFX_fcircle(p->c.x,p->c.y,p->r,p->col); + break; + } + + p=p->next; + rd=GFX_key(&k); + } + + GFX_frect(0,0,SCRW,FH*2,BLUE); + + GFX_print(0,0,WHITE,"Pos: %d,%d,%d Roll: %d Pitch : %d %s", + px,py,pz,roll,pitch,rd ? "Draw interrupted" : "Draw done"); + + if (fade) + GFX_print(0,FH,WHITE,"Fade: Intensity %d",fade); + else + GFX_print(0,FH,WHITE,"Fade: off"); + + GFX_print(SCRW/2,FH,WHITE,"Things: %s",draw_things ? "On":"Off"); + + GFX_redraw(); + } + + if (!rd) + GFX_waitkey(&k); + + redraw=TRUE; + + if (k.code==GFX_ASCII) + switch(toupper(k.ascii)) + { + case 'T': + draw_things=!draw_things; + break; + + default: + redraw=FALSE; + break; + } + else + switch(k.code) + { + case GFX_ESC: + done=TRUE; + break; + + case GFX_LEFT: + if (k.alt) + { + pz+=(int)(cos(RAD(roll-90))*(k.shift ? 128 : 64)); + px+=(int)(sin(RAD(roll-90))*(k.shift ? 128 : 64)); + } + else + roll=(roll+360-(k.shift ? 15:5))%360; + break; + + case GFX_RIGHT: + if (k.alt) + { + pz+=(int)(cos(RAD(roll+90))*(k.shift ? 128 : 64)); + px+=(int)(sin(RAD(roll+90))*(k.shift ? 128 : 64)); + } + else + roll=(roll+(k.shift ? 15:5))%360; + break; + + case GFX_UP: + pz+=(int)(cos(RAD(roll))*(k.shift ? 128 : 64)); + px+=(int)(sin(RAD(roll))*(k.shift ? 128 : 64)); + break; + + case GFX_DOWN: + pz-=(int)(cos(RAD(roll))*(k.shift ? 128 : 64)); + px-=(int)(sin(RAD(roll))*(k.shift ? 128 : 64)); + break; + + case GFX_END: + pitch=0; + break; + + case GFX_PGDN: + if (k.shift) + pitch=MAX(pitch-5,-90); + else + py-=64; + break; + + case GFX_PGUP: + if (k.shift) + pitch=MIN(pitch+5,90); + else + py+=64; + break; + + case GFX_ENTER: + fade=(fade+1)%3; + break; + + default: + redraw=FALSE; + break; + } + } + + FullRedraw(); +} + + +/* END OF FILE */ diff --git a/editcord.c b/editcord.c new file mode 100644 index 0000000..6567be3 --- /dev/null +++ b/editcord.c @@ -0,0 +1,225 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor co-ordinate utilities + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include <math.h> + +#include "texture.h" +#include "editvar.h" + +/* ---------------------------------------- MAP DRAWING AND POSITION FUNCTIONS +*/ +int SnapX(int x) +{ + VIDOOM_TRACE; + + if (!grid_lock) + return(x); + + if (x>=0) + return (((x+grid_size/2)/grid_size)*grid_size); + else + return (((x-grid_size/2)/grid_size)*grid_size); +} + +int SnapY(int y) +{ + VIDOOM_TRACE; + + if (!grid_lock) + return(y); + + if (y>=0) + return (((y+grid_size/2)/grid_size)*grid_size); + else + return (((y-grid_size/2)/grid_size)*grid_size); +} + + +int Len(int x1,int y1,int x2,int y2) +{ + double a,b,c; + + VIDOOM_TRACE; + + x2-=x1; + y2-=y1; + x2=ABS(x2); + y2=ABS(y2); + + if (!x2) + return(y2); + else if (!y2) + return(x2); + + a=x2*x2; + b=y2*y2; + c=sqrt(a+b); + return((int)c); +} + + +/* Many thanks to Mathew Wilson (www.mjwilson.demon.co.uk) for this routine +*/ +int LinesCross(int x1_1,int y1_1,int x1_2,int y1_2, + int x2_1,int y2_1,int x2_2,int y2_2) +{ + double x0,y0,x1,y1,u0,v0,u1,v1; + double t; + double m,l; + + x0=(double)x1_1; + y0=(double)y1_1; + u0=(double)x1_2-x1_1; + v0=(double)y1_2-y1_1; + + x1=(double)x2_1; + y1=(double)y2_1; + u1=(double)x2_2-x2_1; + v1=(double)y2_2-y2_1; + + /* Find 1st divisor (to check for zero) + */ + t=(u1*v0)-(v1*u0); + + if (t==0.0) + return(FALSE); + + m=(v0*(x0-x1)-u0*(y0-y1))/t; + + l=(v1*(x0-x1)-u1*(y0-y1))/t; + + if ((l>=0.0)&&(l<=1.0)&&(m>=0.0)&&(m<=1.0)) + return(TRUE); + + return(FALSE); +} + + +int CalcTextureWidth(DirName u,DirName m,DirName l) +{ + int a,b,c; + int w=99999; + + VIDOOM_TRACE; + + TextureSize(u,&a,NULL); + TextureSize(m,&b,NULL); + TextureSize(l,&c,NULL); + + if ((a)&&(a<w)) + w=a; + + if ((b)&&(b<w)) + w=b; + + if ((c)&&(c<w)) + w=c; + + if (w==99999) + w=0; + + return(w); +} + + +void LineCalcBounding(EditLine *l) +{ + VIDOOM_TRACE; + + l->min_x=MIN(l->v[0]->v.x,l->v[1]->v.x); + l->max_x=MAX(l->v[0]->v.x,l->v[1]->v.x); + l->min_y=MIN(l->v[0]->v.y,l->v[1]->v.y); + l->max_y=MAX(l->v[0]->v.y,l->v[1]->v.y); +} + + +void SectorCalcBounding(EditSect *s) +{ + Iterator i; + Short *f; + EditVert *v; + + VIDOOM_TRACE; + + s->min_x=32000; + s->min_y=32000; + s->max_x=-32000; + s->max_y=-32000; + i=ListIterator(s->v); + + while(i) + { + f=IteratorData(i); + v=GETVERT(*f); + + s->min_x=MIN(s->min_x,v->v.x); + s->max_x=MAX(s->max_x,v->v.x); + s->min_y=MIN(s->min_y,v->v.y); + s->max_y=MAX(s->max_y,v->v.y); + + i=IteratorNext(i); + } +} + + +int LineOnDisplay(EditLine *l) +{ + VIDOOM_TRACE; + + /* Remember: DOOM Y positions are the reverse of the order on screen + */ + return ((MapToX(l->min_x)<SCRW)&&(MapToX(l->max_x)>=0)&& + (MapToY(l->max_y)<SCRH)&&(MapToY(l->min_y)>=0)); +} + + +int SectorOnDisplay(EditSect *s) +{ + VIDOOM_TRACE; + + /* Remember: DOOM Y positions are the reverse of the order on screen + */ + return ((MapToX(s->min_x)<SCRW)&&(MapToX(s->max_x)>=0)&& + (MapToY(s->max_y)<SCRH)&&(MapToY(s->min_y)>=0)); +} + + +int PointOnDisplay(int x,int y,int r) +{ + VIDOOM_TRACE; + + /* Remember: DOOM Y positions are the reverse of the order on screen + */ + return ((MapToX(x+r)>=0)&&(MapToX(x-r)<SCRW)&& + (MapToY(y-r)>=0)&&(MapToY(y+r)<SCRH)); +} + + +/* END OF FILE */ diff --git a/editcrse.c b/editcrse.c new file mode 100644 index 0000000..2e60a01 --- /dev/null +++ b/editcrse.c @@ -0,0 +1,397 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + SECTOR interactive creation + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" +#include "editvar.h" +#include "sectors.h" + +#include <ctype.h> + + +/* ---------------------------------------- SECTOR CREATION PRIVATE ROUTINES +*/ +static void SwapTexture(DirName a, DirName b) +{ + DirName t; + + strcpy(t,a); + strcpy(a,b); + strcpy(b,t); +} + + +static List ReverseList(List l) +{ + Iterator i; + List new; + + new=ListNew(sizeof(int)); + + i=ListIterator(l); + + while(i) + { + ListInsert(new,IteratorData(i)); + i=IteratorNext(i); + } + + return(new); +} + + +static List CopyList(List l) +{ + Iterator i; + List new; + + new=ListNew(sizeof(int)); + + i=ListIterator(l); + + while(i) + { + ListAppend(new,IteratorData(i)); + i=IteratorNext(i); + } + + return(new); +} + + +/* ---------------------------------------- SECTOR CREATION ROUTINES +*/ +int CreateUnboundSector(int special,int floor,int ceiling,int light,int tag, + DirName floor_t,DirName ceiling_t) +{ + Object o; + EditSect *s; + int sn; + + VIDOOM_TRACE; + + sn=MapSize(sector); + + s=Grab(sizeof(EditSect)); + + s->no=sn; + s->s.special=special; + s->s.floor=floor; + s->s.ceiling=ceiling; + s->s.light=light; + strcpy(s->s.floor_t,floor_t); + strcpy(s->s.ceiling_t,ceiling_t); + s->v=ListNew(sizeof(Short)); + s->sr=ListNew(sizeof(EditLine *)); + s->sl=ListNew(sizeof(EditLine *)); + s->all=ListNew(sizeof(EditLine *)); + s->min_x=s->min_y=s->max_x=s->max_y=0; + + o.select=SELECT_NONE; + o.data=s; + MapAdd(sector,sn,&o); + + return(sn); +} + +int CreateSector(List in_v) +{ + List v; + Iterator i; + int *f; + int from; + int to; + int type; + int flag; + int two; + int first; + int sect; + int wr,wl; + int oxr,oxl; + DirName sr_upper,sr_middle,sr_lower; + DirName sl_upper,sl_middle,sl_lower; + EditSect *s; + EditVert *v1,*v2; + int special,floor,ceiling,light; + int in_floor,in_ceiling,in_light; + DirName floor_t,ceiling_t; + int in_sector,new_sec; + int swap; + int nl; + int style_flag; + List left_offset; + + VIDOOM_TRACE; + + /* Check enough vertexes + */ + if (ListSize(in_v)<3) + { + GuiInfoBox("ERROR","Need 3 or more vertices|" + "to create a sector"); + return(FALSE); + } + + /* Check all points lie either inside the same sector or outside a sector + */ + in_sector=-1; + + i=ListIterator(in_v); + + f=IteratorData(i); + v1=GETVERT(*f); + in_sector=SectorHoldingPoint(v1->v.x,v1->v.y); + i=IteratorNext(i); + + /* Check to see if the vertices are all inside the same sector. If some or + all of the vertices lie outside the same sector then the sector is + assumed to not be in another one + */ + while(i) + { + f=IteratorData(i); + v1=GETVERT(*f); + + new_sec=SectorHoldingPoint(v1->v.x,v1->v.y); + + if ((in_sector!=-1)&&(new_sec!=in_sector)) + in_sector=-1; + + i=IteratorNext(i); + } + + if (in_sector!=-1) + { + s=GETSECT(in_sector); + light=in_light=s->s.light; + floor=in_floor=s->s.floor; + ceiling=in_ceiling=s->s.ceiling; + } + else + { + s=NULL; + light=in_light=default_light_level; + floor=in_floor=default_floor_height; + ceiling=in_ceiling=default_ceiling_height; + } + + special=0; + + /* Now get the linedef and sector values + */ + if (!GetSectorValues(&type,&flag,&two, + &floor,&ceiling,&light, + in_floor,in_ceiling, + &style_flag, + floor_t,ceiling_t, + sr_upper,sr_middle,sr_lower, + sl_upper,sl_middle,sl_lower)) + return(FALSE); + + /* See if the sector is inside another sector and see if the user wants + the new sectors right sidedefs pointing out into the containing sector + */ + if (in_sector!=-1) + swap=YesNo("Sector in another sector. Make RIGHT SIDEDEF point out?"); + else + swap=FALSE; + + + /* Past here the user cannot cancel the create operation, so now create + a copy of the vertex list (reversed if necessary) and reverse the + textures as necessary + */ + if (swap) + { + if (two) + { + SwapTexture(sr_upper,sl_upper); + SwapTexture(sr_middle,sl_middle); + SwapTexture(sr_lower,sl_lower); + } + + v=ReverseList(in_v); + } + else + v=CopyList(in_v); + + /* Add either lower/upper unpegged according to the sector style flags + */ + if (style_flag&SSTYLE_UPPER_PEG) + flag|=upper_peg_mask; + + if (style_flag&SSTYLE_LOWER_PEG) + flag|=lower_peg_mask; + + /* Create list for left offsets if needed + */ + if (two) + left_offset=ListNew(sizeof(int)); + else + left_offset=NULL; + + /* Calc texture widths and get a new sector number + */ + oxr=0; + oxl=0; + + wr=CalcTextureWidth(sr_upper,sr_middle,sr_lower); + + if (two) + wl=CalcTextureWidth + (sl_upper,sl_middle,sl_lower); + else + wl=0; + + if (swap) + { + sect=in_sector; + in_sector=CreateUnboundSector + (special,floor,ceiling,light,0, + floor_t,ceiling_t); + } + else + sect=CreateUnboundSector + (special,floor,ceiling,light,0, + floor_t,ceiling_t); + + i=ListIterator(v); + + memcpy(&to,IteratorData(i),sizeof(int)); + i=IteratorNext(i); + first=to; + + while(i) + { + from=to; + memcpy(&to,IteratorData(i),sizeof(int)); + i=IteratorNext(i); + + nl=CreateNewLinedef + (from,to, + flag,type,0,two, + oxr,0,sect, + sr_upper,sr_middle,sr_lower, + oxl,0,in_sector, + sl_upper,sl_middle,sl_lower); + + v1=GETVERT(from); + v2=GETVERT(to); + + IntListUniqAdd(v1->l,nl); + IntListUniqAdd(v2->l,nl); + + if (wr) + oxr=(oxr+Len(v1->v.x,v1->v.y, + v2->v.x,v2->v.y))%wr; + + /* The left sidedef has the offsets coming from the opposite direction + so have to be applied in reverse once all the lines have been + created. Damn, wish I'd realised that earlier + */ + if (two) + ListInsert(left_offset,&nl); + } + + /* Create the line to close the polygon + */ + nl=CreateNewLinedef(to,first, + flag,type,0,two, + oxr,0,sect, + sr_upper,sr_middle,sr_lower, + oxl,0,in_sector, + sl_upper,sl_middle,sl_lower); + + v1=GETVERT(to); + v2=GETVERT(first); + + IntListUniqAdd(v1->l,nl); + IntListUniqAdd(v2->l,nl); + + if (two) + ListInsert(left_offset,&nl); + + /* Apply the left offsets + */ + if (two) + { + EditLine *l; + + i=ListIterator(left_offset); + + while(i) + { + f=IteratorData(i); + + l=GETLINE(*f); + + if (wl) + oxl=(oxl+Len(l->v[0]->v.x,l->v[0]->v.y, + l->v[1]->v.x,l->v[1]->v.y))%wl; + + l->sl->x=oxl; + + i=IteratorNext(i); + } + + ListClear(left_offset); + } + + /* Calc sector bounds + */ + if (swap) + s=GETSECT(in_sector); + else + s=GETSECT(sect); + + SectorCalcContainingAll(); + SectorCalcBounding(s); + + ListClear(v); + + /* Check for mergeable LINEDEFS + */ + i=ListIterator(s->v); + + while(i) + { + Short *s; + + s=IteratorData(i); + CheckMergeLinedef(GETVERT(*s)); + i=IteratorNext(i); + } + + /* Recalc containing in case LINEDEFS where merged + */ + SectorCalcContainingAll(); + + return(TRUE); +} + + +/* END OF FILE */ diff --git a/editdraw.c b/editdraw.c new file mode 100644 index 0000000..3d31f41 --- /dev/null +++ b/editdraw.c @@ -0,0 +1,349 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + General editor drawing routines + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include <math.h> + +#include "editvar.h" + + +/* ---------------------------------------- DRAWING FUNCTIONS +*/ +void DrawArrow(int x1,int y1,int x2,int y2,int c) +{ + int dx,dy; + double ang; + int x,y; + + VIDOOM_TRACE; + + dx=x1-x2; + dy=y1-y2; + + /* Calc angle that the line is at + */ + if (dx==0) + if (dy<0) + ang=RAD(0); + else + ang=RAD(180); + else if (dy==0) + if (dx<0) + ang=RAD(90); + else + ang=RAD(270); + else + { + ang=atan((double)dx/(double)dy); + + if (dy>0) + ang-=RAD(180); + } + + MapLine(x1,y1,x2,y2,c); + + x=(int)(x2-sin(ang-RAD(25))*12.0); + y=(int)(y2-cos(ang-RAD(25))*12.0); + MapLine(x2,y2,x,y,c); + + x=(int)(x2-sin(ang+RAD(25))*12.0); + y=(int)(y2-cos(ang+RAD(25))*12.0); + MapLine(x2,y2,x,y,c); +} + + +void DrawGrid(void) +{ + int c; + + VIDOOM_TRACE; + + if (!grid_onoff) + return; + + agrid=grid_size; + + while((agrid/scale)<8) + agrid*=2; + + c=YToMap(0); + + while(c%agrid) + c++; + + while(c>YToMap(SCRH-1)) + { + GFX_line(0,MapToY(c),SCRW-1,MapToY(c),GRIDCOL); + c-=agrid; + } + + c=ox; + while(c%agrid) + c--; + + while(c<XToMap(SCRW-1)) + { + GFX_line(MapToX(c),0,MapToX(c),SCRH-1,GRIDCOL); + c+=agrid; + } +} + + +void DrawMap(void) +{ + int f; + Object *o; + EditLine *l; + EditVert *v; + EditThing *t; + EditSect *s; + Iterator i; + int *sv; + + VIDOOM_TRACE; + + for(f=0;f<MapSize(linedef);f++) + { + o=MapElem(linedef,f); + l=o->data; + + if (l) + { + /* LINEDEFS can move if we're in certain edit modes + */ + if (edit_mode!=THING_MODE) + LineCalcBounding(l); + + if (edit_mode!=SECTOR_MODE) + if (LineOnDisplay(l)) + switch(edit_mode) + { + case VERTEX_MODE: + case MULTI_MODE: + DrawArrow(l->v[0]->v.x,l->v[0]->v.y, + l->v[1]->v.x,l->v[1]->v.y,LINECOL); + break; + + case LINEDEF_MODE: + DrawObject_LINEDEF(l,o->select); + break; + + case THING_MODE: + MapLineD(l,LINECOL); + break; + } + } + } + + if ((edit_mode==VERTEX_MODE)||(edit_mode==MULTI_MODE)) + for(f=0;f<MapSize(vertex);f++) + { + o=MapElem(vertex,f); + v=o->data; + + if (v) + if (PointOnDisplay(v->v.x,v->v.y,vertex_rad)) + DrawObject_VERTEX(v,o->select); + } + + /* Sectors are drawn in a number of passes as selected sectors can quite + easily be overdrtawn by unselected ones + */ + if (edit_mode==SECTOR_MODE) + { + for(f=0;f<MapSize(sector);f++) + { + o=MapElem(sector,f); + s=o->data; + if (s) + { + SectorCalcBounding(s); + + if (SectorOnDisplay(s)) + DrawObject_SECTOR(s,o->select); + } + } + + i=ListIterator(selected); + + while(i) + { + sv=IteratorData(i); + + o=MapElem(sector,*sv); + s=o->data; + if (s) + { + SectorCalcBounding(s); + + if (SectorOnDisplay(s)) + DrawObject_SECTOR(s,o->select); + } + + i=IteratorNext(i); + } + + /* This slightly dodgy check is because sectors (as explained) do not + order themselves very well. This check wil put the currently + hovered-over object back on top of the display. + */ + if (current!=-1) + { + o=MapElem(sector,current); + s=o->data; + + if (s) + { + SectorCalcBounding(s); + + if (SectorOnDisplay(s)) + DrawObject_SECTOR(s,o->select); + } + } + } + + for(f=0;f<MapSize(thing);f++) + { + o=MapElem(thing,f); + t=o->data; + + if (t) + if ((edit_mode==THING_MODE)||(edit_mode==MULTI_MODE)) + { + if (PointOnDisplay(t->t.x,t->t.y,ThingRadius(t->t.type,NULL))) + DrawObject_THING(t,o->select); + } + else + if (PointOnDisplay(t->t.x,t->t.y,8)) + { + MapLine(t->t.x-8,t->t.y,t->t.x+8,t->t.y,THINGCOL); + MapLine(t->t.x,t->t.y-8,t->t.x,t->t.y+8,THINGCOL); + } + } +} + + +void DrawHeader(void) +{ +#ifdef VIDOOM_DEBUG + static int chk=TRUE; + static int db=FALSE; +#endif + + VIDOOM_TRACE; + + if (hexen_mode) + GFX_frect(0,0,SCRW,FH*2,RED); + else + GFX_frect(0,0,SCRW,FH*2,BLUE); + + GFX_print(0,0,WHITE,"Mode: %-8s Pos: %d,%d", + edit_str[edit_mode],XToMap(ms.x),YToMap(ms.y)); + + if (grid_onoff) + GFX_print(SCRW/2,0,WHITE,"Scale: %-2d Lock: %-3s Grid : %d/%d", + scale,YESNO(grid_lock),grid_size,agrid); + else + GFX_print(SCRW/2,0,WHITE,"Scale: %-2d Lock: %-3s Grid : %d", + scale,YESNO(grid_lock),grid_size); + + DrawObjectHeader(); + +#ifdef VIDOOM_DEBUG + + /* Debug + */ + if (chk) + { + if ((getenv("VIDOOM_VDEBUG"))&&(!strcmp(getenv("VIDOOM_VDEBUG"),"Y"))) + db=TRUE; + else + db=FALSE; + + chk=FALSE; + } + + if (db) + { + static char ds[128]; + int y; + Iterator i; + int *f; + + GFX_frect(0,FH*2,SCRW,FH*2,RED); + GFX_print(0,FH*2,WHITE,"ox=%d oy=%d " + "SectorHoldingPoint(%d,%d)=%d " + "ListSize(selected)=%d", + ox,oy, + XToMap(ms.x),YToMap(ms.y), + SectorHoldingPoint(XToMap(ms.x),YToMap(ms.y)), + ListSize(selected)); + + i=ListIterator(selected); + ds[0]=0; + y=FH*3; + + while(i) + { + f=IteratorData(i); + + if (strlen(ds)>70) + { + GFX_print(0,y,WHITE,ds); + ds[0]=0; + y+=FH; + } + + sprintf(ds+strlen(ds),"%d, ",*f); + + i=IteratorNext(i); + } + + strcat(ds,"<EOL>"); + GFX_print(0,y,WHITE,"%s",ds); + } +#endif +} + + +void FullRedraw(void) +{ + VIDOOM_TRACE; + + GFX_mouse(&ms.x,&ms.y); + GFX_clear(BLACK); + + DrawGrid(); + DrawMap(); + DrawObjectInfo(); + DrawHeader(); +} + + +/* END OF FILE */ diff --git a/editevnt.c b/editevnt.c new file mode 100644 index 0000000..c50c878 --- /dev/null +++ b/editevnt.c @@ -0,0 +1,1079 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor generic event handler definitions + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include <ctype.h> + +#include "editvar.h" +#include "platgui.h" +#include "mem.h" + + +/* ---------------------------------------- PREDICATE FUNCTIONS +*/ +static int PredSelected(void *a,void *b) +{ + int *i1,*i2; + + i1=a; + i2=b; + return(*i1==*i2); +} + + +/* ---------------------------------------- OBJECT LOCATORS +*/ +void GenericCheckMouse(void) +{ + static PLAT_MENU gen_menu[]={{"Insert",TM_INSERT},{NULL,0}}; + Object *o; + int f; + Iterator i; + int temp_select=FALSE; + int map_x,map_y; + + VIDOOM_TRACE; + + map_x=XToMap(ms.x); + map_y=YToMap(ms.y); + + /* If an object is already selected then see if it is still over it + */ + if (current!=-1) + { + o=MapElem(map,current); + + if (!(PositionOnObject(map_x,map_y,o->data))) + { + if (o->select!=SELECT_SELECTED) + SetSelect(current,SELECT_NONE); + + current=-1; + + DrawObject(o->data,o->select); + DrawObjectInfo(); + } + } + + if (current==-1) + for(f=0;(f<MapSize(map))&&(current==-1);f++) + { + o=MapElem(map,f); + + if (o->data) + { + if (PositionOnObject(map_x,map_y,o->data)) + { + current=f; + if (o->select!=SELECT_SELECTED) + SetSelect(current,SELECT_OVER); + + DrawObject(o->data,o->select); + DrawObjectInfo(); + } + } + } + + /* Do selection box if shift pressed + */ + if ((ms.b&GFX_BUTLEFT)&&(ms.shift)) + { + GFXEvent e; + int x1,y1,x2,y2; + + GFX_set_XOR_mode(); + + do + { + int w,h; + + do + { + GFX_await_input_full(&e); + } while(e.type!=GFX_MOUSE_EVENT); + + w=e.mouse.x-ms.x; + h=e.mouse.y-ms.y; + + GFX_rect(ms.x,ms.y,w,h,WHITE); + GFX_redraw(); + GFX_rect(ms.x,ms.y,w,h,WHITE); + + } while(e.mouse.b&GFX_BUTLEFT); + + GFX_clear_XOR_mode(); + GFX_redraw(); + + x1=MIN(XToMap(ms.x),XToMap(e.mouse.x)); + y1=MIN(YToMap(ms.y),YToMap(e.mouse.y)); + x2=MAX(XToMap(ms.x),XToMap(e.mouse.x)); + y2=MAX(YToMap(ms.y),YToMap(e.mouse.y)); + + if (!(e.mouse.ctrl|ms.ctrl)) + ClearSelection(); + + SelectBox(x1,y1,x2,y2); + + memcpy(&ms,&e.mouse,sizeof(e.mouse)); + FullRedraw(); + } + else + /* Handle left (select) and right (menu) buttons + */ + { + if (ms.b&GFX_BUTLEFT) + { + if (current==-1) + { + if (!ms.ctrl) + { + ClearSelection(); + FullRedraw(); + } + } + else + { + if (ms.ctrl) + { + o=MapElem(map,current); + + if (o->select==SELECT_SELECTED) + { + if ((i=ListFindElem(selected,PredSelected,¤t))) + { + i=IteratorDelete(i); + i=IteratorClear(i); + } + + SetSelect(current,SELECT_NONE); + DrawObject(o->data,SELECT_NONE); + } + else + { + SetSelect(current,SELECT_SELECTED); + ListAppend(selected,¤t); + DrawObject(o->data,SELECT_SELECTED); + } + } + else + { + o=MapElem(map,current); + + /* Check for left click move mode + */ + if (o->select==SELECT_SELECTED && left_click_move) + MoveObject(); + else + { + ClearSelectionLeaveCurrent(); + SetSelect(current,SELECT_SELECTED); + ListAppend(selected,¤t); + FullRedraw(); + } + } + } + } + + /* If the middle or right button is pressed over an object, check + adding the object to the selected list + */ + if ((ms.b&(GFX_BUTRIGHT|GFX_BUTMIDDLE))&&(current!=-1)) + { + /* If there is currently nothing selected then this object is + selected for the duration of the move or menu operation + */ + if (ListSize(selected)==0) + { + temp_select=TRUE; + ListAppend(selected,¤t); + o=MapElem(map,current); + SetSelect(current,SELECT_SELECTED); + DrawObject(o->data,o->select); + } + else if (hover_select!=HOVER_NONE) + { + o=MapElem(map,current); + + if (o->select!=SELECT_SELECTED) + { + if (hover_select==HOVER_SINGLE) + ClearSelectionLeaveCurrent(); + + o=MapElem(map,current); + SetSelect(current,SELECT_SELECTED); + ListAppend(selected,¤t); + FullRedraw(); + } + } + } + + + /* If the middle button is pressed move the object. This is provided as + a shortcut to the menu move option + */ + if ((ms.b&GFX_BUTMIDDLE)&&(ListSize(selected))) + MoveObject(); + + /* If the right button is pressed selected the editting options + */ + if (ms.b&GFX_BUTRIGHT) + { + if (ListSize(selected)) + ObjectMenu(); + else + switch(GUI_menu(typename,ms.x,ms.y,gen_menu,GUI_CANCEL)) + { + case TM_INSERT: + ObjectInsert(); + break; + + default: + break; + } + } + + if ((temp_select)&&(!new_selection)) + { + ClearSelection(); + FullRedraw(); + } + } +} + + +/* ---------------------------------------- INPUT HANDLERS +*/ +void SetEditMode(int mode) +{ + VIDOOM_TRACE; + + ClearSelection(); + current=-1; + + edit_mode=mode; + + switch(mode) + { + case SECTOR_MODE: + typename="Sector"; + map=sector; + PositionOnObject=PositionOnObject_SECTOR; + SelectBox=SelectBox_SECTOR; + SelectByType=SelectByType_SECTOR; + DrawObject=DrawObject_SECTOR; + DrawObjectInfo=DrawObjectInfo_SECTOR; + DrawObjectHeader=DrawObjectHeader_SECTOR; + MoveObject=MoveObject_SECTOR; + RotateObject=RotateObject_SECTOR; + ScaleObject=ScaleObject_SECTOR; + SetTagObject=SetTagObject_SECTOR; + LocateObject=LocateObject_SECTOR; + ObjectMenu=ObjectMenu_SECTOR; + ObjectInsert=ObjectInsert_SECTOR; + ObjectDelete=ObjectDelete_SECTOR; + ObjectKey=ObjectKey_SECTOR; + ObjectHasTag=ObjectHasTag_SECTOR; + SetSelect=SetSelect_GENERIC; + ObjectOverlaid=ObjectOverlaid_SECTOR; + break; + + case LINEDEF_MODE: + typename="Linedef"; + map=linedef; + PositionOnObject=PositionOnObject_LINEDEF; + SelectBox=SelectBox_LINEDEF; + SelectByType=SelectByType_LINEDEF; + DrawObject=DrawObject_LINEDEF; + DrawObjectInfo=DrawObjectInfo_LINEDEF; + DrawObjectHeader=DrawObjectHeader_LINEDEF; + MoveObject=MoveObject_LINEDEF; + RotateObject=RotateObject_LINEDEF; + ScaleObject=ScaleObject_LINEDEF; + SetTagObject=SetTagObject_LINEDEF; + LocateObject=LocateObject_LINEDEF; + ObjectMenu=ObjectMenu_LINEDEF; + ObjectInsert=ObjectInsert_LINEDEF; + ObjectDelete=ObjectDelete_LINEDEF; + ObjectKey=ObjectKey_LINEDEF; + ObjectHasTag=ObjectHasTag_LINEDEF; + SetSelect=SetSelect_GENERIC; + ObjectOverlaid=ObjectOverlaid_LINEDEF; + break; + + case VERTEX_MODE: + typename="Vertex"; + map=vertex; + PositionOnObject=PositionOnObject_VERTEX; + SelectBox=SelectBox_VERTEX; + SelectByType=SelectByType_VERTEX; + DrawObject=DrawObject_VERTEX; + DrawObjectInfo=DrawObjectInfo_VERTEX; + DrawObjectHeader=DrawObjectHeader_VERTEX; + MoveObject=MoveObject_VERTEX; + RotateObject=RotateObject_VERTEX; + ScaleObject=ScaleObject_VERTEX; + SetTagObject=SetTagObject_VERTEX; + LocateObject=LocateObject_VERTEX; + ObjectMenu=ObjectMenu_VERTEX; + ObjectInsert=ObjectInsert_VERTEX; + ObjectDelete=ObjectDelete_VERTEX; + ObjectKey=ObjectKey_VERTEX; + ObjectHasTag=ObjectHasTag_VERTEX; + SetSelect=SetSelect_GENERIC; + ObjectOverlaid=ObjectOverlaid_VERTEX; + break; + + case THING_MODE: + typename="Thing"; + map=thing; + PositionOnObject=PositionOnObject_THING; + SelectBox=SelectBox_THING; + SelectByType=SelectByType_THING; + DrawObject=DrawObject_THING; + DrawObjectInfo=DrawObjectInfo_THING; + DrawObjectHeader=DrawObjectHeader_THING; + MoveObject=MoveObject_THING; + RotateObject=RotateObject_THING; + ScaleObject=ScaleObject_THING; + SetTagObject=SetTagObject_THING; + LocateObject=LocateObject_THING; + ObjectMenu=ObjectMenu_THING; + ObjectInsert=ObjectInsert_THING; + ObjectDelete=ObjectDelete_THING; + ObjectKey=ObjectKey_THING; + ObjectHasTag=ObjectHasTag_THING; + SetSelect=SetSelect_GENERIC; + ObjectOverlaid=ObjectOverlaid_THING; + break; + + case MULTI_MODE: + GenerateMultiMap(); + typename="Multimode"; + map=multimap; + PositionOnObject=PositionOnObject_MULTI; + SelectBox=SelectBox_MULTI; + SelectByType=SelectByType_MULTI; + DrawObject=DrawObject_MULTI; + DrawObjectInfo=DrawObjectInfo_MULTI; + DrawObjectHeader=DrawObjectHeader_MULTI; + MoveObject=MoveObject_MULTI; + RotateObject=RotateObject_MULTI; + ScaleObject=ScaleObject_MULTI; + SetTagObject=SetTagObject_MULTI; + LocateObject=LocateObject_MULTI; + ObjectMenu=ObjectMenu_MULTI; + ObjectInsert=ObjectInsert_MULTI; + ObjectDelete=ObjectDelete_MULTI; + ObjectKey=ObjectKey_MULTI; + ObjectHasTag=ObjectHasTag_MULTI; + SetSelect=SetSelect_MULTI; + ObjectOverlaid=ObjectOverlaid_MULTI; + break; + } + + GenericCheckMouse(); + FullRedraw(); +} + + +/* This handles the keys that should usable in all modes (movement, scale, + grid, etc) +*/ +void HandleMoveKey(GFXKey k, int check_mouse) +{ + int cx,cy; + + VIDOOM_TRACE; + + if (k.code!=GFX_ASCII) + switch(k.code) + { + case GFX_DOWN: + if (k.ctrl) + oy-=scale; + else + oy-=scale*(k.shift ? 60 : 20); + + if (check_mouse) + GenericCheckMouse(); + + FullRedraw(); + break; + + case GFX_UP: + if (k.ctrl) + oy+=scale; + else + oy+=scale*(k.shift ? 60 : 20); + + if (check_mouse) + GenericCheckMouse(); + + FullRedraw(); + break; + + case GFX_RIGHT: + if (k.ctrl) + ox+=scale; + else + ox+=scale*(k.shift ? 60 : 20); + + if (check_mouse) + GenericCheckMouse(); + + FullRedraw(); + break; + + case GFX_LEFT: + if (k.ctrl) + ox-=scale; + else + ox-=scale*(k.shift ? 60 : 20); + + if (check_mouse) + GenericCheckMouse(); + + FullRedraw(); + break; + + case GFX_PGDN: + if ((--scale)<1) + scale=1; + else + { + cx=ox+(scale+1)*SCRW/2; + cy=oy-(scale+1)*SCRH/2; + ox=cx-(scale)*SCRW/2; + oy=cy+(scale)*SCRH/2; + } + + if (check_mouse) + GenericCheckMouse(); + + FullRedraw(); + break; + + case GFX_PGUP: + if ((++scale)>32) + scale=32; + else + { + cx=ox+(scale-1)*SCRW/2; + cy=oy-(scale-1)*SCRH/2; + ox=cx-(scale)*SCRW/2; + oy=cy+(scale)*SCRH/2; + } + + if (check_mouse) + GenericCheckMouse(); + + FullRedraw(); + break; + + default: + break; + } + else + switch(toupper(k.ascii)) + { + case 'G': + grid_onoff=!grid_onoff; + FullRedraw(); + break; + + case 'X': + grid_lock=!grid_lock; + DrawHeader(); + break; + + case 'Q': + grid_size/=2; + if (grid_size<2) + grid_size=2; + FullRedraw(); + break; + + case 'W': + grid_size*=2; + if (grid_size>512) + grid_size=512; + FullRedraw(); + break; + +#ifdef VIDOOM_DEBUG + case 'D': + case 'd': + { + static PLAT_MENU debug_menu[]= + { + {"Dump current selection list",1}, + {"Save screen",2}, + {NULL,-1} + }; + + switch(GUI_menu("DEBUG",0,0,debug_menu,-1)) + { + case 1: + { + static char ds[128]; + Iterator i; + int *f; + + Debug(("No selections : %d\n",ListSize(selected))); + + i=ListIterator(selected); + ds[0]=0; + + while(i) + { + f=IteratorData(i); + + if (strlen(ds)>60) + { + Debug(("%s\n",ds)); + ds[0]=0; + } + + sprintf(ds+strlen(ds),"%d, ",*f); + + i=IteratorNext(i); + } + + strcat(ds,"<EOL>"); + Debug(("%s\n",ds)); + } + + break; + + case 2: + { + static int i=0; + char p[10]; + + sprintf(p,"pic%d.bmp",i++); + GFX_save_screen(p); + + } + + break; + + default: + break; + } + + break; + } +#endif + + default: + break; + } +} + + +void HandleKey(GFXKey k) +{ + Object *o; + int f; + + VIDOOM_TRACE; + + HandleMoveKey(k,TRUE); + ObjectKey(k); + + if (k.code!=GFX_ASCII) + switch(k.code) + { + case GFX_F1: + for(f=0;general_help_keys[f];f++) + { + char title[80]; + + sprintf(title,"HELP (Keys) Page %d",f+1); + GuiInfoBox(title,"%s",general_help_keys[f]); + } + + GuiInfoBox("HELP (Mouse)","%s",general_help_mouse); + break; + + case GFX_F2: + { + char s[128]; + + sprintf(s,"%s Mode Keys",typename); + GuiInfoBox(s,"%s",mode_help[edit_mode]); + break; + } + + case GFX_TAB: + if (k.shift) + SetEditMode(PREV_MODE(edit_mode)); + else + SetEditMode(NEXT_MODE(edit_mode)); + break; + + case GFX_INSERT: + ObjectInsert(); + break; + + case GFX_DELETE: + ObjectDelete(); + break; + + case GFX_HOME: + if (game==ZDOOM) + EditLumpMenu(); + else + GuiInfoBox("NOTE","LUMP manipulation only|" + "applies to ZDoom mode"); + break; + + case GFX_F9: + if (k.shift) + { + ListEmpty(selected); + + for(f=0;f<MapSize(map);f++) + { + o=MapElem(map,f); + + if (o->data) + { + if (o->select==SELECT_SELECTED) + SetSelect(f,SELECT_NONE); + else + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + } + } + } + else + ClearSelection(); + + FullRedraw(); + break; + + case GFX_F10: + if (k.shift) + { + SelectByType(); + FullRedraw(); + } + else if ((k.alt)||(k.ctrl)) + { + char title[128]; + char **pick; + ObjDesc *od; + int no,sel; + + od=ObjectOverlaid(XToMap(ms.x),YToMap(ms.y),&no); + + if (od) + { + pick=Grab(sizeof(char *)*(no+1)); + + for(f=0;f<no;f++) + pick[f]=od[f].detail; + + pick[no]=NULL; + + sprintf(title,"Select overlapping %s object",typename); + + sel=GUI_picklist(title,pick); + + if (sel!=-1) + { + if (!k.alt) + ClearSelection(); + + if (!InIntList(selected,sel)) + { + SetSelect(od[sel].no,SELECT_SELECTED); + ListAppend(selected,&od[sel].no); + } + } + + Release(pick); + Release(od); + + FullRedraw(); + } + } + else + { + ClearSelection(); + + for(f=0;f<MapSize(map);f++) + { + o=MapElem(map,f); + + if (o->data) + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + } + + FullRedraw(); + } + + break; + + case GFX_F11: + { + static int last_sel=0; + Iterator i; + int *n; + Object *o; + + if (ListSize(selected)) + { + if (k.shift) + last_sel--; + else + last_sel++; + + if (last_sel<0) + last_sel=ListSize(selected)-1; + + if (last_sel>=ListSize(selected)) + last_sel=0; + + i=ListIterator(selected); + + for(f=0;f<last_sel;f++) + i=IteratorNext(i); + + if ((n=IteratorData(i))) + { + o=MapElem(map,*n); + LocateObject(o->data); + } + + IteratorClear(i); + FullRedraw(); + } + break; + } + + case GFX_F3: + MergeWad(); + + /* In Multi mode we must notify the MultiMap that new vertexes + and things have been added. This should always be safe as + new objects are added at the end of the current objects (ie. + holes in the maps are not filled in), so the current + selection is safe. + */ + if (edit_mode==MULTI_MODE) + GenerateMultiMap(); + + break; + + case GFX_F4: + { + int tmp; + + tmp=TmpAddCurrent(); + + if (ListSize(selected)) + { + MoveObject(); + + if (tmp) + ListEmpty(selected); + } + break; + } + + case GFX_F5: + { + static double last_rot=0.0; + int tmp; + + tmp=TmpAddCurrent(); + + rotate_dialog[D_ROTATE].data.d=last_rot; + + if (ListSize(selected)) + { + if (GUI_dialog("Rotate",D_ROTATE_NO,rotate_dialog)) + { + last_rot=rotate_dialog[D_ROTATE].data.d; + RotateObject(-last_rot); + FullRedraw(); + } + + if (tmp) + ListEmpty(selected); + } + break; + } + + case GFX_F6: + { + static double last_scale=1.0; + int tmp; + + tmp=TmpAddCurrent(); + + scale_dialog[D_SCALE].data.d=last_scale; + + if (ListSize(selected)) + { + if (GUI_dialog("Scale",D_SCALE_NO,scale_dialog)) + { + ScaleObject(last_scale=scale_dialog[D_SCALE].data.d); + FullRedraw(); + } + + if (tmp) + ListEmpty(selected); + } + break; + } + + case GFX_F7: + if (k.shift) + { + static int last_tag=0; + int f; + Object *o; + + tag_dialog[D_TAG].data.i=last_tag; + + if (GUI_dialog("Tag number to select",D_TAG_NO,tag_dialog)) + { + last_tag=tag_dialog[D_TAG].data.i; + + for(f=0;f<MapSize(map);f++) + { + o=MapElem(map,f); + + if ((o->data)&&(ObjectHasTag(o->data,last_tag))) + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + } + + FullRedraw(); + } + } + else + { + static int last_tag=0; + int tmp; + + tmp=TmpAddCurrent(); + + tag_dialog[D_TAG].data.i=last_tag; + + if (ListSize(selected)) + { + if (GUI_dialog("Tag number",D_TAG_NO,tag_dialog)) + { + SetTagObject(last_tag=tag_dialog[D_TAG].data.i); + DrawObjectInfo(); + } + + if (tmp) + ListEmpty(selected); + } + } + break; + + case GFX_F8: + if (edit_mode!=MULTI_MODE) + { + static int last_no=0; + + objno_dialog[D_OBJNO].data.i=last_no; + + if (GUI_dialog("Object number",D_OBJNO_NO,objno_dialog)) + { + Object *o; + int f; + + f=objno_dialog[D_OBJNO].data.i; + o=MapElem(map,f); + + if ((o)&&(o->data)) + { + last_no=objno_dialog[D_OBJNO].data.i; + + if (k.shift) + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + + LocateObject(o->data); + GenericCheckMouse(); + FullRedraw(); + } + else + GuiInfoBox("ERROR","Object does not exist"); + } + } + break; + + case GFX_ESC: + quit=TRUE; + break; + + default: + break; + } + else + switch(toupper(k.ascii)) + { + case '3': + EditPreview3D(); + break; + + case 'R': + FullRedraw(); + break; + + case 'S': + SetEditMode(SECTOR_MODE); + break; + + case 'L': + SetEditMode(LINEDEF_MODE); + break; + + case 'V': + SetEditMode(VERTEX_MODE); + break; + + case 'T': + SetEditMode(THING_MODE); + break; + + case 'C': + SetEditMode(MULTI_MODE); + break; + + case '[': + { + int tmp; + + tmp=TmpAddCurrent(); + + if (ListSize(selected)) + { + RotateObject(5.0); + + if (tmp) + ListEmpty(selected); + + FullRedraw(); + } + + break; + } + + case ']': + { + int tmp; + + tmp=TmpAddCurrent(); + + if (ListSize(selected)) + { + RotateObject(-5.0); + + if (tmp) + ListEmpty(selected); + + FullRedraw(); + } + + break; + } + + case '{': + { + int tmp; + + tmp=TmpAddCurrent(); + + if (ListSize(selected)) + { + ScaleObject(0.90); + + if (tmp) + ListEmpty(selected); + + FullRedraw(); + } + + break; + } + + case '}': + { + int tmp; + + tmp=TmpAddCurrent(); + + if (ListSize(selected)) + { + ScaleObject(1.10); + + if (tmp) + ListEmpty(selected); + + FullRedraw(); + } + + break; + } + + default: + break; + } +} + + +void HandleMouse(GFXMouse m) +{ + VIDOOM_TRACE; + + memcpy(&ms,&m,sizeof(m)); + GenericCheckMouse(); + DrawHeader(); +} + + +/* END OF FILE */ diff --git a/editgui.c b/editgui.c new file mode 100644 index 0000000..c5f8e58 --- /dev/null +++ b/editgui.c @@ -0,0 +1,519 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + General editor GUI routines + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include "editvar.h" +#include "texture.h" +#include "sectors.h" +#include "specials.h" + +#include <stdarg.h> + +#define TXTPMT(lr,ulm) "Pick " ulm " texture for " lr " sidedef" + + +/* ---------------------------------------- GUI FUNCTIONS +*/ +int GetTexture(char *t,DirName d) +{ + int f; + + VIDOOM_TRACE; + + if ((f=GUI_image_picklist(t,texture_picklist,TXT_PICKDEFVAL)) + !=TXT_PICKDEFVAL) + { + strcpy(d,texture_picklist[f].text); + return(TRUE); + } + else + return(FALSE); +} + + +int GetFlat(char *t,DirName d) +{ + int f; + + VIDOOM_TRACE; + + if ((f=GUI_image_picklist(t,flat_picklist,TXT_PICKDEFVAL)) + !=TXT_PICKDEFVAL) + { + strcpy(d,flat_picklist[f].text); + return(TRUE); + } + else + return(FALSE); +} + + + +int GetLinedefValues(int set_class, + int r_floor, int l_floor, int r_ceiling, int l_ceiling, + int *type,int *flag,int *two, + DirName sr_upper,DirName sr_middle,DirName sr_lower, + DirName sl_upper,DirName sl_middle,DirName sl_lower) + +{ + VIDOOM_TRACE; + + if (set_class) + { + if (hexen_mode) + { + if ((*type=SelectSpecial())==SPECIAL_NULLID) + return(FALSE); + } + else + { + if ((*type=SelectLinedef())==LINEDEF_NULLID) + return(FALSE); + } + } + else + *type=0; + + if (ChooseLinedefType(flag,two)) + { + if (*two) + { + if (r_floor<l_floor) + { + if (!GetTexture(TXTPMT("RIGHT","LOWER"),sr_lower)) + return(FALSE); + } + else + strcpy(sr_lower,empty_texture); + + if (r_ceiling>l_ceiling) + { + if (!GetTexture(TXTPMT("RIGHT","UPPER"),sr_upper)) + return(FALSE); + } + else + strcpy(sr_upper,empty_texture); + + if (ask_middle_on_2sided) + { + if (!GetTexture(TXTPMT("RIGHT","MIDDLE"),sr_middle)) + return(FALSE); + } + else + strcpy(sr_middle,empty_texture); + + if (l_floor<r_floor) + { + if (!GetTexture(TXTPMT("LEFT","LOWER"),sl_lower)) + return(FALSE); + } + else + strcpy(sl_lower,empty_texture); + + if (l_ceiling>r_ceiling) + { + if (!GetTexture(TXTPMT("LEFT","UPPER"),sl_upper)) + return(FALSE); + } + else + strcpy(sl_upper,empty_texture); + + if (ask_middle_on_2sided) + { + if (!GetTexture(TXTPMT("LEFT","MIDDLE"),sl_middle)) + return(FALSE); + } + else + strcpy(sl_middle,empty_texture); + } + else + { + strcpy(sr_upper,empty_texture); + strcpy(sr_lower,empty_texture); + + if (!GetTexture("Pick MIDDLE texture for RIGHT sidedef",sr_middle)) + return(FALSE); + } + + return(TRUE); + } + + return(FALSE); +} + + +int GetSectorValues(int *line_type, int *line_flag, int *two_sided, + int *floor, int *ceiling, int *light, + int in_floor, int in_ceiling, + int *style_flags, + DirName floor_t, DirName ceiling_t, + DirName sr_upper, DirName sr_middle, DirName sr_lower, + DirName sl_upper, DirName sl_middle, DirName sl_lower) +{ + VIDOOM_TRACE; + + if (hexen_mode) + { + if ((*line_type=SelectSpecial())==SPECIAL_NULLID) + return(FALSE); + } + else + { + if ((*line_type=SelectLinedef())==LINEDEF_NULLID) + return(FALSE); + } + + sector_val_dialog[D_SVAL_LIGHT].data.i=*light; + sector_val_dialog[D_SVAL_FLOOR].data.i=*floor; + sector_val_dialog[D_SVAL_CEILING].data.i=*ceiling; + + if (!GUI_dialog("Values for sector",D_SECTOR_VAL_NO,sector_val_dialog)) + return(FALSE); + + *floor=sector_val_dialog[D_SVAL_FLOOR].data.i; + *ceiling=sector_val_dialog[D_SVAL_CEILING].data.i; + *light=sector_val_dialog[D_SVAL_LIGHT].data.i; + + if (!ChooseLinedefType(line_flag,two_sided)) + return(FALSE); + + if (NoSectorStyles()) + { + if (!ChooseSectorStyle(style_flags,sr_upper,sr_middle,sr_lower, + floor_t,ceiling_t)) + return(FALSE); + + if (*two_sided) + { + strcpy(sl_upper,sr_upper); + strcpy(sl_middle,sr_middle); + strcpy(sl_lower,sr_lower); + + if (*floor>=in_floor) + strcpy(sr_lower,empty_texture); + + if (*ceiling<=in_ceiling) + strcpy(sr_upper,empty_texture); + + if (!ask_middle_on_2sided) + strcpy(sr_middle,empty_texture); + + if (in_floor>=*floor) + strcpy(sl_lower,empty_texture); + + if (in_ceiling<=*ceiling) + strcpy(sl_upper,empty_texture); + + if (!ask_middle_on_2sided) + strcpy(sl_middle,empty_texture); + } + else + { + strcpy(sr_upper,empty_texture); + strcpy(sr_lower,empty_texture); + } + } + else + { + *style_flags=0; + + if (!GetFlat("Choose floor texture",floor_t)) + return(FALSE); + + if (!GetFlat("Choose ceiling texture",ceiling_t)) + return(FALSE); + + if (*two_sided) + { + if (*floor<in_floor) + { + if (!GetTexture(TXTPMT("RIGHT","LOWER"),sr_lower)) + return(FALSE); + } + else + strcpy(sr_lower,empty_texture); + + if (*ceiling>in_ceiling) + { + if (!GetTexture(TXTPMT("RIGHT","UPPER"),sr_upper)) + return(FALSE); + } + else + strcpy(sr_upper,empty_texture); + + if (ask_middle_on_2sided) + { + if (!GetTexture(TXTPMT("RIGHT","MIDDLE"),sr_middle)) + return(FALSE); + } + else + strcpy(sr_middle,empty_texture); + + if (in_floor<*floor) + { + if (!GetTexture(TXTPMT("LEFT","LOWER"),sl_lower)) + return(FALSE); + } + else + strcpy(sl_lower,empty_texture); + + if (in_ceiling>*ceiling) + { + if (!GetTexture(TXTPMT("LEFT","UPPER"),sl_upper)) + return(FALSE); + } + else + strcpy(sl_upper,empty_texture); + + if (ask_middle_on_2sided) + { + if (!GetTexture(TXTPMT("LEFT","MIDDLE"),sl_middle)) + return(FALSE); + } + else + strcpy(sl_middle,empty_texture); + } + else + { + strcpy(sr_upper,empty_texture); + strcpy(sr_lower,empty_texture); + + if (!GetTexture("Pick MIDDLE texture for RIGHT sidedef",sr_middle)) + return(FALSE); + } + } + + return(TRUE); +} + + +int PickPoint(char *p, int *x, int *y, void (*draw)(int *x,int *y), + void (*key)(GFXKey k), + void (*infobox)(char *p)) +{ + GFXEvent e; + int cx,cy; + int done; + int cancel; + + VIDOOM_TRACE; + + draw_current_info=FALSE; + FullRedraw(); + + done=FALSE; + cancel=FALSE; + + cx=XToMap(ms.x); + cy=YToMap(ms.y); + + GFX_set_XOR_mode(); + + if (draw) + draw(&cx,&cy); + else + { + cx=SnapX(cx); + cy=SnapY(cy); + GFX_line(0,MapToY(cy),SCRW-1,MapToY(cy),WHITE); + GFX_line(MapToX(cx),0,MapToX(cx),SCRH-1,WHITE); + } + + GFX_clear_XOR_mode(); + + if (infobox) + infobox(p); + else + GuiDrawInfoBox(p,GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,TRUE, + "Press button to select point|Press ESC to cancel"); + + GFX_redraw(); + + while(!done) + { + GFX_await_input_full(&e); + + switch(e.type) + { + case GFX_MOUSE_EVENT: + + GFX_set_XOR_mode(); + + if (draw) + draw(&cx,&cy); + else + { + GFX_line(0,MapToY(cy),SCRW-1,MapToY(cy),WHITE); + GFX_line(MapToX(cx),0,MapToX(cx),SCRH-1,WHITE); + } + + GFX_clear_XOR_mode(); + + memcpy(&ms,&e.mouse,sizeof(e.mouse)); + + if (ms.b) + { + done=TRUE; + GFX_bounce(); + } + + DrawHeader(); + cx=XToMap(ms.x); + cy=YToMap(ms.y); + + GFX_set_XOR_mode(); + + if (draw) + draw(&cx,&cy); + else + { + cx=SnapX(cx); + cy=SnapY(cy); + GFX_line(0,MapToY(cy),SCRW-1,MapToY(cy),WHITE); + GFX_line(MapToX(cx),0,MapToX(cx),SCRH-1,WHITE); + } + + GFX_clear_XOR_mode(); + + if (infobox) + infobox(p); + else + GuiDrawInfoBox(p,GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,TRUE, + "Press button to select point|Press ESC to cancel"); + + GFX_redraw(); + + break; + + case GFX_KEY_EVENT: + GFX_set_XOR_mode(); + + if (draw) + draw(&cx,&cy); + else + { + GFX_line(0,MapToY(cy),SCRW-1,MapToY(cy),WHITE); + GFX_line(MapToX(cx),0,MapToX(cx),SCRH-1,WHITE); + } + + GFX_clear_XOR_mode(); + + HandleMoveKey(e.key,FALSE); + cx=SnapX(XToMap(ms.x)); + cy=SnapY(YToMap(ms.y)); + + if (key) + key(e.key); + + GFX_set_XOR_mode(); + + if (draw) + draw(&cx,&cy); + else + { + GFX_line(0,MapToY(cy),SCRW-1,MapToY(cy),WHITE); + GFX_line(MapToX(cx),0,MapToX(cx),SCRH-1,WHITE); + } + + GFX_clear_XOR_mode(); + + if (infobox) + infobox(p); + else + GuiDrawInfoBox(p,GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,TRUE, + "Press button to select point|Press ESC to cancel"); + + GFX_redraw(); + + if (e.key.code==GFX_ESC) + { + done=TRUE; + cancel=TRUE; + } + + break; + + default: + break; + } + } + + draw_current_info=TRUE; + FullRedraw(); + + *x=cx; + *y=cy; + + return(!cancel); +} + + +int YesNo(char *fmt,...) +{ + static char s[512]; + va_list va; + + va_start(va,fmt); + vsprintf(s,fmt,va); + va_end(va); + + return(GUI_yesno(s)); +} + + +int YesNoAll(char *fmt,...) +{ + static char s[512]; + va_list va; + + va_start(va,fmt); + vsprintf(s,fmt,va); + va_end(va); + + return(GUI_yesno_all(s)); +} + + +int GetNumber(char *prompt, int *val) +{ + PLAT_DIALOG d[1]; + int ret; + + d[0].text="Value (prefix with 0x for hex)"; + d[0].type=PLAT_DIAL_STRING; + sprintf(d[0].data.s,"%d",*val); + + if ((ret=GUI_dialog(prompt,1,d))) + *val=ATOI(d[0].data.s); + + return(ret); +} + + +/* END OF FILE */ diff --git a/editilst.c b/editilst.c new file mode 100644 index 0000000..f7a6763 --- /dev/null +++ b/editilst.c @@ -0,0 +1,106 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor integer list routines + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" +#include "editvar.h" + + +/* ---------------------------------------- PRIVATE ROUTINES +*/ + + +/* ---------------------------------------- EXPORTED ROUTINES +*/ +int InIntList(List l, int i) +{ + Iterator li; + int *f; + + li=ListIterator(l); + + while(li) + { + f=IteratorData(li); + + if (*f==i) + { + IteratorClear(li); + return(TRUE); + } + + li=IteratorNext(li); + } + + return(FALSE); +} + + +void IntListUniqAdd(List l, int i) +{ + Iterator li; + int *f; + + li=ListIterator(l); + + while(li) + { + f=IteratorData(li); + + if (*f==i) + { + IteratorClear(li); + return; + } + + li=IteratorNext(li); + } + + ListAppend(l,&i); +} + + +void IntListRm(List l, int i) +{ + Iterator li; + int *f; + + li=ListIterator(l); + + while(li) + { + f=IteratorData(li); + + if (*f==i) + li=IteratorDelete(li); + else + li=IteratorNext(li); + } +} + + +/* END OF FILE */ diff --git a/editline.c b/editline.c new file mode 100644 index 0000000..69d61a3 --- /dev/null +++ b/editline.c @@ -0,0 +1,3812 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor LINEDEF definitions + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include <math.h> +#include <ctype.h> + +#include "editvar.h" +#include "sectors.h" +#include "texture.h" +#include "genlines.h" +#include "specials.h" +#include "flags.h" +#include "util.h" + + +/* ---------------------------------------- MACROS +*/ +#define LinesCrossV(v1,v2,v3,v4) \ + LinesCross ((v1)->v.x,(v1)->v.y,(v2)->v.x,(v2)->v.y, \ + (v3)->v.x,(v3)->v.y,(v4)->v.x,(v4)->v.y) + +#define SetPoint(pt,vt) do {(pt).x=(vt)->v.x;(pt).y=(vt)->v.y;} while(0) + +#define SPLINE_THRESH 1 + +/* This following define relies on the naming of parameters in DoCalcSpline() + and DoCalcLine() +*/ +#define CHECKSTEP(X,Y) do { \ + (*len)++; \ + \ + if (p) \ + if (((*len)%step)==0) \ + { \ + if ((*idx)<MAX_STEPS) \ + { \ + p[*idx].x=X; \ + p[*idx].y=Y; \ + (*idx)++; \ + } \ + } \ + } while(0) + +/* ---------------------------------------- STEP CREATION SHARED DATA +*/ +#define MIN_STEPS 1 +#define MAX_STEPS 128 + +struct { + int x; + int y; + + EditLine *from; + EditLine *to; + int from_c; + int to_c; + int from_f; + int to_f; + + int swap; + Point vf[2]; + Point vt[2]; + + int no; + double step_f; + double step_c; + int circular; + int side; + + Point p[2][MAX_STEPS+2]; + } step; + + +/* ---------------------------------------- PRIVATE UTILS +*/ +static void DrawLinedef(EditLine *l, int col) +{ + double dx,dy; + + VIDOOM_TRACE; + + dx=((l->v[0]->v.x)-(l->v[1]->v.x))/2; + dy=((l->v[0]->v.y)-(l->v[1]->v.y))/2; + + MapLineD(l,col); + + MapLine((int)(l->v[0]->v.x-dx),(int)(l->v[0]->v.y-dy), + (int)(l->v[0]->v.x-dx-dy/6),(int)(l->v[0]->v.y-dy+dx/6),col); +} + + +static int SelColour(int selmode) +{ + switch(selmode) + { + case SELECT_OVER: + return(OVERCOL); + break; + case SELECT_SELECTED: + return(SELCOL); + break; + default: + if (edit_mode==SECTOR_MODE) + return(SECTCOL); + else + return(LINECOL); + break; + } +} + + +static void RemoveHighlights(List l) +{ + Iterator i; + Object *o; + int *n; + + i=ListIterator(l); + + while(i) + { + n=IteratorData(i); + + if ((o=MapElem(linedef,*n))) + DrawLinedef(o->data,SelColour(o->select)); + + i=IteratorNext(i); + } +} + + +static void LineSelectionCentre(int *x,int *y) +{ + int *f; + EditLine *l; + Iterator i; + int min_x,min_y,max_x,max_y; + + VIDOOM_TRACE; + + i=ListIterator(selected); + + min_x=99999; + min_y=99999; + max_x=-99999; + max_y=-99999; + + while(i) + { + f=IteratorData(i); + l=GETLINE(*f); + + min_x=MIN(l->min_x,min_x); + min_y=MIN(l->min_y,min_y); + max_x=MAX(l->max_x,max_x); + max_y=MAX(l->max_y,max_y); + + i=IteratorNext(i); + } + + *x=min_x+(max_x-min_x)/2; + *y=min_y+(max_y-min_y)/2; +} + + +static void DeleteLeftSidedef(EditLine *l) +{ + Object *o; + int n; + int del; + int side; + EditLine *cl; + + VIDOOM_TRACE; + + if (l->l.left==-1) + return; + + l->sl=NULL; + side=l->l.left; + l->l.left=-1; + + /* Free up the sidedef if there is no other linedef + bound to it + */ + del=TRUE; + + for(n=0;(n<MapSize(linedef))&&(del);n++) + { + cl=GETLINE(n); + + if ((cl)&&((cl->l.left==side)||(cl->l.right==side))) + del=FALSE; + } + + if (del) + { + o=MapElem(sidedef,side); + Release(o->data); + o->data=NULL; + } +} + + +static void DeleteRightSidedef(EditLine *l) +{ + Object *o; + int n; + int del; + int side; + EditLine *cl; + + VIDOOM_TRACE; + + if (l->l.right==-1) + return; + + l->sr=NULL; + side=l->l.right; + l->l.right=-1; + + /* Free up the sidedef if there is no other linedef + bound to it + */ + del=TRUE; + + for(n=0;(n<MapSize(linedef))&&(del);n++) + { + cl=GETLINE(n); + + if ((cl)&&((cl->l.left==side)||(cl->l.right==side))) + del=FALSE; + } + + if (del) + { + o=MapElem(sidedef,side); + Release(o->data); + o->data=NULL; + } +} + + +static void DeleteLinedef(int f) +{ + Object *o; + EditLine *l; + + VIDOOM_TRACE; + + o=MapElem(linedef,f); + l=o->data; + DeleteLeftSidedef(l); + DeleteRightSidedef(l); + + IntListRm(l->v[0]->l,f); + IntListRm(l->v[1]->l,f); + + Release(o->data); + o->data=NULL; + o->select=SELECT_NONE; +} + + +static int SplitLinedef(int f) +{ + Object o; + EditLine *l,*nl; + Sidedef *nsl,*nsr; + EditVert *nv; + int n_nl,dx,dy,tw; + + VIDOOM_TRACE; + + l=GETLINE(f); + + o.select=SELECT_NONE; + + /* Create copies of the original linedefs and sidedefs + */ + nl=Grab(sizeof(EditLine)); + memcpy(nl,l,sizeof(EditLine)); + o.data=nl; + n_nl=MapSize(linedef); + nl->no=n_nl; + MapAdd(linedef,n_nl,&o); + + nsr=Grab(sizeof(Sidedef)); + memcpy(nsr,l->sr,sizeof(Sidedef)); + o.data=nsr; + nl->l.right=MapSize(sidedef); + MapAdd(sidedef,nl->l.right,&o); + nl->sr=GETSIDE(nl->l.right); + + if (l->l.left!=-1) + { + nsl=Grab(sizeof(Sidedef)); + memcpy(nsl,l->sl,sizeof(Sidedef)); + o.data=nsl; + nl->l.left=MapSize(sidedef); + MapAdd(sidedef,nl->l.left,&o); + nl->sl=GETSIDE(nl->l.left); + } + else + nsl=NULL; + + /* Work out the middle of the current line and create the vertex there + */ + nv=Grab(sizeof(EditVert)); + nv->l=ListNew(sizeof(int)); + + dx=((l->v[1]->v.x)-(l->v[0]->v.x))/2; + dy=((l->v[1]->v.y)-(l->v[0]->v.y))/2; + + nv->v.x=l->v[0]->v.x+dx; + nv->v.y=l->v[0]->v.y+dy; + o.data=nv; + + /* Update the vertex map, and the from/to in the old/new linedefs + */ + nl->l.from=MapSize(vertex); + MapAdd(vertex,nl->l.from,&o); + + l->l.to=nl->l.from; + + l->v[1]=GETVERT(l->l.to); + nl->v[0]=GETVERT(nl->l.from); + + /* Update the vertex lists + */ + IntListRm(nl->v[1]->l,f); + IntListUniqAdd(nl->v[0]->l,f); + IntListUniqAdd(nl->v[0]->l,n_nl); + + /* Align textures + */ + tw=CalcTextureWidth(nsr->upper,nsr->middle,nsr->lower); + + if (tw) + nsr->x=(l->sr->x+Len(l->v[0]->v.x,l->v[0]->v.y, + l->v[1]->v.x,l->v[1]->v.y))%tw; + else + nsr->x=0; + + if (nsl) + { + tw=CalcTextureWidth(nsl->upper,nsl->middle,nsl->lower); + if (tw) + nsl->x=(l->sl->x+Len(l->v[0]->v.x,l->v[0]->v.y, + l->v[1]->v.x,l->v[1]->v.y))%tw; + else + nsl->x=0; + } + + /* New linedef no + */ + return(n_nl); +} + + +/* ---------------------------------------- STEP CREATION CODE CALCULATIONS +*/ +static void DoCalcLine (int x0,int y0,int x1,int y1, + Point p[],int step,int *idx,int *len) +{ + int dx,dy,ix,iy,incre,incrne,d,x,y,ymode; + int p1x,p1y,p2x,p2y; + + VIDOOM_TRACE; + + p1x=x0; + p1y=y0; + p2x=x1; + p2y=y1; + + dx=p2x-p1x; + dy=p2y-p1y; + + ix=SGN(dx); + iy=SGN(dy); + + dx=ABS(dx); + dy=ABS(dy); + + if (dy>dx) + { + ymode=TRUE; + d=dx*2-dy; + incre=dx*2; + incrne=(dx-dy)*2; + } + else + { + ymode=FALSE; + d=dy*2-dx; + incre=dy*2; + incrne=(dy-dx)*2; + } + + x=p1x; + y=p1y; + + CHECKSTEP(x,y); + + if (ymode) + while(y!=p2y) + { + if (d<=0) + { + d+=incre; + y+=iy; + } + else + { + d+=incrne; + y+=iy; + x+=ix; + } + + CHECKSTEP(x,y); + } + else + while(x!=p2x) + { + if (d<=0) + { + d+=incre; + x+=ix; + } + else + { + d+=incrne; + y+=iy; + x+=ix; + } + + CHECKSTEP(x,y); + } +} + + +static void DoCalcSpline (int x0,int y0,int x1,int y1,int x2,int y2, + Point p[],int step,int *idx,int *len) +{ + int xa, ya, xb, yb, xc, yc, xp, yp; + + VIDOOM_TRACE; + + if ((x0==x1==x2)&&(y0==y1==y2)) + { + CHECKSTEP(x0,y0); + return; + } + + if ((x0==x1)&&(y0==y1)) + { + if (p) + DoCalcLine(x1,y1,x2,y2,p,step,idx,len); + else + *len+=Len(x1,y1,x2,y2); + return; + } + + if ((x0==x2)&&(y0==y2)) + { + if (p) + DoCalcLine(x0,y0,x1,y1,p,step,idx,len); + else + *len+=Len(x0,y0,x1,y1); + return; + } + + if ((x1==x2)&&(y1==y2)) + { + if (p) + DoCalcLine(x0,y0,x2,y2,p,step,idx,len); + else + *len+=Len(x0,y0,x2,y2); + return; + } + + xa = ( x0 + x1 ) / 2; + ya = ( y0 + y1 ) / 2; + xc = ( x1 + x2 ) / 2; + yc = ( y1 + y2 ) / 2; + xb = ( xa + xc ) / 2; + yb = ( ya + yc ) / 2; + + xp = ( x0 + xb ) / 2; + yp = ( y0 + yb ) / 2; + + if ( ABS( xa - xp ) + ABS( ya - yp ) > SPLINE_THRESH ) + DoCalcSpline(x0,y0,xa,ya,xb,yb,p,step,idx,len); + else + if (p) + DoCalcLine(x0,y0,xb,yb,p,step,idx,len); + else + *len+=Len(x0,y0,xb,yb); + + xp = ( x2 + xb ) / 2; + yp = ( y2 + yb ) / 2; + if ( ABS( xc - xp ) + ABS( yc - yp ) > SPLINE_THRESH ) + DoCalcSpline(xb,yb,xc,yc,x2,y2,p,step,idx,len); + else + if (p) + DoCalcLine(xb,yb,x2,y2,p,step,idx,len); + else + *len+=Len(xb,yb,x2,y2); +} + + +static void Spline(int x0,int y0, int x1, int y1, int x2, int y2, + Point p[],int step) +{ + int len; + int idx; + + VIDOOM_TRACE; + + len=0; + idx=0; + DoCalcSpline(x0,y0,x1,y1,x2,y2,p,step,&idx,&len); +} + + +static int SplineLen(int x0,int y0, int x1, int y1, int x2, int y2) +{ + int len; + + VIDOOM_TRACE; + + len=0; + DoCalcSpline(x0,y0,x1,y1,x2,y2,NULL,0,NULL,&len); + return (len); +} + + +static void CalcSteps(void) +{ + double dx,dy; + int f,r; + int len; + + VIDOOM_TRACE; + + step.step_f=((double)step.to_f-step.from_f)/(step.no+2); + step.step_c=((double)step.to_c-step.from_c)/(step.no+2); + + if (step.circular) + { + f=step.side; + + len=SplineLen(step.vf[f].x,step.vf[f].y,step.x,step.y, + step.vt[f].x,step.vt[f].y); + + len=(int)((double)len/((double)step.no+1.0)+1.0); + + if (len<1) + len=1; + + Spline(step.vf[f].x,step.vf[f].y,step.x,step.y, + step.vt[f].x,step.vt[f].y,&step.p[f][1],len); + + step.p[f][0].x=step.vf[f].x; + step.p[f][0].y=step.vf[f].y; + + step.p[f][step.no+1].x=step.vt[f].x; + step.p[f][step.no+1].y=step.vt[f].y; + } + else + for(f=0;f<2;f++) + { + dx=(double)(step.vt[f].x-step.vf[f].x)/(step.no+1); + dy=(double)(step.vt[f].y-step.vf[f].y)/(step.no+1); + + for(r=1;r<=step.no;r++) + { + step.p[f][r].x=(int)(step.vf[f].x+(dx*r)); + step.p[f][r].y=(int)(step.vf[f].y+(dy*r)); + } + + step.p[f][0].x=step.vf[f].x; + step.p[f][0].y=step.vf[f].y; + + step.p[f][step.no+1].x=step.vt[f].x; + step.p[f][step.no+1].y=step.vt[f].y; + } +} + + +int SectorInBox(Point tl, Point br) +{ + int x,y; + + x=tl.x+(br.x-tl.x)/2; + y=tl.y+(br.y-tl.y)/2; + + return (SectorHoldingPoint(x,y)); +} + + +/* ---------------------------------------- STEP CREATION CALLBACKS +*/ +static void PickDraw(int *x, int *y) +{ + int f; + + VIDOOM_TRACE; + + step.x=*x; + step.y=*y; + + if (step.circular) + CalcSteps(); + + for(f=0;f<2;f++) + MapLine(step.p[f][0].x,step.p[f][0].y, + step.p[f][1].x,step.p[f][1].y,LINECOL); + + for(f=1;f<=step.no;f++) + { + MapLine(step.p[0][f].x,step.p[0][f].y, + step.p[1][f].x,step.p[1][f].y,LINECOL); + + MapLine(step.p[0][f].x,step.p[0][f].y, + step.p[0][f+1].x,step.p[0][f+1].y,LINECOL); + + MapLine(step.p[1][f].x,step.p[1][f].y, + step.p[1][f+1].x,step.p[1][f+1].y,LINECOL); + } +} + + +static void PickKey(GFXKey k) +{ + VIDOOM_TRACE; + + if (k.code==GFX_ASCII) + switch(toupper(k.ascii)) + { + case 'C': + step.circular=!step.circular; + CalcSteps(); + break; + + case 'S': + step.side^=1; + CalcSteps(); + break; + + case 'R': + step.swap=!step.swap; + + if (!step.swap) + { + SetPoint(step.vf[0],step.from->v[0]); + SetPoint(step.vf[1],step.from->v[1]); + SetPoint(step.vt[0],step.to->v[0]); + SetPoint(step.vt[1],step.to->v[1]); + } + else + { + SetPoint(step.vf[0],step.from->v[0]); + SetPoint(step.vf[1],step.from->v[1]); + SetPoint(step.vt[0],step.to->v[1]); + SetPoint(step.vt[1],step.to->v[0]); + } + + CalcSteps(); + + if (step.circular) + { + step.side^=1; + CalcSteps(); + step.side^=1; + } + + break; + + case '+': + if (step.no<MAX_STEPS) + { + step.no++; + CalcSteps(); + + if (step.circular) + { + step.side^=1; + CalcSteps(); + step.side^=1; + } + } + break; + + case '-': + if (step.no>MIN_STEPS) + { + step.no--; + CalcSteps(); + + if (step.circular) + { + step.side^=1; + CalcSteps(); + step.side^=1; + } + } + break; + } +} + + +static void PickInfo(char *p) +{ + VIDOOM_TRACE; + + GuiDrawInfoBox(p,GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE, + "No : %d|" + "Step (F) : %d|" + "Step (C) : %d|" + "Mode : %-s|" + "Side : %d", + step.no,ABS((int)step.step_f),ABS((int)step.step_c), + step.circular ? "Curve " : "Straight", + step.side); + + GuiDrawInfoBox(p,GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "+ - increase number of steps|" + "- - decrease number of steps|" + "C - change between straight/curve|" + "S - change wall being moved in curve mode|" + "R - swap vertex matching|" + " |" + "Press left button to complete. ESC to cancel"); +} + + +/* ---------------------------------------- STEP CREATION CODE +*/ +typedef struct + { + int line; + int apply; + } TextureMatch; + +static void ApplyTextureOffset(List li,int tw,int right) +{ + int off; + Iterator i; + TextureMatch *tm; + EditLine *l; + Sidedef *s; + + VIDOOM_TRACE; + + i=ListIterator(li); + off=0; + + while(i) + { + tm=IteratorData(i); + + l=GETLINE(tm->line); + + if (tm->apply) + { + if (right) + s=l->sr; + else + s=l->sl; + + s->x=off; + } + + off=(off+Len(l->v[0]->v.x,l->v[0]->v.y,l->v[1]->v.x,l->v[1]->v.y))%tw; + + i=IteratorNext(i); + } +} + + +static int CreateSteps(int i_from, int i_to) +{ + EditSect *s; + EditLine *from,*to; + DirName upper,lower,middle,floor,ceiling; + int s_flag; + int type; + + VIDOOM_TRACE; + + from=GETLINE(i_from); + to=GETLINE(i_to); + + if ((Len(from->v[0]->v.x,from->v[0]->v.y, + from->v[1]->v.x,from->v[1]->v.y)==0)|| + (Len(to->v[0]->v.x,to->v[0]->v.y,to->v[1]->v.x,to->v[1]->v.y)==0)) + { + GuiInfoBox("ERROR","Lines must be non-zero length"); + return(FALSE); + } + + if (((from->l.from==to->l.from)&&(to->l.to==to->l.to))|| + ((from->l.from==to->l.to)&&(to->l.to==to->l.from))) + { + GuiInfoBox("ERROR","Lines cannot share a vertex"); + return(FALSE); + } + + if (!(LinesCrossV(from->v[0],to->v[0],from->v[1],to->v[1]))) + { + step.swap=FALSE; + SetPoint(step.vf[0],from->v[0]); + SetPoint(step.vf[1],from->v[1]); + SetPoint(step.vt[0],to->v[0]); + SetPoint(step.vt[1],to->v[1]); + } + else + { + step.swap=TRUE; + SetPoint(step.vf[0],from->v[0]); + SetPoint(step.vf[1],from->v[1]); + SetPoint(step.vt[0],to->v[1]); + SetPoint(step.vt[1],to->v[0]); + } + + step.x=ms.x; + step.y=ms.y; + step.from=from; + step.to=to; + step.no=1; + step.circular=FALSE; + step.side=0; + + s=GETSECT(from->sr->sector); + step.from_c=s->s.ceiling; + step.from_f=s->s.floor; + + s=GETSECT(to->sr->sector); + step.to_c=s->s.ceiling; + step.to_f=s->s.floor; + + CalcSteps(); + + if ((PickPoint("Create steps",&step.x,&step.y,PickDraw,PickKey,PickInfo))&& + (ChooseSectorStyle(&s_flag,upper,middle,lower,floor,ceiling))&& + ((type=SelectSector(hexen_mode))!=SECTOR_NULLID)) + { + Object o; + EditLine *el; + EditVert *ev[2][MAX_STEPS+2]; + EditSect *s; + Sidedef *ns; + TextureMatch tm; + List side_sl[2],side_sr[2]; + int n; + int v[2][MAX_STEPS+2]; + int do_ceiling; + double fh,ch; + int prev_fh,prev_ch; + int in_sect[MAX_STEPS],sect,prev_sect; + int nl,flags; + int offy_c,offy_f,tw,th; + int f,r; + + /* Create lists for mainting lines for afterward texture alignment + */ + for(f=0;f<2;f++) + { + side_sl[f]=ListNew(sizeof(TextureMatch)); + side_sr[f]=ListNew(sizeof(TextureMatch)); + } + + if ((do_ceiling=GUI_yesno("Move ceiling with steps?"))) + ch=step.from_c+step.step_c; + else + ch=MAX(step.to_c,step.from_c); + + fh=step.from_f+step.step_f; + + tw=CalcTextureWidth(upper,middle,lower); + + TextureSize(middle,NULL,&th); + + /* Create/get all the vertexes needed + */ + for(f=0;f<=step.no+1;f++) + { + if (f==0) + { + v[0][f]=step.from->l.from; + v[1][f]=step.from->l.to; + ev[0][f]=step.from->v[0]; + ev[1][f]=step.from->v[1]; + } + else if (f==step.no+1) + { + if (step.swap) + { + v[1][f]=step.to->l.from; + v[0][f]=step.to->l.to; + ev[1][f]=step.to->v[0]; + ev[0][f]=step.to->v[1]; + } + else + { + v[0][f]=step.to->l.from; + v[1][f]=step.to->l.to; + ev[0][f]=step.to->v[0]; + ev[1][f]=step.to->v[1]; + } + } + else + for(r=0;r<2;r++) + { + ev[r][f]=Grab(sizeof(EditVert)); + ev[r][f]->v.x=step.p[r][f].x; + ev[r][f]->v.y=step.p[r][f].y; + ev[r][f]->l=ListNew(sizeof(int)); + + o.data=ev[r][f]; + o.select=SELECT_NONE; + v[r][f]=MapSize(vertex); + MapAdd(vertex,-1,&o); + } + + /* While we're here, get all the containing sectors too + */ + in_sect[f]=SectorInBox(step.p[0][f],step.p[1][f+1]); + } + + /* Delete current left sidedefs on the anchor lines + */ + if (step.from->l.left!=-1) + DeleteLeftSidedef(step.from); + + if (step.to->l.left!=-1) + DeleteLeftSidedef(step.to); + + /* Set up the flags for the 1st line and all the previous sector and + height vars + */ + prev_fh=step.from_f; + prev_ch=step.from_c; + prev_sect=step.from->sr->sector; + offy_c=0; + offy_f=0; + + /* Create the steps up to the last one + */ + for(f=0;f<=step.no;f++) + { + if (in_sect[f]!=-1) + s=GETSECT(in_sect[f]); + else + s=NULL; + + sect=CreateUnboundSector + (type,(int)fh,(int)ch,default_light_level,0,floor,ceiling); + + /* Create the linedef at the start of the step. For the 1st step + we adjust the original linedef. Otherwise create a new one + */ + if (!f) + { + el=step.from; + el->l.flags|=side2_mask; + el->l.flags&=~block_mask; + + ns=Grab(sizeof(Sidedef)); + ns->x=0; + ns->y=0; + + el->l.flags|=upper_peg_mask; + el->l.flags&=~lower_peg_mask; + + if (prev_ch>(int)ch) + { + strcpy(el->sr->upper,upper); + strcpy(ns->upper,empty_texture); + } + else if (prev_ch<(int)ch) + { + strcpy(el->sr->upper,empty_texture); + strcpy(ns->upper,upper); + } + else + { + strcpy(el->sr->upper,empty_texture); + strcpy(ns->upper,empty_texture); + } + + if (prev_fh<(int)fh) + { + strcpy(el->sr->lower,lower); + strcpy(ns->lower,empty_texture); + } + else if (prev_fh>(int)fh) + { + strcpy(el->sr->lower,empty_texture); + strcpy(ns->lower,lower); + } + else + { + strcpy(el->sr->lower,empty_texture); + strcpy(ns->lower,empty_texture); + } + + strcpy(el->sr->middle,empty_texture); + strcpy(ns->middle,empty_texture); + + ns->sector=sect; + + o.data=ns; + o.select=SELECT_NONE; + + n=MapSize(sidedef); + MapAdd(sidedef,n,&o); + el->sl=ns; + el->l.left=n; + } + else + { + flags=side2_mask|upper_peg_mask; + + nl=CreateNewLinedef + (v[0][f],v[1][f],flags,normal_linedef,0,TRUE, + 0,0,prev_sect, + (prev_ch > (int)ch) ? upper : empty_texture, + empty_texture, + (prev_fh < (int)fh) ? lower : empty_texture, + 0,0,sect, + (prev_ch < (int)ch) ? upper : empty_texture, + empty_texture, + (prev_fh > (int)fh) ? lower : empty_texture); + + /* Add the new linedef to the vertex lists + */ + IntListUniqAdd(ev[0][f]->l,nl); + IntListUniqAdd(ev[1][f]->l,nl); + } + + /* Create the linedefs for the walls + */ + if (th) + { + offy_f-=(int)fh-prev_fh; + + while (offy_f<0) + offy_f+=th; + + offy_f%=th; + } + + if (do_ceiling) + if (th) + { + offy_c-=(int)ch-prev_ch; + + while (offy_c<0) + offy_c+=th; + + offy_c%=th; + } + + for(r=0;r<2;r++) + { + if (in_sect[f]==-1) + { + int offy; + + flags=block_mask; + + if (!do_ceiling) + { + flags|=upper_peg_mask; + offy=0; + } + else + offy=offy_c; + + nl=CreateNewLinedef(v[r][f+r],v[r][f+(r^1)], + flags,normal_linedef,0,FALSE, + 0,offy,sect, + empty_texture, + middle, + empty_texture, + 0,0,-1, + empty_texture, + empty_texture, + empty_texture); + + tm.line=nl; + + if (r==0) + { + tm.apply=TRUE; + ListAppend(side_sr[r],&tm); + tm.apply=FALSE; + ListInsert(side_sl[r],&tm); + } + else + { + tm.apply=TRUE; + ListInsert(side_sr[r],&tm); + tm.apply=FALSE; + ListAppend(side_sl[r],&tm); + } + } + else + { + int offy_l,offy_r; + + flags=side2_mask; + flags=side2_mask|lower_peg_mask|upper_peg_mask; + + if ((int)ch>s->s.ceiling) + offy_r=offy_c; + else if ((int)fh<s->s.floor) + offy_r=offy_c; + else + offy_r=0; + + offy_l=0; + + nl=CreateNewLinedef(v[r][f+r],v[r][f+(r^1)], + flags,normal_linedef,0,TRUE, + 0,offy_r,sect, + ((int)ch > s->s.ceiling) ? + upper : empty_texture, + empty_texture, + ((int)fh < s->s.floor) ? + lower : empty_texture, + 0,offy_l,in_sect[f], + ((int)ch < s->s.ceiling) ? + upper : empty_texture, + empty_texture, + ((int)fh > s->s.floor) ? + lower : empty_texture); + + tm.line=nl; + + if (r==0) + { + tm.apply=TRUE; + ListAppend(side_sr[r],&tm); + tm.apply=TRUE; + ListInsert(side_sl[r],&tm); + } + else + { + tm.apply=TRUE; + ListInsert(side_sr[r],&tm); + tm.apply=TRUE; + ListAppend(side_sl[r],&tm); + } + } + + /* Add the new linedef to the vertex lists + */ + IntListUniqAdd(ev[r][f]->l,nl); + IntListUniqAdd(ev[r][f+1]->l,nl); + } + + prev_fh=(int)fh; + prev_ch=(int)ch; + + fh+=step.step_f; + + if (do_ceiling) + ch+=step.step_c; + + prev_sect=sect; + } + + /* Adjust the end linedef anchor + */ + el=step.to; + el->l.flags|=side2_mask; + el->l.flags&=~block_mask; + + ns=Grab(sizeof(Sidedef)); + ns->x=0; + ns->y=0; + + if ((prev_ch!=step.to_c)&&(s_flag&SSTYLE_UPPER_PEG)) + el->l.flags|=upper_peg_mask; + else if (!(s_flag&SSTYLE_LEAVE_PEG)) + el->l.flags&=~upper_peg_mask; + + if ((prev_fh!=step.to_f)&&(s_flag&SSTYLE_LOWER_PEG)) + el->l.flags|=lower_peg_mask; + else if (!(s_flag&SSTYLE_LEAVE_PEG)) + el->l.flags&=~lower_peg_mask; + + if (prev_ch<step.to_c) + { + strcpy(el->sr->upper,upper); + strcpy(ns->upper,empty_texture); + } + else if (prev_ch>step.to_c) + { + strcpy(el->sr->upper,empty_texture); + strcpy(ns->upper,upper); + } + else + { + strcpy(el->sr->upper,empty_texture); + strcpy(ns->upper,empty_texture); + } + + if (prev_fh>step.to_f) + { + strcpy(el->sr->lower,lower); + strcpy(ns->lower,empty_texture); + } + else if (prev_fh<step.to_f) + { + strcpy(el->sr->lower,empty_texture); + strcpy(ns->lower,lower); + } + else + { + strcpy(el->sr->lower,empty_texture); + strcpy(ns->lower,empty_texture); + } + + strcpy(el->sr->middle,empty_texture); + strcpy(ns->middle,empty_texture); + + ns->sector=prev_sect; + + o.data=ns; + o.select=SELECT_NONE; + + n=MapSize(sidedef); + MapAdd(sidedef,n,&o); + el->sl=ns; + el->l.left=n; + + /* Calculate the texture offsets for the walls + */ + for(f=0;f<2;f++) + { + ApplyTextureOffset(side_sl[f],tw,FALSE); + ApplyTextureOffset(side_sr[f],tw,TRUE); + + ListClear(side_sl[f]); + ListClear(side_sr[f]); + } + + SectorCalcContainingAll(); + + return(TRUE); + } + else + return(FALSE); +} + + +/* ---------------------------------------- LINEDEF VECTOR MAP UTILS +*/ + +/* Note this is repeated, rather than shared with the sector vector map to save + any unexpected later interactions +*/ +static int vno=0; +static char *vmap=NULL; + +static void InitVMAP(void) +{ + VIDOOM_TRACE; + + if (vno<MapSize(vertex)) + vmap=ReGrab(vmap,MapSize(vertex)); + + memset(vmap,FALSE,MapSize(vertex)); +} + + +/* ---------------------------------------- SECTOR LINEDEF MAP UTILS +*/ +static int lno=0; +static char *lmap=NULL; + +static void InitLMAP(void) +{ + VIDOOM_TRACE; + + if (lno<MapSize(linedef)) + lmap=ReGrab(lmap,MapSize(linedef)); + + memset(lmap,FALSE,MapSize(linedef)); +} + + +/* ---------------------------------------- LINEDEF TRACK FUNCTIONS +*/ +List TrackLinedef(int line_no) +{ + EditSect *s; + EditLine *l; + List ll; + Iterator i; + int sec_no; + int done; + int f; + int vert_no; + + VIDOOM_TRACE; + + if (!(l=GETLINE(line_no))) + return(NULL); + + InitLMAP(); + + lmap[line_no]=TRUE; + + ll=ListNew(sizeof(int)); + ListAppend(ll,&line_no); + + sec_no=l->sr->sector; + + if (!(s=GETSECT(sec_no))) + return(ll); + + vert_no=l->l.to; + + /* If the start LINEDEF is 2-sided and wholy within the sector, we only + track along connected 2-sided lines in the same sector. + + If the start line is 1-sided then we allow both 1 and 2 sided LINEDEFs + in the resulting list, but only 2 sided LINEDEFs that just have this + sector on one side of the line. + + To make this easier we mark the LMAP entries for those lines we wish to + ignore before starting. + */ + if ((l->sl)&&(l->sl->sector==sec_no)) + { + i=ListIterator(s->all); + + while(i) + { + memcpy(&l,IteratorData(i),sizeof(l)); + + if (!l->sl) + lmap[l->no]=TRUE; + else + if (!((l->sl->sector==sec_no)&&(l->sr->sector==sec_no))) + lmap[l->no]=TRUE; + + i=IteratorNext(i); + } + } + else + { + i=ListIterator(s->all); + + while(i) + { + memcpy(&l,IteratorData(i),sizeof(l)); + + if ((l->sl)&&(l->sl->sector==sec_no)&&(l->sr->sector==sec_no)) + lmap[l->no]=TRUE; + + i=IteratorNext(i); + } + } + + done=FALSE; + + while(!done) + { + done=TRUE; + + i=ListIterator(s->all); + + while(i) + { + memcpy(&l,IteratorData(i),sizeof(l)); + + f=l->no; + + if (!lmap[f]) + { + /* We need two seperate checks, as obviously left sidedefs + pointing into the sector will need TO, rather than FROM, + checking + */ + if ((l->sr->sector==sec_no)&&(l->l.from==vert_no)) + { + lmap[f]=TRUE; + ListAppend(ll,&f); + done=FALSE; + vert_no=l->l.to; + } + else if ((l->sl)&&(l->sl->sector==sec_no)&&(l->l.to==vert_no)) + { + lmap[f]=TRUE; + ListAppend(ll,&f); + done=FALSE; + vert_no=l->l.from; + } + } + + i=IteratorNext(i); + } + } + + return(ll); +} + + +/* ---------------------------------------- LINEDEF MERGING +*/ +void CheckMergeLinedef(EditVert *v) +{ + Iterator i; + int *f; + int r; + EditLine *ol,*cl; + int same_dir; + int merge; + + VIDOOM_TRACE; + + i=ListIterator(v->l); + + while(i) + { + f=IteratorData(i); + + ol=GETLINE(*f); + + for(r=0;r<MapSize(linedef);r++) + if ((r!=*f)&&(cl=GETLINE(r))) + { + if (((cl->l.from==ol->l.from)&&(cl->l.to==ol->l.to))|| + ((cl->l.from==ol->l.to)&&(cl->l.to==ol->l.from))) + { + if ((cl->l.from==ol->l.from)&&(cl->l.to==ol->l.to)) + same_dir=TRUE; + else + same_dir=FALSE; + + if(merge_linedef==MERGE_ASK) + merge=YesNo("Linedefs %d and %d overlap. Merge?",*f,r); + else + merge=TRUE; + + if (merge) + { + if (same_dir) + { + if (cl->l.left!=-1) + { + DeleteLeftSidedef(ol); + ol->l.left=cl->l.left; + ol->sl=GETSIDE(ol->l.left); + ol->l.flags|=side2_mask; + + if (auto_block_linedefs) + ol->l.flags&=~block_mask; + } + } + else + { + DeleteLeftSidedef(ol); + ol->l.left=cl->l.right; + ol->sl=GETSIDE(ol->l.left); + ol->l.flags|=side2_mask; + + if (auto_block_linedefs) + ol->l.flags&=~block_mask; + } + + DeleteLinedef(r); + } + } + } + + i=IteratorNext(i); + } +} + + +/* ---------------------------------------- LINEDEF CREATION +*/ +int CreateNewLinedef(int from, int to, + int flags, int type, int tag, int two_sided, + int sr_ox, int sr_oy, int sr_sector, + DirName sr_upper, DirName sr_middle, DirName sr_lower, + int sl_ox, int sl_oy, int sl_sector, + DirName sl_upper, DirName sl_middle, DirName sl_lower) +{ + Object o; + EditLine *l; + Sidedef *s; + int ln,srn,sln,f; + + VIDOOM_TRACE; + + ln=MapSize(linedef); + + srn=MapSize(sidedef); + + if (two_sided) + sln=srn+1; + else + sln=-1; + + o.select=SELECT_NONE; + + s=Grab(sizeof(Sidedef)); + s->x=sr_ox; + s->y=sr_oy; + s->sector=sr_sector; + strcpy(s->upper,sr_upper); + strcpy(s->middle,sr_middle); + strcpy(s->lower,sr_lower); + o.data=s; + MapAdd(sidedef,srn,&o); + + if (sln!=-1) + { + s=Grab(sizeof(Sidedef)); + s->x=sl_ox; + s->y=sl_oy; + s->sector=sl_sector; + strcpy(s->upper,sl_upper); + strcpy(s->middle,sl_middle); + strcpy(s->lower,sl_lower); + o.data=s; + MapAdd(sidedef,sln,&o); + } + + l=Grab(sizeof(EditLine)); + + l->no=ln; + + l->l.from=from; + l->l.to=to; + + l->l.flags=flags; + l->l.type=type; + l->l.tag=tag; + + for(f=0;f<5;f++) + l->l.args[f]=0; + + l->v[0]=GETVERT(l->l.from); + l->v[1]=GETVERT(l->l.to); + + l->l.right=srn; + l->l.left=sln; + + l->sr=GETSIDE(srn); + + if (sln!=-1) + l->sl=GETSIDE(sln); + else + l->sl=NULL; + + if (edit_mode==LINEDEF_MODE) + switch(insert_select) + { + case HOVER_NONE: + o.select=SELECT_NONE; + break; + case HOVER_ADD: + case HOVER_SINGLE: + o.select=SELECT_SELECTED; + break; + } + else + o.select=SELECT_NONE; + + LineCalcBounding(l); + o.data=l; + MapAdd(linedef,ln,&o); + + if (edit_mode==LINEDEF_MODE) + switch(insert_select) + { + case HOVER_NONE: + break; + case HOVER_ADD: + ListAppend(selected,&ln); + break; + case HOVER_SINGLE: + ClearSelection(); + o.select=SELECT_SELECTED; + ListAppend(selected,&ln); + FullRedraw(); + break; + } + + return(ln); +} + + +/* ---------------------------------------- HEXEN TAG RESOLVING +*/ +void SetHexenLinedefTag(EditLine *l) +{ + char *nm; + char *a[5]; + int f; + + l->l.tag=0; + + nm=SpecialName(l->l.type,a); + + if (!STREQ(nm,"UNKNOWN")) + for(f=0;f<5;f++) + if (STREQ(a[f],"tag")) + { + l->l.tag=l->l.args[f]; + f=6; + } +} + + +/* ---------------------------------------- GENERIC LINEDEF FUNCS +*/ +int PositionOnObject_LINEDEF(int x,int y,void *data) +{ + EditLine *l; + + VIDOOM_TRACE; + + l=data; + + /* + if (scale<4) + box=BOXBOUND_FUZZY(x,y,l->min_x,l->min_y,l->max_x,l->max_y,2); + else + box=BOXBOUND_FUZZY(x,y,l->min_x,l->min_y,l->max_x,l->max_y,scale/2); + + if (!box) + return(FALSE); + */ + + if(LinesCross(x-(scale*linedef_select),y,x+(scale*linedef_select),y, + l->v[0]->v.x,l->v[0]->v.y, + l->v[1]->v.x,l->v[1]->v.y)) + return(TRUE); + + return(LinesCross(x,y-(scale*linedef_select),x,y+(scale*linedef_select), + l->v[0]->v.x,l->v[0]->v.y, + l->v[1]->v.x,l->v[1]->v.y)); +} + + +void SelectBox_LINEDEF(int x1,int y1,int x2,int y2) +{ + Object *o; + EditLine *l; + int f; + + VIDOOM_TRACE; + + for(f=0;f<MapSize(linedef);f++) + { + o=MapElem(linedef,f); + + if ((l=o->data)) + { + if ((o->select!=SELECT_SELECTED)&& + (BOXBOUND(l->v[0]->v.x,l->v[0]->v.y,x1,y1,x2,y2))&& + (BOXBOUND(l->v[1]->v.x,l->v[1]->v.y,x1,y1,x2,y2))) + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + } + } +} + + +void SelectByType_LINEDEF(void) +{ + Object *o; + EditLine *l; + int id; + int f; + + if (hexen_mode) + { + if ((id=SelectSpecial())==SPECIAL_NULLID) + id=LINEDEF_NULLID; + } + else + id=SelectLinedef(); + + + if (id!=LINEDEF_NULLID) + { + ClearSelection(); + + for(f=0;f<MapSize(linedef);f++) + if ((o=MapElem(linedef,f))) + if ((l=o->data)&&(l->l.type==id)) + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + } +} + + +void DrawObject_LINEDEF(void *data, int selmode) +{ + static List hilite=NULL; + static int drawn=FALSE; + Iterator i; + EditLine *l; + EditLine *sl=NULL; + EditSect *s; + Object *o; + int f; + + VIDOOM_TRACE; + + if (!hilite) + hilite=ListNew(sizeof(int)); + + l=data; + + /* If the current object has changed, remove the highlighted tag objects + */ + if ((current==-1)&&(drawn)) + { + drawn=FALSE; + RemoveHighlights(hilite); + ListEmpty(hilite); + } + + /* Draw tagged sectors + */ + if ((tag_highlight)&&(current==l->no)) + { + RemoveHighlights(hilite); + ListEmpty(hilite); + + if (l->l.tag) + { + drawn=TRUE; + + for(f=0;f<MapSize(sector);f++) + if ((s=GETSECT(f))) + if ((s->s.tag==l->l.tag)&&(SectorOnDisplay(s))) + { + i=ListIterator(s->all); + + while(i) + { + memcpy(&sl,IteratorData(i),sizeof(sl)); + o=MapElem(linedef,sl->no); + + if (o->select==SELECT_NONE) + { + ListAppend(hilite,&(sl->no)); + DrawLinedef(sl,TAGCOL); + } + + i=IteratorNext(i); + } + } + } + + } + + /* Only draw the line if it's not been drawn tagged or is selected. + */ + if ((!tag_highlight)||(selmode==SELECT_SELECTED)|| + (!InIntList(hilite,l->no))) + DrawLinedef(data,SelColour(selmode)); +} + + +void DrawObjectInfo_LINEDEF(void) +{ + EditLine *l; + char *p; + char *nm=NULL; + char *a[5]; + + VIDOOM_TRACE; + + if (!draw_current_info) + return; + + if ((current!=-1)&&((l=GETLINE(current)))) + { + if (hexen_mode) + nm=SpecialName(l->l.type,a); + + if (show_full_linedef_info) + { + if (hexen_mode) + GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, + "Linedef No : %-5d|" + "Flags : %s|" + "Special : %-35.35s|" + "%-10.10s : %-3d|" + "%-10.10s : %-3d|" + "%-10.10s : %-3d|" + "%-10.10s : %-3d|" + "%-10.10s : %-3d", + current, + FlagText(hexen_mode,LINEDEF_FLAGS, + l->l.flags), + nm, + (a[0] ? a[0] : "N/A"),l->l.args[0], + (a[1] ? a[1] : "N/A"),l->l.args[1], + (a[2] ? a[2] : "N/A"),l->l.args[2], + (a[3] ? a[3] : "N/A"),l->l.args[3], + (a[4] ? a[4] : "N/A"),l->l.args[4]); + else + GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, + "Linedef No : %-5d|" + "Flags : %s|" + "Type [%4.4x]: %-35.35s|" + "Tag : %-5d", + current, + FlagText(hexen_mode, + LINEDEF_FLAGS,l->l.flags), + l->l.type, + (p=GenLineName(hexen_mode,l->l.type, + LinedefName)) ? + p:LinedefName(l->l.type), + l->l.tag); + } + else + { + if (hexen_mode) + { + GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, + "Linedef No : %-5d|" + "Flags : %s|" + "Special : %-20.20s|" + "%-10.10s : %-3d|" + "%-10.10s : %-3d|" + "%-10.10s : %-3d|" + "%-10.10s : %-3d|" + "%-10.10s : %-3d", + current, + FlagText(hexen_mode,LINEDEF_FLAGS, + l->l.flags), + nm, + (a[0] ? a[0] : "N/A"),l->l.args[0], + (a[1] ? a[1] : "N/A"),l->l.args[1], + (a[2] ? a[2] : "N/A"),l->l.args[2], + (a[3] ? a[3] : "N/A"),l->l.args[3], + (a[4] ? a[4] : "N/A"),l->l.args[4]); + } + else + { + if (GenLineClass(hexen_mode,l->l.type,LinedefClass)) + GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, + "Linedef No : %-5d|" + "Flags : %s|" + "Type [%4.4x]: %-14.14s [Gen]|" + "Tag : %-5d", + current, + FlagText(hexen_mode,LINEDEF_FLAGS, + l->l.flags), + l->l.type, + GenLineClass(hexen_mode, + l->l.type,LinedefClass), + l->l.tag); + else + GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, + "Linedef No : %-5d|" + "Flags : %s|" + "Type [%4.4x]: %-20.20s|" + "Tag : %-5d", + current, + FlagText(hexen_mode,LINEDEF_FLAGS, + l->l.flags), + l->l.type, + LinedefClass(l->l.type), + l->l.tag); + } + } + + GuiDrawInfoBox("RIGHT SIDEDEF",GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE, + "Offset : %d,%d|" + "Upper : %-10s|" + "Middle : %-10s|" + "Lower : %-10s|" + "Sector : %d", + l->sr->x,l->sr->y, + l->sr->upper,l->sr->middle,l->sr->lower, + l->sr->sector); + + if (l->sl) + GuiDrawInfoBox("LEFT SIDEDEF",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "Offset : %d,%d|" + "Upper : %-10s|" + "Middle : %-10s|" + "Lower : %-10s|" + "Sector : %d", + l->sl->x,l->sl->y, + l->sl->upper,l->sl->middle,l->sl->lower, + l->sl->sector); + else + GuiDrawInfoBox("NO LEFT SIDEDEF",GUI_FLUSH_LEFT, + GUI_FLUSH_LOWER,FALSE, + " |" + " %-10s|" + " |" + " |" + " ",""); + } + else + { + if (show_full_linedef_info) + if (hexen_mode) + GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, + "Linedef No : -|" + "Flags : -|" + "Special : %35s|" + "- : -|" + "- : -|" + "- : -|" + "- : -|" + "- : -",""); + else + GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, + "Linedef No : -|" + "Flags : -|" + "Type : %35s|" + "Tag : -",""); + else + if (hexen_mode) + GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, + "Linedef No : -|" + "Flags : -|" + "Special : %20s|" + "- : -|" + "- : -|" + "- : -|" + "- : -|" + "- : -",""); + else + GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, + "Linedef No : -|" + "Flags : -|" + "Type : %20s|" + "Tag : -",""); + + GuiDrawInfoBox("RIGHT SIDEDEF",GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE, + " |" + " %-10s|" + " |" + " |" + " ",""); + + GuiDrawInfoBox("LEFT SIDEDEF",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + " |" + " %-10s|" + " |" + " |" + " ",""); + } +} + + +void DrawObjectHeader_LINEDEF(void) +{ + if (tag_highlight) + GFX_print(0,FH,WHITE,"Tag highlight: ON"); + else + GFX_print(0,FH,WHITE,"Tag highlight: OFF"); +} + + +void MoveObject_LINEDEF(void) +{ + GFXEvent ev; + int omx,omy; + int dmx,dmy; + int done; + Iterator i; + Iterator i2; + EditLine *l; + List orig; + int *f,r; + Point p; + unsigned char *vmap; + int cancel; + + VIDOOM_TRACE; + + omx=SnapX(XToMap(ms.x)); + omy=SnapY(YToMap(ms.y)); + GFX_bounce(); + + done=FALSE; + cancel=FALSE; + orig=ListNew(sizeof(Point)); + + vmap=Grab(MapSize(vertex)); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + l=GETLINE(*f); + p.x=l->v[0]->v.x; + p.y=l->v[0]->v.y; + ListAppend(orig,&p); + p.x=l->v[1]->v.x; + p.y=l->v[1]->v.y; + ListAppend(orig,&p); + i=IteratorNext(i); + } + + while(!done) + { + GuiDrawInfoBox("Move LINEDEF",GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE, + "Left mouse button to place|ESC to cancel"); + + GFX_redraw(); + GFX_await_input_full(&ev); + + switch(ev.type) + { + case GFX_KEY_EVENT: + HandleMoveKey(ev.key,FALSE); + + /* Cancel the move + */ + if (ev.key.code==GFX_ESC) + { + cancel=TRUE; + + i=ListIterator(selected); + i2=ListIterator(orig); + + while(i2) + { + f=IteratorData(i); + + l=GETLINE(*f); + + memcpy(&p,IteratorData(i2),sizeof(Point)); + l->v[0]->v.x=p.x; + l->v[0]->v.y=p.y; + i2=IteratorNext(i2); + + memcpy(&p,IteratorData(i2),sizeof(Point)); + l->v[1]->v.x=p.x; + l->v[1]->v.y=p.y; + i2=IteratorNext(i2); + + i=IteratorNext(i); + } + + done=TRUE; + dmx=0; + dmy=0; + } + else + { + dmx=SnapX(XToMap(ms.x))-omx; + dmy=SnapY(YToMap(ms.y))-omy; + } + + break; + + case GFX_MOUSE_EVENT: + memcpy(&ms,&ev.mouse,sizeof(ev.mouse)); + + if (ms.b&GFX_BUTLEFT) + done=TRUE; + + dmx=SnapX(XToMap(ms.x))-omx; + dmy=SnapY(YToMap(ms.y))-omy; + break; + + default: + dmx=0; + dmy=0; + break; + } + + if ((dmx)||(dmy)) + { + for(r=0;r<MapSize(vertex);r++) + vmap[r]=FALSE; + + i=ListIterator(selected); + while(i) + { + f=IteratorData(i); + + l=GETLINE(*f); + + if (!vmap[l->l.from]) + { + l->v[0]->v.x+=dmx; + l->v[0]->v.y+=dmy; + vmap[l->l.from]=TRUE; + } + + if (!vmap[l->l.to]) + { + l->v[1]->v.x+=dmx; + l->v[1]->v.y+=dmy; + vmap[l->l.to]=TRUE; + } + + i=IteratorNext(i); + } + + omx=SnapX(XToMap(ms.x)); + omy=SnapY(YToMap(ms.y)); + + FullRedraw(); + } + } + + ListClear(orig); + Release(vmap); + + if ((clear_on_move)&&(!cancel)) + ClearSelection(); + + FullRedraw(); +} + + +void RotateObject_LINEDEF(double angle) +{ + int cx,cy; + int x,y; + int *f; + Iterator i; + EditLine *l; + + VIDOOM_TRACE; + + InitVMAP(); + + LineSelectionCentre(&cx,&cy); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + l=GETLINE(*f); + + if (!vmap[l->l.from]) + { + vmap[l->l.from]=TRUE; + x=l->v[0]->v.x; + y=l->v[0]->v.y; + + Rotate(cx,cy,&x,&y,angle); + + l->v[0]->v.x=x; + l->v[0]->v.y=y; + } + + if (!vmap[l->l.to]) + { + vmap[l->l.to]=TRUE; + x=l->v[1]->v.x; + y=l->v[1]->v.y; + + Rotate(cx,cy,&x,&y,angle); + + l->v[1]->v.x=x; + l->v[1]->v.y=y; + } + + i=IteratorNext(i); + } +} + + +void ScaleObject_LINEDEF(double scale) +{ + int cx,cy; + int x,y; + int *f; + Iterator i; + EditLine *l; + + VIDOOM_TRACE; + + LineSelectionCentre(&cx,&cy); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + l=GETLINE(*f); + + x=l->v[0]->v.x; + y=l->v[0]->v.y; + + Scale(cx,cy,&x,&y,scale); + + l->v[0]->v.x=x; + l->v[0]->v.y=y; + + x=l->v[1]->v.x; + y=l->v[1]->v.y; + + Scale(cx,cy,&x,&y,scale); + + l->v[1]->v.x=x; + l->v[1]->v.y=y; + + i=IteratorNext(i); + } +} + + +void SetTagObject_LINEDEF(int tag) +{ + int *f; + Iterator i; + EditLine *l; + + VIDOOM_TRACE; + + if (hexen_mode) + return; + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + l=GETLINE(*f); + l->l.tag=tag; + i=IteratorNext(i); + } +} + + +void LocateObject_LINEDEF(void *obj) +{ + int cx,cy; + EditLine *l; + + VIDOOM_TRACE; + + l=obj; + cx=l->min_x+(l->max_x-l->min_x)/2; + cy=l->min_y+(l->max_y-l->min_y)/2; + ox=cx-scale*SCRW/2; + oy=cy+scale*SCRH/2; +} + + +void ObjectInsert_LINEDEF(void) +{ + int type; + int fl,two; + DirName sr_upper,sr_lower,sr_middle; + DirName sl_upper,sl_lower,sl_middle; + int ok; + int from,to; + int nl; + EditVert *v; + + VIDOOM_TRACE; + + if (MapSize(vertex)<2) + GuiInfoBox("ERROR","Need more that 2 vertices|to make a line"); + else + { + vertex_dialog[D_VERTEX_FROM].data.i=MapSize(vertex)-2; + vertex_dialog[D_VERTEX_TO].data.i=MapSize(vertex)-1; + + ok=GUI_dialog("Create LINEDEF",D_VERTEX_NO,vertex_dialog); + + if (ok) + { + from=vertex_dialog[D_VERTEX_FROM].data.i; + to=vertex_dialog[D_VERTEX_TO].data.i; + + if ((from>=MapSize(vertex))||(from<0)||(!GETVERT(from))) + { + GuiInfoBox("ERROR","%d (from) is an invalid vertex",from); + ok=FALSE; + } + else if ((to>=MapSize(vertex))||(to<0)||(!GETVERT(to))) + { + GuiInfoBox("ERROR","%d (to) is an invalid vertex",to); + ok=FALSE; + } + } + + if (ok) + if (GetLinedefValues(TRUE,0,0,0,0, + &type,&fl,&two, + sr_upper,sr_middle,sr_lower, + sl_upper,sl_middle,sl_lower)) + { + nl=CreateNewLinedef(from,to, + fl,type,0,two, + 0,0,0, + sr_upper,sr_middle,sr_lower, + 0,0,0, + sl_upper,sl_middle,sl_lower); + + v=GETVERT(from); + IntListUniqAdd(v->l,from); + v=GETVERT(to); + IntListUniqAdd(v->l,from); + + FullRedraw(); + } + } +} + + +void ObjectDelete_LINEDEF(void) +{ + Iterator i; + int *f; + + VIDOOM_TRACE; + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + DeleteLinedef(*f); + i=IteratorNext(i); + } + + SectorCalcContainingAll(); + ClearSelection(); + FullRedraw(); +} + + +void ObjectMenu_LINEDEF(void) +{ + static int ax=0; + static int ay=0; + EditLine *l; + int f,*s; + Iterator i; + int cancel; + int opt; + + VIDOOM_TRACE; + + s=NULL; + cancel=FALSE; + GFX_redraw(); + + if (hexen_mode) + opt=GUI_menu("Linedef/sidedef",ms.x,ms.y, + linedef_popup_hexen,GUI_CANCEL); + else + opt=GUI_menu("Linedef/sidedef",ms.x,ms.y,linedef_popup,GUI_CANCEL); + + switch(opt) + { + case TM_TYPE: + { + f=SelectLinedef(); + + if (f!=LINEDEF_NULLID) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + l->l.type=f; + i=IteratorNext(i); + } + + FullRedraw(); + } + break; + } + + case TM_SPECIAL: + { + Short id; + + id=(Short)SelectSpecial(); + + if (id!=SPECIAL_NULLID) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + l->l.type=id; + SetHexenLinedefTag(l); + i=IteratorNext(i); + } + + FullRedraw(); + } + break; + } + + case TM_GEN_TYPE: + if (GenLineNoClasses(hexen_mode)) + { + l=SelHead(); + f=SelectGenLine(hexen_mode,l->l.type); + + if (f!=GENLINE_NULLID) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + l->l.type=f; + i=IteratorNext(i); + } + + FullRedraw(); + } + } + else + GuiInfoBox("ERROR","No generalised linedefs defined|in config"); + break; + + case TM_VAL: + { + static int id; + + if (GetNumber("Enter LINEDEF type",&id)) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + l->l.type=id; + i=IteratorNext(i); + } + + FullRedraw(); + } + } + break; + + case TM_FLAGS: + l=SelHead(); + f=l->l.flags; + + if (SelectFlags(hexen_mode,LINEDEF_FLAGS,&f)) + { + int new_side; + List new; + + new=ListNew(sizeof(int)); + new_side=FALSE; + + i=ListIterator(selected); + + while(i) + { + int new_f; + + new_f=f; + s=IteratorData(i); + l=GETLINE(*s); + + /* If the line was 2 sided, and is now one - removed the + left sidedef + */ + if ((l->l.flags&side2_mask)&&(!(new_f&side2_mask))) + { + DeleteLeftSidedef(l); + + if (auto_block_linedefs) + new_f|=block_mask; + } + + /* If the line was 1 sided, and is now two - add the + left sidedef + */ + if ((!(l->l.flags&side2_mask))&&(new_f&side2_mask)) + { + Object o; + Sidedef *ns; + int n; + + ns=Grab(sizeof(Sidedef)); + ns->x=0; + ns->y=0; + strcpy(ns->upper,empty_texture); + strcpy(ns->middle,empty_texture); + strcpy(ns->lower,empty_texture); + ns->sector=-1; + + o.data=ns; + o.select=SELECT_NONE; + + n=MapSize(sidedef); + MapAdd(sidedef,n,&o); + l->sl=ns; + l->l.left=n; + + new_side=TRUE; + + ListAppend(new,s); + + if (auto_block_linedefs) + new_f&=~block_mask; + } + + l->l.flags=new_f; + i=IteratorNext(i); + } + + if (new_side) + switch(new_2sided_select) + { + case NEWSELECT_NEVER: + GuiInfoBox("NOTICE","Left SIDEDEFS have been added|" + "to some or all LINEDEFS"); + break; + + /* WARNING: This case cascades into the following + one + */ + case NEWSELECT_ASK: + if (!YesNo("New left SIDEDEFS created. " + "Select those LINEDEFS?")) + break; + case NEWSELECT_SELECT: + { + Object *o; + + new_selection=TRUE; + ClearSelection(); + i=ListIterator(new); + + while(i) + { + s=IteratorData(i); + o=MapElem(linedef,*s); + SetSelect(*s,SELECT_SELECTED); + ListAppend(selected,s); + i=IteratorNext(i); + } + break; + } + } + + ListClear(new); + + FullRedraw(); + } + + break; + + case TM_SWAP_SIDES: + { + int tmp; + Sidedef *tmp_s; + + i=ListIterator(selected); + GUI_start_yesno_all(); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + + if (l->l.left!=-1) + { + tmp=l->l.left; + tmp_s=l->sl; + + l->sl=l->sr; + l->l.left=l->l.right; + + l->sr=tmp_s; + l->l.right=tmp; + + tmp=l->l.from; + l->l.from=l->l.to; + l->l.to=tmp; + + l->v[0]=GETVERT(l->l.from); + l->v[1]=GETVERT(l->l.to); + } + else if (YesNoAll("Swap one-sided linedefs?")) + { + tmp=l->l.from; + l->l.from=l->l.to; + l->l.to=tmp; + + l->v[0]=GETVERT(l->l.from); + l->v[1]=GETVERT(l->l.to); + } + + i=IteratorNext(i); + } + + SectorCalcContainingAll(); + FullRedraw(); + + break; + } + + case TM_SPLIT_LINE: + { + List new; + int no; + Object *o; + + new=ListNew(sizeof(int)); + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + no=SplitLinedef(*s); + ListAppend(new,&no); + i=IteratorNext(i); + } + + i=ListIterator(new); + + while(i) + { + s=IteratorData(i); + o=MapElem(linedef,*s); + SetSelect(*s,SELECT_SELECTED); + + ListAppend(selected,s); + i=IteratorNext(i); + } + + ListClear(new); + SectorCalcContainingAll(); + FullRedraw(); + + break; + } + + case TM_TRACK_LINE: + { + List new; + Object *o; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + i=IteratorNext(i); + } + + if ((new=TrackLinedef(*s))) + { + ClearSelection(); + i=ListIterator(new); + + while(i) + { + s=IteratorData(i); + + o=MapElem(linedef,*s); + SetSelect(*s,SELECT_SELECTED); + ListAppend(selected,s); + + i=IteratorNext(i); + } + + ListClear(new); + new_selection=TRUE; + FullRedraw(); + } + + break; + } + + case TM_STEPS: + if (ListSize(selected)!=2) + GuiInfoBox("ERROR","Just select 2 linedefs|for this function"); + else + { + int *l1,*l2; + + i=ListIterator(selected); + l1=IteratorData(i); + i=IteratorNext(i); + l2=IteratorData(i); + IteratorClear(i); + + cancel=CreateSteps(*l1,*l2); + FullRedraw(); + } + + break; + + + case TM_OFFSET_R: + l=SelHead(); + + offset_dialog[D_OFFSET_OFFX].data.i=l->sr->x; + offset_dialog[D_OFFSET_OFFY].data.i=l->sr->y; + + if (GUI_dialog("Right offset",D_OFFSET_NO,offset_dialog)) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + if (l->sr) + { + l->sr->x=offset_dialog[D_OFFSET_OFFX].data.i; + l->sr->y=offset_dialog[D_OFFSET_OFFY].data.i; + } + i=IteratorNext(i); + } + + i=IteratorClear(i); + FullRedraw(); + } + + break; + + case TM_ADJUST_R: + l=SelHead(); + + offset_dialog[D_OFFSET_OFFX].data.i=ax; + offset_dialog[D_OFFSET_OFFY].data.i=ay; + + if (GUI_dialog("Right offset adjustment",D_OFFSET_NO,offset_dialog)) + { + ax=offset_dialog[D_OFFSET_OFFX].data.i; + ay=offset_dialog[D_OFFSET_OFFY].data.i; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + if (l->sr) + { + l->sr->x+=ax; + l->sr->y+=ay; + } + i=IteratorNext(i); + } + + i=IteratorClear(i); + FullRedraw(); + } + + break; + + case TM_SECTOR_R: + l=SelHead(); + + sectorn_dialog[D_SECTORN_SECNO].data.i=l->sr->sector; + + if (GUI_dialog("Right sector",D_SECTORN_NO,sectorn_dialog)) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + if (l->sr) + l->sr->sector=sectorn_dialog[D_SECTORN_SECNO].data.i; + i=IteratorNext(i); + } + + i=IteratorClear(i); + SectorCalcContainingAll(); + FullRedraw(); + } + + break; + + case TM_UPPER_T_R: + { + DirName d; + + if ((GetTexture("Upper Right texture",d))) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + strcpy(l->sr->upper,d); + i=IteratorNext(i); + } + + FullRedraw(); + } + + break; + } + + case TM_MIDDLE_T_R: + { + DirName d; + + if ((GetTexture("Middle Right texture",d))) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + strcpy(l->sr->middle,d); + i=IteratorNext(i); + } + + FullRedraw(); + } + + break; + } + + case TM_LOWER_T_R: + { + DirName d; + + if ((GetTexture("Lower Right texture",d))) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + strcpy(l->sr->lower,d); + i=IteratorNext(i); + } + + FullRedraw(); + } + + break; + } + + case TM_ALIGN_R: + { + int tw; + int ox; + + l=SelHead(); + + tw=CalcTextureWidth(l->sr->upper,l->sr->middle,l->sr->lower); + + if (tw==0) + GuiInfoBox("ERROR","First selected linedef has no texture"); + else + { + ox=l->sr->x; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + + l=GETLINE(*s); + + l->sr->x=ox; + + ox=(ox+Len(l->v[0]->v.x,l->v[0]->v.y, + l->v[1]->v.x,l->v[1]->v.y))%tw; + + i=IteratorNext(i); + } + + FullRedraw(); + } + + } + break; + + case TM_TEXTURES_R: + l=SelHead(); + + strcpy(textures_dialog[D_TEXTURE_U].data.s,l->sr->upper); + strcpy(textures_dialog[D_TEXTURE_M].data.s,l->sr->middle); + strcpy(textures_dialog[D_TEXTURE_L].data.s,l->sr->lower); + + if (GUI_dialog("Right textures",D_TEXTURES_NO,textures_dialog)) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + + strcpy(l->sr->upper,textures_dialog[D_TEXTURE_U].data.s); + strcpy(l->sr->middle,textures_dialog[D_TEXTURE_M].data.s); + strcpy(l->sr->lower,textures_dialog[D_TEXTURE_L].data.s); + + i=IteratorNext(i); + } + + i=IteratorClear(i); + FullRedraw(); + } + break; + + + case TM_OFFSET_L: + l=SelHead(); + + if (l->sl) + { + offset_dialog[D_OFFSET_OFFX].data.i=l->sl->x; + offset_dialog[D_OFFSET_OFFY].data.i=l->sl->y; + } + else + { + offset_dialog[D_OFFSET_OFFX].data.i=0; + offset_dialog[D_OFFSET_OFFY].data.i=0; + } + + if (GUI_dialog("Left offset",D_OFFSET_NO,offset_dialog)) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + if (l->sl) + { + l->sl->x=offset_dialog[D_OFFSET_OFFX].data.i; + l->sl->y=offset_dialog[D_OFFSET_OFFY].data.i; + } + i=IteratorNext(i); + } + + i=IteratorClear(i); + FullRedraw(); + } + + break; + + case TM_ADJUST_L: + l=SelHead(); + + offset_dialog[D_OFFSET_OFFX].data.i=ax; + offset_dialog[D_OFFSET_OFFY].data.i=ay; + + if (GUI_dialog("Left offset adjustment",D_OFFSET_NO,offset_dialog)) + { + ax=offset_dialog[D_OFFSET_OFFX].data.i; + ay=offset_dialog[D_OFFSET_OFFY].data.i; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + if (l->sl) + { + l->sl->x+=ax; + l->sl->y+=ay; + } + i=IteratorNext(i); + } + + i=IteratorClear(i); + FullRedraw(); + } + + break; + + case TM_SECTOR_L: + l=SelHead(); + + if (l->sl) + sectorn_dialog[D_SECTORN_SECNO].data.i=l->sl->sector; + else + sectorn_dialog[D_SECTORN_SECNO].data.i=-1; + + if (GUI_dialog("Left sector",D_SECTORN_NO,sectorn_dialog)) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + if (l->sl) + l->sl->sector=sectorn_dialog[D_SECTORN_SECNO].data.i; + i=IteratorNext(i); + } + + i=IteratorClear(i); + SectorCalcContainingAll(); + FullRedraw(); + } + + break; + + case TM_UPPER_T_L: + { + DirName d; + + if ((GetTexture("Upper Left texture",d))) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + if (l->sl) + strcpy(l->sl->upper,d); + i=IteratorNext(i); + } + + FullRedraw(); + } + + break; + } + + case TM_MIDDLE_T_L: + { + DirName d; + + if ((GetTexture("Middle Left texture",d))) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + if (l->sl) + strcpy(l->sl->middle,d); + i=IteratorNext(i); + } + + FullRedraw(); + } + + break; + } + + case TM_LOWER_T_L: + { + DirName d; + + if ((GetTexture("Lower Left texture",d))) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + if (l->sl) + strcpy(l->sl->lower,d); + i=IteratorNext(i); + } + + FullRedraw(); + } + + break; + } + + case TM_ALIGN_L: + { + int tw; + int ox; + int ok; + + ok=TRUE; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + + if (!(l->sl)) + ok=FALSE; + + i=IteratorNext(i); + } + + if (!ok) + GuiInfoBox("ERROR","Some linedefs with no left sidedef"); + else + { + l=SelHead(); + + tw=CalcTextureWidth(l->sl->upper,l->sl->middle,l->sl->lower); + + if (tw==0) + GuiInfoBox("ERROR","First selected linedef has no texture"); + else + { + ox=l->sl->x; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + + l=GETLINE(*s); + + l->sl->x=ox; + + ox=(ox+Len(l->v[0]->v.x,l->v[0]->v.y, + l->v[1]->v.x,l->v[1]->v.y))%tw; + + i=IteratorNext(i); + } + + FullRedraw(); + } + } + + } + break; + + case TM_TEXTURES_L: + l=SelHead(); + + if (l->sl) + { + strcpy(textures_dialog[D_TEXTURE_U].data.s,l->sl->upper); + strcpy(textures_dialog[D_TEXTURE_M].data.s,l->sl->middle); + strcpy(textures_dialog[D_TEXTURE_L].data.s,l->sl->lower); + } + else + { + strcpy(textures_dialog[D_TEXTURE_U].data.s,""); + strcpy(textures_dialog[D_TEXTURE_M].data.s,""); + strcpy(textures_dialog[D_TEXTURE_L].data.s,""); + } + + if (GUI_dialog("Left textures",D_TEXTURES_NO,textures_dialog)) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + + if (l->sl) + { + strcpy(l->sl->upper, + textures_dialog[D_TEXTURE_U].data.s); + strcpy(l->sl->middle, + textures_dialog[D_TEXTURE_M].data.s); + strcpy(l->sl->lower, + textures_dialog[D_TEXTURE_L].data.s); + } + + i=IteratorNext(i); + } + + i=IteratorClear(i); + FullRedraw(); + } + break; + + + case TM_TAG: + l=SelHead(); + tag_dialog[D_TAG].data.i=l->l.tag; + + if (GUI_dialog("Tag",D_TAG_NO,tag_dialog)) + { + SetTagObject_LINEDEF(tag_dialog[D_TAG].data.i); + FullRedraw(); + } + + break; + + case TM_DELETE: + ObjectDelete_LINEDEF(); + break; + + case TM_MOVE: + MoveObject_LINEDEF(); + break; + + case TM_ARGS: + { + int a[5]; + int id; + int diff; + + l=SelHead(); + + id=l->l.type; + + i=ListIterator(selected); + i=IteratorNext(i); + diff=FALSE; + + while((i)&&(!diff)) + { + s=IteratorData(i); + l=GETLINE(*s); + + if (l->l.type!=id) + diff=TRUE; + + i=IteratorNext(i); + } + + if ((diff)&&(YesNo("Selected linedefs have|different types| |" + "Set individually?"))) + { + cancel=TRUE; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + id=l->l.type; + + for(f=0;f<5;f++) + a[f]=l->l.args[f]; + + if (SpecialArgDialog(NULL,id,a)) + { + for(f=0;f<5;f++) + l->l.args[f]=a[f]; + + SetHexenLinedefTag(l); + + cancel=FALSE; + } + + i=IteratorNext(i); + } + } + else + { + l=SelHead(); + + for(f=0;f<5;f++) + a[f]=l->l.args[f]; + + if (SpecialArgDialog(NULL,id,a)) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + + for(f=0;f<5;f++) + l->l.args[f]=a[f]; + + SetHexenLinedefTag(l); + + i=IteratorNext(i); + } + } + else + cancel=TRUE; + } + + FullRedraw(); + break; + } + + case TM_ZERO: + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + + for(f=0;f<5;f++) + l->l.args[f]=0; + + l->l.tag=0; + + i=IteratorNext(i); + } + + FullRedraw(); + break; + + case TM_ARGS_RANGE: + { + int a[5]; + int d[5]; + int id; + int diff; + + l=SelHead(); + + id=l->l.type; + + i=ListIterator(selected); + i=IteratorNext(i); + diff=FALSE; + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + + if (l->l.type!=id) + { + diff=TRUE; + i=IteratorClear(i); + } + else + i=IteratorNext(i); + } + + l=SelHead(); + + for(f=0;f<5;f++) + { + a[f]=l->l.args[f]; + d[f]=0; + } + + if ((!diff)||(YesNo("Selected linedefs have|different types| |" + "Continue?"))) + if ((SpecialArgDialog("Start values",id,a))&& + (SpecialArgDialog("Increments",id,d))) + + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + l=GETLINE(*s); + + for(f=0;f<5;f++) + { + l->l.args[f]=(Byte)a[f]; + a[f]+=d[f]; + } + + SetHexenLinedefTag(l); + + i=IteratorNext(i); + } + + FullRedraw(); + } + break; + } + + default: + cancel=TRUE; + break; + } + + if ((!cancel)&&(!new_selection)&&(clear_on_menu)) + { + ClearSelection(); + FullRedraw(); + } +} + + +void ObjectKey_LINEDEF(GFXKey k) +{ + char s[128]; + EditLine *l; + int f; + int change; + List li; + Iterator i; + EditSect *sr,*sl; + DirName d; + Object *o; + int apply; + + VIDOOM_TRACE; + + switch(k.code) + { + case GFX_F12: + li=ListNew(sizeof(int)); + + change=FALSE; + + if (ListSize(selected)==0) + { + GuiInfoBox("ERROR","No linedefs selected"); + return; + } + + i=ListIterator(selected); + + GUI_start_yesno_all(); + + while(i) + { + memcpy(&f,IteratorData(i),sizeof(f)); + + if ((l=GETLINE(f))) + { + VIDOOM_TRACE; + if (check_1side_lower) + if ((!l->sl)&&(strcmp(l->sr->lower,empty_texture))) + if ((check_line_assume_yes)|| + (YesNoAll + ("LINEDEF %d is one-sided with a " + "lower texture. Remove?",f))) + { + strcpy(l->sr->lower,empty_texture); + change=TRUE; + IntListUniqAdd(li,f); + } + + VIDOOM_TRACE; + if (check_1side_middle) + if ((!l->sl)&&(!strcmp(l->sr->middle,empty_texture))) + if ((check_line_assume_yes)|| + (YesNoAll + ("LINEDEF %d is one-sided with no " + "middle texture. Add?",f))) + { + sprintf(s,"Pick MIDDLE texture " + "for LINEDEF %d",f); + + if (strcmp(linedef_check_default,empty_texture)) + { + apply=TRUE; + strcpy(d,linedef_check_default); + } + else + apply=GetTexture(s,d); + + if (apply) + { + strcpy(l->sr->middle,d); + change=TRUE; + IntListUniqAdd(li,f); + } + } + + VIDOOM_TRACE; + if (check_1side_upper) + if ((!l->sl)&&(strcmp(l->sr->upper,empty_texture))) + if ((check_line_assume_yes)|| + (YesNoAll + ("LINEDEF %d is one-sided with a " + "upper texture. Remove?",f))) + { + strcpy(l->sr->upper,empty_texture); + change=TRUE; + IntListUniqAdd(li,f); + } + + VIDOOM_TRACE; + if ((check_2side_lower)&&(l->sl)&&(l->sl->sector!=-1)&& + (l->sr->sector!=-1)) + { + sr=GETSECT(l->sr->sector); + sl=GETSECT(l->sl->sector); + + if ((sr->s.floor<sl->s.floor)&& + (!strcmp(l->sr->lower,empty_texture))) + if ((check_line_assume_yes)|| + (YesNoAll + ("LINEDEF %d is two-sided with no " + "lower texture. Add?",f))) + { + sprintf(s,"Pick LOWER texture " + "for LINEDEF %d",f); + + if (strcmp(linedef_check_default,empty_texture)) + { + apply=TRUE; + strcpy(d,linedef_check_default); + } + else + apply=GetTexture(s,d); + + if (apply) + { + strcpy(l->sr->lower,d); + strcpy(l->sl->lower,empty_texture); + change=TRUE; + IntListUniqAdd(li,f); + } + } + + if ((sr->s.floor>sl->s.floor)&& + (!strcmp(l->sl->lower,empty_texture))) + if ((check_line_assume_yes)|| + (YesNoAll + ("LINEDEF %d is two-sided with no " + "lower texture. Add?",f))) + { + sprintf(s,"Pick LOWER texture " + "for LINEDEF %d",f); + + if (strcmp(linedef_check_default,empty_texture)) + { + apply=TRUE; + strcpy(d,linedef_check_default); + } + else + apply=GetTexture(s,d); + + if (apply) + { + strcpy(l->sl->lower,d); + strcpy(l->sr->lower,empty_texture); + change=TRUE; + IntListUniqAdd(li,f); + } + } + + if ((sr->s.floor==sl->s.floor)&& + ((strcmp(l->sl->lower,empty_texture))|| + (strcmp(l->sr->lower,empty_texture)))) + if ((check_line_assume_yes)|| + (YesNoAll + ("LINEDEF %d is two-sided with " + "pointless lower texture. Remove?",f))) + { + strcpy(l->sl->lower,empty_texture); + strcpy(l->sr->lower,empty_texture); + change=TRUE; + IntListUniqAdd(li,f); + } + } + + VIDOOM_TRACE; + if ((check_2side_middle)&&(l->sl)&&(l->sl->sector!=-1)) + { + if (((strcmp(l->sr->middle,empty_texture))|| + (strcmp(l->sl->middle,empty_texture)))&& + (l->sl->sector!=l->sr->sector)) + if ((check_line_assume_yes)|| + (YesNoAll + ("LINEDEF %d is two-sided with a " + "middle texture. Remove?",f))) + { + strcpy(l->sr->middle,empty_texture); + strcpy(l->sl->middle,empty_texture); + change=TRUE; + IntListUniqAdd(li,f); + } + } + + VIDOOM_TRACE; + if ((check_2side_upper)&&(l->sl)&&(l->sl->sector!=-1)&& + (l->sr->sector!=-1)) + { + sr=GETSECT(l->sr->sector); + sl=GETSECT(l->sl->sector); + + if ((sr->s.ceiling>sl->s.ceiling)&& + (!strcmp(l->sr->upper,empty_texture))) + if ((check_line_assume_yes)|| + (YesNoAll + ("LINEDEF %d is two-sided with no " + "upper texture. Add?",f))) + { + sprintf(s,"Pick UPPER texture " + "for LINEDEF %d",f); + + if (strcmp(linedef_check_default,empty_texture)) + { + apply=TRUE; + strcpy(d,linedef_check_default); + } + else + apply=GetTexture(s,d); + + if (apply) + { + strcpy(l->sr->upper,d); + strcpy(l->sl->upper,empty_texture); + change=TRUE; + IntListUniqAdd(li,f); + } + } + + if ((sr->s.ceiling<sl->s.ceiling)&& + (!strcmp(l->sl->upper,empty_texture))) + if ((check_line_assume_yes)|| + (YesNoAll + ("LINEDEF %d is two-sided with no " + "upper texture. Add?",f))) + { + sprintf(s,"Pick UPPER texture " + "for LINEDEF %d",f); + + if (strcmp(linedef_check_default,empty_texture)) + { + apply=TRUE; + strcpy(d,linedef_check_default); + } + else + apply=GetTexture(s,d); + + if (apply) + { + strcpy(l->sl->upper,d); + strcpy(l->sr->upper,empty_texture); + change=TRUE; + IntListUniqAdd(li,f); + } + } + + if ((sr->s.ceiling==sl->s.ceiling)&& + ((strcmp(l->sl->upper,empty_texture))|| + (strcmp(l->sr->upper,empty_texture)))) + if ((check_line_assume_yes)|| + (YesNoAll + ("LINEDEF %d is two-sided with " + "pointless upper texture. Remove?",f))) + { + strcpy(l->sl->upper,empty_texture); + strcpy(l->sr->upper,empty_texture); + change=TRUE; + IntListUniqAdd(li,f); + } + } + + VIDOOM_TRACE; + if ((check_2side_same_sector)&&(l->sl)&&(l->sl->sector!=-1)) + { + if ((l->sl->sector==l->sr->sector)&& + ((strcmp(l->sr->lower,empty_texture))|| + (strcmp(l->sr->middle,empty_texture))|| + (strcmp(l->sr->upper,empty_texture))|| + (strcmp(l->sl->lower,empty_texture))|| + (strcmp(l->sl->middle,empty_texture))|| + (strcmp(l->sl->upper,empty_texture)))) + if ((check_line_assume_yes)|| + (YesNoAll + ("LINEDEF %d is two-sided within a " + "sector and is textured. " + "Remove all?",f))) + { + strcpy(l->sr->lower,empty_texture); + strcpy(l->sl->lower,empty_texture); + strcpy(l->sr->middle,empty_texture); + strcpy(l->sl->middle,empty_texture); + strcpy(l->sr->upper,empty_texture); + strcpy(l->sl->upper,empty_texture); + change=TRUE; + IntListUniqAdd(li,f); + } + } + + } + + i=IteratorNext(i); + } + + if (change) + { + if (YesNo("Select altered LINEDEFs?")) + { + ClearSelection(); + i=ListIterator(li); + + while(i) + { + memcpy(&f,IteratorData(i),sizeof(int)); + o=MapElem(linedef,f); + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + i=IteratorNext(i); + } + + FullRedraw(); + } + } + else + GuiInfoBox("NOTICE","No problems found"); + + ListClear(li); + break; + + default: + + case GFX_ASCII: + switch(toupper(k.ascii)) + { + case 'H': + tag_highlight=!tag_highlight; + FullRedraw(); + break; + + default: + break; + } + break; + } +} + + +int ObjectHasTag_LINEDEF(void *obj, int tag) +{ + EditLine *l; + + return ((l=obj)&&(l->l.tag==tag)); +} + + +ObjDesc *ObjectOverlaid_LINEDEF(int x, int y, int *no) +{ + int n; + EditLine *l; + ObjDesc *od; + int f; + + VIDOOM_TRACE; + + od=NULL; + + n=0; + + for(f=0;f<MapSize(linedef);f++) + { + l=GETLINE(f); + + if ((l)&&(PositionOnObject_LINEDEF(x,y,l))) + { + n++; + od=ReGrab(od,sizeof(ObjDesc)*n); + od[n-1].no=f; + + sprintf(od[n-1].detail,"%5d - %s (Tag %d)",f, + TrimStr(LinedefName(l->l.type),60), + l->l.tag); + } + } + + *no=n; + return(od); +} + + +/* END OF FILE */ diff --git a/editlump.c b/editlump.c new file mode 100644 index 0000000..f55d269 --- /dev/null +++ b/editlump.c @@ -0,0 +1,330 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor LUMP manipulations + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" +#include "editvar.h" +#include "platgui.h" +#include "runcmd.h" +#include "file.h" +#include "mem.h" +#include "util.h" + + +#define M_CANCEL -1 +#define M_SWITCH_DOOM 0 +#define M_SWITCH_HEXEN 1 +#define M_EDIT_MAPINFO 2 +#define M_EDIT_SCRIPTS 3 +#define M_MAKE_BEHAVIOR 4 + +static PLAT_MENU menu[6]; +static int no=0; + + +/* ---------------------------------------- PRIVATE UTILS +*/ +static void NewMenu(void) +{ + int f; + + for(f=0;f<no;f++) + { + Release(menu[f].text); + menu[f].text=NULL; + } + + no=0; +} + + +static void AddMenu(char *text, int id) +{ + menu[no].text=Strdup(text); + menu[no].client_index=id; + menu[no].child=NULL; + + menu[++no].text=NULL; +} + + +/* ---------------------------------------- ACS COMPILATION +*/ +static char *MakeArgs(void) +{ + static char s[PATH_MAX+1]=""; + char *p; + int i; + + if (s[0]) + return(s); + + p=acc_args; + i=0; + + while(*p) + if (*p=='%') + { + p++; + + switch(*p) + { + case 'S': + strcat(s,acc_script); + i=strlen(s); + p++; + break; + + case 'O': + strcat(s,acc_object); + i=strlen(s); + p++; + break; + + default: + s[i++]='%'; + s[i]=0; + break; + } + } + else + { + s[i++]=*p++; + s[i]=0; + } + + return(s); +} + + +static int WriteScript(void) +{ + FILE *fp; + + if (!(fp=fopen(acc_script,"w"))) + return(FALSE); + + fputs(scripts,fp); + fclose(fp); + return(TRUE); +} + + +static int ReadBehavior(void) +{ + FILE *fp; + + if (!(fp=fopen(acc_object,"rb"))) + return(FALSE); + + behavior_size=(int)FLen(fp); + behavior=ReGrab(behavior,behavior_size); + + FRead(fp,behavior,behavior_size); + fclose(fp); + + return(TRUE); +} + + +static void MakeBehavior(void) +{ + char cwd[PATH_MAX]; + char path[PATH_MAX]; + char *arg[3]; + int ok; + + strcpy(cwd,Pwd()); + + arg[0]=acc_cmd; + arg[1]=MakeArgs(); + arg[2]=NULL; + + Cd(acc_dir); + + if (!WriteScript()) + { + GuiInfoBox("ERROR","ACS compilation|Failed to write|%s",acc_script); + Cd(cwd); + return; + } + + if ((ok=RunCommand(arg,path))) + { + if ((acc_always_view)&&(path[0])) + GUI_view_file("ACS Compilation",path); + } + else + if (path[0]) + GUI_view_file("ACS Compilation FAILED",path); + else + GuiInfoBox("ERROR","ACS compiler|%s|failed|" + "(no results available)",acc_cmd); + + if (path[0]) + remove(path); + + if (ok) + if (!ReadBehavior()) + GuiInfoBox("ERROR","ACS Compilation|Failed to read|%s",acc_object); + + Cd(cwd); +} + + +/* ---------------------------------------- EXPORTED FUNCS +*/ + +void EditLumpMenu(void) +{ + int redraw; + + NewMenu(); + redraw=FALSE; + + if (mapinfo_lump) + AddMenu("Edit MAPINFO",M_EDIT_MAPINFO); + + if (hexen_mode) + { + AddMenu("Edit SCRIPTS",M_EDIT_SCRIPTS); + AddMenu("Create BEHAVIOR",M_MAKE_BEHAVIOR); + + AddMenu("Switch map to DOOM mode",M_SWITCH_DOOM); + } + else + AddMenu("Switch map to HEXEN mode",M_SWITCH_HEXEN); + + switch(GUI_menu("LUMP menu",ms.x,ms.y,menu,M_CANCEL)) + { + case M_SWITCH_DOOM: + if (YesNo("Switching to DOOM mode will lose any|" + "defined SCRIPTS and BEHAVIOR lumps| |" + "Also THING, LINEDEF and SECTOR types/specials|" + "will ALMOST CERTAINLY be incorrect.| |" + "SURE you want to proceed?")) + { + redraw=TRUE; + hexen_mode=FALSE; + + if (scripts) + Release(scripts); + + scripts=NULL; + scripts_size=0; + + if (behavior) + Release(behavior); + + behavior=NULL; + behavior_size=0; + } + break; + + case M_SWITCH_HEXEN: + if (YesNo("Switching to HEXEN mode will set all|" + "THING Z co-ordinates and special args|" + "to zero, sets LINEDEF special args to|" + "zero and and create empty SCRIPTS and|" + "BEHAVIOR lumps.| |" + "Also THING, LINEDEF and SECTOR types/specials|" + "will ALMOST CERTAINLY be incorrect.| |" + "SURE you want to proceed?")) + { + int f,r; + EditThing *t; + EditLine *l; + + redraw=TRUE; + hexen_mode=TRUE; + + for(f=0;f<MapSize(thing);f++) + if ((t=GETTHING(f))) + { + t->t.z=0; + + for(r=0;r<5;r++) + t->t.args[r]=0; + } + + for(f=0;f<MapSize(linedef);f++) + if ((l=GETLINE(f))) + { + l->l.tag=0; + + for(r=0;r<5;r++) + l->l.args[r]=0; + } + } + break; + + case M_EDIT_MAPINFO: + { + char *new; + + if (!mapinfo) + mapinfo=Strdup(""); + + if ((new=GUI_text_edit("MAPINFO lump",mapinfo))) + { + Release(mapinfo); + mapinfo=new; + } + break; + } + + case M_EDIT_SCRIPTS: + { + char *new; + + if (!scripts) + scripts=Strdup(""); + + if ((new=GUI_text_edit("SCRIPTS lump",scripts))) + { + Release(scripts); + scripts=new; + scripts_size=strlen(scripts); + } + break; + } + + case M_MAKE_BEHAVIOR: + if (!scripts) + GUI_alert("Error","Need to create a script by editing|" + "it before it can be compiled","OK"); + else + MakeBehavior(); + break; + } + + if (redraw) + FullRedraw(); +} + + +/* END OF FILE */ diff --git a/editmrg.c b/editmrg.c new file mode 100644 index 0000000..51197b1 --- /dev/null +++ b/editmrg.c @@ -0,0 +1,564 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Merging of WAD maps + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" +#include "editvar.h" +#include "gui.h" +#include "wad.h" + + +/* ---------------------------------------- LOCAL DATA +*/ +static WadMap *wad; +static int min_x; +static int min_y; +static int max_x; +static int max_y; +static int cx; +static int cy; +static int ang; + + +/* ---------------------------------------- CO-ORD FUNCS +*/ +static void MergeCoord(int *x, int *y) +{ + if (ang) + { + Rotate(min_x,min_y,x,y,(double)ang); + + *x=cx-min_x+(*x); + *y=cy-min_y+(*y); + } + else + { + *x=cx-min_x+(*x); + *y=cy-min_y+(*y); + } +} + +/* ---------------------------------------- PickPoint() CALLBACKS +*/ +static void PickDraw(int *x,int *y) +{ + Map lm,vm; + Linedef *l; + Vertex *v1,*v2; + int x1,y1,x2,y2; + int f; + + cx=*x=SnapX(*x); + cy=*y=SnapY(*y); + + lm=wad->linedef; + vm=wad->vertex; + + for(f=0;f<MapSize(lm);f++) + { + l=MapElem(lm,f); + + v1=MapElem(vm,l->from); + v2=MapElem(vm,l->to); + + x1=v1->x; + y1=v1->y; + MergeCoord(&x1,&y1); + x1=MapToX(x1); + y1=MapToY(y1); + + x2=v2->x; + y2=v2->y; + MergeCoord(&x2,&y2); + x2=MapToX(x2); + y2=MapToY(y2); + + GFX_line(x1,y1,x2,y2,WHITE); + } +} + + +static void PickKey(GFXKey k) +{ + VIDOOM_TRACE; + + if (k.code==GFX_ASCII) + switch(k.ascii) + { + case '.': + if (--ang<0) + ang=359; + break; + + case ',': + ang=(ang+1)%360; + break; + + case '>': + if ((ang-=10)<0) + ang+=360; + break; + + case '<': + ang=(ang+10)%360; + break; + + default: + break; + } +} + + +static void PickInfo(char *p) +{ + VIDOOM_TRACE; + + GuiDrawInfoBox(p,GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE,"Angle : %3d|",ang); + + GuiDrawInfoBox(p,GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + ", - rotate left 1 deg |" + ". - rotate right 1 deg |" + "< - rotate left 10 deg |" + "> - rotate right 10 deg|" + " |" + "Press left button to complete. ESC to cancel"); +} + + +/* ---------------------------------------- WAD MERGEING +*/ +void MergeWad(void) +{ + Object o; + Linedef *l; + Sector *s=NULL; + Thing *t; + Vertex *v; + Sidedef *si; + EditVert *ev; + EditLine *el; + EditSect *es; + EditThing *et; + char *name; + int f; + int unbound; + int n_thing; + int n_line; + int n_side; + int n_vert; + int n_sect; + int n_tag; + int tmp_wad; + char *tmp_n; + char *wadf; + int *secmap; + int x,y; + + if (level_style==DOOM_2_LEVELS) + tmp_n="MAP01"; + else + tmp_n="E1M1"; + + if ((tmp_wad=YesNo("Load a new PWAD and read %s from it?",tmp_n))) + { + if (!(wadf=GUI_fsel("Pick PWAD to merge map from",PWAD_dir,".WAD"))) + return; + + if (AddPWAD(wadf)!=WAD_OK) + { + GuiInfoBox("ERROR","AddPWAD(%s): %s",wadf,WadErrorString()); + Release(wadf); + return; + } + + name=tmp_n; + } + else + { + wadf=NULL; + + if (!(name=GuiPickLevel("Pick map to merge"))) + return; + } + + GuiDrawInfoBox("Loading...",GUI_CENTRE,GUI_CENTRE,TRUE, + "Loading level %s",name); + if (!(wad=LoadMap(name))) + { + GuiInfoBox("ERROR","LoadMap(%s): %s",name,WadErrorString()); + + if (tmp_wad) + { + if (CloseWad(wadf)!=WAD_OK) + GuiInfoBox("ERROR","CloseWad(%s)|%s",wadf,WadErrorString()); + + Release(wadf); + } + + return; + } + + if (MapSize(wad->linedef)==0) + { + GuiInfoBox("ERROR","No LINEDEFS in %s",name); + ClearMap(wad); + + if (tmp_wad) + { + if (CloseWad(wadf)!=WAD_OK) + GuiInfoBox("ERROR","CloseWad(%s)|%s",wadf,WadErrorString()); + + Release(wadf); + } + + return; + } + + FullRedraw(); + + /* Get top left hand corner of map and see if there are unbound left + sidedefs + */ + ang=0; + min_x=99999; + min_y=99999; + max_x=-99999; + max_y=-99999; + + for(f=0;f<MapSize(wad->vertex);f++) + { + v=MapElem(wad->vertex,f); + + min_x=MIN(min_x,v->x); + min_y=MIN(min_y,v->y); + + max_x=MAX(max_x,v->x); + max_y=MAX(max_y,v->y); + } + + min_x=min_x+(max_x-min_x)/2; + min_y=min_y+(max_y-min_y)/2; + + unbound=FALSE; + + for(f=0;(f<MapSize(wad->linedef))&&(!unbound);f++) + { + l=MapElem(wad->linedef,f); + + if (l->left!=-1) + { + si=MapElem(wad->sidedef,l->left); + + if (si->sector==-1) + unbound=TRUE; + } + } + + /* Position + */ + if (!PickPoint("Position MAP",&cx,&cy,PickDraw,PickKey,PickInfo)) + { + ClearMap(wad); + + if (tmp_wad) + { + if (CloseWad(wadf)!=WAD_OK) + GuiInfoBox("ERROR","CloseWad(%s)|%s",wadf,WadErrorString()); + + Release(wadf); + } + + return; + } + + /* Adjust co-ords + */ + for(f=0;f<MapSize(wad->vertex);f++) + { + v=MapElem(wad->vertex,f); + x=v->x; + y=v->y; + MergeCoord(&x,&y); + v->x=x; + v->y=y; + } + + for(f=0;f<MapSize(wad->thing);f++) + { + t=MapElem(wad->thing,f); + x=t->x; + y=t->y; + MergeCoord(&x,&y); + t->x=x; + t->y=y; + } + + /* If the map is a structure with some unbound left sidedefs, calculate + which sectors the new linedefs appear in (taken from the line's + midpoint) + */ + if (unbound) + { + Vertex *v1,*v2; + int lcx,lcy; + + secmap=Grab(sizeof(int)*MapSize(wad->linedef)); + + for(f=0;f<MapSize(wad->linedef);f++) + { + l=MapElem(wad->linedef,f); + + v1=MapElem(wad->vertex,l->from); + v2=MapElem(wad->vertex,l->to); + + lcx=v1->x+((v2->x-v1->x)/2); + lcy=v1->y+((v2->y-v1->y)/2); + + secmap[f]=SectorHoldingPoint(lcx,lcy); + } + + /* See if floor and ceilings shold be adjusted + */ + if (YesNo("Adjust structure floor heights?")) + { + int t,min,diff; + + if ((t=SectorHoldingPoint(cx,cy))!=-1) + es=GETSECT(t); + else + es=NULL; + + if (es) + { + min=99999; + + for(f=0;f<MapSize(wad->sector);f++) + { + s=MapElem(wad->sector,f); + + if (min>s->floor) + min=s->floor; + } + + diff=min-es->s.floor; + + for(f=0;f<MapSize(wad->sector);f++) + { + s=MapElem(wad->sector,f); + s->floor-=diff; + s->ceiling-=diff; + } + } + } + + if (YesNo("Adjust structure ceiling heights?")) + { + int t; + + if ((t=SectorHoldingPoint(cx,cy))!=-1) + es=GETSECT(t); + else + es=NULL; + + if (es) + for(f=0;f<MapSize(wad->sector);f++) + { + s=MapElem(wad->sector,f); + + if ((s->ceiling!=es->s.ceiling)&&(s->floor<es->s.ceiling)) + s->ceiling=es->s.ceiling; + } + } + } + else + secmap=NULL; + + /* Renumber tags? + */ + n_tag=0; + + if (YesNo("Renumber tags?")) + { + for(f=0;f<MapSize(linedef);f++) + if ((el=GETLINE(f))) + n_tag=MAX(n_tag,el->l.tag); + + for(f=0;f<MapSize(sector);f++) + if ((es=GETSECT(f))) + n_tag=MAX(n_tag,es->s.tag); + } + + n_thing=MapSize(thing); + n_line=MapSize(linedef); + n_side=MapSize(sidedef); + n_sect=MapSize(sector); + n_vert=MapSize(vertex); + + /* Insert vertexes + */ + for(f=0;f<MapSize(wad->vertex);f++) + { + v=MapElem(wad->vertex,f); + + ev=Grab(sizeof(EditVert)); + memcpy(&ev->v,v,sizeof(Vertex)); + ev->l=ListNew(sizeof(int)); + + o.select=SELECT_NONE; + o.data=ev; + + MapAdd(vertex,f+n_vert,&o); + } + + /* Insert sidedefs + */ + for(f=0;f<MapSize(wad->sidedef);f++) + { + o.select=SELECT_NONE; + si=o.data=Copy(MapElem(wad->sidedef,f),sizeof(Sidedef)); + + if (si->sector!=-1) + si->sector+=n_sect; + + MapAdd(sidedef,f+n_side,&o); + } + + /* Insert linedefs and get associated sidedefs, vertex pointers and calc + bounding box + */ + for(f=0;f<MapSize(wad->linedef);f++) + { + l=MapElem(wad->linedef,f); + el=Grab(sizeof(EditLine)); + + memcpy(&el->l,l,sizeof(Linedef)); + + if (el->l.tag) + el->l.tag+=n_tag; + + el->l.from+=n_vert; + el->l.to+=n_vert; + + el->l.right+=n_side; + + if (el->l.left!=-1) + el->l.left+=n_side; + + el->no=f+n_line; + el->sr=GETSIDE(el->l.right); + + if (l->left!=-1) + { + el->sl=GETSIDE(el->l.left); + + if ((unbound)&&(el->sl->sector==-1)) + el->sl->sector=secmap[f]; + } + else + el->sl=NULL; + + el->v[0]=GETVERT(el->l.from); + el->v[1]=GETVERT(el->l.to); + + IntListUniqAdd(el->v[0]->l,f+n_line); + IntListUniqAdd(el->v[1]->l,f+n_line); + + LineCalcBounding(el); + + o.select=SELECT_NONE; + o.data=el; + + MapAdd(linedef,f+n_line,&o); + } + + /* Insert sectors + */ + for(f=0;f<MapSize(wad->sector);f++) + { + s=MapElem(wad->sector,f); + es=Grab(sizeof(EditSect)); + + memcpy(&es->s,s,sizeof(Sector)); + + es->no=f+n_sect; + + if (es->s.tag) + es->s.tag+=n_tag; + + es->v=ListNew(sizeof(Short)); + es->sr=ListNew(sizeof(EditLine *)); + es->sl=ListNew(sizeof(EditLine *)); + es->all=ListNew(sizeof(EditLine *)); + + o.select=SELECT_NONE; + o.data=es; + + MapAdd(sector,f+n_sect,&o); + } + + /* Insert things + */ + for(f=0;f<MapSize(wad->thing);f++) + { + t=MapElem(wad->thing,f); + et=Grab(sizeof(EditThing)); + memcpy(&et->t,t,sizeof(Thing)); + o.select=SELECT_NONE; + o.data=et; + MapAdd(thing,f+n_thing,&o); + } + + SectorCalcContainingAll(); + + GuiInfoBox("NOTICE","Vertexes adjusted by %d|" + "Linedefs adjusted by %d|" + "Sidedefs adjusted by %d|" + "Sectors adjusted by %d|" + "Things adjusted by %d|" + "Tags adjusted by %d", + n_vert,n_line,n_side,n_sect,n_thing,n_tag); + + FullRedraw(); + + /* Tidy up + */ + ClearMap(wad); + + if (unbound) + Release(secmap); + + if (tmp_wad) + { + if (CloseWad(wadf)!=WAD_OK) + GuiInfoBox("ERROR","CloseWad(%s)|%s",wadf,WadErrorString()); + + Release(wadf); + } +} + + +/* END OF FILE */ diff --git a/editmult.c b/editmult.c new file mode 100644 index 0000000..c3165ca --- /dev/null +++ b/editmult.c @@ -0,0 +1,708 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor MULTIMODE definitions + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" +#include "util.h" + +#include "editvar.h" + +#define VERTEX 1 +#define THING 2 + + +/* ---------------------------------------- PRIVATE UTILS +*/ +static int GetIterType(Iterator *i,MultiObj **mr,EditVert **v,EditThing **t) +{ + int *f; + MultiObj *m; + + VIDOOM_TRACE; + + f=IteratorData(*i); + m=GETMULTI(*f); + + switch(m->type) + { + case VERTEX: + *v=GETVERT(m->i); + break; + case THING: + *t=GETTHING(m->i); + break; + } + + *i=IteratorNext(*i); + + if (mr) + *mr=m; + + return(m->type); +} + + +static int GetType(MultiObj *m,EditVert **v,EditThing **t) +{ + VIDOOM_TRACE; + + switch(m->type) + { + case VERTEX: + *v=GETVERT(m->i); + break; + case THING: + *t=GETTHING(m->i); + break; + } + + return(m->type); +} + + +static void SelectionCentre(int *x,int *y) +{ + int *f; + MultiObj *m; + Iterator i; + int min_x,min_y,max_x,max_y; + + VIDOOM_TRACE; + + i=ListIterator(selected); + + min_x=99999; + min_y=99999; + max_x=-99999; + max_y=-99999; + + while(i) + { + f=IteratorData(i); + m=GETMULTI(*f); + + min_x=MIN(m->x,min_x); + min_y=MIN(m->y,min_y); + max_x=MAX(m->x,max_x); + max_y=MAX(m->y,max_y); + + i=IteratorNext(i); + } + + *x=min_x+(max_x-min_x)/2; + *y=min_y+(max_y-min_y)/2; +} + + +static void ApplySelectionCoords(void) +{ + MultiObj *m; + Iterator i; + EditVert *v; + EditThing *t; + + VIDOOM_TRACE; + + i=ListIterator(selected); + + while(i) + switch(GetIterType(&i,&m,&v,&t)) + { + case THING: + t->t.x=m->x; + t->t.y=m->y; + break; + + case VERTEX: + v->v.x=m->x; + v->v.y=m->y; + break; + } +} + + +/* ---------------------------------------- EXPORTED UTILS +*/ +void GenerateMultiMap(void) +{ + MultiObj *m; + EditVert *v; + EditThing *t; + Object o; + int f; + + VIDOOM_TRACE; + + if (!multimap) + multimap=MapNew(sizeof(MultiObj)); + else + { + for(f=0;f<MapSize(multimap);f++) + { + memcpy(&o,MapElem(multimap,f),sizeof(Object)); + + if (o.data) + Release(o.data); + } + + multimap=MapEmpty(multimap); + } + + for(f=0;f<MapSize(vertex);f++) + if ((v=GETVERT(f))) + { + m=Grab(sizeof(MultiObj)); + m->type=VERTEX; + m->i=f; + m->x=v->v.x; + m->y=v->v.y; + o.data=m; + o.select=SELECT_NONE; + MapAdd(multimap,-1,&o); + } + + for(f=0;f<MapSize(thing);f++) + if ((t=GETTHING(f))) + { + m=Grab(sizeof(MultiObj)); + m->type=THING; + m->i=f; + m->x=t->t.x; + m->y=t->t.y; + o.data=m; + o.select=SELECT_NONE; + MapAdd(multimap,-1,&o); + } +} + +/* ---------------------------------------- GENERIC THING FUNCS +*/ +int PositionOnObject_MULTI(int x,int y,void *data) +{ + EditVert *v; + EditThing *t; + + VIDOOM_TRACE; + + switch(GetType(data,&v,&t)) + { + case VERTEX: + return (PositionOnObject_VERTEX(x,y,v)); + break; + case THING: + return (PositionOnObject_THING(x,y,t)); + break; + } + + return (FALSE); +} + + +void SelectBox_MULTI(int x1,int y1,int x2,int y2) +{ + Object *o; + MultiObj *m; + int f; + + VIDOOM_TRACE; + + for(f=0;f<MapSize(multimap);f++) + { + o=MapElem(multimap,f); + + if ((m=o->data)) + { + if ((o->select!=SELECT_SELECTED)&& + (BOXBOUND(m->x,m->y,x1,y1,x2,y2))) + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + } + } +} + + +void SelectByType_MULTI(void) +{ + VIDOOM_TRACE; + return; +} + + +void DrawObject_MULTI(void *data, int selmode) +{ + MultiObj *m; + + VIDOOM_TRACE; + m=data; + + switch(m->type) + { + case VERTEX: + DrawObject_VERTEX(GETVERT(m->i),selmode); + break; + case THING: + DrawObject_THING(GETTHING(m->i),selmode); + break; + default: + break; + } +} + + +void DrawObjectInfo_MULTI(void) +{ + MultiObj *m; + + VIDOOM_TRACE; + + if (!draw_current_info) + return; + + if ((current!=-1)&&((m=GETMULTI(current)))) + GuiDrawInfoBox("MULTI MODE",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "Object type : %s|" + "Number : %d", + (m->type == VERTEX ? "VERTEX" : "THING "), + m->i); + else + GuiDrawInfoBox("MULTI MODE",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "Object type : |" + "Number : -"); +} + + +void DrawObjectHeader_MULTI(void) +{ + VIDOOM_TRACE; +} + + +void MoveObject_MULTI(void) +{ + GFXEvent ev; + int omx,omy; + int dmx,dmy; + int done; + Iterator i; + Iterator i2; + MultiObj *m; + List orig; + int *f; + Point p; + int cancel=FALSE; + + VIDOOM_TRACE; + + omx=SnapX(XToMap(ms.x)); + omy=SnapY(YToMap(ms.y)); + GFX_bounce(); + + done=FALSE; + cancel=FALSE; + orig=ListNew(sizeof(Point)); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + m=GETMULTI(*f); + p.x=m->x; + p.y=m->y; + ListAppend(orig,&p); + i=IteratorNext(i); + } + + while(!done) + { + GuiDrawInfoBox("Move Multiple Objects", + GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE, + "Left mouse button to place|ESC to cancel"); + + GFX_redraw(); + GFX_await_input_full(&ev); + + switch(ev.type) + { + case GFX_KEY_EVENT: + HandleMoveKey(ev.key,FALSE); + + /* Cancel the move + */ + if (ev.key.code==GFX_ESC) + { + cancel=TRUE; + + i=ListIterator(selected); + i2=ListIterator(orig); + + while(i2) + { + f=IteratorData(i); + memcpy(&p,IteratorData(i2),sizeof(Point)); + m=GETMULTI(*f); + m->x=p.x; + m->y=p.y; + + i=IteratorNext(i); + i2=IteratorNext(i2); + } + + ApplySelectionCoords(); + + done=TRUE; + dmx=0; + dmy=0; + } + else + { + dmx=SnapX(XToMap(ms.x))-omx; + dmy=SnapY(YToMap(ms.y))-omy; + } + + break; + + case GFX_MOUSE_EVENT: + memcpy(&ms,&ev.mouse,sizeof(ev.mouse)); + + if (ms.b&GFX_BUTLEFT) + done=TRUE; + + dmx=SnapX(XToMap(ms.x))-omx; + dmy=SnapY(YToMap(ms.y))-omy; + break; + + default: + dmx=0; + dmy=0; + break; + } + + if ((dmx)||(dmy)) + { + i=ListIterator(selected); + while(i) + { + f=IteratorData(i); + + m=GETMULTI(*f); + + m->x+=dmx; + m->y+=dmy; + + i=IteratorNext(i); + } + + ApplySelectionCoords(); + + omx=SnapX(XToMap(ms.x)); + omy=SnapY(YToMap(ms.y)); + + FullRedraw(); + } + } + + ListClear(orig); + + if ((clear_on_move)&&(!cancel)) + ClearSelection(); + + FullRedraw(); +} + + +void RotateObject_MULTI(double angle) +{ + int cx,cy; + int x,y; + int *f; + Iterator i; + MultiObj *m; + + VIDOOM_TRACE; + + SelectionCentre(&cx,&cy); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + m=GETMULTI(*f); + + x=m->x; + y=m->y; + + Rotate(cx,cy,&x,&y,angle); + + m->x=x; + m->y=y; + + i=IteratorNext(i); + } + + ApplySelectionCoords(); +} + + +void ScaleObject_MULTI(double scale) +{ + int cx,cy; + int x,y; + int *f; + Iterator i; + MultiObj *m; + + VIDOOM_TRACE; + + SelectionCentre(&cx,&cy); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + m=GETMULTI(*f); + + x=m->x; + y=m->y; + + Scale(cx,cy,&x,&y,scale); + + m->x=x; + m->y=y; + + i=IteratorNext(i); + } + + ApplySelectionCoords(); +} + + +void SetTagObject_MULTI(int tag) +{ + VIDOOM_TRACE; +} + + +void LocateObject_MULTI(void *obj) +{ + MultiObj *m; + + VIDOOM_TRACE; + + m=obj; + + ox=m->x-scale*SCRW/2; + oy=m->y+scale*SCRH/2; + +} + + +void ObjectInsert_MULTI(void) +{ + VIDOOM_TRACE; +} + + +void ObjectDelete_MULTI(void) +{ + VIDOOM_TRACE; +} + + +void ObjectMenu_MULTI(void) +{ + static double last_rot=0.0; + static double last_scale=0.0; + int cancel; + + VIDOOM_TRACE; + + cancel=FALSE; + + switch(GUI_menu("Mutlimode",ms.x,ms.y,multi_popup,GUI_CANCEL)) + { + case TM_SNAP: + { + Iterator i; + MultiObj *m; + int orig_lock; + int *s; + + orig_lock=grid_lock; + grid_lock=TRUE; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + m=GETMULTI(*s); + m->x=SnapX(m->x); + m->y=SnapX(m->y); + i=IteratorNext(i); + } + + grid_lock=orig_lock; + ApplySelectionCoords(); + FullRedraw(); + + break; + } + + case TM_MOVE: + MoveObject_MULTI(); + break; + + case TM_ROTATE: + rotate_dialog[D_ROTATE].data.d=last_rot; + + if (GUI_dialog("Rotate",D_ROTATE_NO,rotate_dialog)) + { + last_rot=rotate_dialog[D_ROTATE].data.d; + RotateObject_MULTI(-last_rot); + FullRedraw(); + } + else + cancel=TRUE; + + break; + + case TM_SCALE: + scale_dialog[D_SCALE].data.d=last_scale; + + if (GUI_dialog("Scale",D_SCALE_NO,scale_dialog)) + { + ScaleObject_MULTI(last_scale=scale_dialog[D_SCALE].data.d); + FullRedraw(); + } + else + cancel=TRUE; + + break; + + default: + cancel=TRUE; + break; + } + + if ((!cancel)&&(clear_on_menu)) + { + ClearSelection(); + FullRedraw(); + } +} + + +void ObjectKey_MULTI(GFXKey k) +{ + VIDOOM_TRACE; +} + + +int ObjectHasTag_MULTI(void *obj, int tag) +{ + VIDOOM_TRACE; + return(FALSE); +} + + +void SetSelect_MULTI(int i, int mode) +{ + Object *o; + MultiObj *m; + Map om; + + VIDOOM_TRACE; + o=MapElem(multimap,i); + + if ((m=o->data)) + { + o->select=mode; + + if (m->type==VERTEX) + om=vertex; + else + om=thing; + + o=MapElem(om,m->i); + o->select=mode; + } +} + + + +ObjDesc *ObjectOverlaid_MULTI(int x, int y, int *no) +{ + int n; + MultiObj *m; + EditThing *t; + EditVert *v; + ObjDesc *od; + int f; + + VIDOOM_TRACE; + + od=NULL; + + n=0; + + for(f=0;f<MapSize(multimap);f++) + { + m=GETMULTI(f); + + if ((m)&&(PositionOnObject_MULTI(x,y,m))) + { + n++; + od=ReGrab(od,sizeof(ObjDesc)*n); + od[n-1].no=f; + + switch(GetType(m,&v,&t)) + { + case VERTEX: + sprintf(od[n-1].detail,"Vertex %-5d - %d,%d", + f,v->v.x,v->v.y); + break; + + case THING: + sprintf(od[n-1].detail,"Thing %-5d - %s",f, + TrimStr(ThingName(t->t.type),29)); + break; + } + } + } + + *no=n; + return(od); +} + + + +/* END OF FILE */ diff --git a/editsect.c b/editsect.c new file mode 100644 index 0000000..3a13300 --- /dev/null +++ b/editsect.c @@ -0,0 +1,1735 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor SECTOR definitions + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" +#include "sectors.h" +#include "editvar.h" +#include "gensect.h" +#include "util.h" + +#include <ctype.h> +#include <math.h> + + +/* This is the data shared between the PickPoint() callback functions and the + Create function +*/ +typedef struct + { + int cx; + int cy; + Point p[64]; + int dx; + int dy; + int r; + double ai; + int sides; + } PickData; + +static PickData pick; + +/* This shows which LINEDEFs are drawn tagged +*/ +static List hilite=NULL; + + +/* ---------------------------------------- PickPoint() CALLBACKS +*/ +static void DrawVertSet(int *x, int *y) +{ + double ang; + int dx,dy; + int f; + + VIDOOM_TRACE; + + pick.dx=*x; + pick.dy=*y; + + dx=pick.cx-pick.dx; + dy=pick.cy-pick.dy; + + /* Calc angle that the line is at + */ + if (dx==0) + if (dy<0) + ang=RAD(0); + else + ang=RAD(180); + else if (dy==0) + if (dx<0) + ang=RAD(90); + else + ang=RAD(270); + else + { + ang=atan((double)dx/(double)dy); + + if (dy>0) + ang-=RAD(180); + } + + /* Calc line len + */ + pick.r=Len(pick.cx,pick.cy,pick.dx,pick.dy); + + for(f=0;f<pick.sides;f++) + { + pick.p[f].x=(int)(pick.cx+(pick.r*sin(ang))); + pick.p[f].y=(int)(pick.cy+(pick.r*cos(ang))); + pick.p[f].x=SnapX(pick.p[f].x); + pick.p[f].y=SnapY(pick.p[f].y); + ang+=pick.ai; + } + + for(f=0;f<pick.sides;f++) + GFX_line(MapToX(pick.p[f].x),MapToY(pick.p[f].y), + MapToX(pick.p[(f+1)%pick.sides].x), + MapToY(pick.p[(f+1)%pick.sides].y),WHITE); +} + + +/* ---------------------------------------- GENERATE VERTEX LIST FROM PICKDATA +*/ +static List GenVertList(void) +{ + Object o; + EditVert *v; + List l; + int f,r,n; + int match; + + for(f=0;f<pick.sides;f++) + { + match=TRUE; + + while(match) + for(match=FALSE,r=0;(r<pick.sides)&&(!match);r++) + if (r!=f) + if ((pick.p[r].x==pick.p[f].x)&& + (pick.p[r].y==pick.p[f].y)) + { + for(n=r;n<(pick.sides-1);n++) + { + pick.p[n].x=pick.p[n+1].x; + pick.p[n].y=pick.p[n+1].y; + } + + pick.sides=MAX(pick.sides-1,0); + + match=TRUE; + } + } + + if (pick.sides<3) + return(NULL); + + l=ListNew(sizeof(int)); + + for(f=0;f<pick.sides;f++) + { + v=Grab(sizeof(EditVert)); + v->v.x=pick.p[f].x; + v->v.y=pick.p[f].y; + v->l=ListNew(sizeof(int)); + + n=MapSize(vertex); + + o.select=SELECT_NONE; + o.data=v; + + MapAdd(vertex,n,&o); + ListAppend(l,&n); + } + + return(l); +} + + +/* ---------------------------------------- PRIVATE UTILS +*/ +static int SelectColour(int selmode) +{ + VIDOOM_TRACE; + + switch(selmode) + { + case SELECT_OVER: + return(OVERCOL); + break; + case SELECT_SELECTED: + return(SELCOL); + break; + default: + return(SECTCOL); + break; + } +} + + +void DrawSector(EditSect *s, int col, int check_tag) +{ + Iterator i; + EditLine *l; + + VIDOOM_TRACE; + + i=ListIterator(s->all); + + while(i) + { + memcpy(&l,IteratorData(i),sizeof(l)); + + if (!(check_tag)||(!tag_highlight)||(!InIntList(hilite,l->no))) + MapLineD(l,col); + + i=IteratorNext(i); + } +} + + +static void RemoveHighlights(void) +{ + Iterator i; + Object *o; + EditLine *l; + int *n; + + i=ListIterator(hilite); + + while(i) + { + n=IteratorData(i); + + if ((l=GETLINE(*n))) + { + if (l->sr->sector>=0) + if ((o=MapElem(sector,l->sr->sector))) + DrawSector(o->data,SelectColour(o->select),FALSE); + + if ((l->sl)&&(l->sl->sector>=0)) + if ((o=MapElem(sector,l->sl->sector))) + DrawSector(o->data,SelectColour(o->select),FALSE); + } + + i=IteratorNext(i); + } +} + + +static void SectorSelectionCentre(int *x,int *y) +{ + int *f; + EditSect *s; + Iterator i; + int min_x,min_y,max_x,max_y; + + VIDOOM_TRACE; + + i=ListIterator(selected); + + min_x=99999; + min_y=99999; + max_x=-99999; + max_y=-99999; + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + + min_x=MIN(s->min_x,min_x); + min_y=MIN(s->min_y,min_y); + max_x=MAX(s->max_x,max_x); + max_y=MAX(s->max_y,max_y); + + i=IteratorNext(i); + } + + *x=min_x+(max_x-min_x)/2; + *y=min_y+(max_y-min_y)/2; +} + + +/* ---------------------------------------- SECTOR VECTOR MAP UTILS +*/ +static int vno=0; +static char *vmap=NULL; + +static void InitVMAP(void) +{ + if (vno<MapSize(vertex)) + vmap=ReGrab(vmap,MapSize(vertex)); + + memset(vmap,FALSE,MapSize(vertex)); +} + + +/* ---------------------------------------- SECTOR LINEDEF MAP UTILS +*/ +static int lno=0; +static char *lmap=NULL; + +static void InitLMAP(void) +{ + if (lno<MapSize(linedef)) + lmap=ReGrab(lmap,MapSize(linedef)); + + memset(lmap,FALSE,MapSize(linedef)); +} + + +/* ---------------------------------------- SECTOR CONTAINER ROUTINES +*/ + +/* Returns -1 for no sector +*/ +int SectorHoldingPoint(int x,int y) +{ + Object *o; + int f; + + VIDOOM_TRACE; + + for(f=0;f<MapSize(sector);f++) + { + o=MapElem(sector,f); + + if ((o->data)&&(PositionOnObject_SECTOR(x,y,o->data))) + return(f); + } + + return(-1); +} + + +void SectorCalcContaining(int no,EditSect *s) +{ + int f; + EditLine *l; + int match; + + VIDOOM_TRACE; + + ListEmpty(s->v); + ListEmpty(s->sr); + ListEmpty(s->sl); + ListEmpty(s->all); + InitVMAP(); + + for(f=0;f<MapSize(linedef);f++) + { + match=FALSE; + l=GETLINE(f); + + if (l) + { + if ((l->sr)&&(l->sr->sector==no)) + { + ListAppend(s->sr,&l); + ListAppend(s->all,&l); + match=TRUE; + } + + if ((l->sl)&&(l->sl->sector==no)) + { + if (!match) + ListAppend(s->all,&l); + + ListAppend(s->sl,&l); + match=TRUE; + } + + if (match) + { + if (!vmap[l->l.from]) + { + ListAppend(s->v,&(l->l.from)); + vmap[l->l.from]=TRUE; + } + + if (!vmap[l->l.to]) + { + ListAppend(s->v,&(l->l.to)); + vmap[l->l.to]=TRUE; + } + } + } + } +} + + +void SectorCalcContainingAll(void) +{ + int f; + EditSect *s; + + VIDOOM_TRACE; + + for(f=0;f<MapSize(sector);f++) + { + s=GETSECT(f); + SectorCalcContaining(f,s); + } +} + + +/* ---------------------------------------- SECTOR STYLING FUNCS +*/ +static void ApplySectorStyle(EditSect *s, int flag, + DirName upper, DirName middle, DirName lower, + DirName floor, DirName ceiling) +{ + EditLine *l; + List track; + Iterator i,ti; + int ox,tw; + int *n; + + VIDOOM_TRACE; + + tw=CalcTextureWidth(upper,middle,lower); + + InitLMAP(); + + /* Dispose of two-sided linedefs wholly within the sector + */ + i=ListIterator(s->all); + + while(i) + { + memcpy(&l,IteratorData(i),sizeof(l)); + + if ((l->sl)&&(l->sl->sector==s->no)&&(l->sr->sector==s->no)) + lmap[l->no]=TRUE; + + i=IteratorNext(i); + } + + /* Set the floor and ceiling + */ + strcpy(s->s.floor_t,floor); + strcpy(s->s.ceiling_t,ceiling); + + /* Now draw a trail through the linedefs, aligning textures as we go along + */ + i=ListIterator(s->all); + + while(i) + { + memcpy(&l,IteratorData(i),sizeof(l)); + + if (!lmap[l->no]) + { + track=TrackLinedef(l->no); + ox=0; + ti=ListIterator(track); + + while(ti) + { + n=IteratorData(ti); + + l=GETLINE(*n); + + if (((l->sr->sector==s->no)&&(flag&SSTYLE_FACING_IN))|| + ((l->sr->sector!=s->no)&&(flag&SSTYLE_FACING_OUT))) + { + if (strcmp(l->sr->upper,empty_texture)) + { + strcpy(l->sr->upper,upper); + + if (flag&SSTYLE_UPPER_PEG) + l->l.flags|=upper_peg_mask; + else if (!(flag&SSTYLE_LEAVE_PEG)) + l->l.flags&=~upper_peg_mask; + } + + if (strcmp(l->sr->middle,empty_texture)) + strcpy(l->sr->middle,middle); + + if (strcmp(l->sr->lower,empty_texture)) + { + strcpy(l->sr->lower,lower); + + if (flag&SSTYLE_LOWER_PEG) + l->l.flags|=lower_peg_mask; + else if (!(flag&SSTYLE_LEAVE_PEG)) + l->l.flags&=~lower_peg_mask; + } + + l->sr->x=ox; + lmap[l->no]=TRUE; + } + + if ((l->sl)&& + (((l->sl->sector==s->no)&&(flag&SSTYLE_FACING_IN))|| + ((l->sl->sector!=s->no)&&(flag&SSTYLE_FACING_OUT)))) + { + if (strcmp(l->sl->upper,empty_texture)) + { + strcpy(l->sl->upper,upper); + + if (flag&SSTYLE_UPPER_PEG) + l->l.flags|=upper_peg_mask; + else if (!(flag&SSTYLE_LEAVE_PEG)) + l->l.flags&=~upper_peg_mask; + } + + if (strcmp(l->sl->middle,empty_texture)) + strcpy(l->sl->middle,middle); + + if (strcmp(l->sl->lower,empty_texture)) + { + strcpy(l->sl->lower,lower); + + if (flag&SSTYLE_LOWER_PEG) + l->l.flags|=lower_peg_mask; + else if (!(flag&SSTYLE_LEAVE_PEG)) + l->l.flags&=~lower_peg_mask; + } + + l->sl->x=ox; + lmap[l->no]=TRUE; + } + + ox=(ox+Len(l->v[0]->v.x,l->v[0]->v.y, + l->v[1]->v.x,l->v[1]->v.y))%tw; + + ti=IteratorNext(ti); + } + + ListClear(track); + } + + i=IteratorNext(i); + } +} + + +/* ---------------------------------------- GENERIC SECTOR FUNCS +*/ +int PositionOnObject_SECTOR(int x,int y,void *data) +{ + int cross; + EditSect *s; + EditLine *l; + Vertex *vi,*vj; + Iterator i; + + VIDOOM_TRACE; + + s=data; + + if (BOXBOUND(x,y,s->min_x,s->min_y,s->max_x,s->max_y)) + { + cross=FALSE; + + i=ListIterator(s->all); + + while(i) + { + memcpy(&l,IteratorData(i),sizeof(l)); + + /* Linedefs that have a right and left sidedef pointing at the + same sector are not counted + */ + if (!((l->sr)&&(l->sl)&&(l->sl->sector==l->sr->sector))) + { + vi=&(l->v[0]->v); + vj=&(l->v[1]->v); + + /* This code is taken from comp.graphics.algorithms FAQ 2.03 + */ + if ((((vi->y<=y) && (y<vj->y)) || + ((vj->y<=y) && (y<vi->y))) && + (x < (vj->x - vi->x) * (y - vi->y) / + (vj->y - vi->y) + vi->x)) + cross=!cross; + } + + i=IteratorNext(i); + } + + return(cross); + } + else + return(FALSE); +} + + +void SelectBox_SECTOR(int x1,int y1,int x2,int y2) +{ + Object *o; + EditSect *s; + int f; + + VIDOOM_TRACE; + + for(f=0;f<MapSize(sector);f++) + { + o=MapElem(sector,f); + + if ((s=o->data)) + { + if ((o->select!=SELECT_SELECTED)&& + (BOXBOUND(s->min_x,s->min_y,x1,y1,x2,y2))&& + (BOXBOUND(s->max_x,s->max_y,x1,y1,x2,y2))) + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + } + } +} + + +void SelectByType_SECTOR(void) +{ + Object *o; + EditSect *s; + int id; + int f; + + if ((id=SelectSector(hexen_mode))!=SECTOR_NULLID) + { + ClearSelection(); + + for(f=0;f<MapSize(sector);f++) + if ((o=MapElem(sector,f))) + if ((s=o->data)&&(s->s.special==id)) + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + } +} + + +void DrawObject_SECTOR(void *data, int selmode) +{ + static int drawn=FALSE; + Object *o; + EditSect *s; + EditLine *l; + int sel; + int f; + + VIDOOM_TRACE; + + if (!hilite) + hilite=ListNew(sizeof(int)); + + s=data; + + /* If the current object has changed, remove the highlighted tag objects + */ + if ((current==-1)&&(drawn)) + { + drawn=FALSE; + RemoveHighlights(); + ListEmpty(hilite); + } + + /* Draw tagged linedefs + */ + if ((tag_highlight)&&(current==s->no)) + { + RemoveHighlights(); + ListEmpty(hilite); + + if (s->s.tag) + { + drawn=TRUE; + + for(f=0;f<MapSize(linedef);f++) + if ((l=GETLINE(f))) + if ((l->l.tag==s->s.tag)&&(LineOnDisplay(l))) + { + sel=FALSE; + + if (l->sr->sector>=0) + if ((o=MapElem(sector,l->sr->sector))) + sel=(o->select!=SELECT_NONE); + + if ((l->sl)&&(l->sl->sector>=0)) + if ((o=MapElem(sector,l->sl->sector))) + sel|=(o->select!=SELECT_NONE); + + if (!sel) + { + IntListUniqAdd(hilite,f); + MapLineD(l,TAGCOL); + } + } + } + + } + + DrawSector(s,SelectColour(selmode),TRUE); +} + + +void DrawObjectInfo_SECTOR(void) +{ + EditSect *s; + + VIDOOM_TRACE; + + if (!draw_current_info) + return; + + if ((current!=-1)&&((s=GETSECT(current)))) + { + if (GenSectName(hexen_mode,s->s.special,SectorName)) + GuiDrawInfoBox("SECTOR",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "Sector Number : %-5d|" + "Floor : %-5d|" + "Ceiling : %-5d|" + "Height : %-5d|" + "Light : %-5d|" + "Floor texture : %-10s|" + "Ceiling texture : %-10s|" + "Sector Type : %-30.30s[Gen]|" + "Tag : %-5d", + current, + s->s.floor, + s->s.ceiling, + s->s.ceiling-s->s.floor, + s->s.light, + s->s.floor_t, + s->s.ceiling_t, + GenSectName(hexen_mode, + s->s. + special,SectorName), + s->s.tag); + else + GuiDrawInfoBox("SECTOR",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "Sector Number : %-5d|" + "Floor : %-5d|" + "Ceiling : %-5d|" + "Height : %-5d|" + "Light : %-5d|" + "Floor texture : %-10s|" + "Ceiling texture : %-10s|" + "Sector Type : %-35.35s|" + "Tag : %-5d", + current, + s->s.floor, + s->s.ceiling, + s->s.ceiling-s->s.floor, + s->s.light, + s->s.floor_t, + s->s.ceiling_t, + SectorName(hexen_mode,s->s.special), + s->s.tag); + } + else + GuiDrawInfoBox("SECTOR",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "Sector Number : -|" + "Floor : -|" + "Ceiling : -|" + "Height : -|" + "Light : -|" + "Floor texture : -|" + "Ceiling texture : -|" + "Sector Type : %-35s|" + "Tag : -","-"); +} + + +void DrawObjectHeader_SECTOR(void) +{ + if (tag_highlight) + GFX_print(0,FH,WHITE,"Tag highlight: ON"); + else + GFX_print(0,FH,WHITE,"Tag highlight: OFF"); + + switch(sector_move) + { + case MOVE_ALL: + GFX_print(SCRW/2,FH,WHITE,"Move mode: ALL"); + break; + case MOVE_LEFT: + GFX_print(SCRW/2,FH,WHITE,"Move mode: LEFT"); + break; + case MOVE_RIGHT: + GFX_print(SCRW/2,FH,WHITE,"Move mode: RIGHT"); + break; + } +} + + +void MoveObject_SECTOR(void) +{ + GFXEvent ev; + int omx,omy; + int dmx,dmy; + int done; + Iterator i; + Iterator i2; + EditSect *s; + EditLine *l; + EditVert *v; + List orig; + int *f; + Point p; + List vert; + int cancel; + + VIDOOM_TRACE; + + omx=SnapX(XToMap(ms.x)); + omy=SnapY(YToMap(ms.y)); + GFX_bounce(); + + done=FALSE; + cancel=FALSE; + orig=ListNew(sizeof(Point)); + vert=ListNew(sizeof(EditVert *)); + + InitVMAP(); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + + switch(sector_move) + { + case MOVE_ALL: + i2=ListIterator(s->all); + break; + + case MOVE_LEFT: + i2=ListIterator(s->sl); + break; + + case MOVE_RIGHT: + default: + i2=ListIterator(s->sr); + break; + } + + while(i2) + { + memcpy(&l,IteratorData(i2),sizeof(l)); + + if (!vmap[l->l.from]) + { + v=GETVERT(l->l.from); + ListAppend(vert,&v); + p.x=l->v[0]->v.x; + p.y=l->v[0]->v.y; + ListAppend(orig,&p); + vmap[l->l.from]=TRUE; + } + + if (!vmap[l->l.to]) + { + v=GETVERT(l->l.to); + ListAppend(vert,&v); + p.x=l->v[1]->v.x; + p.y=l->v[1]->v.y; + ListAppend(orig,&p); + vmap[l->l.to]=TRUE; + } + + i2=IteratorNext(i2); + } + + i=IteratorNext(i); + } + + while(!done) + { + GuiDrawInfoBox("Move SECTOR",GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE, + "Left mouse button to place|ESC to cancel"); + + GFX_redraw(); + GFX_await_input_full(&ev); + + switch(ev.type) + { + case GFX_KEY_EVENT: + HandleMoveKey(ev.key,FALSE); + + /* Cancel the move + */ + if (ev.key.code==GFX_ESC) + { + cancel=TRUE; + + i=ListIterator(vert); + i2=ListIterator(orig); + + while(i2) + { + memcpy(&v,IteratorData(i),sizeof(v)); + + memcpy(&p,IteratorData(i2),sizeof(Point)); + v->v.x=p.x; + v->v.y=p.y; + + i2=IteratorNext(i2); + i=IteratorNext(i); + } + + done=TRUE; + dmx=0; + dmy=0; + } + else + { + dmx=SnapX(XToMap(ms.x))-omx; + dmy=SnapY(YToMap(ms.y))-omy; + } + + break; + + case GFX_MOUSE_EVENT: + memcpy(&ms,&ev.mouse,sizeof(ev.mouse)); + + if (ms.b&GFX_BUTLEFT) + done=TRUE; + + dmx=SnapX(XToMap(ms.x))-omx; + dmy=SnapY(YToMap(ms.y))-omy; + break; + + default: + dmx=0; + dmy=0; + break; + } + + if ((dmx)||(dmy)) + { + i=ListIterator(vert); + while(i) + { + memcpy(&v,IteratorData(i),sizeof(v)); + + v->v.x+=dmx; + v->v.y+=dmy; + + i=IteratorNext(i); + } + + omx=SnapX(XToMap(ms.x)); + omy=SnapY(YToMap(ms.y)); + + FullRedraw(); + } + } + + ListClear(orig); + ListClear(vert); + + if ((clear_on_move)&&(!cancel)) + ClearSelection(); + + FullRedraw(); +} + + +void RotateObject_SECTOR(double angle) +{ + int cx,cy; + int x,y; + int *f; + Short *r; + Iterator i,i2; + EditSect *s; + EditVert *v; + + VIDOOM_TRACE; + + InitVMAP(); + + SectorSelectionCentre(&cx,&cy); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + + i2=ListIterator(s->v); + + while(i2) + { + r=IteratorData(i2); + v=GETVERT(*r); + + if (!vmap[*r]) + { + vmap[*r]=TRUE; + + x=v->v.x; + y=v->v.y; + + Rotate(cx,cy,&x,&y,angle); + + v->v.x=x; + v->v.y=y; + } + + i2=IteratorNext(i2); + } + + i=IteratorNext(i); + } +} + + +void ScaleObject_SECTOR(double scale) +{ + int cx,cy; + int x,y; + int *f; + Short *r; + Iterator i,i2; + EditSect *s; + EditVert *v; + + VIDOOM_TRACE; + + InitVMAP(); + + SectorSelectionCentre(&cx,&cy); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + + i2=ListIterator(s->v); + + while(i2) + { + r=IteratorData(i2); + v=GETVERT(*r); + + if (!vmap[*r]) + { + vmap[*r]=TRUE; + + x=v->v.x; + y=v->v.y; + + Scale(cx,cy,&x,&y,scale); + + v->v.x=x; + v->v.y=y; + } + + i2=IteratorNext(i2); + } + + i=IteratorNext(i); + } +} + + +void SetTagObject_SECTOR(int tag) +{ + int *f; + Iterator i; + EditSect *s; + + VIDOOM_TRACE; + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + s->s.tag=tag; + i=IteratorNext(i); + } +} + + +void LocateObject_SECTOR(void *obj) +{ + int cx,cy; + EditSect *s; + + VIDOOM_TRACE; + + s=obj; + cx=s->min_x+(s->max_x-s->min_x)/2; + cy=s->min_y+(s->max_y-s->min_y)/2; + ox=cx-scale*SCRW/2; + oy=cy+scale*SCRH/2; +} + + +void ObjectInsert_SECTOR(void) +{ +# define SM_UNBOUND 1 +# define SM_POLY 2 + + static PLAT_MENU sector_insert_menu[]= + { + {"Create unbound sector",SM_UNBOUND}, + {"Create polygon",SM_POLY}, + {NULL,GUI_CANCEL} + }; + + VIDOOM_TRACE; + + switch(GUI_menu("Sector",ms.x,ms.y,sector_insert_menu,GUI_CANCEL)) + { + case SM_UNBOUND: + { + int sec; + + sec=CreateUnboundSector(normal_sector,default_floor_height, + default_ceiling_height,default_light_level, + 0,empty_texture,empty_texture); + + GuiInfoBox("NOTICE","A sector number %d has been created.|" + "SIDEDEFS will have to bound to the sector " + "before it|becomes visible and editable.",sec); + break; + } + + case SM_POLY: + { + static int last_sides=4; + List sel; + + nosides_dialog[D_NOSIDES].data.i=last_sides; + + if (GUI_dialog("Create sector",D_NOSIDES_NO,nosides_dialog)) + { + pick.sides=nosides_dialog[D_NOSIDES].data.i; + + if ((pick.sides<3)||(pick.sides>64)) + { + GuiInfoBox("ERROR","Only enter a value between 3 and 64"); + break; + } + else + if (PickPoint("Select centre of new sector", + &pick.cx,&pick.cy,NULL,NULL,NULL)) + { + pick.ai=RAD(360.0/pick.sides); + + if (PickPoint("Select the width and rotation " + "of the sector",&pick.dx,&pick.dy, + DrawVertSet,NULL,NULL)) + { + /* Merge identical vertices from the list and + check that the vertices will not be dubious + */ + if ((sel=GenVertList())) + { + if (!CreateSector(sel)) + { + Iterator i; + Object *o; + EditVert *v; + int *n; + + i=ListIterator(sel); + + while(i) + { + n=IteratorData(i); + o=MapElem(vertex,*n); + v=o->data; + + ListClear(v->l); + Release(v); + o->data=NULL; + + i=IteratorNext(i); + } + } + else + { + FullRedraw(); + last_sides=pick.sides; + } + + ListClear(sel); + } + else + { + GuiInfoBox("ERROR","Shape makes no sense"); + FullRedraw(); + break; + } + } + } + } + + break; + } + + default: + break; + } +} + + +void ObjectDelete_SECTOR(void) +{ + VIDOOM_TRACE; + + GuiInfoBox("NOTICE","Sector cannot be deleted| |" + "Sectors are automatically deleted|" + "when saving the map if no LINEDEFs|" + "are bound to it."); +} + + +void ObjectMenu_SECTOR(void) +{ + Iterator i; + EditSect *s; + int *f; + int cancel; + + VIDOOM_TRACE; + + cancel=FALSE; + + switch(GUI_menu("Sector",ms.x,ms.y,sector_popup,GUI_CANCEL)) + { + case TM_FLOOR: + s=SelHead(); + sector_fl_dialog[D_SECTOR_FLOOR].data.i=s->s.floor; + + if (GUI_dialog("Floor height",D_SECTOR_FL_NO,sector_fl_dialog)) + { + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + s->s.floor=sector_fl_dialog[D_SECTOR_FLOOR].data.i; + i=IteratorNext(i); + } + + i=IteratorClear(i); + FullRedraw(); + } + break; + + case TM_CEILING: + s=SelHead(); + sector_ce_dialog[D_SECTOR_CEILING].data.i=s->s.ceiling; + + if (GUI_dialog("Ceiling height",D_SECTOR_CE_NO,sector_ce_dialog)) + { + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + s->s.ceiling=sector_ce_dialog[D_SECTOR_CEILING].data.i; + i=IteratorNext(i); + } + + i=IteratorClear(i); + FullRedraw(); + } + break; + + case TM_LIGHT: + { + int sweep; + + s=SelHead(); + + if (ListSize(selected)<3) + sweep=FALSE; + else + sweep=YesNo("%d sectors selected. Do lighting sweep?", + ListSize(selected)); + + if (!sweep) + { + sector_li_dialog[D_SECTOR_LIGHT].data.i=s->s.light; + + if (GUI_dialog("Light level",D_SECTOR_LI_NO,sector_li_dialog)) + { + i=ListIterator(selected); + + sector_li_dialog[D_SECTOR_LIGHT].data.i= + MIN(255,MAX(sector_li_dialog[D_SECTOR_LIGHT].data.i,0)); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + s->s.light=sector_li_dialog[D_SECTOR_LIGHT].data.i; + i=IteratorNext(i); + } + + i=IteratorClear(i); + FullRedraw(); + } + } + else + { + static int min=0; + static int max=255; + static int bounce=FALSE; + int t_min=0,t_max=0; + int start; + int inc; + int ok; + int valid; + + start=s->s.light; + + l_range_dialog[D_L_RANGE_START].data.i=start; + l_range_dialog[D_L_RANGE_MIN].data.i=min; + l_range_dialog[D_L_RANGE_MAX].data.i=max; + + if (start<128) + { + inc=(int)((double)(max-start)/ + (double)(ListSize(selected)-2)+0.5); + + if (inc==0) + inc=SGN(max-start); + } + else + { + inc=(int)((double)(min-start)/ + (double)(ListSize(selected)-2)+0.5); + + if (inc==0) + inc=SGN(min-start); + } + + l_range_dialog[D_L_RANGE_INC].data.i=inc; + SET_YESNO_DIAL(l_range_dialog[D_L_RANGE_PULSE],bounce); + + do { + if ((ok=GUI_dialog("Lighting sweep", + D_L_RANGE_NO,l_range_dialog))) + { + inc=l_range_dialog[D_L_RANGE_INC].data.i; + start=l_range_dialog[D_L_RANGE_START].data.i; + t_min=l_range_dialog[D_L_RANGE_MIN].data.i; + t_max=l_range_dialog[D_L_RANGE_MAX].data.i; + + valid=FALSE; + + if ((t_min<0)||(t_min>255)||(t_max<0)|| + (t_max>255)||(start<0)||(start>255)) + GuiInfoBox("ERROR","Light levels must be|" + "in range 0 to 255"); + else if ((t_min>=t_max)||(t_max<=t_min)) + GuiInfoBox("ERROR","min and max make no sense"); + else if (ABS(inc)>(t_max-t_min)) + GuiInfoBox("ERROR", + "There must be enough inbetween|" + "min and max for the adjustment"); + else if ((start<t_min)||(start>t_max)) + GuiInfoBox("ERROR", + "Start must inbetween min and max"); + else + valid=TRUE; + } + else + valid=TRUE; + + } while((ok)&&(!valid)); + + if (ok) + { + min=t_min; + max=t_max; + bounce=l_range_dialog[D_L_RANGE_PULSE].data.pl.current; + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + s->s.light=start; + + start+=inc; + + start=MIN(start,max); + start=MAX(start,min); + + if (((start==min)||(start==max))&&(bounce)) + inc=-inc; + + i=IteratorNext(i); + } + + i=IteratorClear(i); + FullRedraw(); + } + } + + break; + } + + case TM_TAG: + s=SelHead(); + tag_dialog[D_TAG].data.i=s->s.tag; + + if (GUI_dialog("Tag",D_TAG_NO,tag_dialog)) + { + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + s->s.tag=tag_dialog[D_TAG].data.i; + i=IteratorNext(i); + } + + i=IteratorClear(i); + FullRedraw(); + } + break; + + case TM_FLOOR_T: + { + DirName d; + + if ((GetFlat("Floor texture",d))) + { + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + strcpy(s->s.floor_t,d); + i=IteratorNext(i); + } + + FullRedraw(); + } + + break; + } + + case TM_CEILING_T: + { + DirName d; + + if ((GetFlat("Ceiling texture",d))) + { + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + strcpy(s->s.ceiling_t,d); + i=IteratorNext(i); + } + + FullRedraw(); + } + + break; + } + + case TM_TYPE: + { + int id; + + id=SelectSector(hexen_mode); + + if (id!=SECTOR_NULLID) + { + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + s->s.special=id; + i=IteratorNext(i); + } + + FullRedraw(); + } + break; + } + + case TM_GEN_TYPE: + if (GenSectNoClasses(hexen_mode)) + { + int id; + + s=SelHead(); + id=SelectGenSect(hexen_mode,s->s.special); + + if (id!=GENSECT_NULLID) + { + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + s->s.special=id; + i=IteratorNext(i); + } + + FullRedraw(); + } + } + else + GuiInfoBox("ERROR","No generalised sectors defined|in config"); + break; + + case TM_VAL: + { + static int id; + + if (GetNumber("Enter SECTOR type",&id)) + { + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + s->s.special=id; + i=IteratorNext(i); + } + + FullRedraw(); + } + } + break; + + case TM_STYLE: + { + int flag; + DirName upper,middle,lower,floor,ceiling; + + if (ChooseSectorStyle(&flag,upper,middle,lower,floor,ceiling)) + { + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + ApplySectorStyle(s,flag,upper,middle,lower,floor,ceiling); + i=IteratorNext(i); + } + } + + break; + } + + case TM_MOVE: + MoveObject_SECTOR(); + break; + + case TM_DELETE: + ObjectDelete_SECTOR(); + break; + + default: + cancel=TRUE; + break; + } + + if ((!cancel)&&(clear_on_menu)) + { + ClearSelection(); + FullRedraw(); + } +} + + +void ObjectKey_SECTOR(GFXKey k) +{ + Iterator i; + EditSect *s; + int tmpsel; + int *f; + + /* Check keys that need no objects + */ + if (k.code==GFX_ASCII) + switch(toupper(k.ascii)) + { + case 'H': + tag_highlight=!tag_highlight; + FullRedraw(); + break; + + case 'M': + if (sector_move==MOVE_ALL) + sector_move=MOVE_RIGHT; + else if (sector_move==MOVE_RIGHT) + sector_move=MOVE_LEFT; + else if (sector_move==MOVE_LEFT) + sector_move=MOVE_ALL; + + DrawHeader(); + GFX_redraw(); + break; + + default: + break; + } + + tmpsel=TmpAddCurrent(); + + if (ListSize(selected)==0) + return; + + if (k.code!=GFX_ASCII) + switch(k.code) + { + default: + break; + } + else + { + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + s=GETSECT(*f); + + switch(toupper(k.ascii)) + { + case ',': + s->s.floor-=8; + if (s->s.floor>s->s.ceiling) + s->s.floor+=8; + break; + + case '.': + s->s.floor+=8; + if (s->s.floor>s->s.ceiling) + s->s.floor-=8; + break; + + case '<': + s->s.ceiling-=8; + if (s->s.ceiling<s->s.floor) + s->s.ceiling+=8; + break; + + case '>': + s->s.ceiling+=8; + if (s->s.ceiling<s->s.floor) + s->s.ceiling-=8; + break; + + case '-': + s->s.light=MAX(0,s->s.light-16); + break; + + case '+': + s->s.light=MIN(255,s->s.light+16); + break; + + default: + break; + } + + i=IteratorNext(i); + } + + FullRedraw(); + } + + if (tmpsel) + ListEmpty(selected); +} + + +int ObjectHasTag_SECTOR(void *obj, int tag) +{ + EditSect *s; + + return ((s=obj)&&(s->s.tag==tag)); +} + + +ObjDesc *ObjectOverlaid_SECTOR(int x, int y, int *no) +{ + int n; + EditSect *s; + ObjDesc *od; + int f; + + VIDOOM_TRACE; + + od=NULL; + + n=0; + + for(f=0;f<MapSize(sector);f++) + { + s=GETSECT(f); + + if ((s)&&(PositionOnObject_SECTOR(x,y,s))) + { + n++; + od=ReGrab(od,sizeof(ObjDesc)*n); + od[n-1].no=f; + + sprintf(od[n-1].detail,"%5d - %s (Tag %d)",f, + TrimStr(SectorName + (hexen_mode,s->s.special),50), + s->s.tag); + } + } + + *no=n; + return(od); +} + + +/* END OF FILE */ diff --git a/editsel.c b/editsel.c new file mode 100644 index 0000000..2a02755 --- /dev/null +++ b/editsel.c @@ -0,0 +1,133 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor selection definitions + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" +#include "editvar.h" + + + +/* After all that hard work of making the Object type the only thing that + the top level selection code needs to worry about, the Multi edit mode + goes and throws a spanner in the works.... +*/ +void SetSelect_GENERIC(int i, int mode) +{ + Object *o; + + o=MapElem(map,i); + o->select=mode; +} + + +int TmpAddCurrent(void) +{ + if ((ListSize(selected)==0)&&(current!=-1)) + { + ListAppend(selected,¤t); + return(TRUE); + } + else + return(FALSE); +} + + +void ClearSelection(void) +{ + int *n; + Iterator i; + + VIDOOM_TRACE; + + if (map) + { + if (current!=-1) + SetSelect(current,SELECT_NONE); + + i=ListIterator(selected); + + while(i) + { + n=IteratorData(i); + SetSelect(*n,SELECT_NONE); + i=IteratorNext(i); + } + } + + selected=ListEmpty(selected); + current=-1; +} + + +void ClearSelectionLeaveCurrent(void) +{ + int *n; + Iterator i; + + VIDOOM_TRACE; + + if (map) + { + i=ListIterator(selected); + + while(i) + { + n=IteratorData(i); + SetSelect(*n,SELECT_NONE); + i=IteratorNext(i); + } + } + + selected=ListEmpty(selected); +} + + +void *SelHead(void) +{ + Iterator i; + int *f; + Object *o; + void *p; + + VIDOOM_TRACE; + + i=ListIterator(selected); + p=NULL; + + if ((f=IteratorData(i))) + { + o=MapElem(map,*f); + p=o->data; + } + + IteratorClear(i); + + return(p); +} + + +/* END OF FILE */ diff --git a/editsrot.c b/editsrot.c new file mode 100644 index 0000000..c4f1bea --- /dev/null +++ b/editsrot.c @@ -0,0 +1,76 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor scale and rotation routines + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" +#include "editvar.h" + +#include <math.h> + + +/* ---------------------------------------- ROTATION ROUTINES +*/ +void Rotate(int cx,int cy,int *x,int *y, double ang) +{ + double r,t; + + VIDOOM_TRACE; + + ang=RAD(ang); + + *x-=cx; + *y-=cy; + + r=hypot((double)*x,(double)*y); + t=atan2((double)*y,(double)*x); + + *x=(int)(r*cos(t+ang)+0.5); + *y=(int)(r*sin(t+ang)+0.5); + + *x+=cx; + *y+=cy; +} + + +/* ---------------------------------------- SCALE ROUTINES +*/ +void Scale(int cx,int cy,int *x,int *y, double sc) +{ + VIDOOM_TRACE; + + *x-=cx; + *y-=cy; + + *x=(int)((*x)*sc); + *y=(int)((*y)*sc); + + *x+=cx; + *y+=cy; +} + + +/* END OF FILE */ diff --git a/editthng.c b/editthng.c new file mode 100644 index 0000000..79d8130 --- /dev/null +++ b/editthng.c @@ -0,0 +1,1126 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor THING definitions + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" +#include "specials.h" +#include "flags.h" +#include "util.h" + +#include <ctype.h> + +#include "editvar.h" + + +/* ---------------------------------------- PRIVATE UTILS +*/ +static void ThingSelectionCentre(int *x,int *y) +{ + int *f; + EditThing *t; + Iterator i; + int min_x,min_y,max_x,max_y; + + VIDOOM_TRACE; + + i=ListIterator(selected); + + min_x=99999; + min_y=99999; + max_x=-99999; + max_y=-99999; + + while(i) + { + f=IteratorData(i); + t=GETTHING(*f); + + min_x=MIN(t->t.x,min_x); + min_y=MIN(t->t.y,min_y); + max_x=MAX(t->t.x,max_x); + max_y=MAX(t->t.y,max_y); + + i=IteratorNext(i); + } + + *x=min_x+(max_x-min_x)/2; + *y=min_y+(max_y-min_y)/2; +} + + +/* ---------------------------------------- GENERIC THING FUNCS +*/ +int PositionOnObject_THING(int x,int y,void *data) +{ + EditThing *t; + int r; + + VIDOOM_TRACE; + + t=data; + r=ThingRadius(t->t.type,NULL); + + return (RADBOUND(x,y,t->t.x,t->t.y,r)); +} + + +void SelectBox_THING(int x1,int y1,int x2,int y2) +{ + Object *o; + EditThing *t; + int f; + + VIDOOM_TRACE; + + for(f=0;f<MapSize(thing);f++) + { + o=MapElem(thing,f); + + if ((t=o->data)) + { + if ((o->select!=SELECT_SELECTED)&& + (BOXBOUND(t->t.x,t->t.y,x1,y1,x2,y2))) + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + } + } +} + + +void SelectByType_THING(void) +{ + Object *o; + EditThing *t; + int id; + int f; + + if ((hexen_mode)&&(YesNo("Select by special action|rather than type?"))) + { + if ((id=SelectSpecial())!=SPECIAL_NULLID) + { + ClearSelection(); + + for(f=0;f<MapSize(thing);f++) + if ((o=MapElem(thing,f))) + if ((t=o->data)&&(t->t.special==id)) + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + } + } + else + if ((id=SelectThing())!=THING_NULLID) + { + ClearSelection(); + + for(f=0;f<MapSize(thing);f++) + if ((o=MapElem(thing,f))) + if ((t=o->data)&&(t->t.type==id)) + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + } +} + + +void DrawObject_THING(void *data, int selmode) +{ + EditThing *t; + int a; + int lx,ly; + int r; + int col; + + VIDOOM_TRACE; + t=data; + + a=ANGLENUM(t->t.ang); + r=ThingRadius(t->t.type,&col); + + switch(selmode) + { + case SELECT_OVER: + col=OVERCOL; + break; + case SELECT_SELECTED: + col=SELCOL; + break; + default: + break; + } + + lx=t->t.x+(t_arrow[a].x*r); + ly=t->t.y+(t_arrow[a].y*r); + + MapCircle(t->t.x,t->t.y,r,col); + DrawArrow(t->t.x,t->t.y,lx,ly,col); +} + + +void DrawObjectInfo_THING(void) +{ + EditThing *ti; + char *nm; + char *a[5]; + + VIDOOM_TRACE; + + if (!draw_current_info) + return; + + if ((current!=-1)&&((ti=GETTHING(current)))) + { + if (hexen_mode) + { + nm=SpecialName(ti->t.special,a); + + GuiDrawInfoBox("THING",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "Thing No : %d|" + "Thing ID : %d|" + "Z Pos : %-30d|" + "Angle : %s (%d)|" + "Flags : %s (0x%.2x)|" + "Type : %-30.30s", + current, + ti->t.id, + ti->t.z, + ANGLESTR(ti->t.ang),ti->t.ang, + FlagText(hexen_mode,THING_FLAGS, + (int)ti->t.flags),ti->t.flags, + ThingName(ti->t.type)); + + GuiDrawInfoBox("THING SPECIAL",GUI_FLUSH_RIGHT, + GUI_FLUSH_LOWER,FALSE, + "Special : %-20.20s|" + "%-10s : %-3d|" + "%-10s : %-3d|" + "%-10s : %-3d|" + "%-10s : %-3d|" + "%-10s : %-3d", + nm, + (a[0] ? a[0] : "N/A"),ti->t.args[0], + (a[1] ? a[1] : "N/A"),ti->t.args[1], + (a[2] ? a[2] : "N/A"),ti->t.args[2], + (a[3] ? a[3] : "N/A"),ti->t.args[3], + (a[4] ? a[4] : "N/A"),ti->t.args[4]); + } + else + GuiDrawInfoBox("THING",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "Thing No : %d|" + "Angle : %s (%d)|" + "Flags : %s (0x%.2x)|" + "Type : %-60s", + current, + ANGLESTR(ti->t.ang),ti->t.ang, + FlagText(hexen_mode,THING_FLAGS, + (int)ti->t.flags),ti->t.flags, + ThingName(ti->t.type)); + } + else + if (hexen_mode) + { + GuiDrawInfoBox("THING",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "Thing No : -|" + "Thing ID : -|" + "Z Pos : -|" + "Angle : -|" + "Flags : - (-)|" + "Type : %-30s",""); + + GuiDrawInfoBox("THING SPECIAL",GUI_FLUSH_RIGHT, + GUI_FLUSH_LOWER,FALSE, + "Special : %-20s|" + "- : -|" + "- : -|" + "- : -|" + "- : -|" + "- : -|",""); + } + else + GuiDrawInfoBox("THING",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "Thing No : -|" + "Angle : -|" + "Flags : - (-)|" + "Type : %-60s",""); +} + + +void DrawObjectHeader_THING(void) +{ +} + + +void MoveObject_THING(void) +{ + GFXEvent ev; + int omx,omy; + int dmx,dmy; + int done; + Iterator i; + Iterator i2; + EditThing *t; + List orig; + int *f; + Point p; + int cancel=FALSE; + + VIDOOM_TRACE; + + omx=SnapX(XToMap(ms.x)); + omy=SnapY(YToMap(ms.y)); + GFX_bounce(); + + done=FALSE; + cancel=FALSE; + orig=ListNew(sizeof(Point)); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + t=GETTHING(*f); + p.x=t->t.x; + p.y=t->t.y; + ListAppend(orig,&p); + i=IteratorNext(i); + } + + while(!done) + { + GuiDrawInfoBox("Move THING",GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE, + "Left mouse button to place|ESC to cancel"); + + GFX_redraw(); + GFX_await_input_full(&ev); + + switch(ev.type) + { + case GFX_KEY_EVENT: + HandleMoveKey(ev.key,FALSE); + + /* Cancel the move + */ + if (ev.key.code==GFX_ESC) + { + cancel=TRUE; + + i=ListIterator(selected); + i2=ListIterator(orig); + + while(i2) + { + f=IteratorData(i); + memcpy(&p,IteratorData(i2),sizeof(Point)); + t=GETTHING(*f); + t->t.x=p.x; + t->t.y=p.y; + + i=IteratorNext(i); + i2=IteratorNext(i2); + } + + done=TRUE; + dmx=0; + dmy=0; + } + else + { + dmx=SnapX(XToMap(ms.x))-omx; + dmy=SnapY(YToMap(ms.y))-omy; + } + + break; + + case GFX_MOUSE_EVENT: + memcpy(&ms,&ev.mouse,sizeof(ev.mouse)); + + if (ms.b&GFX_BUTLEFT) + done=TRUE; + + dmx=SnapX(XToMap(ms.x))-omx; + dmy=SnapY(YToMap(ms.y))-omy; + break; + + default: + dmx=0; + dmy=0; + break; + } + + if ((dmx)||(dmy)) + { + i=ListIterator(selected); + while(i) + { + f=IteratorData(i); + + t=GETTHING(*f); + + t->t.x+=dmx; + t->t.y+=dmy; + + i=IteratorNext(i); + } + + omx=SnapX(XToMap(ms.x)); + omy=SnapY(YToMap(ms.y)); + + FullRedraw(); + } + } + + ListClear(orig); + + if ((clear_on_move)&&(!cancel)) + ClearSelection(); + + FullRedraw(); +} + + +void RotateObject_THING(double angle) +{ + int cx,cy; + int x,y; + int *f; + Iterator i; + EditThing *t; + + VIDOOM_TRACE; + + ThingSelectionCentre(&cx,&cy); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + t=GETTHING(*f); + + x=t->t.x; + y=t->t.y; + + Rotate(cx,cy,&x,&y,angle); + + t->t.x=x; + t->t.y=y; + + i=IteratorNext(i); + } +} + + +void ScaleObject_THING(double scale) +{ + int cx,cy; + int x,y; + int *f; + Iterator i; + EditThing *t; + + VIDOOM_TRACE; + + ThingSelectionCentre(&cx,&cy); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + t=GETTHING(*f); + + x=t->t.x; + y=t->t.y; + + Scale(cx,cy,&x,&y,scale); + + t->t.x=x; + t->t.y=y; + + i=IteratorNext(i); + } +} + + +void SetTagObject_THING(int tag) +{ + VIDOOM_TRACE; +} + + +void LocateObject_THING(void *obj) +{ + EditThing *t; + + VIDOOM_TRACE; + + t=obj; + + ox=t->t.x-scale*SCRW/2; + oy=t->t.y+scale*SCRH/2; +} + + +void ObjectInsert_THING(void) +{ + Object o; + EditThing *t; + int f; + + VIDOOM_TRACE; + + t=Grab(sizeof(EditThing)); + memcpy(&t->t,&ed_thing,sizeof(Thing)); + t->t.x=SnapX(XToMap(ms.x)); + t->t.y=SnapY(YToMap(ms.y)); + + f=MapSize(thing); + + switch(insert_select) + { + case HOVER_NONE: + o.select=SELECT_NONE; + break; + case HOVER_ADD: + case HOVER_SINGLE: + o.select=SELECT_SELECTED; + break; + } + + o.data=t; + DrawObject_THING(t,o.select); + MapAdd(thing,f,&o); + + switch(insert_select) + { + case HOVER_NONE: + break; + case HOVER_ADD: + ListAppend(selected,&f); + break; + case HOVER_SINGLE: + ClearSelection(); + ListAppend(selected,&f); + FullRedraw(); + break; + } +} + + +void ObjectDelete_THING(void) +{ + Iterator i; + Object *o; + int *f; + + VIDOOM_TRACE; + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + + o=MapElem(thing,*f); + + Release(o->data); + o->data=NULL; + o->select=SELECT_NONE; + + i=IteratorNext(i); + } + + ClearSelection(); + FullRedraw(); +} + + +void ObjectMenu_THING(void) +{ + EditThing *t; + int f,*s; + Iterator i; + int cancel; + int redraw=FALSE; + + VIDOOM_TRACE; + + cancel=FALSE; + GFX_redraw(); + + switch(GUI_menu("Thing",ms.x,ms.y, + (hexen_mode ? thing_popup_hexen:thing_popup),GUI_CANCEL)) + { + case TM_TYPE: + { + Short id; + + id=(Short)SelectThing(); + + if (id!=THING_NULLID) + { + ed_thing.type=id; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + t->t.type=id; + i=IteratorNext(i); + } + + redraw=TRUE; + } + } + break; + + case TM_SPECIAL: + { + Short id; + + id=(Short)SelectSpecial(); + + if (id!=SPECIAL_NULLID) + { + ed_thing.special=id; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + t->t.special=id; + i=IteratorNext(i); + } + + redraw=TRUE; + } + break; + } + + case TM_THING_ID: + { + static int id; + + if (GetNumber("Enter THING ID",&id)) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + t->t.id=id; + i=IteratorNext(i); + } + + redraw=TRUE; + } + break; + } + + case TM_VAL: + { + static int id; + + if (GetNumber("Enter THING type",&id)) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + t->t.type=id; + i=IteratorNext(i); + } + + redraw=TRUE; + } + } + break; + + case TM_SPECIAL_VAL: + { + static int id; + + if (GetNumber("Enter THING special action",&id)) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + t->t.special=id; + i=IteratorNext(i); + } + + redraw=TRUE; + } + } + break; + + case TM_ANGLE: + { + int a; + + t=SelHead(); + a=t->t.ang; + + if ((a=GUI_radio_box + ("Angle",thing_angle,a,GUI_CANCEL))!=GUI_CANCEL) + { + ed_thing.ang=a; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + t->t.ang=a; + i=IteratorNext(i); + } + } + + redraw=TRUE; + } + break; + + case TM_ANGLE_VAL: + { + int a; + + t=SelHead(); + a=t->t.ang; + if (GetNumber("Enter angle value",&a)) + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + t->t.ang=a; + i=IteratorNext(i); + } + } + + redraw=TRUE; + } + break; + + case TM_FLAGS: + i=ListIterator(selected); + s=IteratorData(i); + t=GETTHING(*s); + f=t->t.flags; + + if (SelectFlags(hexen_mode,THING_FLAGS,&f)) + { + ed_thing.flags=f; + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + t->t.flags=ed_thing.flags; + i=IteratorNext(i); + } + + redraw=TRUE; + } + break; + + case TM_SNAP: + { + int orig_lock; + + orig_lock=grid_lock; + grid_lock=TRUE; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + t->t.x=SnapX(t->t.x); + t->t.y=SnapX(t->t.y); + i=IteratorNext(i); + } + + grid_lock=orig_lock; + redraw=TRUE; + + break; + } + + case TM_DELETE: + ObjectDelete_THING(); + break; + + case TM_MOVE: + MoveObject_THING(); + break; + + case TM_Z_POS: + t=SelHead(); + zcoord_dialog[D_ZCOORD].data.i=t->t.z; + + if (GUI_dialog("Change Z co-ord",D_ZCOORD_NO,zcoord_dialog)) + { + ed_thing.z=zcoord_dialog[D_ZCOORD].data.i; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + t->t.z=ed_thing.z; + i=IteratorNext(i); + } + } + else + cancel=TRUE; + + redraw=TRUE; + break; + + case TM_ARGS: + { + int a[5]; + int id; + int diff; + + t=SelHead(); + + id=t->t.special; + + i=ListIterator(selected); + i=IteratorNext(i); + diff=FALSE; + + while((i)&&(!diff)) + { + s=IteratorData(i); + t=GETTHING(*s); + + if (t->t.special!=id) + diff=TRUE; + + i=IteratorNext(i); + } + + if ((diff)&&(YesNo("Selected things have|different types| |" + "Set individually?"))) + { + cancel=TRUE; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + id=t->t.special; + + for(f=0;f<5;f++) + a[f]=t->t.args[f]; + + if (SpecialArgDialog(NULL,id,a)) + { + for(f=0;f<5;f++) + t->t.args[f]=ed_thing.args[f]=a[f]; + + cancel=FALSE; + } + + i=IteratorNext(i); + } + } + else + { + t=SelHead(); + + for(f=0;f<5;f++) + a[f]=t->t.args[f]; + + if (SpecialArgDialog(NULL,id,a)) + { + for(f=0;f<5;f++) + ed_thing.args[f]=a[f]; + + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + + for(f=0;f<5;f++) + t->t.args[f]=ed_thing.args[f]; + + i=IteratorNext(i); + } + } + else + cancel=TRUE; + } + + redraw=TRUE; + break; + } + + case TM_ZERO: + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + + for(f=0;f<5;f++) + t->t.args[f]=0; + + i=IteratorNext(i); + } + + FullRedraw(); + break; + + case TM_ARGS_RANGE: + { + int a[5]; + int d[5]; + int id; + int diff; + + t=SelHead(); + + id=t->t.special; + + i=ListIterator(selected); + i=IteratorNext(i); + diff=FALSE; + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + + if (t->t.special!=id) + { + diff=TRUE; + i=IteratorClear(i); + } + else + i=IteratorNext(i); + } + + t=SelHead(); + + for(f=0;f<5;f++) + { + a[f]=t->t.args[f]; + d[f]=0; + } + + if ((!diff)||(YesNo("Selected things have|different types| |" + "Continue?"))) + if ((SpecialArgDialog("Start values",id,a))&& + (SpecialArgDialog("Increments",id,d))) + + { + i=ListIterator(selected); + + while(i) + { + s=IteratorData(i); + t=GETTHING(*s); + + for(f=0;f<5;f++) + { + t->t.args[f]=(Byte)a[f]; + a[f]+=d[f]; + } + + i=IteratorNext(i); + } + + FullRedraw(); + } + break; + } + + default: + cancel=TRUE; + break; + } + + if ((!cancel)&&(clear_on_menu)) + { + ClearSelection(); + FullRedraw(); + } + else if (redraw) + FullRedraw(); +} + + +void ObjectKey_THING(GFXKey k) +{ + Iterator i; + EditThing *t; + int tmpsel; + int redraw; + int *f; + int a; + + tmpsel=TmpAddCurrent(); + redraw=FALSE; + + if (ListSize(selected)==0) + return; + + if (k.code!=GFX_ASCII) + switch(k.code) + { + default: + break; + } + else + { + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + t=GETTHING(*f); + + switch(toupper(k.ascii)) + { + case ',': + a=ANGLENUM(t->t.ang); + if (++a==8) + a=0; + t->t.ang=a*45; + redraw=TRUE; + break; + + case '.': + a=ANGLENUM(t->t.ang); + if (--a<0) + a=7; + t->t.ang=a*45; + redraw=TRUE; + break; + + case '<': + if (hexen_mode) + { + t->t.z--; + redraw=TRUE; + } + break; + + case '>': + if (hexen_mode) + { + t->t.z++; + redraw=TRUE; + } + break; + + default: + break; + } + + i=IteratorNext(i); + } + + } + + if (redraw) + FullRedraw(); + + if (tmpsel) + ListEmpty(selected); +} + + +int ObjectHasTag_THING(void *obj, int tag) +{ + return(FALSE); +} + + +ObjDesc *ObjectOverlaid_THING(int x, int y, int *no) +{ + int n; + EditThing *t; + ObjDesc *od; + int f; + + VIDOOM_TRACE; + + od=NULL; + + n=0; + + for(f=0;f<MapSize(thing);f++) + { + t=GETTHING(f); + + if ((t)&&(PositionOnObject_THING(x,y,t))) + { + n++; + od=ReGrab(od,sizeof(ObjDesc)*n); + od[n-1].no=f; + sprintf(od[n-1].detail,"%5d - %s (%s)",f, + TrimStr(ThingName(t->t.type),29), + TrimStr(FlagText(hexen_mode,THING_FLAGS, + t->t.flags),19)); + } + } + + *no=n; + return(od); +} + + +/* END OF FILE */ diff --git a/editvar.c b/editvar.c new file mode 100644 index 0000000..7ed4cb9 --- /dev/null +++ b/editvar.c @@ -0,0 +1,492 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor global varaiable definitions + + Note that the lindefs, vertices, etc here are copied from the WadMap + loaded prior to editting. They are then reconverted back to the WadMap + for saving. + + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include "editvar.h" + + +/* ---------------------------------------- EDIT TYPES +*/ + +char *edit_str[]={"Sector","Linedef","Vertex","Thing","Multimode"}; + + +/* ---------------------------------------- EDIT VARS +*/ + +void HandleMoveKey(GFXKey k,int check_mouse); + +int edit_mode=SECTOR_MODE; + +Map vertex=NULL; +Map linedef=NULL; +Map sidedef=NULL; +Map sector=NULL; +Map thing=NULL; +Map multimap=NULL; +char *mapinfo=NULL; +int hexen_mode=FALSE; + +char *behavior=NULL; +int behavior_size=0; +char *scripts=NULL; +int scripts_size=0; + +int SCRW; +int SCRH; +int FH; + +int scale=5; +int ox; +int oy; + +GFXMouse ms; + +int quit; + +int agrid; + +int current=-1; +List selected=NULL; + +/* This variable should be set to try by any actions in the generic object + routines that have generated a new selection list. +*/ +int new_selection=FALSE; + +/* Hardcoded values!!! Remove sometime +*/ +Thing ed_thing={0,0,0,1,0x07,0,0,0,{0,0,0,0,0}}; + +/* This variable stops the DrawObjectInfo() routine being called on redraws +*/ +int draw_current_info=TRUE; + +/* ---------------------------------------- USED AS GENERIC FUNCTIONS AND VALUES +*/ + +/* Data in all these functions is the Object.data pointer +*/ +int (*PositionOnObject)(int x, int y, void *data); +void (*SelectBox)(int x1, int y1, int x2, int y2); +void (*SelectByType)(void); +void (*DrawObject)(void *data, int selmode); +void (*DrawObjectInfo)(void); +void (*DrawObjectHeader)(void); +void (*MoveObject)(void); +void (*RotateObject)(double angle); +void (*ScaleObject)(double scale); +void (*SetTagObject)(int tag); +void (*LocateObject)(void *obj); +void (*ObjectMenu)(void); +void (*ObjectInsert)(void); +void (*ObjectDelete)(void); +void (*ObjectKey)(GFXKey k); +int (*ObjectHasTag)(void *obj, int tag); +void (*SetSelect)(int i, int mode); +ObjDesc *(*ObjectOverlaid)(int x,int y,int *no); + +char *typename; +Map map; + + +/* ---------------------------------------- MENUS AND DIALOGS +*/ +PLAT_MENU thing_popup[]={ + {"Change type",TM_TYPE,NULL}, + {"Set type to value",TM_VAL,NULL}, + {"Change flags",TM_FLAGS,NULL}, + {"Change angle",TM_ANGLE,NULL}, + {"Snap selected things",TM_SNAP,NULL}, + {"Move",TM_MOVE,NULL}, + {"Delete",TM_DELETE,NULL}, + {NULL,GUI_CANCEL,NULL} + }; + +PLAT_MENU thing_popup_hexen[]={ + {"Change type",TM_TYPE,NULL}, + {"Set type to value",TM_VAL,NULL}, + + {"Change special action",TM_SPECIAL,NULL}, + {"Set special action to value", + TM_SPECIAL_VAL,NULL}, + {"Change special action args",TM_ARGS,NULL}, + {"Clear special action args to zero", + TM_ZERO,NULL}, + {"Set ranged special args",TM_ARGS_RANGE,NULL}, + {"Change flags",TM_FLAGS,NULL}, + {"Change angle",TM_ANGLE,NULL}, + {"Change angle to value",TM_ANGLE_VAL,NULL}, + {"Set Z postion",TM_Z_POS,NULL}, + {"Set thing ID",TM_THING_ID,NULL}, + {"Snap",TM_SNAP,NULL}, + {"Move",TM_MOVE,NULL}, + {"Delete",TM_DELETE,NULL}, + {NULL,GUI_CANCEL,NULL} + }; + +PLAT_MENU vertex_popup[]={ + {"Chain selected vertices",TM_CHAIN,NULL}, + {"Chain selected vertices " + "into a sector",TM_CHAIN_SECTOR,NULL}, + {"Merge selected vertices",TM_MERGE,NULL}, + {"Snap",TM_SNAP,NULL}, + {"Move",TM_MOVE,NULL}, + {"Delete",TM_DELETE,NULL}, + {NULL,GUI_CANCEL,NULL} + }; + +PLAT_MENU right_popup[]= + { + {"Change upper texture",TM_UPPER_T_R,NULL}, + {"Change middle texture",TM_MIDDLE_T_R,NULL}, + {"Change lower texture",TM_LOWER_T_R,NULL}, + {"Change textures to any value",TM_TEXTURES_R}, + {"Change texture offset",TM_OFFSET_R,NULL}, + {"Adjust texture offset",TM_ADJUST_R,NULL}, + {"Change sector",TM_SECTOR_R,NULL}, + {"Align textures",TM_ALIGN_R,NULL}, + + {NULL,GUI_CANCEL,NULL} + }; + +PLAT_MENU left_popup[]= + { + {"Change upper texture",TM_UPPER_T_L,NULL}, + {"Change middle texture",TM_MIDDLE_T_L,NULL}, + {"Change lower texture",TM_LOWER_T_L,NULL}, + {"Change textures to any value",TM_TEXTURES_L}, + {"Change texture offset",TM_OFFSET_L,NULL}, + {"Adjust texture offset",TM_ADJUST_L,NULL}, + {"Change sector",TM_SECTOR_L,NULL}, + {"Align textures",TM_ALIGN_L,NULL}, + + {NULL,GUI_CANCEL,NULL} + }; + +PLAT_MENU linedef_popup[]= + { + {"Change linedef type",TM_TYPE,NULL}, + {"Set generalised linedef type",TM_GEN_TYPE,NULL}, + {"Set type to value",TM_VAL,NULL}, + {"Change linedef flags",TM_FLAGS,NULL}, + {"Swap sides",TM_SWAP_SIDES,NULL}, + {"Split line",TM_SPLIT_LINE,NULL}, + {"Select trail from this linedef",TM_TRACK_LINE,NULL}, + {"Join linedefs with steps",TM_STEPS,NULL}, + + {"Right sidedef",0,right_popup}, + {"Left sidedef",0,left_popup}, + + {"Change tag",TM_TAG,NULL}, + {"Move",TM_MOVE,NULL}, + {"Delete",TM_DELETE,NULL}, + {NULL,GUI_CANCEL,NULL} + }; + +PLAT_MENU linedef_popup_hexen[]= + { + {"Change special action",TM_SPECIAL,NULL}, + {"Set special action to value",TM_VAL,NULL}, + {"Change special action args",TM_ARGS,NULL}, + {"Clear special action args to zero",TM_ZERO,NULL}, + {"Set ranged special args",TM_ARGS_RANGE,NULL}, + {"Change linedef flags",TM_FLAGS,NULL}, + {"Swap sides",TM_SWAP_SIDES,NULL}, + {"Split line",TM_SPLIT_LINE,NULL}, + {"Select trail from this linedef",TM_TRACK_LINE,NULL}, + {"Join linedefs with steps",TM_STEPS,NULL}, + + {"Right sidedef",0,right_popup}, + {"Left sidedef",0,left_popup}, + + {"Move",TM_MOVE,NULL}, + {"Delete",TM_DELETE,NULL}, + {NULL,GUI_CANCEL,NULL} + }; + +PLAT_MENU sector_popup[]= + { + {"Change type",TM_TYPE,NULL}, + {"Change generalised type",TM_GEN_TYPE,NULL}, + {"Set type to value",TM_VAL,NULL}, + {"Change floor height",TM_FLOOR,NULL}, + {"Change ceiling height",TM_CEILING,NULL}, + {"Change light level",TM_LIGHT,NULL}, + {"Change tag",TM_TAG,NULL}, + {"Change floor",TM_FLOOR_T,NULL}, + {"Change ceiling",TM_CEILING_T,NULL}, + {"Paint sector in style",TM_STYLE,NULL}, + + {"Move",TM_MOVE,NULL}, + {"Delete",TM_DELETE,NULL}, + {NULL,GUI_CANCEL,NULL} + }; + +PLAT_MENU multi_popup[]= + { + {"Snap",TM_SNAP,NULL}, + {"Rotate",TM_ROTATE,NULL}, + {"Scale",TM_SCALE,NULL}, + {"Move",TM_MOVE,NULL}, + {NULL,GUI_CANCEL,NULL} + }; + + +PLAT_RADIO thing_angle[]={ + {"N",90}, + {"NE",45}, + {"E",0}, + {"SE",315}, + {"S",270}, + {"SW",225}, + {"W",180}, + {"NW",135}, + {NULL,GUI_CANCEL} + }; + +PLAT_DIALOG offset_dialog[D_OFFSET_NO]= + { + {"X-offset",PLAT_DIAL_INTEGER,{0}}, + {"Y-offset",PLAT_DIAL_INTEGER,{0}} + }; + +PLAT_DIALOG sectorn_dialog[D_OFFSET_NO]= + { + {"Sector no",PLAT_DIAL_INTEGER,{0}}, + }; + +PLAT_DIALOG coord_dialog[D_COORD_NO]= + { + {"X",PLAT_DIAL_INTEGER,{0}}, + {"Y",PLAT_DIAL_INTEGER,{0}} + }; + +PLAT_DIALOG vertex_dialog[D_VERTEX_NO]= + { + {"Vertex from",PLAT_DIAL_INTEGER,{0}}, + {"Vertex to",PLAT_DIAL_INTEGER,{0}} + }; + +PLAT_DIALOG sector_fl_dialog[D_SECTOR_FL_NO]= + { + {"Floor height",PLAT_DIAL_INTEGER,{0}}, + }; + +PLAT_DIALOG sector_ce_dialog[D_SECTOR_CE_NO]= + { + {"Ceiling height",PLAT_DIAL_INTEGER,{0}}, + }; + +PLAT_DIALOG sector_li_dialog[D_SECTOR_LI_NO]= + { + {"Light level",PLAT_DIAL_INTEGER,{0}}, + }; + +PLAT_DIALOG sector_val_dialog[D_SECTOR_VAL_NO]= + { + {"Light level",PLAT_DIAL_INTEGER,{0}}, + {"Floor height",PLAT_DIAL_INTEGER,{0}}, + {"Ceiling height",PLAT_DIAL_INTEGER,{0}}, + }; + +PLAT_DIALOG tag_dialog[D_TAG_NO]= + { + {"Tag",PLAT_DIAL_INTEGER,{0}}, + }; + +PLAT_DIALOG scale_dialog[D_SCALE_NO]= + { + {"Scale",PLAT_DIAL_DOUBLE,{0}}, + }; + +PLAT_DIALOG rotate_dialog[D_ROTATE_NO]= + { + {"Angle",PLAT_DIAL_DOUBLE,{0}}, + }; + +PLAT_DIALOG objno_dialog[D_OBJNO_NO]= + { + {"Object number",PLAT_DIAL_INTEGER,{0}}, + }; + +PLAT_DIALOG nosides_dialog[D_NOSIDES_NO]= + { + {"Number of sides",PLAT_DIAL_INTEGER,{0}}, + }; + +PLAT_DIALOG zcoord_dialog[D_COORD_NO]= + { + {"Z co-ord",PLAT_DIAL_INTEGER,{0}}, + }; + + +PLAT_DIALOG l_range_dialog[D_L_RANGE_NO]= + { + {"Starting light level", + PLAT_DIAL_INTEGER,{0}}, + {"Minimum light level", + PLAT_DIAL_INTEGER,{0}}, + {"Maximum light level", + PLAT_DIAL_INTEGER,{0}}, + {"Lighting adjustment", + PLAT_DIAL_INTEGER,{0}}, + {"Pulse",PLAT_DIAL_PICKLIST,{0}}, + }; + +PLAT_DIALOG textures_dialog[D_TEXTURES_NO]= + { + {"Upper",PLAT_DIAL_STRING,{0}}, + {"Middle",PLAT_DIAL_STRING,{0}}, + {"Lower",PLAT_DIAL_STRING,{0}}, + }; + + +/* Generic yes/no dialog picklist array +*/ +char *edit_dial_picklist_yes_no[2]={"No","Yes"}; + + + +/* ---------------------------------------- DRAWING TABLES +*/ +Point t_arrow[8]={{1,0}, + {1,1}, + {0,1}, + {-1,1}, + {-1,0}, + {-1,-1}, + {0,-1}, + {1,-1}}; + +char *angle_str[8]={"E","NE","N","NW","W","SW","S","SE"}; + + +/* ---------------------------------------- HELP PAGES +*/ +char *general_help_keys[]= + { + "F1 - general help |" + "F2 - edit mode help |" + "Escape - finish editting |" + "Cursor keys - move |" + "Shift + Cursor keys - move quickly |" + "Ctrl + Cursor keys - move by one scale position |" + "Page down/up - zoom in/out |" + "Q/W - decrease/increase grid scale |" + "G - grid lines on/off |" + "X - grid snap on/off |" + "Tab/Shift + Tab - next/previous edit mode |" + "V - VERTEX edit mode |" + "L - LINEDEF edit mode |" + "S - SECTOR edit mode |" + "T - THING edit mode |" + "C - MULTI edit mode |" + "F9 - deselect all |" + "Shift + F9 - invert selection |" + "F10 - select all |" + "Shift + F10 - select by type (if applicable) |" + "Ctrl + F10 - select one from a number of |" + " overlaid objects |" + "Alt + F10 - additionally select one |" + " overlaid object |" + "F11/Shift + F11 - locate next/previous selection ", + + "Insert - insert new object |" + "Delete - delete selected objects |" + "Home - LUMP menu (ZDoom only) |" + "F3 - merge a PWAD map |" + "F4 - move selected objects |" + "F5 - rotate selected objects |" + "[ - rotate 5 degrees left |" + "] - rotate 5 degrees right |" + "F6 - scale selected objects |" + "{ - scale by 90% |" + "} - scale by 110% |" + "F7 - set tag (if applicable) |" + "Shift + F7 - select objects with tag |" + "F8 - locate by object number |" + "Shift + F8 - locate and select object number|" + "R - redraw [useful if selected |" + " sectors look unselected] |" + " |" + "Note: Grid lock not honoured on rotate or scale ", + + NULL + }; + +char *general_help_mouse= + "Left button - select object |" + "Ctrl + Left button - additionally select object |" + "Shift + Left button - select objects in box |" + "Ctrl + Shift + Left button - additionally select objects in box|" + "Right button - pop-up object menu |" + "Middle button - move selected objects "; + +char *mode_help[]= + { + /* SECTOR + */ + "M - change sector move mode |" + "H - toggle SECTOR tag highligting|" + ", - decrease sector floor by 8 |" + ". - increase sector floor by 8 |" + "< - decrease sector ceiling by 8 |" + "> - increase sector ceiling by 8 |" + "- - decrease lighting level by 16|" + "+ - increase lighting level by 16", + + /* LINEDEF + */ + "H - toggle LINEDEF tag highligting |" + "F12 - perform checks for LINEDEF textures", + + /* VERTEX + */ + "F12 - remove vertices to bound to linedefs", + + /* THING + */ + ", - rotate THINGs angle anti-clockwise |" + ". - rotate THINGs angle clockwise |" + "< - decrement THING Z co-ord (Hexen only) |" + "> - increment THING Z co-ord (Hexen only) ", + + /* MULTI + */ + "No additional key functions" + }; + + +/* END OF FILE */ diff --git a/editvar.h b/editvar.h new file mode 100644 index 0000000..dfb1173 --- /dev/null +++ b/editvar.h @@ -0,0 +1,701 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor global variable definitions + + $Id$ + +*/ + +#ifndef VIDOOM_EDITVAR_H + +#define VIDOOM_EDITVAR_H + +#include "things.h" +#include "linedefs.h" +#include "wad.h" +#include "map.h" +#include "list.h" +#include "platgui.h" +#include "gui.h" +#include "mem.h" + +#define MAXLEN 1024 + + +/* ---------------------------------------- EDIT COLOURS +*/ +#define GRIDCOL V_RGB(0x20,0x20,0x80) + +#define LINECOL V_RGB(0xa0,0x9a,0x90) + +#define SECTCOL V_RGB(0xc0,0x00,0x00) + +#define VERTCOL V_RGB(0xff,0xff,0xff) +#define VERTBOXCOL V_RGB(0x60,0x60,0x60) + +#define THINGCOL V_RGB(0xa0,0x10,0x10) +#define SELCOL V_RGB(0xff,0xff,0x00) +#define OVERCOL V_RGB(0xc0,0xc0,0x00) + +#define TAGCOL V_RGB(0xff,0xe0,0xe0) + + +/* ---------------------------------------- EDIT TYPES +*/ + +#define SECTOR_MODE 0 +#define LINEDEF_MODE 1 +#define VERTEX_MODE 2 +#define THING_MODE 3 +#define MULTI_MODE 4 + +#define NEXT_MODE(m) ((m == MULTI_MODE) ? SECTOR_MODE : m+1) +#define PREV_MODE(m) ((m == SECTOR_MODE) ? MULTI_MODE : m-1) + +extern char *edit_str[]; + +#define SELECT_NONE 0 +#define SELECT_OVER 1 +#define SELECT_SELECTED 2 + + +typedef struct + { + int x,y; + } Point; + +/* Edittable types of the lumps from the WAD +*/ +typedef struct + { + Vertex v; + List l; + } EditVert; + +typedef struct + { + int no; + Linedef l; + Sidedef *sr; + Sidedef *sl; + EditVert *v[2]; + int min_x; + int min_y; + int max_x; + int max_y; + } EditLine; + +typedef struct + { + int no; + Sector s; + int min_x; + int min_y; + int max_x; + int max_y; + List v; + List sr; + List sl; + List all; + } EditSect; + +typedef struct + { + Thing t; + } EditThing; + +typedef struct + { + int type; + int i; + int x,y; + } MultiObj; + +typedef struct + { + int select; + void *data; + } Object; + +typedef struct + { + int no; + char detail[128]; + } ObjDesc; + + +/* ---------------------------------------- EDIT VARS +*/ + +extern void HandleMoveKey(GFXKey k,int check_mouse); + +extern int edit_mode; + +extern Map vertex; +extern Map linedef; +extern Map sidedef; +extern Map sector; +extern Map thing; +extern Map multimap; +extern char *mapinfo; +extern int hexen_mode; + +extern char *behavior; +extern int behavior_size; +extern char *scripts; +extern int scripts_size; + +extern int SCRW; +extern int SCRH; +extern int FH; + +extern int scale; +extern int ox; +extern int oy; + +extern GFXMouse ms; + +extern int quit; + +extern int agrid; + +extern int current; +extern List selected; + +extern int new_selection; + +extern Thing ed_thing; + +extern int draw_current_info; + +/* ---------------------------------------- USED AS GENERIC FUNCTIONS AND VALUES +*/ + +/* Data in all these functions is the Object.data pointer +*/ +extern int (*PositionOnObject)(int x, int y, void *data); +extern void (*SelectBox)(int x1, int y1, int x2, int y2); +extern void (*SelectByType)(void); +extern void (*DrawObject)(void *data, int selmode); +extern void (*DrawObjectInfo)(void); +extern void (*DrawObjectHeader)(void); +extern void (*MoveObject)(void); +extern void (*RotateObject)(double angle); +extern void (*ScaleObject)(double scale); +extern void (*SetTagObject)(int tag); +extern void (*LocateObject)(void *obj); +extern void (*ObjectMenu)(void); +extern void (*ObjectInsert)(void); +extern void (*ObjectDelete)(void); +extern void (*ObjectKey)(GFXKey k); +extern int (*ObjectHasTag)(void *obj, int tag); +extern void (*SetSelect)(int i, int mode); +extern ObjDesc *(*ObjectOverlaid)(int x,int y,int *no); + +extern char *typename; +extern Map map; + + +/* ---------------------------------------- MENUS AND DIALOGS +*/ +#define GUI_CANCEL -666 + +#define TM_TYPE 0 +#define TM_MOVE 1 +#define TM_DELETE 2 +#define TM_ANGLE 3 +#define TM_FLAGS 4 +#define TM_INSERT 5 +#define TM_UPPER_T_R 6 +#define TM_MIDDLE_T_R 7 +#define TM_LOWER_T_R 8 +#define TM_OFFSET_R 9 +#define TM_UPPER_T_L 10 +#define TM_MIDDLE_T_L 11 +#define TM_LOWER_T_L 12 +#define TM_OFFSET_L 13 +#define TM_FLOOR 14 +#define TM_CEILING 15 +#define TM_LIGHT 16 +#define TM_TAG 17 +#define TM_FLOOR_T 18 +#define TM_CEILING_T 19 +#define TM_SECTOR_R 20 +#define TM_SECTOR_L 21 +#define TM_CHAIN 22 +#define TM_CHAIN_SECTOR 23 +#define TM_ALIGN_R 24 +#define TM_ALIGN_L 25 +#define TM_SWAP_SIDES 26 +#define TM_SPLIT_LINE 27 +#define TM_MERGE 28 +#define TM_SNAP 29 +#define TM_STYLE 30 +#define TM_TRACK_LINE 31 +#define TM_STEPS 32 +#define TM_RIGHT_SIDE 33 +#define TM_LEFT_SIDE 34 +#define TM_ADJUST_R 35 +#define TM_ADJUST_L 36 +#define TM_ROTATE 37 +#define TM_SCALE 38 +#define TM_Z_POS 39 +#define TM_ARGS 40 +#define TM_GEN_TYPE 41 +#define TM_VAL 42 +#define TM_TEXTURES_L 43 +#define TM_TEXTURES_R 44 +#define TM_ZERO 45 +#define TM_SPECIAL 46 +#define TM_SPECIAL_VAL 47 +#define TM_THING_ID 48 +#define TM_ANGLE_VAL 49 +#define TM_ARGS_RANGE 50 + + +extern PLAT_MENU thing_popup[]; +extern PLAT_MENU thing_popup_hexen[]; + +extern PLAT_RADIO thing_angle[]; + +extern PLAT_MENU vertex_popup[]; + +extern PLAT_MENU right_popup[]; +extern PLAT_MENU left_popup[]; +extern PLAT_MENU linedef_popup[]; +extern PLAT_MENU linedef_popup_hexen[]; + +extern PLAT_MENU sector_popup[]; + +extern PLAT_MENU multi_popup[]; + +#define NO_FIELDS(x) (sizeof(x)/sizeof(x[0])) + +#define D_OFFSET_NO 2 +#define D_OFFSET_OFFX 0 +#define D_OFFSET_OFFY 1 +extern PLAT_DIALOG offset_dialog[D_OFFSET_NO]; + + +#define D_SECTORN_NO 1 +#define D_SECTORN_SECNO 0 +extern PLAT_DIALOG sectorn_dialog[D_OFFSET_NO]; + + +#define D_COORD_NO 2 +#define D_COORD_X 0 +#define D_COORD_Y 1 +extern PLAT_DIALOG coord_dialog[D_COORD_NO]; + + +#define D_VERTEX_NO 2 +#define D_VERTEX_FROM 0 +#define D_VERTEX_TO 1 +extern PLAT_DIALOG vertex_dialog[D_VERTEX_NO]; + + +#define D_SECTOR_FL_NO 1 +#define D_SECTOR_FLOOR 0 +extern PLAT_DIALOG sector_fl_dialog[D_SECTOR_FL_NO]; + + +#define D_SECTOR_CE_NO 1 +#define D_SECTOR_CEILING 0 +extern PLAT_DIALOG sector_ce_dialog[D_SECTOR_CE_NO]; + + +#define D_SECTOR_LI_NO 1 +#define D_SECTOR_LIGHT 0 +extern PLAT_DIALOG sector_li_dialog[D_SECTOR_LI_NO]; + + +#define D_SECTOR_VAL_NO 3 +#define D_SVAL_LIGHT 0 +#define D_SVAL_FLOOR 1 +#define D_SVAL_CEILING 2 +extern PLAT_DIALOG sector_val_dialog[D_SECTOR_VAL_NO]; + + +#define D_TAG_NO 1 +#define D_TAG 0 +extern PLAT_DIALOG tag_dialog[D_TAG_NO]; + + +#define D_SCALE_NO 1 +#define D_SCALE 0 +extern PLAT_DIALOG scale_dialog[D_SCALE_NO]; + + +#define D_ROTATE_NO 1 +#define D_ROTATE 0 +extern PLAT_DIALOG rotate_dialog[D_ROTATE_NO]; + + +#define D_OBJNO_NO 1 +#define D_OBJNO 0 +extern PLAT_DIALOG objno_dialog[D_OBJNO_NO]; + + +#define D_NOSIDES_NO 1 +#define D_NOSIDES 0 +extern PLAT_DIALOG nosides_dialog[D_NOSIDES_NO]; + + +#define D_ZCOORD_NO 1 +#define D_ZCOORD 0 +extern PLAT_DIALOG zcoord_dialog[D_COORD_NO]; + + +#define D_L_RANGE_NO 5 +#define D_L_RANGE_START 0 +#define D_L_RANGE_MIN 1 +#define D_L_RANGE_MAX 2 +#define D_L_RANGE_INC 3 +#define D_L_RANGE_PULSE 4 /* NOTE: This is a picklist */ +extern PLAT_DIALOG l_range_dialog[D_L_RANGE_NO]; + + +#define D_TEXTURES_NO 3 +#define D_TEXTURE_U 0 +#define D_TEXTURE_M 1 +#define D_TEXTURE_L 2 +extern PLAT_DIALOG textures_dialog[D_TEXTURES_NO]; + + + + +/* Use these macros to set yes/no dialog picklist entries up + d - dialog entry to set + s - TRUE == Yes, FALSE == No +*/ +extern char *edit_dial_picklist_yes_no[2]; + +#define SET_YESNO_DIAL(d,s) \ + do { \ + d.data.pl.no=2; \ + d.data.pl.current=(s ? 1:0); \ + d.data.pl.text=edit_dial_picklist_yes_no; \ + } while(0); + + + +/* ---------------------------------------- DRAWING TABLES +*/ +extern Point t_arrow[8]; + +extern char *angle_str[8]; + +/* #define ANGLENUM(a) ((((a)&0xff)+22)/45%8) */ +#define ANGLENUM(a) (((a)+22)/45%8) +#define ANGLESTR(a) (angle_str[ANGLENUM(a)]) + +/* ---------------------------------------- MACROS +*/ +#define PI_VAL 3.14159265358979323846 + +#define RAD(a) (PI_VAL/180.0*(a)) + +#define XToMap(x) (ox+((x)*scale)) +#define YToMap(y) (oy-((y)*scale)) + +#define MapToX(x) (((x)-ox)/scale) +#define MapToY(y) ((oy-(y))/scale) + +#define MapRect(x,y,w,h,c) \ + GFX_rect(MapToX(x),MapToY(y),(w)/scale,(h)/scale,c) + +#define MapLine(x1,y1,x2,y2,c) \ + GFX_line(MapToX(x1),MapToY(y1),MapToX(x2),MapToY(y2),c) + +#define MapLineD(l,c) GFX_line(MapToX((l)->v[0]->v.x), \ + MapToY((l)->v[0]->v.y), \ + MapToX((l)->v[1]->v.x), \ + MapToY((l)->v[1]->v.y),c) + +#define MapPlot(x,y,c) GFX_plot(MapToX(x),MapToY(y),c) + +#define MapCircle(x,y,r,c) \ + GFX_circle(MapToX(x),MapToY(y),(r)/scale,c) + +#define BOXBOUND(tx,ty,x1,y1,x2,y2) \ + (((tx)>=(x1))&&((tx)<=(x2))&&((ty)>=(y1))&&((ty)<=(y2))) +#define BOXBOUND_FUZZY(tx,ty,x1,y1,x2,y2,f) \ + (((tx)>=(x1-(f)))&&((tx)<=(x2+(f)))&&((ty)>=(y1-(f)))&&((ty)<=(y2+(f)))) + +#define ROUGHLY(a,b) (((a)>=(b)-0.01)&&((a)<=(b)+0.01)) + +#define RADBOUND(tx,ty,x,y,r) \ + (((tx)>=(x)-(r))&&((tx)<=(x)+(r))&& \ + ((ty)>=(y)-(r))&&((ty)<=(y)+(r))) + + +#define GETVERT(n) ((EditVert *)(((Object *)MapElem(vertex,(n)))->data)) +#define VERTFROM(m,n) ((EditVert *)(((Object *)MapElem((m),(n)))->data)) +#define GETLINE(n) ((EditLine *)(((Object *)MapElem(linedef,(n)))->data)) +#define GETSIDE(n) ((Sidedef *)(((Object *)MapElem(sidedef,(n)))->data)) +#define SIDEFROM(m,n) ((Sidedef *)(((Object *)MapElem((m),(n)))->data)) +#define GETSECT(n) ((EditSect *)(((Object *)MapElem(sector,(n)))->data)) +#define GETTHING(n) ((EditThing *)(((Object *)MapElem(thing,(n)))->data)) +#define GETMULTI(n) ((MultiObj *)(((Object *)MapElem(multimap,(n)))->data)) + +#define YESNO(x) ((x) ? "Yes":"No") + +/* ------------------------------ HELP PAGES +*/ +extern char *general_help_keys[]; +extern char *general_help_mouse; +extern char *mode_help[]; + + +/* ------------------------------ FUNCTION PROTOTYPES FOR ALL EDIT ROUTINES +*/ +void SectorCalcContaining(int no,EditSect *s); +void SectorCalcContainingAll(void); +int SnapX(int x); +int SnapY(int y); +int Len(int x1,int y1,int x2,int y2); +int LinesCross(int x1_1,int y1_1,int x1_2,int y1_2, + int x2_1,int y2_1,int x2_2,int y2_2); +int CalcTextureWidth(DirName u,DirName m,DirName l); +void LineCalcBounding(EditLine *l); +void SectorCalcBounding(EditSect *s); +int LineOnDisplay(EditLine *l); +int SectorOnDisplay(EditSect *s); +int PointOnDisplay(int x,int y,int r); +void DrawArrow(int x1,int y1,int x2,int y2,int c); +void DrawGrid(void); +void DrawMap(void); +void DrawHeader(void); +void FullRedraw(void); + +void SetSelect_GENERIC(int i, int mode); +int TmpAddCurrent(void); +void ClearSelection(void); +void ClearSelectionLeaveCurrent(void); +void *SelHead(void); + +int InIntList(List l, int i); +void IntListUniqAdd(List l, int i); +void IntListRm(List l, int i); + + +/* If set_class is TRUE then *type is set from the classes loaded in the + config file. + + The floor and ceiling values are used to decide which if the lower, middle + and upper textures to ask for. If the selected linedef style is not two + sided these values are ignored anyway and only the middle one on the right + sidedef is requested. +*/ +int GetLinedefValues(int set_class, + int r_floor,int l_floor,int r_ceiling,int l_ceiling, + int *type,int *flag,int *two, + DirName sr_upper,DirName sr_middle,DirName sr_lower, + DirName sl_upper,DirName sl_middle,DirName sl_lower); + +/* This supercedes the above function when creating sectors. + + THe floor, ceiling and light levels are in/out. The displayed dialog is + loaded with the values sent in and any changes are reflected on return. + + The in_floor and in_ceiling values are used to decide which of the + lower, middle and upper textures set up. If the selected linedef style + is not two sided these values are ignored anyway and only the middle + one on the right sidedef is set to non-empty. When doing comparisons it + is assumed that the in_ values are on the left of the linedefs. + + Note that for this routine rather than being individually requested the + linedef textures are obtained from the defined sector styles (unless there + are no sector styles). Style flags is also set to the flags for sector + style (unless there is no styles, in which case it's zero). + + Note the the sector flags for the style indicating if just the inward + or outward facing sidedefs have the textures applied will be ignored, ie. + both the right and left texture values will have the style applied. + + Returns FALSE if at any point the selections are cancelled. +*/ +int GetSectorValues(int *line_type, int *line_flag, int *two_sided, + int *floor, int *ceiling, int *light, + int in_floor, int in_ceiling, + int *style_flags, + DirName floor_t, DirName ceiling_t, + DirName sr_upper, DirName sr_middle, DirName sr_lower, + DirName sl_upper, DirName sl_middle, DirName sl_lower); + +void GenericCheckMouse(void); +int GetTexture(char *t,DirName d); +int GetFlat(char *t,DirName d); +int YesNo(char *fmt,...); +int YesNoAll(char *fmt,...); +int GetNumber(char *prompt, int *val); + +/* If draw is not NULL it will be used to draw the selection thing. Note that + the x an y coming into the draw will NOT be snapped. They can be updated + by the drawing mechanism if snapping is requried. + + If key is not NULL any keys read will be passed to the key reoutine (after + being passed to HandleMoveKey()). + + Finally if infobox is not NULL this function is used to draw the describing + infobox, rather than PickPoint()s default one. The prompt (p) PickPoint() + was called with will be passed to the infobox routine. +*/ +int PickPoint(char *p,int *x,int *y,void (*draw)(int *x,int *y), + void (*key)(GFXKey k), + void (*infobox)(char *p)); + +void SetEditMode(int mode); +void HandleMoveKey(GFXKey k, int check_mouse); +void HandleKey(GFXKey k); +void HandleMouse(GFXMouse m); + +void MergeWad(void); + +void EditPreview3D(void); + +void Rotate(int cx,int cy,int *x,int *y, double ang); +void Scale(int cx,int cy,int *x,int *y, double sc); + +int PositionOnObject_VERTEX(int x,int y,void *data); +void SelectBox_VERTEX(int x1,int y1,int x2,int y2); +void SelectByType_VERTEX(void); +void DrawObject_VERTEX(void *data, int selmode); +void DrawObjectInfo_VERTEX(void); +void DrawObjectHeader_VERTEX(void); +void MoveObject_VERTEX(void); +void RotateObject_VERTEX(double angle); +void ScaleObject_VERTEX(double scale); +void SetTagObject_VERTEX(int tag); +void LocateObject_VERTEX(void *obj); +void ObjectInsert_VERTEX(void); +void ObjectDelete_VERTEX(void); +void ObjectMenu_VERTEX(void); +void ObjectKey_VERTEX(GFXKey k); +int ObjectHasTag_VERTEX(void *obj, int tag); +ObjDesc *ObjectOverlaid_VERTEX(int x,int y,int *no); + +void SetHexenLinedefTag(EditLine *l); +List TrackLinedef(int line_no); +void CheckMergeLinedef(EditVert *v); +int CreateNewLinedef(int from, int to, + int flags, int type, int tag, int two_sided, + int sr_ox, int sr_oy, int sr_sector, + DirName sr_upper, DirName sr_middle, DirName sr_lower, + int sl_ox, int sl_oy, int sl_sector, + DirName sl_upper, DirName sl_middle, DirName sl_lower); + +int PositionOnObject_LINEDEF(int x,int y,void *data); +void SelectBox_LINEDEF(int x1,int y1,int x2,int y2); +void SelectByType_LINEDEF(void); +void DrawObject_LINEDEF(void *data, int selmode); +void DrawObjectInfo_LINEDEF(void); +void DrawObjectHeader_LINEDEF(void); +void MoveObject_LINEDEF(void); +void RotateObject_LINEDEF(double angle); +void ScaleObject_LINEDEF(double scale); +void SetTagObject_LINEDEF(int tag); +void LocateObject_LINEDEF(void *obj); +void ObjectInsert_LINEDEF(void); +void ObjectDelete_LINEDEF(void); +void ObjectMenu_LINEDEF(void); +void ObjectKey_LINEDEF(GFXKey k); +int ObjectHasTag_LINEDEF(void *obj, int tag); +ObjDesc *ObjectOverlaid_LINEDEF(int x,int y,int *no); + +int PositionOnObject_THING(int x,int y,void *data); +void SelectBox_THING(int x1,int y1,int x2,int y2); +void SelectByType_THING(void); +void DrawObject_THING(void *data, int selmode); +void DrawObjectInfo_THING(void); +void DrawObjectHeader_THING(void); +void MoveObject_THING(void); +void RotateObject_THING(double angle); +void ScaleObject_THING(double scale); +void SetTagObject_THING(int tag); +void LocateObject_THING(void *obj); +void ObjectInsert_THING(void); +void ObjectDelete_THING(void); +void ObjectMenu_THING(void); +void ObjectKey_THING(GFXKey k); +int ObjectHasTag_THING(void *obj, int tag); +ObjDesc *ObjectOverlaid_THING(int x,int y,int *no); + +/* Returns -1 if point not in a sector +*/ +int SectorHoldingPoint(int x,int y); + +/* Creates a sector for the list of vertex numbers, which is assumed to go + clockwise. Return is TRUE if operation completed, FALSE if cancelled. +*/ +int CreateSector(List v); +int CreateUnboundSector(int special,int floor,int ceiling,int light,int tag, + DirName floor_t,DirName ceiling_t); + +void SectorCalcContaining(int no,EditSect *s); +void SectorCalcContainingAll(void); + +int PositionOnObject_SECTOR(int x,int y,void *data); +void SelectBox_SECTOR(int x1,int y1,int x2,int y2); +void SelectByType_SECTOR(void); +void DrawObject_SECTOR(void *data, int selmode); +void DrawObjectInfo_SECTOR(void); +void DrawObjectHeader_SECTOR(void); +void MoveObject_SECTOR(void); +void RotateObject_SECTOR(double angle); +void ScaleObject_SECTOR(double scale); +void SetTagObject_SECTOR(int tag); +void LocateObject_SECTOR(void *obj); +void ObjectInsert_SECTOR(void); +void ObjectDelete_SECTOR(void); +void ObjectMenu_SECTOR(void); +void ObjectKey_SECTOR(GFXKey k); +int ObjectHasTag_SECTOR(void *obj, int tag); +ObjDesc *ObjectOverlaid_SECTOR(int x,int y,int *no); + +void GenerateMultiMap(void); +int PositionOnObject_MULTI(int x,int y,void *data); +void SelectBox_MULTI(int x1,int y1,int x2,int y2); +void SelectByType_MULTI(void); +void DrawObject_MULTI(void *data, int selmode); +void DrawObjectInfo_MULTI(void); +void DrawObjectHeader_MULTI(void); +void MoveObject_MULTI(void); +void RotateObject_MULTI(double angle); +void ScaleObject_MULTI(double scale); +void SetTagObject_MULTI(int tag); +void LocateObject_MULTI(void *obj); +void ObjectInsert_MULTI(void); +void ObjectDelete_MULTI(void); +void ObjectMenu_MULTI(void); +void ObjectKey_MULTI(GFXKey k); +int ObjectHasTag_MULTI(void *obj, int tag); +void SetSelect_MULTI(int i, int mode); +ObjDesc *ObjectOverlaid_MULTI(int x,int y,int *no); + + +void EditLumpMenu(void); + +#endif + +/* END OF FILE */ diff --git a/editvert.c b/editvert.c new file mode 100644 index 0000000..361aa00 --- /dev/null +++ b/editvert.c @@ -0,0 +1,834 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Editor VERTEX definitions + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include "editvar.h" + + +/* ---------------------------------------- PRIVATE UTILS +*/ +static void VertexSelectionCentre(int *x,int *y) +{ + int *f; + EditVert *v; + Iterator i; + int min_x,min_y,max_x,max_y; + + VIDOOM_TRACE; + + i=ListIterator(selected); + + min_x=99999; + min_y=99999; + max_x=-99999; + max_y=-99999; + + while(i) + { + f=IteratorData(i); + v=GETVERT(*f); + + min_x=MIN(v->v.x,min_x); + min_y=MIN(v->v.y,min_y); + max_x=MAX(v->v.x,max_x); + max_y=MAX(v->v.y,max_y); + + i=IteratorNext(i); + } + + *x=min_x+(max_x-min_x)/2; + *y=min_y+(max_y-min_y)/2; +} + + +static void MergeVertex(int new,int old) +{ + Object *o; + EditVert *v; + EditLine *l; + int f; + + o=MapElem(vertex,old); + v=o->data; + ListClear(v->l); + Release(o->data); + o->data=NULL; + o->select=SELECT_NONE; + + v=GETVERT(new); + + for(f=0;f<MapSize(linedef);f++) + if ((l=GETLINE(f))) + { + if (l->l.from==old) + { + l->l.from=new; + l->v[0]=v; + IntListUniqAdd(v->l,f); + } + + if (l->l.to==old) + { + l->l.to=new; + l->v[1]=v; + IntListUniqAdd(v->l,f); + } + } + + if (merge_linedef!=MERGE_NEVER) + { + v=GETVERT(new); + CheckMergeLinedef(v); + } +} + +/* ---------------------------------------- GENERIC VERTEX FUNCS +*/ +int PositionOnObject_VERTEX(int x,int y,void *data) +{ + EditVert *v; + + VIDOOM_TRACE; + + v=data; + return (RADBOUND(x,y,v->v.x,v->v.y,vertex_rad)); +} + + +void SelectBox_VERTEX(int x1,int y1,int x2,int y2) +{ + Object *o; + EditVert *v; + int f; + + VIDOOM_TRACE; + + for(f=0;f<MapSize(vertex);f++) + { + o=MapElem(vertex,f); + + if ((v=o->data)) + { + if ((o->select!=SELECT_SELECTED)&& + (BOXBOUND(v->v.x,v->v.y,x1,y1,x2,y2))) + { + SetSelect(f,SELECT_SELECTED); + ListAppend(selected,&f); + } + } + } +} + + +void SelectByType_VERTEX(void) +{ + return; +} + + +void DrawObject_VERTEX(void *data, int selmode) +{ + EditVert *v; + int col; + + VIDOOM_TRACE; + v=data; + + MapPlot(v->v.x,v->v.y,VERTCOL); + + switch(selmode) + { + case SELECT_OVER: + col=OVERCOL; + break; + case SELECT_SELECTED: + col=SELCOL; + break; + default: + col=VERTBOXCOL; + break; + } + + MapRect(v->v.x-vertex_rad,v->v.y+vertex_rad,vertex_rad*2,vertex_rad*2,col); +} + + +void DrawObjectInfo_VERTEX(void) +{ + EditVert *v; + + VIDOOM_TRACE; + + if (!draw_current_info) + return; + + if ((current!=-1)&&((v=GETVERT(current)))) + GuiDrawInfoBox("VERTEX",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "Vertex No : %10d|" + "X : %10d|" + "Y : %10d", + current, + v->v.x,v->v.y); + else + GuiDrawInfoBox("VERTEX",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, + "Vertex No : - |" + "X : - |" + "Y : - "); +} + + +void DrawObjectHeader_VERTEX(void) +{ +} + + +void MoveObject_VERTEX(void) +{ + GFXEvent ev; + int omx,omy; + int dmx,dmy; + int done; + Iterator i; + Iterator i2; + EditVert *v,*v2; + List orig; + int *f; + int r; + Point p; + int cancel; + int done_merge; + + VIDOOM_TRACE; + + omx=SnapX(XToMap(ms.x)); + omy=SnapY(YToMap(ms.y)); + GFX_bounce(); + + done=FALSE; + cancel=FALSE; + done_merge=FALSE; + orig=ListNew(sizeof(Point)); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + v=GETVERT(*f); + p.x=v->v.x; + p.y=v->v.y; + ListAppend(orig,&p); + i=IteratorNext(i); + } + + while(!done) + { + GuiDrawInfoBox("Move VERTEX",GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE, + "Left mouse button to place|ESC to cancel"); + + GFX_redraw(); + GFX_await_input_full(&ev); + + switch(ev.type) + { + case GFX_KEY_EVENT: + HandleMoveKey(ev.key,FALSE); + + /* Cancel the move + */ + if (ev.key.code==GFX_ESC) + { + cancel=TRUE; + + i=ListIterator(selected); + i2=ListIterator(orig); + + while(i2) + { + f=IteratorData(i); + memcpy(&p,IteratorData(i2),sizeof(Point)); + v=GETVERT(*f); + v->v.x=p.x; + v->v.y=p.y; + + i=IteratorNext(i); + i2=IteratorNext(i2); + } + + done=TRUE; + dmx=0; + dmy=0; + } + else + { + dmx=SnapX(XToMap(ms.x))-omx; + dmy=SnapY(YToMap(ms.y))-omy; + } + + break; + + case GFX_MOUSE_EVENT: + memcpy(&ms,&ev.mouse,sizeof(ev.mouse)); + + if (ms.b&GFX_BUTLEFT) + done=TRUE; + + dmx=SnapX(XToMap(ms.x))-omx; + dmy=SnapY(YToMap(ms.y))-omy; + break; + + default: + dmx=0; + dmy=0; + break; + } + + if ((dmx)||(dmy)) + { + i=ListIterator(selected); + while(i) + { + f=IteratorData(i); + + v=GETVERT(*f); + + v->v.x+=dmx; + v->v.y+=dmy; + + i=IteratorNext(i); + } + + omx=SnapX(XToMap(ms.x)); + omy=SnapY(YToMap(ms.y)); + + FullRedraw(); + } + } + + ListClear(orig); + + if (!cancel) + { + int ask; + int ok; + + ask=TRUE; + ok=TRUE; + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + + for(r=0;(r<MapSize(vertex))&&(ok);r++) + if (r!=*f) + { + v=GETVERT(*f); + v2=GETVERT(r); + + if ((v)&&(v2)) + if ((v->v.x==v2->v.x)&&(v->v.y==v2->v.y)) + { + if (ask) + { + ok=YesNo("Merge overlapping vertexes?"); + ask=FALSE; + } + + if (ok) + { + MergeVertex(*f,r); + done_merge=TRUE; + } + } + } + + i=IteratorNext(i); + } + + if (done_merge) + SectorCalcContainingAll(); + } + + if ((clear_on_move)&&(!cancel)) + ClearSelection(); + + FullRedraw(); +} + + +void RotateObject_VERTEX(double angle) +{ + int cx,cy; + int x,y; + int *f; + Iterator i; + EditVert *v; + + VIDOOM_TRACE; + + VertexSelectionCentre(&cx,&cy); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + v=GETVERT(*f); + + x=v->v.x; + y=v->v.y; + + Rotate(cx,cy,&x,&y,angle); + + v->v.x=x; + v->v.y=y; + + i=IteratorNext(i); + } +} + + +void ScaleObject_VERTEX(double scale) +{ + int cx,cy; + int x,y; + int *f; + Iterator i; + EditVert *v; + + VIDOOM_TRACE; + + VertexSelectionCentre(&cx,&cy); + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + v=GETVERT(*f); + + x=v->v.x; + y=v->v.y; + + Scale(cx,cy,&x,&y,scale); + + v->v.x=x; + v->v.y=y; + + i=IteratorNext(i); + } +} + + +void SetTagObject_VERTEX(int tag) +{ + VIDOOM_TRACE; +} + + +void LocateObject_VERTEX(void *obj) +{ + EditVert *v; + + VIDOOM_TRACE; + + v=obj; + + ox=v->v.x-scale*SCRW/2; + oy=v->v.y+scale*SCRH/2; +} + + +void ObjectInsert_VERTEX(void) +{ + Object o; + EditVert *v; + int f; + + VIDOOM_TRACE; + + v=Grab(sizeof(EditVert)); + v->v.x=SnapX(XToMap(ms.x)); + v->v.y=SnapY(YToMap(ms.y)); + v->l=ListNew(sizeof(int)); + + f=MapSize(vertex); + + switch(insert_select) + { + case HOVER_NONE: + o.select=SELECT_NONE; + break; + case HOVER_ADD: + case HOVER_SINGLE: + o.select=SELECT_SELECTED; + break; + } + + o.data=v; + DrawObject_VERTEX(v,o.select); + MapAdd(vertex,f,&o); + + switch(insert_select) + { + case HOVER_NONE: + break; + case HOVER_ADD: + ListAppend(selected,&f); + break; + case HOVER_SINGLE: + ClearSelection(); + ListAppend(selected,&f); + FullRedraw(); + break; + } +} + + +void ObjectDelete_VERTEX(void) +{ + Iterator i; + Object *o; + EditVert *v; + int *f; + int bound; + + VIDOOM_TRACE; + + i=ListIterator(selected); + bound=FALSE; + + while(i) + { + f=IteratorData(i); + + o=MapElem(vertex,*f); + v=o->data; + + if (ListSize(v->l)) + bound=TRUE; + else + { + Release(o->data); + o->data=NULL; + o->select=SELECT_NONE; + } + + i=IteratorNext(i); + } + + ClearSelection(); + FullRedraw(); + + if (bound) + GuiInfoBox("WARNING","1 or more VERTEX found still bound to LINEDEF|" + "These have not been deleted"); +} + + +void ObjectMenu_VERTEX(void) +{ + Iterator i; + int from; + int to; + int first; + int type; + int flag; + int two; + int wr,wl; + int oxr,oxl; + DirName sr_upper,sr_middle,sr_lower; + DirName sl_upper,sl_middle,sl_lower; + EditVert *v1=NULL,*v2=NULL; + int cancel=FALSE; + int redraw=FALSE; + int in_sector; + int loop; + int nl; + + VIDOOM_TRACE; + + GFX_redraw(); + + switch(GUI_menu("Vertex",ms.x,ms.y,vertex_popup,GUI_CANCEL)) + { + case TM_CHAIN: + cancel=TRUE; + + if (ListSize(selected)<2) + GuiInfoBox("ERROR","Need 2 or more vertices|to chain a line"); + else + { + if (GetLinedefValues(TRUE,0,0,0,0, + &type,&flag,&two, + sr_upper,sr_middle,sr_lower, + sl_upper,sl_middle,sl_lower)) + { + if (ListSize(selected)>2) + loop=YesNo("Join last vertex to first one?"); + else + loop=FALSE; + + oxr=0; + oxl=0; + + wr=CalcTextureWidth(sr_upper,sr_middle,sr_lower); + + if (two) + wl=CalcTextureWidth(sl_upper,sl_middle,sl_lower); + else + wl=0; + + i=ListIterator(selected); + + memcpy(&to,IteratorData(i),sizeof(int)); + i=IteratorNext(i); + first=to; + + while(i) + { + from=to; + memcpy(&to,IteratorData(i),sizeof(int)); + i=IteratorNext(i); + + v1=GETVERT(from); + v2=GETVERT(to); + + in_sector=SectorHoldingPoint(v1->v.x,v1->v.y); + + nl=CreateNewLinedef(from,to, + flag,type,0,two, + oxr,0,in_sector, + sr_upper,sr_middle,sr_lower, + oxl,0,in_sector, + sl_upper,sl_middle,sl_lower); + + IntListUniqAdd(v1->l,nl); + IntListUniqAdd(v2->l,nl); + + if (wr) + oxr=(oxr+Len(v1->v.x,v1->v.y,v2->v.x,v2->v.y))%wr; + + if ((two)&&(wl)) + oxl=(oxl+Len(v1->v.x,v1->v.y,v2->v.x,v2->v.y))%wl; + } + + in_sector=SectorHoldingPoint(v2->v.x,v2->v.y); + + if (loop) + { + v1=GETVERT(to); + v2=GETVERT(first); + + nl=CreateNewLinedef(to,first, + flag,type,0,two, + oxr,0,in_sector, + sr_upper,sr_middle,sr_lower, + oxl,0,in_sector, + sl_upper,sl_middle,sl_lower); + + IntListUniqAdd(v1->l,nl); + IntListUniqAdd(v2->l,nl); + } + + cancel=FALSE; + redraw=TRUE; + + SectorCalcContainingAll(); + } + } + + break; + + case TM_CHAIN_SECTOR: + if (CreateSector(selected)) + redraw=TRUE; + else + cancel=TRUE; + break; + + case TM_MERGE: + if (ListSize(selected)<2) + { + GuiInfoBox("ERROR","Need 2 or more vertices|to merge them"); + cancel=TRUE; + } + else + { + redraw=TRUE; + i=ListIterator(selected); + memcpy(&first,IteratorData(i),sizeof(int)); + i=IteratorNext(i); + + while(i) + { + memcpy(&to,IteratorData(i),sizeof(int)); + MergeVertex(first,to); + i=IteratorNext(i); + } + + SectorCalcContainingAll(); + } + break; + + case TM_SNAP: + { + int orig_lock; + int *f; + EditVert *v; + + orig_lock=grid_lock; + grid_lock=TRUE; + + i=ListIterator(selected); + + while(i) + { + f=IteratorData(i); + v=GETVERT(*f); + + v->v.x=SnapX(v->v.x); + v->v.y=SnapX(v->v.y); + + i=IteratorNext(i); + } + + grid_lock=orig_lock; + redraw=TRUE; + + break; + } + + case TM_DELETE: + ObjectDelete_VERTEX(); + break; + + case TM_MOVE: + MoveObject_VERTEX(); + break; + + default: + cancel=TRUE; + break; + } + + if ((!cancel)&&(clear_on_menu)) + { + ClearSelection(); + FullRedraw(); + } + else if (redraw) + FullRedraw(); +} + + +void ObjectKey_VERTEX(GFXKey k) +{ + int f; + int n; + Object *o; + EditVert *v; + + switch(k.code) + { + case GFX_F12: + ClearSelection(); + n=0; + + for(f=0;f<MapSize(vertex);f++) + { + o=MapElem(vertex,f); + + if (o->data) + { + v=o->data; + + if (ListSize(v->l)==0) + { + ListClear(v->l); + Release(o->data); + o->data=NULL; + n++; + } + } + } + + FullRedraw(); + GFX_redraw(); + + GuiInfoBox("NOTICE","Deleted %d vertices",n); + + break; + + default: + break; + } +} + + +int ObjectHasTag_VERTEX(void *obj, int tag) +{ + return(FALSE); +} + + +ObjDesc *ObjectOverlaid_VERTEX(int x, int y, int *no) +{ + int n; + EditVert *v; + ObjDesc *od; + int f; + + VIDOOM_TRACE; + + od=NULL; + + n=0; + + for(f=0;f<MapSize(vertex);f++) + { + v=GETVERT(f); + + if ((v)&&(PositionOnObject_VERTEX(x,y,v))) + { + n++; + od=ReGrab(od,sizeof(ObjDesc)*n); + od[n-1].no=f; + sprintf(od[n-1].detail,"%5d - %d,%d",f,v->v.x,v->v.y); + } + } + + *no=n; + return(od); +} + + +/* END OF FILE */ @@ -0,0 +1,58 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + File system interfaces + + $Id$ + + +*/ + +#ifndef VIDOOM_FILE_H + +#define VIDOOM_FILE_H + +/* Return current working directory +*/ +char *Pwd(void); + +/* Change directory +*/ +void Cd(char *path); + +/* Dirname portion of a path +*/ +char *Dirname(char *path); + +/* Basename portion of a path +*/ +char *Basename(char *path); + +/* Returns TRUE if file exists +*/ +int FileExists(char *path); + +/* Returns TRUE if the the two paths point to the same file +*/ +int FilenamesEqual(char *path1, char *path2); + +#endif @@ -0,0 +1,203 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Generic flag handling. Used for THING and LINEDEF flags + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include "platgui.h" +#include "flags.h" +#include "mem.h" +#include "list.h" + + +/* ---------------------------------------- TYPES AND VARS +*/ +typedef struct + { + int group; + int mask1; + int mask2; + char *name; + char flag[2]; + } Flag; + +static List list[2][2]; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static int Index(int hexen) +{ + if (hexen) + return(1); + else + return(0); +} + +static int IsSet(Flag *f,int v) +{ + v&=f->mask1; + return(v==f->mask2); +} + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ + +void FlagAdd(int hexen, int class, int group, + char *name, int mask1, int mask2, char flag) +{ + Flag f; + Iterator i; + Flag *pf; + + hexen=Index(hexen); + + if (!(list[hexen][class])) + list[hexen][class]=ListNew(sizeof(Flag)); + + f.name=Strdup(name); + f.mask1=mask1; + f.mask2=mask2; + f.flag[0]=flag; + f.flag[1]=0; + f.group=group; + + i=ListIterator(list[hexen][class]); + + while(i) + { + pf=IteratorData(i); + + if ((pf->mask1==mask1)&&(pf->mask2==mask2)) + { + Release(pf->name); + IteratorUpdate(i,&f); + IteratorClear(i); + return; + } + + i=IteratorNext(i); + } + + ListAppend(list[hexen][class],&f); +} + + +int SelectFlags(int hexen, int class, int *flags) +{ + static char *title[]={"THING FLAGS","LINEDEF FLAGS"}; + PLAT_MULTI *p; + Flag *pf; + int no; + int f; + Iterator i; + int res; + + hexen=Index(hexen); + + no=ListSize(list[hexen][class]); + p=Grab(sizeof(PLAT_MULTI)*(no+1)); + + i=ListIterator(list[hexen][class]); + + for(f=0;f<no;f++) + { + pf=IteratorData(i); + i=IteratorNext(i); + p[f].val=IsSet(pf,*flags); + p[f].text=pf->name; + p[f].group=pf->group; + } + + p[f].text=NULL; + + if (GUI_multi_box(title[class],p)) + { + i=ListIterator(list[hexen][class]); + + for(f=0;f<no;f++) + { + pf=IteratorData(i); + i=IteratorNext(i); + *flags&=~(pf->mask1); + } + + i=ListIterator(list[hexen][class]); + + for(f=0;f<no;f++) + { + pf=IteratorData(i); + i=IteratorNext(i); + + if (p[f].val) + *flags|=pf->mask2; + } + + res=TRUE; + } + else + res=FALSE; + + Release(p); + return(res); +} + + +char *FlagText(int hexen, int class, int flags) +{ + static char s[128]; + Flag *pf; + Iterator i; + + hexen=Index(hexen); + + i=ListIterator(list[hexen][class]); + strcpy(s,""); + + while(i) + { + pf=IteratorData(i); + + if (IsSet(pf,flags)) + { + /* + if (s[0]) + strcat(s,","); + */ + + strcat(s,pf->flag); + } + + i=IteratorNext(i); + } + + return(s); +} + + +/* END OF FILE */ @@ -0,0 +1,66 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Generic flag handling. Used for THING and LINEDEF flags + + $Id$ + +*/ + +#ifndef VIDOOM_FLAGS_H + +#define VIDOOM_FLAGS_H + +/* In all the following calls: + + - Set hexen to TRUE if editting or defining for HEXEN mode + - Set class to either THING_FLAGS or LINEDEF_FLAGS + +*/ + + +/* The different flag sets +*/ +#define THING_FLAGS 0 +#define LINEDEF_FLAGS 1 + + +/* Add a new flag +*/ +void FlagAdd(int hexen, int class, int group, + char *name, int mask1, int mask2, char flag); + + +/* Allows deinfition of flags. Returns TRUE if accepted, FALSE is cancelled. +*/ +int SelectFlags(int hexen, int class, int *flags); + + +/* Return a static string representing the meaning of the passed in flags +*/ +char *FlagText(int hexen, int class, int flags); + + +#endif + + +/* END OF FILE */ @@ -0,0 +1 @@ +dir djgpp diff --git a/genlines.c b/genlines.c new file mode 100644 index 0000000..98ca1dd --- /dev/null +++ b/genlines.c @@ -0,0 +1,460 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Handles definition and storage of the supported generalised LINEDEFs + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include "platgui.h" +#include "gfx.h" +#include "platgui.h" +#include "genlines.h" +#include "mem.h" +#include "map.h" +#include "linedefs.h" + + +/* ---------------------------------------- TYPES AND VARS +*/ +typedef struct + { + char *name; + char *abbrev; + int val; + } gl_BitField; + +typedef struct + { + int i; + + char *name; + int no; + gl_BitField *bf; + } gl_BitMask; + +typedef struct + { + int mask; + int shift; + gl_BitMask *bm; + } gl_ClassBitMask; + +typedef struct + { + int i; + + char *name; + int hi; + int lo; + int no; + int mask; + gl_ClassBitMask *bm; + + PLAT_DIALOG *dial; + } gl_Class; + + +static Map mask_map=NULL; +static Map class_map[2]={NULL,NULL}; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static int Index(int for_hexen) +{ + if (for_hexen) + return(1); + else + return(0); +} + + +static gl_BitMask *GetMask(char *class) +{ + gl_BitMask *m; + int f; + + for(f=0;f<MapSize(mask_map);f++) + { + m=MapElem(mask_map,f); + + if ((m)&&(STREQ(class,m->name))) + return(m); + } + + return(NULL); +} + + +static gl_Class *GetClass(int i,char *class) +{ + gl_Class *c; + int f; + + for(f=0;f<MapSize(class_map[i]);f++) + { + c=MapElem(class_map[i],f); + + if ((c)&&(STREQ(class,c->name))) + return(c); + } + + return(NULL); +} + + +static gl_Class *ClassMenu(int i) +{ + static PLAT_MENU *menu[2]={NULL,NULL}; + gl_Class *cl; + int f,x,y; + + if (!menu[i]) + { + menu[i]=Grab(sizeof(PLAT_MENU)*(MapSize(class_map[i])+1)); + + for(f=0;f<MapSize(class_map[i]);f++) + { + cl=MapElem(class_map[i],f); + + menu[i][f].text=cl->name; + menu[i][f].client_index=f; + menu[i][f].child=NULL; + } + + menu[i][f].text=NULL; + } + + GFX_mouse(&x,&y); + f=GUI_menu("Generalised LINEDEF type",x,y,menu[i],GENLINE_NULLID); + + if (f==GENLINE_NULLID) + return(NULL); + else + return(MapElem(class_map[i],f)); +} + + +static void InitDialog(gl_Class *cl, int type) +{ + int f,r; + int mask; + + /* Create the dialog if not already done so + */ + if (!cl->dial) + { + if (cl->i!=cl->no) + GFX_exit(EXIT_FAILURE, + "Not all bitfields defined for class:\n %s\n",cl->name); + + cl->dial=Grab(sizeof(PLAT_DIALOG)*cl->no); + + for(f=0;f<cl->no;f++) + { + cl->dial[f].text=cl->bm[f].bm->name; + cl->dial[f].type=PLAT_DIAL_PICKLIST; + + cl->dial[f].data.pl.no=cl->bm[f].bm->no; + cl->dial[f].data.pl.current=0; + cl->dial[f].data.pl.text=Grab(cl->bm[f].bm->no*sizeof(char *)); + + for(r=0;r<cl->bm[f].bm->no;r++) + cl->dial[f].data.pl.text[r]=Strdup(cl->bm[f].bm->bf[r].name); + } + } + + /* Set the flags from the current value + */ + for(f=0;f<cl->no;f++) + { + cl->dial[f].data.pl.current=0; + + mask=(type>>cl->bm[f].shift)&cl->bm[f].mask; + + for(r=0;r<cl->bm[f].bm->no;r++) + if (cl->bm[f].bm->bf[r].val==mask) + cl->dial[f].data.pl.current=r; + } +} + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ + +void GenLineNewBitClass(char *class, int no_fields) +{ + gl_BitMask *bm; + + bm=Grab(sizeof(gl_BitMask)); + + if (!mask_map) + mask_map=MapNew(sizeof(gl_BitMask)); + + bm->name=Strdup(class); + bm->no=no_fields; + bm->i=0; + bm->bf=Grab(sizeof(gl_BitField)*bm->no); + + MapAdd(mask_map,-1,bm); +} + + +void GenLineAddBitmask(char *class, char *name, char *abbrev, int mask) +{ + gl_BitField *bf; + gl_BitMask *bm; + + if (!(bm=GetMask(class))) + GFX_exit(EXIT_FAILURE,"Adding bit field:\n%s\n\nfailed as " + "there is no bitmask:\n%s\n",name,class); + + if (bm->i==bm->no) + GFX_exit(EXIT_FAILURE,"Adding bit field:\n%s\n\n " + "overflows bitmask:\n%s\n",name,class); + + bf=&(bm->bf[bm->i++]); + + bf->name=Strdup(name); + bf->abbrev=Strdup(abbrev); + bf->val=mask; +} + + +void GenLineNewClass(int for_hexen, char *class, + int lo, int hi, int mask, int no_bitmasks) +{ + gl_Class *cl; + int i; + + i=Index(for_hexen); + + cl=Grab(sizeof(gl_Class)); + + if (!class_map[i]) + class_map[i]=MapNew(sizeof(gl_Class)); + + cl->name=Strdup(class); + cl->hi=hi; + cl->lo=lo; + cl->mask=mask; + cl->no=no_bitmasks; + cl->i=0; + cl->bm=Grab(sizeof(gl_ClassBitMask)*cl->no); + cl->dial=NULL; + + MapAdd(class_map[i],-1,cl); +} + + +void GenLineAdd(int for_hexen, char *class, char *bitmask, int shift) +{ + gl_Class *cl; + gl_BitMask *bm; + int f; + int i; + + i=Index(for_hexen); + + if (!(bm=GetMask(bitmask))) + GFX_exit(EXIT_FAILURE,"Adding bitmask:\n%s\n\nto class:\n%s\n\n" + "failed as there is no such bitmask class\n\n", + bitmask,class); + + if (bm->i!=bm->no) + GFX_exit(EXIT_FAILURE,"Adding bitmask:\n%s\n\nto class:\n%s\n\n" + "failed as not all fields have been defined " + "in the bitmask\n\n", + bitmask,class); + + if (!(cl=GetClass(i,class))) + GFX_exit(EXIT_FAILURE,"Adding bitmask:\n%s\n\nto class:\n%s\n\n" + "failed as there is no such class\n\n", + bitmask,class); + + if (cl->i==cl->no) + GFX_exit(EXIT_FAILURE,"Adding bitmask:\n%s\n\nto class:\n%s\n\n" + "overflows class\n\n", + bitmask,class); + + cl->bm[cl->i].bm=bm; + cl->bm[cl->i].shift=shift; + cl->bm[cl->i].mask=0; + + for(f=0;f<cl->bm[cl->i].bm->no;f++) + cl->bm[cl->i].mask|=cl->bm[cl->i].bm->bf[f].val; + + cl->i++; +} + + +int SelectGenLine(int for_hexen, int current_type) +{ + gl_Class *cl; + int n; + int f; + int i; + + i=Index(for_hexen); + + if (MapSize(class_map[i])==0) + return(GENLINE_NULLID); + + if (MapSize(class_map[i])==1) + cl=MapElem(class_map[i],0); + else + if (!(cl=ClassMenu(i))) + return(GENLINE_NULLID); + + InitDialog(cl,current_type); + + if (GUI_dialog(cl->name,cl->no,cl->dial)) + { + if (cl->mask!=-1) + n=cl->mask; + else + { + n=current_type; + + for(f=0;f<cl->no;f++) + n&=~(cl->bm[f].mask<<cl->bm[f].shift); + } + + for(f=0;f<cl->no;f++) + n|=(cl->bm[f].bm->bf[cl->dial[f].data.pl.current].val) + <<cl->bm[f].shift; + } + else + n=GENLINE_NULLID; + + return(n); +} + + +char *GenLineName(int for_hexen, int id,char *(*GetName)(int id)) +{ + static char s[512]; + gl_Class *cl; + int f,r,i; + int got; + int mask; + int index; + + index=Index(for_hexen); + + for(f=0;f<MapSize(class_map[index]);f++) + { + cl=MapElem(class_map[index],f); + + if ((id>=cl->lo)&&(id<=cl->hi)) + { + s[0]=0; + + for(r=0;r<cl->no;r++) + { + if (r) + strcat(s,"/"); + + mask=(id>>cl->bm[r].shift)&cl->bm[r].mask; + + got=FALSE; + + for(i=0;(i<cl->bm[r].bm->no)&&(!got);i++) + if (mask==cl->bm[r].bm->bf[i].val) + { + strcat(s,cl->bm[r].bm->bf[i].abbrev); + got=TRUE; + } + + if (!got) + strcat(s,"???"); + } + + if (cl->mask==-1) + { + for(f=0;f<cl->no;f++) + id&=~(cl->bm[f].mask<<cl->bm[f].shift); + + if (GetName(id)) + { + strcat(s," & "); + strcat(s,GetName(id)); + } + } + + return(s); + } + } + + return(NULL); +} + + +char *GenLineClass(int for_hexen, int id,char *(*GetClass)(int id)) +{ + static char s[512]; + gl_Class *cl; + int f; + int i; + + i=Index(for_hexen); + + for(f=0;f<MapSize(class_map[i]);f++) + { + cl=MapElem(class_map[i],f); + + if ((id>=cl->lo)&&(id<=cl->hi)) + { + strcpy(s,cl->name); + + if (cl->mask==-1) + { + for(f=0;f<cl->no;f++) + id&=~(cl->bm[f].mask<<cl->bm[f].shift); + + if (GetClass(id)) + { + strcat(s," & "); + strcat(s,GetClass(id)); + } + } + + return(s); + } + } + + return(NULL); +} + + + +int GenLineNoClasses(int for_hexen) +{ + return(MapSize(class_map[Index(for_hexen)])); +} + + +/* END OF FILE */ diff --git a/genlines.h b/genlines.h new file mode 100644 index 0000000..1fc6bfb --- /dev/null +++ b/genlines.h @@ -0,0 +1,86 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Handles definition and storage of the supported generalised LINEDEFs + + $Id$ + +*/ + +#ifndef VIDOOM_GENLINES_H + +#define VIDOOM_GENLINES_H + +/* This is not going to be an ID, so is used to detect whether LinedefSelect() + was cancelled. +*/ +#define GENLINE_NULLID -666 + + +/* Add a new class of bitmask +*/ +void GenLineNewBitClass(char *class, int no_fields); + +/* Add a bitmask to a defined class +*/ +void GenLineAddBitmask(char *class, char *name, char *abbrev, int mask); + + +/* Define a generalised linedef type class +*/ +void GenLineNewClass(int for_hexen,char *class, + int lo, int hi, int mask, int no_bitmasks); + +/* Add the named bitmask to the supplied class +*/ +void GenLineAdd(int for_hexen, char *class,char *bitmask, int shift); + + +/* Selects a generalised linedef type, returning the bit fields or + GENLINE_NULLID if cancelled +*/ +int SelectGenLine(int for_hexen, int current_type); + + +/* Returns the full name of a generalised linedef type. If the generalised + type is added to a normal linedef type the passed function GetName is + used to get the name for that part. +*/ +char *GenLineName(int for_hexen, int id,char *(*GetName)(int id)); + + +/* Returns the class name of a generalised linedef type. If the generalised + type is added to a normal linedef type the passed function GetClass is + used to get the class for that part. +*/ +char *GenLineClass(int for_hexen, int id,char *(*GetClass)(int id)); + + +/* Returns the number of classes defined +*/ +int GenLineNoClasses(int for_hexen); + + +#endif + + +/* END OF FILE */ diff --git a/gensect.c b/gensect.c new file mode 100644 index 0000000..74cbafa --- /dev/null +++ b/gensect.c @@ -0,0 +1,423 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Handles definition and storage of the supported generalised SECTOR types. + Basically a copy of genlines.c. + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include "platgui.h" +#include "gfx.h" +#include "platgui.h" +#include "gensect.h" +#include "mem.h" +#include "map.h" + + +/* ---------------------------------------- TYPES AND VARS +*/ +typedef struct + { + char *name; + char *abbrev; + int val; + } gs_BitField; + +typedef struct + { + int i; + + char *name; + int no; + gs_BitField *bf; + } gs_BitMask; + +typedef struct + { + int mask; + int shift; + gs_BitMask *bm; + } gs_ClassBitMask; + +typedef struct + { + int i; + + char *name; + int hi; + int lo; + int mask; + int no; + gs_ClassBitMask *bm; + + PLAT_DIALOG *dial; + } gs_Class; + + +static Map mask_map=NULL; +static Map class_map[2]={NULL,NULL}; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static int Index(int for_hexen) +{ + if (for_hexen) + return(1); + else + return(0); +} + + +static gs_BitMask *GetMask(char *class) +{ + gs_BitMask *m; + int f; + + for(f=0;f<MapSize(mask_map);f++) + { + m=MapElem(mask_map,f); + + if ((m)&&(STREQ(class,m->name))) + return(m); + } + + return(NULL); +} + + +static gs_Class *GetClass(int index, char *class) +{ + gs_Class *c; + int f; + + for(f=0;f<MapSize(class_map[index]);f++) + { + c=MapElem(class_map[index],f); + + if ((c)&&(STREQ(class,c->name))) + return(c); + } + + return(NULL); +} + + +static gs_Class *ClassMenu(int index) +{ + static PLAT_MENU *menu[2]={NULL,NULL}; + gs_Class *cl; + int f,x,y; + + if (!menu[index]) + { + menu[index]=Grab(sizeof(PLAT_MENU)*(MapSize(class_map[index])+1)); + + for(f=0;f<MapSize(class_map[index]);f++) + { + cl=MapElem(class_map[index],f); + + menu[index][f].text=cl->name; + menu[index][f].client_index=f; + menu[index][f].child=NULL; + } + + menu[index][f].text=NULL; + } + + GFX_mouse(&x,&y); + f=GUI_menu("Generalised SECTOR type",x,y,menu[index],GENSECT_NULLID); + + if (f==GENSECT_NULLID) + return(NULL); + else + return(MapElem(class_map[index],f)); +} + + +static void InitDialog(gs_Class *cl, int type) +{ + int f,r; + int mask; + + /* Create the dialog if not already done so + */ + if (!cl->dial) + { + if (cl->i!=cl->no) + GFX_exit(EXIT_FAILURE, + "Not all bitfields defined for class:\n %s\n",cl->name); + + cl->dial=Grab(sizeof(PLAT_DIALOG)*cl->no); + + for(f=0;f<cl->no;f++) + { + cl->dial[f].text=cl->bm[f].bm->name; + cl->dial[f].type=PLAT_DIAL_PICKLIST; + + cl->dial[f].data.pl.no=cl->bm[f].bm->no; + cl->dial[f].data.pl.current=0; + cl->dial[f].data.pl.text=Grab(cl->bm[f].bm->no*sizeof(char *)); + + for(r=0;r<cl->bm[f].bm->no;r++) + cl->dial[f].data.pl.text[r]=Strdup(cl->bm[f].bm->bf[r].name); + } + } + + /* Set the flags from the current value + */ + for(f=0;f<cl->no;f++) + { + cl->dial[f].data.pl.current=0; + + mask=(type>>cl->bm[f].shift)&cl->bm[f].mask; + + for(r=0;r<cl->bm[f].bm->no;r++) + if (cl->bm[f].bm->bf[r].val==mask) + cl->dial[f].data.pl.current=r; + } +} + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ + +void GenSectNewBitClass(char *class, int no_fields) +{ + gs_BitMask *bm; + + bm=Grab(sizeof(gs_BitMask)); + + if (!mask_map) + mask_map=MapNew(sizeof(gs_BitMask)); + + bm->name=Strdup(class); + bm->no=no_fields; + bm->i=0; + bm->bf=Grab(sizeof(gs_BitField)*bm->no); + + MapAdd(mask_map,-1,bm); +} + + +void GenSectAddBitmask(char *class, char *name, char *abbrev, int mask) +{ + gs_BitField *bf; + gs_BitMask *bm; + + if (!(bm=GetMask(class))) + GFX_exit(EXIT_FAILURE,"Adding bit field:\n%s\n\nfailed as " + "there is no bitmask:\n%s\n",name,class); + + if (bm->i==bm->no) + GFX_exit(EXIT_FAILURE,"Adding bit field:\n%s\n\n " + "overflows bitmask:\n%s\n",name,class); + + bf=&(bm->bf[bm->i++]); + + bf->name=Strdup(name); + bf->abbrev=Strdup(abbrev); + bf->val=mask; +} + + +void GenSectNewClass(int for_hexen, char *class, + int lo, int hi, int mask, int no_bitmasks) +{ + gs_Class *cl; + int i; + + i=Index(for_hexen); + + cl=Grab(sizeof(gs_Class)); + + if (!class_map[i]) + class_map[i]=MapNew(sizeof(gs_Class)); + + cl->name=Strdup(class); + cl->hi=hi; + cl->lo=lo; + cl->mask=mask; + cl->no=no_bitmasks; + cl->i=0; + cl->bm=Grab(sizeof(gs_ClassBitMask)*cl->no); + cl->dial=NULL; + + MapAdd(class_map[i],-1,cl); +} + + +void GenSectAdd(int for_hexen, char *class, char *bitmask, int shift) +{ + gs_Class *cl; + gs_BitMask *bm; + int f; + int i; + + i=Index(for_hexen); + + if (!(bm=GetMask(bitmask))) + GFX_exit(EXIT_FAILURE,"Adding bitmask:\n%s\n\nto class:\n%s\n\n" + "failed as there is no such bitmask class\n\n", + bitmask,class); + + if (bm->i!=bm->no) + GFX_exit(EXIT_FAILURE,"Adding bitmask:\n%s\n\nto class:\n%s\n\n" + "failed as not all fields have been defined " + "in the bitmask\n\n", + bitmask,class); + + if (!(cl=GetClass(i,class))) + GFX_exit(EXIT_FAILURE,"Adding bitmask:\n%s\n\nto class:\n%s\n\n" + "failed as there is no such class\n\n", + bitmask,class); + + if (cl->i==cl->no) + GFX_exit(EXIT_FAILURE,"Adding bitmask:\n%s\n\nto class:\n%s\n\n" + "overflows class\n\n", + bitmask,class); + + cl->bm[cl->i].bm=bm; + cl->bm[cl->i].shift=shift; + cl->bm[cl->i].mask=0; + + for(f=0;f<cl->bm[cl->i].bm->no;f++) + cl->bm[cl->i].mask|=cl->bm[cl->i].bm->bf[f].val; + + cl->i++; +} + + +int SelectGenSect(int for_hexen, int current_type) +{ + gs_Class *cl; + int n; + int f; + int i; + + i=Index(for_hexen); + + if (MapSize(class_map[i])==0) + return(GENSECT_NULLID); + + if (MapSize(class_map[i])==1) + cl=MapElem(class_map[i],0); + else + if (!(cl=ClassMenu(i))) + return(GENSECT_NULLID); + + InitDialog(cl,current_type); + + if (GUI_dialog(cl->name,cl->no,cl->dial)) + { + if (cl->mask!=-1) + n=cl->mask; + else + { + n=current_type; + + for(f=0;f<cl->no;f++) + n&=~(cl->bm[f].mask<<cl->bm[f].shift); + } + + for(f=0;f<cl->no;f++) + n|=(cl->bm[f].bm->bf[cl->dial[f].data.pl.current].val) + <<cl->bm[f].shift; + } + else + n=GENSECT_NULLID; + + return(n); +} + + +char *GenSectName(int for_hexen, int id,char *(*GetName)(int hexen, int id)) +{ + static char s[512]; + gs_Class *cl; + int f,r,i; + int got; + int mask; + int index; + + index=Index(for_hexen); + + for(f=0;f<MapSize(class_map[index]);f++) + { + cl=MapElem(class_map[index],f); + + if ((id>=cl->lo)&&(id<=cl->hi)) + { + s[0]=0; + + for(r=0;r<cl->no;r++) + { + if (r) + strcat(s,"/"); + + mask=(id>>cl->bm[r].shift)&cl->bm[r].mask; + + got=FALSE; + + for(i=0;(i<cl->bm[r].bm->no)&&(!got);i++) + if (mask==cl->bm[r].bm->bf[i].val) + { + strcat(s,cl->bm[r].bm->bf[i].abbrev); + got=TRUE; + } + + if (!got) + strcat(s,"???"); + } + + if (cl->mask==-1) + { + for(f=0;f<cl->no;f++) + id&=~(cl->bm[f].mask<<cl->bm[f].shift); + + if (GetName(for_hexen,id)) + { + strcat(s," & "); + strcat(s,GetName(for_hexen,id)); + } + } + + + return(s); + } + } + + return(NULL); +} + + +int GenSectNoClasses(int for_hexen) +{ + return(MapSize(class_map[Index(for_hexen)])); +} + + +/* END OF FILE */ diff --git a/gensect.h b/gensect.h new file mode 100644 index 0000000..deba763 --- /dev/null +++ b/gensect.h @@ -0,0 +1,80 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Handles definition and storage of the supported generalised SECTORs + + $Id$ + +*/ + +#ifndef VIDOOM_GENSECT_H + +#define VIDOOM_GENSECT_H + +/* This is not going to be an ID, so is used to detect whether SelectGenSect() + was cancelled. +*/ +#define GENSECT_NULLID -666 + + +/* Add a new class of bitmask +*/ +void GenSectNewBitClass(char *class, int no_fields); + +/* Add a bitmask to a defined class +*/ +void GenSectAddBitmask(char *class, char *name, char *abbrev, int mask); + + +/* Define a generalised linedef type class +*/ +void GenSectNewClass(int for_hexen, char *class, + int lo, int hi, int mask, int no_bitmasks); + +/* Add the named bitmask to the supplied class +*/ +void GenSectAdd(int for_hexen,char *class,char *bitmask, int shift); + + +/* Selects a generalised linedef type, returning the bit fields or + GENSECT_NULLID if cancelled +*/ +int SelectGenSect(int for_hexen,int current_type); + + +/* Returns the full name of a generalised sector type. If the generalised + type is added to a normal linedef type the passed function GetName is + used to get the name for that part. +*/ +char *GenSectName(int for_hexem, int id, + char *(*GetName)(int hexen, int id)); + + +/* Returns the number of classes defined +*/ +int GenSectNoClasses(int for_hexen); + + +#endif + + +/* END OF FILE */ @@ -0,0 +1,319 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Provides a skin of graphics and input functions (in case of future + ports). + + Assumes these features: + + - A true, or hicolor, display + - A Fixed font + - Default origin is in the top left of the display, with X positve + along and Y positive down + - A buffered display. The screen contents should not change until + GFX_redraw() is called. If not honoured viDOOM should still work up + to a point, but it's use of the XOR mode may not be apparent to the + user. + + $Id$ + +*/ +#ifndef VIDOOM_GFX_H + +#define VIDOOM_GFX_H + +/* Opaque type to define a bitmap object and the public creator type, along + with the picklist data providers. Note that the bitmap object is assumed + to have a maximum of 256 unique colours. +*/ +typedef void *GFX_IMAGE; + +#define MAX_GFXBM_W 256 +#define MAX_GFXBM_H 256 + +typedef struct + { + int w; /* Width */ + int h; /* Height */ + int pal[256]; /* Palette */ + unsigned char *data; /* Accessed as data+(x)+(y)*(width) */ + } GFX_BITMAP; + +/* Key press structure +*/ +typedef struct + { + int type; /* Event type (for GFX_await_input()) */ + int shift; /* Is shift pressed? */ + int ctrl; /* Is control pressed? */ + int alt; /* Is ALT pressed? */ + char ascii; /* ASCII code for the char. If zero ... */ + int code; /* ... this holds a special code defined below */ + } GFXKey; + + +/* Special non-ASCII keys that can be returned +*/ +#define GFX_ASCII 0 /* Not a special key - just ASCII */ + +#define GFX_F1 1001 +#define GFX_F2 1002 +#define GFX_F3 1003 +#define GFX_F4 1004 +#define GFX_F5 1005 +#define GFX_F6 1006 +#define GFX_F7 1007 +#define GFX_F8 1008 +#define GFX_F9 1009 +#define GFX_F10 1010 +#define GFX_F11 1011 +#define GFX_F12 1012 + +#define GFX_ESC 1021 +#define GFX_INSERT 1022 +#define GFX_HOME 1023 +#define GFX_PGUP 1024 +#define GFX_DELETE 1025 +#define GFX_END 1026 +#define GFX_PGDN 1027 + +#define GFX_UP 1028 +#define GFX_DOWN 1029 +#define GFX_LEFT 1030 +#define GFX_RIGHT 1031 + +#define GFX_ENTER 1032 +#define GFX_BACKSPACE 1033 +#define GFX_TAB 1034 + + +/* Mouse buttons +*/ +#define GFX_BUTLEFT 0x01 +#define GFX_BUTRIGHT 0x02 +#define GFX_BUTMIDDLE 0x04 + +/* The following aren't required by viDOOM (but can be used) +*/ +#define GFX_MSWHEELUP 0x08 +#define GFX_MSWHEELDOWN 0x10 + + +/* Event structs for GFX_await_input(). +*/ +#define GFX_KEY_EVENT 1 /* Key pressed */ +#define GFX_MOUSE_EVENT 2 /* Mouse button pressed or mouse moved */ + +typedef struct GFXMouse + { + int type; /* Event type (for GFX_await_input()) */ + int shift; /* Is shift pressed? */ + int ctrl; /* Is control pressed? */ + int alt; /* Is ALT pressed? */ + int x; /* Mouse X co-ord */ + int y; /* Mouse X co-ord */ + int b; /* Mouse button mask */ + } GFXMouse; + +typedef union GFXEvent + { + int type; + GFXKey key; + GFXMouse mouse; + } GFXEvent; + + + +/* Assume an intensity of 0-255 for each RGB component. The library assumes + that colours are represented as an RGB triplet in that order. +*/ +#define V_RGB(r,g,b) ((r)<<16|(g)<<8|(b)) + +#define BLACK 0x000000 +#define WHITE 0xffffff +#define RED 0xff0000 +#define GREEN 0x00ff00 +#define BLUE 0x0000ff +#define GREY(l) V_RGB(l,l,l) + +/* Initialise graphics. This is pretty much called the first thing called. + After calling this it must be that the following functions at least are + operational: + + GFX_close() + GFX_open() + GFX_exit() + GFX_create_image() + + The platform GFX driver can assume no drawing primitives are called till + the screen is opended. + +*/ +void GFX_init(void); + + +/* Closedown for clean, non-error exits. See GFX_exit() for error exits +*/ +void GFX_close(void); + + +/* Create a GFX_IMAGE for the passed GFX_BITMAP. Returns NULL on failure. +*/ +GFX_IMAGE GFX_create_image(GFX_BITMAP *bm); + +/* Destory a bitmap +*/ +void GFX_destroy_image(GFX_IMAGE img); + +/* Draw an image +*/ +void GFX_draw_image(GFX_IMAGE i,int x,int y); + + +/* Fill the screen with an image. Not really crucial as it's just used for + the splash screen. If it cannot be honoured properly just drawing the + GFX_IMAGE centred in the screen should be adequate +*/ +void GFX_fill_screen(GFX_IMAGE i); + + +/* Open a screen/window/whatever. Expected to be true colour, or an + approximation of. Should exit cleanly on failure. +*/ +void GFX_open(int width, int height); + + +/* Clear the screen to a colour +*/ +void GFX_clear(int col); + + +/* It is assumed by viDOOM that what has been drawn will NOT appear on screen + till this is called. +*/ +void GFX_redraw(void); + + +/* Draw a line +*/ +void GFX_line(int x1, int y1, int x2, int y2, int col); + + +/* Plot a point +*/ +void GFX_plot(int x, int y, int col); + + +/* Draw a circle (and filled version) +*/ +void GFX_circle(int x, int y, int radius, int col); +void GFX_fcircle(int x, int y, int radius, int col); + + +/* Draw a rectangle (and filled version). Note that zero and negative width + and heights must be allowed. +*/ +void GFX_rect(int x, int y, int w, int h, int col); +void GFX_frect(int x, int y, int w, int h, int col); + + +/* Sets and clears XOR mode (used for rubber bands, etc). +*/ +void GFX_set_XOR_mode(void); +void GFX_clear_XOR_mode(void); + + +/* printf() text at x,y. The baseline is assumed to be the top left corner of + the box enclosing the text and text is assumed to be transparent +*/ +void GFX_print(int x, int y, int col, char *fmt, ...); + + +/* Return the dimension of the (fixed width!) font used for printing +*/ +int GFX_fh(void); +int GFX_fw(void); + + +/* Return the number of mouse buttons. Should be at least 2. +*/ +int GFX_mouse_buttons(void); + + +/* Sample the current state of the mouse. Return is the button mask. If X or + Y is NULL that position is not returned. +*/ +int GFX_mouse(int *x, int *y); + + +/* Wait for a key to be pressed. key can be NULL if you're not interested in + the key. +*/ +void GFX_waitkey(GFXKey *key); + + +/* Check for a key to be pressed. Returns TRUE or FALSE accordingly and sets + the GFXKey struct up. +*/ +int GFX_key(GFXKey *key); + + +/* If this platform is not event driven, this call should wait kill all keys + and mouse buttons have been released. On event driven ones this may clear + any outstanding input events. +*/ +void GFX_bounce(void); + + +/* Await for any sort of input from the keyboard or mouse buttons. This is + included in the platform specific so that event driven environs (eg. X11) + can work naturally, while non-event driven environments (eg. DOS) can have + the busy polling loop here, rather than in the generic code. +*/ +void GFX_await_input(GFXEvent *ev); + + +/* This works like the above call, but returns mouse movement events (as a + GFX_MOUSE_EVENT) as well. +*/ +void GFX_await_input_full(GFXEvent *ev); + + +/* Tidy up the graphics (switch them off, release mem, etc), print the + supplied error message and then exit with the return code. +*/ +void GFX_exit(int code,char *fmt,...); + + +/* This interface _really_ does not have to do anything. It is just on the + development system so that grabs could be made for the docs. + + If you feel the urge though this should save a snap shot of the current + screen in the passed file. +*/ +void GFX_save_screen(char *path); + + +#endif + + +/* END OF FILE */ diff --git a/gfxtest.c b/gfxtest.c new file mode 100644 index 0000000..2392f90 --- /dev/null +++ b/gfxtest.c @@ -0,0 +1,862 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Test program for GFX interface +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" +#include <stdio.h> +#include <ctype.h> +#include <time.h> +#include "gfx.h" +#include "platgui.h" +#include "editvar.h" +#include "mem.h" + +#define SCRW 640 +#define SCRH 480 + +#define RND(x) (rand()%(x)) +#define RCOL V_RGB(RND(255),RND(255),RND(255)) + +void TestPrint(void) +{ + int f; + + GFX_clear(BLACK); + + GFX_print(0,0,WHITE,"Top left"); + GFX_print(0,SCRH-GFX_fh(),WHITE,"Bottom left"); + GFX_print(SCRW-GFX_fw()*strlen("Top right"),0,WHITE,"Top right"); + GFX_print(SCRW-GFX_fw()*strlen("Bottom right"), + SCRH-GFX_fh(),WHITE,"Bottom right"); + + for(f=1;f<4;f++) + GFX_print(0,100+f*GFX_fh(),WHITE,"Row %d",f); + + GFX_redraw(); + GFX_waitkey(NULL); +} + + +void TestMouse(void) +{ + GFXEvent e; + int q; + int f; + + GFX_clear(BLACK); + + q=FALSE; + + while(!q) + { + GFX_redraw(); + GFX_await_input_full(&e); + + switch(e.type) + { + case GFX_KEY_EVENT: + if (e.key.code==GFX_ESC) + q=TRUE; + + if (e.key.code==GFX_ENTER) + GFX_clear(BLACK); + break; + + case GFX_MOUSE_EVENT: + f=0; + if (e.mouse.b&GFX_BUTLEFT) + f|=0xff0000; + if (e.mouse.b&GFX_BUTMIDDLE) + f|=0xff00; + if (e.mouse.b&GFX_BUTRIGHT) + f|=0xff; + + if (f==0) + f=0x808080; + + GFX_frect(0,0,SCRW,GFX_fh(),BLACK); + GFX_print(0,0,WHITE,"x:%-4d y:%-4d b:%c%c%c", + e.mouse.x,e.mouse.y, + e.mouse.b&GFX_BUTLEFT ? 'L':' ', + e.mouse.b&GFX_BUTMIDDLE ? 'M':' ', + e.mouse.b&GFX_BUTRIGHT ? 'R':' '); + + GFX_plot(e.mouse.x,e.mouse.y,f); + break; + } + } +} + + +void TestRect(void) +{ + int f,w; + int ox,oy; + + ox=20; + oy=100; + + for(w=3;w>-4;w--) + { + GFX_clear(BLACK); + + GFX_print(0,0,WHITE,"Width/height %d",w); + + for(f=0;f<5;f++) + { + GFX_plot(ox+f*20,oy+20,WHITE); + GFX_rect(ox+f*20+10,oy+20,w,w,WHITE); + + GFX_plot(ox+f*20,oy+40,WHITE); + GFX_frect(ox+f*20+10,oy+40,w,w,WHITE); + + GFX_plot(ox+f*20,oy+60,WHITE); + GFX_rect(ox+f*20+10,oy+60,w,w*2,WHITE); + + GFX_plot(ox+f*20,oy+80,WHITE); + GFX_frect(ox+f*20+10,oy+80,w,w*2,WHITE); + + GFX_plot(ox+f*20,oy+100,WHITE); + GFX_rect(ox+f*20+10,oy+100,w*2,w,WHITE); + + GFX_plot(ox+f*20,oy+120,WHITE); + GFX_frect(ox+f*20+10,oy+120,w*2,w,WHITE); + } + + GFX_redraw(); + GFX_waitkey(NULL); + } +} + + +void TestDim(void) +{ + GFX_clear(BLACK); + + GFX_line(0,0,SCRW-1,0,WHITE); + GFX_line(0,SCRH-1,SCRW-1,SCRH-1,WHITE); + + GFX_line(0,0,0,SCRH-1,WHITE); + GFX_line(SCRW-1,0,SCRW-1,SCRH-1,WHITE); + + GFX_line(0,0,SCRW-1,SCRH-1,WHITE); + GFX_line(SCRW-1,0,0,SCRH-1,WHITE); + + GFX_print(20,SCRH/2,WHITE,"GFX_line()"); + + GFX_redraw(); + GFX_waitkey(NULL); + + GFX_clear(BLACK); + + GFX_rect(0,0,SCRW,SCRH,WHITE); + GFX_print(20,SCRH/2,WHITE,"GFX_rect()"); + + GFX_redraw(); + GFX_waitkey(NULL); + + GFX_clear(BLACK); + + GFX_frect(0,0,SCRW,SCRH,RED); + GFX_print(20,SCRH/2,WHITE,"GFX_frect()"); + + GFX_redraw(); + GFX_waitkey(NULL); +} + + +void TestCol(void) +{ + GFX_clear(BLACK); + + GFX_frect(0,0,SCRW,10,WHITE); + GFX_print(0,10,WHITE,"WHITE"); + + GFX_frect(0,20,SCRW,10,RED); + GFX_print(0,30,RED,"RED"); + + GFX_frect(0,40,SCRW,10,BLUE); + GFX_print(0,50,BLUE,"BLUE"); + + GFX_frect(0,60,SCRW,10,GREEN); + GFX_print(0,70,GREEN,"GREEN"); + + GFX_frect(0,80,SCRW,10,GREY(128)); + GFX_print(0,90,GREY(128),"GREY(128)"); + + GFX_redraw(); + GFX_waitkey(NULL); +} + +static void TestDraw(void) +{ + int f; + + GFX_clear(BLACK); + + for(f=0;f<100;f++) + GFX_plot(f,10,WHITE); + GFX_print(0,22,WHITE,"GFX_plot()"); + + GFX_line(0,40,99,40,WHITE); + GFX_print(0,42,WHITE,"GFX_line()"); + + GFX_rect(0,60,100,10,WHITE); + GFX_print(0,70,WHITE,"GFX_rect()"); + + GFX_frect(0,80,100,10,WHITE); + GFX_print(0,90,WHITE,"GFX_frect()"); + + GFX_circle(50,150,50,WHITE); + GFX_print(50,150,WHITE,"GFX_circle()"); + + GFX_fcircle(50,250,50,GREEN); + GFX_print(50,250,WHITE,"GFX_fircle()"); + + for(f=101;f>=99;f--) + { + GFX_line(f,0,f,100,RED); + GFX_redraw(); + GFX_waitkey(NULL); + } +} + + +static void TestBm(void) +{ + static unsigned char data[64]= + { + 0,0,0,0,0,0,0,0, + 0,1,1,1,1,1,1,0, + 0,1,2,2,0,0,1,0, + 0,1,2,2,0,0,1,1, + 0,1,0,0,2,2,1,1, + 0,1,0,0,2,2,1,0, + 0,1,1,1,1,1,1,0, + 0,0,0,1,1,0,0,0, + }; + GFX_IMAGE i; + GFX_BITMAP bm; + int f; + + bm.w=8; + bm.h=8; + bm.pal[0]=BLACK; + bm.pal[1]=RED; + bm.pal[2]=WHITE; + bm.data=data; + + i=GFX_create_image(&bm); + GFX_clear(GREEN); + + for(f=0;f<100;f++) + { + GFX_draw_image(i,f,f); + GFX_redraw(); + } + + GFX_waitkey(NULL); + GFX_fill_screen(i); + GFX_redraw(); + GFX_waitkey(NULL); + + GFX_destroy_image(i); +} + + +void TestKey(void) +{ + GFXEvent e; + int q; + + q=FALSE; + + GFX_clear(BLACK); + + while(!q) + { + GFX_print(0,0,WHITE,"Press key or mouse button to quit"); + GFX_redraw(); + GFX_clear(BLACK); + GFX_await_input(&e); + + switch(e.type) + { + case GFX_KEY_EVENT: + GFX_print(0,100,WHITE,".code %d",e.key.code); + GFX_print(0,100+GFX_fh()*2,WHITE,".ascii %d (%c)",e.key.ascii, + isprint(e.key.ascii) ? e.key.ascii : '?'); + + GFX_print(0,100+GFX_fh()*4,WHITE,".modif %c%c%c", + e.key.shift ? 'S':' ', + e.key.ctrl ? 'C':' ', + e.key.alt ? 'A':' '); + break; + + case GFX_MOUSE_EVENT: + q=TRUE; + break; + } + } +} + + +void TestGui(void) +{ + static PLAT_PICKLIST cpl[]= + { + {"CPL 1",1}, + {"CPL 2",2}, + {"CPL 23",23}, + {"CPL 10",10}, + {"CPL 0",0}, + {NULL,0}, + }; + + static char *pl_short[]= + { + "Picklist entry 0", + "Picklist entry 1", "Picklist entry 2", "Picklist entry 3", + NULL + }; + + static char *pl_d_short[]= + { + "Picklist entry 0", "Picklist entry 1", + NULL + }; + + static char *pl[]= + { + "Picklist entry 0", + "Picklist entry 1", "Picklist entry 2", "Picklist entry 3", + "Picklist entry 4", "Picklist entry 5", "Picklist entry 6", + "Picklist entry 7", "Picklist entry 8", "Picklist entry 9", + "Picklist entry 10", "Picklist entry 11", "Picklist entry 12", + "Picklist entry 13", "Picklist entry 14", "Picklist entry 15", + "Picklist entry 16", "Picklist entry 17", "Picklist entry 18", + "Picklist entry 19", "Picklist entry 20", "Picklist entry 21", + "Picklist entry 22", "Picklist entry 23", "Picklist entry 24", + "Picklist entry 25", "Picklist entry 26", "Picklist entry 27", + "Picklist entry 28", "Picklist entry 29", "Picklist entry 30", + "Picklist entry 31", "Picklist entry 32", "Picklist entry 33", + "Picklist entry 34", "Picklist entry 35", "Picklist entry 36", + "Picklist entry 37", "Picklist entry 38", "Picklist entry 39", + "Picklist entry 40", "Picklist entry 41", "Picklist entry 42", + "Picklist entry 43", "Picklist entry 44", "Picklist entry 45", + "Picklist entry 46", "Picklist entry 47", "Picklist entry 48", + "Picklist entry 49", "Picklist entry 50", "Picklist entry 51", + "Picklist entry 52", "Picklist entry 53", "Picklist entry 54", + "Picklist entry 55", "Picklist entry 56", "Picklist entry 57", + "Picklist entry 58", "Picklist entry 59", "Picklist entry 60", + "Picklist entry 61", "Picklist entry 62", "Picklist entry 63", + "Picklist entry 64", "Picklist entry 65", "Picklist entry 66", + "Picklist entry 67", "Picklist entry 68", "Picklist entry 69", + "Picklist entry 70", "Picklist entry 71", "Picklist entry 72", + "Picklist entry 73", "Picklist entry 74", "Picklist entry 75", + "Picklist entry 76", "Picklist entry 77", "Picklist entry 78", + "Picklist entry 79", "Picklist entry 80", "Picklist entry 81", + "Picklist entry 82", "Picklist entry 83", "Picklist entry 84", + "Picklist entry 85", "Picklist entry 86", "Picklist entry 87", + "Picklist entry 88", "Picklist entry 89", "Picklist entry 90", + "Picklist entry 91", "Picklist entry 92", "Picklist entry 93", + "Picklist entry 94", "Picklist entry 95", "Picklist entry 96", + "Picklist entry 97", "Picklist entry 98", "Picklist entry 99", + NULL + }; + + static PLAT_IMG_PICKLIST ipl[]= + { + {"IPL 1 (BLUE)",NULL,1}, + {"IPL 2",NULL,2}, + {"IPL 23 (GREEN)",NULL,23}, + {"IPL 10",NULL,10}, + {"IPL 0 (RED)",NULL,0}, + {NULL,NULL,0}, + }; + + static PLAT_MENU ch2[]= { + {"Opt3",3,NULL}, + {"Opt4",4,NULL}, + {NULL,0,NULL}, + }; + + static PLAT_MENU ch1[]= { + {"Opt1",1,NULL}, + {"Opt2",2,NULL}, + {"Child 2",0,ch2}, + {NULL,0,NULL}, + }; + + static char *pl_text[17]={"Opt 1","Opt 2","Opt 3","Opt 4","Opt 5", + "Opt 6","Opt 7","Opt 8","Opt 9","Opt 10", + "Opt 11","Opt 12","Opt 13","Opt 14","Opt 15", + "Opt 16", + "A very very very very long option"}; + + static PLAT_DIALOG dialog[6]={ + {"String",PLAT_DIAL_STRING,{0}}, + {"Integer",PLAT_DIAL_INTEGER,{0}}, + {"Double",PLAT_DIAL_DOUBLE,{0}}, + {"Picklist 1",PLAT_DIAL_PICKLIST,{0}}, + {"Picklist 2",PLAT_DIAL_PICKLIST,{0}}, + {"Picklist 3",PLAT_DIAL_PICKLIST,{0}}, + }; + + static PLAT_RADIO radio[]= { + {"Radio 0",0}, + {"Radio 2",2}, + {"Radio 4",4}, + {"Radio 8",8}, + {"Radio -33",-33}, + {NULL,0} + }; + + int cur_radio=4; + + static PLAT_MULTI multi[]= { + {"Opt 1",0,TRUE}, + {"Opt 2",0,FALSE}, + {"Opt 3",0,FALSE}, + {"Group 1.1",1,TRUE}, + {"Group 1.2",1,FALSE}, + {"Group 1.3",1,FALSE}, + {"Opt 4",0,FALSE}, + {"Group 2.1",2,FALSE}, + {"Group 2.2",2,TRUE}, + {"Opt 5",0,FALSE}, + {NULL,0,0} + }; + + PLAT_DIALOG work_dial[6]; + + static PLAT_MENU menu[]={ + {"Recursive",0,menu}, + {"Child 1",0,ch1}, + {"Test fsel and file_view",100,NULL}, + {"Test text edit",101,NULL}, + {"Test picklist",102,NULL}, + {"Test client data picklist",103,NULL}, + {"Test client data image picklist",104,NULL}, + {"Test DIALOG",105,NULL}, + {"Test GUI_alert()",106,NULL}, + {"Test GUI_yesno()",107,NULL}, + {"Test GUI_yesno_all()",108,NULL}, + {"Test GUI_radio_box()",109,NULL}, + {"Test GUI_multi_box()",110,NULL}, + {"Quit",999,NULL}, + {NULL,0,NULL}, + }; + static char *text=NULL; + + int opt; + char s[80]; + char *path; + char *new; + int ret; + + strcpy(dialog[0].data.s,"String"); + dialog[1].data.i=123; + dialog[2].data.d=1.23; + + dialog[3].data.pl.no=17; + dialog[3].data.pl.current=0; + dialog[3].data.pl.text=pl_text; + + dialog[4].data.pl.no=2; + dialog[4].data.pl.current=0; + dialog[4].data.pl.text=pl_d_short; + + dialog[5].data.pl.no=100; + dialog[5].data.pl.current=0; + dialog[5].data.pl.text=pl; + + opt=0; + + if (!text) + { + GFX_BITMAP bm; + int c; + int f; + + text=Strdup("Test text\nof two lines follwed by\n" + "a big line (nnA - nnP):\n" + "123456790A 123456790B 123456790C 123456790D " + "123456790E 123456790F 123456790G 123456790H " + "123456790I 123456790J 123456790K 123456790L " + "123456790M 123456790N 123456790O 123456790P <END>" + ); + bm.pal[0]=BLACK; + + c=0xff; + + for(f=0;f<3;f++) + { + int w,h; + + w=256-(f)*64; + h=256-(f)*64; + bm.w=w; + bm.h=h; + bm.pal[1]=c; + bm.data=Grab(w*h); + + memset(bm.data,1,w*h); + ipl[f*2].img=GFX_create_image(&bm); + Release(bm.data); + c=c<<8; + } + } + + while(opt!=999) + { + opt=GUI_menu("Menu",10,10,menu,-1); + + switch(opt) + { + case 100: + if (GUI_yesno("Use filter?")) + path=GUI_fsel("File selector","",".c"); + else + path=GUI_fsel("File selector","",NULL); + + if (path) + { + GUI_view_file(path,path); + Release(path); + } + + break; + + case 101: + new=GUI_text_edit("Test",text); + + if (new) + { + Release(text); + text=new; + } + break; + + case 102: + ret=GUI_picklist("Long Picklist",pl); + sprintf(s,"You chose option|%d",ret); + GUI_yesno(s); + + ret=GUI_picklist("Short Picklist",pl_short); + sprintf(s,"You chose option|%d",ret); + GUI_yesno(s); + break; + + case 103: + ret=GUI_client_picklist("Client Picklist",cpl,-1); + sprintf(s,"You chose option|%d",ret); + GUI_yesno(s); + break; + + case 104: + ret=GUI_image_picklist("Image Picklist",ipl,-1); + sprintf(s,"You chose option|%d",ret); + GUI_yesno(s); + break; + + case 105: + memcpy(&work_dial,&dialog,sizeof(dialog)); + + if (GUI_dialog("Dialog",6,work_dial)) + memcpy(&dialog,&work_dial,sizeof(dialog)); + break; + + case 106: + GUI_alert("This is a long title","Text1","OK"); + GUI_alert("T","Longer text 1|Longer text 2","OK"); + GUI_alert("T","Text1","Longer button text"); + break; + + case 107: + if (GUI_yesno("Pick")) + GUI_alert("Answer","YES","OK"); + else + GUI_alert("Answer","NO","OK"); + + if (GUI_yesno("With a much much much MUCH longer string|" + "and some multiple|lines")) + GUI_alert("Answer","YES","OK"); + else + GUI_alert("Answer","NO","OK"); + + break; + + case 108: + GUI_start_yesno_all(); + + if (GUI_yesno_all("Pick")) + GUI_alert("Pick","YES","OK"); + else + GUI_alert("Pick","NO","OK"); + + if (GUI_yesno_all("With a much much much MUCH longer string|" + "and some multiple|lines")) + GUI_alert("Long string","YES","OK"); + else + GUI_alert("Long string","NO","OK"); + + if (GUI_yesno_all("Pick 2")) + GUI_alert("Pick 2","YES","OK"); + else + GUI_alert("Pick 2","NO","OK"); + + if (GUI_yesno_all("Pick 3")) + GUI_alert("Pick 3","YES","OK"); + else + GUI_alert("Pick 3","NO","OK"); + + break; + + case 109: + ret=GUI_radio_box("Radio Box",radio,cur_radio,-666); + sprintf(s,"You chose option|%d",ret); + GUI_yesno(s); + cur_radio=ret; + break; + + case 110: + GUI_multi_box("Multi Box",multi); + break; + + case 999: + break; + + default: + sprintf(s,"You chose option|%d",opt); + GUI_alert("Other",s,"OK"); + break; + } + } +} + +#define PERF_COUNT 1000000 + +time_t PerfClr(time_t start) +{ + int f; + + for(f=0;f<PERF_COUNT;f++) + GFX_clear(BLACK); + + return time(NULL)-start; +} + + +time_t PerfRect(time_t start) +{ + int f; + + for(f=0;f<PERF_COUNT;f++) + GFX_rect(10,10,600,600,WHITE); + + return time(NULL)-start; +} + + +time_t PerfFRect(time_t start) +{ + int f; + + for(f=0;f<PERF_COUNT;f++) + GFX_frect(10,10,600,600,WHITE); + + return time(NULL)-start; +} + + +time_t PerfPlot(time_t start) +{ + int f; + + for(f=0;f<PERF_COUNT;f++) + GFX_plot(10,10,WHITE); + + return time(NULL)-start; +} + + +time_t PerfLine(time_t start) +{ + int f; + + for(f=0;f<PERF_COUNT;f++) + GFX_line(10,10,610,610,WHITE); + + return time(NULL)-start; +} + + +time_t PerfLRect(time_t start) +{ + int f; + + for(f=0;f<PERF_COUNT;f++) + { + GFX_line(10,10,610,610,WHITE); + GFX_line(610,610,610,610,WHITE); + GFX_line(610,610,10,610,WHITE); + GFX_line(10,610,10,10,WHITE); + } + + return time(NULL)-start; +} + + +time_t PerfCirc(time_t start) +{ + int f; + + for(f=0;f<PERF_COUNT;f++) + GFX_circle(10,10,600,WHITE); + + return time(NULL)-start; +} + + +void TestPerformance(void) +{ + static struct + { + char *t; + time_t (*func)(time_t); + } test[]= { + {"Clear screen",PerfClr}, + {"Rectangles",PerfRect}, + {"Filled rectangles",PerfFRect}, + {"Plot",PerfPlot}, + {"Line",PerfLine}, + {"Rectangles using GFX_line",PerfLRect}, + {"Circles",PerfCirc}, + {NULL,NULL} + }; + + PLAT_MENU pm[32]; + int quit; + time_t t; + int f; + + quit=FALSE; + + GFX_clear(BLACK); + GFX_print(0,0,RED,"Select:"); + + while(!quit) + { + f=0; + while(test[f].t) + { + pm[f].text=test[f].t; + pm[f].client_index=f; + pm[f].child=NULL; + f++; + } + + pm[f].text=NULL; + + GFX_redraw(); + + if ((f=GUI_menu("Performance",16,16,pm,-1))==-1) + quit=TRUE; + else + { + t=time(NULL); + while (time(NULL)==t); + + t=test[f].func(time(NULL)); + + GFX_clear(BLACK); + GFX_print(0,0,WHITE,"Test %s completed in %ld seconds",test[f].t,t); + GFX_redraw(); + } + } +} + + + + +int viDOOM(int argc,char *argv[]) +{ + static struct + { + char *t; + void (*func)(void); + } test[]= { + {"Test printing",TestPrint}, + {"Test mouse",TestMouse}, + {"Test rectangles",TestRect}, + {"Test screen dimensions",TestDim}, + {"Test colours",TestCol}, + {"Test drawing",TestDraw}, + {"Test bitmap",TestBm}, + {"Test keyboard",TestKey}, + {"Test GUI",TestGui}, + {"Test Performance",TestPerformance}, + {NULL,NULL} + }; + + PLAT_MENU pm[32]; + int quit; + int f; + + INI_Load("vidoom.ini"); + GFX_init(); + GFX_open(SCRW,SCRH); + GUI_setscreen(SCRW,SCRH); + + quit=FALSE; + + while(!quit) + { + GFX_clear(BLACK); + GFX_print(0,0,RED,"Select:"); + + f=0; + while(test[f].t) + { + pm[f].text=test[f].t; + pm[f].client_index=f; + pm[f].child=NULL; + f++; + } + + pm[f].text=NULL; + + GFX_redraw(); + + if ((f=GUI_menu("GFXTEST",16,16,pm,-1))==-1) + quit=TRUE; + else + test[f].func(); + } + + return(EXIT_SUCCESS); +} diff --git a/globals.c b/globals.c new file mode 100644 index 0000000..86a0add --- /dev/null +++ b/globals.c @@ -0,0 +1,1375 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Global data + + All the stuff from INI file and from the config files. Note this object + also drives other objects so that the list of things they maintain is + initialised. + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "ini.h" +#include "gfx.h" +#include "mem.h" +#include "texture.h" +#include "things.h" +#include "linedefs.h" +#include "sectors.h" +#include "genlines.h" +#include "gensect.h" +#include "specials.h" +#include "flags.h" + + +/* Constants +*/ +#define MAXLEN 1024 + +#define M_NONE -1 +#define M_THING_TYPES 0 +#define M_SECTOR_TYPES 1 +#define M_LINEDEF_FLAGS 2 +#define M_INCLUDE 3 +#define M_THING_FLAGS 4 +#define M_LINEDEF_TYPES 5 +#define M_LINEDEF_DEFAULTS 6 +#define M_SECTOR_STYLES 7 +#define M_LINEDEF_FLAGS_EXTRA 8 +#define M_EMPTY_TEXTURE_NAME 9 +#define M_THING_CLASSES 10 +#define M_LINEDEF_CLASSES 11 +#define M_SECTOR_CLASSES 12 +#define M_NORMAL_TYPES 13 +#define M_LINEDEF_CHECK_DEFAULT 14 +#define M_LINEDEF_GEN_BITMASKS 15 +#define M_LINEDEF_GEN_TYPES 16 +#define M_SECTOR_GEN_BITMASKS 17 +#define M_SECTOR_GEN_TYPES 18 +#define M_HEXEN_ACTION_CLASSES 19 +#define M_HEXEN_ACTION_SPECIALS 20 + +/* Masks for edit mode +*/ +#define DOOM_EDIT_MASK 0x01 +#define HEXEN_EDIT_MASK 0x02 + + +/* INI sections +*/ +#define GAME_INI "Game" +#define EDITOR_INI "Editor" +#define VIDOOM_INI "viDOOM" +#define CLINEDEF_INI "Check LINEDEF" +#define BUILD_INI "Node Builder" +#define ACC_INI "ACS" + +#define DOOM_INI "Doom" +#define ULT_DOOM_INI "Ultimate Doom" +#define DOOM2_INI "Doom 2" +#define FINAL_TNT_INI "TNT:Evilution" +#define FINAL_PLUT_INI "Plutonia Experiment" +#define ZDOOM_INI "ZDoom" + +/* ------------------------------ Game type +*/ +GameType game=DOOM_2; +LevelStyle level_style=DOOM_2_LEVELS; +int ask_for_game_type=FALSE; + + +/* ------------------------------ Paths +*/ +char IWAD_path[PATH_MAX+1]="." DIRSEP "DOOM2.WAD"; +char PWAD_dir[PATH_MAX+1]="." DIRSEP; +char PWAD_preload[MAX_PRELOAD_LEN]=""; +static char config_file[PATH_MAX+1]="doom2.cfg"; + + +/* ------------------------------ Editor configuration +*/ +int disp_width=640; +int disp_height=480; +int grid_onoff=TRUE; +int grid_lock=TRUE; +int grid_size=64; +double gfx_brighten=1.0; +int vertex_rad=5; +Hover hover_select=HOVER_NONE; +int clear_on_move=TRUE; +int clear_on_menu=FALSE; +Hover insert_select=HOVER_NONE; +SectorMoveMode sector_move=MOVE_RIGHT; +int ask_middle_on_2sided=TRUE; +int default_light_level=200; +int default_floor_height=0; +int default_ceiling_height=256; +NewSelect new_2sided_select=NEWSELECT_ASK; +LinedefMerge merge_linedef=MERGE_ASK; +int auto_block_linedefs=TRUE; +int default_scale=10; +DefaultEdit default_edit_mode=EDIT_SECTOR; +int left_click_move=FALSE; +int linedef_select=2; +int tag_highlight=TRUE; +int show_full_linedef_info=FALSE; + + +/* ------------------------------ viDOOM configuration +*/ +char auto_loadmap[PATH_MAX+1]=""; +int sort_textures=TRUE; +int sort_flats=TRUE; +int show_titlepic=TRUE; +int load_textures=TRUE; +int load_flats=TRUE; +int load_sprites=TRUE; +int map_warn=TRUE; +int map_exit_warn=TRUE; +int mapinfo_lump=FALSE; /* Only for ZDOOM/BOOM */ +GenericYNA create_hexen=GEN_NO; /* Only for ZDOOM/BOOM */ +int initial_empty_map=FALSE; +int overwrite_warning=TRUE; + +/* ------------------------------ Linedef flags bit and mask +*/ +int side2_bit=-1; +int side2_mask; +int block_bit=-1; +int block_mask; +int lower_peg_bit=-1; +int lower_peg_mask; +int upper_peg_bit=-1; +int upper_peg_mask; + +/* ------------------------------ Normal values +*/ +int normal_linedef=0; +int normal_sector=0; + +/* ------------------------------ LINEDEF checking +*/ +int check_line_assume_yes=FALSE; +int check_1side_lower=TRUE; +int check_1side_middle=TRUE; +int check_1side_upper=TRUE; +int check_2side_lower=TRUE; +int check_2side_middle=TRUE; +int check_2side_same_sector=TRUE; +int check_2side_upper=TRUE; + +/* ------------------------------ Node Builder config +*/ +int use_build=FALSE; +char build_cmd[PATH_MAX+1]="bsp.exe"; +char build_ignore[PATH_MAX+1]=""; +char build_in[PATH_MAX+1]="%"; +char build_out[PATH_MAX+1]="%"; +int build_in_before_out=TRUE; +int build_always_view=TRUE; + + +/* ------------------------------ ACC config +*/ +char acc_cmd[PATH_MAX+1]="acc.exe"; +char acc_dir[PATH_MAX+1]="."; +char acc_script[PATH_MAX+1]="vidoom.acc"; +char acc_object[PATH_MAX+1]="vidoom.o"; +char acc_args[PATH_MAX+1]="%S %O"; +int acc_always_view=TRUE; + + +/* ------------------------------ Texture names +*/ +char empty_texture[256]="-"; +char linedef_check_default[256]="-"; + + +/* ------------------------------ INI tables to data +*/ + +#define INI_FOR_GAME(g) { \ + INI_STR, g, "iwad", \ + IWAD_path, NULL \ + }, \ + { \ + INI_STR, g, "pwad_dir", \ + PWAD_dir, NULL \ + }, \ + { \ + INI_TOK, g, "level_style", \ + &level_style, level_style_tok \ + }, \ + { \ + INI_STR, g, "preload", \ + PWAD_preload, NULL \ + }, \ + { \ + INI_STR, g, "vidoom_config", \ + config_file, NULL \ + } + + +static TokenTable game_type_tok[]= + { + {"Doom",DOOM}, + {"Ultimate Doom",ULTIMATE_DOOM}, + {"Doom 2",DOOM_2}, + {"TNT:Evilution",FINAL_TNT}, + {"Plutonia Experiment",FINAL_PLUTONIA}, + {"ZDoom",ZDOOM}, + {NULL,0} + }; + +static TokenTable level_style_tok[]= + { + {"Doom",DOOM_LEVELS}, + {"Ultimate Doom",ULTIMATE_DOOM_LEVELS}, + {"Doom 2",DOOM_2_LEVELS}, + {NULL,0} + }; + +static TokenTable hover_type_tok[]= + { + {"none",HOVER_NONE}, + {"add",HOVER_ADD}, + {"single",HOVER_SINGLE}, + {NULL,0} + }; + +static TokenTable smove_type_tok[]= + { + {"all",MOVE_ALL}, + {"left",MOVE_LEFT}, + {"right",MOVE_RIGHT}, + {NULL,0} + }; + +static TokenTable new_select_tok[]= + { + {"never",NEWSELECT_NEVER}, + {"ask",NEWSELECT_ASK}, + {"select",NEWSELECT_SELECT}, + {NULL,0} + }; + +static TokenTable merge_mode_tok[]= + { + {"never",MERGE_NEVER}, + {"ask",MERGE_ASK}, + {"always",MERGE_ALWAYS}, + {NULL,0} + }; + +static TokenTable edit_mode_tok[]= + { + {"sector",EDIT_SECTOR}, + {"vertex",EDIT_VERTEX}, + {"linedef",EDIT_LINEDEF}, + {"thing",EDIT_THING}, + {"multi",EDIT_MULTI}, + {NULL,0} + }; + +static TokenTable YNA_tok[]= + { + {"no",GEN_NO}, + {"yes",GEN_YES}, + {"ask",GEN_ASK}, + {NULL,0} + }; + + +static INI_Table part1_ini[]= + { + /* Game type + */ + { + INI_TOK, GAME_INI, "game", + &game, game_type_tok + }, + { + INI_TOK, GAME_INI, "ask", + &ask_for_game_type, ini_yesno + }, + { + INI_INT, EDITOR_INI, "width", + &disp_width, NULL + }, + { + INI_INT, EDITOR_INI, "height", + &disp_height, NULL + }, + }; + +static INI_Table part2_ini[]= + { + /* Editor + */ + { + INI_TOK, EDITOR_INI, "grid", + &grid_onoff, ini_yesno + }, + { + INI_TOK, EDITOR_INI, "grid_lock", + &grid_lock, ini_yesno + }, + { + INI_INT, EDITOR_INI, "grid_size", + &grid_size, NULL + }, + { + INI_DOUBLE, EDITOR_INI, "bright", + &gfx_brighten, NULL + }, + { + INI_INT, EDITOR_INI, "vertex_radius", + &vertex_rad, NULL + }, + { + INI_TOK, EDITOR_INI, "hover_select", + &hover_select, hover_type_tok + }, + { + INI_TOK, EDITOR_INI, "clear_on_move", + &clear_on_move, ini_yesno + }, + { + INI_TOK, EDITOR_INI, "clear_on_menu", + &clear_on_menu, ini_yesno + }, + { + INI_TOK, EDITOR_INI, "insert_select", + &insert_select, hover_type_tok + }, + { + INI_TOK, EDITOR_INI, "sector_move", + §or_move, smove_type_tok + }, + { + INI_TOK, EDITOR_INI, "ask_middle_on_2sided", + &ask_middle_on_2sided, ini_yesno + }, + { + INI_INT, EDITOR_INI, "default_light_level", + &default_light_level, NULL + }, + { + INI_INT, EDITOR_INI, "default_floor_height", + &default_floor_height, NULL + }, + { + INI_INT,EDITOR_INI,"default_ceiling_height", + &default_ceiling_height, NULL + }, + { + INI_TOK, EDITOR_INI, "new_2sided_select", + &new_2sided_select, new_select_tok + }, + { + INI_TOK, EDITOR_INI, "merge_linedef", + &merge_linedef, merge_mode_tok + }, + { + INI_TOK, EDITOR_INI, "auto_block_linedefs", + &auto_block_linedefs, ini_yesno + }, + { + INI_INT, EDITOR_INI, "default_scale", + &default_scale, NULL + }, + { + INI_TOK, EDITOR_INI, "default_edit_mode", + &default_edit_mode, edit_mode_tok + }, + { + INI_TOK, EDITOR_INI, "left_click_move", + &left_click_move, ini_yesno + }, + { + INI_INT, EDITOR_INI, "linedef_select", + &linedef_select, NULL + }, + { + INI_TOK, EDITOR_INI,"tag_highlight", + &tag_highlight, ini_yesno + }, + { + INI_TOK,EDITOR_INI,"show_full_linedef_info", + &show_full_linedef_info, ini_yesno + }, + + /* viDOOM + */ + { + INI_TOK, VIDOOM_INI, "sort_texture_names", + &sort_textures, ini_yesno + }, + { + INI_TOK, VIDOOM_INI, "sort_flat_names", + &sort_flats, ini_yesno + }, + { + INI_TOK, VIDOOM_INI, "show_titlepic", + &show_titlepic, ini_yesno + }, + { + INI_TOK, VIDOOM_INI, "load_textures", + &load_textures, ini_yesno + }, + { + INI_TOK, VIDOOM_INI, "load_flats", + &load_flats, ini_yesno + }, + { + INI_TOK, VIDOOM_INI, "load_sprites", + &load_sprites, ini_yesno + }, + { + INI_TOK, VIDOOM_INI, "map_clear_warning", + &map_warn, ini_yesno + }, + { + INI_TOK, VIDOOM_INI, "map_exit_warning", + &map_exit_warn, ini_yesno + }, + { + INI_TOK, VIDOOM_INI, "initial_empty_map", + &initial_empty_map, ini_yesno + }, + { + INI_TOK, VIDOOM_INI, "overwrite_warning", + &overwrite_warning, ini_yesno + }, + { + INI_STR, VIDOOM_INI, "auto_loadmap", + auto_loadmap, NULL + }, + + /* Check LINEDEF + */ + { + INI_TOK, CLINEDEF_INI, "assume_yes", + &check_line_assume_yes, ini_yesno + }, + { + INI_TOK, CLINEDEF_INI, "check_1side_lower", + &check_1side_lower, ini_yesno + }, + { + INI_TOK, CLINEDEF_INI, "check_1side_middle", + &check_1side_middle, ini_yesno + }, + { + INI_TOK, CLINEDEF_INI, "check_1side_upper", + &check_1side_upper, ini_yesno + }, + { + INI_TOK, CLINEDEF_INI, "check_2side_lower", + &check_2side_lower, ini_yesno + }, + { + INI_TOK, CLINEDEF_INI, "check_2side_middle", + &check_2side_middle, ini_yesno + }, + { + INI_TOK, CLINEDEF_INI, + "check_2side_same_sector", + &check_2side_same_sector, ini_yesno + }, + { + INI_TOK, CLINEDEF_INI, "check_2side_upper", + &check_2side_upper, ini_yesno + }, + + /* Node builder + */ + { + INI_TOK, BUILD_INI, "use", + &use_build, ini_yesno + }, + { + INI_STR, BUILD_INI, "command", + build_cmd, NULL + }, + { + INI_STR, BUILD_INI, "ignore", + build_ignore, NULL + }, + { + INI_STR, BUILD_INI, "infile", + build_in, NULL + }, + { + INI_STR, BUILD_INI, "outfile", + build_out, NULL + }, + { + INI_TOK, BUILD_INI, "in_before_out", + &build_in_before_out, ini_yesno + }, + { + INI_TOK, BUILD_INI, "always_view_output", + &build_always_view, ini_yesno + }, + + /* ACC + */ + { + INI_STR, ACC_INI, "command", + acc_cmd, NULL + }, + { + INI_STR, ACC_INI, "dir", + acc_dir, NULL + }, + { + INI_STR, ACC_INI, "script", + acc_script, NULL + }, + { + INI_STR, ACC_INI, "object", + acc_object, NULL + }, + { + INI_STR, ACC_INI, "args", + acc_args, NULL + }, + { + INI_TOK, ACC_INI, "always_view_output", + &acc_always_view, ini_yesno + }, + }; + +static INI_Table doom_ini[]={INI_FOR_GAME(DOOM_INI)}; +static INI_Table ult_doom_ini[]={INI_FOR_GAME(ULT_DOOM_INI)}; +static INI_Table doom_2_ini[]={INI_FOR_GAME(DOOM2_INI)}; +static INI_Table tnt_ini[]={INI_FOR_GAME(FINAL_TNT_INI)}; +static INI_Table plut_ini[]={INI_FOR_GAME(FINAL_PLUT_INI)}; + +static INI_Table zdoom_ini[]= + { + INI_FOR_GAME(ZDOOM_INI), + { + INI_TOK, ZDOOM_INI, "mapinfo_lump", + &mapinfo_lump, ini_yesno + }, + { + INI_TOK, ZDOOM_INI, "create_hexen", + &create_hexen, YNA_tok + } + }; + + + +/* ------------------------------ TYPES +*/ +typedef struct + { + char *str; + int val; + } StrList; + + +/* ------------------------------ PRIVATE FUNCTIONS +*/ + +static void CheckVals(void) +{ + FILE *fp; + + grid_size=MAX(grid_size,2); + default_light_level=default_light_level%256; + linedef_select=MAX(linedef_select,1); + + /* Check config file exists + */ + if (!(fp=fopen(config_file,"r"))) + GFX_exit(EXIT_FAILURE,"Couldn't open config file %s\n",config_file); + + fclose(fp); +} + + +static int StrToInt(StrList s[],char *val,int def) +{ + int f; + int m; + + f=0; + m=def; + + while((s[f].str)&&(m==def)) + { + if (STREQ(s[f].str,val)) + m=s[f].val; + f++; + } + + return(m); +} + + +static char *GetLine(FILE *fp) +{ + static char s[MAXLEN+1]; + int l,f; + + fgets(s,MAXLEN,fp); + + if (feof(fp)) + return(FALSE); + + l=strlen(s)-1; + + if ((l>=0)&&(s[l]=='\n')) + s[l]=0; + + l=strlen(s)-1; + + if ((l>=0)&&(s[l]=='\r')) + s[l]=0; + + l=strlen(s); + for(f=0;f<l;f++) + if (!isspace(s[f])) + { + if (s[f]=='#') + return(GetLine(fp)); + else + return(s+f); + } + + return(GetLine(fp)); +} + + +static int EditMode(char *p, int *m) +{ + char *pp; + *m=0; + + pp=strtok(p,","); + + while(pp) + { + if (STREQ(pp,"Doom")) + *m|=DOOM_EDIT_MASK; + else if (STREQ(pp,"Hexen")) + *m|=HEXEN_EDIT_MASK; + else + return(FALSE); + + pp=strtok(NULL,","); + } + + return(TRUE); +} + + +static void DoLoadConfig(char *fn) +{ + static StrList mode_list[]={{"%THING_TYPES",M_THING_TYPES}, + {"%SECTOR_TYPES",M_SECTOR_TYPES}, + {"%LINEDEF_TYPES",M_LINEDEF_TYPES}, + {"%INCLUDE_FILES",M_INCLUDE}, + {"%THING_FLAGS",M_THING_FLAGS}, + {"%LINEDEF_FLAGS",M_LINEDEF_FLAGS}, + {"%LINEDEF_DEFAULTS",M_LINEDEF_DEFAULTS}, + {"%SECTOR_STYLES",M_SECTOR_STYLES}, + {"%LINEDEF_FLAGS_EXTRA",M_LINEDEF_FLAGS_EXTRA}, + {"%EMPTY_TEXTURE_NAME",M_EMPTY_TEXTURE_NAME}, + {"%THING_CLASSES",M_THING_CLASSES}, + {"%LINEDEF_CLASSES",M_LINEDEF_CLASSES}, + {"%SECTOR_CLASSES",M_SECTOR_CLASSES}, + {"%NORMAL_TYPES",M_NORMAL_TYPES}, + {"%LINEDEF_CHECK_DEFAULT", + M_LINEDEF_CHECK_DEFAULT}, + {"%LINEDEF_GEN_BITMASKS", + M_LINEDEF_GEN_BITMASKS}, + {"%LINEDEF_GEN_TYPES",M_LINEDEF_GEN_TYPES}, + {"%SECTOR_GEN_BITMASKS", + M_SECTOR_GEN_BITMASKS}, + {"%SECTOR_GEN_TYPES",M_SECTOR_GEN_TYPES}, + {"%HEXEN_ACTION_SPECIAL_CLASSES", + M_HEXEN_ACTION_CLASSES}, + {"%HEXEN_ACTION_SPECIALS", + M_HEXEN_ACTION_SPECIALS}, + {NULL,0}}; + + FILE *fp; + char *line; + char orig[MAXLEN+1]; + int mode; + char *err; + int skip_cmd; + char *curr_cmd; + char curr_class[MAXLEN+1]; + int no; + int game_mode; + + mode=M_NONE; + + if (!(fp=fopen(fn,"r"))) + GFX_exit(EXIT_FAILURE,"Couldn't open config file %s\n",fn); + + skip_cmd=FALSE; + curr_cmd=NULL; + no=0; + + while((line=GetLine(fp))) + { + strcpy(orig,line); + err=NULL; + + /* Check for commands first + */ + if (*line=='@') + { + if (STRNEQ("END",line+1)) + { + char *p; + + if (skip_cmd) + { + p=strtok(line," \t"); + + if (p) + p=strtok(NULL," \t"); + + if (p) + { + if (STREQ(curr_cmd,p)) + { + Release(curr_cmd); + curr_cmd=NULL; + skip_cmd=FALSE; + } + } + else + err="Missing argument to @END"; + } + } + else + { + int do_skip; + + do_skip=FALSE; + + if (STREQ(line+1,"DOOM_LEVEL_STYLE")) + { + do_skip=(level_style!=DOOM_LEVELS)&& + (level_style!=ULTIMATE_DOOM_LEVELS); + } + else if (STREQ(line+1,"DOOM_1_LEVEL_STYLE")) + { + do_skip=(level_style!=DOOM_LEVELS); + } + else if (STREQ(line+1,"ULT_DOOM_LEVEL_STYLE")) + { + do_skip=(level_style!=ULTIMATE_DOOM_LEVELS); + } + else if (STREQ(line+1,"DOOM_2_LEVEL_STYLE")) + { + do_skip=(level_style!=DOOM_2_LEVELS); + } + else + err="Unknown directive"; + + if (do_skip) + { + curr_cmd=Strdup(line+1); + skip_cmd=TRUE; + } + } + } + + /* Skip lines if requested by the directive + */ + if (skip_cmd) + continue; + + if (*line=='%') + { + mode=StrToInt(mode_list,line,M_NONE); + + /* Clear classes and counts for generalised settings + */ + strcpy(curr_class,""); + no=0; + } + else if (*line!='@') + switch(mode) + { + case M_THING_CLASSES: + { + char *p[2]; + + p[0]=strtok(line,"|"); + + if (p[0]) + p[1]=strtok(NULL,"|"); + + if (p[1]) + ThingNewClass(p[0],ATOI(p[1])); + else + err="THING_CLASSES"; + + break; + } + + case M_LINEDEF_CLASSES: + LinedefNewClass(line); + break; + + case M_SECTOR_CLASSES: + { + char *p[2]; + + p[0]=strtok(line,"|"); + + if (p[0]) + p[1]=strtok(NULL,"|"); + + if ((p[1])&&(EditMode(p[0],&game_mode))) + { + if (game_mode&DOOM_EDIT_MASK) + SectorNewClass(FALSE,p[1]); + + if (game_mode&HEXEN_EDIT_MASK) + SectorNewClass(TRUE,p[1]); + } + else + err="SECTOR_CLASSES"; + + break; + } + + case M_SECTOR_TYPES: + { + char *p[5]; + int f; + + p[0]=strtok(line,"|"); + + for(f=1;(f<5)&&(p[f-1]);f++) + p[f]=strtok(NULL,"|"); + + if ((p[4])&&(EditMode(p[0],&game_mode))) + { + if (game_mode&DOOM_EDIT_MASK) + SectorAdd(FALSE,p[1],ATOI(p[2]),p[3],p[4]); + + if (game_mode&HEXEN_EDIT_MASK) + SectorAdd(TRUE,p[1],ATOI(p[2]),p[3],p[4]); + } + else + err="SECTOR_TYPES"; + + break; + } + + case M_THING_TYPES: + { + char *p[5]; + int f; + + p[0]=strtok(line,"|"); + + for(f=1;(f<5)&&(p[f-1]);f++) + p[f]=strtok(NULL,"|"); + + if (p[4]) + ThingAdd(p[0],p[1],ATOI(p[2]),ATOI(p[3]), + (load_sprites ? DecodeGraphicsLump(p[4]) : + NULL)); + else + err="THING_TYPES"; + + break; + } + + case M_LINEDEF_TYPES: + { + char *p[3]; + + p[0]=strtok(line,"|"); + + if (p[0]) + p[1]=strtok(NULL,"|"); + + if (p[1]) + p[2]=strtok(NULL,"|"); + + if (p[2]) + LinedefAdd(p[0],p[2],ATOI(p[1])); + else + err="LINEDEF_TYPES"; + + break; + } + + case M_LINEDEF_FLAGS: + case M_THING_FLAGS: + { + char *p[6]; + int f; + int class; + int group; + + p[0]=strtok(line,"|"); + + for(f=1;(f<6)&&(p[f-1]);f++) + p[f]=strtok(NULL,"|"); + + if (mode==M_LINEDEF_FLAGS) + class=LINEDEF_FLAGS; + else + class=THING_FLAGS; + + if ((p[5])&&(EditMode(p[0],&game_mode))) + { + group=ATOI(p[1]); + + if (game_mode&DOOM_EDIT_MASK) + FlagAdd(FALSE,class,group, + p[4],ATOI(p[2]),ATOI(p[3]),*p[5]); + + if (game_mode&HEXEN_EDIT_MASK) + FlagAdd(TRUE,class,group, + p[4],ATOI(p[2]),ATOI(p[3]),*p[5]); + } + else + if (mode==M_LINEDEF_FLAGS) + err="LINEDEF_FLAGS"; + else + err="THING_FLAGS"; + + break; + } + + case M_INCLUDE: + { + char s[PATH_MAX]; + + strcpy(s,line); + DoLoadConfig(s); + break; + } + + case M_LINEDEF_DEFAULTS: + { + int flags; + char *p[2]; + + if (side2_bit==-1) + GFX_exit + (EXIT_FAILURE, + "LINEDEF_DEFAULTS appeared before " + "LINEDEF_FLAGS_EXTRA\n"); + + p[0]=strtok(line,"|"); + + if (p[0]) + p[1]=strtok(NULL,"|"); + + if (p[1]) + { + flags=ATOI(p[1]); + AddLinedefType(p[0],flags,flags&side2_mask); + } + else + err="LINEDEF_DEFAULTS"; + + break; + } + + case M_SECTOR_STYLES: + { + char *p[7]; + int f; + + p[0]=strtok(line,"|"); + + for(f=1;(f<7)&&(p[f-1]);f++) + p[f]=strtok(NULL,"|"); + + if (p[6]) + AddSectorStyle(p[1],ATOI(p[0]),p[2],p[3], + p[4],p[5],p[6]); + else + err="SECTOR_STYLES"; + + break; + } + + case M_LINEDEF_FLAGS_EXTRA: + { + char *p[4]; + int f; + + p[0]=strtok(line,"|"); + + for(f=1;(f<4)&&(p[f-1]);f++) + p[f]=strtok(NULL,"|"); + + if (p[3]) + { + side2_bit=ATOI(p[0]); + side2_mask=1<<side2_bit; + + block_bit=ATOI(p[1]); + block_mask=1<<block_bit; + + upper_peg_bit=ATOI(p[2]); + upper_peg_mask=1<<upper_peg_bit; + + lower_peg_bit=ATOI(p[3]); + lower_peg_mask=1<<lower_peg_bit; + } + else + err="LINEDEF_FLAGS_EXTRA"; + + break; + } + + case M_EMPTY_TEXTURE_NAME: + strncpy(empty_texture,line,sizeof(empty_texture)-1); + empty_texture[sizeof(empty_texture)-1]=0; + break; + + case M_NORMAL_TYPES: + { + char *p[2]; + + p[0]=strtok(line,"|"); + + if (p[0]) + p[1]=strtok(NULL,"|"); + + if (p[1]) + { + normal_sector=ATOI(p[0]); + normal_linedef=ATOI(p[1]); + } + else + err="NORMAL_TYPES"; + + break; + } + + case M_LINEDEF_CHECK_DEFAULT: + strncpy(linedef_check_default,line, + sizeof(linedef_check_default)-1); + linedef_check_default[sizeof(linedef_check_default)-1]=0; + break; + + case M_LINEDEF_GEN_BITMASKS: + case M_SECTOR_GEN_BITMASKS: + if (no) + { + char *p[3]; + + p[0]=strtok(line,"|"); + + if (p[0]) + { + p[1]=strtok(NULL,"|"); + + if (p[1]) + p[2]=strtok(NULL,"|"); + } + + if (p[2]) + { + if (mode==M_LINEDEF_GEN_BITMASKS) + GenLineAddBitmask + (curr_class,p[0],p[1],ATOI(p[2])); + else + GenSectAddBitmask + (curr_class,p[0],p[1],ATOI(p[2])); + } + else + { + if (mode==M_LINEDEF_GEN_BITMASKS) + err="LINEDEF_GEN_BITMASKS (Bitmask definition)"; + else + err="SECTOR_GEN_BITMASKS (Bitmask definition)"; + } + + no--; + } + else + { + char *p[2]; + + p[0]=strtok(line,"|"); + + if (p[0]) + p[1]=strtok(NULL,"|"); + + if (p[1]) + { + strcpy(curr_class,p[0]); + no=ATOI(p[1]); + + if (mode==M_LINEDEF_GEN_BITMASKS) + GenLineNewBitClass(curr_class,no); + else + GenSectNewBitClass(curr_class,no); + } + else + if (mode==M_LINEDEF_GEN_BITMASKS) + err="LINEDEF_GEN_BITMASKS (Class definition)"; + else + err="SECTOR_GEN_BITMASKS (Class definition)"; + } + break; + + case M_LINEDEF_GEN_TYPES: + case M_SECTOR_GEN_TYPES: + if (no) + { + char *p[2]; + + p[0]=strtok(line,"|"); + + if (p[0]) + p[1]=strtok(NULL,"|"); + + if (p[1]) + { + if (mode==M_LINEDEF_GEN_TYPES) + { + if (game_mode&DOOM_EDIT_MASK) + GenLineAdd(FALSE,curr_class,p[0], + ATOI(p[1])); + + if (game_mode&HEXEN_EDIT_MASK) + GenLineAdd(TRUE,curr_class,p[0],ATOI(p[1])); + } + else + { + if (game_mode&DOOM_EDIT_MASK) + GenSectAdd(FALSE,curr_class,p[0], + ATOI(p[1])); + + if (game_mode&HEXEN_EDIT_MASK) + GenSectAdd(TRUE,curr_class,p[0],ATOI(p[1])); + } + } + else + if (mode==M_LINEDEF_GEN_TYPES) + err="LINEDEF_GEN_TYPES (Field definition)"; + else + err="SECTOR_GEN_TYPES (Field definition)"; + + no--; + } + else + { + char *p[6]; + int f; + + p[0]=strtok(line,"|"); + + for(f=1;(f<6)&&(p[f-1]);f++) + p[f]=strtok(NULL,"|"); + + if ((p[5])&&(EditMode(p[0],&game_mode))) + { + strcpy(curr_class,p[1]); + no=ATOI(p[5]); + + if (mode==M_LINEDEF_GEN_TYPES) + { + if (game_mode&DOOM_EDIT_MASK) + GenLineNewClass + (FALSE,curr_class,ATOI(p[2]), + ATOI(p[3]),ATOI(p[4]),no); + + if (game_mode&HEXEN_EDIT_MASK) + GenLineNewClass + (TRUE,curr_class,ATOI(p[2]), + ATOI(p[3]),ATOI(p[4]),no); + } + else + { + if (game_mode&DOOM_EDIT_MASK) + GenSectNewClass + (FALSE,curr_class,ATOI(p[2]), + ATOI(p[3]),ATOI(p[4]),no); + + if (game_mode&HEXEN_EDIT_MASK) + GenSectNewClass + (TRUE,curr_class,ATOI(p[2]), + ATOI(p[3]),ATOI(p[4]),no); + } + } + else + if (mode==M_LINEDEF_GEN_TYPES) + err="LINEDEF_GEN_TYPES (Class definition)"; + else + err="SECTOR_GEN_TYPES (Class definition)"; + } + break; + + case M_HEXEN_ACTION_CLASSES: + SpecialNewClass(line); + break; + + case M_HEXEN_ACTION_SPECIALS: + { + char *p[4]; + char *a[5]; + int f; + + p[0]=strtok(line,"|"); + + for(f=1;(f<4)&&(p[f-1]);f++) + p[f]=strtok(NULL,"|"); + + if (p[2]) + { + for(f=0;f<5;f++) + a[f]=NULL; + + if (p[3]) + { + a[0]=strtok(p[3],","); + + for(f=1;(f<5)&&(a[f-1]);f++) + a[f]=strtok(NULL,","); + } + + SpecialAdd(p[0],p[2],ATOI(p[1]),a); + } + else + err="HEXEN_ACTION_SPECIALS"; + + break; + } + + case M_NONE: + break; + } + + if (err) + GFX_exit + (EXIT_FAILURE, + "%s: section %s\nError in line '%s'\n",fn,err,orig); + } + + if (curr_cmd) + Release(curr_cmd); + + fclose(fp); +} + + +/* ------------------------------ EXPORTED FUNCTIONS +*/ + +void LoadGlobalsPart1(void) +{ + INI_Load("vidoom.ini"); + INI_GetTable(part1_ini,INI_TAB_SIZE(part1_ini)); +} + + +void LoadGlobalsPart2(void) +{ + INI_GetTable(part2_ini,INI_TAB_SIZE(part2_ini)); + + switch(game) + { + case DOOM: + INI_GetTable(doom_ini,INI_TAB_SIZE(doom_ini)); + break; + case ULTIMATE_DOOM: + INI_GetTable(ult_doom_ini,INI_TAB_SIZE(ult_doom_ini)); + break; + case DOOM_2: + INI_GetTable(doom_2_ini,INI_TAB_SIZE(doom_2_ini)); + break; + case FINAL_TNT: + INI_GetTable(tnt_ini,INI_TAB_SIZE(tnt_ini)); + break; + case FINAL_PLUTONIA: + INI_GetTable(plut_ini,INI_TAB_SIZE(plut_ini)); + break; + case ZDOOM: + INI_GetTable(zdoom_ini,INI_TAB_SIZE(zdoom_ini)); + break; + } + + CheckVals(); +} + + +void SaveGlobals(void) +{ + INI_PutTable(part1_ini,INI_TAB_SIZE(part1_ini)); + INI_PutTable(part2_ini,INI_TAB_SIZE(part2_ini)); + + switch(game) + { + case DOOM: + INI_PutTable(doom_ini,INI_TAB_SIZE(doom_ini)); + break; + case ULTIMATE_DOOM: + INI_PutTable(ult_doom_ini,INI_TAB_SIZE(ult_doom_ini)); + break; + case DOOM_2: + INI_PutTable(doom_2_ini,INI_TAB_SIZE(doom_2_ini)); + break; + case FINAL_TNT: + INI_PutTable(tnt_ini,INI_TAB_SIZE(tnt_ini)); + break; + case FINAL_PLUTONIA: + INI_PutTable(plut_ini,INI_TAB_SIZE(plut_ini)); + break; + case ZDOOM: + INI_PutTable(zdoom_ini,INI_TAB_SIZE(zdoom_ini)); + break; + } + + INI_Save(); +} + + +void LoadConfig(void) +{ + DoLoadConfig(config_file); +} + + +/* END OF FILE */ + diff --git a/globals.h b/globals.h new file mode 100644 index 0000000..d3ac7f4 --- /dev/null +++ b/globals.h @@ -0,0 +1,232 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Global data - just about all from INI file + + $Id$ + +*/ + +#ifndef VIDOOM_GLOBALS_H + +#define VIDOOM_GLOBALS_H + + +#define MAX_PRELOAD_LEN 4096 + +/* ------------------------------ Game type +*/ + +typedef enum game_type { + DOOM, + ULTIMATE_DOOM, + DOOM_2, + FINAL_TNT, + FINAL_PLUTONIA, + ZDOOM + } GameType; + +typedef enum lev_style { + DOOM_LEVELS, + ULTIMATE_DOOM_LEVELS, + DOOM_2_LEVELS + } LevelStyle; + +/* These are set by LoadGlobalsGameSection() +*/ +extern GameType game; +extern int ask_for_game_type; +extern int disp_width; +extern int disp_height; + + +/* This and everthing from here on is set by LoadGlobalsOtherSections() +*/ +extern LevelStyle level_style; + + +/* ------------------------------ Paths +*/ +extern char IWAD_path[]; +extern char PWAD_dir[]; +extern char PWAD_preload[]; + + +/* ------------------------------ Editor configuration +*/ +typedef enum hover { + HOVER_NONE, + HOVER_ADD, + HOVER_SINGLE + } Hover; + +typedef enum smovemode { + MOVE_ALL, + MOVE_RIGHT, + MOVE_LEFT + } SectorMoveMode; + +typedef enum newselect { + NEWSELECT_NEVER, + NEWSELECT_ASK, + NEWSELECT_SELECT + } NewSelect; + +typedef enum ldefmerge { + MERGE_ALWAYS, + MERGE_ASK, + MERGE_NEVER + } LinedefMerge; + +typedef enum defedit { + EDIT_SECTOR, + EDIT_VERTEX, + EDIT_LINEDEF, + EDIT_THING, + EDIT_MULTI + } DefaultEdit; + +typedef enum gen_yna { + GEN_NO, + GEN_YES, + GEN_ASK + } GenericYNA; + + +extern int grid_onoff; +extern int grid_lock; +extern int grid_size; +extern double gfx_brighten; +extern int vertex_rad; +extern Hover hover_select; +extern int clear_on_move; +extern int clear_on_menu; +extern Hover insert_select; +extern SectorMoveMode sector_move; +extern int ask_middle_on_2sided; +extern int default_light_level; +extern int default_floor_height; +extern int default_ceiling_height; +extern NewSelect new_2sided_select; +extern LinedefMerge merge_linedef; +extern int auto_block_linedefs; +extern int default_scale; +extern DefaultEdit default_edit_mode; +extern int left_click_move; +extern int linedef_select; +extern int tag_highlight; +extern int show_full_linedef_info; + + +/* ------------------------------ viDOOM configuration +*/ +extern char auto_loadmap[]; +extern int sort_textures; +extern int sort_flats; +extern int show_titlepic; +extern int load_textures; +extern int load_flats; +extern int load_sprites; +extern int map_warn; +extern int map_exit_warn; +extern int initial_empty_map; +extern int overwrite_warning; + +extern int mapinfo_lump; /* For ZDOOM/BOOM only */ +extern GenericYNA create_hexen; /* For ZDOOM/BOOM only */ + +/* ------------------------------ Required linedef flags bit and mask +*/ +extern int side2_bit; +extern int side2_mask; +extern int block_bit; +extern int block_mask; +extern int lower_peg_bit; +extern int lower_peg_mask; +extern int upper_peg_bit; +extern int upper_peg_mask; + + +/* ------------------------------ Normal values +*/ +extern int normal_linedef; +extern int normal_sector; + + +/* ------------------------------ LINEDEF checking +*/ +extern int check_line_assume_yes; +extern int check_1side_lower; +extern int check_1side_middle; +extern int check_1side_upper; +extern int check_2side_lower; +extern int check_2side_middle; +extern int check_2side_same_sector; +extern int check_2side_upper; + + +/* ------------------------------ Node Builder config +*/ +extern int use_build; +extern char build_cmd[]; +extern char build_ignore[]; +extern char build_in[]; +extern char build_out[]; +extern int build_in_before_out; +extern int build_always_view; + + +/* ------------------------------ ACC config +*/ +extern char acc_cmd[]; +extern char acc_dir[]; +extern char acc_script[]; +extern char acc_object[]; +extern char acc_args[]; +extern int acc_always_view; + + +/* ------------------------------ Texture names +*/ +extern char empty_texture[]; +extern char linedef_check_default[]; + +/* ------------------------------ Interfaces +*/ + +/* Save and load global data to INI file +*/ +void LoadGlobalsPart1(void); +void LoadGlobalsPart2(void); + +void SaveGlobals(void); + +/* Read config file. This must be done once the default IWAD and any patch + PWADs have been read. +*/ +void LoadConfig(void); + + +#endif + + +/* END OF FILE */ @@ -0,0 +1,337 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Generic GUI type routines + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> + +#include "gui.h" +#include "platgui.h" +#include "mem.h" +#include "ini.h" +#include "names.h" + +/* ---------------------------------------- VARS +*/ +#define GUI_INI "GUI" + +#define GUI_BORDER (fh/2) +#define GUI_BORDERFULL (GUI_BORDER*2) + +#define GUI_TITLEH (fh*2) +#define GUI_TITLEOFF (fh/2) +#define GUI_FOOTERH (fh*2) +#define GUI_FOOTEROFF (fh/2) + +#define GUI_BEVEL 2 + +#define MAXLINES 512 +#define MAXTEXT 4096 + +#define ANY_CLOSE "Press any key/mouse button to close" +#define ESC_CLOSE "Press ESC key to close" + +#define VIEW_LEN 70 + +int GUI_HI=GREY(210); +int GUI_MID=GREY(180); +int GUI_LO=GREY(140); +int GUI_TEXT=WHITE; +int GUI_TEXTSHADOW=BLACK; +int GUI_TEXTBOLD=BLACK; + +static INI_Table gui_ini[]= + { + {INI_INT,GUI_INI,"high",&GUI_HI,NULL}, + {INI_INT,GUI_INI,"mid",&GUI_MID,NULL}, + {INI_INT,GUI_INI,"low",&GUI_LO,NULL}, + {INI_INT,GUI_INI,"text",&GUI_TEXT,NULL}, + {INI_INT,GUI_INI,"shadow",&GUI_TEXTSHADOW,NULL}, + {INI_INT,GUI_INI,"bold",&GUI_TEXTBOLD,NULL} + }; + +static int SCRW; +static int SCRH; +static int fw; +static int fh; + +static char *doom_levels[]= + {"E1M1","E1M2","E1M3","E1M4","E1M5", + "E1M6","E1M7","E1M8","E1M9", + "E2M1","E2M2","E2M3","E2M4","E2M5", + "E2M6","E2M7","E2M8","E2M9", + "E3M1","E3M2","E3M3","E3M4","E3M5", + "E3M6","E3M7","E3M8","E3M9",NULL}; + +static char *ult_doom_levels[]= + {"E1M1","E1M2","E1M3","E1M4","E1M5", + "E1M6","E1M7","E1M8","E1M9", + "E2M1","E2M2","E2M3","E2M4","E2M5", + "E2M6","E2M7","E2M8","E2M9", + "E3M1","E3M2","E3M3","E3M4","E3M5", + "E3M6","E3M7","E3M8","E3M9", + "E4M1","E4M2","E4M3","E4M4","E4M5", + "E4M6","E4M7","E4M8","E4M9",NULL}; + +static char *doom2_levels[]= + {"MAP01","MAP02","MAP03","MAP04","MAP05", + "MAP06","MAP07","MAP08","MAP09","MAP10", + "MAP11","MAP12","MAP13","MAP14","MAP15", + "MAP16","MAP17","MAP18","MAP19","MAP20", + "MAP21","MAP22","MAP23","MAP24","MAP25", + "MAP26","MAP27","MAP28","MAP29","MAP30", + "MAP31","MAP32",NULL}; + +static char **level_list=NULL; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ + +static void CalcBounding(char *text,int *num,char *t[],int min_len, + int *x,int *y,int *w,int *h) +{ + int no; + int f; + char *p; + + no=0; + p=strtok(text,"|"); + + while(p) + { + t[no++]=p; + p=strtok(NULL,"|"); + } + + t[no]=NULL; + + *h=fh*no+GUI_BORDERFULL; + + for(f=0;f<no;f++) + if (strlen(t[f])>min_len) + min_len=strlen(t[f]); + + *num=no; + *w=fw*min_len+GUI_BORDERFULL; + *x=SCRW/2-*w/2; + *y=SCRH/2-*h/2; +} + + +static void DrawBox(int x,int y,int w,int h,int bevel) +{ + int f; + + GFX_frect(x,y,w,h,GUI_MID); + + for(f=0;f<bevel;f++) + { + GFX_line(x+1+f,y+h-1-f,x+w-1-f,y+h-1-f,GUI_LO); + GFX_line(x+w-1-f,y+1+f,x+w-1-f,y+h-1-f,GUI_LO); + GFX_line(x+f,y+f,x+w-1-f,y+f,GUI_HI); + GFX_line(x+f,y+f,x+f,y+h-1-f,GUI_HI); + } +} + + +static void DrawTitle(int x,int y,int w,char *title) +{ + GFX_print((x+w/2)-(strlen(title)*fw)/2,y+GUI_TITLEOFF, + GUI_TEXTBOLD,"%s",title); + GFX_line(x+GUI_BEVEL,y+GUI_TITLEOFF+fh,x+w-GUI_BEVEL-1, + y+GUI_TITLEOFF+fh,GUI_HI); + GFX_line(x+GUI_BEVEL,y+GUI_TITLEOFF+fh+1,x+w-GUI_BEVEL-1, + y+GUI_TITLEOFF+fh+1,GUI_LO); +} + + +static void DrawText(int x,int y,char *p) +{ + /* + if ((STRNEQ("HTTP:",p))||(STRNEQ("FTP:",p))||(STRNEQ("MAILTO:",p))) + { + GFX_print(x+1,y+1,WHITE,"%s",p); + GFX_print(x,y,V_RGB(0x60,0x60,0xff),"%s",p); + } + else + */ + { + if (GUI_TEXTSHADOW!=GUI_TEXT) + GFX_print(x+1,y+1,GUI_TEXTSHADOW,"%s",p); + + GFX_print(x,y,GUI_TEXT,"%s",p); + } +} + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ + +void GuiSetScreen(int w,int h) +{ + INI_GetTable(gui_ini,INI_TAB_SIZE(gui_ini)); + + SCRW=w; + SCRH=h; + fw=GFX_fw(); + fh=GFX_fh()+2; +} + + +void GuiInfoBox(char *title,char *fmt,...) +{ + static char p[MAXTEXT]; + va_list va; + + va_start(va,fmt); + vsprintf(p,fmt,va); + va_end(va); + + GUI_alert(title,p,"OK"); +} + + +void GuiDrawInfoBox(char *title,int px,int py,int centre,char *fmt,...) +{ + static char p[MAXTEXT]; + char *t[MAXLINES]; + int no,x,y,w,h; + int f; + va_list va; + int cen; + + va_start(va,fmt); + vsprintf(p,fmt,va); + va_end(va); + + CalcBounding(p,&no,t,strlen(title),&x,&y,&w,&h); + h+=GUI_TITLEH; + + switch(px) + { + case GUI_CENTRE: + break; + case GUI_FLUSH_LEFT: + x=0; + break; + case GUI_FLUSH_RIGHT: + x=SCRW-w-1; + break; + default: + x=px; + break; + } + + switch(py) + { + case GUI_CENTRE: + break; + case GUI_FLUSH_TOP: + y=0; + break; + case GUI_FLUSH_LOWER: + y=SCRH-h-1; + break; + default: + y=py; + break; + } + + cen=(x+w/2); + + DrawBox(x,y,w,h,GUI_BEVEL); + + for(f=0;f<no;f++) + if (centre) + DrawText(cen-strlen(t[f])*(fw/2)+1, + y+GUI_BORDER+GUI_TITLEH+fh*f+1,t[f]); + else + DrawText(x+GUI_BEVEL+GUI_BORDER,y+GUI_BORDER+GUI_TITLEH+fh*f,t[f]); + + DrawTitle(x,y,w,title); +} + + +char *GuiPickLevel(char *prompt) +{ + int f; + static char s[256]; + char **orig; + int no; + + switch(level_style) + { + case DOOM_LEVELS: + orig=doom_levels; + break; + + case ULTIMATE_DOOM_LEVELS: + orig=ult_doom_levels; + break; + + case DOOM_2_LEVELS: + orig=doom2_levels; + break; + + default: + orig=doom_levels; + break; + } + + if (!level_list) + { + no=0; + while(orig[no++]); + + level_list=Grab(sizeof(char *)*no); + + for(f=0;f<no-1;f++) + { + strcpy(s,orig[f]); + strcat(s," - "); + strcat(s,MapName(orig[f])); + level_list[f]=Strdup(s); + } + + level_list[no-1]=NULL; + } + + f=GUI_picklist(prompt,level_list); + + if (f<0) + return(NULL); + + strcpy(s,orig[f]); + return(s); +} + + +/* END OF FILE */ @@ -0,0 +1,84 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Generic GUI routines + + $Id$ + +*/ + +#ifndef VIDOOM_GUI_H +#define VIDOOM_GUI_H + +#include "gfx.h" + + +/* If the platform specific GUI wants to provide a bas-relief GUI + in the same colours as the generic GUI it should use these colours. + + Note these values are not initialised until after GuiSetScreen() has been + called. +*/ +extern int GUI_HI; +extern int GUI_MID; +extern int GUI_LO; +extern int GUI_TEXT; +extern int GUI_TEXTSHADOW; +extern int GUI_TEXTBOLD; + + +/* These values can be passed to the InfoBox functions and the box will be + positioned accordingly. CENTRE can be used for both X and Y co-ords. +*/ +#define GUI_CENTRE -1000 +#define GUI_FLUSH_TOP -1002 +#define GUI_FLUSH_LEFT -1003 +#define GUI_FLUSH_RIGHT -1004 +#define GUI_FLUSH_LOWER -1005 + + +/* Inform GUI of screen size if required +*/ +void GuiSetScreen(int w,int h); + +/* Draw a box with filled with the supplied formatted text and wait for a + key/mouse button. Text is split into lines with a | char. +*/ +void GuiInfoBox(char *title, char *fmt,...); + +/* Draw a box identical to the Alert one, but don't wait for any input. Also + allows a position to be supplied for the top-left of the box. If any + position is -1 then the box is centred on that axis. If centre is set then + the text in the box is centred. +*/ +void GuiDrawInfoBox(char *title, int x, int y, int centre, char *fmt,...); + +/* Returns a string in the form MAPxx or ExMy, depending on the game type + defined in globals. Returns NULL if cancelled. + + Note the return is static and must be copied elsewhere if permanence is + required. +*/ +char *GuiPickLevel(char *prompt); + + +#endif @@ -0,0 +1,532 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Routines to read our version of an INI file + + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include "ini.h" +#include "mem.h" +#include "file.h" +#include "vstring.h" +#include "gfx.h" + +#define MAXLEN (PATH_MAX*2) +#define ROOTVAR "%WADDED%" + +typedef struct Token + { + char *key; + char *valkey; + char *val; + struct Token *next; + struct Token *prev; + } Token; + +static Token *ini=NULL; + +static char ini_dir[PATH_MAX]; +static char ini_file[PATH_MAX]; + +/* Token tables to export +*/ +TokenTable ini_yesno[]={{"yes",TRUE}, + {"no",FALSE}, + {"true",TRUE}, + {"false",FALSE}, + {"y",TRUE}, + {"n",FALSE}, + {"1",TRUE}, + {"0",FALSE}, + {NULL,0}}; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static int cmpstr(char *a,char *b) +{ + if ((a)&&(b)) + return(StrCaseCmp(a,b)); + else if ((!a)&&(!b)) + return(0); + else if (!a) + return(-1); + else + return(1); +} + + +static int CompareToken(Token *a, Token *b) +{ + int n; + + if ((n=cmpstr(a->key,b->key))) + return(n); + + return (cmpstr(a->valkey,b->valkey)); +} + +static Token *FindToken(char *key, char *val) +{ + Token *tok; + + tok=ini; + + while(tok) + { + if ((cmpstr(tok->key,key)==0)&& + (cmpstr(tok->valkey,val)==0)) + return(tok); + + tok=tok->next; + } + + return(NULL); +} + + +static char *GetLine(FILE *fp, int len) +{ + static char s[MAXLEN]; + int l,f; + + fgets(s,len-1,fp); + + if (feof(fp)) + return(FALSE); + + l=strlen(s)-1; + + if ((l>=0)&&(s[l]=='\n')) + s[l]=0; + + l=strlen(s)-1; + + if ((l>=0)&&(s[l]=='\r')) + s[l]=0; + + for(f=0;f<l;f++) + if (!isspace(s[f])) + if (s[f]=='#') + return(GetLine(fp,len)); + else + return(s+f); + + return(GetLine(fp,len)); +} + + +static void AddToken(char *key, char *line) +{ + Token *tok; + Token *ins; + char *valkey,*val; + + valkey=strtok(line,"="); + val=strtok(NULL,"="); + + if (!val) + val=""; + + tok=Grab(sizeof(Token)); + + tok->key=Strdup(key); + tok->valkey=Strdup(valkey); + tok->val=Strdup(val); + tok->next=NULL; + tok->prev=NULL; + + if (!ini) + { + ini=tok; + return; + } + + ins=ini; + + while(ins) + { + if (CompareToken(ins,tok)>0) + if (ins->prev) + { + ins->prev->next=tok; + tok->prev=ins->prev; + tok->next=ins; + ins->prev=tok; + + return; + } + else + { + tok->next=ins; + ins->prev=tok; + ini=tok; + + return; + } + + if (ins->next) + ins=ins->next; + else + { + ins->next=tok; + tok->prev=ins; + return; + } + } +} + +static int TokenToNum(char *val, TokenTable tokens[]) +{ + int f; + + f=0; + + while(tokens[f].token) + if (cmpstr(tokens[f].token,val)==0) + return(tokens[f].val); + else + f++; + + return(0); +} + +static char *NumToToken(int val, TokenTable tokens[]) +{ + int f; + + f=0; + + while(tokens[f].token) + if (tokens[f].val==val) + return(tokens[f].token); + else + f++; + + return(""); +} + + +static char *Expand(char *p) +{ + static char r[MAXLEN]; + + if (strncmp(p,ROOTVAR,strlen(ROOTVAR))==0) + { + strcpy(r,ini_dir); + strcat(r,p+strlen(ROOTVAR)); + return(r); + } + else + return(p); +} + + +static char *Unexpand(char *p) +{ + static char r[MAXLEN]; + + if (strncmp(p,ini_dir,strlen(ini_dir))==0) + { + strcpy(r,ROOTVAR); + strcat(r,p+strlen(ini_dir)); + return(r); + } + else + return(p); +} + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ + +int INI_ReadInt(char *key, char *val) +{ + Token *tok; + + if ((tok=FindToken(key,val))) + return((int)strtol(tok->val,(char **)NULL,0)); + else + return(0); +} + +char *INI_ReadStr(char *key, char *val) +{ + Token *tok; + + if ((tok=FindToken(key,val))) + return(Expand(tok->val)); + else + return(""); +} + +int INI_ReadToken(char *key, char *val, TokenTable tokens[]) +{ + Token *tok; + + if ((tok=FindToken(key,val))) + return(TokenToNum(tok->val,tokens)); + else + return(0); +} + +double INI_ReadDouble(char *key, char *val) +{ + Token *tok; + char *dum; + + if ((tok=FindToken(key,val))) + return(strtod(tok->val,&dum)); + else + return(0.0); +} + +void INI_SaveInt(char *key, char *valkey, int val) +{ + char s[MAXLEN]; + Token *tok; + + sprintf(s,"%d",val); + + if ((tok=FindToken(key,valkey))) + { + Release(tok->val); + tok->val=Strdup(s); + } + else + { + sprintf(s,"%s=%d",valkey,val); + AddToken(key,s); + } +} + +void INI_SaveStr(char *key, char *valkey, char *val) +{ + char s[MAXLEN]; + Token *tok; + + if ((tok=FindToken(key,valkey))) + { + Release(tok->val); + tok->val=Strdup(Unexpand(val)); + } + else + { + sprintf(s,"%s=%s",valkey,val); + AddToken(key,s); + } +} + +void INI_SaveToken(char *key, char *valkey, int val, + TokenTable tokens[]) +{ + char s[MAXLEN]; + Token *tok; + + if ((tok=FindToken(key,valkey))) + { + Release(tok->val); + tok->val=Strdup(NumToToken(val,tokens)); + } + else + { + sprintf(s,"%s=%s",valkey,NumToToken(val,tokens)); + AddToken(key,s); + } +} + +void INI_SaveDouble(char *key, char *valkey, double val) +{ + char s[MAXLEN]; + Token *tok; + + sprintf(s,"%f",val); + + if ((tok=FindToken(key,valkey))) + { + Release(tok->val); + tok->val=Strdup(s); + } + else + { + sprintf(s,"%s=%f",valkey,val); + AddToken(key,s); + } +} + +void INI_DeleteKey(char *key,char *valkey) +{ + Token *tok; + + if ((tok=FindToken(key,valkey))) + { + if (tok->prev) + tok->prev->next=tok->next; + + if (tok->next) + tok->next->prev=tok->prev; + + if (ini==tok) + ini=tok->next; + + Release(tok->key); + Release(tok->valkey); + Release(tok->val); + Release(tok); + } +} + +void INI_Load(char *name) +{ + FILE *fp; + char *line; + char token[32]; + + strcpy(ini_dir,Pwd()); + strcpy(ini_file,ini_dir); + strcat(ini_file,DIRSEP); + strcat(ini_file,name); + + if ((fp=fopen(ini_file,"r"))) + { + while((line=GetLine(fp,MAXLEN))) + if (line[0]=='[') + { + line[strlen(line)-1]=0; + strcpy(token,line+1); + } + else + AddToken(token,line); + } + else + GFX_exit(EXIT_FAILURE,"Couldn't open INI file. " + "Check doc/overview.htm for config details.\n"); +} + +void INI_Save(void) +{ + FILE *fp; + Token *tok; + char *last_key; + int first=TRUE; + + if ((fp=fopen(ini_file,"w"))) + { + tok=ini; + last_key=NULL; + + while(tok) + { + if (cmpstr(last_key,tok->key)) + { + if (first) + fprintf(fp,"[%s]\n",tok->key); + else + fprintf(fp,"\n[%s]\n",tok->key); + last_key=tok->key; + first=FALSE; + } + + fprintf(fp,"%s=%s\n",tok->valkey,tok->val); + + tok=tok->next; + } + + fclose(fp); + } +} + +void INI_GetTable(INI_Table table[],int no) +{ + int f; + int *i; + char *p; + double *d; + + for(f=0;f<no;f++) + if (FindToken(table[f].key,table[f].valkey)) + switch(table[f].type) + { + case INI_INT: + i=(int *)table[f].data; + *i=INI_ReadInt(table[f].key,table[f].valkey); + break; + + case INI_STR: + p=(char *)table[f].data; + strcpy(p,INI_ReadStr(table[f].key,table[f].valkey)); + break; + + case INI_TOK: + i=(int *)table[f].data; + *i=INI_ReadToken(table[f].key,table[f].valkey, + table[f].tokens); + break; + + case INI_DOUBLE: + d=(double *)table[f].data; + *d=INI_ReadDouble(table[f].key,table[f].valkey); + break; + } +} + +void INI_PutTable(INI_Table table[],int no) +{ + int f; + int i; + char *p; + double d; + + for(f=0;f<no;f++) + switch(table[f].type) + { + case INI_INT: + i=*((int *)table[f].data); + INI_SaveInt(table[f].key,table[f].valkey,i); + break; + + case INI_STR: + p=(char *)table[f].data; + INI_SaveStr(table[f].key,table[f].valkey,p); + break; + + case INI_TOK: + i=*((int *)table[f].data); + INI_SaveToken(table[f].key,table[f].valkey,i,table[f].tokens); + break; + + case INI_DOUBLE: + d=*((double *)table[f].data); + INI_SaveDouble(table[f].key,table[f].valkey,d); + break; + } +} + + +/* END OF FILE */ @@ -0,0 +1,106 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Routines and common keys used to read an INI file + Format of INI file: + + [key] + value_key=value + + $Id$ + +*/ + +#ifndef VIDOOM_INI_H + +#define VIDOOM_INI_H + + +/* Types for table filling queries +*/ +#define INI_INT 0 +#define INI_STR 1 +#define INI_TOK 2 +#define INI_DOUBLE 3 + + +/* Defines the possible values and the corresponding string token for the + value. +*/ +typedef struct + { + char *token; + int val; + } TokenTable; + +/* .data is treated as (int *) for INI_INT and INI_TOK. It is treated as + (char *) for INI_TOK and (double *) INI_DOUBLE. Tokens can be NULL for + non-INI_TOK types. +*/ +typedef struct + { + int type; + char *key; + char *valkey; + void *data; + TokenTable *tokens; + } INI_Table; + + +/* General yes/no token definitions +*/ +extern TokenTable ini_yesno[]; + +/* Macro for calculating table size +*/ +#define INI_TAB_SIZE(x) (sizeof(x)/sizeof(INI_Table)) + + +/* Note all returns are static +*/ +int INI_ReadInt(char *key,char *value_key); +char *INI_ReadStr(char *key,char *value_key); +int INI_ReadToken(char *key,char *value_key,TokenTable tokens[]); +double INI_ReadDouble(char *key,char *value_key); + +void INI_SaveInt(char *key,char *value_key,int val); +void INI_SaveStr(char *key,char *value_key,char *val); +void INI_SaveToken(char *key,char *value_key,int val,TokenTable tokens[]); +void INI_SaveDouble(char *key,char *value_key,double val); + +void INI_DeleteKey(char *key,char *value_key); + +void INI_Load(char *name); +void INI_Save(void); + +void INI_GetTable(INI_Table table[],int no); +void INI_PutTable(INI_Table table[],int no); + +/* This automatically takes iyase_dir from [config] and dir from [key]{iyase} + and adds on the passed filename. Note that return is static. +*/ +char *INI_FileName(char *key,char *fname); + +#endif + + +/* END OF FILE */ diff --git a/linedefs.c b/linedefs.c new file mode 100644 index 0000000..135c4a5 --- /dev/null +++ b/linedefs.c @@ -0,0 +1,316 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Handles definition and storage of the supported LINEDEFS + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include "platgui.h" +#include "gfx.h" +#include "linedefs.h" +#include "mem.h" +#include "list.h" +#include "map.h" + + +/* ---------------------------------------- TYPES AND VARS +*/ +typedef struct + { + char *name; + } LineClass; + +typedef struct + { + int class; + int id; + char *name; + } LineInfo; + +typedef struct + { + char *name; + int id; + int two; + } LineDefault; + + +static int no_classes=0; + +static Map line_class=NULL; +static List line_list=NULL; +static Map line_def=NULL; + +static PLAT_PICKLIST **picklist=NULL; + +static PLAT_MENU *line_menu=NULL; + +static char **line_def_plist=NULL; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static PLAT_PICKLIST *LinedefPicklist(int class) +{ + LineInfo *li; + Iterator i; + int no; + int f; + + if (picklist[class]) + return(picklist[class]); + else + { + no=0; + i=ListIterator(line_list); + + while(i) + { + li=IteratorData(i); + + if (li->class==class) + no++; + + i=IteratorNext(i); + } + + picklist[class]=Grab(sizeof(PLAT_PICKLIST)*(no+1)); + + i=ListIterator(line_list); + + f=0; + while(i) + { + li=IteratorData(i); + + if (li->class==class) + { + picklist[class][f].text=li->name; + picklist[class][f].client_index=li->id; + f++; + } + + i=IteratorNext(i); + } + picklist[class][f].text=NULL; + + return(picklist[class]); + } +} + + + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ +void LinedefNewClass(char *class) +{ + LineClass c; + + if (!line_class) + line_class=MapNew(sizeof(LineClass)); + + c.name=Strdup(class); + + MapAdd(line_class,no_classes++,&c); +} + + +void LinedefAdd(char *class,char *name,int id) +{ + LineInfo *li; + int i_class; + int f; + + if (!line_list) + line_list=ListNew(sizeof(LineInfo)); + + i_class=-1; + + for(f=0;f<MapSize(line_class);f++) + { + LineClass *c; + + c=MapElem(line_class,f); + + if (STREQ(c->name,class)) + i_class=f; + } + + if (i_class==-1) + i_class=0; + + li=Grab(sizeof(LineInfo)); + + li->class=i_class; + li->name=Strdup(name); + li->id=id; + ListAppend(line_list,li); +} + + +int SelectLinedef(void) +{ + int class; + int f; + int x,y; + + if (!line_menu) + { + line_menu=Grab(sizeof(PLAT_MENU)*(no_classes+1)); + picklist=Grab(sizeof(PLAT_PICKLIST *)*(no_classes+1)); + + for(f=0;f<no_classes;f++) + { + LineClass *c; + + c=MapElem(line_class,f); + line_menu[f].text=c->name; + line_menu[f].client_index=f; + } + + for(f=0;f<no_classes+1;f++) + picklist[f]=NULL; + + line_menu[no_classes].text=NULL; + } + + GFX_mouse(&x,&y); + + if (no_classes<2) + class=0; + else + class=GUI_menu("Pick class of LINEDEF",x,y,line_menu,LINEDEF_NULLID); + + if (class!=LINEDEF_NULLID) + class=GUI_client_picklist("LINEDEF",LinedefPicklist(class), + LINEDEF_NULLID); + + return(class); +} + + +char *LinedefName(int id) +{ + LineInfo *li; + Iterator i; + + i=ListIterator(line_list); + + while(i) + { + li=IteratorData(i); + + if (li->id==id) + { + IteratorClear(i); + return(li->name); + } + + i=IteratorNext(i); + } + + return("UNKNOWN"); +} + + +char *LinedefClass(int id) +{ + LineInfo *li; + LineClass *lc; + Iterator i; + + if (no_classes==0) + return("NO CLASSES!"); + + i=ListIterator(line_list); + + while(i) + { + li=IteratorData(i); + + if (li->id==id) + { + IteratorClear(i); + lc=MapElem(line_class,li->class); + return(lc->name); + } + + i=IteratorNext(i); + } + + return("UNKNOWN"); +} + + +void AddLinedefType(char *name,int flags,int two_sided) +{ + LineDefault l; + + if (!line_def) + line_def=MapNew(sizeof(LineDefault)); + + l.name=Strdup(name); + l.id=flags; + l.two=two_sided; + + MapAdd(line_def,-1,&l); +} + + +int ChooseLinedefType(int *flag, int *two) +{ + LineDefault *l; + int f; + + if (!line_def_plist) + { + line_def_plist=Grab(sizeof(char *)*(MapSize(line_def)+1)); + + for(f=0;f<MapSize(line_def);f++) + { + l=MapElem(line_def,f); + line_def_plist[f]=l->name; + } + + line_def_plist[MapSize(line_def)]=NULL; + } + + f=GUI_picklist("LINEDEF TYPE",line_def_plist); + + if (f!=-1) + { + l=MapElem(line_def,f); + *flag=l->id; + *two=l->two; + return(TRUE); + } + else + return(FALSE); +} + + +/* END OF FILE */ diff --git a/linedefs.h b/linedefs.h new file mode 100644 index 0000000..336525c --- /dev/null +++ b/linedefs.h @@ -0,0 +1,84 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Handles definition and storage of the supported LINEDEFs + + $Id$ + +*/ + +#ifndef VIDOOM_LINEDEFS_H + +#define VIDOOM_LINEDEFS_H + +#include "wad.h" +#include "gfx.h" +#include "platgui.h" + + +/* This is not going to be an ID, so is used to detect whether LinedefSelect() + was cancelled. +*/ +#define LINEDEF_NULLID -666 + + +/* Add a new class of linedef +*/ +void LinedefNewClass(char *class); + + +/* Add the named type to the supplied class with the ID +*/ +void LinedefAdd(char *class,char *name,int id); + + +/* Selects a type of linedef, returning the ID or LINEDEF_NULLID if cancelled +*/ +int SelectLinedef(void); + + +/* Returns the name of a linedef type +*/ +char *LinedefName(int id); + + +/* Returns the class of a linedef type +*/ +char *LinedefClass(int id); + + +/* Define a default linedef flags values +*/ +void AddLinedefType(char *name,int flags,int two_sided); + + +/* Return a default linedef flag value into flags after showing a picklist to + choose them from, and two is TRUE if the linedef is 2-sided. Returns TRUE + for accepted, FALSE cancelled. +*/ +int ChooseLinedefType(int *flags,int *two); + + +#endif + + +/* END OF FILE */ diff --git a/linux/file.c b/linux/file.c new file mode 100644 index 0000000..6094021 --- /dev/null +++ b/linux/file.c @@ -0,0 +1,103 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + File system interfaces + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include <unistd.h> + + +/* ---------------------------------------- EXPORTED FUNCS +*/ +char *Pwd(void) +{ + static char s[PATH_MAX]; + + getcwd(s,PATH_MAX); + + return s; +} + +void Cd(char *path) +{ + chdir(path); +} + +char *Dirname(char *path) +{ + static char s[PATH_MAX]; + char *p; + + strcpy(s,path); + + p=s+strlen(s)-1; + + while((*p)&&(p>s)) + if (*p=='/') + *(p+1)=0; + else + p--; + + return s; +} + +char *Basename(char *path) +{ + static char s[PATH_MAX]; + char *p; + + strcpy(s,path); + + p=s+strlen(s)-1; + + while(p>s) + if (*p=='/') + return p+1; + else + p--; + + return(s); +} + +int FileExists(char *path) +{ + FILE *fp; + + if ((fp=fopen(path,"r"))) + { + fclose(fp); + return TRUE; + } + else + return FALSE; +} + +int FilenamesEqual(char *path1, char *path2) +{ + return !strcasecmp(path1,path2); +} + + +/* END OF FILE */ diff --git a/linux/gfx.c b/linux/gfx.c new file mode 100644 index 0000000..01c7f4a --- /dev/null +++ b/linux/gfx.c @@ -0,0 +1,1013 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Graphics functions + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "gfx.h" +#include "mem.h" + +#include <stdio.h> +#include <ctype.h> +#include <limits.h> +#include <stdarg.h> +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> + +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + + +/* ---------------------------------------- VARS +*/ +static int init=FALSE; + +static Window win=None; +static Pixmap pix=None; +static Pixmap saved=None; +static GC gc=None; +static Display *disp=NULL; +static XVisualInfo visual; +static XFontStruct *font=NULL; +static int curr_func; + +#define FONTGAP 2 +#define FONTW (font->max_bounds.rbearing - \ + font->min_bounds.lbearing) +#define FONTH (font->max_bounds.descent + \ + font->max_bounds.ascent + FONTGAP) + +static int width=0; +static int height=0; + +static int fw=0; +static int fh=0; + +static int mbuttons=3; /* Assume 3 for X11 */ + +static int dirty_min_x=0; +static int dirty_max_x=0; +static int dirty_min_y=0; +static int dirty_max_y=0; + +static char *font_names[]= + { + "6x9", + NULL + }; + +#define DIRTY(x,y) do { \ + dirty_min_x=MIN(x,dirty_min_x); \ + dirty_min_y=MIN(y,dirty_min_y); \ + dirty_max_x=MAX((x)+1,dirty_max_x); \ + dirty_max_y=MAX((y)+1,dirty_max_y); \ + } while(0) + + +/* Internal representation used for GFX_IMAGE +*/ +typedef struct + { + int w,h; + XImage *p; + } GfxImage; + +/* These are used to define the mappings between X11 scancodes and GFX keys. + Then the scancode can be used to directly index and array. +*/ +static struct + { + int is_special; + int key; + } keycode_map[sizeof(KeyCode)<< CHAR_BIT]; + +typedef struct KBuff + { + GFXKey k; + struct KBuff *next; + } KeyBuff; + +static KeyBuff *kb_head=NULL; +static KeyBuff *kb_tail=NULL; + +static struct + { + KeySym keysym; + int code; + } key_map[]= { + {XK_F1, GFX_F1}, + {XK_F2, GFX_F2}, + {XK_F3, GFX_F3}, + {XK_F4, GFX_F4}, + {XK_F5, GFX_F5}, + {XK_F6, GFX_F6}, + {XK_F7, GFX_F7}, + {XK_F8, GFX_F8}, + {XK_F9, GFX_F9}, + {XK_F10, GFX_F10}, + {XK_F11, GFX_F11}, + {XK_F12, GFX_F12}, + {XK_Escape, GFX_ESC}, + {XK_Insert, GFX_INSERT}, + {XK_Home, GFX_HOME}, + {XK_Page_Up, GFX_PGUP}, + {XK_Delete, GFX_DELETE}, + {XK_End, GFX_END}, + {XK_Page_Down, GFX_PGDN}, + {XK_Up, GFX_UP}, + {XK_Down, GFX_DOWN}, + {XK_Left, GFX_LEFT}, + {XK_Right, GFX_RIGHT}, + {XK_Return, GFX_ENTER}, + {XK_BackSpace, GFX_BACKSPACE}, + {XK_Tab, GFX_TAB}, + + {XK_VoidSymbol, -1}, + }; + +/* Types and vars for event handling +*/ +typedef enum + { + X11_GFXnone=0, + X11_GFXmouse=1, + X11_GFXmouseMove=2, + X11_GFXkey=4, + X11_GFXall=4|2|1 + } EventType; + +typedef enum + { + X11_GFXpress, X11_GFXrelease, X11_GFXmove + } MouseEventType; + +static int mouse_b=0; +static int mouse_x=0; +static int mouse_y=0; +static int is_shift=FALSE; +static int is_ctrl=FALSE; +static int is_alt=FALSE; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ + +static unsigned long getXCol(int col) +{ + XColor pix; + + pix.red=(col&0xff0000)>>8; + pix.green=(col&0xff00); + pix.blue=(col&0xff)<<8; + pix.flags=DoRed|DoBlue|DoGreen; + + XAllocColor(disp,DefaultColormap(disp,DefaultScreen(disp)),&pix); + + return pix.pixel; +} + + +static void setXCol(int col) +{ + static int last_col=-1; + + if (col==last_col) + return; + + last_col=col; + + XSetForeground(disp,gc,getXCol(col)); +} + + +static void KeyBuffAdd(int code, char ascii, int shift, int ctrl, int alt) +{ + KeyBuff *key; + + key=Grab(sizeof *key); + + key->k.code=code; + key->k.ascii=ascii; + key->k.shift=shift; + key->k.ctrl=ctrl; + key->k.alt=alt; + key->next=NULL; + + if (!kb_head) + kb_head=kb_tail=key; + else + { + kb_tail->next=key; + kb_tail=key; + } +} + + +static void GetKey(GFXKey *key) +{ + if (kb_head) + { + KeyBuff *k=kb_head; + + *key=k->k; + + if (!(kb_head=k->next)) + kb_tail=NULL; + + Release(k); + } +} + + +static int HandleKey(XKeyEvent *key) +{ + int code; + char ascii; + + if (keycode_map[key->keycode].is_special) + { + code=keycode_map[key->keycode].key; + ascii=0; + } + else + { + char buff[2]=""; + + code=GFX_ASCII; + + XLookupString(key,buff,2,NULL,NULL); + + if (!isprint(buff[0])) + return FALSE; + + ascii=buff[0]; + } + + KeyBuffAdd(code,ascii, + key->state&ShiftMask, + key->state&ControlMask, + key->state&Mod1Mask); + + return TRUE; +} + + +static void HandleMouse(int x, int y, int b, MouseEventType type, + unsigned int state) +{ + int mask; + + mouse_x=x; + mouse_y=y; + + if (type!=X11_GFXmove) + { + switch(b) + { + case Button1: + mask=GFX_BUTLEFT; + break; + case Button2: + mask=GFX_BUTMIDDLE; + break; + case Button3: + mask=GFX_BUTRIGHT; + break; + case Button4: + mask=GFX_MSWHEELUP; + break; + case Button5: + mask=GFX_MSWHEELDOWN; + break; + default: + mask=0; + break; + } + + if (type==X11_GFXpress) + mouse_b|=mask; + else + mouse_b&=~mask; + } + + is_shift=state&ShiftMask; + is_ctrl=state&ControlMask; + is_alt=state&Mod1Mask; +} + + +static EventType Wait(EventType etype, int wait) +{ + XEvent ev; + + while(wait || XPending(disp)) + { + XNextEvent(disp,&ev); + + switch(ev.type) + { + case KeyPress: + if (HandleKey(&ev.xkey) && (etype & X11_GFXkey)) + return X11_GFXkey; + + break; + + case ButtonPress: + HandleMouse(ev.xbutton.x,ev.xbutton.y,ev.xbutton.button, + X11_GFXpress,ev.xbutton.state); + + if (etype & X11_GFXmouse) + return X11_GFXmouse; + + break; + + case ButtonRelease: + HandleMouse(ev.xbutton.x,ev.xbutton.y,ev.xbutton.button, + X11_GFXrelease,ev.xbutton.state); + + if (etype & X11_GFXmouse) + return X11_GFXmouse; + + break; + + case MotionNotify: + HandleMouse(ev.xbutton.x,ev.xbutton.y,0, + X11_GFXmove,ev.xbutton.state); + + if (etype & X11_GFXmouseMove) + return X11_GFXmouseMove; + + break; + + case Expose: + DIRTY(ev.xexpose.x,ev.xexpose.y); + DIRTY(ev.xexpose.x+ev.xexpose.width, + ev.xexpose.y+ev.xexpose.height); + + if (ev.xexpose.count==0) + GFX_redraw(); + + break; + + default: + break; + } + } + + return X11_GFXnone; +} + + + +/* ---------------------------------------- 'PRIVATE' EXPORTED FUNCTIONS +*/ +void VIDOOM_GET_GFX_VARS(Display **ret_disp, Window *ret_win, Pixmap *ret_pix, + GC *ret_gc) +{ + *ret_disp=disp; + *ret_win=win; + *ret_pix=pix; + *ret_gc=gc; +} + + +void VIDOOM_GFX_FORCE_REDRAW(void) +{ + dirty_min_x=0; + dirty_max_x=width-1; + dirty_min_y=0; + dirty_max_y=width-1; + + GFX_redraw(); +} + + +void VIDOOM_GFX_SAVE_DISPLAY(void) +{ + if (curr_func!=GXcopy) + XSetFunction(disp,gc,GXcopy); + + XCopyArea(disp,pix,saved,gc,0,0,width,height,0,0); +} + + +void VIDOOM_GFX_RESTORE_DISPLAY(void) +{ + XCopyArea(disp,saved,pix,gc,0,0,width,height,0,0); + VIDOOM_GFX_FORCE_REDRAW(); + + if (curr_func!=GXcopy) + XSetFunction(disp,gc,curr_func); +} + + +Pixmap VIDOOM_GFX_SAVE_UNDER(int x, int y, int w, int h) +{ + Pixmap p; + + p=XCreatePixmap(disp,win,w,h,visual.depth); + XCopyArea(disp,pix,p,gc,x,y,w,h,0,0); + + return p; +} + + +void VIDOOM_GFX_RESTORE_UNDER(Pixmap p, int x, int y, int w, int h) +{ + DIRTY(x,y); + DIRTY(x+w,y+h); + XCopyArea(disp,p,pix,gc,0,0,w,h,x,y); + XFreePixmap(disp,p); +} + + +/* This is done in a *very* bad fashion.... +*/ +void VIDOOM_GFX_WAIT_FOR(pid_t pid, int *status) +{ + XEvent ev; + + while(TRUE) + { + while (XPending(disp)) + { + XNextEvent(disp,&ev); + + if (ev.type==Expose) + { + DIRTY(ev.xexpose.x,ev.xexpose.y); + DIRTY(ev.xexpose.x+ev.xexpose.width, + ev.xexpose.y+ev.xexpose.height); + + if (ev.xexpose.count==0) + GFX_redraw(); + } + } + + if (waitpid(pid,status,WNOHANG)) + return; + + /* Now, this is nasty... + */ + usleep(10000); + } +} + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ +void GFX_init(void) +{ + if (!init) + { + int f; + + init=TRUE; + + /* Open display and find an appropriate visual + */ + if (!(disp=XOpenDisplay(NULL))) + GFX_exit(EXIT_FAILURE,"Couldn't open X display\n"); + + if (!XMatchVisualInfo(disp,DefaultScreen(disp),32,TrueColor,&visual) && + !XMatchVisualInfo(disp,DefaultScreen(disp),24,TrueColor,&visual) && + !XMatchVisualInfo(disp,DefaultScreen(disp),16,TrueColor,&visual) && + !XMatchVisualInfo(disp,DefaultScreen(disp),15,TrueColor,&visual)) + GFX_exit(EXIT_FAILURE,"Couldn't find a TrueColor visual\n"); + + f=0; + while(font_names[f] && !font) + font=XLoadQueryFont(disp,font_names[f++]); + + if (!font) + { + font=XLoadQueryFont(disp,"fixed"); + + if (!font) + GFX_exit(EXIT_FAILURE,"Couldn't even load font fixed\n"); + } + + fw=FONTW; + fh=FONTH; + + for(f=0;f<(sizeof(KeyCode)<< CHAR_BIT);f++) + keycode_map[f].is_special=FALSE; + + f=0; + + while(key_map[f].keysym!=XK_VoidSymbol) + { + KeyCode code; + + code=XKeysymToKeycode(disp,key_map[f].keysym); + + keycode_map[code].is_special=TRUE; + keycode_map[code].key=key_map[f].code; + + f++; + } + } +} + + +void GFX_close(void) +{ + if (gc!=None) + { + XFreeGC(disp,gc); + gc=None; + } + + if (pix!=None) + { + XFreePixmap(disp,pix); + pix=None; + } + + if (saved!=None) + { + XFreePixmap(disp,saved); + saved=None; + } + + if (win!=None) + { + XDestroyWindow(disp,win); + win=None; + } +} + + +void GFX_open(int w,int h) +{ + XGCValues gc_val; + XSizeHints hint; + + if (!init) + GFX_exit(EXIT_FAILURE,"GFX_Open() called before GFX_init()\n"); + + height=h; + width=w; + + win=XCreateWindow(disp,DefaultRootWindow(disp), + 0,0,width,height,0, + visual.depth, + InputOutput, + visual.visual, + 0,NULL); + + XSelectInput + (disp,win, + KeyPressMask|ButtonPressMask|ButtonReleaseMask| + PointerMotionMask|ExposureMask); + + XStoreName(disp,win,"viDOOM " VIDOOMVER VIDOOMRELEASE); + + hint.width=hint.min_width=hint.max_width=width; + hint.height=hint.min_height=hint.max_height=height; + hint.flags=PSize|PMinSize|PMaxSize; + + XSetWMNormalHints(disp,win,&hint); + + pix=XCreatePixmap(disp,win,width,height,visual.depth); + saved=XCreatePixmap(disp,win,width,height,visual.depth); + + curr_func=gc_val.function=GXcopy; + gc_val.plane_mask=AllPlanes; + gc_val.line_width=0; + gc_val.line_style=LineSolid; + gc_val.graphics_exposures=False; + gc_val.foreground=WhitePixel(disp,DefaultScreen(disp)); + gc_val.background=BlackPixel(disp,DefaultScreen(disp)); + + gc=XCreateGC(disp,pix, + GCFunction|GCPlaneMask|GCLineWidth|GCForeground|GCBackground| + GCLineStyle|GCGraphicsExposures, + &gc_val); + + GFX_clear(BLACK); + GFX_redraw(); + + XMapWindow(disp,win); +} + + +void GFX_clear(int col) +{ + if (curr_func!=GXcopy) + XSetFunction(disp,gc,GXcopy); + + setXCol(col); + XFillRectangle(disp,pix,gc,0,0,width,height); + + if (curr_func!=GXcopy) + XSetFunction(disp,gc,curr_func); + + dirty_min_x=0; + dirty_max_x=width; + dirty_min_y=0; + dirty_max_y=height; +} + + +void GFX_redraw(void) +{ + if (dirty_max_x<dirty_min_x) + return; + + if (curr_func!=GXcopy) + XSetFunction(disp,gc,GXcopy); + + dirty_min_x=MAX(0,dirty_min_x); + dirty_min_y=MAX(0,dirty_min_y); + dirty_max_x=MIN(width,dirty_max_x); + dirty_max_y=MIN(height,dirty_max_y); + + XCopyArea(disp,pix,win,gc, + dirty_min_x,dirty_min_y, + dirty_max_x-dirty_min_x+1,dirty_max_y-dirty_min_y+1, + dirty_min_x,dirty_min_y); + + if (curr_func!=GXcopy) + XSetFunction(disp,gc,curr_func); + + dirty_max_x=0; + dirty_min_x=width-1; + dirty_max_y=0; + dirty_min_y=width-1; + + XFlush(disp); +} + + +void GFX_line(int x1,int y1,int x2,int y2,int col) +{ + DIRTY(x1,y1); + DIRTY(x2,y2); + + setXCol(col); + XDrawLine(disp,pix,gc,x1,y1,x2,y2); +} + + +void GFX_plot(int x,int y,int col) +{ + DIRTY(x,y); + + setXCol(col); + XDrawPoint(disp,pix,gc,x,y); +} + + +void GFX_circle(int x,int y,int r,int col) +{ + DIRTY(x-r-1,y-r-1); + DIRTY(x+r+1,y+r+1); + + setXCol(col); + XDrawArc(disp,pix,gc,x-r,y-r,r*2,r*2,0,64*360); +} + + +void GFX_fcircle(int x,int y,int r,int col) +{ + DIRTY(x-r-1,y-r-1); + DIRTY(x+r+1,y+r+1); + + setXCol(col); + XFillArc(disp,pix,gc,x-r,y-r,r*2,r*2,0,64*360); +} + + +void GFX_rect(int x,int y,int w,int h,int col) +{ + if (w==0 && h==0) + { + GFX_plot(x,y,col); + return; + } + + w-=SGN(w); + h-=SGN(h); + + if (w<0) + { + x+=w; + w=-w; + } + + if (h<0) + { + y+=h; + h=-h; + } + + DIRTY(x,y); + DIRTY(x+w,y+h); + + setXCol(col); + XDrawRectangle(disp,pix,gc,x,y,w,h); +} + + +void GFX_frect(int x,int y,int w,int h,int col) +{ + if (w==0 && h==0) + { + GFX_plot(x,y,col); + return; + } + + if (w<0) + { + x+=(w+1); + w=-w; + } + + if (h<0) + { + y+=(h+1); + h=-h; + } + + DIRTY(x,y); + DIRTY(x+w,y+h); + + setXCol(col); + XFillRectangle(disp,pix,gc,x,y,w,h); +} + + +void GFX_set_XOR_mode(void) +{ + XSetFunction(disp,gc,curr_func=GXxor); +} + + +void GFX_clear_XOR_mode(void) +{ + XSetFunction(disp,gc,curr_func=GXcopy); +} + + +void GFX_print(int x,int y,int col,char *fmt,...) +{ + char s[1024]; + va_list va; + size_t len; + + va_start(va,fmt); + vsprintf(s,fmt,va); + va_end(va); + + len=strlen(s); + + y+=fh-FONTGAP; + + DIRTY(x,y); + DIRTY(x+fw*strlen(s),y+fh); + + setXCol(col); + XDrawString(disp,pix,gc,x,y,s,len); +} + + +int GFX_fh(void) +{ + return fh; +} + + +int GFX_fw(void) +{ + return fw; +} + + +int GFX_mouse_buttons(void) +{ + return mbuttons; +} + + +int GFX_mouse(int *x,int *y) +{ + Wait(X11_GFXall,FALSE); + + if (x) + *x=mouse_x; + + if (y) + *y=mouse_y; + + return mouse_b; +} + + +void GFX_waitkey(GFXKey *key) +{ + GFXKey ev; + + Wait(X11_GFXkey,TRUE); + GetKey(key ? key:&ev); +} + + +int GFX_key(GFXKey *key) +{ + Wait(X11_GFXall,FALSE); + + if (kb_head) + { + GetKey(key); + return TRUE; + } + else + return FALSE; +} + + +void GFX_bounce(void) +{ + GFXKey d; + + while (kb_head) + GetKey(&d); + + mouse_b=0; +} + + +void GFX_await_input(GFXEvent *ev) +{ + switch(Wait(X11_GFXkey|X11_GFXmouse,TRUE)) + { + case X11_GFXkey: + GetKey(&(ev->key)); + ev->type=GFX_KEY_EVENT; + break; + + case X11_GFXmouse: + ev->type=GFX_MOUSE_EVENT; + ev->mouse.shift=is_shift; + ev->mouse.ctrl=is_ctrl; + ev->mouse.alt=is_alt; + ev->mouse.x=mouse_x; + ev->mouse.y=mouse_y; + ev->mouse.b=mouse_b; + break; + + default: + break; + } +} + + +void GFX_await_input_full(GFXEvent *ev) +{ + switch(Wait(X11_GFXall,TRUE)) + { + case X11_GFXkey: + GetKey(&(ev->key)); + ev->type=GFX_KEY_EVENT; + break; + + case X11_GFXmouse: + case X11_GFXmouseMove: + ev->type=GFX_MOUSE_EVENT; + ev->mouse.shift=is_shift; + ev->mouse.ctrl=is_ctrl; + ev->mouse.alt=is_alt; + ev->mouse.x=mouse_x; + ev->mouse.y=mouse_y; + ev->mouse.b=mouse_b; + break; + + default: + break; + } +} + + +void GFX_exit(int code,char *fmt,...) +{ + va_list va; + + va_start(va,fmt); + vfprintf(stderr,fmt,va); + va_end(va); + + /* Be lazy, and leave the might of Unix to clear up, though may as well + let X know. + */ + if (disp) + XCloseDisplay(disp); + + exit(code); +} + + +GFX_IMAGE GFX_create_image(GFX_BITMAP *bm) +{ + GfxImage i; + char *data; + int x,y; + unsigned long pal[256]; + + data=Grab(bm->w*bm->h*4); + + i.p=XCreateImage(disp,visual.visual,visual.depth, + ZPixmap,0,data,bm->w,bm->h,32,4*bm->w); + + if (!i.p) + { + Release(data); + return NULL; + } + + for(x=0;x<256;x++) + pal[x]=getXCol(bm->pal[x]); + + i.w=bm->w; + i.h=bm->h; + + for(x=0;x<bm->w;x++) + for(y=0;y<bm->h;y++) + XPutPixel(i.p,x,y,pal[*(bm->data+x+y*bm->w)]); + + return Copy(&i,sizeof i); +} + + +void GFX_destroy_image(GFX_IMAGE img) +{ + GfxImage *p=img; + + if (p) + { + XDestroyImage(p->p); + Release(p); + } +} + + +void GFX_draw_image(GFX_IMAGE i, int x, int y) +{ + GfxImage *p=i; + + if (p) + { + if (curr_func!=GXcopy) + XSetFunction(disp,gc,GXcopy); + + XPutImage(disp,pix,gc,p->p,0,0,x,y,p->w,p->h); + + DIRTY(x,y); + DIRTY(x+p->w,y+p->h); + + if (curr_func!=GXcopy) + XSetFunction(disp,gc,curr_func); + } +} + + +void GFX_fill_screen(GFX_IMAGE i) +{ + GfxImage *p=i; + + if (p) + GFX_draw_image(i,(width-p->w)/2,(height-p->h)/2); +} + + +void GFX_save_screen(char *path) +{ +} + + +/* END OF FILE */ diff --git a/linux/main.c b/linux/main.c new file mode 100644 index 0000000..9cda67e --- /dev/null +++ b/linux/main.c @@ -0,0 +1,33 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Startup code + +*/ +static const char rcs_id[]="$Id$"; + +#include "vidoom.h" + +int main(int argc,char *argv[]) +{ + return viDOOM(argc,argv); +} diff --git a/linux/mem.c b/linux/mem.c new file mode 100644 index 0000000..a23b4ce --- /dev/null +++ b/linux/mem.c @@ -0,0 +1,107 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Memory allocation code + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" + +#include <stdio.h> +#include <string.h> + +#include "mem.h" +#include "gfx.h" + +void *FGrab(char *fn, int line, int len) +{ + char *ptr; + + if (len==0) + len=1; + + if (!(ptr=malloc(len))) + GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n" + "%s:%d Grab(%d)\n",fn,line,len); + + memset(ptr,0,len); + + return ptr; +} + + +void *FReGrab(char *fn, int line, void *ptr, int len) +{ + if (len==0) + len=1; + + if (!(ptr=realloc(ptr,len))) + GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n" + "%s:%d ReGrab(%d)\n",fn,line,len); + + return ptr; +} + + +void FRelease(char *fn, int line, void *p) +{ + free(p); +} + + +void *FCopy(char *fn, int line, void *p,int len) +{ + void *ptr; + + if (len==0) + ptr=malloc(1); + else + ptr=malloc(len); + + if (!ptr) + GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n" + "%s:%d Copy(%p,%d)\n",fn,line,p,len); + + if (len) + memcpy(ptr,p,len); + + return ptr; +} + + +char *FStrdup(char *fn, int line, char *p) +{ + char n_fn[PATH_MAX]; + char *ptr; + + if (!p) + return NULL; + + if (!(ptr=strdup(p))) + GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n" + "%s:%d Stdup(%s)\n",fn,line,p); + + return ptr; +} + +/* END OF FILE */ diff --git a/linux/platgui.c b/linux/platgui.c new file mode 100644 index 0000000..5fd9c74 --- /dev/null +++ b/linux/platgui.c @@ -0,0 +1,3703 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Platform specific GUI type routines + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" + +#include "platgui.h" +#include "gui.h" +#include "gfx.h" +#include "mem.h" +#include "file.h" +#include "ini.h" +#include "util.h" + +#include "debug.h" + +#include <stdio.h> +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <time.h> +#include <unistd.h> +#include <ctype.h> +#include <dirent.h> + + +/* ---------------------------------------- MACROS +*/ +#define CENTRE(x,y,c,t) GFX_print((x)-strlen(t)*FW/2,y,c,"%s",t) + +#define CENTREX(x,y,w,c,t) GFX_print(((x)+(w)/2)-strlen(t)*FW/2,y,c,"%s",t) +#define CENTREY(x,y,h,c,t) GFX_print((x),((y)+(h)/2)-FH/2,c,"%s",t) +#define CENTREXY(x,y,w,h,c,t) GFX_print(((x)+(w)/2)-strlen(t)*FW/2,\ + ((y)+(h)/2)-FH/2,c,"%s",t) +#define LENGTH(t) (strlen(t)*FW) + + +/* ---------------------------------------- CONFIG +*/ +static char GUI_EDITOR[PATH_MAX]=""; + + +static INI_Table ini_conf[]= + { + {INI_STR,"linux","edit",GUI_EDITOR,NULL}, + }; + +/* ---------------------------------------- VARS +*/ +static Display *disp=NULL; +static Window win=None; +static Pixmap pix=None; +static GC gc=None; + +static int SCRW,SCRH; +static int FW,FH; + +static unsigned long hi_col; +static unsigned long mid_col; +static unsigned long lo_col; +static unsigned long txt_col; +static unsigned long shadow_col; +static unsigned long bold_col; + +static const int BEVEL=2; +static const int SMALL_BEVEL=1; + +#define LIST_SCROLLSIZE 20 +#define LIST_TEXTHEIGHT (FH+(FH>>1)) + +/* Internal representation used for GFX_IMAGE + + IMPORTANT: This *MUST* match the definition in linux/gfx.c + +*/ +typedef struct + { + int w,h; + XImage *p; + } GfxImage; + + +/* Vars for GUI_yesno_all() +*/ +static int all_pressed=FALSE; +static int all_result=FALSE; + +/* Event Vars +*/ +static int mouse_x=0; +static int mouse_y=0; +static int mouse_b=0; +static int last_mouse_b=0; +static int is_key=FALSE; +static int key=0; +static int shift=0; +static int ctrl=0; +static char ascii=0; + +/* 'Hidden' interfaces to gfx.c +*/ +extern void VIDOOM_GET_GFX_VARS(Display **ret_disp, + Window *ret_win, + Pixmap *ret_pix, + GC *ret_gc); +extern void VIDOOM_GFX_FORCE_REDRAW(void); +extern void VIDOOM_GFX_SAVE_DISPLAY(void); +extern void VIDOOM_GFX_RESTORE_DISPLAY(void); +extern Pixmap VIDOOM_GFX_SAVE_UNDER(int x, int y, int w, int h); +extern void VIDOOM_GFX_RESTORE_UNDER(Pixmap p, int x, int y, int w, int h); +extern void VIDOOM_GFX_WAIT_FOR(pid_t pid, int *status); + + +/* Types to define GUI objects. Note picklist done as an image list with + no images. +*/ +typedef enum {GuiBox, + GuiLabel, + GuiButton, + GuiText, + GuiToggle, + GuiRadio, + GuiImageList, + GuiFileList, + GuiTextEdit} ObjType; + +typedef enum {GuiKeyPress, + GuiButtonPress, + GuiWheelUp, + GuiWheelDown, + GuiDraw, + GuiExit} ObjEvent; + +typedef enum {GuiOk, + GuiDone, + GuiRedraw} CallbackReturn; + +typedef enum {GuiTextString, + GuiTextInteger, + GuiTextDouble, + GuiTextReadOnly} GuiTextMode; + +typedef struct + { + int x,y,w,h; + } BoundBox; + +typedef struct + { + ObjType type; + BoundBox box; + int focus; + int take_focus; + } GuiObjCommon; + +typedef struct + { + GuiObjCommon common; + const char *title; + int invert; + GfxImage *img; + } GuiObjBox; + +typedef struct + { + GuiObjCommon common; + const char *text; + int col; + int centre; + } GuiObjLabel; + +typedef struct + { + GuiObjCommon common; + const char *text; + } GuiObjButton; + +typedef struct + { + GuiObjCommon common; + char text[PATH_MAX+1]; + GuiTextMode mode; + int len; + int no_chars; + int first; + int curs; + int allow_exit; + } GuiObjText; + +typedef struct + { + GuiObjCommon common; + const char *text; + int state; + } GuiObjToggle; + +typedef struct + { + GuiObjCommon common; + int state; + const char *text; + int group; + } GuiObjRadio; + +typedef struct + { + GuiObjCommon common; + int curr; + int img_box; + int no; + char **items; + GFX_IMAGE *img; + int no_lines; + int top; + double sbar_step; + int scrollbar; + BoundBox scroll; + BoundBox bar; + int double_click; + } GuiObjImageList; + +typedef struct + { + char name[PATH_MAX+1]; + int is_dir; + off_t size; + } FileEnt; + +typedef struct + { + GuiObjCommon common; + int curr; + int no; + FileEnt *ent; + char dir[PATH_MAX+1]; + char *filter; + int no_lines; + int top; + double sbar_step; + int scrollbar; + BoundBox scroll; + BoundBox bar; + int double_click; + int path_text; + int file_text; + int name_width; + int size_width; + } GuiObjFileList; + +#define TE_CHUNK 256 + +typedef struct + { + int len; + char *p; + } TextEditLine; + +typedef struct + { + GuiObjCommon common; + TextEditLine *tline; + int top; + int x,y; + int col; + int lines; + int width; + int height; + int info; + int read_only; + } GuiObjTextEdit; + +typedef union + { + GuiObjCommon common; + GuiObjBox box; + GuiObjLabel lbl; + GuiObjButton but; + GuiObjText txt; + GuiObjToggle tog; + GuiObjRadio rad; + GuiObjImageList pck; + GuiObjFileList fle; + GuiObjTextEdit edt; + } GuiObj; + +static int focus=-1; + +static int dialog_no; +static GuiObj *dialog=NULL; + + +/* Types to define menus +*/ +typedef enum {MENU_OK, + MENU_CANCEL_ESC, + MENU_CANCEL_CLICK, + MENU_CHILD_RETURN} MenuStatus; + +typedef struct + { + MenuStatus mode; + int ret; + } MenuControl; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static void ExecProgram(char *prog_in, char *file) +{ + char prog[PATH_MAX]; + char *arg[PATH_MAX]; + int no; + int f; + + strcpy(prog,prog_in); + + no=0; + + arg[no]=strtok(prog," "); + + while (arg[no]) + arg[++no]=strtok(NULL," "); + + for(f=0;f<no;f++) + if (strcmp(arg[f],"%")==0) + arg[f]=file; + + execvp(arg[0],arg); +} + + +static unsigned long XCol(int col) +{ + XColor pix; + + pix.red=((col&0xff0000)>>8)|((col&0xff0000)>>16); + pix.green=(col&0xff00)|((col&0xff00)>>8); + pix.blue=((col&0xff)<<8)|(col&0xff); + pix.flags=DoRed|DoBlue|DoGreen; + + XAllocColor(disp,DefaultColormap(disp,DefaultScreen(disp)),&pix); + + return pix.pixel; +} + + +static void DumpBox(const char *title, BoundBox b) +{ + Debug(("%s.x=%d\n",title,b.x)); + Debug(("%s.y=%d\n",title,b.y)); + Debug(("%s.w=%d\n",title,b.w)); + Debug(("%s.h=%d\n",title,b.h)); +} + + +static int MouseInBox(BoundBox *box, int no) +{ + int f; + + for(f=0;f<no;f++) + if (((mouse_x>=box[f].x)&&(mouse_x<box[f].x+box[f].w))&& + ((mouse_y>=box[f].y)&&(mouse_y<box[f].y+box[f].h))) + return f; + + return -1; +} + + +static int IsInBox(BoundBox *box) +{ + if (((mouse_x>=box->x)&&(mouse_x<box->x+box->w))&& + ((mouse_y>=box->y)&&(mouse_y<box->y+box->h))) + return TRUE; + + return FALSE; +} + + +static void Rect3D(int x,int y,int w,int h, int invert,int bevel) +{ + int f; + int mid,lo,hi; + + mid=GUI_MID; + + if (invert) + { + lo=GUI_HI; + hi=GUI_LO; + } + else + { + hi=GUI_HI; + lo=GUI_LO; + } + + GFX_frect(x,y,w,h,mid); + + for(f=0;f<bevel;f++) + { + GFX_line(x+f,y+f,x+f,y+h-1-f,hi); + GFX_line(x+w-1-f,y+f,x+w-1-f,y+h-1-f,lo); + + GFX_line(x+f,y+f,x+w-1-f,y+f,hi); + GFX_line(x+f,y+h-1-f,x+w-1-f,y+h-1-f,lo); + } +} + + +static void Rect3DBox(BoundBox *b, int invert, int bevel) +{ + Rect3D(b->x,b->y,b->w,b->h,invert,bevel); +} + + +static void Tickbox(int x,int y,int w,int h, int pick) +{ + int f; + + if (pick) + GFX_frect(x,y,w,h,BLACK); + else + GFX_frect(x,y,w,h,WHITE); + + + for(f=0;f<SMALL_BEVEL;f++) + { + GFX_line(x+f,y+f,x+f,y+h-1-f,GUI_LO); + GFX_line(x+w-1-f,y+f,x+w-1-f,y+h-1-f,GUI_HI); + + GFX_line(x+f,y+f,x+w-1-f,y+f,GUI_LO); + GFX_line(x+f,y+h-1-f,x+w-1-f,y+h-1-f,GUI_HI); + } +} + + +static void GetEvent(void) +{ + GFXEvent e; + + GFX_await_input_full(&e); + + switch(e.type) + { + case GFX_KEY_EVENT: + is_key=TRUE; + key=e.key.code; + shift=e.key.shift; + ascii=e.key.ascii; + ctrl=e.key.ctrl; + break; + + case GFX_MOUSE_EVENT: + is_key=FALSE; + last_mouse_b=mouse_b; + mouse_x=e.mouse.x; + mouse_y=e.mouse.y; + mouse_b=e.mouse.b; + break; + } +} + + +static void EventTidy() +{ + is_key=FALSE; + last_mouse_b=0; + + while(mouse_b) + GetEvent(); + + GFX_bounce(); +} + + +static void ChopLines(char *text, char *line[],int *no,int *max) +{ + char *p; + int f; + + *no=0; + *max=0; + p=text; + + line[(*no)++]=p; + + while(*p) + { + if (*p=='|') + { + *p++=0; + + if (*p) + line[(*no)++]=p; + } + else + p++; + } + + for(f=0;f<*no;f++) + *max=MAX(LENGTH(line[f]),*max); +} + + +/* ---------------------------------------- DIALOG CREATION FUNCTIONS +*/ +static void InitDial(void) +{ + if (dialog) + { + Release(dialog); + dialog=NULL; + } + + focus=-1; + dialog_no=0; +} + + +static void ResizeBox(int i) +{ + int f; + int x1,y1,x2,y2; + + x1=9999; + y1=9999; + x2=-9999; + y2=-9999; + + for(f=0;f<dialog_no;f++) + if (f!=i) + { + x1=MIN(x1,dialog[f].common.box.x); + y1=MIN(y1,dialog[f].common.box.y); + + x2=MAX(x2,dialog[f].common.box.x+dialog[f].common.box.w); + y2=MAX(y2,dialog[f].common.box.y+dialog[f].common.box.h); + } + + x1-=BEVEL*3; + y1-=BEVEL*3; + x2+=BEVEL*3; + y2+=BEVEL*3; + + if (dialog[i].box.title) + y1-=FH*2; + + dialog[i].common.box.x=x1; + dialog[i].common.box.y=y1; + dialog[i].common.box.w=x2-x1; + dialog[i].common.box.h=y2-y1; +} + + +static void DialCommon(int no, ObjType t, int x, int y, int w, int h, int focus) +{ + dialog=ReGrab(dialog,sizeof(GuiObj)*(no+1)); + + dialog[no].common.type=t; + dialog[no].common.box.x=x; + dialog[no].common.box.y=y; + dialog[no].common.box.w=w; + dialog[no].common.box.h=h; + dialog[no].common.take_focus=focus; + dialog[no].common.focus=FALSE; +} + + +static int CreateBox(int x, int y, int w, int h, const char *title, int invert) +{ + int no=dialog_no++; + + DialCommon(no,GuiBox,x,y,w,h,FALSE); + + dialog[no].box.title=title; + dialog[no].box.invert=invert; + dialog[no].box.img=NULL; + + return no; +} + + +static int CreateLabel(int x, int y, int w, int h, const char *label, + int col,int centre) +{ + int no=dialog_no++; + + if (w==-1) + w=LENGTH(label); + + DialCommon(no,GuiLabel,x,y,w,h,FALSE); + + dialog[no].lbl.text=label; + dialog[no].lbl.col=col; + dialog[no].lbl.centre=centre; + + return no; +} + + +static int CreateButton(int x, int y, int w, int h, const char *label) +{ + int no=dialog_no++; + + DialCommon(no,GuiButton,x,y,w,h,TRUE); + + dialog[no].lbl.text=label; + + return no; +} + + +static int CreateToggle(int x, int y, int w, int h, + const char *label, int state) +{ + int no=dialog_no++; + + DialCommon(no,GuiToggle,x,y,w,h,TRUE); + + dialog[no].tog.text=label; + dialog[no].tog.state=state; + + return no; +} + + +static int CreateRadio(int x, int y, int w, int h, + const char *label, int group, int state) +{ + int no=dialog_no++; + + DialCommon(no,GuiRadio,x,y,w,h,TRUE); + + dialog[no].rad.text=label; + dialog[no].rad.group=group; + dialog[no].rad.state=state; + + return no; +} + + +static int CreateText(int x, int y, int w, int h, char *text, + GuiTextMode mode, int allow_exit) +{ + int no=dialog_no++; + + DialCommon(no,GuiText,x,y,w,h,mode!=GuiTextReadOnly); + + strcpy(dialog[no].txt.text,text); + + dialog[no].txt.len=strlen(text); + dialog[no].txt.no_chars=(w-BEVEL*2)/FW; + dialog[no].txt.curs=dialog[no].txt.len; + dialog[no].txt.mode=mode; + dialog[no].txt.allow_exit=allow_exit; + + dialog[no].txt.first=MAX(0,dialog[no].txt.len-dialog[no].txt.no_chars); + + return no; +} + + +static void ResetText(int no, char *text) +{ + if (dialog[no].common.type!=GuiText) + return; + + strcpy(dialog[no].txt.text,text); + + dialog[no].txt.len=strlen(text); + dialog[no].txt.curs=dialog[no].txt.len; + dialog[no].txt.first=MAX(0,dialog[no].txt.len-dialog[no].txt.no_chars); +} + + +static int CreateImageList(int x, int y, int w, int h, + int no_items, char *items[], + GFX_IMAGE img[], int curr, int img_box, + int allow_double_click) +{ + int no=dialog_no++; + + DialCommon(no,GuiImageList,x,y,w,h,TRUE); + + dialog[no].pck.curr=curr; + dialog[no].pck.no=no_items; + dialog[no].pck.img_box=img_box; + dialog[no].pck.items=items; + dialog[no].pck.img=img; + dialog[no].pck.double_click=allow_double_click; + + dialog[no].pck.no_lines=h/LIST_TEXTHEIGHT; + + if (curr>dialog[no].pck.no_lines) + dialog[no].pck.top=curr; + else + dialog[no].pck.top=0; + + dialog[no].pck.scroll.x=x+w-BEVEL-LIST_SCROLLSIZE; + dialog[no].pck.scroll.y=y+BEVEL; + dialog[no].pck.scroll.w=LIST_SCROLLSIZE; + dialog[no].pck.scroll.h=h-BEVEL*2+1; + + if (no_items>dialog[no].pck.no_lines) + { + dialog[no].pck.scrollbar=TRUE; + + dialog[no].pck.sbar_step=(double)h/(double)no_items; + dialog[no].pck.bar.x=dialog[no].pck.scroll.x; + dialog[no].pck.bar.y=dialog[no].pck.scroll.y; + dialog[no].pck.bar.w=dialog[no].pck.scroll.w; + dialog[no].pck.bar.h= + (dialog[no].pck.sbar_step*dialog[no].pck.no_lines+0.5); + } + else + { + dialog[no].pck.scrollbar=FALSE; + + dialog[no].pck.bar=dialog[no].pck.scroll; + } + + return no; +} + + +static int CreateFileList(int x, int y, int w, int h, + char *path, char *filter, + int path_text, int file_text, + int allow_double_click) +{ + int no=dialog_no++; + int charw; + + DialCommon(no,GuiFileList,x,y,w,h,TRUE); + + dialog[no].fle.ent=NULL; + strcpy(dialog[no].fle.dir,path); + dialog[no].fle.filter=filter; + dialog[no].fle.path_text=path_text; + dialog[no].fle.file_text=file_text; + dialog[no].fle.double_click=allow_double_click; + + dialog[no].fle.no_lines=h/LIST_TEXTHEIGHT; + + dialog[no].fle.scroll.x=x+w-BEVEL-LIST_SCROLLSIZE; + dialog[no].fle.scroll.y=y+BEVEL; + dialog[no].fle.scroll.w=LIST_SCROLLSIZE; + dialog[no].fle.scroll.h=h-BEVEL*2+1; + + charw=(w-BEVEL*2-LIST_SCROLLSIZE)/FW; + + dialog[no].fle.size_width=12; + dialog[no].fle.name_width=charw-dialog[no].fle.size_width; + + ResetText(path_text,path); + ResetText(file_text,""); + + chdir(path); + + return no; +} + +static int CreateTextEdit(int x, int y, int w, int h, + TextEditLine *lines, int no_lines, int info, + int read_only) +{ + static void TextEditInfo(GuiObjTextEdit *o); + int no=dialog_no++; + + DialCommon(no,GuiTextEdit,x,y,w,h,TRUE); + + dialog[no].edt.top=0; + dialog[no].edt.x=0; + dialog[no].edt.y=0; + dialog[no].edt.col=0; + + dialog[no].edt.tline=lines; + dialog[no].edt.lines=no_lines; + + dialog[no].edt.info=info; + dialog[no].edt.read_only=read_only; + + dialog[no].edt.width=w/FW; + dialog[no].edt.height=h/FH; + + TextEditInfo(&dialog[no].edt); + + return no; +} + + +/* ---------------------------------------- DIALOG CALLBACK FUNCTIONS +*/ +static CallbackReturn Callback(GuiObj *o, ObjEvent ev) +{ + static CallbackReturn BoxCallback(GuiObj *o ,ObjEvent ev); + static CallbackReturn LabelCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn ButtonCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn ToggleCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn RadioCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn ImageListCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn TextCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn FileListCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn TextEditCallback(GuiObj *o,ObjEvent ev); + + switch(o->common.type) + { + case GuiBox: + return BoxCallback(o,ev); + case GuiLabel: + return LabelCallback(o,ev); + case GuiButton: + return ButtonCallback(o,ev); + case GuiToggle: + return ToggleCallback(o,ev); + case GuiRadio: + return RadioCallback(o,ev); + case GuiImageList: + return ImageListCallback(o,ev); + case GuiText: + return TextCallback(o,ev); + case GuiFileList: + return FileListCallback(o,ev); + case GuiTextEdit: + return TextEditCallback(o,ev); + default: + return GuiOk; + } +} + + +static CallbackReturn BoxCallback(GuiObj *o ,ObjEvent ev) +{ + GuiObjBox *b; + + b=&(o->box); + + switch (ev) + { + case GuiDraw: + if (!b->img) + { + Rect3DBox(&b->common.box,b->invert,BEVEL); + + if (b->title) + { + int x,y,w; + + x=b->common.box.x+BEVEL*2; + y=b->common.box.y+BEVEL; + w=b->common.box.w-BEVEL*4; + + CENTREX(x,y,w,GUI_TEXTBOLD,b->title); + + GFX_line(x,y+FH+FH/2,x+w,y+FH+FH/2,GUI_HI); + GFX_line(x,y+FH+FH/2+1,x+w,y+FH+FH/2+1,GUI_LO); + } + } + else + { + Rect3DBox(&b->common.box,b->invert,BEVEL); + + GFX_draw_image(b->img, + b->common.box.x+b->common.box.w/2-b->img->w/2, + b->common.box.y+b->common.box.h/2-b->img->h/2); + } + break; + + case GuiKeyPress: + break; + + case GuiButtonPress: + break; + + case GuiWheelUp: + break; + + case GuiWheelDown: + break; + + case GuiExit: + break; + } + return GuiOk; +} + + +static CallbackReturn LabelCallback(GuiObj *o ,ObjEvent ev) +{ + GuiObjLabel *l; + + l=&(o->lbl); + + switch (ev) + { + case GuiDraw: + GFX_frect(l->common.box.x,l->common.box.y, + l->common.box.w,l->common.box.h,GUI_MID); + + if (l->centre) + CENTREXY(l->common.box.x,l->common.box.y, + l->common.box.w,l->common.box.h,l->col,l->text); + else + CENTREY(l->common.box.x,l->common.box.y, + l->common.box.h,l->col,l->text); + break; + + case GuiKeyPress: + break; + + case GuiButtonPress: + break; + + case GuiWheelUp: + break; + + case GuiWheelDown: + break; + + case GuiExit: + break; + } + return GuiOk; +} + + +static CallbackReturn ButtonCallback(GuiObj *o ,ObjEvent ev) +{ + GuiObjButton *b; + + b=&(o->but); + + switch (ev) + { + case GuiDraw: + if (b->common.focus) + GFX_rect(b->common.box.x,b->common.box.y, + b->common.box.w,b->common.box.h,GUI_TEXTBOLD); + else + GFX_rect(b->common.box.x,b->common.box.y, + b->common.box.w,b->common.box.h,GUI_MID); + + Rect3D(b->common.box.x+1,b->common.box.y+1, + b->common.box.w-2,b->common.box.h-2, + FALSE,BEVEL); + + CENTREXY(b->common.box.x,b->common.box.y, + b->common.box.w,b->common.box.h,GUI_TEXTBOLD,b->text); + break; + + case GuiKeyPress: + if (key!=GFX_ENTER) + break; + + /* CAUTION: Case above runs into this + */ + case GuiButtonPress: + Rect3D(b->common.box.x+1,b->common.box.y+1, + b->common.box.w-2,b->common.box.h-2, + TRUE,BEVEL); + + CENTREXY(b->common.box.x,b->common.box.y, + b->common.box.w,b->common.box.h,GUI_TEXTBOLD,b->text); + + return GuiDone; + break; + + case GuiWheelUp: + break; + + case GuiWheelDown: + break; + + case GuiExit: + break; + } + + return GuiOk; +} + + +static CallbackReturn ToggleCallback(GuiObj *op ,ObjEvent ev) +{ + GuiObjToggle *o; + int x,y,w,h; + + o=&(op->tog); + + x=o->common.box.x; + y=o->common.box.y; + w=o->common.box.h; + h=o->common.box.h; + + switch (ev) + { + case GuiDraw: + if (o->common.focus) + GFX_rect(x,y,w,h,GUI_TEXTBOLD); + else + GFX_rect(x,y,w,h,GUI_MID); + + CENTREY(o->common.box.x+o->common.box.h+FW,o->common.box.y, + o->common.box.h,GUI_TEXT,o->text); + + Tickbox(x+1,y+1,w-2,h-2,o->state); + + break; + + case GuiKeyPress: + if (key!=GFX_ENTER && ascii!=' ') + break; + + /* CAUTION: Case above runs into this + */ + case GuiButtonPress: + o->state=!o->state; + return GuiRedraw; + break; + + case GuiWheelUp: + break; + + case GuiWheelDown: + break; + + case GuiExit: + break; + } + + return GuiOk; +} + + +static CallbackReturn RadioCallback(GuiObj *op ,ObjEvent ev) +{ + GuiObjRadio *o; + int x,y,w,h; + + o=&(op->rad); + + x=o->common.box.x; + y=o->common.box.y; + w=o->common.box.h; + h=o->common.box.h; + + switch (ev) + { + case GuiDraw: + if (o->common.focus) + GFX_rect(x,y,w,h,GUI_TEXTBOLD); + else + GFX_rect(x,y,w,h,GUI_MID); + + CENTREY(o->common.box.x+o->common.box.h+FW,o->common.box.y, + o->common.box.h,GUI_TEXT,o->text); + + Tickbox(x+1,y+1,w-2,h-2,o->state); + + break; + + case GuiKeyPress: + if (key!=GFX_ENTER && ascii!=' ') + break; + + /* CAUTION: Case above runs into this + */ + case GuiButtonPress: + if (!o->state) + { + int f; + + for(f=0;f<dialog_no;f++) + if (dialog[f].common.type==GuiRadio && + dialog[f].rad.group==o->group) + dialog[f].rad.state=FALSE; + + o->state=TRUE; + + return GuiRedraw; + } + break; + + case GuiWheelUp: + break; + + case GuiWheelDown: + break; + + case GuiExit: + break; + } + + return GuiOk; +} + + +static CallbackReturn HandleImageListPage(GuiObjImageList *o, int keycode) +{ + switch(keycode) + { + case GFX_UP: + case GFX_PGUP: + if (o->curr>0) + { + o->curr=MAX(0,o->curr-(key==GFX_UP ? 1:o->no_lines)); + + if (o->curr<o->top) + o->top=o->curr; + + if ((o->top+o->no_lines)<=o->curr) + o->top=o->curr-o->no_lines+1; + + return GuiRedraw; + } + break; + + case GFX_DOWN: + case GFX_PGDN: + if (o->curr<(o->no-1)) + { + o->curr=MIN(o->no-1,o->curr+ + (key==GFX_DOWN ? 1:o->no_lines)); + + if (o->curr<o->top) + o->top=o->curr; + + if ((o->top+o->no_lines)<=o->curr) + o->top=o->curr-o->no_lines+1; + + return GuiRedraw; + } + break; + + default: + break; + } + + return GuiOk; +} + + +static CallbackReturn ImageListCallback(GuiObj *op ,ObjEvent ev) +{ + static time_t last=0; + GuiObjImageList *o; + int x,y,w,h; + int sx,sy,sw,sh; + int f; + + o=&(op->pck); + + x=o->common.box.x+BEVEL; + y=o->common.box.y+BEVEL; + w=o->common.box.w-BEVEL*2; + h=o->common.box.h-BEVEL*2; + + sx=o->scroll.x; + sy=o->scroll.y; + sw=o->scroll.w; + sh=o->scroll.h; + + switch (ev) + { + case GuiDraw: + if (o->common.focus) + GFX_rect(x-BEVEL-1,y-BEVEL-1, + w+BEVEL*2+2,h+BEVEL*2+2,GUI_TEXTBOLD); + else + GFX_rect(x-BEVEL-1,y-BEVEL-1, + w+BEVEL*2+2,h+BEVEL*2+2,GUI_MID); + + Rect3DBox(&o->common.box,TRUE,BEVEL); + + GFX_frect(x,y,w,h,WHITE); + + GFX_frect(sx,sy,sw,sh,GUI_MID); + + o->bar.y=sy+o->sbar_step*o->top; + Rect3DBox(&o->bar,FALSE,SMALL_BEVEL); + + for(f=0;f<o->no_lines;f++) + { + int col; + int p=f+o->top; + int tx=x; + int ty=y+(f*LIST_TEXTHEIGHT); + int tw=w-sw; + int th=LIST_TEXTHEIGHT; + + if (p==o->curr) + { + col=WHITE; + GFX_frect(tx,ty,tw,th,BLACK); + } + else + col=BLACK; + + if (p<o->no) + CENTREY(tx,ty,th,col,o->items[p]); + } + + if (o->img) + { + dialog[o->img_box].box.img=o->img[o->curr]; + Callback(dialog+o->img_box,GuiDraw); + } + + break; + + case GuiKeyPress: + switch(key) + { + case GFX_UP: + case GFX_PGUP: + case GFX_DOWN: + case GFX_PGDN: + return HandleImageListPage(o,key); + break; + + case GFX_ENTER: + return GuiDone; + break; + + default: + break; + } + + break; + + case GuiButtonPress: + if (IsInBox(&o->scroll)) + { + if (!o->scrollbar) + return GuiOk; + + if (IsInBox(&o->bar)) + { + int orig; + int y; + double dtop; + + orig=mouse_y; + dtop=o->top; + + while (GFX_mouse(NULL,&y)==GFX_BUTLEFT) + { + int diff; + + diff=y-orig; + + dtop+=diff/o->sbar_step; + + o->top=(int)dtop; + + o->top=MAX(0,o->top); + o->top=MIN(o->no-o->no_lines,o->top); + + Callback(op,GuiDraw); + GFX_redraw(); + + orig=y; + } + } + else + { + if (mouse_y<o->bar.y) + o->top=MAX(0,o->top-o->no_lines); + else + o->top=MIN(o->no-1-o->no_lines,o->top+o->no_lines); + + return GuiRedraw; + } + } + else + { + int my; + int pick; + + my=mouse_y-y; + + pick=o->top+my/LIST_TEXTHEIGHT; + + if (pick<o->no) + { + if (pick==o->curr && (time(NULL)-last)<2 && + o->double_click) + return GuiDone; + + last=time(NULL); + + o->curr=pick; + return GuiRedraw; + } + } + break; + + case GuiWheelUp: + return HandleImageListPage(o,GFX_PGUP); + break; + + case GuiWheelDown: + return HandleImageListPage(o,GFX_PGDN); + break; + + case GuiExit: + break; + } + + return GuiOk; +} + + +static CallbackReturn TextCallback(GuiObj *op ,ObjEvent ev) +{ + int x,y,w,h; + GuiObjText *o; + int f; + int ok; + + o=&(op->txt); + + x=o->common.box.x+BEVEL; + y=o->common.box.y+BEVEL; + w=o->common.box.w-BEVEL*2; + h=o->common.box.h-BEVEL*2; + + switch (ev) + { + case GuiDraw: + if (o->common.focus) + GFX_rect(x-BEVEL-1,y-BEVEL-1, + w+BEVEL*2+2,h+BEVEL*2+2,GUI_TEXTBOLD); + else + GFX_rect(x-BEVEL-1,y-BEVEL-1, + w+BEVEL*2+2,h+BEVEL*2+2,GUI_MID); + + Rect3DBox(&o->common.box,TRUE,BEVEL); + + if (o->mode!=GuiTextReadOnly) + GFX_frect(x,y,w,h,WHITE); + + for(f=0;f<o->no_chars;f++) + { + int col; + int pos; + int tx; + + tx=x+f*FW; + pos=o->first+f; + + if (o->common.focus) + { + if (pos==o->curs) + { + GFX_frect(tx,y,FW,FH,BLACK); + col=WHITE; + } + else + col=BLACK; + } + else + col=BLACK; + + if (pos<o->len) + GFX_print(tx,y,col,"%c",o->text[pos]); + + tx+=FW; + } + + break; + + case GuiKeyPress: + switch(key) + { + case GFX_LEFT: + o->curs--; + o->curs=MAX(0,o->curs); + o->curs=MIN(o->len,o->curs); + + if (o->curs<o->first) + o->first--; + + return GuiRedraw; + break; + + case GFX_RIGHT: + o->curs++; + o->curs=MIN(o->len,o->curs); + + if (o->curs>(o->first+o->no_chars-1)) + o->first++; + + return GuiRedraw; + break; + + case GFX_ENTER: + if (o->allow_exit) + return GuiDone; + break; + + case GFX_BACKSPACE: + case GFX_DELETE: + if (o->curs) + { + memmove(o->text+o->curs-1,o->text+o->curs, + o->len-o->curs+1); + + if (--o->curs<o->first) + o->first--; + + o->len=strlen(o->text); + + return GuiRedraw; + } + break; + + case GFX_END: + o->curs=o->len; + o->first=MAX(0,o->len-o->no_chars-1); + return GuiRedraw; + break; + + case GFX_HOME: + o->curs=0; + o->first=0; + return GuiRedraw; + break; + + default: + switch(o->mode) + { + case GuiTextString: + ok=isprint(ascii); + break; + case GuiTextInteger: + if (strchr("-+0123456789",ascii)) + ok=TRUE; + else + ok=FALSE; + break; + case GuiTextDouble: + if (strchr(".-+0123456789",ascii)) + ok=TRUE; + else + ok=FALSE; + break; + case GuiTextReadOnly: + ok=FALSE; + break; + default: + ok=FALSE; + break; + } + + if (ok && o->len<PATH_MAX) + { + if (o->curs==o->len) + { + o->text[o->curs++]=ascii; + o->text[o->curs]=0; + } + else + { + memmove(o->text+o->curs+1,o->text+o->curs, + o->len-o->curs+1); + o->text[o->curs++]=ascii; + } + + o->len=strlen(o->text); + + if (o->curs>(o->first+o->no_chars-1)) + o->first++; + + return GuiRedraw; + } + + break; + } + + break; + + case GuiButtonPress: + break; + + case GuiWheelUp: + break; + + case GuiWheelDown: + break; + + case GuiExit: + break; + } + + return GuiOk; +} + + +static CallbackReturn HandleFileListPage(GuiObjFileList *o, int keycode) +{ + switch(keycode) + { + case GFX_UP: + case GFX_PGUP: + if (o->curr>0) + { + o->curr=MAX(0,o->curr-(key==GFX_UP ? 1:o->no_lines)); + + if (o->curr<o->top) + o->top=o->curr; + + if ((o->top+o->no_lines)<=o->curr) + o->top=o->curr-o->no_lines+1; + } + break; + + case GFX_DOWN: + case GFX_PGDN: + if (o->curr<(o->no-1)) + { + o->curr=MIN(o->no-1,o->curr+ + (key==GFX_DOWN ? 1:o->no_lines)); + + if (o->curr<o->top) + o->top=o->curr; + + if ((o->top+o->no_lines)<=o->curr) + o->top=o->curr-o->no_lines+1; + } + break; + + default: + break; + } + + if (o->ent[o->curr].is_dir) + ResetText(o->file_text,""); + else + ResetText(o->file_text,o->ent[o->curr].name); + + return GuiRedraw; +} + + +static int SortFiles(const void *a, const void *b) +{ + const FileEnt *fa,*fb; + + fa=a; + fb=b; + + if (fa->is_dir && !fb->is_dir) + return -1; + else if (!fa->is_dir && fb->is_dir) + return 1; + else + return strcmp(fa->name,fb->name); +} + + +static int FilesMatch(const char *file, const char *filter) +{ + if (!filter) + return TRUE; + + if (strlen(file)<strlen(filter)) + return FALSE; + + if (strcasecmp(file+strlen(file)-strlen(filter),filter)) + return FALSE; + else + return TRUE; +} + + +static void GetFileList(GuiObjFileList *o) +{ + DIR *d; + int l; + + if (o->ent) + Release(o->ent); + + o->ent=NULL; + o->no=0; + o->curr=0; + o->top=0; + + getcwd(o->dir,PATH_MAX); + + if (o->dir[l=(strlen(o->dir)-1)]!='/') + { + o->dir[++l]='/'; + o->dir[++l]=0; + } + + if ((d=opendir(o->dir))) + { + struct dirent *ent; + struct stat sbuf; + + while ((ent=readdir(d))) + { + if (!stat(ent->d_name,&sbuf)) + if (S_ISDIR(sbuf.st_mode) || FilesMatch(ent->d_name,o->filter)) + { + o->no++; + o->ent=ReGrab(o->ent,sizeof(FileEnt)*o->no); + + strcpy(o->ent[o->no-1].name,ent->d_name); + o->ent[o->no-1].is_dir=S_ISDIR(sbuf.st_mode); + o->ent[o->no-1].size=sbuf.st_size; + } + } + + closedir(d); + } + + ResetText(o->file_text,""); + ResetText(o->path_text,o->dir); + + qsort(o->ent,o->no,sizeof(FileEnt),SortFiles); + + if (o->no>o->no_lines) + { + o->scrollbar=TRUE; + + o->sbar_step=(double)o->common.box.h/(double)o->no; + o->bar.x=o->scroll.x; + o->bar.y=o->scroll.y; + o->bar.w=o->scroll.w; + o->bar.h=(o->sbar_step*o->no_lines+0.5); + } + else + { + o->scrollbar=FALSE; + o->bar=o->scroll; + } +} + + +static CallbackReturn FileListCallback(GuiObj *op ,ObjEvent ev) +{ + static time_t last=0; + static char buff[PATH_MAX+10]; + GuiObjFileList *o; + int x,y,w,h; + int sx,sy,sw,sh; + int f; + + o=&(op->fle); + + if (!o->ent) + GetFileList(o); + + x=o->common.box.x+BEVEL; + y=o->common.box.y+BEVEL; + w=o->common.box.w-BEVEL*2; + h=o->common.box.h-BEVEL*2; + + sx=o->scroll.x; + sy=o->scroll.y; + sw=o->scroll.w; + sh=o->scroll.h; + + switch (ev) + { + case GuiDraw: + if (o->common.focus) + GFX_rect(x-BEVEL-1,y-BEVEL-1, + w+BEVEL*2+2,h+BEVEL*2+2,GUI_TEXTBOLD); + else + GFX_rect(x-BEVEL-1,y-BEVEL-1, + w+BEVEL*2+2,h+BEVEL*2+2,GUI_MID); + + Rect3DBox(&o->common.box,TRUE,BEVEL); + + GFX_frect(x,y,w,h,WHITE); + + GFX_frect(sx,sy,sw,sh,GUI_MID); + + o->bar.y=sy+o->sbar_step*o->top; + Rect3DBox(&o->bar,FALSE,SMALL_BEVEL); + + for(f=0;f<o->no_lines;f++) + { + int col; + int p=f+o->top; + int tx=x; + int ty=y+(f*LIST_TEXTHEIGHT); + int tw=w-sw; + int th=LIST_TEXTHEIGHT; + + if (p==o->curr) + { + col=WHITE; + GFX_frect(tx,ty,tw,th,BLACK); + } + else + col=BLACK; + + if (p<o->no) + { + if (o->ent[p].is_dir) + sprintf(buff,"%-*.*s%*.*s", + o->name_width, + o->name_width, + o->ent[p].name, + o->size_width, + o->size_width, + "<Directory>"); + else + sprintf(buff,"%-*.*s%*ld", + o->name_width, + o->name_width, + o->ent[p].name, + o->size_width, + o->ent[p].size); + + CENTREY(tx,ty,th,col,buff); + } + } + + break; + + case GuiKeyPress: + switch(key) + { + case GFX_UP: + case GFX_PGUP: + case GFX_DOWN: + case GFX_PGDN: + return HandleFileListPage(o,key); + break; + + case GFX_ENTER: + if (!o->ent[o->curr].is_dir) + return GuiDone; + break; + + default: + break; + } + + break; + + case GuiButtonPress: + if (IsInBox(&o->scroll)) + { + if (!o->scrollbar) + return GuiOk; + + if (IsInBox(&o->bar)) + { + int orig; + int y; + double dtop; + + orig=mouse_y; + dtop=o->top; + + while (GFX_mouse(NULL,&y)==GFX_BUTLEFT) + { + int diff; + + diff=y-orig; + + dtop+=diff/o->sbar_step; + + o->top=(int)dtop; + + o->top=MAX(0,o->top); + o->top=MIN(o->no-o->no_lines,o->top); + + Callback(op,GuiDraw); + GFX_redraw(); + + orig=y; + } + } + else + { + if (mouse_y<o->bar.y) + o->top=MAX(0,o->top-o->no_lines); + else + o->top=MIN(o->no-1-o->no_lines,o->top+o->no_lines); + + return GuiRedraw; + } + } + else + { + int my; + int pick; + + my=mouse_y-y; + + pick=o->top+my/LIST_TEXTHEIGHT; + + if (pick<o->no) + { + if (pick==o->curr && (time(NULL)-last)<2 && o->double_click) + { + if (o->ent[o->curr].is_dir) + { + chdir(o->ent[o->curr].name); + GetFileList(o); + last=0; + return GuiRedraw; + } + else + return GuiDone; + } + + last=time(NULL); + + o->curr=pick; + + if (!o->ent[o->curr].is_dir) + ResetText(o->file_text,o->ent[o->curr].name); + + return GuiRedraw; + } + } + break; + + case GuiWheelUp: + return HandleFileListPage(o,GFX_PGUP); + break; + + case GuiWheelDown: + return HandleFileListPage(o,GFX_PGDN); + break; + + case GuiExit: + if (o->ent) + Release(o->ent); + break; + } + + return GuiOk; +} + + +static void TextEditInfo(GuiObjTextEdit *o) +{ + char b1[128]; + char b2[128]; + char buff[128]; + + sprintf(b1,"Cursor: %d,%d",o->x+1,o->y+1); + sprintf(b2,"Lines: %d",o->lines); + sprintf(buff,"%-20.20s %s",b1,b2); + + ResetText(o->info,buff); +} + + +static CallbackReturn TextEditCallback(GuiObj *op ,ObjEvent ev) +{ + char out; + char *tmp; + GuiObjTextEdit *te; + TextEditLine *p; + int bx,by,bw,bh; + int x,y,sx,sy; + int f; + int mod; + + te=&op->edt; + p=te->tline; + + bx=te->common.box.x; + by=te->common.box.y; + bw=te->common.box.w; + bh=te->common.box.h; + + switch(ev) + { + case GuiDraw: + if (te->common.focus) + GFX_rect(bx-2,by-2,bw+4,bh+4,GUI_TEXTBOLD); + else + GFX_rect(bx-2,by-2,bw+4,bh+4,GUI_MID); + + GFX_frect(bx,by,bw,bh,WHITE); + + y=te->top; + + for(sy=0;sy<te->height;sy++,y++) + if (y<te->lines) + { + x=te->col; + + for(sx=0;sx<te->width;sx++,x++) + { + if (x<p[y].len && isprint(p[y].p[x])) + out=p[y].p[x]; + else + out=' '; + + if (x==te->x && y==te->y) + { + GFX_frect(bx+(sx*FW),by+(sy*FH),FW,FH,BLACK); + GFX_print(bx+(sx*FW),by+(sy*FH),WHITE,"%c",out); + } + else + GFX_print(bx+(sx*FW),by+(sy*FH),BLACK,"%c",out); + } + } + + break; + + case GuiButtonPress: + te->y=(mouse_y-by)/FH; + te->x=te->col+(mouse_x-bx)/FW; + + if (te->y>=te->lines) + te->y=te->lines-1; + + if (te->x>p[te->y].len) + te->x=p[te->y].len; + + TextEditInfo(te); + return GuiRedraw; + break; + + case GuiKeyPress: + switch(key) + { + case GFX_HOME: + te->x=0; + te->col=0; + break; + + case GFX_END: + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + break; + + case GFX_LEFT: + mod=ctrl||shift; + + if (mod) + while((te->x)&&(isspace(p[te->y].p[te->x]))) + { + if (te->x) + te->x--; + else + mod=FALSE; + + if (te->x<te->col) + te->col--; + } + + do { + if (te->x) + te->x--; + else + mod=FALSE; + + if (te->x<te->col) + te->col--; + + if (isspace(p[te->y].p[te->x])) + mod=FALSE; + + } while(mod); + + break; + + case GFX_RIGHT: + mod=ctrl||shift; + + if (mod) + while((te->x<p[te->y].len)&& + (isspace(p[te->y].p[te->x]))) + { + te->x++; + + if ((te->x-te->width)>te->col) + te->col++; + } + + do { + if (te->x<p[te->y].len) + { + te->x++; + + if ((te->x-te->width)>te->col) + te->col++; + } + else + mod=FALSE; + + if (isspace(p[te->y].p[te->x])) + mod=FALSE; + + } while(mod); + + break; + + case GFX_UP: + if (te->y) + { + te->y--; + + if (te->y<te->top) + te->top--; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + } + } + break; + + case GFX_PGUP: + if (te->y) + { + te->y-=te->height; + + if (te->y<0) + te->y=0; + + if (te->y<te->top) + te->top=te->y; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + } + } + break; + + case GFX_DOWN: + if (p[te->y+1].p) + { + te->y++; + + if (te->y>=te->top+te->height) + te->top=te->y-te->height+1; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + } + } + break; + + case GFX_PGDN: + te->y+=te->height; + + if (te->y>=te->lines) + te->y=te->lines-1; + + if (te->y>=te->top+te->height) + te->top=te->y-te->height+1; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + } + + break; + + case GFX_DELETE: + if (te->read_only) + break; + + if (p[te->y].p[te->x]) + { + for(f=te->x;f<p[te->y].len;f++) + p[te->y].p[f]=p[te->y].p[f+1]; + + p[te->y].len--; + p[te->y].p[p[te->y].len]=0; + } + else if (p[te->y+1].p) + { + p[te->y].len+=p[te->y+1].len; + + p[te->y].p=ReGrab(p[te->y].p, + (p[te->y].len/TE_CHUNK+1)*TE_CHUNK+1); + + strcat(p[te->y].p,p[te->y+1].p); + + Release(p[te->y+1].p); + + for(f=te->y+1;f<te->lines;f++) + p[f]=p[f+1]; + + te->lines--; + + p=te->tline=ReGrab(te->tline, + sizeof(TextEditLine)*(te->lines+1)); + p[te->lines].p=NULL; + } + break; + + case GFX_BACKSPACE: + if (te->read_only) + break; + + if (te->x) + { + for(f=te->x;f<p[te->y].len;f++) + p[te->y].p[f-1]=p[te->y].p[f]; + + p[te->y].len--; + te->x--; + p[te->y].p[p[te->y].len]=0; + + if (te->x<te->col) + te->col--; + } + else if (te->y) + { + te->x=p[te->y-1].len; + te->col=MAX(0,te->x-te->width); + + p[te->y-1].len+=p[te->y].len; + + p[te->y-1].p=ReGrab(p[te->y-1].p, + (p[te->y-1].len/TE_CHUNK+1)*TE_CHUNK+1); + + strcat(p[te->y-1].p,p[te->y].p); + + Release(p[te->y].p); + + for(f=te->y;f<te->lines;f++) + p[f]=p[f+1]; + + if (--te->y<te->top) + te->top--; + + te->lines--; + p=te->tline=ReGrab(te->tline, + sizeof(TextEditLine)*(te->lines+1)); + p[te->lines].p=NULL; + } + break; + + case GFX_ENTER: + if (te->read_only) + break; + + te->lines++; + p=te->tline=ReGrab(te->tline, + sizeof(TextEditLine)*(te->lines+1)); + + for(f=te->lines;f>te->y;f--) + p[f]=p[f-1]; + + tmp=Strdup(p[te->y].p+te->x); + tmp=ReGrab(tmp,(strlen(tmp)/TE_CHUNK+1)*TE_CHUNK+1); + + p[te->y].p[te->x]=0; + p[te->y].len=strlen(p[te->y].p); + + te->y++; + te->x=0; + te->col=0; + + p[te->y].p=tmp; + p[te->y].len=strlen(tmp); + + if (te->y>=te->top+te->height) + te->top=te->y-te->height+1; + + break; + + default: + if (ascii && !te->read_only) + { + p[te->y].len++; + + if (!(p[te->y].len%TE_CHUNK)) + p[te->y].p=ReGrab(p[te->y].p, + (p[te->y].len/TE_CHUNK+1)* + TE_CHUNK+1); + + for(f=p[te->y].len;f>te->x;f--) + p[te->y].p[f]=p[te->y].p[f-1]; + + p[te->y].p[te->x]=ascii; + + te->x++; + + if ((te->x-te->width)>te->col) + te->col++; + } + break; + } + + TextEditInfo(te); + return GuiRedraw; + break; + + case GuiWheelUp: + if (te->y) + { + te->y-=te->height; + + if (te->y<0) + te->y=0; + + if (te->y<te->top) + te->top=te->y; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + } + } + + TextEditInfo(te); + return GuiRedraw; + break; + + case GuiWheelDown: + te->y+=te->height; + + if (te->y>=te->lines) + te->y=te->lines-1; + + if (te->y>=te->top+te->height) + te->top=te->y-te->height+1; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + } + + TextEditInfo(te); + return GuiRedraw; + break; + + case GuiExit: + break; + } + + return GuiOk; +} + + +/* ---------------------------------------- DIALOG MAIN LOOP +*/ +static int DoDial(void) +{ + Pixmap saved; + int done=FALSE; + int f; + + saved=VIDOOM_GFX_SAVE_UNDER(dialog[0].common.box.x, + dialog[0].common.box.y, + dialog[0].common.box.w, + dialog[0].common.box.h); + + for(f=0;(f<dialog_no)&&(focus==-1);f++) + if (dialog[f].common.take_focus) + focus=f; + + for(f=0;f<dialog_no;f++) + dialog[f].common.focus=(f==focus); + + for(f=0;f<dialog_no;f++) + Callback(dialog+f,GuiDraw); + + GFX_redraw(); + + while(!done) + { + CallbackReturn cbret=GuiOk; + int in=-1; + + GetEvent(); + + if (is_key) + { + if (key==GFX_TAB) + { + int new; + int i; + + i=(shift ? -1:1); + + new=focus; + + do { + new+=i; + + if (new<0) + new=dialog_no-1; + else if (new==dialog_no) + new=0; + } while(new!=focus && !dialog[new].common.take_focus); + + dialog[focus].common.focus=FALSE; + Callback(dialog+focus,GuiDraw); + + focus=new; + dialog[focus].common.focus=TRUE; + Callback(dialog+focus,GuiDraw); + } + else if (key==GFX_ESC) + { + done=TRUE; + focus=-1; + } + else + { + if (focus!=-1) + cbret=Callback(dialog+focus,GuiKeyPress); + } + } + else + { + for(f=0;(f<dialog_no)&&(in==-1);f++) + if (dialog[f].common.take_focus) + if (MouseInBox(&dialog[f].common.box,1)!=-1) + in=f; + + if (mouse_b && !last_mouse_b) + { + if (in!=-1) + { + if (mouse_b&GFX_BUTLEFT) + cbret=Callback(dialog+in,GuiButtonPress); + else if (mouse_b&GFX_MSWHEELUP) + cbret=Callback(dialog+in,GuiWheelUp); + else if (mouse_b&GFX_MSWHEELDOWN) + cbret=Callback(dialog+in,GuiWheelDown); + } + } + else + { + if (in!=-1 && dialog[in].common.take_focus) + { + if (focus!=-1) + { + dialog[focus].common.focus=FALSE; + Callback(dialog+focus,GuiDraw); + } + + focus=in; + dialog[focus].common.focus=TRUE; + Callback(dialog+focus,GuiDraw); + } + } + } + + switch(cbret) + { + case GuiDone: + done=TRUE; + break; + case GuiRedraw: + for(f=0;f<dialog_no;f++) + Callback(dialog+f,GuiDraw); + break; + case GuiOk: + break; + } + + GFX_redraw(); + } + + for(f=0;f<dialog_no;f++) + Callback(dialog+f,GuiExit); + + EventTidy(); + + VIDOOM_GFX_RESTORE_UNDER(saved,dialog[0].common.box.x, + dialog[0].common.box.y, + dialog[0].common.box.w, + dialog[0].common.box.h); + + return focus; +} + + +/* ---------------------------------------- MENU FUNCTIONS +*/ +static void DrawMenuItem(BoundBox *b,char *txt,int sel,PLAT_MENU *child) +{ + int h,hy; + + if (sel) + Rect3DBox(b,FALSE,SMALL_BEVEL); + else + GFX_frect(b->x,b->y,b->w,b->h,GUI_MID); + + GFX_print(b->x+SMALL_BEVEL+2,b->y+b->h/2-FH/2,GUI_TEXT,txt); + + if (child) + { + h=b->h-(SMALL_BEVEL*2)-2; + hy=b->y+SMALL_BEVEL+1; + + GFX_line(b->x+b->w-SMALL_BEVEL-2-h/2,hy, + b->x+b->w-SMALL_BEVEL-2,hy+h/2,GUI_HI); + + GFX_line(b->x+b->w-SMALL_BEVEL-2-h/2,hy, + b->x+b->w-SMALL_BEVEL-2-h/2,hy+h,GUI_HI); + + GFX_line(b->x+b->w-SMALL_BEVEL-2-h/2,hy+h, + b->x+b->w-SMALL_BEVEL-2,hy+h/2,GUI_LO); + } +} + + +static MenuControl do_GUI_menu(char *title, int x, int y, + PLAT_MENU menu[],int defval,int is_child) +{ + Pixmap under; + MenuControl ret; + MenuControl cr; + int no; + int f; + int cur; + int last_cur; + int cx; + int by; + int done; + int quit; + int child_done; + BoundBox *box; + BoundBox menu_box; + int do_child; + + /* Calc dimensions + */ + no=0; + menu_box.x=x; + menu_box.y=y; + menu_box.w=LENGTH(title); + + while(menu[no].text) + { + int l; + + l=LENGTH(menu[no].text)+(menu[no].child ? 12 : 0); + menu_box.w=MAX(menu_box.w,l); + no++; + } + + menu_box.h=BEVEL*2+FH+4; + menu_box.h+=(FH+SMALL_BEVEL*2+2)*no; + menu_box.h+=2; + menu_box.w+=BEVEL+SMALL_BEVEL+4; + + box=Grab(sizeof(*box)*no); + + if ((menu_box.x+menu_box.w)>SCRW) + menu_box.x=SCRW-menu_box.w; + + if ((menu_box.y+menu_box.h)>SCRH) + menu_box.y=SCRH-menu_box.h; + + if ((menu_box.x<0)||(menu_box.y<0)) + { + fprintf(stderr, + "Cannot display menu:%s\nMenu is bigger than display!\n",title); + + ret.mode=MENU_CHILD_RETURN; + ret.ret=defval; + + return ret; + } + + under=VIDOOM_GFX_SAVE_UNDER(menu_box.x,menu_box.y,menu_box.w,menu_box.h); + + cx=menu_box.x+menu_box.w/2; + + /* Draw menu border and title + */ + Rect3DBox(&menu_box,FALSE,BEVEL); + CENTRE(cx,menu_box.y+BEVEL*2,GUI_TEXTBOLD,title); + + /* Draw menu items + */ + cur=0; + last_cur=0; + + by=menu_box.y+BEVEL*2+FH+4; + + for(f=0;f<no;f++) + { + box[f].x=menu_box.x+BEVEL; + box[f].y=by; + box[f].w=menu_box.w-BEVEL*2; + box[f].h=FH+SMALL_BEVEL*2+2; + by+=box[f].h; + + DrawMenuItem(&box[f],menu[f].text,(f==cur),menu[f].child); + } + + GFX_redraw(); + + /* Main loop + */ + quit=FALSE; + done=FALSE; + child_done=FALSE; + do_child=FALSE; + + while((!done)&&(!quit)&&(!child_done)) + { + GetEvent(); + + /* Handle input + */ + if (!do_child) + { + if (mouse_b) + { + int new; + + new=MouseInBox(box,no); + + if (new==-1) + { + if (MouseInBox(&menu_box,1)==-1) + { + quit=TRUE; + ret.mode=MENU_CANCEL_CLICK; + } + } + else + { + if (menu[new].child) + { + cur=new; + do_child=TRUE; + } + else + { + done=TRUE; + cur=new; + ret.mode=MENU_OK; + } + } + } + else if (is_key) + switch(key) + { + case GFX_ESC: + quit=TRUE; + ret.mode=MENU_CANCEL_ESC; + break; + + case GFX_ENTER: + ret.mode=MENU_OK; + done=TRUE; + break; + + case GFX_DOWN: + DrawMenuItem(&box[cur],menu[cur].text,FALSE, + menu[cur].child); + + cur=(cur+1)%no; + + DrawMenuItem(&box[cur],menu[cur].text,TRUE, + menu[cur].child); + + GFX_redraw(); + break; + + case GFX_UP: + DrawMenuItem(&box[cur],menu[cur].text,FALSE, + menu[cur].child); + + if (cur) + cur--; + else + cur=no-1; + + DrawMenuItem(&box[cur],menu[cur].text,TRUE, + menu[cur].child); + + GFX_redraw(); + break; + + case GFX_RIGHT: + if (menu[cur].child) + do_child=TRUE; + + break; + + case GFX_LEFT: + if (is_child) + { + quit=TRUE; + ret.mode=MENU_CHILD_RETURN; + } + break; + } + else + { + int new; + + new=MouseInBox(box,no); + + if (new!=-1) + cur=new; + } + } + + + /* Draw menu selection if changed + */ + if (cur!=last_cur) + { + if (last_cur!=-1) + DrawMenuItem(&box[last_cur],menu[last_cur].text,FALSE, + menu[last_cur].child); + + DrawMenuItem(&box[cur],menu[cur].text,TRUE, + menu[cur].child); + GFX_redraw(); + } + + last_cur=cur; + + /* Handle child menus + */ + if (do_child) + { + do_child=FALSE; + + cr=do_GUI_menu(menu[cur].text, + box[cur].x+box[cur].w,box[cur].y, + menu[cur].child,defval,TRUE); + + switch(cr.mode) + { + case MENU_OK: + child_done=TRUE; + break; + + case MENU_CANCEL_ESC: + quit=TRUE; + ret.mode=MENU_CANCEL_ESC; + break; + + case MENU_CANCEL_CLICK: + if (MouseInBox(&menu_box,1)==-1) + { + quit=TRUE; + ret.mode=MENU_CANCEL_CLICK; + } + else + { + if ((cur=MouseInBox(box,no))==-1) + cur=0; + else if (menu[cur].child) + do_child=TRUE; + } + + break; + + case MENU_CHILD_RETURN: + break; + } + } + } + + Release(box); + + if (child_done) + ret=cr; + else + { + if (!quit) + ret.ret=menu[cur].client_index; + else + ret.ret=defval; + } + + VIDOOM_GFX_RESTORE_UNDER(under,menu_box.x,menu_box.y,menu_box.w,menu_box.h); + + GFX_redraw(); + + return ret; +} + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ + +void GUI_setscreen(int w,int h) +{ + VIDOOM_GET_GFX_VARS(&disp,&win,&pix,&gc); + + hi_col=XCol(GUI_HI); + mid_col=XCol(GUI_MID); + lo_col=XCol(GUI_LO); + txt_col=XCol(GUI_TEXT); + shadow_col=XCol(GUI_TEXTSHADOW); + bold_col=XCol(GUI_TEXTBOLD); + + SCRW=w; + SCRH=h; + + FW=GFX_fw(); + FH=GFX_fh(); + + /* Read in GUI section of [linux] ini + */ + INI_GetTable(ini_conf,INI_TAB_SIZE(ini_conf)); +} + + +int GUI_yesno(char *question) +{ + char *cp; + char *line[128]; + int no; + int max; + int f; + int box; + int yes; + int x,y; + int ret; + + cp=Strdup(question); + + ChopLines(cp,line,&no,&max); + + max=MAX(max,FW*20); + + max+=BEVEL*2+10; + + InitDial(); + + box=CreateBox(0,0,0,0,NULL,FALSE); + + x=(SCRW-max)/2; + y=(SCRH-(no*(FH+2)))/2; + + for(f=0;f<no;f++) + CreateLabel(x,y+(FH+2)*f,max,FH,line[f],GUI_TEXT,TRUE); + + yes=CreateButton(x+10,y+(FH+2)*(no+1),max/2-20,FH*2,"Yes"); + CreateButton(SCRW/2+10,y+(FH+2)*(no+1),max/2-20,FH*2,"No"); + + ResizeBox(box); + + ret=DoDial(); + + Release(cp); + + return ret==yes; +} + + +void GUI_start_yesno_all(void) +{ + all_pressed=FALSE; + all_result=FALSE; +} + + +int GUI_yesno_all(char *question) +{ + char *cp; + char *line[128]; + int no; + int max; + int f; + int box; + int yes_but; + int no_but; + int yes_all_but; + int no_all_but; + int x,y; + int ret; + + if (all_pressed) + return all_result; + + cp=Strdup(question); + + ChopLines(cp,line,&no,&max); + + max=MAX(max,FW*30); + + max+=BEVEL*2+10; + + InitDial(); + + box=CreateBox(0,0,0,0,NULL,FALSE); + + x=(SCRW-max)/2; + y=(SCRH-(no*(FH+2)))/2; + + for(f=0;f<no;f++) + CreateLabel(x,y+(FH+2)*f,max,FH,line[f],GUI_TEXT,TRUE); + + yes_but=CreateButton(x+10,y+(FH+2)*(no+1),max/2-20,FH*2,"Yes"); + no_but=CreateButton(SCRW/2+10,y+(FH+2)*(no+1),max/2-20,FH*2,"No"); + yes_all_but=CreateButton(x+10,y+(FH+2)*(no+3),max/2-20,FH*2,"Yes to all"); + no_all_but=CreateButton(SCRW/2+10,y+(FH+2)*(no+3), + max/2-20,FH*2,"No to all"); + + ResizeBox(box); + + ret=DoDial(); + + Release(cp); + + all_result=(ret==yes_but || ret==yes_all_but); + + if (ret==yes_all_but || ret==no_all_but) + all_pressed=TRUE; + + return all_result; +} + + +void GUI_alert(char *title, char *text, char *button_text) +{ + char *cp; + char *line[128]; + int no; + int max; + int f; + int box; + int x,y; + + cp=Strdup(text); + + ChopLines(cp,line,&no,&max); + + max=MAX(max,LENGTH(title)); + max=MAX(max,LENGTH("OK")); + + max+=BEVEL*2+10; + + InitDial(); + + box=CreateBox(0,0,0,0,title,FALSE); + + x=(SCRW-max)/2; + y=(SCRH-(no*(FH+2)))/2; + + for(f=0;f<no;f++) + CreateLabel(x,y+(FH+2)*f,max,FH,line[f],GUI_TEXT,TRUE); + + CreateButton(x,y+(FH+2)*(no+1),max,FH*2,"OK"); + + ResizeBox(box); + + DoDial(); + + Release(cp); +} + + +int GUI_menu(char *title, int x, int y, PLAT_MENU menu[],int defval) +{ + int ret; + + ret=do_GUI_menu(title,x,y,menu,defval,FALSE).ret; + + EventTidy(); + + return ret; +} + + +char *GUI_fsel(char *title, char *default_path, char *filter) +{ + char pwd[PATH_MAX+1]; + char ret[PATH_MAX+1]; + char *new_title; + int ok,cancel,path,file,list,box; + int x,y,w,h; + int lx,ly,lw,lh; + int tw; + int press; + + getcwd(pwd,sizeof pwd); + strcpy(ret,default_path); + + if (!ret[0]) + strcpy(ret,pwd); + + if (ret[strlen(ret)-1]!='/') + strcat(ret,"/"); + + x=50; + y=50; + w=SCRW-x*2; + h=SCRH-y*2; + + lx=x+20; + ly=y+20; + lw=w-40; + lh=h-FH*8; + + while(lh%(LIST_TEXTHEIGHT+BEVEL*2)!=0) + lh--; + + InitDial(); + + if (filter) + new_title=Grab(strlen(title)+10+strlen(filter)); + else + new_title=Grab(strlen(title)+32); + + if (filter) + sprintf(new_title,"%s [*%s]",title,filter); + else + sprintf(new_title,"%s [no filter]",title); + + box=CreateBox(0,0,0,0,new_title,FALSE); + + CreateLabel(x+20,ly+lh+FH*1,-1,FH*1.5,"Dir",GUI_TEXT,FALSE); + CreateLabel(x+20,ly+lh+FH*3,-1,FH*1.5,"File",GUI_TEXT,FALSE); + + tw=FW*5; + + path=CreateText(x+20+tw,ly+lh+FH*1,w-tw-50,FH*1.5,"",GuiTextReadOnly,FALSE); + file=CreateText(x+20+tw,ly+lh+FH*3,w-tw-50,FH*1.5,"",GuiTextString,TRUE); + + tw=FW*10; + + ok=CreateButton(x+20,ly+lh+FH*5,tw,FH*2,"OK"); + cancel=CreateButton(x+tw+20,ly+lh+FH*5,tw,FH*2,"Cancel"); + + list=CreateFileList(lx,ly,lw,lh,ret,filter,path,file,TRUE); + + ResizeBox(box); + + press=DoDial(); + + chdir(pwd); + + Release(new_title); + + if (press!=-1 && press!=cancel) + { + strcpy(ret,dialog[path].txt.text); + strcat(ret,dialog[file].txt.text); + return Strdup(ret); + } + else + return NULL; +} + + +int GUI_picklist(char *title,char *opt[]) +{ + int max; + int no; + int box; + int pick; + int ok; + int cancel; + int x,y; + int press; + int boxh; + + max=MAX(FW*20,LENGTH(title)); + + no=0; + while(opt[no]) + { + max=MAX(max,LENGTH(opt[no])); + + no++; + } + + max+=FW+LIST_SCROLLSIZE; + boxh=LIST_TEXTHEIGHT*20+SMALL_BEVEL*2; + + InitDial(); + + box=CreateBox(0,0,0,0,title,FALSE); + + x=(SCRW-max)/2; + y=(SCRH-(boxh))/2; + + pick=CreateImageList(x,y,max,boxh,no,opt,NULL,0,-1,TRUE); + + ok=CreateButton(x+10,y+boxh+FH,max/2-20,FH*2,"OK"); + cancel=CreateButton(SCRW/2+10,y+boxh+FH,max/2-20,FH*2,"Cancel"); + + ResizeBox(box); + + press=DoDial(); + + if (press==ok || press==pick) + return dialog[pick].pck.curr; + else + return -1; +} + + +int GUI_client_picklist(char *title,PLAT_PICKLIST opt[],int defval) +{ + int max; + int no; + int box; + int pick; + int ok; + int cancel; + int x,y; + int press; + int boxh; + char **optset; + int f; + + max=MAX(FW*20,LENGTH(title)); + + no=0; + while(opt[no].text) + { + max=MAX(max,LENGTH(opt[no].text)); + + no++; + } + + optset=Grab(sizeof(char *)*no); + + for(f=0;f<no;f++) + optset[f]=opt[f].text; + + max+=FW+LIST_SCROLLSIZE; + boxh=LIST_TEXTHEIGHT*20+SMALL_BEVEL*2; + + InitDial(); + + box=CreateBox(0,0,0,0,title,FALSE); + + x=(SCRW-max)/2; + y=(SCRH-(boxh))/2; + + pick=CreateImageList(x,y,max,boxh,no,optset,NULL,0,-1,TRUE); + + ok=CreateButton(x+10,y+boxh+FH,max/2-20,FH*2,"OK"); + cancel=CreateButton(SCRW/2+10,y+boxh+FH,max/2-20,FH*2,"Cancel"); + + ResizeBox(box); + + press=DoDial(); + + if (press==ok || press==pick) + return opt[dialog[pick].pck.curr].client_index; + else + return defval; +} + + +int GUI_image_picklist(char *title,PLAT_IMG_PICKLIST opt[],int defval) +{ + int max; + int max_imgw; + int max_imgh; + int no; + int box; + int img_box; + int pick; + int ok; + int cancel; + int x,y; + int press; + int boxh; + char **optset; + GFX_IMAGE *imgset; + int f; + + max=MAX(FW*20,LENGTH(title)); + max_imgw=0; + max_imgh=0; + + no=0; + while(opt[no].text) + { + GfxImage *i; + + max=MAX(max,LENGTH(opt[no].text)); + + i=opt[no].img; + + if (i) + { + max_imgw=MAX(max_imgw,i->w); + max_imgh=MAX(max_imgh,i->h); + } + + no++; + } + + optset=Grab(sizeof(char *)*no); + imgset=Grab(sizeof(GFX_IMAGE)*no); + + for(f=0;f<no;f++) + { + optset[f]=opt[f].text; + imgset[f]=opt[f].img; + } + + max+=FW+LIST_SCROLLSIZE; + boxh=LIST_TEXTHEIGHT*20+SMALL_BEVEL*2; + + InitDial(); + + box=CreateBox(0,0,0,0,title,FALSE); + + x=(SCRW-max-20-max_imgw)/2; + y=(SCRH-(boxh))/2; + + img_box=CreateBox(x+max+20,y,max_imgw,max_imgh,NULL,FALSE); + + pick=CreateImageList(x,y,max,boxh,no,optset,imgset,0,img_box,TRUE); + + max+=20+max_imgw; + + ok=CreateButton(x+10,y+boxh+FH,max/2-20,FH*2,"OK"); + cancel=CreateButton(SCRW/2+10,y+boxh+FH,max/2-20,FH*2,"Cancel"); + + ResizeBox(box); + + press=DoDial(); + + Release(optset); + Release(imgset); + + if (press==ok || press==pick) + return opt[dialog[pick].pck.curr].client_index; + else + return defval; +} + + +int GUI_radio_box(char *title,PLAT_RADIO opt[],int current,int defval) +{ + int *radio; + int max; + int no; + int f; + int cur; + int box; + int ok; + int cancel; + int x,y; + int press; + int ret; + int opt_h; + + max=MAX(FW*20,LENGTH(title)); + cur=0; + ret=defval; + opt_h=FH+FH/2; + + no=0; + while(opt[no].text) + { + max=MAX(max,LENGTH(opt[no].text)); + + if (opt[no].client_index==current) + cur=no; + + no++; + } + + radio=Grab(no*sizeof(*radio)); + + max+=FW*3; + + InitDial(); + + box=CreateBox(0,0,0,0,title,FALSE); + + x=(SCRW-max)/2; + y=(SCRH-(no*opt_h))/2; + + for(f=0;f<no;f++) + radio[f]=CreateRadio(x,y+opt_h*f,max,FH,opt[f].text,1,(cur==f)); + + ok=CreateButton(x+10,y+opt_h*(no+1),max/2-20,FH*2,"OK"); + cancel=CreateButton(SCRW/2+10,y+opt_h*(no+1),max/2-20,FH*2,"Cancel"); + + ResizeBox(box); + + press=DoDial(); + + if (press==ok) + for(f=0;f<no;f++) + if (dialog[radio[f]].rad.state) + ret=opt[f].client_index; + + Release(radio); + + return ret; +} + + +int GUI_multi_box(char *title,PLAT_MULTI p[]) +{ + int *obj; + int max; + int no; + int f; + int box; + int ok; + int cancel; + int x,y; + int press; + int opt_h; + + max=MAX(FW*20,LENGTH(title)); + opt_h=FH+FH/2; + + no=0; + while(p[no].text) + { + max=MAX(max,LENGTH(p[no].text)); + + no++; + } + + obj=Grab(no*sizeof(*obj)); + + max+=FW*3; + + InitDial(); + + box=CreateBox(0,0,0,0,title,FALSE); + + x=(SCRW-max)/2; + y=(SCRH-(no*opt_h))/2; + + for(f=0;f<no;f++) + if (p[f].group) + obj[f]=CreateRadio(x,y+opt_h*f,max,FH, + p[f].text,p[f].group,p[f].val); + else + obj[f]=CreateToggle(x,y+opt_h*f,max,FH, + p[f].text,p[f].val); + + ok=CreateButton(x+10,y+opt_h*(no+1),max/2-20,FH*2,"OK"); + cancel=CreateButton(SCRW/2+10,y+opt_h*(no+1),max/2-20,FH*2,"Cancel"); + + ResizeBox(box); + + press=DoDial(); + + if (press==ok) + { + for(f=0;f<no;f++) + if (dialog[obj[f]].common.type==GuiRadio) + p[f].val=dialog[obj[f]].rad.state; + else + p[f].val=dialog[obj[f]].tog.state; + } + + Release(obj); + + return press==ok; +} + + +int GUI_dialog(char *title, int no, PLAT_DIALOG pd[]) +{ + char buff[256]; + int lmax; + int pmax; + int width; + int height; + int x,y; + int f,r; + int box; + int ok; + int cancel; + int *obj; + int press; + + lmax=LENGTH(title); + pmax=FW*20; + obj=Grab(sizeof(int)*no); + + for(f=0;f<no;f++) + lmax=MAX(lmax,LENGTH(pd[f].text)); + + for(f=0;f<no;f++) + if (pd[f].type==PLAT_DIAL_PICKLIST) + for(r=0;r<pd[f].data.pl.no;r++) + pmax=MAX(pmax,LENGTH(pd[f].data.pl.text[r])); + + pmax+=LIST_SCROLLSIZE+FW; + + width=lmax+pmax+FW*3; + + height=0; + + for(f=0;f<no;f++) + { + if (pd[f].type==PLAT_DIAL_PICKLIST) + height+=LIST_TEXTHEIGHT*3+FH/2; + else + height+=FH*2; + } + + InitDial(); + + box=CreateBox(0,0,0,0,title,FALSE); + + x=(SCRW-width)/2; + y=(SCRH-height)/2; + + for(f=0;f<no;f++) + switch(pd[f].type) + { + case PLAT_DIAL_STRING: + CreateLabel(x+FW,y,lmax,FH*1.5,pd[f].text,GUI_TEXT,FALSE); + obj[f]=CreateText(x+lmax+FW,y,pmax,FH*1.5, + pd[f].data.s,GuiTextString,TRUE); + y+=FH*2; + break; + + case PLAT_DIAL_INTEGER: + CreateLabel(x+FW,y,lmax,FH*1.5,pd[f].text,GUI_TEXT,FALSE); + sprintf(buff,"%d",pd[f].data.i); + obj[f]=CreateText(x+lmax+FW,y,pmax,FH*1.5,buff, + GuiTextInteger,TRUE); + y+=FH*2; + break; + + case PLAT_DIAL_DOUBLE: + CreateLabel(x+FW,y,lmax,FH*1.5,pd[f].text,GUI_TEXT,FALSE); + sprintf(buff,"%f",pd[f].data.d); + obj[f]=CreateText(x+lmax+FW,y,pmax,FH*1.5,buff, + GuiTextDouble,TRUE); + y+=FH*2; + break; + + case PLAT_DIAL_PICKLIST: + CreateLabel(x+FW,y,lmax,FH*1.5,pd[f].text,GUI_TEXT,FALSE); + obj[f]=CreateImageList(x+lmax+FW,y, + pmax,LIST_TEXTHEIGHT*3+BEVEL*2, + pd[f].data.pl.no,pd[f].data.pl.text, + NULL,pd[f].data.pl.current,-1,TRUE); + y+=LIST_TEXTHEIGHT*3+FH/2; + break; + + default: + break; + } + + ok=CreateButton(x+10,y+FH*2,width/2-20,FH*2,"OK"); + cancel=CreateButton(SCRW/2+10,y+FH*2,width/2-20,FH*2,"Cancel"); + + ResizeBox(box); + + press=DoDial(); + + if (press!=-1 && press!=cancel) + { + for(f=0;f<no;f++) + switch(pd[f].type) + { + case PLAT_DIAL_STRING: + strcpy(pd[f].data.s,dialog[obj[f]].txt.text); + break; + + case PLAT_DIAL_INTEGER: + pd[f].data.i=(int)strtol(dialog[obj[f]].txt.text,NULL,0); + break; + + case PLAT_DIAL_DOUBLE: + pd[f].data.d=strtod(dialog[obj[f]].txt.text,NULL); + break; + + case PLAT_DIAL_PICKLIST: + pd[f].data.pl.current=dialog[obj[f]].pck.curr; + break; + + default: + break; + } + } + + Release(obj); + + return (press!=-1 && press!=cancel); +} + + +void GUI_view_file (char *title, char *path) +{ + TextEditLine *line; + int no; + int f; + int x,y,width,height; + int box,info,edit,ok; + FILE *fp; + char buff[1024]; + + if (!(fp=fopen(path,"r"))) + { + GUI_alert(path,"File not found","OK"); + return; + } + + line=Grab(sizeof *line); + + no=0; + + fgets(buff,sizeof buff,fp); + + while(!feof(fp)) + { + /* No overflow checks, but it is a hack this thing! + */ + f=0; + + while(f<strlen(buff)) + { + if (buff[f]=='\t') + { + buff[f++]=' '; + while((f%8)) + { + memmove(buff+f+1,buff+f,strlen(buff)-f+1); + buff[f]=' '; + f++; + } + } + else + f++; + } + + line[no].p=Strdup(buff); + line[no].len=strlen(buff); + no++; + + line=ReGrab(line,sizeof(TextEditLine)*(no+1)); + + fgets(buff,sizeof buff,fp); + } + + line[no].p=NULL; + line[no].len=0; + + InitDial(); + + box=CreateBox(0,0,0,0,title,FALSE); + + width=80*FW; + height=20*FH; + + while (width>(SCRW-FW*5)) + width-=FW; + + while (height>(SCRH-FH*5)) + height-=FH; + + x=(SCRW-width)/2; + y=(SCRH-height)/2; + + info=CreateText(x,y-FH*2,width,FH*1.5,"",GuiTextReadOnly,FALSE); + edit=CreateTextEdit(x,y,width,height,line,no,info,TRUE); + + ok=CreateButton(x+10,y+height+FH*2,width-20,FH*2,"OK"); + + ResizeBox(box); + + DoDial(); + + for(f=0;f<dialog[edit].edt.lines;f++) + Release(dialog[edit].edt.tline[f].p); + + Release(dialog[edit].edt.tline); +} + + +static char *GUI_text_edit_external (char *title, char *text) +{ + pid_t pid; + int status; + FILE *fp; + char *name; + char *new; + long len; + + name=tempnam(NULL,"vidoom"); + + if (!(fp=fopen(name,"w"))) + { + free(name); + return NULL; + } + + FWrite(fp,text,strlen(text)); + fclose(fp); + + switch(pid=fork()) + { + case 0: + ExecProgram(GUI_EDITOR,name); + _exit(33); + break; + case -1: + break; + default: + VIDOOM_GFX_SAVE_DISPLAY(); + + Rect3D(0,0,SCRW,SCRH,FALSE,20); + CENTREX(0,SCRH/2,SCRW,GUI_TEXTBOLD, + "Window locked - editing:"); + CENTREX(0,SCRH/2+FH*2,SCRW,GUI_TEXTBOLD,title); + + GFX_redraw(); + + VIDOOM_GFX_WAIT_FOR(pid,&status); + + VIDOOM_GFX_RESTORE_DISPLAY(); + + if (WIFEXITED(status) && WEXITSTATUS(status)==33) + { + char *p; + + p=Grab(strlen(GUI_EDITOR)+128); + + sprintf(p,"Failed to exec:|%s",GUI_EDITOR); + + GUI_alert("Error",p,"OK"); + + Release(p); + + free(name); + + return NULL; + } + break; + } + + if (!(fp=fopen(name,"r"))) + { + free(name); + return NULL; + } + + len=FLen(fp); + new=Grab(len+1); + + FRead(fp,new,len); + + new[len]=0; + + fclose(fp); + + return new; +} + + +char *GUI_text_edit (char *title, char *text) +{ + char *cp_text; + char *p; + TextEditLine *line; + char *ret; + int no; + int f; + int x,y,width,height; + int box,info,edit,ok,cancel; + int press; + + if (GUI_EDITOR[0]) + return GUI_text_edit_external(title,text); + + p=text; + + if (*(p+strlen(p)-1)=='\n') + no=0; + else + no=1; + + while(*p) + if (*p++=='\n') + no++; + + line=Grab(sizeof(TextEditLine)*(no+1)); + cp_text=Strdup(text); + p=cp_text; + + for(f=0;f<no;f++) + { + char *e; + + e=p; + + while((*e)&&(*e!='\n')) + e++; + + *e=0; + + /* We rely on the fact that Grab() zeroes data... + */ + line[f].len=strlen(p); + line[f].p=Grab((line[f].len/TE_CHUNK+1)*TE_CHUNK+1); + strcpy(line[f].p,p); + p=e+1; + } + + line[f].p=NULL; + line[f].len=0; + Release(cp_text); + + InitDial(); + + box=CreateBox(0,0,0,0,"Text Editor",FALSE); + + width=80*FW; + height=20*FH; + + while (width>(SCRW-FW*5)) + width-=FW; + + while (height>(SCRH-FH*5)) + height-=FH; + + x=(SCRW-width)/2; + y=(SCRH-height)/2; + + info=CreateText(x,y-FH*2,width,FH*1.5,"",GuiTextReadOnly,FALSE); + edit=CreateTextEdit(x,y,width,height,line,no,info,FALSE); + + ok=CreateButton(x+10,y+height+FH*2,width/2-20,FH*2,"OK"); + cancel=CreateButton(SCRW/2+10,y+height+FH*2,width/2-20,FH*2,"Cancel"); + + ResizeBox(box); + + press=DoDial(); + + if (press==ok) + { + int tot; + + tot=0; + + for(f=0;f<dialog[edit].edt.lines;f++) + tot+=dialog[edit].edt.tline[f].len+1; + + ret=Grab(tot+1); + *ret=0; + + for(f=0;f<dialog[edit].edt.lines;f++) + { + strcat(ret,dialog[edit].edt.tline[f].p); + strcat(ret,"\n"); + } + } + else + ret=NULL; + + for(f=0;f<dialog[edit].edt.lines;f++) + Release(dialog[edit].edt.tline[f].p); + + Release(dialog[edit].edt.tline); + + return ret; +} + + +/* END OF FILE */ diff --git a/linux/runcmd.c b/linux/runcmd.c new file mode 100644 index 0000000..bea822b --- /dev/null +++ b/linux/runcmd.c @@ -0,0 +1,82 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Command execution interface + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "mem.h" + +#include <unistd.h> +#include <stdio.h> + +#define MAX_CMDSIZE 8192 + +static void Write(int fd, char *p) +{ + /* Should really do a loopy proper one... + */ + write(fd,p,strlen(p)); +} + + +int RunCommand(char *argv[], char *path) +{ + FILE *fp; + int output; + int f; + char buff[MAX_CMDSIZE]; + + f=0; + buff[0]=0; + + while(argv[f]) + { + strcat(buff,argv[f++]); + strcat(buff," "); + } + + strcpy(path,"vidoomXXXXXX"); + + if ((output=mkstemp(path))==-1) + return FALSE; + + if (!(fp=popen(buff,"r"))) + return FALSE; + + fgets(buff,sizeof buff,fp); + + while(!feof(fp)) + { + Write(output,buff); + fgets(buff,sizeof buff,fp); + } + + /* Assume that non-zero is error + */ + return pclose(fp)==0; +} + + +/* END OF FILE */ diff --git a/linux/vstring.c b/linux/vstring.c new file mode 100644 index 0000000..947ca99 --- /dev/null +++ b/linux/vstring.c @@ -0,0 +1,48 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Provides portable versions of necessary string routines + +*/ +static const char rcs_id[]="$Id$"; + +#include <string.h> + + +int StrCaseCmp(char *a, char *b) +{ + if (!a || !b) + return 0; + + return strcasecmp(a,b); +} + +int StrNCaseCmp(char *a, char *b, int n) +{ + if (!a || !b) + return 0; + + return strncasecmp(a,b,n); +} + + +/* END OF FILE */ @@ -0,0 +1,288 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Provides a double linked list + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" + +#include <string.h> +#include <stdio.h> + +#include "list.h" +#include "mem.h" + +struct VIDOOM_Elem + { + char *data; + struct VIDOOM_Elem *next; + struct VIDOOM_Elem *prev; + }; + +struct VIDOOM_List + { + int no; + int size; + struct VIDOOM_Elem *head; + struct VIDOOM_Elem *tail; + }; + +struct VIDOOM_Iterator + { + List l; + struct VIDOOM_Elem *e; + }; + +/* ---------------------------------------- +*/ +List ListNew(int type_size) +{ + List nl; + + nl=Grab(sizeof(struct VIDOOM_List)); + + nl->no=0; + nl->size=type_size; + nl->head=NULL; + nl->tail=NULL; + + return(nl); +} + +List ListClear(List l) +{ + struct VIDOOM_Elem *e,*d; + + if (l) + { + e=l->head; + + while(e) + { + d=e; + e=e->next; + Release(d->data); + Release(d); + } + + Release(l); + } + + return(NULL); +} + +List ListEmpty(List l) +{ + struct VIDOOM_Elem *e,*d; + + if (l) + { + e=l->head; + + while(e) + { + d=e; + e=e->next; + Release(d->data); + Release(d); + } + + l->no=0; + l->head=NULL; + l->tail=NULL; + } + + return(l); +} + +int ListSize(List l) +{ + if (l) + return(l->no); + else + return(0); +} + +Iterator ListIterator(List l) +{ + struct VIDOOM_Iterator *i; + + if (!l->head) + return(NULL); + + i=Grab(sizeof(struct VIDOOM_Iterator)); + i->l=l; + i->e=l->head; + + return(i); +} + +Iterator IteratorClear(Iterator i) +{ + if (i) + Release(i); + + return(NULL); +} + +void *IteratorData(Iterator i) +{ + if (i) + return(i->e->data); + else + return(NULL); +} + +Iterator IteratorNext(Iterator i) +{ + if (i) + if (!(i->e=i->e->next)) + { + Release(i); + i=NULL; + } + + return(i); +} + +Iterator IteratorPrev(Iterator i) +{ + if (i) + if (!(i->e=i->e->prev)) + { + Release(i); + i=NULL; + } + + return(i); +} + +Iterator IteratorUpdate(Iterator i,void *data) +{ + if (i) + { + Release(i->e->data); + i->e->data=Copy(data,i->l->size); + } + return(i); +} + +Iterator IteratorDelete(Iterator i) +{ + struct VIDOOM_Elem *e; + + e=i->e; + i->e=i->e->next; + + i->l->no--; + + if (e==i->l->head) + i->l->head=e->next; + + if (e==i->l->tail) + i->l->tail=e->prev; + + if (e->next) + e->next->prev=e->prev; + + if (e->prev) + e->prev->next=e->next; + + Release(e->data); + Release(e); + + if (!(i->e)) + { + Release(i); + i=NULL; + } + + return(i); +} + +void ListAppend(List l,void *data) +{ + struct VIDOOM_Elem *e; + + e=Grab(sizeof(struct VIDOOM_Elem)); + + e->next=NULL; + e->prev=l->tail; + e->data=Copy(data,l->size); + l->no++; + + if (l->head) + { + l->tail->next=e; + l->tail=e; + } + else + l->head=l->tail=e; +} + +void ListInsert(List l,void *data) +{ + struct VIDOOM_Elem *e; + + e=Grab(sizeof(struct VIDOOM_Elem)); + + e->prev=NULL; + e->next=l->head; + e->data=Copy(data,l->size); + l->no++; + + if (l->head) + { + l->head->prev=e; + l->head=e; + } + else + l->head=l->tail=e; +} + +Iterator ListFindElem(List l,int (*pred)(void *, void *),void *data) +{ + Iterator i; + struct VIDOOM_Elem *e; + + i=NULL; + + e=l->head; + + while((e)&&(!i)) + { + if ((*pred)(e->data,data)) + { + i=ListIterator(l); + i->e=e; + } + + e=e->next; + } + + return(i); +} + + +/* END OF FILE */ @@ -0,0 +1,104 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Provides a double-linked list + + $Id$ + +*/ + +#ifndef VIDOOM_LIST_H + +#define VIDOOM_LIST_H + +/* Opaque types for the list and iterator. NULL is the null list and iterator. +*/ +struct VIDOOM_List; +typedef struct VIDOOM_List *List; + +struct VIDOOM_Iterator; +typedef struct VIDOOM_Iterator *Iterator; + + +/* Create a list +*/ +List ListNew(int type_size); + +/* Free a list object +*/ +List ListClear(List l); + +/* Free all elements in a list +*/ +List ListEmpty(List l); + +/* Return number of elements in list +*/ +int ListSize(List l); + +/* Create an iterator over the list +*/ +Iterator ListIterator(List l); + +/* Destory an iterator +*/ +Iterator IteratorClear(Iterator i); + +/* Get data for pointed to by this iterator object +*/ +void *IteratorData(Iterator i); + +/* Move the iterator to the next element. NULL once done. +*/ +Iterator IteratorNext(Iterator i); + +/* Move the iterator to the previous element. NULL once done. +*/ +Iterator IteratorPrev(Iterator i); + +/* Replace the data currently stored in this this position with the new one +*/ +Iterator IteratorUpdate(Iterator i,void *data); + +/* Delete the data currently stored in this position and move the iterator to + the next element. +*/ +Iterator IteratorDelete(Iterator i); + +/* Append to the tail of the the list +*/ +void ListAppend(List l,void *data); + +/* Insert at the head of the the list +*/ +void ListInsert(List l,void *data); + +/* Return an iterator for the searched element. Note the fist void* passed + to the predicate will be of the type stored in the lists. The second + argument will be whatever was passed as data. +*/ +Iterator ListFindElem(List l,int (*pred)(void *, void *),void *data); + +#endif + + +/* END OF FILE */ diff --git a/make/cygwin-xfree.cfg b/make/cygwin-xfree.cfg new file mode 100644 index 0000000..64303af --- /dev/null +++ b/make/cygwin-xfree.cfg @@ -0,0 +1,41 @@ +# viDOOM - level editor for DOOM +# +# Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# ------------------------------------------------------------------------- +# +# Makefile config for CYGWIN (using X11) +# +# $Id: cygwin-xfree.cfg,v 1.1 2002/09/05 20:00:23 ianc Exp ianc $ +# +CC= gcc +LD= gcc +PLATFORM= linux +EXE_EXT= .exe +OBJ_EXT= .o +LIBS= -lX11 +EXTRACF= -g -O -Wall -I/usr/X11R6/include +EXTRALF= -L/usr/X11R6/lib +DIRSEP= "/" +MATHLIB= -lm +TRACEFORM= "%s(%d):%s\n",__FILE__,__LINE__,__FUNCTION__ +EXEFLAG= -o +OBJFLAG= -c +DEFINEFLAG= -D +INCFLAG= -I +MAKEINSTALL= make INSTALLDIR='$(INSTALLDIR)' -f install +RMCMD= rm -f diff --git a/make/djgpp.cfg b/make/djgpp.cfg new file mode 100644 index 0000000..637d07e --- /dev/null +++ b/make/djgpp.cfg @@ -0,0 +1,41 @@ +# viDOOM - level editor for DOOM +# +# Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# ------------------------------------------------------------------------- +# +# Makefile config for DJGPP +# +# $Id: djgpp.cfg,v 1.8 2001/12/25 09:34:38 ianc Exp ianc $ +# +CC= gcc +LD= gcc +PLATFORM= djgpp +EXE_EXT= .exe +OBJ_EXT= .o +LIBS= -lalleg +EXTRACF= -g -O -Wall +EXTRALF= +DIRSEP= "/" +MATHLIB= -lm +TRACEFORM= "%s(%d):%s\n",__FILE__,__LINE__,__FUNCTION__ +EXEFLAG= -o +OBJFLAG= -c +DEFINEFLAG= -D +INCFLAG= -I +MAKEINSTALL= make INSTALLDIR='$(INSTALLDIR)' -f install +RMCMD= rm -f diff --git a/make/linux.cfg b/make/linux.cfg new file mode 100644 index 0000000..d6d22c9 --- /dev/null +++ b/make/linux.cfg @@ -0,0 +1,41 @@ +# viDOOM - level editor for DOOM +# +# Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# ------------------------------------------------------------------------- +# +# Makefile config for LINUX (using X11) +# +# $Id: linux.cfg,v 1.2 2002/09/05 19:59:11 ianc Exp ianc $ +# +CC= gcc +LD= gcc +PLATFORM= linux +EXE_EXT= +OBJ_EXT= .o +LIBS= -lX11 +EXTRACF= -g -O -Wall -I/usr/X11R6/include +EXTRALF= -L/usr/X11R6/lib +DIRSEP= "/" +MATHLIB= -lm +TRACEFORM= "%s(%d):%s\n",__FILE__,__LINE__,__FUNCTION__ +EXEFLAG= -o +OBJFLAG= -c +DEFINEFLAG= -D +INCFLAG= -I +MAKEINSTALL= make INSTALLDIR='$(INSTALLDIR)' -f install +RMCMD= rm -f @@ -0,0 +1,162 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Provides a dynamic sort-of-array type + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" + +#include <string.h> + +#include "map.h" +#include "mem.h" + +#define MAPCHUNK 100 + +struct VIDOOM_Map + { + int top; + int alloc; + int size; + char *data; + }; + +/* ---------------------------------------- +*/ +Map MapNew(int type_size) +{ + Map nm; + + nm=Grab(sizeof(struct VIDOOM_Map)); + + nm->top=-1; + nm->size=type_size; + nm->alloc=0; + nm->data=NULL; + + return(nm); +} + +Map MapCopy(Map m) +{ + Map nm; + + nm=NULL; + + if (m) + { + nm=Copy(m,sizeof(struct VIDOOM_Map)); + nm->data=Copy(m->data,m->alloc*m->size); + } + + return(nm); +} + +Map MapClear(Map m) +{ + if (m) + { + if (m->data) + Release(m->data); + Release(m); + } + + return(NULL); +} + +Map MapEmpty(Map m) +{ + if (m) + { + if (m->data) + Release(m->data); + + m->top=-1; + m->alloc=0; + m->data=NULL; + } + + return(m); +} + +int MapSize(Map m) +{ + if (m) + return(m->top+1); + else + return(0); +} + +void *MapElem(Map m,int no) +{ + if ((no<0)||(no>m->top)) + return(NULL); + else + return(m->data+(no*m->size)); +} + +void MapAdd(Map m,int no,void *data) +{ + if (no==-1) + no=m->top+1; + + if (m->alloc<no+1) + { + m->alloc=(no/MAPCHUNK+1)*MAPCHUNK; + + if (m->data) + m->data=ReGrab(m->data,m->alloc*m->size); + else + m->data=Grab(m->alloc*m->size); + } + + memcpy(m->data+(no*m->size),data,m->size); + + if (m->top<no) + m->top=no; +} + +void *MapFindElem(Map m,int (*pred)(void *, void *),void *data) +{ + int f; + void *ret,*e; + + ret=NULL; + f=0; + + while((f<=m->top)&&(!ret)) + { + e=MapElem(m,f); + + if ((*pred)(e,data)) + ret=e; + + f++; + } + + return(ret); +} + + +/* END OF FILE */ @@ -0,0 +1,86 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Provides a dynamic sort-of-array type + + $Id$ + +*/ + +#ifndef VIDOOM_MAP_H + +#define VIDOOM_MAP_H + +/* Opaque types for the map. NULL is the null map. +*/ +struct VIDOOM_Map; +typedef struct VIDOOM_Map *Map; + +/* Create a new Map that handles objects of type_size +*/ +Map MapNew(int type_size); + + +/* Copy a Map +*/ +Map MapCopy(Map m); + + +/* Destroy a Map object +*/ +Map MapClear(Map m); + + +/* Destroy all elements in a Map +*/ +Map MapEmpty(Map m); + + +/* Return number of elements in the Map +*/ +int MapSize(Map m); + + +/* Get the element from index no +*/ +void *MapElem(Map m,int no); + + +/* Add an element at position no. no==-1 positions the element at the end +*/ +void MapAdd(Map m,int no,void *data); + + +/* Find and element, searching using the passed predicate function. Function + should return TRUE for found, FALSE for not found. + + Note the fist void* passed to the predicate will be of the type stored in + the Map. The second argument will be whatever was passed as data. + + Returns NULL if the entry cannot be found. +*/ +void *MapFindElem(Map m,int (*pred)(void *, void *),void *data); + +#endif + + +/* END OF FILE */ @@ -0,0 +1,79 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Memory allocation + + $Id$ + +*/ + +#ifndef VIDOOM_MEM_H + +#define VIDOOM_MEM_H + +/* Notes: All these functions take debug information. The versions actually + used in the code are the #define's following the prototypes +*/ + + +/* Grab len bytes of memory. len==0 is valid. Memory is zereod on allocation. +*/ +void *FGrab(char *file, int line, int len); + + +/* Given a pointer returned by Grab() and a new size reallocates it to a + larger or smaller block. The orignal contents are copied to the new + block. Passing an NULL for ptr makes this call the same as FGrab(len). +*/ +void *FReGrab(char *file, int line, void *ptr, int len); + + +/* Reserve len bytes and copy len bytes from p to the new memory. len==0 is + valid +*/ +void *FCopy(char *file, int line, void *p,int len); + + +/* Reserve space for the nul terminated string and copy the source there. + Return is NULL if source is NULL +*/ +char *FStrdup(char *file, int line, char *s); + + +/* Release allocated memory +*/ +void FRelease(char *file, int line, void *p); + + + +/* Actually use these +*/ +#define Grab(n) FGrab(__FILE__,__LINE__,(n)) +#define ReGrab(p,n) FReGrab(__FILE__,__LINE__,(p),(n)) +#define Copy(p,n) FCopy(__FILE__,__LINE__,(p),(n)) +#define Strdup(p) FStrdup(__FILE__,__LINE__,(p)) +#define Release(p) FRelease(__FILE__,__LINE__,(p)) + +#endif + + +/* END OF FILE */ @@ -0,0 +1,256 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Returns the constant names for the game and the map names + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "names.h" +#include "globals.h" +#include "file.h" + +typedef struct + { + char *map; + char *name; + } MapNameList; + +static MapNameList doom_maps[]= + { + {"E1M1","Hangar"}, + {"E1M2","Nuclear Plant"}, + {"E1M3","Toxin Refinery"}, + {"E1M4","Command Control"}, + {"E1M5","Phobos Lab"}, + {"E1M6","Central Processing"}, + {"E1M7","Computer Station"}, + {"E1M8","Phobos Anomaly"}, + {"E1M9","Military Base"}, + {"E2M1","Deimos Anomaly"}, + {"E2M2","Containment Area"}, + {"E2M3","Refinery"}, + {"E2M4","Deimos Lab"}, + {"E2M5","Command Center"}, + {"E2M6","Halls of the Damned"}, + {"E2M7","Spawning Vats"}, + {"E2M8","Tower of Babel"}, + {"E2M9","Fortress of Mystery"}, + {"E3M1","Hell Keep"}, + {"E3M2","Slough of Despair"}, + {"E3M3","Pandemonium"}, + {"E3M4","House of Pain"}, + {"E3M5","Unholy Cathedral"}, + {"E3M6","Mt. Erebus"}, + {"E3M7","Limbo"}, + {"E3M8","Dis"}, + {"E3M9","Warrens"}, + {"E4M1","Hell Beneath"}, + {"E4M2","Perfect Hatred"}, + {"E4M3","Sever The Wicked"}, + {"E4M4","Unruly Evil"}, + {"E4M5","They Will Repent"}, + {"E4M6","Against Thee Wickedly"}, + {"E4M7","And Hell Followed"}, + {"E4M8","Unto The Cruel"}, + {"E4M9","Fear"}, + {NULL,NULL} + }; + +static MapNameList doom2_maps[]= + { + {"MAP01","Entryway"}, + {"MAP02","Underhalls"}, + {"MAP03","The Gantlet"}, + {"MAP04","The Focus"}, + {"MAP05","The Waste Tunnels"}, + {"MAP06","The Crusher"}, + {"MAP07","Dead Simple"}, + {"MAP08","Tricks and Traps"}, + {"MAP09","The Pit"}, + {"MAP10","Refueling Base"}, + {"MAP11","'O' of Destruction!"}, + {"MAP12","The Factory"}, + {"MAP13","Downtown"}, + {"MAP14","The Inmost Dens"}, + {"MAP15","Industrial Zone"}, + {"MAP16","Suburbs"}, + {"MAP17","Tenements"}, + {"MAP18","The Courtyard"}, + {"MAP19","The Citadel"}, + {"MAP20","Gotcha!"}, + {"MAP21","Nirvana"}, + {"MAP22","The catacombs"}, + {"MAP23","Barrels O' Fun"}, + {"MAP24","The Chasm"}, + {"MAP25","Bloodfalls"}, + {"MAP26","The Abandoned Mines"}, + {"MAP27","Monster Condo"}, + {"MAP28","The Spirit World"}, + {"MAP29","The Living End"}, + {"MAP30","Icon Of Sin"}, + {"MAP31","Wolfenstein"}, + {"MAP32","Grosse"}, + {NULL,NULL} + }; + +static MapNameList plut_maps[]= + { + {"MAP01","Congo"}, + {"MAP02","Well of Souls"}, + {"MAP03","Aztec"}, + {"MAP04","Caged"}, + {"MAP05","Ghost Town"}, + {"MAP06","Baron's Lair"}, + {"MAP07","Caughtyard"}, + {"MAP08","Realm"}, + {"MAP09","Abattoire"}, + {"MAP10","Onslaught"}, + {"MAP11","Hunted"}, + {"MAP12","Speed"}, + {"MAP13","The Crypt"}, + {"MAP14","Genesis"}, + {"MAP15","The Twilight"}, + {"MAP16","The Omen"}, + {"MAP17","Compound"}, + {"MAP18","Neurosphere"}, + {"MAP19","NME"}, + {"MAP20","The Death Domain"}, + {"MAP21","Slayer"}, + {"MAP22","Impossible Mission"}, + {"MAP23","Tombstone"}, + {"MAP24","The Final Frontier"}, + {"MAP25","The Temple of Darkness"}, + {"MAP26","Bunker"}, + {"MAP27","Anti-Christ"}, + {"MAP28","The Sewers"}, + {"MAP29","Odyssey of Noises"}, + {"MAP30","The Gateway of Hell"}, + {"MAP31","Cyberden"}, + {"MAP32","Go 2 It"}, + {NULL,NULL} + }; + +static MapNameList tnt_maps[]= + { + {"MAP01","System Control"}, + {"MAP02","Human BBQ"}, + {"MAP03","Power Control"}, + {"MAP04","Wormhole"}, + {"MAP05","Hanger"}, + {"MAP06","Open Season"}, + {"MAP07","Prison"}, + {"MAP08","Metal"}, + {"MAP09","Stronghold"}, + {"MAP10","Redemption"}, + {"MAP11","Storage Facility"}, + {"MAP12","Crater"}, + {"MAP13","Nukage Processing"}, + {"MAP14","Steel Works"}, + {"MAP15","Dead Zone"}, + {"MAP16","Deepest Reaches"}, + {"MAP17","Processing Area"}, + {"MAP18","Mill"}, + {"MAP19","Shipping/Respawning"}, + {"MAP20","Central Processing"}, + {"MAP21","Administration Center"}, + {"MAP22","Habitat"}, + {"MAP23","Lunar Mining Project"}, + {"MAP24","Quarry"}, + {"MAP25","Baron's Den"}, + {"MAP26","Ballistyx"}, + {"MAP27","Mount Pain"}, + {"MAP28","Heck"}, + {"MAP29","River Styx"}, + {"MAP30","Last Call"}, + {"MAP31","Pharaoh"}, + {"MAP32","Caribbean"}, + {NULL,NULL} + }; + +/* ---------------------------------------- +*/ +static char *FindName(MapNameList *nl, char *map) +{ + int f; + + f=0; + while(nl[f].map) + { + if (STREQ(map,nl[f].map)) + return(nl[f].name); + + f++; + } + + return ("Unknown map!"); +} + + +/* ---------------------------------------- +*/ + +char *GameName(void) +{ + switch(game) + { + case DOOM: + return ("Doom - Knee Deep in The Dead"); + break; + case ULTIMATE_DOOM: + return ("Ultimate Doom - Thy Flesh Consumed"); + break; + case DOOM_2: + return ("Doom 2 - Hell On Earth"); + break; + case FINAL_TNT: + return ("Final Doom - TNT:Evilution"); + break; + case FINAL_PLUTONIA: + return ("Final Doom - The Plutonia Experiment"); + break; + case ZDOOM: + return ("ZDoom/BOOM extensions to Doom"); + break; + } + + return ("Game type undefined!"); +} + + +char *MapName(char *map) +{ + if (STREQ(Basename(IWAD_path),"doom.wad")) + return (FindName(doom_maps,map)); + else if (STREQ(Basename(IWAD_path),"doom2.wad")) + return (FindName(doom2_maps,map)); + else if (STREQ(Basename(IWAD_path),"tnt.wad")) + return (FindName(tnt_maps,map)); + else if (STREQ(Basename(IWAD_path),"plutonia.wad")) + return (FindName(plut_maps,map)); + + return ("Unknown IWAD - no map names!"); +} + + +/* END OF FILE */ @@ -0,0 +1,43 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Returns the constant names for the game and the map names + + $Id$ + + +*/ + +#ifndef VIDOOM_NAMES_H + +#define VIDOOM_NAMES_H + +/* Return game name. Bases this on game from globals.o +*/ +char *GameName(void); + +/* Given a level name returns the map name. Bases this on the basename() of + IWAD_path from globals.o +*/ +char *MapName(char *map); + +#endif diff --git a/platgui.h b/platgui.h new file mode 100644 index 0000000..9a27994 --- /dev/null +++ b/platgui.h @@ -0,0 +1,258 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Platform specific GUI routines + + This is the platform specific routines for pop-up menus, dialogs and + file selectors. More DOOM graphics orientied GUI is handled by the + generic GUI (gui.h) that is built on top of gfx. + + As with the platform specific GFX all calls can be differentiated from + the generic gui by starting with GUI_, rather than Gui. + + Note that all these routines are expected to preserve screen contents. + + $Id$ + +*/ + +#ifndef VIDOOM_PLATGUI_H +#define VIDOOM_PLATGUI_H + +#include "gfx.h" + +/* Type for a picklist, where each entry has an associated grahpic. If the + image is NULL then no picture should be shown for that entry. +*/ +typedef struct + { + char *text; + GFX_IMAGE img; + int client_index; /* Returned by GUI_image_picklist() */ + } PLAT_IMG_PICKLIST; + + +/* Type for a picklist +*/ +typedef struct + { + char *text; + int client_index; /* Returned by GUI_client_picklist() */ + } PLAT_PICKLIST; + + +/* Types to define a menu +*/ +typedef struct PLATMENU + { + char *text; /* Text to display */ + int client_index; /* Value returned by GUI_menu() */ + struct PLATMENU *child; /* NULL if no child menu, else */ + /* pointer to other menu array. */ + } PLAT_MENU; + + +/* Types to define a radio box +*/ +typedef struct + { + char *text; /* Label for this button. NULL = end */ + int client_index; /* Value returned by GUI_radio() */ + } PLAT_RADIO; + +/* Types to define a multi flag box +*/ +typedef struct + { + char *text; /* Label. NULL = end */ + int group; /* To group flags (0 = don't group) */ + int val; /* TRUE = set, FALSE = flag clear */ + } PLAT_MULTI; + +/* Types to define a simple dialog +*/ +#define PLAT_DIAL_MAXSTRLEN 128 + +#define PLAT_DIAL_STRING 0 +#define PLAT_DIAL_INTEGER 1 +#define PLAT_DIAL_DOUBLE 2 +#define PLAT_DIAL_PICKLIST 3 + +/* This is the picklist type that can be used inside a DIALOG. It matters + not how it's implemented, but it should be able to use large picklists + (eg. the amount of the list seen should be automatically limited). +*/ +typedef struct + { + int no; /* No of items */ + int current; /* Currently selected item */ + char **text; /* Text for items - used as *item[] */ + } PLAT_DIAL_PL; + +typedef struct + { + char *text; /* Label for item. NULL = no label */ + int type; /* Data type (PLAT_DIAL_xxx) */ + union /* Data */ + { + int i; + char s[PLAT_DIAL_MAXSTRLEN+1]; + double d; + PLAT_DIAL_PL pl; + } data; + } PLAT_DIALOG; + + +/* Inform GUI of screen size if required +*/ +void GUI_setscreen(int w,int h); + +/* Display a Yes/No option box. Returns TRUE/FALSE accordingly. The text + defined by question should be split over lines where a '|' character appears. +*/ +int GUI_yesno(char *question); + + +/* Starts a Yes/No/Yes to All/No to All cycle. After calling this + GUI_yesno_all() is reset to it's initial state. +*/ +void GUI_start_yesno_all(void); + + +/* Works like GUI_yesno(), but displays to extra options - "Yes to All" and + "No to All". If either of these options are selected then further calls + to this function should return TRUE/FALSE accordingly, until + GUI_start_yesno__all() is called. +*/ +int GUI_yesno_all(char *question); + + +/* Displays a dialog with the title and text supplied. This text should split + where a '|' character appears. A sole button containing the supplied + button_text should be displayed. +*/ +void GUI_alert(char *title, char *text, char *button_text); + + +/* Pop-up/display a menu. Return the client_index of the selected item from the + menus, or defval for no selection. The co-ords can obviously be ignored or + honoured, though honouring is preferable. + + MENU array is terminated by one with a NULL text entry. +*/ +int GUI_menu(char *title, int x, int y, PLAT_MENU menu[], int defval); + + +/* Allow selection of a filename. Returns NULL if cancelled, otherwise the + selected path with must be Releas()'ed later. + + If supported then files ending with filter should only be displayed. Filter + should be NULL to show all files. +*/ +char *GUI_fsel(char *title, char *default_path, char *filter); + + +/* Display a picklist. The items are described by the array of pointers + passed in, which terminates with a NULL pointer. The index to the selected + item is returned, or -1 if the pickbox was cancelled. +*/ +int GUI_picklist(char *title,char *opts[]); + + +/* Does a picklist as above, but works on structures cntaining client return + values, rather than simple array indexes. +*/ +int GUI_client_picklist(char *title,PLAT_PICKLIST opts[],int defval); + + +/* Display a picklist with associated images. The items are described by the + array of structs passed in, which terminates with a NULL pointer in the + text field. The client index data in the selected item is returned, or + defval if the pickbox was cancelled. + + If this cannot be supported on the platform then the minimum this should + do is a sideways call to GUI_client_picklist() +*/ +int GUI_image_picklist(char *title,PLAT_IMG_PICKLIST opts[],int defval); + + +/* Display a radio box. The items are described by the array of structs + passed in, which terminates with a NULL pointer in the text field. + The client index data in the selected item is returned, or defval if the + radio box was cancelled. + + If one of the entries client index values is the same as current then that + option will be the one selected when the radio box is displayed. Otherwise + the first item will be the selected one. +*/ +int GUI_radio_box(char *title,PLAT_RADIO opts[],int current,int defval); + +/* Display a mutli-selection radio box. The items are described by the array + of PLAT_MULTI passed in. + + Returns TRUE if dialog accepted, otherwise FALSE. +*/ +int GUI_multi_box(char *title,PLAT_MULTI p[]); + + +/* Displays a very simple dialog made up of 'no' fields as defined in pd[]. + Returns TRUE if the dialog is accepted, FALSE otherwise. + + Note that as the values in the PLAT_DIALOG array are not pointing to + actual client data they need not be preserved on a cancel of the dialog. +*/ +int GUI_dialog(char *title, int no, PLAT_DIALOG pd[]); + + +/* Displays the contents of a file. The form this display takes is not really + of any concern to viDOOM. + + Control codes should just be filtered out. Tabs should be converted to + spaces, or honoured. + + This interface is used for reading the license from the menu, and for + reading the results from running the node builder. +*/ +void GUI_view_file(char *title, char *path); + + +/* Allows simple text editting. The form this display takes is not really + of any concern to viDOOM (if applicable it would be more than OK to start + an external text editor). + + text is a pointer to the original text, which is one long string with line + breaks denoted by the '\n' character. Note that lines should _only_ be + terminated with the \n character (ie. MSDOS stlye \r\n need not be accepted + by this routine). + + The return is a newly allocated copy of the edited text is the text is OKed. + The return is NULL if the text is cancelled. + + In either case, the original string pointed to by text should be as it was. +*/ +char *GUI_text_edit(char *title, char *text); + + +#endif + + +/* END OF FILE */ diff --git a/runcmd.h b/runcmd.h new file mode 100644 index 0000000..06c341e --- /dev/null +++ b/runcmd.h @@ -0,0 +1,57 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Command execution interface + + $Id$ + + +*/ + +#ifndef VIDOOM_RUNCMD_H + +#define VIDOOM_RUNCMD_H + +/* Run a command. Te output from the command (if any) should NOT disturb the + screen contents. The call should return TRUE if the call succeeds, FALSE + otherwise. + + The argv list is an array of pointers to various sections of the command + and it's arguemnts, terminated with a NULL pointer. + + Note that arguments may contain more than one argument in each line, ie. + the actual command is described simply by concatanating all the + pointers together, eg. + argv[0]="bsp.exe" + argv[1]="x.wad" + argv[2]="-o x.wad" + argv[3]=NULL + + The path argument is a place to copy a file where the output from the + comand has been stored. If this is not supported then the empty string + should be assigned to it. viDOOM will remove() the file after it has + read it. +*/ +int RunCommand(char *argv[],char *path); + + +#endif diff --git a/sectors.c b/sectors.c new file mode 100644 index 0000000..63331de --- /dev/null +++ b/sectors.c @@ -0,0 +1,319 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Handles definition and storage of the supported SECTORS + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include "sectors.h" +#include "mem.h" +#include "list.h" +#include "map.h" + + +/* ---------------------------------------- TYPES AND VARS +*/ +typedef struct + { + char *name; + } + SectorClass; + +typedef struct + { + int class; + int id; + char *long_name; + char *short_name; + } SectorInfo; + +typedef struct + { + int flags; + char *name; + DirName upper; + DirName middle; + DirName lower; + DirName floor; + DirName ceiling; + } SectorStyle; + +static int no_classes[2]={0,0}; + +static Map sect_class[2]={NULL,NULL}; +static List sect_list[2]={NULL,NULL}; +static Map sect_style=NULL; + +static PLAT_MENU *menu[2]={NULL,NULL}; +static PLAT_PICKLIST **picklist[2]={NULL,NULL}; + +static char **sect_style_plist=NULL; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static int Index(int flag) +{ + if (flag) + return(1); + else + return(0); +} + + +static PLAT_PICKLIST *SectorPicklist(int index, int class) +{ + SectorInfo *si; + Iterator i; + int no; + int f; + + if (picklist[index][class]) + return(picklist[index][class]); + + no=0; + i=ListIterator(sect_list[index]); + + while(i) + { + si=IteratorData(i); + + if (si->class==class) + no++; + + i=IteratorNext(i); + } + + picklist[index][class]=Grab(sizeof(PLAT_PICKLIST)*(no+1)); + + i=ListIterator(sect_list[index]); + + f=0; + while(i) + { + si=IteratorData(i); + + if (si->class==class) + { + picklist[index][class][f].text=si->long_name; + picklist[index][class][f].client_index=si->id; + f++; + } + + i=IteratorNext(i); + } + + picklist[index][class][f].text=NULL; + + return(picklist[index][class]); +} + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ +void SectorNewClass(int hexen, char *class) +{ + SectorClass c; + + hexen=Index(hexen); + + if (!sect_class[hexen]) + sect_class[hexen]=MapNew(sizeof(SectorClass)); + + c.name=Strdup(class); + + MapAdd(sect_class[hexen],no_classes[hexen]++,&c); +} + + +void SectorAdd(int hexen, char *class,int id,char *long_name,char *short_name) +{ + SectorInfo *s; + int i_class; + int f; + + hexen=Index(hexen); + + if (!sect_list[hexen]) + sect_list[hexen]=ListNew(sizeof(SectorInfo)); + + s=Grab(sizeof(SectorInfo)); + + i_class=-1; + + for(f=0;f<MapSize(sect_class[hexen]);f++) + { + SectorClass *c; + + c=MapElem(sect_class[hexen],f); + + if (STREQ(c->name,class)) + i_class=f; + } + + if (i_class==-1) + i_class=0; + + s->class=i_class; + s->id=id; + s->long_name=Strdup(long_name); + s->short_name=Strdup(short_name); + ListAppend(sect_list[hexen],s); +} + + +int SelectSector(int hexen) +{ + int class; + int f; + int x,y; + + hexen=Index(hexen); + + if (!menu[hexen]) + { + menu[hexen]=Grab(sizeof(PLAT_MENU)*(no_classes[hexen]+1)); + picklist[hexen]=Grab(sizeof(PLAT_IMG_PICKLIST *)*(no_classes[hexen]+1)); + + for(f=0;f<no_classes[hexen];f++) + { + SectorClass *c; + + c=MapElem(sect_class[hexen],f); + menu[hexen][f].text=c->name; + menu[hexen][f].client_index=f; + } + + for(f=0;f<no_classes[hexen]+1;f++) + picklist[hexen][f]=NULL; + + menu[hexen][no_classes[hexen]].text=NULL; + } + + GFX_mouse(&x,&y); + + if (no_classes[hexen]<2) + class=0; + else + class=GUI_menu("Pick class of SECTOR",x,y,menu[hexen],SECTOR_NULLID); + + if (class!=SECTOR_NULLID) + class=GUI_client_picklist("SECTOR",SectorPicklist(hexen,class), + SECTOR_NULLID); + + return(class); +} + + +char *SectorName(int hexen, int id) +{ + SectorInfo *si; + Iterator i; + + hexen=Index(hexen); + + i=ListIterator(sect_list[hexen]); + + while(i) + { + si=IteratorData(i); + + if (si->id==id) + { + IteratorClear(i); + return(si->short_name); + } + + i=IteratorNext(i); + } + + return("UNKNOWN"); +} + + +void AddSectorStyle(char *name, int flags, DirName upper, DirName middle, + DirName lower, DirName floor, DirName ceiling) +{ + SectorStyle s; + + if (!sect_style) + sect_style=MapNew(sizeof(SectorStyle)); + + s.flags=flags; + s.name=Strdup(name); + strcpy(s.upper,upper); + strcpy(s.middle,middle); + strcpy(s.lower,lower); + strcpy(s.floor,floor); + strcpy(s.ceiling,ceiling); + + MapAdd(sect_style,-1,&s); +} + + +int ChooseSectorStyle(int *flags, DirName upper,DirName middle,DirName lower, + DirName floor,DirName ceiling) +{ + SectorStyle *s; + int f; + + if (!sect_style_plist) + { + sect_style_plist=Grab(sizeof(char *)*(MapSize(sect_style)+1)); + + for(f=0;f<MapSize(sect_style);f++) + { + s=MapElem(sect_style,f); + sect_style_plist[f]=s->name; + } + + sect_style_plist[MapSize(sect_style)]=NULL; + } + + f=GUI_picklist("SECTOR STYLE",sect_style_plist); + + if (f!=-1) + { + s=MapElem(sect_style,f); + *flags=s->flags; + strcpy(lower,s->lower); + strcpy(middle,s->middle); + strcpy(upper,s->upper); + strcpy(floor,s->floor); + strcpy(ceiling,s->ceiling); + return(TRUE); + } + else + return(FALSE); +} + +int NoSectorStyles(void) +{ + return(MapSize(sect_style)); +} + + +/* END OF FILE */ diff --git a/sectors.h b/sectors.h new file mode 100644 index 0000000..550fe88 --- /dev/null +++ b/sectors.h @@ -0,0 +1,95 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Handles definition and storage of the supported SECTORs + + $Id$ + +*/ + +#ifndef VIDOOM_SECTORS_H + +#define VIDOOM_SECTORS_H + +#include "wad.h" +#include "gfx.h" +#include "platgui.h" + +/* This is not going to be an ID, so is returned from SelectSector() if it is + cancelled. +*/ +#define SECTOR_NULLID -666 + + +/* Meanings for the flags in the sector styles +*/ +#define SSTYLE_FACING_IN 0x01 +#define SSTYLE_FACING_OUT 0x02 +#define SSTYLE_LEAVE_PEG 0x04 +#define SSTYLE_UPPER_PEG 0x08 +#define SSTYLE_LOWER_PEG 0x10 + + +/* Add a new class of sector. Set hexen TRUE for a HEXEN sector. +*/ +void SectorNewClass(int hexen, char *class); + + +/* Add the named type to the supplied class with the ID. + Set hexen TRUE for a HEXEN sector. +*/ +void SectorAdd(int hexen,char *class,int id, + char *long_name, char *short_name); + + +/* Select the ID type for a sector. Returns SECTOR_NULLID if cancelled. + Set hexen TRUE for a HEXEN sector. +*/ +int SelectSector(int hexen); + + +/* Returns the short name of a sector type +*/ +char *SectorName(int hexen, int id); + + +/* Define a linedef style +*/ +void AddSectorStyle(char *name, int flags, + DirName upper,DirName middle,DirName lower, + DirName floor,DirName ceiling); + +/* Return the number of defined sector styles +*/ +int NoSectorStyles(void); + +/* Choose a sector style. TRUE is accepted, FALSE cancelled. +*/ +int ChooseSectorStyle(int *flags, + DirName upper,DirName middle,DirName lower, + DirName floor,DirName ceiling); + + +#endif + + +/* END OF FILE */ diff --git a/specials.c b/specials.c new file mode 100644 index 0000000..44bb5e7 --- /dev/null +++ b/specials.c @@ -0,0 +1,319 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Handles definition and storage of the supported ACTION SPECIALS + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include "platgui.h" +#include "gui.h" +#include "gfx.h" +#include "specials.h" +#include "mem.h" +#include "list.h" +#include "map.h" + + +/* ---------------------------------------- TYPES AND VARS +*/ +typedef struct + { + char *name; + } SpecialClass; + +typedef struct + { + int class; + int id; + char *name; + int no_args; + char *arg[5]; + } SpecialInfo; + +static int no_classes=0; + +static Map special_class=NULL; +static List special_list=NULL; + +static PLAT_PICKLIST **picklist=NULL; + +static PLAT_MENU *special_menu=NULL; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static PLAT_PICKLIST *SpecialPicklist(int class) +{ + SpecialInfo *si; + Iterator i; + int no; + int f; + + if (picklist[class]) + return(picklist[class]); + else + { + no=0; + i=ListIterator(special_list); + + while(i) + { + si=IteratorData(i); + + if (si->class==class) + no++; + + i=IteratorNext(i); + } + + picklist[class]=Grab(sizeof(PLAT_PICKLIST)*(no+1)); + + i=ListIterator(special_list); + + f=0; + while(i) + { + si=IteratorData(i); + + if (si->class==class) + { + picklist[class][f].text=si->name; + picklist[class][f].client_index=si->id; + f++; + } + + i=IteratorNext(i); + } + + picklist[class][f].text=NULL; + + return(picklist[class]); + } +} + + + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ +void SpecialNewClass(char *class) +{ + SpecialClass c; + + if (!special_class) + special_class=MapNew(sizeof(SpecialClass)); + + c.name=Strdup(class); + + MapAdd(special_class,no_classes++,&c); +} + + +void SpecialAdd(char *class,char *name,int id, char *arg[5]) +{ + SpecialInfo *si; + int i_class; + int f; + + if (!special_list) + special_list=ListNew(sizeof(SpecialInfo)); + + i_class=-1; + + for(f=0;f<MapSize(special_class);f++) + { + SpecialClass *c; + + c=MapElem(special_class,f); + + if (STREQ(c->name,class)) + i_class=f; + } + + if (i_class==-1) + i_class=0; + + si=Grab(sizeof(SpecialInfo)); + + si->class=i_class; + si->name=Strdup(name); + si->id=id; + si->no_args=0; + + for(f=0;(f<5)&&(arg[f]);f++) + { + si->arg[f]=Strdup(arg[f]); + si->no_args++; + } + + ListAppend(special_list,si); +} + + +int SelectSpecial(void) +{ + int class; + int f; + int x,y; + + if (!special_menu) + { + special_menu=Grab(sizeof(PLAT_MENU)*(no_classes+1)); + picklist=Grab(sizeof(PLAT_PICKLIST *)*(no_classes+1)); + + for(f=0;f<no_classes;f++) + { + SpecialClass *c; + + c=MapElem(special_class,f); + special_menu[f].text=c->name; + special_menu[f].client_index=f; + } + + for(f=0;f<no_classes+1;f++) + picklist[f]=NULL; + + special_menu[no_classes].text=NULL; + } + + GFX_mouse(&x,&y); + + if (no_classes<2) + class=0; + else + class=GUI_menu("Pick class of ACTION SPECIAL",x,y, + special_menu,SPECIAL_NULLID); + + if (class!=SPECIAL_NULLID) + class=GUI_client_picklist("ACTION SPECIAL",SpecialPicklist(class), + SPECIAL_NULLID); + + return(class); +} + + +char *SpecialName(int id, char *arg[5]) +{ + SpecialInfo *si; + Iterator i; + int f; + + for(f=0;f<5;f++) + arg[f]=NULL; + + i=ListIterator(special_list); + + while(i) + { + si=IteratorData(i); + + if (si->id==id) + { + IteratorClear(i); + + if (arg) + for(f=0;f<si->no_args;f++) + arg[f]=si->arg[f]; + + return(si->name); + } + + i=IteratorNext(i); + } + + return("UNKNOWN"); +} + + +int SpecialArgDialog(char *prompt, int id, int arg[5]) +{ + PLAT_DIALOG d[5]; + SpecialInfo *si; + Iterator i; + char *pd; + int f; + + i=ListIterator(special_list); + + while(i) + { + si=IteratorData(i); + + if (si->id==id) + { + IteratorClear(i); + + if (si->no_args) + { + for(f=0;f<si->no_args;f++) + { + d[f].text=si->arg[f]; + d[f].type=PLAT_DIAL_INTEGER; + d[f].data.i=arg[f]; + } + + if (!prompt) + pd=Strdup(si->name); + else + { + pd=Grab(strlen(si->name)+strlen(prompt)+4); + sprintf(pd,"%s - %s",si->name,prompt); + } + + if (GUI_dialog(pd,si->no_args,d)) + { + for(f=0;f<5;f++) + arg[f]=0; + + for(f=0;f<si->no_args;f++) + arg[f]=d[f].data.i; + + Release(pd); + return(TRUE); + } + else + { + Release(pd); + return(FALSE); + } + } + else + { + GuiInfoBox("ACTION SPECIAL ARGUMENTS", + "Special '%s'|has no arguments",si->name); + return(FALSE); + } + } + + i=IteratorNext(i); + } + + GuiInfoBox("ACTION SPECIAL ARGUMENTS","Passed in type %s unknown",id); + return(FALSE); +} + + +/* END OF FILE */ diff --git a/specials.h b/specials.h new file mode 100644 index 0000000..a0b6e6b --- /dev/null +++ b/specials.h @@ -0,0 +1,74 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Handles definition and storage of the supported ACTION SPECIALS (Hexen) + + $Id$ + +*/ + +#ifndef VIDOOM_SPECIALS_H + +#define VIDOOM_SPECIALS_H + +/* This is not going to be an ID, so is used to detect whether LinedefSelect() + was cancelled. +*/ +#define SPECIAL_NULLID -666 + + +/* Add a new class of special +*/ +void SpecialNewClass(char *class); + + +/* Add the named type to the supplied class with the ID and 5 arguments. + Unused arguments can be NULL +*/ +void SpecialAdd(char *class,char *name,int id,char *arg[5]); + + +/* Selects a type of special, returning the ID or SPECIAL_NULLID if cancelled +*/ +int SelectSpecial(void); + + +/* Returns the name of a special action. If not NULL then the argument names + are placed in args. +*/ +char *SpecialName(int id,char *arg[5]); + + +/* Displays a dialog box to set arguments for the supplied ID. Returns + FALSE if the dialog is cancelled. If OKed then TRUE is returned and arg[] + will hold the new argument values. + + If prompt is not NULL it is displayed along with the special name in the + dialog where the arguments are entered. +*/ +int SpecialArgDialog(char *prompt,int id,int arg[5]); + + +#endif + + +/* END OF FILE */ diff --git a/texture.c b/texture.c new file mode 100644 index 0000000..1d109e4 --- /dev/null +++ b/texture.c @@ -0,0 +1,592 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Stores lists of the graphical information from the WADs + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include <string.h> + +#include "texture.h" +#include "wad.h" +#include "mem.h" +#include "gfx.h" +#include "platgui.h" +#include "gui.h" +#include "vstring.h" + + +PLAT_IMG_PICKLIST *flat_picklist=NULL; +PLAT_IMG_PICKLIST *texture_picklist=NULL; + +/* The height and width of a texture are actually stored along with the + texture name +*/ +#define TXTNUMWID 8 +#define TXTHEIGHT 10 +#define TXTWIDTH 20 +#define TXTMALLOC 31 + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static int CmpText(const void *a,const void *b) +{ + const PLAT_IMG_PICKLIST *p1,*p2; + + p1=a; + p2=b; + + return(StrCaseCmp(p1->text,p2->text)); +} + + +static Byte *GetMemShort(Byte *p,Short *s) +{ + *s=(Short)(*p++); + *s|=((Short)(*p++))<<8; + return(p); +} + + +static Byte *GetMemLong(Byte *p,Long *l) +{ + *l=(Long)(*p++); + *l|=((Long)(*p++))<<8; + *l|=((Long)(*p++))<<16; + *l|=((Long)(*p++))<<24; + return(p); +} + +static Byte *GetName(Byte *p,char *s) +{ + int f; + + for(f=0;f<8;f++) + *s++=*p++; + + *s=0; + return(p); +} + + +static void BMPlot(int x,int y,int c,GFX_BITMAP *bm) +{ + if ((x>=0)&&(x<bm->w)&&(y>=0)&&(y<bm->h)) + *(bm->data+(x)+(y*bm->w))=c; +} + + +static void DecodeLump(Byte *lump, int ox, int oy, GFX_BITMAP *bm) +{ + Short lw,lh,lox,loy; + Byte *base; + Byte *col; + Long off; + int x,y,f; + Byte cno; + + base=lump; + lump=GetMemShort(lump,&lw); + lump=GetMemShort(lump,&lh); + lump=GetMemShort(lump,&lox); + lump=GetMemShort(lump,&loy); + + lox=MAX(lox,0); + loy=MAX(loy,0); + + for(x=0;x<lw;x++) + { + lump=GetMemLong(lump,&off); + col=base+off; + + while(*col!=0xff) + { + y=*col++; + cno=*col++; + col++; + + for(f=0;f<cno;f++) + { + BMPlot(ox+x,oy+y,(int)*col,bm); + col++; + y++; + } + + col++; + } + } +} + + +static GFX_IMAGE GetTexture(Byte *t_ent, Byte *pn, GFX_BITMAP *bm) +{ + char name[9]; + Short no,w,h,xo,yo,pno; + int f; + Byte *patch; + + /* Skip over name and 2 always zero fields + */ + t_ent+=8+2+2; + + /* Get height and width of texture (limited to max size of bitmap) + */ + t_ent=GetMemShort(t_ent,&w); + bm->w=MIN(w,MAX_GFXBM_W); + + t_ent=GetMemShort(t_ent,&h); + bm->h=MIN(h,MAX_GFXBM_H); + + /* Once we have the width and height, return if the actual image is not to + be loaded + */ + if (!load_textures) + return(NULL); + + /* Skip over always zero fields + */ + t_ent+=4; + + /* The bitmap is filled with the transparent colour + */ + memset(bm->data,243,w*h); + + /* Get no of patches that make up this texture + */ + t_ent=GetMemShort(t_ent,&no); + + /* Read each patch and add to the texture + */ + for(f=0;f<no;f++) + { + /* Patch offsets and number then skip remaining + */ + t_ent=GetMemShort(t_ent,&xo); + t_ent=GetMemShort(t_ent,&yo); + t_ent=GetMemShort(t_ent,&pno); + t_ent+=4; + + /* Get the name from the Pname entry and load the lump + */ + GetName(pn+4+8*pno,name); + + if (!(patch=GetLump(name,NULL))) + GFX_exit(EXIT_FAILURE,"Failed to find patch %s\n",name); + + /* Decode the graphics lump and load it into the bitmap + */ + DecodeLump(patch,xo,yo,bm); + + Release(patch); + } + + return(GFX_create_image(bm)); +} + + +static void ReadTextures(char *name,Byte *texture, + Byte *pn,int *i,GFX_BITMAP *bm) +{ + Long no; + Long off; + Byte *base; + int f; + + base=texture; + + /* Get the no of the textures + */ + texture=GetMemLong(texture,&no); + + /* Loop through all the textures and create the picklist entry + */ + for(f=0;f<no;f++) + { + texture=GetMemLong(texture,&off); + + texture_picklist[*i].text=Grab(TXTMALLOC); + + GetName(base+off,texture_picklist[*i].text); + + GuiDrawInfoBox((load_textures ? + "Please wait. Reading wall texture graphics": + "Please wait. Reading wall texture names"), + GUI_CENTRE,GUI_CENTRE,TRUE, + "Lump %s|Texture %s|%d%% complete", + name,texture_picklist[*i].text, + f*100/no); + GFX_redraw(); + + texture_picklist[*i].img=GetTexture(base+off,pn,bm); + sprintf(texture_picklist[*i].text+TXTWIDTH, + "%*.*d",TXTNUMWID,TXTNUMWID,bm->w); + sprintf(texture_picklist[*i].text+TXTHEIGHT, + "%*.*d",TXTNUMWID,TXTNUMWID,bm->h); + + + (*i)++; + } +} + + +/* ------------------------------ EXPORTED FUNCTIONS +*/ + +void ReadWADFlats(void) +{ + WadDir *wd; + Iterator i; + int found; + int done; + GFX_BITMAP bm; + Byte *pal; + int no; + int ip; + int f; + + if (!(pal=GetLump("PLAYPAL",NULL))) + GFX_exit(EXIT_FAILURE,"There MUST be a PLAYPAL lump in the WADs!!\n"); + + if (flat_picklist) + { + ip=0; + while(flat_picklist[ip].text) + { + Release(flat_picklist[ip].text); + if (flat_picklist[ip].img) + GFX_destroy_image(flat_picklist[ip].img); + } + + Release(flat_picklist); + } + + /* Get number of flats (Naughty.. Means we read through dir twice...) + */ + i=GetWadDir(); + + found=FALSE; + done=FALSE; + no=0; + + while((i)&&(!done)) + { + wd=IteratorData(i); + + if (!found) + found=!StrCaseCmp(wd->name,"F_START"); + else + if (!(done=!StrCaseCmp(wd->name,"F_END"))) + if (wd->size==0x1000) + no++; + + i=IteratorNext(i); + } + + IteratorClear(i); + flat_picklist=Grab(sizeof(PLAT_IMG_PICKLIST)*(no+1)); + flat_picklist[no].text=NULL; + flat_picklist[no].img=NULL; + + /* Palette and size for bitmap + */ + bm.w=64; + bm.h=64; + + for(f=0;f<256;f++) + { + int r,g,b; + + r=(int)(pal[f*3]*gfx_brighten); + g=(int)(pal[f*3+1]*gfx_brighten); + b=(int)(pal[f*3+2]*gfx_brighten); + + r=MIN(255,r); + g=MIN(255,g); + b=MIN(255,b); + + bm.pal[f]=V_RGB(r,g,b); + } + + /* Load in the names and graphics + */ + i=GetWadDir(); + + found=FALSE; + done=FALSE; + ip=0; + + while((i)&&(!done)) + { + wd=IteratorData(i); + + if (!found) + found=!StrCaseCmp(wd->name,"F_START"); + else + if (!(done=!StrCaseCmp(wd->name,"F_END"))) + if (wd->size==0x1000) + { + flat_picklist[ip].text=Strdup(wd->name); + + if (load_flats) + { + /* GFX_BITMAP and DOOM flats are actually in the same + format + */ + if (!(bm.data=GetLump(wd->name,NULL))) + GFX_exit(EXIT_FAILURE, + "Failed to load flat %s\n",wd->name); + + flat_picklist[ip].img=GFX_create_image(&bm); + + Release(bm.data); + } + else + flat_picklist[ip].img=NULL; + + GuiDrawInfoBox((load_flats ? + "Please wait. Reading flat graphics": + "Please wait. Reading flat names"), + GUI_CENTRE,GUI_CENTRE,TRUE, + "Flat %s|%d%% complete", + wd->name,ip*100/no); + GFX_redraw(); + + ip++; + } + + i=IteratorNext(i); + } + + IteratorClear(i); + Release(pal); + + /* Sort the list if requested + */ + if (sort_flats) + qsort(flat_picklist,no-1,sizeof(PLAT_IMG_PICKLIST),CmpText); + + /* Set the client indexes + */ + for(f=0;f<no;f++) + flat_picklist[f].client_index=f; +} + + +void ReadWADTextures(void) +{ + Long t1_size,t2_size; + int no; + Byte *t1=NULL; + Byte *t2=NULL; + Byte *pn=NULL; + Byte *pal=NULL; + GFX_BITMAP bm; + int ip; + int f; + + if (!(pal=GetLump("PLAYPAL",NULL))) + GFX_exit(EXIT_FAILURE,"There MUST be a PLAYPAL lump in the WADs!!\n"); + + if (!(t1=GetLump("TEXTURE1",NULL))) + GFX_exit(EXIT_FAILURE,"There MUST be a TEXTURE1 lump in the WADs!!\n"); + + t2=GetLump("TEXTURE2",NULL); + + if (!(pn=GetLump("PNAMES",NULL))) + GFX_exit(EXIT_FAILURE,"There MUST be a PNAMES lump in the WADs!!\n"); + + if (texture_picklist) + { + ip=0; + while(texture_picklist[ip].text) + { + Release(texture_picklist[ip].text); + if (texture_picklist[ip].img) + GFX_destroy_image(texture_picklist[ip].img); + } + + Release(texture_picklist); + } + + /* Calculate the size of the resulting picklist + extra two - + one for the empty '-' texture and the other for the end of list marker. + */ + GetMemLong(t1,&t1_size); + + if (t2) + GetMemLong(t2,&t2_size); + else + t2_size=0; + + no=t1_size+t2_size+2; + + texture_picklist=Grab(sizeof(PLAT_IMG_PICKLIST)*no); + + /* Storage and palette for bitmap + */ + bm.data=Grab(MAX_GFXBM_W*MAX_GFXBM_H); + + for(f=0;f<256;f++) + { + int r,g,b; + + r=(int)(pal[f*3]*gfx_brighten); + g=(int)(pal[f*3+1]*gfx_brighten); + b=(int)(pal[f*3+2]*gfx_brighten); + + r=MIN(255,r); + g=MIN(255,g); + b=MIN(255,b); + + bm.pal[f]=V_RGB(r,g,b); + } + + /* Create '-' texture + */ + ip=0; + + texture_picklist[ip].text=Grab(TXTMALLOC); + strcpy(texture_picklist[ip].text,empty_texture); + sprintf(texture_picklist[ip].text+TXTHEIGHT, + "%*.*d",TXTNUMWID,TXTNUMWID,0); + sprintf(texture_picklist[ip].text+TXTWIDTH, + "%*.*d",TXTNUMWID,TXTNUMWID,0); + + texture_picklist[ip].img=NULL; + ip++; + + /* Read the patches from TEXTURE1 and possible TEXTURE2 + */ + ReadTextures("TEXTURE1",t1,pn,&ip,&bm); + + if (t2) + ReadTextures("TEXTURE2",t2,pn,&ip,&bm); + + /* Create end of picklist marker + */ + texture_picklist[ip].text=NULL; + + Release(bm.data); + Release(t1); + if (t2) + Release(t2); + Release(pn); + Release(pal); + + /* Sort the list if requested + */ + if (sort_textures) + qsort(&texture_picklist[1],no-2,sizeof(PLAT_IMG_PICKLIST),CmpText); + + /* Set the client indexes + */ + for(f=0;f<no;f++) + texture_picklist[f].client_index=f; +} + + +void TextureSize(char *name, int *width, int *height) +{ + int f; + + f=0; + while(texture_picklist[f].text) + { + if (STREQ(texture_picklist[f].text,name)) + { + if (width) + *width=atoi(texture_picklist[f].text+TXTWIDTH); + + if (height) + *height=atoi(texture_picklist[f].text+TXTHEIGHT); + + return; + } + + f++; + } + + if (width) + *width=0; + + if (height) + *height=0; +} + + +GFX_IMAGE DecodeGraphicsLump(char *lump) +{ + Byte *obj; + Byte *base; + Byte *pal; + GFX_BITMAP bm; + GFX_IMAGE img; + int f; + Short w,h; + + if (!(obj=GetLump(lump,NULL))) + return(NULL); + + if (!(pal=GetLump("PLAYPAL",NULL))) + GFX_exit(EXIT_FAILURE,"There MUST be a PLAYPAL lump in the WADs!!\n"); + + base=obj; + obj=GetMemShort(obj,&w); + obj=GetMemShort(obj,&h); + + bm.w=w; + bm.h=h; + bm.data=Grab(w*h); + + for(f=0;f<256;f++) + { + int r,g,b; + + r=(int)(pal[f*3]*gfx_brighten); + g=(int)(pal[f*3+1]*gfx_brighten); + b=(int)(pal[f*3+2]*gfx_brighten); + + r=MIN(255,r); + g=MIN(255,g); + b=MIN(255,b); + + bm.pal[f]=V_RGB(r,g,b); + } + + DecodeLump(base,0,0,&bm); + + img=GFX_create_image(&bm); + + Release(bm.data); + Release(base); + Release(pal); + + return(img); +} + + +/* END OF FILE */ + diff --git a/texture.h b/texture.h new file mode 100644 index 0000000..c737f9e --- /dev/null +++ b/texture.h @@ -0,0 +1,69 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Stores lists of the graphical information from the WADs + + $Id$ + +*/ + +#ifndef VIDOOM_TEXTURE_H + +#define VIDOOM_TEXTURE_H + +#include "gfx.h" +#include "platgui.h" + +/* Picklists to allow selection of textures and flats. The client data returns + from these picklists will be the index into the picklist. +*/ +extern PLAT_IMG_PICKLIST *flat_picklist; +extern PLAT_IMG_PICKLIST *texture_picklist; + +#define TXT_PICKDEFVAL -1 /* Use as cancel/defval in picklist */ + + +/* Create the picklists. Note these expect the graphics routines to be + initialised so that progress meters can be drawn. +*/ +void ReadWADFlats(void); +void ReadWADTextures(void); + +/* Given a texture name give the size of the texture. Note that 0 is returned + for the "-" empty texture and unknown names. +*/ +void TextureSize(char *name,int *width,int *height); + + +/* Utility to return a GFX_IMAGE for the supplied graphics lump. Note the + lump must be in the Doom graphics format used for textures and sprites - + flats cannot be read with this. Returns NULL if the lump cannot be found. + + Note that error checking on the type of lump given is not *AT ALL* done. +*/ +GFX_IMAGE DecodeGraphicsLump(char *lump); + + +#endif + + +/* END OF FILE */ diff --git a/things.c b/things.c new file mode 100644 index 0000000..10e407b --- /dev/null +++ b/things.c @@ -0,0 +1,288 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Handles definition and storage of the supported THINGS + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include "platgui.h" +#include "gfx.h" +#include "things.h" +#include "mem.h" +#include "list.h" +#include "map.h" + + +/* ---------------------------------------- TYPES AND VARS +*/ +#define DEFAULT_RAD 20 +#define DEFAULT_COL V_RGB(0xff,0x80,0x80) + +typedef struct + { + char *name; + int col; + } ThingClass; + +typedef struct + { + int class; + int id; + char *name; + GFX_IMAGE img; + } ThingInfo; + +typedef struct + { + int id; + int rad; + int col; + } ThingRad; + +static int no_classes=0; + +static Map thing_class=NULL; +static List thing_list=NULL; +static List rad_list=NULL; + +static PLAT_IMG_PICKLIST **picklist=NULL; +static PLAT_MENU *thing_menu=NULL; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static PLAT_IMG_PICKLIST *ThingPicklist(int class) +{ + ThingInfo *ti; + Iterator i; + int no; + int f; + + if (picklist[class]) + return(picklist[class]); + else + { + no=0; + i=ListIterator(thing_list); + + while(i) + { + ti=IteratorData(i); + + if (ti->class==class) + no++; + + i=IteratorNext(i); + } + + picklist[class]=Grab(sizeof(PLAT_IMG_PICKLIST)*(no+1)); + + i=ListIterator(thing_list); + + f=0; + while(i) + { + ti=IteratorData(i); + + if (ti->class==class) + { + picklist[class][f].text=ti->name; + picklist[class][f].img=ti->img; + picklist[class][f].client_index=ti->id; + f++; + } + + i=IteratorNext(i); + } + picklist[class][f].text=NULL; + picklist[class][f].img=NULL; + + return(picklist[class]); + } +} + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ +void ThingNewClass(char *class, int col) +{ + ThingClass c; + + if (!thing_class) + thing_class=MapNew(sizeof(ThingClass)); + + c.name=Strdup(class); + c.col=col; + + MapAdd(thing_class,no_classes++,&c); +} + + +void ThingAdd(char *class,char *name,int id,int rad,GFX_IMAGE sprite) +{ + ThingInfo *ti; + ThingRad *tr; + int i_class; + int col; + int f; + + if (!thing_list) + { + thing_list=ListNew(sizeof(ThingInfo)); + rad_list=ListNew(sizeof(ThingInfo)); + } + + i_class=-1; + col=DEFAULT_COL; + + for(f=0;f<MapSize(thing_class);f++) + { + ThingClass *c; + + c=MapElem(thing_class,f); + + if (STREQ(c->name,class)) + { + i_class=f; + col=c->col; + } + } + + if (i_class==-1) + i_class=0; + + ti=Grab(sizeof(ThingInfo)); + + ti->class=i_class; + ti->name=Strdup(name); + ti->id=id; + ti->img=sprite; + ListAppend(thing_list,ti); + + tr=Grab(sizeof(ThingRad)); + tr->id=id; + tr->rad=rad; + tr->col=col; + ListAppend(rad_list,tr); +} + + +int SelectThing(void) +{ + int class; + int f; + int x,y; + + if (!thing_menu) + { + thing_menu=Grab(sizeof(PLAT_MENU)*(no_classes+1)); + picklist=Grab(sizeof(PLAT_IMG_PICKLIST *)*(no_classes+1)); + + for(f=0;f<no_classes;f++) + { + ThingClass *c; + + c=MapElem(thing_class,f); + thing_menu[f].text=c->name; + thing_menu[f].client_index=f; + } + + for(f=0;f<no_classes+1;f++) + picklist[f]=NULL; + + thing_menu[no_classes].text=NULL; + } + + GFX_mouse(&x,&y); + + if (no_classes<2) + class=0; + else + class=GUI_menu("Pick class of THING",x,y,thing_menu,THING_NULLID); + + if (class!=THING_NULLID) + class=GUI_image_picklist("THING",ThingPicklist(class),THING_NULLID); + + return(class); +} + + +int ThingRadius(int id, int *col) +{ + Iterator i; + ThingRad *r; + + i=ListIterator(rad_list); + + while(i) + { + r=IteratorData(i); + + if (r->id==id) + { + IteratorClear(i); + + if (col) + *col=r->col; + + return(r->rad); + } + + i=IteratorNext(i); + } + + if (col) + *col=DEFAULT_COL; + + return(DEFAULT_RAD); +} + + +char *ThingName(int id) +{ + ThingInfo *ti; + Iterator i; + + i=ListIterator(thing_list); + + while(i) + { + ti=IteratorData(i); + + if (ti->id==id) + { + IteratorClear(i); + return(ti->name); + } + + i=IteratorNext(i); + } + + return("UNKNOWN"); +} + + +/* END OF FILE */ diff --git a/things.h b/things.h new file mode 100644 index 0000000..49f03a5 --- /dev/null +++ b/things.h @@ -0,0 +1,72 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Handles definition and storage of the supported THINGS + + $Id$ + +*/ + +#ifndef VIDOOM_THINGS_H + +#define VIDOOM_THINGS_H + +#include "gfx.h" +#include "platgui.h" + + +/* This is not going to be an ID, so is used to detect whether ThingSelect() + was cancelled. +*/ +#define THING_NULLID -666 + + +/* Add a new class of things +*/ +void ThingNewClass(char *class, int col); + + +/* Add the named type to the supplied class with the ID and radius +*/ +void ThingAdd(char *class,char *name,int id,int rad,GFX_IMAGE sprite); + + +/* Selects a type of thing, returning the ID or THING_NULLID if cancelled +*/ +int SelectThing(void); + + +/* Given an ID returns the radius of the THING. If col is not NULL also + returns the colour of the thing. +*/ +int ThingRadius(int id,int *col); + + +/* Returns the name of a thing +*/ +char *ThingName(int id); + + +#endif + + +/* END OF FILE */ @@ -0,0 +1,4 @@ +* Update docs with DJGPP sys dependent part. Update docs for new editing + functions. + +* Add left_click_move option to docs @@ -0,0 +1,253 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Utility functions + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" + +#include <string.h> +#include <ctype.h> +#include "mem.h" +#include "util.h" + +#define TRIM_NO 10 +#define TRIM_MAX 80 + + +char *TrimStr(char *p, int n) +{ + static int i=0; + static char trim[TRIM_NO][TRIM_MAX+1]; + int l; + + i=(i+1)%TRIM_NO; + n=MIN(n,TRIM_MAX); + l=strlen(p); + + if (n>l) + strcpy(trim[i],p); + else + { + strncpy(trim[i],p,n); + trim[i][n-2]='.'; + trim[i][n-1]='.'; + trim[i][n]=0; + } + + return(trim[i]); +} + + +char *UnMSDOS(char *p) +{ + char *op; + + op=p; + + while(p && *p) + { + if ((*p=='\r')&&(*(p+1)=='\n')) + { + char *s1,*s2; + + s1=p; + s2=p+1; + + while((*s1++=*s2++)); + } + + p++; + } + + return(op); +} + + +char *ApplyMSDOS(char *p, int rel) +{ + char *ret; + char *t,*o; + int l; + + t=o=p; + + l=0; + + while(*t) + { + if (*t++=='\n') + l++; + + l++; + } + + ret=Grab(l+1); + t=ret; + + while(*p) + { + if (*p=='\n') + *t++='\r'; + + *t++=*p++; + } + + if (rel) + Release(o); + + return(ret); +} + + +void UCase(char *p) +{ + while(*p) + { + if (islower(*p)) + *p=toupper(*p); + p++; + } +} + + +void LCase(char *p) +{ + while(*p) + { + if (isupper(*p)) + *p=tolower(*p); + p++; + } +} + + +Byte GetByte(FILE *fp) +{ + return ((Byte)fgetc(fp)); +} + + +Word GetWord(FILE *fp) +{ + return ((Word)fgetc(fp)|((Word)fgetc(fp))<<8); +} + + +Long GetLong(FILE *fp) +{ + return ((Long)fgetc(fp)| + ((Long)fgetc(fp))<<8| + ((Long)fgetc(fp))<<16| + ((Long)fgetc(fp))<<24); +} + + +void PutByte(FILE *fp,Byte b) +{ + fputc(b,fp); +} + + +void PutShort(FILE *fp,Short s) +{ + fputc(s&0xff,fp); + fputc((s>>8)&0xff,fp); +} + + +void PutUShort(FILE *fp,UShort s) +{ + fputc(s&0xff,fp); + fputc((s>>8)&0xff,fp); +} + + +void PutLong(FILE *fp,Long l) +{ + fputc(l&0xff,fp); + fputc((l>>8)&0xff,fp); + fputc((l>>16)&0xff,fp); + fputc((l>>24)&0xff,fp); +} + + +int FRead(FILE *fp, void *buff, int size) +{ + char *p; + int tot,rd; + + tot=0; + p=buff; + + while(tot<size) + { + rd=fread(p+tot,1,size-tot,fp); + + if (rd<1) + return(-1); + + tot+=rd; + } + + return(tot); +} + + +int FWrite(FILE *fp, void *buff, int size) +{ + char *p; + int tot,wr; + + tot=0; + p=buff; + + while(tot<size) + { + wr=fwrite(p+tot,1,size-tot,fp); + + if (wr<1) + return(-1); + + tot+=wr; + } + + return(tot); +} + + +long FLen(FILE *fp) +{ + long orig,off; + + orig=ftell(fp); + fseek(fp,0,SEEK_END); + off=ftell(fp); + fseek(fp,orig,SEEK_SET); + + return(off); +} + + +/* END OF FILE */ @@ -0,0 +1,132 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Various utility functions + + $Id$ + + +*/ + +#ifndef VIDOOM_UTIL_H + +#define VIDOOM_UTIL_H + +#include "config.h" +#include <stdio.h> + +/* Returns a static copy of the string, which if it is over n characters long + will be trimmed to n-3 character and "..." appended. + + Note this function's return is static, but there can be 10 invocations + before the same memory is used (ie. it's safe to use more than once in + function calls). + + Note that string longer than a certain length will be trimmed regardless. +*/ +char *TrimStr(char *p, int n); + + +/* Alters the string so that all occurances of '\r\n' are converted to '\n'. + The return is the same pointer passed in p, after the conversion. + + NULL is returned unaltered. +*/ +char *UnMSDOS(char *p); + + +/* Alters the string so that all occurances of '\n' are converted to '\r\n'. + If rel is TRUE, the passed pointer p is released with Release() after + conversion. + + The return is a newly allocated copy of the converted string. +*/ +char *ApplyMSDOS(char *p, int rel); + + +/* Uppercase the supplied string +*/ +void UCase(char *p); + + +/* Lowercase the supplied string +*/ +void LCase(char *p); + + +/* Read a byte from a file +*/ +Byte GetByte(FILE *fp); + + +/* Read a word from a file, and macros to read Short and UShort +*/ +Word GetWord(FILE *fp); + +#define GetShort(fp) ((Short)GetWord(fp)) +#define GetUShort(fp) ((UShort)GetWord(fp)) + + +/* Read a long from a file +*/ +Long GetLong(FILE *fp); + + +/* Put a byte to a file +*/ +void PutByte(FILE *fp,Byte b); + + +/* Put a short to a file +*/ +void PutShort(FILE *fp,Short s); + + +/* Put an unsigned short to a file +*/ +void PutUShort(FILE *fp,UShort s); + + +/* Put a long to a file +*/ +void PutLong(FILE *fp,Long l); + + +/* A skin over fread(), that does any necessary looping. Returns 'size' on + sucess, -1 on failure. size is in bytes. +*/ +int FRead(FILE *fp, void *buff, int size); + + +/* A skin over fwrite(), that does any necessary looping. Returns 'size' on + sucess, -1 on failure. size is in bytes. +*/ +int FWrite(FILE *fp, void *buff, int size); + + +/* Returns the size of a file. Does this by seeking to end and getting the + position. Note that the original position is restored. +*/ +long FLen(FILE *fp); + + +#endif diff --git a/vidoom.c b/vidoom.c new file mode 100644 index 0000000..467912c --- /dev/null +++ b/vidoom.c @@ -0,0 +1,892 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Doom I/II editor + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "wad.h" +#include "platgui.h" +#include "gui.h" +#include "gfx.h" +#include "file.h" +#include "mem.h" +#include "edit.h" +#include "texture.h" +#include "things.h" +#include "runcmd.h" +#include "names.h" +#include "util.h" + +/* ---------------------------------------- VARS +*/ +static WadMap *wad=NULL; +static char mapname[32]="NONE"; +static GFX_IMAGE doom_image; + +/* Menu for asking game type +*/ +static PLAT_MENU game_type_menu[]= + { + {"Doom", DOOM}, + {"Ultimate Doom", ULTIMATE_DOOM}, + {"Doom II", DOOM_2}, + {"TNT:Evilution", FINAL_TNT}, + {"The Plutonia Experiment", FINAL_PLUTONIA}, + {"ZDoom", ZDOOM}, + {NULL,0} + }; + + +/* ---------------------------------------- PROTOS +*/ +static void MainMenu(void); +static void GPL_clear(void); +static int CreateHexenMap(void); + + +/* ---------------------------------------- MAIN +*/ +int viDOOM(int argc,char *argv[]) +{ + char *load,*p; + int f; + + if (getenv("VIDOOM_DIR")) + Cd(getenv("VIDOOM_DIR")); + + GFX_init(); + + if (GFX_mouse_buttons()<2) + GFX_exit(EXIT_FAILURE,"viDOOM expects at least a 2-button mouse\n"); + + LoadGlobalsPart1(); + + if ((disp_width<640)||(disp_height<480)) + GFX_exit(EXIT_FAILURE,"viDOOM expects at least a display of 640x480\n"); + + GFX_open(disp_width,disp_height); + EditSetScreen(disp_width,disp_height); + GuiSetScreen(disp_width,disp_height); + GUI_setscreen(disp_width,disp_height); + + GPL_clear(); + + /* Ask for game type? + */ + if (ask_for_game_type) + { + int new; + + GFX_redraw(); + + new=GUI_menu("Select type of game to edit", + disp_width/2,disp_height/2,game_type_menu,-666); + + if (new!=-666) + game=(GameType)new; + } + + LoadGlobalsPart2(); + + if (AddIWAD(IWAD_path)!=WAD_OK) + { + GuiInfoBox("ERROR","AddIWAD(%s): %s",IWAD_path,WadErrorString()); + GFX_close(); + return(EXIT_FAILURE); + } + + /* Read in resource WADS from preloaded config + */ + if (PWAD_preload[0]) + { + load=Strdup(PWAD_preload); + + p=strtok(load,";"); + + while(p) + { + if (AddPWAD(p)!=WAD_OK) + GuiInfoBox("ERROR","AddPWAD(%s): %s",p,WadErrorString()); + + p=strtok(NULL,";"); + } + + Release(load); + } + + /* Read in WADS from command line + */ + for(f=1;f<argc;f++) + if (AddPWAD(argv[f])!=WAD_OK) + GuiInfoBox("ERROR","AddPWAD(%s): %s",argv[f],WadErrorString()); + + /* Read in editor configuration + */ + GPL_clear(); + GuiDrawInfoBox("Please wait",GUI_CENTRE,GUI_CENTRE,TRUE, + "Reading editor|config file"); + GFX_redraw(); + LoadConfig(); + + /* Read in TEXTURES and FLATS + */ + GPL_clear(); + ReadWADTextures(); + + GPL_clear(); + ReadWADFlats(); + + if (show_titlepic) + doom_image=DecodeGraphicsLump("TITLEPIC"); + else + doom_image=NULL; + + + /* Auto load levels + */ + if (!STREQ(auto_loadmap,"")) + { + char *orig; + + UCase(auto_loadmap); + orig=Strdup(auto_loadmap); + p=strtok(auto_loadmap,","); + + while((p)&&(!wad)) + { + if ((wad=LoadMap(p))) + { + strcpy(mapname,p); + EditLoad(wad); + } + + p=strtok(NULL,","); + } + + if (!wad) + GuiInfoBox("ERROR","Failed to load any of the following:|%s",orig); + + Release(orig); + } + else + { + /* Create initial empty map + */ + if (initial_empty_map) + { + switch(level_style) + { + case DOOM_LEVELS: + case ULTIMATE_DOOM_LEVELS: + strcpy(mapname,"E1M1"); + break; + + case DOOM_2_LEVELS: + strcpy(mapname,"MAP01"); + break; + + } + + wad=NewMap(CreateHexenMap()); + EditLoad(wad); + } + } + + MainMenu(); + + GFX_close(); + + return(EXIT_SUCCESS); +} + +/* Clear screen and show simple GPL warning during startup +*/ +static void GPL_clear(void) +{ + GFX_clear(BLACK); + + GuiDrawInfoBox("viDOOM " VIDOOMVER " " VIDOOMRELEASE, + GUI_CENTRE,GUI_FLUSH_TOP,TRUE, + "Copyright (c) 2000 Ian Cowburn| |" + "viDOOM comes with ABSOLUTELY NO WARRANTY.|" + "For details see LICENSE.| |" + "This is free software, and you are welcome|" + "to redistribute it under certain conditions.|" + "See LICENSE for details."); +} + + +/* Returns TRUE/FALSE as to whether to create a HEXEN map +*/ +static int CreateHexenMap(void) +{ + switch(create_hexen) + { + case GEN_NO: + return(FALSE); + break; + + case GEN_YES: + return(TRUE); + break; + + case GEN_ASK: + return(GUI_yesno("Create as a HEXEN map?")); + break; + } + + return(FALSE); +} + + +/* Format an argument for the node builder command +*/ +static char *FormatArg(char *path, char *arg) +{ + char *ret; + char *p; + + ret=Grab(PATH_MAX); + p=ret; + + while(*arg) + if (*arg=='%') + { + *p=0; + strcat(ret,path); + p+=strlen(path); + arg++; + } + else + *p++=*arg++; + + return(ret); +} + + +/* Upper case a string +*/ +static void ToUpper(char *p) +{ + while(*p) + { + *p=toupper(*p); + p++; + } +} + + +/* Check whether file should be ignored for node building +*/ +static int IsIgnored(char *path) +{ + char i[PATH_MAX]; + char w[PATH_MAX]; + + if (!build_ignore[0]) + return(FALSE); + + strcpy(w,Basename(path)); + strcpy(i,build_ignore); + + ToUpper(w); + ToUpper(i); + + return((int)strstr(w,i)); +} + + +/* ---------------------------------------- TOP LEVEL MENU +*/ +static void MainMenu(void) +{ +# define MENU_NEW 1 +# define MENU_LOAD 2 +# define MENU_EDIT 3 +# define MENU_SAVE 4 +# define MENU_OPEN 5 +# define MENU_CLOSE 6 +# define MENU_PREVIEW 7 +# define MENU_ABOUT 8 +# define MENU_QUIT 9 +# define MENU_RENAME 10 +# define MENU_LISTDIR 11 +# define MENU_DEBUG 12 +# define MENU_LICENSE 13 + + static PLAT_MENU main_menu[]= + { + {"Edit current level", MENU_EDIT}, + {"Load level from open WADs", MENU_LOAD}, + {"Save current level", MENU_SAVE}, + {"Create new empty level", MENU_NEW}, + {"Change current level name", MENU_RENAME}, + {"Open PWAD file", MENU_OPEN}, + {"Close PWAD file", MENU_CLOSE}, + {"Preview level", MENU_PREVIEW}, + {"View entries in WAD directory", MENU_LISTDIR}, + {"About viDOOM " VIDOOMVER, MENU_ABOUT}, + {"View License", MENU_LICENSE}, +#ifdef VIDOOM_DEBUG + {"DEBUG", MENU_DEBUG}, +#endif + {"Quit", MENU_QUIT}, + {NULL,0} + }; + + int quit; + int edited; + + quit=FALSE; + edited=FALSE; + +#ifdef VIDOOM_DEBUG + if (FileExists("todo")) + GUI_view_file("TODO list","todo"); +#endif + + while(!quit) + { + char *open; + + GFX_clear(BLACK); + + if (doom_image) + GFX_fill_screen(doom_image); + + if (STREQ(mapname,"NONE")) + GuiDrawInfoBox(GameName(),GUI_FLUSH_RIGHT,GUI_FLUSH_TOP,FALSE, + "Current map : %s|" + " | |" + "Open WAD files :|%s", + mapname,open=OpenWads()); + else + GuiDrawInfoBox(GameName(),GUI_FLUSH_RIGHT,GUI_FLUSH_TOP,FALSE, + "Current map : %s|" + " %s| |" + "Open WAD files :|%s", + mapname,MapName(mapname),open=OpenWads()); + + Release(open); + + GFX_redraw(); + + switch(GUI_menu("MAIN MENU",10,10,main_menu,-1)) + { + case MENU_NEW: + { + char *name; + int new=TRUE; + + if ((name=GuiPickLevel("Pick map to create"))) + { + if (wad) + if ((!map_warn)|| + (new=GUI_yesno("Map already open. Continue?"))) + { + strcpy(mapname,"NONE"); + wad=ClearMap(wad); + edited=FALSE; + } + + if (new) + { + strcpy(mapname,name); + wad=NewMap(CreateHexenMap()); + EditLoad(wad); + edited=FALSE; + } + } + + break; + } + + case MENU_LOAD: + { + char *name; + int load=TRUE; + + if ((name=GuiPickLevel("Pick map to load for editting"))) + { + if (wad) + if ((!map_warn)|| + (load=GUI_yesno("Map already open. Continue?"))) + { + strcpy(mapname,"NONE"); + wad=ClearMap(wad); + edited=FALSE; + } + + if (load) + { + GuiDrawInfoBox("Loading...",GUI_CENTRE,GUI_CENTRE,TRUE, + "Loading level %s",name); + GFX_redraw(); + + if (!(wad=LoadMap(name))) + GuiInfoBox("ERROR","LoadMap(%s): %s", + name,WadErrorString()); + else + { + strcpy(mapname,name); + EditLoad(wad); + edited=FALSE; + } + } + } + + break; + } + + case MENU_EDIT: + if (!wad) + GuiInfoBox("ERROR","No map currently being edited"); + else + { + EditLoop(); + edited=TRUE; + } + break; + + case MENU_SAVE: + { + char *wadf; + + if (!wad) + GuiInfoBox("ERROR","No map currently being edited"); + else + if ((wadf=GUI_fsel + ("Select WAD to save map as",PWAD_dir,".WAD"))) + { + int ok; + + if (WadFileType(wadf)==FILE_IS_IWAD) + { + GuiInfoBox("ERROR","Cannot overwrite %s|" + "It is an IWAD",wadf); + + ok=FALSE; + } + else + { + if ((overwrite_warning)&&(FileExists(wadf))) + ok=GUI_yesno("Overwrite file?"); + else + ok=TRUE; + } + + if (ok) + { + GuiDrawInfoBox + ("Saving...",GUI_CENTRE,GUI_CENTRE,TRUE, + "Saving level %s to %s",mapname,wadf); + GFX_redraw(); + + EditCompact(); + EditSave(wad); + + if (SaveMap(wad,mapname,wadf)!=WAD_OK) + GuiInfoBox("ERROR","SaveMap(%s)|%s", + wadf,WadErrorString()); + else + { + int do_build; + char *open; + char *p; + + edited=FALSE; + + if (use_build) + do_build=!IsIgnored(wadf); + else + do_build=FALSE; + + if (do_build) + { + char path[PATH_MAX]; + char *arg[4]; + + arg[0]=build_cmd; + + if (build_in_before_out) + { + arg[1]=FormatArg(wadf,build_in); + arg[2]=FormatArg(wadf,build_out); + } + else + { + arg[2]=FormatArg(wadf,build_in); + arg[1]=FormatArg(wadf,build_out); + } + + arg[3]=NULL; + + if (RunCommand(arg,path)) + { + if ((build_always_view)&&(path[0])) + GUI_view_file("Node Builder",path); + } + else + if (path[0]) + GUI_view_file("Node Builder FAILED", + path); + else + GuiInfoBox("ERROR", + "Node builder|%s|failed|" + "(no results available)", + build_cmd); + + if (path[0]) + remove(path); + + Release(arg[1]); + Release(arg[2]); + } + + /* Check to see if we've saved on of the + currently loaded PWADS + */ + if ((open=OpenWads())) + { + p=open; + + p=strtok(p,"|"); + + while(p) + { + if (FilenamesEqual(p,wadf)) + { + if (CloseWad(wadf)!=WAD_OK) + GuiInfoBox + ("ERROR","CloseWad(%s)|%s", + wadf,WadErrorString()); + + if (AddPWAD(wadf)!=WAD_OK) + GuiInfoBox + ("ERROR","AddPWAD(%s)|%s", + wadf,WadErrorString()); + + p=NULL; + } + else + p=strtok(NULL,"|"); + } + + Release(open); + } + } + } + + Release(wadf); + } + break; + } + + case MENU_RENAME: + { + char *name; + + if (!wad) + GuiInfoBox("ERROR","No map currently being edited"); + else + if ((name=GuiPickLevel("Pick new name for level"))) + strcpy(mapname,name); + + break; + } + + case MENU_OPEN: + { + char *wadf; + + if ((wadf=GUI_fsel("Select WAD to open",PWAD_dir,".WAD"))) + { + GuiDrawInfoBox("Loading...",GUI_CENTRE,GUI_CENTRE,TRUE, + "Loading PWAD %s",wadf); + GFX_redraw(); + + if (AddPWAD(wadf)!=WAD_OK) + GuiInfoBox("ERROR","AddPWAD(%s)|%s", + wadf,WadErrorString()); + + Release(wadf); + } + } + break; + + case MENU_CLOSE: + { + char *open; + char *wadf[512]; + char *p; + int f; + + if ((open=OpenWads())) + { + p=strtok(open,"|"); + f=0; + + while(p) + { + wadf[f++]=p; + p=strtok(NULL,"|"); + } + + wadf[f]=NULL; + + f=GUI_picklist("Select WAD file to close",wadf); + + if (f!=-1) + { + if (strcmp(wadf[f],IWAD_path)==0) + GuiInfoBox("ERROR","Cannot unload base IWAD|%s", + wadf[f]); + else + { + GuiDrawInfoBox("Closing...", + GUI_CENTRE,GUI_CENTRE,TRUE, + "Closing PWAD %s",wadf[f]); + + if (CloseWad(wadf[f])!=WAD_OK) + GuiInfoBox("ERROR","CloseWad(%s)|%s", + wadf[f],WadErrorString()); + } + } + + Release(open); + } + + break; + } + + case MENU_LISTDIR: + { + char s[256]; + WadDir *wd; + Iterator i; + char **list; + WadDir **dir; + int f; + int opt; + + list=Grab(sizeof(char *)*(GetWadDirSize()+1)); + dir=Grab(sizeof(WadDir *)*(GetWadDirSize())); + i=GetWadDir(); + + f=0; + while(i) + { + wd=IteratorData(i); + i=IteratorNext(i); + + sprintf(s,"%-9s 0x%.8lx 0x%.8lx %s", + wd->name,wd->off,wd->size,Basename(wd->wad)); + + dir[f]=wd; + list[f]=Strdup(s); + + f++; + } + + list[f]=NULL; + + if ((opt=GUI_picklist + ("WAD DIR (Name, Offset, Size, WAD file)",list))!=-1) + { + char asc[9]; + unsigned char *lump; + Long len; + FILE *fp; + char *path; + int r; + + if ((lump=GetLumpFrom(dir[opt],&len))) + { + if (len) + { + path=tmpnam(NULL); + if ((fp=fopen(path,"w"))) + { + f=0; + + while(f<len) + { + strcpy(asc," "); + + fprintf(fp,"%6.6x: ",f); + + for(r=0;r<8;r++) + { + if ((f+r)<len) + { + fprintf(fp,"%2.2x ", + (unsigned char)lump[f+r]); + + if (isprint(lump[f+r])) + asc[r]=lump[f+r]; + else + asc[r]='.'; + } + else + fprintf(fp,"** "); + } + + fprintf(fp," \"%s\"\n",asc); + + f+=8; + } + + fclose(fp); + + sprintf(s,"%s (Offset 0x%lx, " + "Size 0x%lx) from %s", + dir[opt]->name, + dir[opt]->off, + dir[opt]->size, + Basename(dir[opt]->wad)); + + GUI_view_file(s,path); + + remove(path); + } + } + else + GuiInfoBox("NOTICE", + "%s is a zero length lump",dir[opt]->name); + + Release(lump); + } + } + + f=0; + while(list[f]) + { + Release(list[f]); + f++; + } + + Release(list); + Release(dir); + + break; + } + + case MENU_PREVIEW: + { + char *name; + WadMap *preview; + + if ((name=GuiPickLevel("Pick map to preview"))) + { + if (!(preview=LoadMap(name))) + GuiInfoBox("ERROR","LoadMap(%s): %s", + name,WadErrorString()); + else + { + EditPreviewWadMap(name,preview); + preview=ClearMap(preview); + } + } + + break; + } + + case MENU_ABOUT: + GuiInfoBox ("viDOOM " VIDOOMVER " " VIDOOMRELEASE, + "Copyright (c) 2000 Ian Cowburn|" + "http://www.noddybox.demon.co.uk/vidoom/| |" + + "viDOOM comes with ABSOLUTELY NO WARRANTY.|" + "For details see LICENSE.| |" + + "This is free software, and you are welcome|" + "to redistribute it under certain conditions.|" + "See LICENSE for details.| |" + + "DOOM, Ultimate DOOM, DOOM 2 and Final DOOM|" + "(c) id Software.|" + "http://www.idsoftware.com/| |" + "Levels in DOOM, DOOM 2 and Ultimate DOOM|" + "designed by id Software.| |" + "Levels in The Plutonia Experiment designed by|" + "Milo & Dario Casali whilst in Team TNT.| |" + "Levels in TNT:Evilution designed by|" + "various members of Team TNT. Get details at|" + "http://www.teamtnt.com/| |" + "Thanks to all concerned for some of my favourite|" + "Lovecraftian Dark Evil Places.| |" + "viDOOM will only work if you have the registered|" + "version of any of the follow id Sofwtare games:|" + "DOOM, Ultimate DOOM, DOOM 2 or Final DOOM"); + break; + + case MENU_LICENSE: + GUI_view_file("viDOOM " VIDOOMVER " " VIDOOMRELEASE, + "LICENSE"); + break; + + case MENU_QUIT: + if ((map_exit_warn)&&(edited)) + quit=GUI_yesno("Map may have been changed " + "- really quit?"); + else + quit=TRUE; + break; + +#ifdef VIDOOM_DEBUG + case MENU_DEBUG: + GuiInfoBox("DEBUG", + "block_bit=%d|" + "block_mask=%d|" + "side2_bit=%d|" + "side2_mask=%d|" + "upper_bit=%d|" + "upper_mask=%d|" + "lower_bit=%d|" + "lower_mask=%d|" + "empty_texture='%s'|" + "", + block_bit,block_mask, + side2_bit,side2_mask, + upper_peg_bit,upper_peg_mask, + lower_peg_bit,lower_peg_mask, + empty_texture); + break; +#endif + + default: + break; + } + } +} + + +/* END OF FILE */ diff --git a/vidoom.h b/vidoom.h new file mode 100644 index 0000000..cf4178c --- /dev/null +++ b/vidoom.h @@ -0,0 +1,44 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + viDOOM main entry point + + Provided to allow systems that don't start via main() to be used. The + platform dependent object main.o should do any necessary initialisation + for this port and then call the entry point here. + + $Id$ + +*/ + +#ifndef VIDOOM_VIDOOM_H + +#define VIDOOM_VIDOOM_H + +/* Main viDOOM entry point +*/ +int viDOOM(int argc, char *argv[]); + +#endif + +/* END OF FILE */ + diff --git a/vidoom.ini b/vidoom.ini new file mode 100644 index 0000000..a657523 --- /dev/null +++ b/vidoom.ini @@ -0,0 +1,186 @@ +# viDOOM - level editor for DOOM +# +# Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# +# Development copy of BASE.INI +# + +[Game] +game=zdoom +ask=no + +[Editor] +ask_middle_on_2sided=no +auto_block_linedefs=yes +bright=1.50 +clear_on_menu=no +clear_on_move=yes +default_ceiling_height=256 +default_edit_mode=thing +default_floor_height=0 +default_light_level=200 +default_scale=5 +grid=yes +grid_lock=yes +grid_size=64 +hover_select=add +insert_select=add +left_click_move=yes +linedef_select=2 +merge_linedef=ask +new_2sided_select=ask +sector_move=all +show_full_linedef_info=yes +tag_highlight=yes +vertex_radius=8 +width=800 +height=600 + +[Check LINEDEF] +assume_yes=yes +check_1side_lower=yes +check_1side_middle=yes +check_1side_upper=yes +check_2side_lower=yes +check_2side_middle=yes +check_2side_same_sector=yes +check_2side_upper=yes + +[viDOOM] +auto_loadmap=MAP01,E1M1 +initial_empty_map=no +load_flats=yes +load_sprites=yes +load_textures=yes +map_clear_warning=no +map_exit_warning=no +overwrite_warning=no +show_titlepic=yes +sort_flat_names=yes +sort_texture_names=yes + +[ACS] +always_view_output=yes +command=acc +dir=/tmp +script=vidoom.acs +object=vidoom.o +args=%S %O + +[Node Builder] +always_view_output=yes +command=warm +ignore=str +in_before_out=yes +infile=% +outfile=% +use=yes + +[GUI] +high=0xd2d2d2 +mid=0xb4b4b4 +low=0x8c8c8c +text=0xffffff +shadow=0x000000 +bold=0x000000 + + +[Doom] +iwad=c:\ianc\doom\doom.wad +pwad_dir=c:\ianc\doom\ +preload= +level_style=doom +vidoom_config=doom.cfg + +[Ultimate Doom] +iwad=c:\ianc\doom\doom.wad +pwad_dir=c:\ianc\doom\ +preload= +level_style=ultimate doom +vidoom_config=doom.cfg + +[Doom 2] +iwad=D:\doom\doom2.wad +pwad_dir=e:\doom\mywads\ +preload= +level_style=doom 2 +vidoom_config=doom2.cfg + +[TNT:Evilution] +iwad=D:\doom\tnt.wad +pwad_dir=e:\doom\mywads\ +preload= +level_style=doom 2 +vidoom_config=doom2.cfg + +[Plutonia Experiment] +iwad=D:\games\doom95\plutonia.wad +pwad_dir=c:\games\teamtntwads\ +preload= +level_style=doom 2 +vidoom_config=doom2.cfg + +# Home (dos) +# +[ZDoom] +iwad=D:/DOOM/DOOM2.WAD +mapinfo_lump=no +create_hexen=yes +pwad_dir=D:/DOOM/MYWAD +preload= +level_style=doom 2 +vidoom_config=zdoom.cfg + +# Home (linux) +# +[ZDoom] +iwad=/usr/games/wad/doom2.wad +mapinfo_lump=no +create_hexen=yes +pwad_dir=/home/ianc/wad +preload= +level_style=doom 2 +vidoom_config=zdoom.cfg + +# Laptop (cygwin) +# +#[ZDoom] +#iwad=f:\games\doom\doom2.wad +#mapinfo_lump=yes +#create_hexen=yes +#pwad_dir=f:\games\doom\mywads\ +#preload= +#level_style=doom 2 +#vidoom_config=zdoom.cfg + +# Work +# +#[ZDoom] +#iwad=c:\ianc\doom\doom.wad +#mapinfo_lump=yes +#create_hexen=yes +#pwad_dir=c:\ianc\doom\ +#preload= +#level_style=ultimate doom +#vidoom_config=zdoom.cfg + + +# Platform dependent info +# +[linux] +edit= diff --git a/vstring.h b/vstring.h new file mode 100644 index 0000000..5b101d6 --- /dev/null +++ b/vstring.h @@ -0,0 +1,48 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Provides portable versions of necessary string routines + + $Id$ + + +*/ + +#ifndef VIDOOM_VSTRING_H + +#define VIDOOM_VSTRING_H + +/* Does exactly the same as strcmp(), but ignores case. + The passed in strings should not be altered. + + Returns 0 if either argument is NULL +*/ +int StrCaseCmp(char *a, char *b); + +/* Does exactly the same as strncmp(), but ignores case. + The passed in strings should not be altered. + + Returns 0 if either argument is NULL +*/ +int StrNCaseCmp(char *a, char *b, int n); + +#endif @@ -0,0 +1,1144 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + WAD file definitions and readers + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include <stdio.h> +#include <string.h> + +#include "wad.h" +#include "gfx.h" +#include "mem.h" +#include "file.h" +#include "util.h" + +/* ---------------------------------------- TYPES +*/ + +typedef struct WadEnt + { + DirName name; + Long off; + Long size; + } WadEnt; + +typedef struct WadDirTable + { + int no; + WadEnt *ent; + } WadDirTable; + +typedef struct WadFile + { + FILE *fp; + char path[PATH_MAX+1]; + } WadFile; + + +/* ---------------------------------------- VARS +*/ + +#define NO_MAP_LUMPS 7 + +#define NOT_MAP_LUMP -2 +#define IGNORE_LUMP -1 +#define THING_LUMP 0 +#define VERTEX_LUMP 1 +#define LINEDEF_LUMP 2 +#define SIDEDEF_LUMP 3 +#define SECTOR_LUMP 4 +#define BEHAVIOR_LUMP 5 +#define SCRIPTS_LUMP 6 + + +#define ERR(e,r) do {wad_err=e;return(r);} while(0) +static const char *wad_errstr[WAD_NOERROR]= + { + "no error", + "File not found", + "File not an IWAD or PWAD", + "File not an IWAD", + "File not a PWAD", + "Map not found", + "Lump not found", + "Could not create file", + "Could not create MAPINFO.WAD", + "MAP mangled - spread over more than one file", + __FILE__ " - ??? BROKEN ???", + }; + +static int wad_err=WAD_OK; + +static List waddir=NULL; +static List wadlist=NULL; + + +/* ---------------------------------------- MACROS +*/ +#define ERR(e,r) do {wad_err=e;return(r);} while(0) + + +/* ---------------------------------------- PREDICATE FUNCTIONS +*/ +static int FindDirEnt(void *a,void *b) +{ + WadDir *aa; + + aa=(WadDir *)a; + + return(STREQ(aa->name,b)); +} + + +static int FindWAD(void *a,void *b) +{ + WadFile *aa; + + aa=a; + + return(FilenamesEqual(aa->path,b)); +} + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static char *GetName(FILE *fp) +{ + static DirName d; + + FRead(fp,d,8); + d[sizeof(d)-1]=0; + return(d); +} + + +static void PutName(FILE *fp,char *p) +{ + int f; + + f=0; + while(f<8) + { + fputc(*p,fp); + + if (*p) + p++; + + f++; + } +} + + +static void PutDirEnt(FILE *fp,Long off,Long size,char *p) +{ + PutLong(fp,off); + PutLong(fp,size); + PutName(fp,p); +} + + +static int HandledMapLump(char *n) +{ + static struct + { + char *name; + int lump; + } lump[]= { + {"THINGS",THING_LUMP}, + {"VERTEXES",VERTEX_LUMP}, + {"SIDEDEFS",SIDEDEF_LUMP}, + {"LINEDEFS",LINEDEF_LUMP}, + {"SECTORS",SECTOR_LUMP}, + {"BEHAVIOR",BEHAVIOR_LUMP}, + {"SCRIPTS",SCRIPTS_LUMP}, + {"SEGS",IGNORE_LUMP}, + {"SSECTORS",IGNORE_LUMP}, + {"NODES",IGNORE_LUMP}, + {"REJECT",IGNORE_LUMP}, + {"BLOCKMAP",IGNORE_LUMP}, + {NULL,NOT_MAP_LUMP}, + }; + + int f; + + f=0; + while(lump[f].name) + { + if (STREQ(lump[f].name,n)) + return(lump[f].lump); + + f++; + } + + return(NOT_MAP_LUMP); +} + + +static void GetMapLumps(Iterator i, WadDir *l[]) +{ + WadDir *dir; + int lc; + int f; + + for(f=0;f<NO_MAP_LUMPS;f++) + l[f]=NULL; + + /* Skip map entry + */ + i=IteratorNext(i); + + while(i) + { + dir=IteratorData(i); + + lc=HandledMapLump(dir->name); + + switch(lc) + { + case IGNORE_LUMP: + break; + + case NOT_MAP_LUMP: + i=IteratorClear(i); + break; + + default: + l[lc]=dir; + break; + } + + if (i) + i=IteratorNext(i); + } +} + + +static WadDir *GetDir(char *name) +{ + Iterator i; + WadDir *w; + + if (!(i=ListFindElem(waddir,FindDirEnt,name))) + return(NULL); + + w=IteratorData(i); + IteratorClear(i); + + return(w); +} + + +static WadFile *GetWADFile(char *name) +{ + Iterator i; + WadFile *w; + + if (!(i=ListFindElem(wadlist,FindWAD,name))) + return(NULL); + + w=IteratorData(i); + IteratorClear(i); + + return(w); +} + + +static void *LoadDirEnt(WadDir *d,Long *size) +{ + void *ret; + WadFile *wf; + + if (!(wf=GetWADFile(d->wad))) + return(NULL); + + ret=Grab(d->size); + fseek(wf->fp,(long)d->off,SEEK_SET); + FRead(wf->fp,ret,d->size); + + if (size) + *size=d->size; + + return(ret); +} + + +static WadDirTable *ReadDir(char *wad, char *expect) +{ + char type[4]; + WadDirTable *d; + int f; + FILE *fp; + + if (!(fp=fopen(wad,"rb"))) + ERR(WAD_FILE_NOT_FOUND,NULL); + + FRead(fp,type,4); + + if (STRNEQ("IWAD",expect)&&(!STRNEQ("IWAD",type))) + { + fclose(fp); + ERR(WAD_NOT_IWAD,NULL); + } + else if (STRNEQ("PWAD",expect)&&(!STRNEQ("PWAD",type))) + { + ERR(WAD_NOT_PWAD,NULL); + fclose(fp); + } + + d=Grab(sizeof(WadDirTable)); + d->no=GetLong(fp); + + d->ent=Grab(sizeof(WadEnt)*d->no); + + fseek(fp,(long)GetLong(fp),SEEK_SET); + + for(f=0;f<d->no;f++) + { + d->ent[f].off=GetLong(fp); + d->ent[f].size=GetLong(fp); + strcpy(d->ent[f].name,GetName(fp)); + } + + fclose(fp); + return(d); +} + + +static int AddWAD(char *wad,char *type) +{ + Iterator i; + WadDirTable *d; + WadDir w; + int f; + char *wadname; + WadFile wf; + int check; + int e2m1; + int map01; + + /* Read in the directory from the WAD + */ + if (!waddir) + { + waddir=ListNew(sizeof(WadDir)); + wadlist=ListNew(sizeof(WadFile)); + } + + if (!(d=ReadDir(wad,type))) + return(wad_err); + + /* Find the WAD name in the WAD list + */ + if ((i=ListFindElem(wadlist,FindWAD,wad))) + { + wadname=((WadFile *)IteratorData(i))->path; + IteratorClear(i); + } + else + { + if (!(wf.fp=fopen(wad,"rb"))) + GFX_exit(EXIT_FAILURE,"BIZARRE: WAD opening failed after " + "first open succeeded on\n\t%s\n",wad); + + strcpy(wf.path,wad); + wadname=Strdup(wf.path); + ListInsert(wadlist,&wf); + } + + /* Add the directory entries to the global dir + */ + e2m1=FALSE; + map01=FALSE; + check=STRNEQ("IWAD",type); + + for(f=d->no-1;f>=0;f--) + { + w.wad=wadname; + w.off=d->ent[f].off; + w.size=d->ent[f].size; + strcpy(w.name,d->ent[f].name); + + if (check) + { + if (STREQ("E2M1",d->ent[f].name)) + e2m1=TRUE; + + if (STREQ("MAP01",d->ent[f].name)) + map01=TRUE; + } + + ListInsert(waddir,&w); + } + + /* Shareware checks fo IWAD files + */ + if (check) + switch(level_style) + { + case DOOM_LEVELS: + case ULTIMATE_DOOM_LEVELS: + if (e2m1) + break; + case DOOM_2_LEVELS: + if (map01) + break; + default: + GFX_exit(EXIT_FAILURE, + "You MUST have an IWAD from the registered " + "version of\nDOOM, ULTIMATE DOOM, DOOM II " + "or FINAL DOOM\n"); + break; + } + + Release(d->ent); + Release(d); + + ERR(WAD_OK,WAD_OK); +} + + +/* ---------------------------------------- MAPINFO FUNCTIONS +*/ +static char *LoadMAPINFO(void) +{ + char path[PATH_MAX+1]; + char *lump; + Long size; + + strcpy(path,PWAD_dir); + strcat(path,"mapinfo.wad"); + + if (AddPWAD(path)!=WAD_OK) + return(NULL); + + lump=GetLump("MAPINFO",&size); + CloseWad(path); + + if (lump) + { + lump=ReGrab(lump,size+1); + lump[size]=0; + } + + return(lump); +} + + +int SaveMAPINFO(char *info) +{ + char path[PATH_MAX+1]; + FILE *fp; + Long off; + + strcpy(path,PWAD_dir); + strcat(path,"mapinfo.wad"); + + if (!(fp=fopen(path,"wb"))) + return(FALSE); + + off=strlen(info)+12; + + /* Put PWAD header + */ + fputs("PWAD",fp); + PutLong(fp,1); + PutLong(fp,off); + + /* Put MAPINFO + */ + fputs(info,fp); + + /* Create directory + */ + PutDirEnt(fp,12,strlen(info),"MAPINFO"); + fclose(fp); + + return(TRUE); +} + + +/* ---------------------------------------- EXPORTED FUNCTIONS +*/ + +int AddIWAD(char *wad) +{ + return (AddWAD(wad,"IWAD")); +} + + +int AddPWAD(char *wad) +{ + return (AddWAD(wad,"PWAD")); +} + + +int CloseWad(char *wad) +{ + WadDir *w; + WadFile *wf; + Iterator i; + char *p=NULL; + + if (!(i=ListFindElem(wadlist,FindWAD,wad))) + ERR(WAD_FILE_NOT_FOUND,WAD_FILE_NOT_FOUND); + + wf=IteratorData(i); + + if (wf->fp) + fclose(wf->fp); + + i=IteratorDelete(i); + IteratorClear(i); + + i=ListIterator(waddir); + + while(i) + { + w=IteratorData(i); + + if (FilenamesEqual(w->wad,wad)) + { + p=w->wad; + i=IteratorDelete(i); + } + else + i=IteratorNext(i); + } + + if (p) + Release(p); + + IteratorClear(i); + + ERR(WAD_OK,WAD_OK); +} + + +char *OpenWads(void) +{ + Iterator i; + WadFile *w; + char *ret; + + if (!(i=ListIterator(wadlist))) + ret=Strdup(""); + else + { + w=IteratorData(i); + ret=Grab(PATH_MAX+1); + strcpy(ret,w->path); + i=IteratorNext(i); + + while(i) + { + strcat(ret,"|"); + w=IteratorData(i); + ret=ReGrab(ret,strlen(ret)+PATH_MAX+1); + strcat(ret,w->path); + i=IteratorNext(i); + } + } + + return(ret); +} + + +Iterator GetWadDir(void) +{ + return(ListIterator(waddir)); +} + + +int GetWadDirSize(void) +{ + return(ListSize(waddir)); +} + + +void *GetLump(char *name, Long *size) +{ + WadDir *d; + void *ret; + + if (size) + *size=0; + + if (!(d=GetDir(name))) + ERR(WAD_LUMP_NOT_FOUND,NULL); + + if ((ret=LoadDirEnt(d,size))) + ERR(WAD_OK,ret); + else + ERR(WAD_FILE_NOT_FOUND,NULL); +} + + +void *GetLumpFrom(WadDir *d, Long *size) +{ + void *ret; + + if (size) + *size=0; + + if ((ret=LoadDirEnt(d,size))) + ERR(WAD_OK,ret); + else + ERR(WAD_FILE_NOT_FOUND,NULL); +} + + +WadMap *LoadMap(char *name) +{ + Iterator i; + WadMap *map; + WadDir *dir; + WadFile *wf; + Thing thing; + Vertex vertex; + Sidedef sidedef; + Linedef linedef; + Sector sector; + int f; + int lc; + WadDir *lump[NO_MAP_LUMPS]; + + if (!(i=ListFindElem(waddir,FindDirEnt,name))) + ERR(WAD_MAP_NOT_FOUND,NULL); + + dir=IteratorData(i); + + if (!(wf=GetWADFile(dir->wad))) + ERR(WAD_FILE_NOT_FOUND,NULL); + + GetMapLumps(i,lump); + + /* Check for a mangled map + */ + for(f=0;f<NO_MAP_LUMPS;f++) + if ((lump[f])&&(!FilenamesEqual(wf->path,lump[f]->wad))) + ERR(WAD_MAP_MANGLED,NULL); + + map=Grab(sizeof(WadMap)); + map->thing=MapNew(sizeof(Thing)); + map->vertex=MapNew(sizeof(Vertex)); + map->sidedef=MapNew(sizeof(Sidedef)); + map->linedef=MapNew(sizeof(Linedef)); + map->sector=MapNew(sizeof(Sector)); + + map->scripts=NULL; + map->behavior=NULL; + map->mapinfo=NULL; + + if (lump[BEHAVIOR_LUMP]) + map->hexen=TRUE; + else + map->hexen=FALSE; + + for(lc=0;lc<NO_MAP_LUMPS;lc++) + { + dir=lump[lc]; + + if (dir) + switch (lc) + { + case THING_LUMP: + fseek(wf->fp,(long)dir->off,SEEK_SET); + + if (map->hexen) + for(f=0;f<dir->size/THING_SIZE_HEXEN;f++) + { + int a; + + thing.id=GetUShort(wf->fp); + thing.x=GetShort(wf->fp); + thing.y=GetShort(wf->fp); + thing.z=GetShort(wf->fp); + thing.ang=GetShort(wf->fp); + thing.type=GetShort(wf->fp); + thing.flags=GetShort(wf->fp); + thing.special=GetByte(wf->fp); + + for(a=0;a<5;a++) + thing.args[a]=GetByte(wf->fp); + + MapAdd(map->thing,f,&thing); + } + else + for(f=0;f<dir->size/THING_SIZE;f++) + { + thing.x=GetShort(wf->fp); + thing.y=GetShort(wf->fp); + thing.ang=GetShort(wf->fp); + thing.type=GetShort(wf->fp); + thing.flags=GetShort(wf->fp); + + MapAdd(map->thing,f,&thing); + } + break; + + case VERTEX_LUMP: + fseek(wf->fp,(long)dir->off,SEEK_SET); + + for(f=0;f<dir->size/VERTEX_SIZE;f++) + { + vertex.x=GetShort(wf->fp); + vertex.y=GetShort(wf->fp); + MapAdd(map->vertex,f,&vertex); + } + break; + + case SIDEDEF_LUMP: + fseek(wf->fp,(long)dir->off,SEEK_SET); + + for(f=0;f<dir->size/SIDEDEF_SIZE;f++) + { + sidedef.x=GetShort(wf->fp); + sidedef.y=GetShort(wf->fp); + strcpy(sidedef.upper,GetName(wf->fp)); + strcpy(sidedef.lower,GetName(wf->fp)); + strcpy(sidedef.middle,GetName(wf->fp)); + sidedef.sector=GetShort(wf->fp); + MapAdd(map->sidedef,f,&sidedef); + } + break; + + case LINEDEF_LUMP: + fseek(wf->fp,(long)dir->off,SEEK_SET); + + if (map->hexen) + for(f=0;f<dir->size/LINEDEF_SIZE_HEXEN;f++) + { + int a; + + linedef.from=GetShort(wf->fp); + linedef.to=GetShort(wf->fp); + linedef.flags=GetShort(wf->fp); + linedef.type=GetByte(wf->fp); + + for(a=0;a<5;a++) + linedef.args[a]=GetByte(wf->fp); + + linedef.right=GetShort(wf->fp); + linedef.left=GetShort(wf->fp); + MapAdd(map->linedef,f,&linedef); + } + else + for(f=0;f<dir->size/LINEDEF_SIZE;f++) + { + linedef.from=GetShort(wf->fp); + linedef.to=GetShort(wf->fp); + linedef.flags=GetShort(wf->fp); + linedef.type=GetShort(wf->fp); + linedef.tag=GetShort(wf->fp); + linedef.right=GetShort(wf->fp); + linedef.left=GetShort(wf->fp); + MapAdd(map->linedef,f,&linedef); + } + break; + + case SECTOR_LUMP: + fseek(wf->fp,(long)dir->off,SEEK_SET); + + for(f=0;f<dir->size/SECTOR_SIZE;f++) + { + sector.floor=GetShort(wf->fp); + sector.ceiling=GetShort(wf->fp); + strcpy(sector.floor_t,GetName(wf->fp)); + strcpy(sector.ceiling_t,GetName(wf->fp)); + sector.light=GetShort(wf->fp); + sector.special=GetShort(wf->fp); + sector.tag=GetShort(wf->fp); + MapAdd(map->sector,f,§or); + } + break; + + /* No action yet for these - will simply be written back + */ + case BEHAVIOR_LUMP: + map->behavior_size=dir->size; + map->behavior=Grab(dir->size); + fseek(wf->fp,(long)dir->off,SEEK_SET); + FRead(wf->fp,map->behavior,dir->size); + break; + + case SCRIPTS_LUMP: + map->scripts_size=dir->size; + map->scripts=Grab(dir->size+1); + fseek(wf->fp,(long)dir->off,SEEK_SET); + FRead(wf->fp,map->scripts,dir->size); + map->scripts[map->scripts_size]=0; + break; + + default: + break; + } + } + + /* Locate the MAPINFO lump if config says so + */ + if (mapinfo_lump) + map->mapinfo=UnMSDOS(LoadMAPINFO()); + + ERR(WAD_OK,map); +} + + +WadMap *NewMap(int hexen) +{ + WadMap *map; + + map=Grab(sizeof(WadMap)); + + map->hexen=hexen; + + map->thing=MapNew(sizeof(Thing)); + map->vertex=MapNew(sizeof(Vertex)); + map->sidedef=MapNew(sizeof(Sidedef)); + map->linedef=MapNew(sizeof(Linedef)); + map->sector=MapNew(sizeof(Sector)); + + if (mapinfo_lump) + map->mapinfo=UnMSDOS(LoadMAPINFO()); + else + map->mapinfo=NULL; + + map->behavior_size=0; + map->behavior=NULL; + map->scripts_size=0; + map->scripts=NULL; + + return(map); +} + + +WadMap *ClearMap(WadMap *map) +{ + MapClear(map->thing); + MapClear(map->vertex); + MapClear(map->sidedef); + MapClear(map->linedef); + MapClear(map->sector); + + if (map->mapinfo) + Release(map->mapinfo); + + if (map->behavior) + Release(map->behavior); + + if (map->scripts) + Release(map->scripts); + + Release(map); + return(NULL); +} + + +int SaveMap(WadMap *map, char *name, char *wad) +{ + FILE *fp; + Long off; + int f; + Long no; + + if (!(fp=fopen(wad,"wb"))) + ERR(WAD_COULD_NOT_CREATE,WAD_COULD_NOT_CREATE); + + /* Calc number of entries in MAP + */ + if (map->hexen) + no=13; + else + no=11; + + /* Calc offset for directory + */ + off=12; + + off+=MapSize(map->vertex)*VERTEX_SIZE+ + MapSize(map->sidedef)*SIDEDEF_SIZE+ + MapSize(map->sector)*SECTOR_SIZE; + + if (map->hexen) + { + off+=MapSize(map->linedef)*LINEDEF_SIZE_HEXEN+ + MapSize(map->thing)*THING_SIZE_HEXEN; + + off+=map->behavior_size+map->scripts_size; + } + else + { + off+=MapSize(map->linedef)*LINEDEF_SIZE+ + MapSize(map->thing)*THING_SIZE; + } + + /* Save MAPINFO.WAD + */ + if ((map->mapinfo)&&(strlen(map->mapinfo))) + { + char *info; + + info=ApplyMSDOS(map->mapinfo,FALSE); + + if (!SaveMAPINFO(info)) + { + Release(info); + ERR(WAD_MAPINFO_FAILED,WAD_MAPINFO_FAILED); + } + + Release(info); + } + + /* Put PWAD header + */ + fputs("PWAD",fp); + PutLong(fp,no); + PutLong(fp,off); + + /* Put THINGS + */ + for(f=0;f<MapSize(map->thing);f++) + { + Thing *t; + + t=MapElem(map->thing,f); + + if (map->hexen) + { + int i; + + PutUShort(fp,t->id); + PutShort(fp,t->x); + PutShort(fp,t->y); + PutShort(fp,t->z); + PutShort(fp,t->ang); + PutShort(fp,t->type); + PutShort(fp,t->flags); + PutByte(fp,t->special); + + for(i=0;i<5;i++) + PutByte(fp,t->args[i]); + } + else + { + PutShort(fp,t->x); + PutShort(fp,t->y); + PutShort(fp,t->ang); + PutShort(fp,t->type); + PutShort(fp,t->flags); + } + } + + /* Put LINEDEFS + */ + for(f=0;f<MapSize(map->linedef);f++) + { + Linedef *l; + + l=MapElem(map->linedef,f); + + if (map->hexen) + { + int i; + + PutShort(fp,l->from); + PutShort(fp,l->to); + PutShort(fp,l->flags); + PutByte(fp,(Byte)l->type); + + for(i=0;i<5;i++) + PutByte(fp,l->args[i]); + + PutShort(fp,l->right); + PutShort(fp,l->left); + } + else + { + PutShort(fp,l->from); + PutShort(fp,l->to); + PutShort(fp,l->flags); + PutShort(fp,l->type); + PutShort(fp,l->tag); + PutShort(fp,l->right); + PutShort(fp,l->left); + } + } + + /* Put SIDEDEFS + */ + for(f=0;f<MapSize(map->sidedef);f++) + { + Sidedef *s; + + s=MapElem(map->sidedef,f); + PutShort(fp,s->x); + PutShort(fp,s->y); + PutName(fp,s->upper); + PutName(fp,s->lower); + PutName(fp,s->middle); + PutShort(fp,s->sector); + } + + /* Put VERTEXES + */ + for(f=0;f<MapSize(map->vertex);f++) + { + Vertex *v; + + v=MapElem(map->vertex,f); + PutShort(fp,v->x); + PutShort(fp,v->y); + } + + /* Skip SEGS, SSECTORS, NODE + */ + + /* Put SECTORS + */ + for(f=0;f<MapSize(map->sector);f++) + { + Sector *s; + + s=MapElem(map->sector,f); + PutShort(fp,s->floor); + PutShort(fp,s->ceiling); + PutName(fp,s->floor_t); + PutName(fp,s->ceiling_t); + PutShort(fp,s->light); + PutShort(fp,s->special); + PutShort(fp,s->tag); + } + + /* Skip REJECT, BLOCKMAP + */ + + /* Put BEHAVIOR and SCRIPTS + */ + if (map->hexen) + { + int f; + + if (map->behavior) + for(f=0;f<map->behavior_size;f++) + PutByte(fp,map->behavior[f]); + + if (map->scripts) + for(f=0;f<map->scripts_size;f++) + PutByte(fp,map->scripts[f]); + } + + /* Create directory + */ + off=12; + PutDirEnt(fp,off,0,name); + + if (map->hexen) + { + PutDirEnt(fp,off,MapSize(map->thing)*THING_SIZE_HEXEN,"THINGS"); + off+=MapSize(map->thing)*THING_SIZE_HEXEN; + } + else + { + PutDirEnt(fp,off,MapSize(map->thing)*THING_SIZE,"THINGS"); + off+=MapSize(map->thing)*THING_SIZE; + } + + if (map->hexen) + { + PutDirEnt(fp,off,MapSize(map->linedef)*LINEDEF_SIZE_HEXEN,"LINEDEFS"); + off+=MapSize(map->linedef)*LINEDEF_SIZE_HEXEN; + } + else + { + PutDirEnt(fp,off,MapSize(map->linedef)*LINEDEF_SIZE,"LINEDEFS"); + off+=MapSize(map->linedef)*LINEDEF_SIZE; + } + + PutDirEnt(fp,off,MapSize(map->sidedef)*SIDEDEF_SIZE,"SIDEDEFS"); + off+=MapSize(map->sidedef)*SIDEDEF_SIZE; + + PutDirEnt(fp,off,MapSize(map->vertex)*VERTEX_SIZE,"VERTEXES"); + off+=MapSize(map->vertex)*VERTEX_SIZE; + + PutDirEnt(fp,off,0,"SEGS"); + PutDirEnt(fp,off,0,"SSECTORS"); + PutDirEnt(fp,off,0,"NODES"); + + PutDirEnt(fp,off,MapSize(map->sector)*SECTOR_SIZE,"SECTORS"); + off+=MapSize(map->sector)*SECTOR_SIZE; + + PutDirEnt(fp,off,0,"REJECT"); + PutDirEnt(fp,off,0,"BLOCKMAP"); + + if (map->hexen) + { + PutDirEnt(fp,off,map->behavior_size,"BEHAVIOR"); + off+=map->behavior_size; + + PutDirEnt(fp,off,map->scripts_size,"SCRIPTS"); + off+=map->scripts_size; + } + + fclose(fp); + + ERR(WAD_OK,WAD_OK); +} + + +int WadError(void) +{ + return(wad_err); +} + + +const char *WadErrorString(void) +{ + return(wad_errstr[wad_err]); +} + + +int WadFileType(char *wad) +{ + FILE *fp; + char type[4]; + + if (!(fp=fopen(wad,"rb"))) + return(FILE_IS_NOT_WAD); + + FRead(fp,type,4); + fclose(fp); + + if (STRNEQ("PWAD",type)) + return(FILE_IS_PWAD); + + if (STRNEQ("IWAD",type)) + return(FILE_IS_IWAD); + + return(FILE_IS_NOT_WAD); +} + +/* END OF FILE */ @@ -0,0 +1,294 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + WAD file definitions and readers + + These things should be noted about the handling of mutiple entries + from different wad files: + + - Entries in later WADs overwrite earlier loaded WADs. + + - New entries for MAPxy and ExMy automatically overwrite the + following entries from the previous map: + THINGS + LINEDEFS + SIDEDEFS + VERTEXES + SEGS + SSECTORS + NODES + SECTORS + REJECT + BLOCKMAP + + If these entries don't appear in the new map they are + replaced by dummy entries of zero length. + + $Id$ + +*/ + +#ifndef VIDOOM_WAD_H + +#define VIDOOM_WAD_H + +#include "map.h" +#include "list.h" + +/* Basic WAD types +*/ +#define DIRNAME_LEN 8 +typedef char DirName[DIRNAME_LEN+1]; + +/* Directory types +*/ +typedef struct WadDir + { + char *wad; /* WAD the entry is from */ + DirName name; /* Lump name */ + Long off; /* Lump offset */ + Long size; /* Lump size */ + } WadDir; + + +/* Base level types and their sizes in the WAD +*/ +#define THING_SIZE (sizeof(Short)*5) +#define THING_SIZE_HEXEN (sizeof(Short)*6+sizeof(UShort)+6) + +/* Note that the fields are NOT in the order they appear in the WAD +*/ +typedef struct Thing + { + Short x; + Short y; + Short ang; + Short type; + Short flags; + + /* Extra fields for HEXEN things + */ + UShort id; + Byte special; + Short z; + Short args[5]; + } Thing; + +#define VERTEX_SIZE (sizeof(Short)*2) + +typedef struct Vertex + { + Short x; + Short y; + } Vertex; + +#define SIDEDEF_SIZE (sizeof(Short)*3+DIRNAME_LEN*3) + +typedef struct Sidedef + { + Short x; + Short y; + DirName upper; + DirName lower; + DirName middle; + Short sector; + } Sidedef; + +#define LINEDEF_SIZE (sizeof(Short)*7) +#define LINEDEF_SIZE_HEXEN (sizeof(Short)*5+6) + +/* Note that the fields are NOT in the order they appear in the WAD +*/ +typedef struct + { + Short from; + Short to; + Short flags; + Short type; /* In HEXEN mode this is a Byte */ + Short tag; + Short right; + Short left; + + /* Extra fields for HEXEN linedefs + */ + Byte args[5]; + } Linedef; + +#define SECTOR_SIZE (sizeof(Short)*5+DIRNAME_LEN*2) + +typedef struct Sector + { + Short floor; + Short ceiling; + DirName floor_t; + DirName ceiling_t; + Short light; + Short special; + Short tag; + } Sector; + +typedef struct WadMap + { + int hexen; /* TRUE if map in HEXEN/extended format */ + + Map thing; + Map vertex; + Map linedef; + Map sidedef; + Map sector; + + /* ZDOOM/HEXEN lumps and info + */ + int behavior_size; + char *behavior; + int scripts_size; + char *scripts; + char *mapinfo; + } WadMap; + + +/* Error codes +*/ +#define WAD_OK 0 +#define WAD_FILE_NOT_FOUND 1 +#define WAD_NOT_WAD 2 +#define WAD_NOT_IWAD 3 +#define WAD_NOT_PWAD 4 +#define WAD_MAP_NOT_FOUND 5 +#define WAD_LUMP_NOT_FOUND 6 +#define WAD_COULD_NOT_CREATE 7 +#define WAD_MAPINFO_FAILED 8 +#define WAD_MAP_MANGLED 9 +#define WAD_BROKEN 10 /* Must be last error code */ + +#define WAD_NOERROR (WAD_BROKEN+1) + + +/* Returns for WAD file type +*/ +#define FILE_IS_NOT_WAD 0 +#define FILE_IS_IWAD 1 +#define FILE_IS_PWAD 2 + + +/* Add the named IWAD to the list of open WADs. Returns non-zero on error. +*/ +int AddIWAD(char *wad); + + +/* Add the named PWAD to the list of open WADs. Returns non-zero on error. + AddPWAD() and AddIWAD() are by and large identical - they just include + different checking so that the initial loading of IWAD will not allow it + be a PWAD, and also do shareware checking. +*/ +int AddPWAD(char *wad); + + +/* Close a wad from the open list. Removes entries for this wad fom the + global directory, replacing entries from prevous WADs (if any). + + Returns non-zero on error. +*/ +int CloseWad(char *wad); + + +/* Returns a list of open WADS as a string like "<wad>|<wad>|..". The return + must be Release()ed after use. +*/ +char *OpenWads(void); + + +/* If you wish access to the directory of all the open wads, this allows + them to retrieved as a List of WadDir. Returns NULL if none. + + Note the directory is organised with the last loaded WAD files first. + This is the quickest (though hungriest) method for ensuring that later + WAD resources override earlier ones. +*/ +Iterator GetWadDir(void); + +/* No of entries in WadDir list +*/ +int GetWadDirSize(void); + + +/* Retrieves the named lump from the WADs. Return is a pointer to size + bytes. If size is NULL it is not set. + + The pointer must be Release()'ed by the user afterwards. + + NULL indicates the lump could not be found. +*/ +void *GetLump(char *name, Long *size); + + +/* Retrieves the lump pointed to by the WAD directory entry. + Return is a pointer to size bytes. If size is NULL it is not set. + + The pointer must be Release()'ed by the user afterwards. + + NULL indicates the WAD dir entry was invalid. +*/ +void *GetLumpFrom(WadDir *ent, Long *size); + + +/* Load a MAPxx or ExMy map from the open wads. Returns NULL on error. +*/ +WadMap *LoadMap(char *name); + + +/* Create a new empty map. Creates a HEXEN map if hexen is TRUE. +*/ +WadMap *NewMap(int hexen); + + +/* Release the memory for a WadMap +*/ +WadMap *ClearMap(WadMap *map); + + +/* Save the passed WadMap as a MAPxx or ExMy PWAD. Note that this PWAD is + overwritten and the map is the sole object written to the PWAD. Returns + non-zero on error. +*/ +int SaveMap(WadMap *map, char *name, char *wad); + + +/* Return the current error code +*/ +int WadError(void); + + +/* Return a string for the error code +*/ +const char *WadErrorString(void); + + +/* Given a filename returns the type of WAD file it is. + Returns FILE_IS_NOT_WAD if file is not a WAD file, or doesn't exist. +*/ +int WadFileType(char *wad); + + +#endif + +/* END OF FILE */ diff --git a/waddir.c b/waddir.c new file mode 100644 index 0000000..1487f21 --- /dev/null +++ b/waddir.c @@ -0,0 +1,76 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Tester for routines in wad.c + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" + +#include <stdio.h> + +#include "wad.h" +#include "gfx.h" +#include "mem.h" +#include "file.h" + +#define SCRW 640 +#define SCRH 480 + + +/* ---------------------------------------- MAIN +*/ +int viDOOM(int argc,char *argv[]) +{ + Iterator i; + + if (argc<2) + GFX_exit(1,"usage:%s wad\n",argv[0]); + + LoadGlobalsPart1(); + LoadGlobalsPart2(); + + if (AddIWAD(argv[1])!=WAD_OK) + if (AddPWAD(argv[1])!=WAD_OK) + GFX_exit(1,"wadderr=%s\n",WadErrorString()); + + i=GetWadDir(); + + while(i) + { + WadDir *w; + + w=IteratorData(i); + + printf("%-10s 0x%.8lx 0x%.8lx %s\n", + w->name,w->off,w->size,Basename(w->wad)); + + i=IteratorNext(i); + } + + return(0); +} + + +/* END OF FILE */ Binary files differdiff --git a/wadtest.c b/wadtest.c new file mode 100644 index 0000000..1d1b586 --- /dev/null +++ b/wadtest.c @@ -0,0 +1,79 @@ +/* + + viDOOM - level editor for DOOM + + Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Tester for routines in wad.c + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include "globals.h" +#include "wad.h" +#include "gfx.h" +#include "mem.h" + +#include <stdio.h> + + +/* ---------------------------------------- MAIN +*/ +int viDOOM(int argc,char *argv[]) +{ + int c; + + if (argc<3) + GFX_exit(1,"usage:%s IWAD PWAD\n",argv[0]); + + LoadGlobalsPart1(); + LoadGlobalsPart2(); + + if (AddIWAD(argv[1])!=WAD_OK) + GFX_exit(1,"wadderr=%s\n",WadErrorString()); + + c=0; + + while(1) + { + void *lump; + + if (AddPWAD(argv[2])!=WAD_OK) + GFX_exit(1,"AddPWAD()=%s (cycles=%d)\n",WadErrorString(),c); + + if (CloseWad(argv[2])!=WAD_OK) + GFX_exit(1,"CloseWad()=%s (cycles=%d)\n",WadErrorString(),c); + + if (!(lump=GetLump("TITLEPIC",NULL))) + GFX_exit(1,"GetLump()=%s (cycles=%d)\n",WadErrorString(),c); + + Release(lump); + + c++; + + if ((c%1000)==0) + printf("%d cycles done\n",c); + } + + return(0); +} + + +/* END OF FILE */ diff --git a/zdoom.cfg b/zdoom.cfg new file mode 100644 index 0000000..8aeb27a --- /dev/null +++ b/zdoom.cfg @@ -0,0 +1,719 @@ +# viDOOM - level editor for DOOM +# +# Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# ------------------------------------------------------------------------- +# +# $Id: zdoom.cfg,v 1.8 2000/11/13 16:16:38 dosuser Exp dosuser $ +# +# ZDoom config file. See the heading of doom.cfg for details +# + +%INCLUDE_FILES +@DOOM_2_LEVEL_STYLE +doom2.cfg +@END DOOM_2_LEVEL_STYLE + +@DOOM_LEVEL_STYLE +doom.cfg +@END DOOM_LEVEL_STYLE + +# THING_CLASSES section +# +%THING_CLASSES +Sound|0x808080 +Stealth Monster|0xd01010 +Special ZDOOM/BOOM|0xff0000 + +# THING_TYPES section +# +%THING_TYPES +Stealth Monster|Stealth Former Human|9061|20|POSSA1 +Stealth Monster|Stealth Former Sergeant|9060|20|SPOSA1 +Stealth Monster|Stealth Imp|9057|20|TROOA1 +Stealth Monster|Stealth Demon|3002|30|SARGA1 +Stealth Monster|Stealth Cacodemon|9053|31|HEADA1 +Stealth Monster|Stealth Baron of Hell|9052|24|BOSSA1 +@DOOM_2_LEVEL_STYLE +Stealth Monster|Stealth Chaingunner|9060|20|CPOSA1 +Stealth Monster|Stealth Hell Knight|9056|24|BOS2A1C1 +Stealth Monster|Stealth Arachnotron|9050|64|BSPIA1D1 +Stealth Monster|Stealth Revenant|9059|20|SKELA1D1 +Stealth Monster|Stealth Mancubus|9058|48|FATTA1 +Stealth Monster|Stealth Arch Vile|9051|20|VILEA1D1 +@END DOOM_2_LEVEL_STYLE + +Scenery|Spark|9206|16|- +Scenery|Red fountain|9027|16|- +Scenery|Green fountain|9028|16|- +Scenery|Blue fountain|9029|16|- +Scenery|Yellow fountain|9030|16|- +Scenery|Purple fountain|9031|16|- +Scenery|Black fountain|9032|16|- +Scenery|White fountain|9033|16|- + +Player Start|Player 4 start|4001|16|PLAYA1 +Player Start|Player 5 start|4002|16|PLAYA1 +Player Start|Player 6 start|4003|16|PLAYA1 +Player Start|Player 7 start|4004|16|PLAYA1 + +Sound|Sector plays seq 0|1400|16|- +Sound|Sector plays seq 1|1401|16|- +Sound|Sector plays seq 2|1402|16|- +Sound|Sector plays seq 3|1403|16|- +Sound|Sector plays seq 4|1404|16|- +Sound|Sector plays seq 5|1405|16|- +Sound|Sector plays seq 6|1406|16|- +Sound|Sector plays seq 7|1407|16|- +Sound|Sector plays seq 8|1408|16|- +Sound|Sector plays seq 9|1409|16|- +Sound|Sector plays param|1411|16|- +Sound|Ambient sound 1|14001|16|- +Sound|Ambient sound 2|14002|16|- +Sound|Ambient sound 3|14003|16|- +Sound|Ambient sound 4|14004|16|- +Sound|Ambient sound 5|14005|16|- +Sound|Ambient sound 6|14006|16|- +Sound|Ambient sound 7|14007|16|- +Sound|Ambient sound 8|14008|16|- +Sound|Ambient sound 9|14009|16|- +Sound|Ambient sound 10|14010|16|- +Sound|Ambient sound 11|14011|16|- +Sound|Ambient sound 12|14012|16|- +Sound|Ambient sound 13|14013|16|- +Sound|Ambient sound 14|14014|16|- +Sound|Ambient sound 15|14015|16|- +Sound|Ambient sound 16|14016|16|- +Sound|Ambient sound 17|14017|16|- +Sound|Ambient sound 18|14018|16|- +Sound|Ambient sound 19|14019|16|- +Sound|Ambient sound 20|14020|16|- +Sound|Ambient sound 21|14021|16|- +Sound|Ambient sound 22|14022|16|- +Sound|Ambient sound 23|14023|16|- +Sound|Ambient sound 24|14024|16|- +Sound|Ambient sound 25|14025|16|- +Sound|Ambient sound 26|14026|16|- +Sound|Ambient sound 27|14027|16|- +Sound|Ambient sound 28|14028|16|- +Sound|Ambient sound 29|14029|16|- +Sound|Ambient sound 30|14030|16|- +Sound|Ambient sound 31|14031|16|- +Sound|Ambient sound 32|14032|16|- +Sound|Ambient sound 33|14033|16|- +Sound|Ambient sound 34|14034|16|- +Sound|Ambient sound 35|14035|16|- +Sound|Ambient sound 36|14036|16|- +Sound|Ambient sound 37|14037|16|- +Sound|Ambient sound 38|14038|16|- +Sound|Ambient sound 39|14039|16|- +Sound|Ambient sound 40|14040|16|- +Sound|Ambient sound 41|14041|16|- +Sound|Ambient sound 42|14042|16|- +Sound|Ambient sound 43|14043|16|- +Sound|Ambient sound 44|14044|16|- +Sound|Ambient sound 45|14045|16|- +Sound|Ambient sound 46|14046|16|- +Sound|Ambient sound 47|14047|16|- +Sound|Ambient sound 48|14048|16|- +Sound|Ambient sound 49|14049|16|- +Sound|Ambient sound 50|14050|16|- +Sound|Ambient sound 51|14051|16|- +Sound|Ambient sound 52|14052|16|- +Sound|Ambient sound 53|14053|16|- +Sound|Ambient sound 54|14054|16|- +Sound|Ambient sound 55|14055|16|- +Sound|Ambient sound 56|14056|16|- +Sound|Ambient sound 57|14057|16|- +Sound|Ambient sound 58|14058|16|- +Sound|Ambient sound 59|14059|16|- +Sound|Ambient sound 60|14060|16|- +Sound|Ambient sound 61|14061|16|- +Sound|Ambient sound 62|14062|16|- +Sound|Ambient sound 63|14063|16|- +Sound|Ambient sound 64|14064|16|- +Sound|Ambient sound 0-255 (Hexen)|14065|16|- + +Teleport|Teleport landing (remembers height)|9044|16|TFOGA0 + +Special ZDOOM/BOOM|BOOM Point pusher|5001|16|- +Special ZDOOM/BOOM|BOOM Point puller|5002|16|- +Special ZDOOM/BOOM|Map spot|9001|16|- +Special ZDOOM/BOOM|Map spot (with gravity)|9002|16|- +Special ZDOOM/BOOM|Path node|9024|16|- +Special ZDOOM/BOOM|Camera|9025|16|- +Special ZDOOM/BOOM|Water filled sector|9045|16|- +Special ZDOOM/BOOM|Secret|9046|16|- +Special ZDOOM/BOOM|Polyobject anchor|9300|16|- +Special ZDOOM/BOOM|Polyobject spawn spot|9301|16|- +Special ZDOOM/BOOM|Crushing polyobject spawn spot|9302|16|- + + +# LINEDEF_CLASSES +# +%LINEDEF_CLASSES +BOOM/ZDoom + +# LINEDEF_TYPES +# +%LINEDEF_TYPES +BOOM/ZDoom|333|Tagged sector gravity +BOOM/ZDoom|334|Tagged sector fog/light colour +BOOM/ZDoom|335|Tagged Sector damage + + +# SECTOR_CLASSES +# +%SECTOR_CLASSES +Doom|Hexen Lighting +Hexen|Normal +Hexen|Lights +Hexen|Stairs +Hexen|Damage +Hexen|Door +Hexen|Scroll Slow +Hexen|Scroll Medium +Hexen|Scroll Fast + +# SECTOR_TYPES +# +%SECTOR_TYPES +Doom|Hexen Lighting|21|Light Phased|Phase +Doom|Hexen Lighting|22|Light Seq Start|LSeq Start +Doom|Hexen Lighting|23|Light Seq Special 1|LSeq Spec1 +Doom|Hexen Lighting|24|Light Seq Special 2|LSeq Spec2 + +Hexen|Normal|0|Normal|Normal + +Hexen|Lights|1|Light Phased|Phase +Hexen|Lights|2|Light Seq Start|LSeq Start +Hexen|Lights|3|Light Seq Special 1|LSeq Spec1 +Hexen|Lights|4|Light Seq Special 2|LSeq Spec2 + +Hexen|Stairs|26|Stairs Special 1|StairSpec1 +Hexen|Stairs|27|Stairs Special 2|StairSpecb + +Hexen|Scroll Slow|201|Scroll North Slow|ScrollNorthSlow +Hexen|Scroll Medium|202|Scroll North Medium|ScrollNorthMedium +Hexen|Scroll Fast|203|Scroll North Fast|ScrollNorthFast +Hexen|Scroll Slow|204|Scroll East Slow|ScrollEastSlow +Hexen|Scroll Medium|205|Scroll East Medium|ScrollEastMedium +Hexen|Scroll Fast|206|Scroll East Fast|ScrollEastFast +Hexen|Scroll Slow|207|Scroll South Slow|ScrollSouthSlow +Hexen|Scroll Medium|208|Scroll South Medium|ScrollSouthMedium +Hexen|Scroll Fast|209|Scroll South Fast|ScrollSouthFast +Hexen|Scroll Slow|210|Scroll West Slow|ScrollWestSlow +Hexen|Scroll Medium|211|Scroll West Medium|ScrollWestMedium +Hexen|Scroll Fast|212|Scroll West Fast|ScrollWestFast +Hexen|Scroll Slow|213|Scroll NorthWest Slow|ScrollNorthWestSlow +Hexen|Scroll Medium|214|Scroll NorthWest Medium|ScrollNorthWestMedium +Hexen|Scroll Fast|215|Scroll NorthWest Fast|ScrollNorthWestFast +Hexen|Scroll Slow|216|Scroll NorthEast Slow|ScrollNorthEastSlow +Hexen|Scroll Medium|217|Scroll NorthEast Medium|ScrollNorthEastMedium +Hexen|Scroll Fast|218|Scroll NorthEast Fast|ScrollNorthEastFast +Hexen|Scroll Slow|219|Scroll SouthEast Slow|ScrollSouthEastSlow +Hexen|Scroll Medium|220|Scroll SouthEast Medium|ScrollSouthEastMedium +Hexen|Scroll Fast|221|Scroll SouthEast Fast|ScrollSouthEastFast +Hexen|Scroll Slow|222|Scroll SouthWest Slow|ScrollSouthWestSlow +Hexen|Scroll Medium|223|Scroll SouthWest Medium|ScrollSouthWestMedium +Hexen|Scroll Fast|224|Scroll SouthWest Fast|ScrollSouthWestFast + +# Doom sector types in their hexen numbers +# +Hexen|Lights|65|Light random off (Doom)|dLight_Flicker +Hexen|Lights|66|Blink lights 0.5 second (Doom)|dLight_StrobeFast +Hexen|Lights|67|Blink lights 1.0 second (Doom)|dLight_StrobeSlow +Hexen|Lights|68|Lose -10/20% health & blink lights 0.5 sec (Doom)|dLightStrobeHurt +Hexen|Damage|69|Lose -5/10% health (Doom)|dDamage_Hellslime +Hexen|Damage|71|Lose -2/5% health (Doom)|dDamage_Nukage +Hexen|Lights|72|Oscillating light (Doom)|dLightGlow +Hexen|Door|74|Door closes after 30 seconds (Doom)|dSector_DoorClose +Hexen|Damage|75|-10/20% health - end level if < 11% (Doom)|dDamageEnd +Hexen|Lights|76|Blink lights synchronised 0.5 second (Doom)|dLight_StrobeSlowSync +Hexen|Lights|77|Blink lights synchronised 1.0 second (Doom)|dLight_StrobeFastSync +Hexen|Door|78|Door opens after 300 seconds (Doom)|dSector_DoorRaiseIn5Min +Hexen|Damage|80|Lose -10/20% health (Doom)|dDamage_SuperHellslime +Hexen|Lights|81|Light random on/off (Doom)|dLight_FireFlicker + + +# THING_FLAGS section +# +%THING_FLAGS +Hexen|0|0x001|0x001|Skill 1 and 2|E +Hexen|0|0x002|0x002|Skill 3|M +Hexen|0|0x004|0x004|Skill 4 and 5|H +Hexen|0|0x008|0x008|Deaf|D +Hexen|0|0x010|0x010|Dormant|A +Hexen|0|0x100|0x100|Appears in 1 player|S +Hexen|0|0x200|0x200|Appears in Co-op|C +Hexen|0|0x400|0x400|Appears in Deathmatch|X + + +# LINEDEF_FLAGS section +# +%LINEDEF_FLAGS +Hexen|0|0x0001|0x0001|Impassible|I +Hexen|0|0x0002|0x0002|Block monsters|M +Hexen|0|0x0004|0x0004|Two-sided|2 +Hexen|0|0x0008|0x0008|Upper unpegged|U +Hexen|0|0x0010|0x0010|Lower unpegged|L +Hexen|0|0x0020|0x0020|Secret|S +Hexen|0|0x0040|0x0040|Blocks sound|B +Hexen|0|0x0080|0x0080|Not on map|N +Hexen|0|0x0100|0x0100|Already on map|A +Hexen|0|0x0200|0x0200|Repeatable|R +Hexen|1|0x1c00|0x0000|Activated by player crossing|C +Hexen|1|0x1c00|0x0400|Activated by player using|u +Hexen|1|0x1c00|0x0800|Activated by monster crossing|c +Hexen|1|0x1c00|0x0c00|Activated by projectile hitting|P +Hexen|1|0x1c00|0x1000|Activated by player bumping|B +Hexen|1|0x1c00|0x1400|Activated by projectile crossing|p +Hexen|1|0x1c00|0x1800|Activated by player crossing (passthru)|T +Hexen|0|0x2000|0x2000|Can be activated by monsters|a +Hexen|0|0x8000|0x8000|Blocks everything|! + + +# SECTOR_STYLES and LINEDEF_DEFAULT we will automatically inherit from the +# DOOM config or the DOOM 2 config +# + + +# LINEDEF_GEN_BITMASKS section. This defines the bitmasks that are used to +# build up generalised linedef types. +# +# This section is a bit more complex than most of the others as the format of +# the data is not fixed from line to line. +# +# Each set starts with the class name for the bitmask. Format for this line is: +# +# "class name|no of fields" +# +# After this follows <no of fields> repititions of the following: +# +# "field name|shorthand name|value" +# +# Remember that value will be shifted left a specified amount when used in the +# LINEDEF_GEN_TYPES section and so they should be defined relative to bit 0. +# +# Also note that inside the editor all the values associated with a bitmask +# are ORed together to create a mask that can extract that information from a +# generalised linedef to be displayed in the editor. +# +# Following all this can follow another class, and so on. Also remember that +# blank lines and comments are OK, so that sections can be readably split using +# white space. +# +%LINEDEF_GEN_BITMASKS +Trigger|8 +Walk over (once)|W1|0 +Walk over (multi)|WR|1 +Push (once)|P1|2 +Push (multi)|PR|3 +Switch (once)|S1|4 +Switch (multi)|SR|5 +Shoot (once)|G1|6 +Shoot (multi)|GR|7 + +Opens with|8 +Any|NK|0 +Red keycard|RC|1 +Blue keycard|BC|2 +Yellow keycard|YC|3 +Red skull|RS|4 +Blue skull|BS|5 +Yellow skull|YS|6 +All keys|AK|7 + +Speed|4 +Slow|Slow|0 +Normal|Norm|1 +Fast|Fast|2 +Turbo|Turb|3 + +Delay|4 +1s|1s|0 +4s|4s|1 +9s|9s|2 +30s|30s|3 + +Kind|4 +Open, Wait, Then Close|OWC|0 +Open and Stay Open|OSO|1 +Close and Stay Closed|CSC|2 +Close, Wait, Then Open|CWO|3 + +Locked door kind|2 +Open, close|OC|0 +Open|O|1 + +Number of Keys|2 +All 6 used|6|0 +3 used (skulls/cards same)|3|1 + +Floor Target|8 +Lowest Neighbor Floor|LnF|0 +Next Neighbor Floor|NnF|1 +Lowest Neighbor Ceiling|LnC|2 +Highest Neighbor Floor|HnF|3 +Ceiling|C|4 +24 Units|24u|5 +32 Units|32u|6 +Shortest Lower Texture|SLT|7 + +Monsters Can Use|2 +No|MoY|0 +Yes|MoN|1 + +Direction|2 +Down|Dn|0 +Up|Up|1 + +Crush|2 +No|CrN|0 +Yes|CrY|1 + +Floor Model|2 +Trigger|Trg|0 +Numeric|Num|1 + +Floor Change|4 +None|N|0 +Texture only|Tx|1 +Type zeroed|Tx0|2 +Texture and type changed|TxTy|3 + + +# LINEDEF_GEN_TYPES section. This defines the combinations of +# LINEDEF_GEN_BITMASKS that go to make up a generalised linedef type. +# +# This section is a bit more complex than most of the others as the format of +# the data is not fixed from line to line. +# +# Each set starts with the class name describing the type of generalised +# linedef, the edit mode this is for, the number of different bit patterns +# defined in the class and the low and high values that the type occupies. +# The edit mode is described in doom.cfg. Format for this line is: +# +# "edit mode|class name|low value|high value|mask|no of bit-field classes" +# +# After this follows <no of fields> repititions of the following: +# +# "bit-field name|shift" +# +# Note that different sets of bitmasks can have overlapping low/high values. +# When deciding which one to show in the editor display the first one defined +# will be used. +# +# The shift defines how much the bitmask is shifted to the left to generate +# the actual values stored in the linedef type field. +# +# If the mask is not -1 then in the editor it is ORed with the selected bit +# mask values to generate the linedef type value. +# +# If the mask is -1 then the original value of the linedef has the bits that +# would be occupied by the bitmasks cleared. The select bit mask values are +# then ORed with the ORIGINAL linedef value. +# +# Following all this can follow another class, and so on. Also remember that +# blank lines and comments are OK, so that sections can be readably split using +# white space. +# +# NB: HEXEN mode does not support generic linedef types anyhow... So, err, +# don't ask why the edit mode was added. Just seemed easy at the time as +# I was edit modeing generic sectors. +# +%LINEDEF_GEN_TYPES +Doom|Locked Door|0x3800|0x3bff|0x3800|5 +Trigger|0 +Speed|3 +Locked door kind|5 +Opens with|6 +Number of keys|9 + +Doom|Floor|0x6000|0x7fff|0x6000|7 +Trigger|0 +Speed|3 +Floor Model|5 +Direction|6 +Floor Target|7 +Floor Change|10 +Crush|12 + + +# SECTOR_GEN_BITMASKS section. This defines the bitmasks that are used to +# build up generalised sector types. This section works identically to the +# LINEDEF_GEN_BITMASKS section. +# +# +%SECTOR_GEN_BITMASKS +Lighting|9 +Normal|NL|0 +Random off|RO|1 +Blink 0.5 seconds|0.5|2 +Blink 1.0 seconds|1.0|3 +-10/20% health and blink 0.5 seconds|DmgL|4 +Oscillates|Osc|8 +Blink 0.5 seconds, synchronised|0.5s|12 +Blink 1.0 seconds, synchronised|1.0s|13 +Flickers on/off randomly|Rand|17 + +Damage|4 +None|D0|0 +5 units|D5|1 +10 units|D10|2 +20 units|D20|3 + +Secret|2 +No|NSec|0 +Yes|Sec|1 + +Friction|2 +Disabled|NFri|0 +Enabled|Fri|1 + +Wind|2 +Disabled|NWin|0 +Enabled|Win|1 + + +# SECTOR_GEN_TYPES section. This defines the combinations of +# SECTOR_GEN_BITMASKS that go to make up a generalised sector type. See +# LINEDEF_GEN_BITMASKS for details. +# +# +%SECTOR_GEN_TYPES +Doom|Generalised sector|0x20|0xffff|0|5 +Lighting|0 +Damage|5 +Secret|7 +Friction|8 +Wind|9 + +Hexen|Generalised Sector|0x100|0xffff|-1|4 +Damage|8 +Secret|10 +Friction|11 +Wind|12 + + +# HEXEN configuration +# +# ZDoom supports DOOM maps using the extended WAD format introduced by Raven +# for Hexen. As with ZDoom, viDOOM recognises HEXEN format maps by their +# inclusion of a BEHAVIOR lump. +# +# In the editor, these following definitions will be used only when a HEXEN +# format map is being edited, and can be left unset otherwise. +# + +# HEXEN_ACTION_SPECIAL_CLASSES +# +# Action special classes in HEXEN. Format is: +# "class name" +# +%HEXEN_ACTION_SPECIAL_CLASSES +Normal +Polyobject +Door +Floor +Ceiling +Floor and ceiling +Stairs +Platform +Teleport/exit +Lighting +Sector property +Things +ACS +Misc + + +# HEXEN_ACTION_SPECIALS +# +# The action specials themselves. Format is: +# "class name|id|name|[arg0[,arg1[,arg2[,arg3[,arg4]]]]]" +# +%HEXEN_ACTION_SPECIALS +Normal|0|No action| + +Polyobject|1|Polyobj_StartLine|po,mirror,sound +Polyobject|2|Polyobj_RotateLeft|po,speed,angle +Polyobject|3|Polyobj_RotateRight|po,speed,angle +Polyobject|4|Polyobj_Move|po|speed,angle,dist +Polyobject|5|Polyobj_ExplicitLine|po,order,mirror,sound +Polyobject|6|Polyobj_MoveTimes8|po,speed,angle,dist +Polyobject|7|Polyobj_DoorSwing|po,speed,angle,delay +Polyobject|8|Polyobj_DoorSlide|po,speed,angle,dist,delay +Polyobject|90|Polyobj_OR_RotateLeft|po,speed +Polyobject|91|Polyobj_OR_RotateRight|po,speed,angle +Polyobject|92|Polyobj_OR_Move|po|speed,angle,distance +Polyobject|93|Polyobj_OR_MoveTimes8|po,speed,angle,distance + +Door|10|Door_Close|tag,speed +Door|11|Door_Open|tag,speed +Door|12|Door_Raise|tag,speed,delay +Door|13|Door_LockedRaise|tag,speed,delay,lock +Door|249|Door_CloseWaitOpen|tag,speed,delay +Door|202|Generic_Door|tag,speed,kind,delay,lock + +Floor|20|Floor_LowerByValue|tag,speed,height +Floor|36|Floor_LowerByValueTimes8|tag,speed,height +Floor|66|Floor_LowerInstant|tag,arg1,height +Floor|21|Floor_LowerToLowest|tag,speed +Floor|241|Floor_LowerToLowestTxTy|tag,speed +Floor|242|Floor_LowerToHighest|tag,speed,adjust +Floor|22|Floor_LowerToNearest|tag,speed +Floor|23|Floor_RaiseByValue|tag,speed,height +Floor|35|Floor_RaiseByValueTimes8|tag,speed,height +Floor|67|Floor_RaiseInstant|tag,arg1,height +Floor|24|Floor_RaiseToHighest|tag,speed +Floor|25|Floor_RaiseToNearest|tag,speed +Floor|238|Floor_RaiseToLowestCeiling|tag,speed +Floor|239|Floor_RaiseByValueTxTy|tag,speed,height +Floor|240|Floor_RaiseByTexture|tag,speed +Floor|28|Floor_RaiseAndCrush|tag,speed,crush +Floor|46|Floor_CrushStop|tag +Floor|68|Floor_MoveToValueTimes8|tag,speed,height,neg +Floor|138|Floor_Waggle|tag,amp,freq,offset,time +Floor|250|Floor_Donut|ptag,pspeed,sspeed +Floor|235|Floor_TransferTrigger|tag +Floor|236|Floor_TransferNumeric|tag +Floor|200|Generic_Floor|tag,speed,height,target,flags + +Ceiling|40|Ceiling_LowerByValue|tag,speed,height +Ceiling|41|Ceiling_RaiseByValue|tag,speed,height +Ceiling|199|Ceiling_LowerByValueTimes8|tag,speed,height +Ceiling|198|Ceiling_RaiseByValueTimes8|tag,speed,height +Ceiling|193|Ceiling_LowerInstant|tag,arg1,height +Ceiling|194|Ceiling_RaiseInstant|tag,arg1,height +Ceiling|252|Ceiling_RaiseToNearest|tag,speed +Ceiling|192|Ceiling_LowerToHighestFloor|tag,speed +Ceiling|253|Ceiling_LowerToLowest|tag,speed +Ceiling|254|Ceiling_LowerToFloor|tag,speed +Ceiling|69|Ceiling_MoveToValueTimes8|tag,speed,height,neg +Ceiling|42|Ceiling_CrushAndRaise|tag,speed,crush +Ceiling|45|Ceiling_CrushRaiseAndStay|tag,speed,crush +Ceiling|43|Ceiling_LowerAndCrush|tag,speed,crush +Ceiling|195|Ceiling_CrushRaiseAndStayA|tag,dspeed,uspeed,crush +Ceiling|196|Ceiling_CrushAndRaiseA|tag,dspeed,uspeed,crush +Ceiling|197|Ceiling_CrushAndRaiseSilentA|tag,dspeed,uspeed,crush +Ceiling|255|Ceiling_CrushRaiseAndStaySilA|tag,dspeed,uspeed,crush +Ceiling|44|Ceiling_CrushStop|tag +Ceiling|201|Generic_Ceiling|tag,speed,height,target,flag +Ceiling|205|Generic_Crusher|tag,dspeed,uspeed,silent,crush + +Floor and ceiling|95|FloorAndCeiling_LowerByValue|tag,speed,height +Floor and ceiling|96|FloorAndCeiling_RaiseByValue|tag,speed,height +Floor and ceiling|251|FloorAndCeiling_LowerRaise|tag,fspeed,cspeed +Floor and ceiling|245|Elevator_RaiseToNearest|tag,speed +Floor and ceiling|246|Elevator_MoveToFloor|tag,speed +Floor and ceiling|247|Elevator_LowerToNearest|tag,speed +Floor and ceiling|29|Pillar_Build|tag,speed,height +Floor and ceiling|94|Pillar_BuildAndCrush|tag,speed,height,crush +Floor and ceiling|30|Pillar_Open|tag,speed,fdist,cdist + +Stairs|26|Stairs_BuildDown|tag,speed,height,delay,reset +Stairs|27|Stairs_BuildUp|tag,speed,height,delay,reset +Stairs|31|Stairs_BuildDownSync|tag,speed,height,reset +Stairs|32|Stairs_BuildUpSync|tag,speed,height,reset +Stairs|217|Stairs_BuildUpDoom|tag,speed,height,delay,reset +Stairs|204|Generic_Stairs|tag,speed,height,flags,reset + +Platform|60|Plat_PerpetualRaise|tag,speed,delay +Platform|207|Plat_PerpetualRaiseLip|tag,speed,delay,lip +Platform|61|Plat_Stop|tag +Platform|62|Plat_DownWaitUpStay|tag,speed,delay +Platform|206|Plat_DownWaitUpStayLip|tag,speed,delay,lip +Platform|63|Plat_DownByValue|tag,speed,delay,height +Platform|65|Plat_UpByValue|tag,speed,delay,height +Platform|64|Plat_UpWaitDownStay|tag,speed,delay +Platform|228|Plat_RaiseAndStayTx0|tag,speed +Platform|230|Plat_UpByValueStayTx|tag,speed,height +Platform|231|Plat_ToggleCeiling|tag +Platform|203|Generic_Lift|tag,speed,delay,type,height + +Teleport/exit|70|Teleport|tid +Teleport/exit|71|Teleport_NoFog|tid +Teleport/exit|215|Teleport_Line|thisid,destid,flip +Teleport/exit|74|Teleport_NewMap|map,pos +Teleport/exit|75|Teleport_EndGame +Teleport/exit|243|Exit_Normal|pos +Teleport/exit|244|Exit_Secret|pos + +Lighting|110|Light_RaiseByValue|tag,value +Lighting|111|Light_LowerByValue|tag,value +Lighting|112|Light_ChangeToValue|tag,value +Lighting|113|Light_Fade|tag,value,tics +Lighting|114|Light_Glow|tag,upper,lower,tics +Lighting|115|Light_Flicker|tag,upper,lower +Lighting|116|Light_Strobe|tag,upper,lower,u-tics,l-tics +Lighting|232|Light_StrobeDoom|tag,u-tics,l-tics +Lighting|233|Light_MinNeighbor|tag +Lighting|234|Light_MaxNeighbor|tag + +Sector property|212|Sector_SetColor|tag,r,g,b +Sector property|213|Sector_SetFade|tag,r,g,b +Sector property|214|Sector_SetDamage|tag,amount,mod +Sector property|216|Sector_SetGravity|tag,ipart,fpart +Sector property|219|Sector_SetFriction|tag,amount +Sector property|218|Sector_SetWind|tag,amount,angle,useline +Sector property|220|Sector_SetCurrent|tag,amount,angle,useline +Sector property|183|Line_AlignFloor|lineid,side +Sector property|184|Line_AlignCeiling|lineid,side +Sector property|185|Sector_SetRotation|tag,floor-angle,ceiling-angle +Sector property|186|Sector_SetCeilingPanning|tag,u-int,u-frac,v-int,v-frac +Sector property|187|Sector_SetFloorPanning|tag,u-int,u-frac,v-int,v-frac +Sector property|188|Sector_SetCeilingScale|tag,u-int,u-frac,v-int,v-frac +Sector property|189|Sector_SetFloorScale|tag,u-int,u-frac,v-int,v-frac + +Things|72|ThrustThing|angle,force +Things|73|DamageThing|amount +Things|248|HealThing|amount +Things|130|Thing_Activate|tid +Things|131|Thing_Deactivate|tid +Things|132|Thing_Remove|tid +Things|133|Thing_Destroy|tid +Things|134|Thing_Projectile|tid,type,angle,speed,vspeed +Things|136|Thing_ProjectileGravity|tid,type,angle,speed,vspeed +Things|135|Thing_Spawn|tid,type,angle +Things|137|Thing_SpawnNoFog|tid,type,angle +Things|229|Thing_SetGoal|tid,goal,delay + +ACS|80|ACS_Execute|script,map,s_arg1,s_arg2,s_arg3 +ACS|226|ACS_ExecuteAlways|script,map,s_arg1,s_arg2,s_arg3 +ACS|83|ACS_LockedExecute|script,map,s_arg1,s_arg2,lock +ACS|81|ACS_Suspend|script,map +ACS|82|ACS_Terminate|script,map + +Misc|121|Line_SetIdentification|lineid +Misc|208|TranslucentLine|lineid,amount +Misc|100|Scroll_Texture_Left|speed +Misc|101|Scroll_Texture_Right|speed +Misc|102|Scroll_Texture_Up|speed +Misc|103|Scroll_Texture_Down|speed +Misc|221|Scroll_Texture_Both|lineid,left,right,down,up +Misc|225|Scroll_Texture_Offsets +Misc|222|Scroll_Texture_Model|lineid,scrollbits +Misc|223|Scroll_Floor|tag,scrollbits,0=scroll:1=carry:2=both,x-move,y-move +Misc|224|Scroll_Ceiling|tag,scrollbits,0,x-move,y-move +Misc|209|Transfer_Heights|tag,when +Misc|210|Transfer_FloorLight|tag +Misc|211|Transfer_CeilingLight|tag +Misc|120|Radius_Quake|intensity,duration,damrad,tremrad,tid +Misc|227|PointPush_SetForce|tag,tid,amount,useline +Misc|237|ChangeCamera|tid,who,revert +Misc|191|SetPlayerProperty|who,set,which |