summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2011-06-09 13:46:28 +0000
committerIan C <ianc@noddybox.co.uk>2011-06-09 13:46:28 +0000
commita9022b5972dc49d86f617a27940fafe9c4d0e7e7 (patch)
tree61405aa4ade91ed1057f863ddf118ceb38e14f8e
Initial import of (very old) vidoom sources.
-rw-r--r--LICENSE341
-rw-r--r--Makefile334
-rw-r--r--README29
-rw-r--r--base.ini154
-rw-r--r--config.h109
-rw-r--r--debug.c231
-rw-r--r--debug.h66
-rw-r--r--djgpp/file.c118
-rw-r--r--djgpp/gfx.c559
-rw-r--r--djgpp/install31
-rw-r--r--djgpp/install.c119
-rw-r--r--djgpp/main.c34
-rw-r--r--djgpp/mem.c262
-rw-r--r--djgpp/platgui.c4235
-rw-r--r--djgpp/runcmd.c143
-rw-r--r--djgpp/vstring.c48
-rw-r--r--doc/bugs.htm40
-rw-r--r--doc/building.htm88
-rw-r--r--doc/changelog.htm64
-rw-r--r--doc/djgpp.htm58
-rwxr-xr-xdoc/ed_ex1.gifbin0 -> 530 bytes
-rwxr-xr-xdoc/ed_ex2.gifbin0 -> 596 bytes
-rwxr-xr-xdoc/ed_ex3.gifbin0 -> 702 bytes
-rwxr-xr-xdoc/ed_l_hex.gifbin0 -> 2499 bytes
-rwxr-xr-xdoc/ed_line.gifbin0 -> 15339 bytes
-rwxr-xr-xdoc/ed_lninf.gifbin0 -> 2275 bytes
-rwxr-xr-xdoc/ed_merge.gifbin0 -> 19280 bytes
-rwxr-xr-xdoc/ed_multi.gifbin0 -> 19790 bytes
-rwxr-xr-xdoc/ed_sect.gifbin0 -> 17537 bytes
-rwxr-xr-xdoc/ed_step.gifbin0 -> 15527 bytes
-rwxr-xr-xdoc/ed_t_hx1.gifbin0 -> 2383 bytes
-rwxr-xr-xdoc/ed_t_hx2.gifbin0 -> 2177 bytes
-rwxr-xr-xdoc/ed_thing.gifbin0 -> 17621 bytes
-rwxr-xr-xdoc/ed_vert.gifbin0 -> 13192 bytes
-rwxr-xr-xdoc/editing.htm1603
-rw-r--r--doc/glossary.htm152
-rw-r--r--doc/index.htm45
-rw-r--r--doc/license.htm360
-rw-r--r--doc/mainmenu.htm177
-rw-r--r--doc/overview.htm1164
-rw-r--r--doc/porting.htm1452
-rw-r--r--doc/sys.htm24
-rw-r--r--doc/thanks.htm91
-rw-r--r--doom.cfg571
-rw-r--r--doom2.cfg77
-rw-r--r--edit.c751
-rw-r--r--edit.h70
-rw-r--r--edit3d.c474
-rw-r--r--editcord.c225
-rw-r--r--editcrse.c397
-rw-r--r--editdraw.c349
-rw-r--r--editevnt.c1079
-rw-r--r--editgui.c519
-rw-r--r--editilst.c106
-rw-r--r--editline.c3812
-rw-r--r--editlump.c330
-rw-r--r--editmrg.c564
-rw-r--r--editmult.c708
-rw-r--r--editsect.c1735
-rw-r--r--editsel.c133
-rw-r--r--editsrot.c76
-rw-r--r--editthng.c1126
-rw-r--r--editvar.c492
-rw-r--r--editvar.h701
-rw-r--r--editvert.c834
-rw-r--r--file.h58
-rw-r--r--flags.c203
-rw-r--r--flags.h66
-rw-r--r--gdb.ini1
-rw-r--r--genlines.c460
-rw-r--r--genlines.h86
-rw-r--r--gensect.c423
-rw-r--r--gensect.h80
-rw-r--r--gfx.h319
-rw-r--r--gfxtest.c862
-rw-r--r--globals.c1375
-rw-r--r--globals.h232
-rw-r--r--gui.c337
-rw-r--r--gui.h84
-rw-r--r--ini.c532
-rw-r--r--ini.h106
-rw-r--r--linedefs.c316
-rw-r--r--linedefs.h84
-rw-r--r--linux/file.c103
-rw-r--r--linux/gfx.c1013
-rw-r--r--linux/main.c33
-rw-r--r--linux/mem.c107
-rw-r--r--linux/platgui.c3703
-rw-r--r--linux/runcmd.c82
-rw-r--r--linux/vstring.c48
-rw-r--r--list.c288
-rw-r--r--list.h104
-rw-r--r--make/cygwin-xfree.cfg41
-rw-r--r--make/djgpp.cfg41
-rw-r--r--make/linux.cfg41
-rw-r--r--map.c162
-rw-r--r--map.h86
-rw-r--r--mem.h79
-rw-r--r--names.c256
-rw-r--r--names.h43
-rw-r--r--platgui.h258
-rw-r--r--runcmd.h57
-rw-r--r--sectors.c319
-rw-r--r--sectors.h95
-rw-r--r--specials.c319
-rw-r--r--specials.h74
-rw-r--r--texture.c592
-rw-r--r--texture.h69
-rw-r--r--things.c288
-rw-r--r--things.h72
-rw-r--r--todo4
-rw-r--r--util.c253
-rw-r--r--util.h132
-rw-r--r--vidoom.c892
-rw-r--r--vidoom.h44
-rw-r--r--vidoom.ini186
-rw-r--r--vstring.h48
-rw-r--r--wad.c1144
-rw-r--r--wad.h294
-rw-r--r--waddir.c76
-rwxr-xr-xwadtestbin0 -> 703835 bytes
-rw-r--r--wadtest.c79
-rw-r--r--zdoom.cfg719
123 files changed, 45128 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..abd3cf7
--- /dev/null
+++ b/LICENSE
@@ -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
diff --git a/README b/README
new file mode 100644
index 0000000..af04e60
--- /dev/null
+++ b/README
@@ -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
diff --git a/debug.c b/debug.c
new file mode 100644
index 0000000..e318482
--- /dev/null
+++ b/debug.c
@@ -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 */
diff --git a/debug.h b/debug.h
new file mode 100644
index 0000000..1fe51ab
--- /dev/null
+++ b/debug.h
@@ -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
new file mode 100755
index 0000000..49f03f7
--- /dev/null
+++ b/doc/ed_ex1.gif
Binary files differ
diff --git a/doc/ed_ex2.gif b/doc/ed_ex2.gif
new file mode 100755
index 0000000..e50d72f
--- /dev/null
+++ b/doc/ed_ex2.gif
Binary files differ
diff --git a/doc/ed_ex3.gif b/doc/ed_ex3.gif
new file mode 100755
index 0000000..c68be6a
--- /dev/null
+++ b/doc/ed_ex3.gif
Binary files differ
diff --git a/doc/ed_l_hex.gif b/doc/ed_l_hex.gif
new file mode 100755
index 0000000..044078d
--- /dev/null
+++ b/doc/ed_l_hex.gif
Binary files differ
diff --git a/doc/ed_line.gif b/doc/ed_line.gif
new file mode 100755
index 0000000..094e0d4
--- /dev/null
+++ b/doc/ed_line.gif
Binary files differ
diff --git a/doc/ed_lninf.gif b/doc/ed_lninf.gif
new file mode 100755
index 0000000..f663393
--- /dev/null
+++ b/doc/ed_lninf.gif
Binary files differ
diff --git a/doc/ed_merge.gif b/doc/ed_merge.gif
new file mode 100755
index 0000000..806d808
--- /dev/null
+++ b/doc/ed_merge.gif
Binary files differ
diff --git a/doc/ed_multi.gif b/doc/ed_multi.gif
new file mode 100755
index 0000000..207a96e
--- /dev/null
+++ b/doc/ed_multi.gif
Binary files differ
diff --git a/doc/ed_sect.gif b/doc/ed_sect.gif
new file mode 100755
index 0000000..5d8f9b4
--- /dev/null
+++ b/doc/ed_sect.gif
Binary files differ
diff --git a/doc/ed_step.gif b/doc/ed_step.gif
new file mode 100755
index 0000000..58285e3
--- /dev/null
+++ b/doc/ed_step.gif
Binary files differ
diff --git a/doc/ed_t_hx1.gif b/doc/ed_t_hx1.gif
new file mode 100755
index 0000000..d6d0dee
--- /dev/null
+++ b/doc/ed_t_hx1.gif
Binary files differ
diff --git a/doc/ed_t_hx2.gif b/doc/ed_t_hx2.gif
new file mode 100755
index 0000000..61ed203
--- /dev/null
+++ b/doc/ed_t_hx2.gif
Binary files differ
diff --git a/doc/ed_thing.gif b/doc/ed_thing.gif
new file mode 100755
index 0000000..af4785a
--- /dev/null
+++ b/doc/ed_thing.gif
Binary files differ
diff --git a/doc/ed_vert.gif b/doc/ed_vert.gif
new file mode 100755
index 0000000..eaf5a0f
--- /dev/null
+++ b/doc/ed_vert.gif
Binary files differ
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>&lt;</td>
+ <td valign="top">Decrease selected sectors ceiling height
+ by 8</td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap>&gt;</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 &quot;tag&quot;.</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>&lt;</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>&gt;</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>&lt;</td>
+ <td valign="top">Rotate the map 10° to the left.</td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap>&gt;</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">&quot;... but what I talk about is DOOM,
+because in the end, DOOM is all that counts.&quot;<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:\&gt; 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>&nbsp;</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
+ &quot;Class Name|Colour&quot;. 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 &quot;Edit mode|Class Name&quot;. 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
+ &quot;Class|Name|ID|Radius|Sprite Name&quot;. 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 &quot;Edit
+ Mode|Group|Mask1|Mask2|Name|Single flag character&quot;.
+ 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
+ &quot;Class|ID|Name&quot;, 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 &quot;Bit number controlling
+ 2-sided|Bit controlling Impassible|Bit controlling lower
+ unpegged|Bit controlling upper unpegged&quot;. 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
+ &quot;Name|Flags value&quot;, 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
+ &quot;Edit Mode|Class|ID|Long name|Short name&quot;. 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
+ &quot;Mode|Name|Upper|Middle|Lower|Floor|Ceiling&quot;.
+ 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 &quot;Id for normal linedef|Id for normal
+ sector&quot;. 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 &quot;Class
+ Name|Id|Name|arg0,arg1,arg2,arg3,arg4&quot;. 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 &quot;tag&quot; 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>
+&lt;data&gt;<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=&quot;/&quot;</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=&quot;%s:%d&quot;,__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 - &quot;Yes to All&quot; and &quot;No to
+ All&quot;. 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]=&quot;bsp&quot;<br>
+ argv[1]=&quot;file.wad&quot;<br>
+ argv[2]=&quot;-o file.wad&quot;<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>&nbsp;</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
diff --git a/edit.c b/edit.c
new file mode 100644
index 0000000..27c700d
--- /dev/null
+++ b/edit.c
@@ -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 */
diff --git a/edit.h b/edit.h
new file mode 100644
index 0000000..20482ee
--- /dev/null
+++ b/edit.h
@@ -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,&current)))
+ {
+ i=IteratorDelete(i);
+ i=IteratorClear(i);
+ }
+
+ SetSelect(current,SELECT_NONE);
+ DrawObject(o->data,SELECT_NONE);
+ }
+ else
+ {
+ SetSelect(current,SELECT_SELECTED);
+ ListAppend(selected,&current);
+ 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,&current);
+ 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,&current);
+ 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,&current);
+ 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,&current);
+ 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 */
diff --git a/file.h b/file.h
new file mode 100644
index 0000000..10f87b0
--- /dev/null
+++ b/file.h
@@ -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
diff --git a/flags.c b/flags.c
new file mode 100644
index 0000000..08ed020
--- /dev/null
+++ b/flags.c
@@ -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 */
diff --git a/flags.h b/flags.h
new file mode 100644
index 0000000..6db4f29
--- /dev/null
+++ b/flags.h
@@ -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 */
diff --git a/gdb.ini b/gdb.ini
new file mode 100644
index 0000000..26f2e6b
--- /dev/null
+++ b/gdb.ini
@@ -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 */
diff --git a/gfx.h b/gfx.h
new file mode 100644
index 0000000..790838d
--- /dev/null
+++ b/gfx.h
@@ -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",
+ &sector_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 */
diff --git a/gui.c b/gui.c
new file mode 100644
index 0000000..489a4ee
--- /dev/null
+++ b/gui.c
@@ -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 */
diff --git a/gui.h b/gui.h
new file mode 100644
index 0000000..c6c0b8d
--- /dev/null
+++ b/gui.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
+
+ -------------------------------------------------------------------------
+
+ 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
diff --git a/ini.c b/ini.c
new file mode 100644
index 0000000..e9353aa
--- /dev/null
+++ b/ini.c
@@ -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 */
diff --git a/ini.h b/ini.h
new file mode 100644
index 0000000..0b3117a
--- /dev/null
+++ b/ini.h
@@ -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 */
diff --git a/list.c b/list.c
new file mode 100644
index 0000000..7249db4
--- /dev/null
+++ b/list.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
+
+ -------------------------------------------------------------------------
+
+ 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 */
diff --git a/list.h b/list.h
new file mode 100644
index 0000000..f3c67ac
--- /dev/null
+++ b/list.h
@@ -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
diff --git a/map.c b/map.c
new file mode 100644
index 0000000..bf02b07
--- /dev/null
+++ b/map.c
@@ -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 */
diff --git a/map.h b/map.h
new file mode 100644
index 0000000..e8b8b65
--- /dev/null
+++ b/map.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
+
+ -------------------------------------------------------------------------
+
+ 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 */
diff --git a/mem.h b/mem.h
new file mode 100644
index 0000000..2795af5
--- /dev/null
+++ b/mem.h
@@ -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 */
diff --git a/names.c b/names.c
new file mode 100644
index 0000000..5db6102
--- /dev/null
+++ b/names.c
@@ -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 */
diff --git a/names.h b/names.h
new file mode 100644
index 0000000..bd4bc11
--- /dev/null
+++ b/names.h
@@ -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 */
diff --git a/todo b/todo
new file mode 100644
index 0000000..1c926c1
--- /dev/null
+++ b/todo
@@ -0,0 +1,4 @@
+* Update docs with DJGPP sys dependent part. Update docs for new editing
+ functions.
+
+* Add left_click_move option to docs
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..f8837c4
--- /dev/null
+++ b/util.c
@@ -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 */
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..d49fd5b
--- /dev/null
+++ b/util.h
@@ -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
diff --git a/wad.c b/wad.c
new file mode 100644
index 0000000..fbd89e2
--- /dev/null
+++ b/wad.c
@@ -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,&sector);
+ }
+ 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 */
diff --git a/wad.h b/wad.h
new file mode 100644
index 0000000..8174052
--- /dev/null
+++ b/wad.h
@@ -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 */
diff --git a/wadtest b/wadtest
new file mode 100755
index 0000000..6c8742f
--- /dev/null
+++ b/wadtest
Binary files differ
diff --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