summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE341
-rw-r--r--README33
-rw-r--r--base.ini135
-rw-r--r--config.h108
-rw-r--r--debug.c212
-rw-r--r--debug.h66
-rw-r--r--djgpp/file.c129
-rw-r--r--djgpp/gfx.c535
-rw-r--r--djgpp/install31
-rw-r--r--djgpp/install.c119
-rw-r--r--djgpp/main.c34
-rw-r--r--djgpp/mem.c325
-rw-r--r--djgpp/platgui.c1975
-rw-r--r--djgpp/runcmd.c61
-rw-r--r--djgpp/vstring.c42
-rw-r--r--doc/bugs.htm40
-rw-r--r--doc/building.htm90
-rw-r--r--doc/ed_ex1.pngbin0 -> 262 bytes
-rw-r--r--doc/ed_ex2.pngbin0 -> 304 bytes
-rw-r--r--doc/ed_ex3.pngbin0 -> 327 bytes
-rw-r--r--doc/ed_line.pngbin0 -> 7837 bytes
-rw-r--r--doc/ed_lninf.pngbin0 -> 1267 bytes
-rw-r--r--doc/ed_merge.pngbin0 -> 10423 bytes
-rw-r--r--doc/ed_multi.pngbin0 -> 11443 bytes
-rw-r--r--doc/ed_sect.pngbin0 -> 10062 bytes
-rw-r--r--doc/ed_step.pngbin0 -> 9039 bytes
-rw-r--r--doc/ed_thing.pngbin0 -> 10467 bytes
-rw-r--r--doc/ed_vert.pngbin0 -> 8125 bytes
-rw-r--r--doc/editing.htm1261
-rw-r--r--doc/glossary.htm127
-rw-r--r--doc/index.htm45
-rw-r--r--doc/license.htm362
-rw-r--r--doc/mainmenu.htm111
-rw-r--r--doc/overview.htm843
-rw-r--r--doc/porting.htm1316
-rw-r--r--doc/thanks.htm70
-rw-r--r--doom.cfg527
-rw-r--r--doom2.cfg72
-rw-r--r--edit.c734
-rw-r--r--edit.h70
-rw-r--r--editcord.c225
-rw-r--r--editcrse.c398
-rw-r--r--editdraw.c346
-rw-r--r--editevnt.c991
-rw-r--r--editgui.c470
-rw-r--r--editilst.c106
-rw-r--r--editline.c3322
-rw-r--r--editmrg.c567
-rw-r--r--editmult.c582
-rw-r--r--editsect.c1577
-rw-r--r--editsel.c133
-rw-r--r--editsrot.c76
-rw-r--r--editthng.c636
-rw-r--r--editvar.c383
-rw-r--r--editvar.h619
-rw-r--r--editvert.c809
-rw-r--r--file.h58
-rw-r--r--gfx.h314
-rw-r--r--gfxtest.c430
-rw-r--r--globals.c983
-rw-r--r--globals.h211
-rw-r--r--gui.c391
-rw-r--r--gui.h88
-rw-r--r--ini.c526
-rw-r--r--ini.h106
-rw-r--r--linedefs.c371
-rw-r--r--linedefs.h100
-rw-r--r--list.c288
-rw-r--r--list.h104
-rw-r--r--make/djgpp.cfg41
-rw-r--r--makefile256
-rw-r--r--map.c162
-rw-r--r--map.h86
-rw-r--r--mem.h79
-rw-r--r--platgui.h199
-rw-r--r--runcmd.h57
-rw-r--r--sectors.c318
-rw-r--r--sectors.h96
-rw-r--r--texture.c594
-rw-r--r--texture.h69
-rw-r--r--things.c331
-rw-r--r--things.h88
-rw-r--r--vidoom.c723
-rw-r--r--vidoom.h44
-rw-r--r--vstring.h44
-rw-r--r--wad.c806
-rw-r--r--wad.h242
-rw-r--r--waddir.c75
88 files changed, 29334 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/README b/README
new file mode 100644
index 0000000..b3652b3
--- /dev/null
+++ b/README
@@ -0,0 +1,33 @@
+ viDOOM v0.00
+
+
+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'.
+
+ If install fails, or you wish to do it manully, simply copy the viDOOM
+ executable and the files doom.cfg and doom2.cfg to a directory.
+ Copy base.ini to the directory and rename it as vidoom.ini.
+
+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.4 2000/07/21 16:23:57 dosuser Exp dosuser $
diff --git a/base.ini b/base.ini
new file mode 100644
index 0000000..69e203b
--- /dev/null
+++ b/base.ini
@@ -0,0 +1,135 @@
+# 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.32 2000/07/28 16:21:28 dosuser Exp dosuser $
+#
+
+[Game]
+game=doom
+ask=no
+
+[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
+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]
+initial_empty_map=yes
+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
+
+[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
+pwad_dir=c:\wads\
+preload=
+level_style=doom 2
+vidoom_config=doom2.cfg
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..43ba4b5
--- /dev/null
+++ b/config.h
@@ -0,0 +1,108 @@
+/*
+
+ 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 _CONFIG_H
+
+#define _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.01"
+#define VIDOOMRELEASE "(Build 05/08/2000)"
+
+
+/* Basic, global machine types
+*/
+typedef unsigned char Byte;
+typedef unsigned short Word;
+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..5b54b17
--- /dev/null
+++ b/debug.c
@@ -0,0 +1,212 @@
+/*
+
+ 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"
+
+static FILE *trace_fp=NULL;
+
+void _Debug(char *fmt,...)
+{
+ static char last[512]="";
+ static char this[512]="";
+ static int rep=0;
+ int f;
+ va_list va;
+
+ if (!trace_fp)
+ {
+ 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 _Trace(char *fmt,...)
+{
+#ifdef 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)
+ {
+ 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
+ {
+ 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..b1b2a4b
--- /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 _DEBUG_H
+
+#define _DEBUG_H
+
+#include <stdio.h>
+
+#ifdef DEBUG
+# define Debug(x) _Debug x
+#else
+# define Debug(x)
+#endif
+
+/* Macro for function tracing. Use as:
+ void func(void)
+ {
+ int a;
+
+ TRACE;
+ ...
+ }
+*/
+#define TRACE _Trace(TRACEFORM)
+
+void _Debug(char *fmt,...);
+void _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..583a9df
--- /dev/null
+++ b/djgpp/file.c
@@ -0,0 +1,129 @@
+/*
+
+ 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);
+
+ if (s[strlen(s)-1]!='\\')
+ strcat(s,"\\");
+
+ 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)
+{
+ FILE *fp;
+
+ if ((fp=fopen(path,"rb")))
+ {
+ fclose(fp);
+ return(TRUE);
+ }
+ else
+ return(FALSE);
+}
+
+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..8d8e849
--- /dev/null
+++ b/djgpp/gfx.c
@@ -0,0 +1,535 @@
+/*
+
+ 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_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..f40c226
--- /dev/null
+++ b/djgpp/mem.c
@@ -0,0 +1,325 @@
+/*
+
+ 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 "debug.h"
+
+/* Comment out this define to switch off memory stats in the working version
+*/
+#define MEMSTAT
+
+#ifdef MEMSTAT
+
+typedef struct
+ {
+ Long size;
+ Long realloc;
+ Long free;
+ char file[32];
+ Long line;
+ } MemInfo;
+
+static Long grab_call=0;
+static Long regrab_call=0;
+static Long release_call=0;
+static Long copy_call=0;
+static Long strdup_call=0;
+static Long total=0;
+static Long total_grab=0;
+static Long total_regrab=0;
+static Long total_copy=0;
+static Long total_free=0;
+static Long total_strdup=0;
+
+static void status(void)
+{
+ Debug(("Total calls to Grab() : %lu\n",grab_call));
+ Debug(("Total calls to Regrab() : %lu\n",regrab_call));
+ Debug(("Total calls to Release() : %lu\n",release_call));
+ Debug(("Total calls to Copy() : %lu\n",copy_call));
+ Debug(("Total calls to Strdup() : %lu\n",strdup_call));
+ Debug(("Total bytes allocated : %lu\n",total));
+ Debug(("Total bytes allocated over life : %lu\n",total_grab));
+ Debug(("Total bytes regrabbed over life : %lu\n",total_regrab));
+ Debug(("Total bytes copied over life : %lu\n",total_copy));
+ Debug(("Total bytes released over life : %lu\n",total_free));
+ Debug(("Total bytes strdupped over life : %lu\n\n",total_strdup));
+}
+
+#endif
+
+
+#ifdef MEMSTAT
+
+void *FGrab(char *fn, int line, int len)
+{
+ char *ptr;
+ MemInfo *mi;
+
+ if (len==0)
+ len=1;
+
+ if (!(ptr=malloc(len+sizeof(MemInfo))))
+ {
+ status();
+ GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n"
+ "%s:%d Grab(%d)\n",fn,line,len);
+ }
+
+ mi=(MemInfo *)ptr;
+ mi->size=len;
+ mi->realloc=0;
+ mi->free=0;
+ ptr+=sizeof(MemInfo);
+
+ grab_call++;
+ total_grab+=len;
+ total+=len;
+
+ memset(ptr,0,len);
+
+ return(ptr);
+}
+
+#else
+
+void *FGrab(char *fn, int line, int len)
+{
+ void *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);
+}
+
+#endif
+
+
+#ifdef MEMSTAT
+
+void *FReGrab(char *fn, int line, void *ptr, int len)
+{
+ Long old;
+ MemInfo *mi;
+ char *p;
+
+ if (len==0)
+ len=1;
+
+ p=ptr;
+
+ if (p)
+ p-=sizeof(MemInfo);
+
+ if (!(p=realloc(p,len+sizeof(MemInfo))))
+ {
+ status();
+ GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n"
+ "%s:%d ReGrab(%d)\n",fn,line,len);
+ }
+
+ regrab_call++;
+ total_regrab+=len;
+
+ mi=(MemInfo *)p;
+ old=mi->size;
+ mi->size=len;
+ mi->realloc++;
+ total-=old;
+ total+=len;
+
+ p+=sizeof(MemInfo);
+
+ return(p);
+}
+
+#else
+
+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);
+}
+
+#endif
+
+
+#ifdef MEMSTAT
+
+void FRelease(char *fn, int line, void *p)
+{
+ char *cp;
+ MemInfo *mi;
+
+ if (!p)
+ return;
+
+ cp=p;
+ cp-=sizeof(MemInfo);
+ mi=(MemInfo *)cp;
+
+ if (mi->free==1)
+ Debug(("%p already freed!!! Current=%s:%d, previous=%s:%d\n",
+ p,fn,line,mi->file,mi->line));
+
+ release_call++;
+
+ total_free+=mi->size;
+ total-=mi->size;
+
+ strcpy(mi->file,fn);
+ mi->line=line;
+ mi->free=1;
+
+ free(cp);
+}
+
+#else
+
+void FRelease(char *fn, int line, void *p)
+{
+ free(p);
+}
+
+#endif
+
+
+#ifdef MEMSTAT
+
+void *FCopy(char *fn, int line, void *p,int len)
+{
+ char s[128];
+ void *ptr;
+
+ strcpy(s,fn);
+ strcat(s," [FCopy()]");
+
+ if (len==0)
+ ptr=FGrab(s,line,1);
+ else
+ ptr=FGrab(s,line,len);
+
+ copy_call++;
+ total_copy+=len;
+
+ if (len)
+ memcpy(ptr,p,len);
+
+ return(ptr);
+}
+
+#else
+
+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);
+}
+
+#endif
+
+
+#ifdef MEMSTAT
+
+char *FStrdup(char *fn, int line, char *p)
+{
+ char s[128];
+ char *ptr;
+
+ strcpy(s,fn);
+ strcat(s," [Strdup()]");
+
+ if (p)
+ {
+ ptr=FGrab(s,line,strlen(p)+1);
+
+ strdup_call++;
+ total_strdup+=strlen(p)+1;
+
+ strcpy(ptr,p);
+ return(ptr);
+ }
+ else
+ return(NULL);
+}
+
+#else
+
+char *FStrdup(char *fn, int line, char *p)
+{
+ char *ptr;
+
+ if (p)
+ {
+ if (!(ptr=malloc(strlen(p)+1)))
+ GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n"
+ "%s:%d Stdup(%s)\n",fn,line,p);
+
+ strcpy(ptr,p);
+ return(ptr);
+ }
+ else
+ return(NULL);
+}
+
+#endif
+
+
+/* END OF FILE */
diff --git a/djgpp/platgui.c b/djgpp/platgui.c
new file mode 100644
index 0000000..887ab9c
--- /dev/null
+++ b/djgpp/platgui.c
@@ -0,0 +1,1975 @@
+/*
+
+ 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 "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
+
+/* ---------------------------------------- VARS
+*/
+static int SCRW,SCRH;
+
+/* Global picklist data and the picklist dialog
+*/
+#define PL_BORDER 0
+#define PL_TITLE 1
+#define PL_PICKLIST 2
+#define PL_END 3
+
+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_END 5
+
+static DIALOG img_picklist[IPL_END+1];
+static int img_pick_no;
+static PLAT_IMG_PICKLIST
+ *img_pick_data;
+
+
+typedef struct
+ {
+ int x;
+ int y;
+ int w;
+ int h;
+ } BoundBox;
+
+
+/* ---------------------------------------- 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)
+ {
+ d_inv_3d_box(MSG_DRAW,&img_picklist[IPL_IMG_BORDER],0);
+
+ if ((bm=d->dp))
+ draw_rle_sprite(screen,bm,d->x,d->y);
+ else
+ gui_textout(screen,"No Image",
+ d->x+d->w/2,d->y+d->h/2,ACOL(BLACK),TRUE);
+ }
+
+ 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_inv_3d_box(MSG_DRAW,&img_picklist[IPL_IMG_BORDER],0);
+ 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 = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
+ 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)
+ DrawTick(x+SMALL_BEVEL,d->y+SMALL_BEVEL,
+ d->h-SMALL_BEVEL*2,d->h-SMALL_BEVEL*2);
+
+ return D_O_K;
+ }
+
+ return d_button_proc(msg, d, 0);
+}
+
+
+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_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-GFX_fh()/2,d->fg,TRUE);
+
+ break;
+
+ default:
+ return(d_button_proc(msg,d,c));
+ break;
+ }
+
+ 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.
+
+ Shawn Hargreaves can be contacted at:
+
+ http://www.talula.demon.co.uk/allegro/
+ shawn@talula.demon.co.uk
+
+ 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_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)
+{
+ 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-GFX_fh()/2,fg);
+}
+
+
+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;
+
+ 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_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;
+ 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;
+
+ 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_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;
+ 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;
+
+ 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;
+}
+
+
+int GUI_yesno(char *question)
+{
+ extern void GFX_FORCE_REDRAW(void);
+ DIALOG *d;
+ int ml;
+ int h;
+ int ret;
+ int x,y;
+
+ d=Grab(sizeof(DIALOG)*5);
+
+ h=(GFX_fh()+10)*3;
+ ml=text_length(font,question)+GFX_fw()*2;
+
+ 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;
+
+ /* Question
+ */
+ d[1].proc=d_ctext_proc;
+ d[1].x=SCRW/2;
+ d[1].w=ml-20;
+ d[1].y=y+BEVEL+GFX_fh();
+ d[1].h=10;
+ d[1].fg=ACOL(GUI_TEXT);
+ d[1].bg=ACOL(GUI_MID);
+ d[1].key=0;
+ d[1].flags=0;
+ d[1].d1=d[1].d2=0;
+ d[1].dp=question;
+ d[1].dp2=d[1].dp3=NULL;
+
+ /* YES
+ */
+ d[2].proc=my_d_button_proc;
+ d[2].x=x+10;
+ d[2].w=(ml/2)-20;
+ d[2].h=GFX_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='Y';
+ d[2].flags=D_EXIT;
+ d[2].d1=d[2].d2=0;
+ d[2].dp="&Yes";
+ d[2].dp2=d[2].dp3=NULL;
+
+ /* NO
+ */
+ 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='N';
+ d[3].flags=D_EXIT;
+ d[3].d1=d[3].d2=0;
+ d[3].dp="&No";
+ d[3].dp2=d[3].dp3=NULL;
+
+ d[4].proc=NULL;
+
+ GFX_bounce();
+
+ if(do_dialog(d,-1)==2)
+ ret=TRUE;
+ else
+ ret=FALSE;
+
+ GFX_FORCE_REDRAW();
+ Release(d);
+
+ return(ret);
+}
+
+
+int GUI_menu(char *title, int x, int y, PLAT_MENU menu[],int defval)
+{
+ int lx,ly;
+ int no;
+ int f;
+ int h;
+ int w;
+ BITMAP *under;
+ int cur;
+ int cx;
+ int by;
+ int done;
+ int quit;
+ BoundBox *box;
+
+ /* Calc dimensions
+ */
+ no=0;
+ w=text_length(font,title);
+
+ while(menu[no].text)
+ {
+ w=MAX(w,text_length(font,menu[no].text));
+ no++;
+ }
+
+ h=BEVEL*2+GFX_fh()+4;
+
+ for(f=0;f<no;f++)
+ h+=GFX_fh()+SMALL_BEVEL*2+2;
+
+ h+=2;
+ w+=BEVEL+SMALL_BEVEL+4;
+
+ box=Grab(sizeof(BoundBox)*no);
+
+ if ((x+w)>SCRW)
+ x=SCRW-w;
+
+ if ((y+h)>SCRH)
+ y=SCRH-h;
+
+ if ((x<0)||(y<0))
+ {
+ alert("Cannot display menu:",title,"Menu is bigger than display!",
+ "OK",NULL,0,0);
+
+ return(defval);
+ }
+
+ cx=x+w/2;
+
+ cur=0;
+
+ /* Save screen contents
+ */
+ show_mouse(NULL);
+
+ if (!(under=create_bitmap(w+1,h+1)))
+ GFX_exit(EXIT_FAILURE,"No more memory to create MENU save under\n");
+
+ blit(screen,under,x,y,0,0,w+1,h+1);
+
+ /* Draw menu border and title
+ */
+ Rect3D(x,y,w,h,FALSE,BEVEL);
+ text_mode(-1);
+ textout_centre(screen,font,title,cx,y+BEVEL*2,ACOL(GUI_TEXTBOLD));
+
+ /* Draw menu items
+ */
+ by=y+BEVEL*2+GFX_fh()+4;
+
+ for(f=0;f<no;f++)
+ {
+ box[f].x=x+BEVEL;
+ box[f].y=by;
+ box[f].w=w-BEVEL*2;
+ box[f].h=GFX_fh()+SMALL_BEVEL*2+2;
+ by+=box[f].h;
+
+ DrawMenuItem(&box[f],menu[f].text,(f==cur));
+ }
+
+ show_mouse(screen);
+ GFX_bounce();
+
+ /* Main loop
+ */
+ quit=FALSE;
+ done=FALSE;
+ lx=mouse_x;
+ ly=mouse_y;
+
+ while((!done)&&(!quit))
+ {
+ if (mouse_b)
+ {
+ int new;
+
+ new=MouseInBox(box,no);
+
+ if (new==-1)
+ {
+ quit=TRUE;
+
+ while(mouse_b);
+ }
+ else
+ {
+ done=TRUE;
+ cur=new;
+ }
+ }
+ else
+ if (keypressed())
+ switch(readkey()>>8)
+ {
+ case KEY_ESC:
+ quit=TRUE;
+ break;
+
+ case KEY_ENTER:
+ done=TRUE;
+ break;
+
+ case KEY_DOWN:
+ show_mouse(NULL);
+ DrawMenuItem(&box[cur],menu[cur].text,FALSE);
+
+ cur=(cur+1)%no;
+
+ DrawMenuItem(&box[cur],menu[cur].text,TRUE);
+ show_mouse(screen);
+ break;
+
+ case KEY_UP:
+ show_mouse(NULL);
+ DrawMenuItem(&box[cur],menu[cur].text,FALSE);
+
+ if (cur)
+ cur--;
+ else
+ cur=no-1;
+
+ DrawMenuItem(&box[cur],menu[cur].text,TRUE);
+ show_mouse(screen);
+ break;
+ }
+ else
+ if ((lx!=mouse_x)||(ly!=mouse_y))
+ {
+ int new;
+
+ lx=mouse_x;
+ ly=mouse_y;
+
+ new=MouseInBox(box,no);
+
+ if ((new!=-1)&&(new!=cur))
+ {
+ show_mouse(NULL);
+ DrawMenuItem(&box[cur],menu[cur].text,FALSE);
+ cur=new;
+ DrawMenuItem(&box[cur],menu[cur].text,TRUE);
+ show_mouse(screen);
+ }
+ }
+ }
+
+ show_mouse(NULL);
+ blit(under,screen,0,0,x,y,w+1,h+1);
+ show_mouse(screen);
+ destroy_bitmap(under);
+ Release(box);
+
+ if (!quit)
+ no=menu[cur].client_index;
+ else
+ no=defval;
+
+ return(no);
+}
+
+
+char *GUI_fsel(char *title, char *default_path, char *filter)
+{
+ char path[PATH_MAX+1];
+
+ if ((filter)&&(*filter=='.'))
+ filter++;
+
+ strcpy(path,default_path);
+
+ GFX_bounce();
+ if (my_file_select(title,path,filter))
+ return(Strdup(path));
+
+ return(NULL);
+}
+
+
+int GUI_picklist(char *title,char *opt[])
+{
+ extern void GFX_FORCE_REDRAW(void);
+ int ml;
+ int ret;
+
+ pick_no=0;
+
+ 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();
+
+ if (ret==-1)
+ return(-1);
+
+ return(picklist[IPL_PICKLIST].d1);
+}
+
+
+int GUI_client_picklist(char *title,PLAT_PICKLIST opt[],int defval)
+{
+ extern void GFX_FORCE_REDRAW(void);
+ int ml;
+ int ret;
+ char **c_opt;
+ int f;
+
+ pick_no=0;
+
+ 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);
+
+ if (ret==-1)
+ return(defval);
+
+ return(opt[picklist[IPL_PICKLIST].d1].client_index);
+}
+
+
+int GUI_image_picklist(char *title,PLAT_IMG_PICKLIST opt[],int defval)
+{
+ extern void GFX_FORCE_REDRAW(void);
+ 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);
+
+ return(opt[img_picklist[IPL_PICKLIST].d1].client_index);
+}
+
+
+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=(GFX_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+=GFX_fh()*4;
+ ml+=GFX_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_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,char *opt[],int *val)
+{
+ extern void GFX_FORCE_REDRAW(void);
+ int no;
+ DIALOG *d;
+ int ml,l;
+ int h;
+ int f;
+ int ret;
+ int x,y;
+
+ no=0;
+ while(opt[no])
+ no++;
+
+ d=Grab(sizeof(DIALOG)*(no+6));
+
+ h=(GFX_fh()+5)*no;
+ ml=text_length(font,"CANCEL CANCEL");
+
+ for(f=0;f<no;f++)
+ if ((l=text_length(font,opt[f]))>ml)
+ ml=l;
+
+ h+=GFX_fh()*4;
+ ml+=GFX_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_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 ((*val)&(1<<f))
+ d[4+f].flags=D_SELECTED;
+ else
+ d[4+f].flags=0;
+
+ d[4+f].d1=f;
+ d[4+f].d2=0;
+ d[4+f].dp=opt[f];
+ d[4+f].dp2=d[4+f].dp3=NULL;
+ }
+
+ d[4+no].proc=NULL;
+
+ GFX_bounce();
+ if(do_dialog(d,-1)==2)
+ {
+ *val=0;
+
+ for(f=0;f<no;f++)
+ if (d[4+f].flags&D_SELECTED)
+ *val|=(1<<f);
+
+ ret=TRUE;
+ }
+ else
+ ret=FALSE;
+
+ GFX_FORCE_REDRAW();
+ Release(d);
+
+ return(ret);
+}
+
+
+int GUI_dialog(char *title, int no, PLAT_DIALOG pd[])
+{
+ extern void GFX_FORCE_REDRAW(void);
+ DIALOG *d;
+ int ml,l;
+ int h;
+ int f;
+ int ret;
+ int x,y;
+ char *p;
+ int tmp;
+ double d_tmp;
+
+ d=Grab(sizeof(DIALOG)*(no*3+6));
+
+ h=(GFX_fh()+10)*no;
+ ml=0;
+
+ for(f=0;f<no;f++)
+ if ((pd[f].text)&&((l=text_length(font,pd[f].text))>ml))
+ ml=l;
+
+ ml+=text_length(font," ")*(DIALOG_STRLEN+3);
+
+ h+=GFX_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_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=GFX_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_text_proc;
+ d[4+(f*3)].x=x+10;
+ 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_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=x+ml-text_length(font," ")*DIALOG_STRLEN-9-BEVEL;
+ 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=x+ml-text_length(font," ")*DIALOG_STRLEN-9;
+ 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*4)].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;
+ }
+ }
+
+ ret=TRUE;
+ }
+ else
+ ret=FALSE;
+
+ GFX_FORCE_REDRAW();
+ Release(d);
+
+ return(ret);
+}
+
+
+/* END OF FILE */
diff --git a/djgpp/runcmd.c b/djgpp/runcmd.c
new file mode 100644
index 0000000..66d56ba
--- /dev/null
+++ b/djgpp/runcmd.c
@@ -0,0 +1,61 @@
+/*
+
+ 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 <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+int RunCommand(char *argv[], char *path)
+{
+ char cmd[PATH_MAX*2];
+ int ret;
+ int f;
+
+ tmpnam(path);
+
+ f=0;
+ cmd[0]=0;
+ while(argv[f])
+ {
+ strcat(cmd,argv[f]);
+ strcat(cmd," ");
+ f++;
+ }
+
+ strcat(cmd,">> ");
+ strcat(cmd,path);
+
+ ret=system(cmd);
+
+ /* 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..2cc5ee4
--- /dev/null
+++ b/djgpp/vstring.c
@@ -0,0 +1,42 @@
+/*
+
+ 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)
+{
+ return(strcasecmp(a,b));
+}
+
+int StrNCaseCmp(char *a, char *b, int n)
+{
+ return(strncasecmp(a,b,n));
+}
+
+
+/* END OF FILE */
diff --git a/doc/bugs.htm b/doc/bugs.htm
new file mode 100644
index 0000000..40dcfc0
--- /dev/null
+++ b/doc/bugs.htm
@@ -0,0 +1,40 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
+<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>&nbsp;</p>
+
+<hr>
+
+<p><a href="index.htm">Back to index</a></p>
+
+<p><tt>$Id: bugs.htm,v 1.4 2000/07/18 16:49:48 dosuser Exp dosuser $</tt></p>
+</body>
+</html>
diff --git a/doc/building.htm b/doc/building.htm
new file mode 100644
index 0000000..5dd09b0
--- /dev/null
+++ b/doc/building.htm
@@ -0,0 +1,90 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
+<title>viDOOM - Free Software DOOM editor</title>
+</head>
+
+<body>
+
+<h1 align="center">Building</h1>
+
+<p>This part of the documentation simply expands <u>slighty</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 seperator for this platform.
+This is used so that the makefile can safely access
+subdirectories:</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.4 2000/07/28 15:29:15 dosuser Exp dosuser $ </tt></p>
+</body>
+</html>
diff --git a/doc/ed_ex1.png b/doc/ed_ex1.png
new file mode 100644
index 0000000..503fdbc
--- /dev/null
+++ b/doc/ed_ex1.png
Binary files differ
diff --git a/doc/ed_ex2.png b/doc/ed_ex2.png
new file mode 100644
index 0000000..8e4986d
--- /dev/null
+++ b/doc/ed_ex2.png
Binary files differ
diff --git a/doc/ed_ex3.png b/doc/ed_ex3.png
new file mode 100644
index 0000000..aee4bc6
--- /dev/null
+++ b/doc/ed_ex3.png
Binary files differ
diff --git a/doc/ed_line.png b/doc/ed_line.png
new file mode 100644
index 0000000..a4aa32a
--- /dev/null
+++ b/doc/ed_line.png
Binary files differ
diff --git a/doc/ed_lninf.png b/doc/ed_lninf.png
new file mode 100644
index 0000000..46cbc8a
--- /dev/null
+++ b/doc/ed_lninf.png
Binary files differ
diff --git a/doc/ed_merge.png b/doc/ed_merge.png
new file mode 100644
index 0000000..f08707a
--- /dev/null
+++ b/doc/ed_merge.png
Binary files differ
diff --git a/doc/ed_multi.png b/doc/ed_multi.png
new file mode 100644
index 0000000..a1bbf59
--- /dev/null
+++ b/doc/ed_multi.png
Binary files differ
diff --git a/doc/ed_sect.png b/doc/ed_sect.png
new file mode 100644
index 0000000..3bd3fad
--- /dev/null
+++ b/doc/ed_sect.png
Binary files differ
diff --git a/doc/ed_step.png b/doc/ed_step.png
new file mode 100644
index 0000000..ab4140f
--- /dev/null
+++ b/doc/ed_step.png
Binary files differ
diff --git a/doc/ed_thing.png b/doc/ed_thing.png
new file mode 100644
index 0000000..d063772
--- /dev/null
+++ b/doc/ed_thing.png
Binary files differ
diff --git a/doc/ed_vert.png b/doc/ed_vert.png
new file mode 100644
index 0000000..5ab35dc
--- /dev/null
+++ b/doc/ed_vert.png
Binary files differ
diff --git a/doc/editing.htm b/doc/editing.htm
new file mode 100644
index 0000000..3805271
--- /dev/null
+++ b/doc/editing.htm
@@ -0,0 +1,1261 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
+<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>
+ </ul>
+ </li>
+ <li><a href="#MERGE_MAP">Mergeing Maps</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.png" 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>The main central part of the screen is made up the map being
+edited.</p>
+
+<p>At the bottom (and sometimes the middle right) 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. 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 Editting</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>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">Swith grid snapping on/off</td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap>Tab/Shift Tab</td>
+ <td valign="top">Goto 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>C</td>
+ <td><a href="#MULTI_MODE">MULTI edit mode</a></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).</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">map mergeing</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> Iin viDOOM it is not permissable 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 scaleing 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.png" 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 sceen 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
+ visibily 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.</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 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>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>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.png" width="640" height="480"></p>
+
+<p>When in this mode lindefs 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 sceen 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.png" 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>
+
+<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>Note that 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 vertcies
+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 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>Change linedef type</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>Swap sides</strong></td>
+ <td valign="top">Swaps the sides of the linedef, so that
+ the linedef points in the other directions. Note that the
+ sidedefs are re-arranged so that the textureing and
+ sector numbers on the sides of the linedef are not
+ switched.</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><strong>Right Sidedef</strong></td>
+ <td>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><strong>Left Sidedef</strong></td>
+ <td>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</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 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.png" width="204" height="97"></p>
+
+<p><img src="ed_ex2.png" 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.png" width="204" height="97"></p>
+
+<p>Having got the excuses over with we can now go onto the
+process involved with creating the steps. On seleting this option
+you will be presented with a display like the following:</p>
+
+<p><img src="ed_step.png" 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 corrider seen above.</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.png" width="640" height="480"></p>
+
+<p>When in this mode lindefs 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 sceen is a box which shows the details of
+the thing the pointer is currently over.</p>
+
+<h3><a name="THING_KEYS"></a>Extra Keys in Thing Mode</h3>
+
+<p>There are no extra keys in thing edit mode.</p>
+
+<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>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 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>Snap selected things</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.png" width="640" height="480"></p>
+
+<p>When in this mode lindefs 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 sceen 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 vertexs (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 selected vertices</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.png" width="640" height="480"></p>
+
+<p>When in this mode lindefs 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 sceen is a box which shows what object
+type (vertex or thing) the pointer is currently over and it's
+number.</p>
+
+<p>&nbsp;</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>
+
+<hr>
+
+<h2><a name="MERGE_MAP"></a>Mergeing 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. 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
+it will generally be required to check the sidedefs to make sure
+they have all their necessary textures and remember that the
+strucutre will have it's original sector propertires (floor,
+ceiling and light levels).</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.png" 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>
+
+<p><a href="index.htm">Back to index</a></p>
+
+<p><tt>$Id: editing.htm,v 1.8 2000/07/27 16:56:05 dosuser Exp dosuser $</tt></p>
+</body>
+</html>
diff --git a/doc/glossary.htm b/doc/glossary.htm
new file mode 100644
index 0000000..ef5912a
--- /dev/null
+++ b/doc/glossary.htm
@@ -0,0 +1,127 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
+<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><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="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>
+
+<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. </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
+development. </p>
+
+<p>&nbsp;</p>
+
+<hr width="99%">
+
+<p><a href="index.htm">Back to index</a></p>
+
+<p><tt>$Id: glossary.htm,v 1.2 2000/07/18 16:49:48 dosuser Exp dosuser $ </tt></p>
+</body>
+</html>
diff --git a/doc/index.htm b/doc/index.htm
new file mode 100644
index 0000000..477f32e
--- /dev/null
+++ b/doc/index.htm
@@ -0,0 +1,45 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
+<title>viDOOM - Free Software DOOM editor</title>
+</head>
+
+<body>
+
+<h1 align="center">viDOOM</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="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>
+</ol>
+
+<hr width="99%">
+
+<p><a href="http://www.noddybox.demon.co.uk/vidoom">viDOOM home
+page</a></p>
+
+<p><tt>$Id: index.htm,v 1.6 2000/07/24 16:31:58 dosuser Exp dosuser $ </tt></p>
+</body>
+</html>
diff --git a/doc/license.htm b/doc/license.htm
new file mode 100644
index 0000000..d07b0b8
--- /dev/null
+++ b/doc/license.htm
@@ -0,0 +1,362 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
+<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..0468a8a
--- /dev/null
+++ b/doc/mainmenu.htm
@@ -0,0 +1,111 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
+<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>
+
+<p>Edit the current level. See <a href="editing.htm">editing</a>
+for details.</p>
+
+<h3>Load Level from open WADs</h3>
+
+<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 procede. This warning can be
+disabled in the INI file (see <a href="overview.htm#MAP_CLEAR">overview</a>).</p>
+
+<h3>Save Current Level</h3>
+
+<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>
+
+<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 bulding
+section in the config file</a> has not been set up.</p>
+
+<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>
+
+<h3>Create New Empty Level</h3>
+
+<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 procede. This warning can be
+disabled in the INI file (see <a href="overview.htm#MAP_CLEAR">overview</a>).</p>
+
+<h3>Change Current Level Name</h3>
+
+<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>
+
+<h3>Open PWAD file</h3>
+
+<p>Allows a PWAD file to be opened.</p>
+
+<h3>Close PWAD file</h3>
+
+<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>
+
+<h3>Preview Level</h3>
+
+<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>
+
+<h3>About viDOOM</h3>
+
+<p>See who was responsible for this.</p>
+
+<h3>Quit</h3>
+
+<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 procede. This warning can be
+disabled in the INI file (see <a href="overview.htm#MAP_EXIT">overview</a>).</p>
+
+<hr>
+
+<p><a href="index.htm">Back to index</a></p>
+
+<p><tt>$Id: mainmenu.htm,v 1.5 2000/07/19 16:39:26 dosuser Exp dosuser $ </tt></p>
+</body>
+</html>
diff --git a/doc/overview.htm b/doc/overview.htm
new file mode 100644
index 0000000..f10576e
--- /dev/null
+++ b/doc/overview.htm
@@ -0,0 +1,843 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
+<title>viDOOM - Free Software DOOM editor</title>
+</head>
+
+<body>
+
+<h1 align="center">viDOOM Overview</h1>
+
+<h2>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 accomadte 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>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
+offchance other people may too. </p>
+
+<hr>
+
+<h2>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>Initialisation Files</h2>
+
+<h3>VIDOOM.INI</h3>
+
+<p>On starting viDOOM will look for a file in the current
+directory called <strong>vidoom.ini</strong>. 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="#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><strong>Doom</strong></li>
+ <li><strong>Ultimate Doom</strong></li>
+ <li><strong>Doom 2</strong></li>
+ <li><strong>TNT: Evilution</strong></li>
+ <li><strong>Plutonia Experiment</strong></li>
+ <li><strong>ZDoom</strong></li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td><a name="GAME_ASK"></a><strong>ask</strong></td>
+ <td>If set to <strong>yes</strong> 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 configuraiton</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 <strong>yes</strong>
+ and <strong>no</strong></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 <strong>yes</strong>, 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 <strong>yes</strong> 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 <strong>yes</strong> 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><strong><tt>default_edit_mode</tt></strong></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"><strong>sector</strong></a></li>
+ <li><a href="editing.htm#LINEDEF_MODE"><strong>linedef</strong></a></li>
+ <li><a href="editing.htm#THING_MODE"><strong>thing</strong></a></li>
+ <li><a href="editing.htm#VERTEX_MODE"><strong>vertex</strong></a></li>
+ <li><a href="editing.htm#MULTI_MODE"><strong>multi</strong></a></li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><strong><tt>default_scale</tt></strong></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 <strong>yes</strong>
+ and <strong>no</strong>. </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 <strong>yes</strong> and <strong>no</strong>.
+ </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><strong>none</strong> - nothing is done. The
+ right mouse button works as expected.</li>
+ <li><strong>add</strong> - the object the mouse is
+ over is added to the selected objects before
+ displaying the menu.</li>
+ <li><strong>single</strong> - 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><strong>none</strong> - nothing is done. The new
+ object is left inselected.</li>
+ <li><strong>add</strong> - the new object is added to
+ the selected objects.</li>
+ <li><strong>single</strong> - 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><strong>always</strong> - always merge the
+ overlapping linedefs without any prompting.</li>
+ <li><strong>ask</strong> - confirms with the user
+ before mergeing linedefs or not as requested. </li>
+ <li><strong>never</strong> - 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><strong>select</strong> - clears the currently
+ selected linedefs (if any) and selects the
+ linedefs that have new double sides.</li>
+ <li><strong>ask</strong> - confirms with the user. If
+ the user opts to select the linedefs, the current
+ selection is cleared and the altered linedefs
+ selected.</li>
+ <li><strong>never</strong> - 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><strong>all</strong> - move all linedefs that
+ border the sector.</li>
+ <li><strong>right</strong> - move only linedefs that
+ have this sector to their right.</li>
+ <li><strong>left</strong> - 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><strong><tt>show_full_linedef_info</tt></strong></td>
+ <td valign="top">If set to <strong>yes</strong> 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 <strong>no</strong> then linedef type
+ will be displayed as a hex value and it's class.</td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><strong><tt>tag_highlight</tt></strong></td>
+ <td valign="top">If set to <strong>yes</strong> 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 name="viDOOM"></a>[viDOOM]</h3>
+
+<p>Main menu configuration</p>
+
+<table border="1" cellpadding="3">
+ <tr>
+ <td valign="top" nowrap><b><tt>initial_empty_map</tt></b></td>
+ <td valign="top">If set to <strong>yes</strong> then on
+ startup viDOOM will have an empty map, either MAP01 or
+ E1M1, ready for editing.</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
+ effecient 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
+ effecient 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
+ effecient 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><strong>[Check LINEDEF]</strong></h3>
+
+<p>This defines how the <strong>Check LINEDEF</strong> 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 defineable
+within the INI file:</p>
+
+<table border="1" cellpadding="3">
+ <tr>
+ <td valign="top" nowrap><strong><tt>assume_yes</tt></strong></td>
+ <td valign="top">If this is set to <strong>yes</strong>
+ then any time a question would be asked to see if it's OK
+ to remove a texture that viDOOM considers unneccessary
+ then it will be removed without asking. Likewise when
+ asking to add missing textures the texture picklist will
+ appear (with the lindef 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><strong><tt>check_1side_lower</tt></strong></td>
+ <td valign="top">If set to <strong>yes</strong> 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><strong><tt>check_1side_middle</tt></strong></td>
+ <td valign="top">If set to <strong>yes</strong> 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><strong><tt>check_1side_upper</tt></strong></td>
+ <td valign="top">If set to <strong>yes</strong> 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><strong><tt>check_2side_lower</tt></strong></td>
+ <td valign="top">If set to <strong>yes</strong> 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><strong><tt>check_2side_middle</tt></strong></td>
+ <td valign="top">If set to <strong>yes</strong> 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><strong><tt>check_2side_upper</tt></strong></td>
+ <td valign="top">If set to <strong>yes</strong> 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><strong><tt>check_2side_same_sector</tt></strong></td>
+ <td valign="top">If set to <strong>yes</strong> 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 <strong>yes</strong> 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><strong><tt>ignore</tt></strong></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 mergeing
+ 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 <strong>MAP01.WAD</strong>
+ will be passed through the node builder, whereas <strong>WALL_STRUCT.WAD</strong>
+ 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 occurance 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 occurance 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 <strong>after</strong>
+ 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 <strong>yes</strong> then
+ the node builder is invoked as:<p><tt>command infile
+ outfile</tt></p>
+ <p>If set to <strong>no</strong> 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 <strong>yes</strong> then the
+ node builder described above is used on any maps being
+ saved. If set to <strong>no</strong> then the map is
+ saved and the nodes must be built outside of viDOOM.</td>
+ </tr>
+</table>
+
+<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><strong><tt>gui_hi</tt></strong></td>
+ <td valign="top">The brightest colour used to draw the 3D
+ looking interface.</td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><strong><tt>gui_mid</tt></strong></td>
+ <td valign="top">The medium colour used to draw the 3D
+ looking interface.</td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><strong><tt>gui_lo</tt></strong></td>
+ <td valign="top">The darkest colour used to draw the 3D
+ looking interface.</td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><strong><tt>gui_text</tt></strong></td>
+ <td valign="top">The colour of text.</td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><strong><tt>gui_textshadow</tt></strong></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 <strong>text</strong>
+ then no shadows are drawn.</td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><strong><tt>gui_bold</tt></strong></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><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><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 seperated 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, lindefs, sectors and so
+ on. <br>
+ viDOOM is currently supplied with <b>doom.cfg</b> and <b>doom2.cfg</b>.
+ <br>
+ It is planned to soon include a <b>zdoom.cfg</b> so that
+ ZDOOM/BOOM special features can be added to levels.</td>
+ </tr>
+</table>
+
+<hr>
+
+<h3><a name="CONFIG_FILE"></a>Config file</h3>
+
+<p>There is two config files supplied with viDOOM, <strong>doom.cfg</strong>
+and <strong>doom2.cfg</strong>. 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 seperate fields, e.g.</p>
+
+<p><tt>Field 1|Field 2</tt></p>
+
+<p>Blank lines and lines starting with a comment character (#)
+are ignored. The following sections are defined:</p>
+
+<table border="1" cellpadding="3">
+ <tr>
+ <td valign="top" nowrap><strong><tt>%INCLUDE_FILES</tt></strong></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><strong><tt>%THING_CLASSES</tt></strong></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><strong><tt>%LINEDEF_CLASSES</tt></strong></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><strong><tt>%SECTOR_CLASSES</tt></strong></td>
+ <td valign="top">Defines the classes to group SECTOR
+ types into in the editor picklists. Each line is a class
+ name.</td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><a name="THING_TYPES"></a><strong><tt>%THING_TYPES</tt></strong></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><strong><tt>%THING_FLAGS</tt></strong></td>
+ <td valign="top">Defines the flags used when defining
+ THINGs. Each line is in the form &quot;Bit
+ Number|Name|Shorthand Name&quot;. e.g.<p><tt>0|Skill 1
+ and 2|Sk 1/2</tt></p>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><a name="LINEDEF_TYPES"></a><strong><tt>%LINEDEF_TYPES</tt></strong></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><strong>Note:</strong> 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).</p>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><a name="LINEDEF_FLAGS"></a><strong><tt>%LINEDEF_FLAGS</tt></strong></td>
+ <td valign="top">Defines the flags used when defining
+ LINEDEFs. Each line is in the form &quot;Bit
+ Number|Name|Shorthand Character&quot;. e.g.<p><tt>0|Impassible|I</tt></p>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><strong><tt>%LINEDEF_FLAGS_EXTRA</tt></strong></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><strong><tt>%LINEDEF_DEFAULTS</tt></strong></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><strong><tt>%SECTOR_TYPES</tt></strong></td>
+ <td valign="top">Defines the names and IDs for the
+ different sector types. Each line is in the form
+ &quot;Class|ID|Long name|Short name&quot;. e.g.<p><tt>Damage/Lights|4|Lose
+ -10/20% health &amp; blink lights 0.5 sec|-10/20% &amp;
+ 0.5 blink</tt></p>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><a name="SECTOR_STYLES"></a><strong><tt>%SECTOR_STYLES</tt></strong></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><strong><tt>%EMPTY_TEXTURE_NAME</tt></strong></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><strong><tt>%NORMAL_TYPES</tt></strong></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><strong><tt>%LINEDEF_CHECK_DEFAULT</tt></strong></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>
+</table>
+
+<hr>
+
+<p><a href="index.htm">Back to index</a></p>
+
+<p><tt>$Id: overview.htm,v 1.7 2000/08/05 21:30:12 dosuser Exp dosuser $ </tt></p>
+</body>
+</html>
diff --git a/doc/porting.htm b/doc/porting.htm
new file mode 100644
index 0000000..1dd3725
--- /dev/null
+++ b/doc/porting.htm
@@ -0,0 +1,1316 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
+<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>
+subdirectoy called <em>OS</em>.cfg. Also all the OS dependent C
+source should go into a subdirectory 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 subdirectory containing
+ the OS dependent C sources. The idea of defineing this
+ 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 optimisations 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 librarypaths
+ required by this OS.</td>
+ </tr>
+ <tr>
+ <td valign="top" nowrap><strong><tt>DIRSEP</tt></strong></td>
+ <td valign="top">The directory seperator 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 exectuable. 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>
+guimode=3D</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
+ positve 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>.
+ Some examples are:<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 pallete 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 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
+ {
+ char *text;
+ int client_index;
+ } 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.</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
+ {
+ char *text;
+ int type;
+ union /* Data */
+ {
+ int i;
+ char s[PLAT_DIAL_MAXSTRLEN+1];
+ double d;
+ } data;
+ } PLAT_DIALOG;</pre>
+ </td>
+ <td valign="top">This structure is used to define entries
+ for a simple dialog.<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 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 and PLAT_DIAL_DOUBLE.</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>
+ </table>
+ </td>
+ </tr>
+</table>
+
+<p>Note that along with the types, the following predifined
+values are set (these are read from the INI file). Note that they
+shold 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 destory 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_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 <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 matchs <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, char *opts[], int
+*val)</tt></strong></p>
+
+<blockquote>
+ <p>Display a mutli-selection radio box. The items are
+ described <em>opts</em>, which terminates with a NULL
+ pointer. <em>Val</em> points to a value which is used to
+ enable/disable the options dependent on the integers bit
+ setting. The integer pointed to by <em>val</em> is updated on
+ exit of the multi-selection box if it is not cancelled.</p>
+ <p>Note that the bit patterns are matched bit number to opt
+ index. ie.</p>
+ <ul>
+ <li>val &amp; 0x0001 corresponds to opts[0]</li>
+ <li>val &amp; 0x0002 corresponds to opts[1] ...</li>
+ <li>val &amp; 0x0010 corresponds to opts[4] ...</li>
+ <li>val &amp; 0x0100 corresponds to opts[8] ...</li>
+ <li>val &amp; 0x8000 corresponds to opts[15]</li>
+ </ul>
+ <p>A maximum of 16 bits should be all that needs supporting
+ currently. 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
+ withing the <em>dial</em> elements is undefined.</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 filname 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 seperators 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. Te 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 arguemnts, terminated with a
+ NULL pointer. Note that arguments may contain more than one
+ argument in each line - the actual command is described
+ simply by concatanating 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 comand 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:</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 should 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 <em><strong>must</strong></em> be copied into the
+ installation directory.</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/*.png</tt></td>
+ <td valign="top"><tt>$INSTALLDIR/doc/*.png</tt></td>
+ </tr>
+</table>
+
+<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.</p>
+
+<hr>
+
+<p><a href="index.htm">Back to index</a></p>
+
+<p><tt>$Id: porting.htm,v 1.17 2000/08/05 19:58:21 dosuser Exp dosuser $</tt></p>
+</body>
+</html>
diff --git a/doc/thanks.htm b/doc/thanks.htm
new file mode 100644
index 0000000..0cd1173
--- /dev/null
+++ b/doc/thanks.htm
@@ -0,0 +1,70 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
+<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 mis-informed 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="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="http://doomgate.gamers.org/dhs/helpdocs/dmsp1666.html">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><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() alogrithm - 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>Various
+authors</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.4 2000/08/05 21:30:12 dosuser Exp dosuser $ </tt></p>
+</body>
+</html>
diff --git a/doom.cfg b/doom.cfg
new file mode 100644
index 0000000..2f7c71d
--- /dev/null
+++ b/doom.cfg
@@ -0,0 +1,527 @@
+# 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.26 2000/07/19 16:36:12 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
+#
+
+
+# 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 "class name"
+#
+#
+%SECTOR_CLASSES
+Normal
+Lights
+Damage
+Damage/Lights
+Damage/Exit
+Door
+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 "bit number|name|short hand name"
+#
+# Note there can be no gaps in the bit numbers. If there are bits that
+# don't matter they must still defined, eg.
+# 0|Bit 0
+# 1|Unused
+# 2|Bit 2
+#
+%THING_FLAGS
+0|Skill 1 and 2|Sk 1/2
+1|Skill 3|Sk 3
+2|Skill 4 and 5|Sk 4/5
+3|Deaf|Deaf
+4|Deathmatch only|Deathmatch
+
+
+# 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 section. Each line is "bit number|name|short hand char"
+#
+# Note there can be no gaps in the bit numbers. If there are bits that
+# don't matter they must still defined, eg.
+# 0|Bit 0|1
+# 1|Unused|-
+# 2|Bit 2|1
+#
+%LINEDEF_FLAGS
+0|Impassible|I
+1|Block monsters|M
+2|Two-sided|2
+3|Upper unpegged|U
+4|Lower unpegged|L
+5|Secret|S
+6|Blocks sound|B
+7|Not on map|N
+8|Already on map|A
+
+
+# LINEDEF_FLAGS_EXTRA
+#
+# Simply defines which of the LINEDEF flag bits are the ones that indicates
+# certain eatures. 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:
+# 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
+Normal|0|Normal|Normal
+Lights|1|Light random off|Random off
+Lights|2|Blink lights 0.5 second|0.5 blink
+Lights|3|Blink lights 1.0 second|1.5 blink
+Damage/Lights|4|Lose -10/20% health & blink lights 0.5 sec|-10/20% & 0.5 blink
+Damage|5|Lose -5/10% health|-5/10%
+Damage|7|Lose -2/5% health|-2/5%
+Lights|8|Oscillating light|Oscillating light
+Secret|9|Secret|Secret
+Door|10|Door closes after 30 seconds|30 sec door
+Damage/Exit|11|-10/20% health - end level if < 11%|Damage/end level
+Lights|12|Blink lights synchronised 0.5 second|0.5 blink sync
+Lights|13|Blink lights synchronised 1.0 second|1.5 blink sync
+Door|14|Door opens after 300 seconds|300 sec door
+Damage|16|Lose -10/20% health|-10/20%
+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.
+#
+%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
+
+
+# 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
+#
+%LINEDEF_CHECK_DEFAULT
+ASHWALL2
diff --git a/doom2.cfg b/doom2.cfg
new file mode 100644
index 0000000..1f58a0a
--- /dev/null
+++ b/doom2.cfg
@@ -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
+#
+# -------------------------------------------------------------------------
+#
+# $Id: doom2.cfg,v 1.12 2000/06/12 15:58:54 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
diff --git a/edit.c b/edit.c
new file mode 100644
index 0000000..0402287
--- /dev/null
+++ b/edit.c
@@ -0,0 +1,734 @@
+/*
+
+ 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)
+{
+ 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;
+
+ 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;
+
+ 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);
+
+ 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);
+
+ 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;
+
+ 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));
+ }
+}
+
+
+void EditLoop(void)
+{
+ GFXEvent ev;
+
+ 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;
+
+ 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..56c11df
--- /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 _EDIT_H
+#define _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/editcord.c b/editcord.c
new file mode 100644
index 0000000..da3097f
--- /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)
+{
+ 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)
+{
+ 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;
+
+ 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;
+
+ 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)
+{
+ 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;
+
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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..c4f745f
--- /dev/null
+++ b/editcrse.c
@@ -0,0 +1,398 @@
+/*
+
+ 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;
+
+ 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;
+
+ TRACE;
+
+ /* Check enough vertexes
+ */
+ if (ListSize(in_v)<3)
+ {
+ GuiInfoBox("ERROR","Need 3 or more vertices|"
+ "to create a sector");
+ FullRedraw();
+ 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..0ec30a7
--- /dev/null
+++ b/editdraw.c
@@ -0,0 +1,346 @@
+/*
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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 DEBUG
+ static int chk=TRUE;
+ static int db=FALSE;
+#endif
+
+ TRACE;
+
+ 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 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)
+{
+ 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..d87deeb
--- /dev/null
+++ b/editevnt.c
@@ -0,0 +1,991 @@
+/*
+
+ 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"
+
+
+/* ---------------------------------------- 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;
+
+ 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
+ {
+ 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)
+{
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+
+ TRACE;
+
+ if (k.code!=GFX_ASCII)
+ switch(k.code)
+ {
+ case GFX_DOWN:
+ oy-=scale*(k.shift ? 60 : 20);
+
+ if (check_mouse)
+ GenericCheckMouse();
+
+ FullRedraw();
+ break;
+
+ case GFX_UP:
+ oy+=scale*(k.shift ? 60 : 20);
+
+ if (check_mouse)
+ GenericCheckMouse();
+
+ FullRedraw();
+ break;
+
+ case GFX_RIGHT:
+ ox+=scale*(k.shift ? 60 : 20);
+
+ if (check_mouse)
+ GenericCheckMouse();
+
+ FullRedraw();
+ break;
+
+ case GFX_LEFT:
+ 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 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;
+
+ TRACE;
+
+ HandleMoveKey(k,TRUE);
+ ObjectKey(k);
+
+ if (k.code!=GFX_ASCII)
+ switch(k.code)
+ {
+ case GFX_F1:
+ GuiInfoBox("HELP (Keys)","%s",general_help_keys);
+ GuiInfoBox("HELP (Mouse)","%s",general_help_mouse);
+ FullRedraw();
+ break;
+
+ case GFX_F2:
+ {
+ char s[128];
+
+ sprintf(s,"%s Mode Keys",typename);
+ GuiInfoBox(s,"%s",mode_help[edit_mode]);
+ FullRedraw();
+ 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_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
+ {
+ 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");
+ FullRedraw();
+ }
+ }
+ }
+ break;
+
+ case GFX_ESC:
+ quit=TRUE;
+ break;
+
+ default:
+ break;
+ }
+ else
+ switch(toupper(k.ascii))
+ {
+ 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)
+{
+ 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..afb922d
--- /dev/null
+++ b/editgui.c
@@ -0,0 +1,470 @@
+/*
+
+ 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 <stdarg.h>
+
+#define TXTPMT(lr,ulm) "Pick " ulm " texture for " lr " sidedef"
+
+
+/* ---------------------------------------- GUI FUNCTIONS
+*/
+int GetTexture(char *t,DirName d)
+{
+ int f;
+
+ 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;
+
+ 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)
+
+{
+ TRACE;
+
+ if (set_class)
+ {
+ 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)
+{
+ TRACE;
+
+ 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;
+
+ 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;
+
+ 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));
+}
+
+
+/* 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..1efcb7f
--- /dev/null
+++ b/editline.c
@@ -0,0 +1,3322 @@
+/*
+
+ 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"
+
+
+/* ---------------------------------------- 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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)
+{
+ 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)
+{
+ 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;
+
+ 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;
+
+ 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())!=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,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);
+ }
+
+ /* 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++)
+ {
+ in_sect=SectorInBox(step.p[0][f],step.p[1][f+1]);
+
+ if (in_sect!=-1)
+ s=GETSECT(in_sect);
+ 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==-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,
+ ((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)
+{
+ 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)
+{
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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);
+}
+
+
+/* ---------------------------------------- GENERIC LINEDEF FUNCS
+*/
+int PositionOnObject_LINEDEF(int x,int y,void *data)
+{
+ EditLine *l;
+
+ 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;
+
+ 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 ((id=SelectLinedef())!=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;
+
+ 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;
+
+ TRACE;
+
+ if (!draw_current_info)
+ return;
+
+ if ((current!=-1)&&((l=GETLINE(current))))
+ {
+ if (show_full_linedef_info)
+ GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE,
+ "Linedef No : %-5d|"
+ "Flags : %s|"
+ "Type [%4.4x]: %-35.35s|"
+ "Tag : %-5d",
+ current,
+ LinedefFlagText(l->l.flags),
+ l->l.type,LinedefName(l->l.type),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,
+ LinedefFlagText(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)
+ GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE,
+ "Linedef No : -|"
+ "Flags : -|"
+ "Type : %35s|"
+ "Tag : -","");
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ TRACE;
+
+ 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;
+
+ 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;
+
+ TRACE;
+
+ if (MapSize(vertex)<2)
+ {
+ GuiInfoBox("ERROR","Need more that 2 vertices|to make a line");
+ FullRedraw();
+ }
+ 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);
+ FullRedraw();
+ ok=FALSE;
+ }
+ else if ((to>=MapSize(vertex))||(to<0)||(!GETVERT(to)))
+ {
+ GuiInfoBox("ERROR","%d (to) is an invalid vertex",to);
+ FullRedraw();
+ 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;
+
+ 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;
+
+ TRACE;
+
+ s=NULL;
+ cancel=FALSE;
+ GFX_redraw();
+
+ opt=GUI_menu("Linedef/sidedef",ms.x,ms.y,linedef_popup,GUI_CANCEL);
+
+ switch(opt)
+ {
+ case TM_RIGHT_SIDE:
+ opt=GUI_menu("Right sidedef",ms.x,ms.y,right_popup,GUI_CANCEL);
+ break;
+
+ case TM_LEFT_SIDE:
+ opt=GUI_menu("Left sidedef",ms.x,ms.y,left_popup,GUI_CANCEL);
+ break;
+
+ default:
+ break;
+ }
+
+ 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_FLAGS:
+ l=SelHead();
+ f=l->l.flags;
+
+ if (GUI_multi_box("Flags",LinedefFlagArray(),&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);
+
+ 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);
+ }
+
+ 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);
+
+ *s=MapSize(linedef)+1;
+
+ 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");
+ FullRedraw();
+ }
+ 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_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;
+ }
+
+ 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;
+
+ 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");
+ FullRedraw();
+ }
+ 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");
+ FullRedraw();
+ }
+ 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_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;
+
+ 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;
+
+ TRACE;
+
+ switch(k.code)
+ {
+ case GFX_F12:
+ li=ListNew(sizeof(int));
+
+ change=FALSE;
+
+ if (ListSize(selected)==0)
+ {
+ GuiInfoBox("ERROR","No linedefs selected");
+ FullRedraw();
+ return;
+ }
+
+ i=ListIterator(selected);
+
+ while(i)
+ {
+ memcpy(&f,IteratorData(i),sizeof(f));
+
+ if ((l=GETLINE(f)))
+ {
+ TRACE;
+ if (check_1side_lower)
+ if ((!l->sl)&&(strcmp(l->sr->lower,empty_texture)))
+ if ((check_line_assume_yes)||
+ (YesNo("LINEDEF %d is one-sided with a "
+ "lower texture. Remove?",f)))
+ {
+ strcpy(l->sr->lower,empty_texture);
+ change=TRUE;
+ IntListUniqAdd(li,f);
+ }
+
+ TRACE;
+ if (check_1side_middle)
+ if ((!l->sl)&&(!strcmp(l->sr->middle,empty_texture)))
+ if ((check_line_assume_yes)||
+ (YesNo("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);
+ }
+ }
+
+ TRACE;
+ if (check_1side_upper)
+ if ((!l->sl)&&(strcmp(l->sr->upper,empty_texture)))
+ if ((check_line_assume_yes)||
+ (YesNo("LINEDEF %d is one-sided with a "
+ "upper texture. Remove?",f)))
+ {
+ strcpy(l->sr->upper,empty_texture);
+ change=TRUE;
+ IntListUniqAdd(li,f);
+ }
+
+ 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)||
+ (YesNo("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)||
+ (YesNo("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)||
+ (YesNo("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);
+ }
+ }
+
+ 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)||
+ (YesNo("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);
+ }
+ }
+
+ 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)||
+ (YesNo("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)||
+ (YesNo("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)||
+ (YesNo("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);
+ }
+ }
+
+ 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)||
+ (YesNo("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");
+ FullRedraw();
+ }
+
+ 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));
+}
+
+
+/* END OF FILE */
diff --git a/editmrg.c b/editmrg.c
new file mode 100644
index 0000000..7aaa10b
--- /dev/null
+++ b/editmrg.c
@@ -0,0 +1,567 @@
+/*
+
+ 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)
+{
+ 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)
+{
+ 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());
+ FullRedraw();
+ 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());
+ FullRedraw();
+
+ 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);
+ FullRedraw();
+
+ 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..3e78da0
--- /dev/null
+++ b/editmult.c
@@ -0,0 +1,582 @@
+/*
+
+ 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 "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;
+
+ 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)
+{
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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)
+{
+ TRACE;
+ return;
+}
+
+
+void DrawObject_MULTI(void *data, int selmode)
+{
+ MultiObj *m;
+
+ 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;
+
+ 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)
+{
+ 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;
+
+ 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;
+
+ 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;
+
+ 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)
+{
+ TRACE;
+}
+
+
+void LocateObject_MULTI(void *obj)
+{
+ MultiObj *m;
+
+ TRACE;
+
+ m=obj;
+
+ ox=m->x-scale*SCRW/2;
+ oy=m->y+scale*SCRH/2;
+
+}
+
+
+void ObjectInsert_MULTI(void)
+{
+ TRACE;
+}
+
+
+void ObjectDelete_MULTI(void)
+{
+ TRACE;
+}
+
+
+void ObjectMenu_MULTI(void)
+{
+ TRACE;
+}
+
+
+void ObjectKey_MULTI(GFXKey k)
+{
+ TRACE;
+}
+
+
+int ObjectHasTag_MULTI(void *obj, int tag)
+{
+ TRACE;
+ return(FALSE);
+}
+
+
+void SetSelect_MULTI(int i, int mode)
+{
+ Object *o;
+ MultiObj *m;
+ Map om;
+
+ 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;
+ }
+}
+
+
+/* END OF FILE */
diff --git a/editsect.c b/editsect.c
new file mode 100644
index 0000000..4c1cb8f
--- /dev/null
+++ b/editsect.c
@@ -0,0 +1,1577 @@
+/*
+
+ 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 <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;
+
+ 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)
+{
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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);
+}
+
+
+/* This code is kept as a semi-working base if the routine based on the
+ comp.graphics.algorithm FAQ fails (this could be as I don't do it on a list
+ of ordered vertexes as the original does - doing on a line basis should be
+ the same though).
+*/
+int OLD_PositionOnObject_SECTOR(int x,int y,void *data)
+{
+ int x_no;
+ int y_no;
+ EditSect *s;
+ EditLine *l;
+ Iterator i;
+
+ TRACE;
+
+ s=data;
+
+ if (BOXBOUND(x,y,s->min_x,s->min_y,s->max_x,s->max_y))
+ {
+ x_no=0;
+ y_no=0;
+
+ 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)))
+ {
+ /* This is a bit (understatement) kludgy, but we do 2 line
+ crossing checks, one in X and one in the Y direction. This
+ way we can omit a lot of extra checking for lines bisecting
+ shared vertexes and the like.
+ */
+ if (LinesCross(s->min_x,y,x,y,
+ l->v[0]->v.x,l->v[0]->v.y,
+ l->v[1]->v.x,l->v[1]->v.y))
+ x_no++;
+
+ if (LinesCross(x,s->min_y,x,y,
+ l->v[0]->v.x,l->v[0]->v.y,
+ l->v[1]->v.x,l->v[1]->v.y))
+ y_no++;
+ }
+
+ i=IteratorNext(i);
+ }
+
+ /* Odd number of crossed lines means we're in the sector
+ */
+ return((x_no%2)||(y_no%2));
+ }
+ else
+ return(FALSE);
+}
+
+
+void SelectBox_SECTOR(int x1,int y1,int x2,int y2)
+{
+ Object *o;
+ EditSect *s;
+ int f;
+
+ 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())!=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;
+
+ 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;
+
+ TRACE;
+
+ if (!draw_current_info)
+ return;
+
+ if ((current!=-1)&&((s=GETSECT(current))))
+ 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 : %-30s|"
+ "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(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 : %-30s|"
+ "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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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}
+ };
+
+ 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 edittable.",sec);
+
+ FullRedraw();
+ 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");
+ FullRedraw();
+ 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)
+{
+ TRACE;
+
+ GuiInfoBox("NOTICE","Sector not deleted| |"
+ "Sectors are automatically deleted|"
+ "when saving the map if no LINEDEFs|"
+ "are bound to it.");
+ FullRedraw();
+}
+
+
+void ObjectMenu_SECTOR(void)
+{
+ Iterator i;
+ EditSect *s;
+ int *f;
+ int cancel;
+
+ 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:
+ s=SelHead();
+ 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();
+ }
+ 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();
+
+ 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_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));
+}
+
+
+/* END OF FILE */
diff --git a/editsel.c b/editsel.c
new file mode 100644
index 0000000..7c64ece
--- /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;
+
+ 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;
+
+ 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;
+
+ 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..b6764d1
--- /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;
+
+ 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)
+{
+ 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..ecc9dc6
--- /dev/null
+++ b/editthng.c
@@ -0,0 +1,636 @@
+/*
+
+ 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 "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;
+
+ 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;
+
+ 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;
+
+ 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 ((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;
+
+ TRACE;
+ t=data;
+
+ a=((t->t.ang+22)/45%8);
+ 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;
+
+ TRACE;
+
+ if (!draw_current_info)
+ return;
+
+ if ((current!=-1)&&((ti=GETTHING(current))))
+ GuiDrawInfoBox("THING",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE,
+ "Thing No : %d|"
+ "Thing Id : %d|"
+ "Angle : %s (%d)|"
+ "Flags : %s (0x%.2x)|"
+ "Name : %-60s",
+ current,
+ ti->t.type,
+ ANGLESTR(ti->t.ang),ti->t.ang,
+ ThingFlagText((int)ti->t.opt),ti->t.opt,
+ ThingName(ti->t.type));
+ else
+ GuiDrawInfoBox("THING",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE,
+ "Thing No : -|"
+ "Thing Id : -|"
+ "Angle : -|"
+ "Flags : - (-)|"
+ "Name : %-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;
+
+ 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;
+
+ 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;
+
+ 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)
+{
+ TRACE;
+}
+
+
+void LocateObject_THING(void *obj)
+{
+ EditThing *t;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ TRACE;
+
+ cancel=FALSE;
+ GFX_redraw();
+
+ switch(GUI_menu("Thing",ms.x,ms.y,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_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_FLAGS:
+ i=ListIterator(selected);
+ s=IteratorData(i);
+ t=GETTHING(*s);
+ f=t->t.opt;
+
+ if (GUI_multi_box("Flags",ThingFlagArray(),&f))
+ {
+ ed_thing.opt=f;
+
+ while(i)
+ {
+ s=IteratorData(i);
+ t=GETTHING(*s);
+ t->t.opt=ed_thing.opt;
+ 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;
+
+ default:
+ cancel=TRUE;
+ break;
+ }
+
+ if ((!cancel)&&(clear_on_menu))
+ {
+ ClearSelection();
+ FullRedraw();
+ }
+ else if (redraw)
+ FullRedraw();
+}
+
+
+void ObjectKey_THING(GFXKey k)
+{
+}
+
+
+int ObjectHasTag_THING(void *obj, int tag)
+{
+ return(FALSE);
+}
+
+
+/* END OF FILE */
diff --git a/editvar.c b/editvar.c
new file mode 100644
index 0000000..b28fc9f
--- /dev/null
+++ b/editvar.c
@@ -0,0 +1,383 @@
+/*
+
+ 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;
+
+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;
+
+Thing ed_thing={0,0,0,1,0x07};
+
+/* 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);
+
+char *typename;
+Map map;
+
+
+/* ---------------------------------------- MENUS AND DIALOGS
+*/
+PLAT_MENU thing_popup[]={
+ {"Change type",TM_TYPE},
+ {"Change angle",TM_ANGLE},
+ {"Change flags",TM_FLAGS},
+ {"Snap selected things",TM_SNAP},
+ {"Move",TM_MOVE},
+ {"Delete",TM_DELETE},
+ {NULL,GUI_CANCEL}
+ };
+
+PLAT_RADIO thing_angle[]={
+ {"N",90},
+ {"NE",45},
+ {"E",0},
+ {"SE",315},
+ {"S",270},
+ {"SW",225},
+ {"W",180},
+ {"NW",135},
+ {NULL,GUI_CANCEL}
+ };
+
+PLAT_MENU vertex_popup[]={
+ {"Chain selected vertices",TM_CHAIN},
+ {"Chain selected vertices "
+ "into a sector",TM_CHAIN_SECTOR},
+ {"Merge selected vertices",TM_MERGE},
+ {"Snap selected vertices",TM_SNAP},
+ {"Move",TM_MOVE},
+ {"Delete",TM_DELETE},
+ {NULL,GUI_CANCEL}
+ };
+
+PLAT_MENU right_popup[]=
+ {
+ {"Change upper texture",TM_UPPER_T_R},
+ {"Change middle texture",TM_MIDDLE_T_R},
+ {"Change lower texture",TM_LOWER_T_R},
+ {"Change texture offset",TM_OFFSET_R},
+ {"Adjust texture offset",TM_ADJUST_R},
+ {"Change sector",TM_SECTOR_R},
+ {"Align textures",TM_ALIGN_R},
+
+ {NULL,GUI_CANCEL}
+ };
+
+PLAT_MENU left_popup[]=
+ {
+ {"Change upper texture",TM_UPPER_T_L},
+ {"Change middle texture",TM_MIDDLE_T_L},
+ {"Change lower texture",TM_LOWER_T_L},
+ {"Change texture offset",TM_OFFSET_L},
+ {"Adjust texture offset",TM_ADJUST_L},
+ {"Change sector",TM_SECTOR_L},
+ {"Align textures",TM_ALIGN_L},
+
+ {NULL,GUI_CANCEL}
+ };
+
+PLAT_MENU linedef_popup[]=
+ {
+ {"Change linedef flags",TM_FLAGS},
+ {"Change linedef type",TM_TYPE},
+ {"Swap sides",TM_SWAP_SIDES},
+ {"Split line",TM_SPLIT_LINE},
+ {"Select trail from this linedef",TM_TRACK_LINE},
+ {"Join linedefs with steps",TM_STEPS},
+
+ {"Right sidedef =>",TM_RIGHT_SIDE},
+ {"Left sidedef =>",TM_LEFT_SIDE},
+
+ {"Change tag",TM_TAG},
+ {"Move",TM_MOVE},
+ {"Delete",TM_DELETE},
+ {NULL,GUI_CANCEL}
+ };
+
+PLAT_MENU sector_popup[]=
+ {
+ {"Change floor height",TM_FLOOR},
+ {"Change ceiling height",TM_CEILING},
+ {"Change light level",TM_LIGHT},
+ {"Change tag",TM_TAG},
+ {"Change floor",TM_FLOOR_T},
+ {"Change ceiling",TM_CEILING_T},
+ {"Change type",TM_TYPE},
+ {"Paint sector in style",TM_STYLE},
+
+ {"Move",TM_MOVE},
+ {"Delete",TM_DELETE},
+ {NULL,GUI_CANCEL}
+ };
+
+PLAT_MENU multi_popup[]=
+ {
+ {"Move",TM_MOVE},
+ {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}},
+ };
+
+
+/* ---------------------------------------- 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 |"
+ "Page down/up - zoom in/out |"
+ "Q/W - alter grid lock scale |"
+ "G - grid lock 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) |"
+ "F11/Shift + F11 - locate next/previous selection |"
+ "DELETE - delete selected objects |"
+ "INSERT - insert new object |"
+ "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 an 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 ";
+
+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
+ */
+ "No additional key functions",
+
+ /* MULTI
+ */
+ "No additional key functions"
+ };
+
+
+/* END OF FILE */
diff --git a/editvar.h b/editvar.h
new file mode 100644
index 0000000..410b4dd
--- /dev/null
+++ b/editvar.h
@@ -0,0 +1,619 @@
+/*
+
+ 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 _EDITVAR_H
+
+#define _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;
+
+
+/* ---------------------------------------- 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 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 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
+
+
+extern PLAT_MENU thing_popup[];
+
+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 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];
+
+
+
+/* ---------------------------------------- DRAWING TABLES
+*/
+extern Point t_arrow[8];
+
+extern char *angle_str[8];
+
+#define ANGLESTR(a) (angle_str[(((a)+22)/45%8)])
+
+/* ---------------------------------------- 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,...);
+
+/* 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 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);
+
+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);
+
+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);
+
+/* 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);
+
+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);
+
+#endif
+
+/* END OF FILE */
diff --git a/editvert.c b/editvert.c
new file mode 100644
index 0000000..69a4955
--- /dev/null
+++ b/editvert.c
@@ -0,0 +1,809 @@
+/*
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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)
+{
+ TRACE;
+}
+
+
+void LocateObject_VERTEX(void *obj)
+{
+ EditVert *v;
+
+ 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;
+
+ 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;
+
+ 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");
+
+ FullRedraw();
+ }
+}
+
+
+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;
+
+ 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");
+ redraw=TRUE;
+ }
+ 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");
+ redraw=TRUE;
+ 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++;
+ }
+ }
+ }
+
+ GuiInfoBox("NOTICE","Deleted %d vertices",n);
+ FullRedraw();
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+int ObjectHasTag_VERTEX(void *obj, int tag)
+{
+ return(FALSE);
+}
+
+
+/* END OF FILE */
diff --git a/file.h b/file.h
new file mode 100644
index 0000000..76d2248
--- /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 _FILE_H
+
+#define _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/gfx.h b/gfx.h
new file mode 100644
index 0000000..7194a3c
--- /dev/null
+++ b/gfx.h
@@ -0,0 +1,314 @@
+/*
+
+ 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 _GFX_H
+
+#define _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
+
+
+/* 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..8d80731
--- /dev/null
+++ b/gfxtest.c
@@ -0,0 +1,430 @@
+/*
+
+ 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 "gfx.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 TestRotate(void)
+{
+ GFXEvent e;
+ int q;
+ int x1,y1,x2,y2;
+
+ GFX_clear(BLACK);
+
+ q=FALSE;
+ x1=200;
+ y1=0;
+ x2=0;
+ y2=-190;
+
+ 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:
+ Rotate(0,0,&x1,&y1,10.0);
+ Rotate(0,0,&x2,&y2,-10.0);
+
+ GFX_plot(e.mouse.x+x1,e.mouse.y+y1,RCOL);
+ GFX_plot(e.mouse.x+x2,e.mouse.y+y2,RCOL);
+ break;
+ }
+ }
+}
+
+
+void TestScale(void)
+{
+ GFXEvent e;
+ int q;
+ int f;
+ int x,y;
+
+ GFX_clear(BLACK);
+
+ q=FALSE;
+ x=10;
+ y=10;
+ f=0;
+
+ 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:
+ if (((f++)%20)>10)
+ Scale(0,0,&x,&y,0.9);
+ else
+ Scale(0,0,&x,&y,1.1);
+
+ GFX_plot(e.mouse.x+x,e.mouse.y+y,RCOL);
+ break;
+ }
+ }
+}
+
+
+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);
+}
+
+
+int viDOOM(int argc,char *argv[])
+{
+ static struct
+ {
+ char *t;
+ char k;
+ void (*func)(void);
+ } test[]= {
+ {"Test printing",'P',TestPrint},
+ {"Test mouse",'M',TestMouse},
+ {"Test rectangles",'B',TestRect},
+ {"Test screen dimensions",'D',TestDim},
+ {"Test EDIT Rotate",'R',TestRotate},
+ {"Test EDIT Scale",'S',TestScale},
+ {"Test colours",'C',TestCol},
+ {"Test drawing",'G',TestDraw},
+ {"Test bitmap",'X',TestBm},
+ {NULL,0,NULL}
+ };
+
+ GFXKey k;
+ int quit;
+ int f;
+
+ GFX_init();
+ GFX_open(SCRW,SCRH);
+
+ quit=FALSE;
+
+ while(!quit)
+ {
+ GFX_clear(BLACK);
+ GFX_print(0,0,RED,"Select (ESC quit):");
+
+ f=0;
+ while(test[f].t)
+ {
+ GFX_print(0,GFX_fh()*(f+2),WHITE,"%c - %s",test[f].k,test[f].t);
+ f++;
+ }
+
+ GFX_redraw();
+ GFX_waitkey(&k);
+
+ if (k.code==GFX_ESC)
+ quit=TRUE;
+ else
+ {
+ f=0;
+ while(test[f].t)
+ {
+ if (toupper(k.ascii)==test[f].k)
+ test[f].func();
+ f++;
+ }
+ }
+ }
+
+ return(EXIT_SUCCESS);
+}
diff --git a/globals.c b/globals.c
new file mode 100644
index 0000000..0eb0ad7
--- /dev/null
+++ b/globals.c
@@ -0,0 +1,983 @@
+/*
+
+ 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 "texture.h"
+#include "things.h"
+#include "linedefs.h"
+#include "sectors.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
+
+
+/* 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 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;
+char *game_name="Doom 2 - Hell on Earth";
+
+
+/* ------------------------------ 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 linedef_select=2;
+int tag_highlight=TRUE;
+int show_full_linedef_info=FALSE;
+
+
+/* ------------------------------ viDOOM configuration
+*/
+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 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;
+
+
+
+/* ------------------------------ 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 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_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
+ },
+
+ /* 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
+ },
+ };
+
+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)};
+
+
+
+/* ------------------------------ 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 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},
+ {NULL,0}};
+
+ FILE *fp;
+ char *line;
+ char orig[MAXLEN+1];
+ int mode;
+ char *err;
+
+ mode=M_NONE;
+
+ if (!(fp=fopen(fn,"r")))
+ GFX_exit(EXIT_FAILURE,"Couldn't open config file %s\n",fn);
+
+ while((line=GetLine(fp)))
+ {
+ strcpy(orig,line);
+ err=NULL;
+
+ if (*line=='%')
+ {
+ mode=StrToInt(mode_list,line,M_NONE);
+
+ switch(mode)
+ {
+ case M_SECTOR_STYLES:
+ StartSectorStyles();
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ 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:
+ SectorNewClass(line);
+ 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_SECTOR_TYPES:
+ {
+ 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])
+ SectorAdd(p[0],ATOI(p[1]),p[2],p[3]);
+ else
+ err="SECTOR_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:
+ {
+ char *p[3];
+ int f;
+
+ p[0]=strtok(line,"|");
+
+ if (p[0])
+ p[1]=strtok(NULL,"|");
+
+ if (p[1])
+ p[2]=strtok(NULL,"|");
+
+ f=ATOI(p[0]);
+
+ if ((f>=0)&&(f<=15)&&(p[2]))
+ LinedefFlag(f,p[1],*p[2]);
+ else
+ err="LINEDEF_FLAGS";
+
+ break;
+ }
+
+ case M_INCLUDE:
+ {
+ char s[PATH_MAX];
+
+ strcpy(s,line);
+ DoLoadConfig(s);
+ break;
+ }
+
+ case M_THING_FLAGS:
+ {
+ char *p[3];
+ int f;
+
+ p[0]=strtok(line,"|");
+
+ if (p[0])
+ p[1]=strtok(NULL,"|");
+
+ if (p[1])
+ p[2]=strtok(NULL,"|");
+
+ f=ATOI(p[0]);
+
+ if ((f>=0)&&(f<=15)&&(p[2]))
+ ThingFlag(f,p[1],p[2]);
+ else
+ err="THING_FLAGS";
+
+ 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_NONE:
+ break;
+ }
+
+ if (err)
+ GFX_exit
+ (EXIT_FAILURE,
+ "%s: section %s\nError in line '%s'\n",fn,err,orig);
+ }
+
+ 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));
+ game_name="Doom - Knee Deep in The Dead";
+ break;
+ case ULTIMATE_DOOM:
+ INI_GetTable(ult_doom_ini,INI_TAB_SIZE(ult_doom_ini));
+ game_name="Utlimate Doom - Thy Flesh Consumed";
+ break;
+ case DOOM_2:
+ INI_GetTable(doom_2_ini,INI_TAB_SIZE(doom_2_ini));
+ game_name="Doom 2 - Hell On Earth";
+ break;
+ case FINAL_TNT:
+ INI_GetTable(tnt_ini,INI_TAB_SIZE(tnt_ini));
+ game_name="Final Doom - TNT:Evilution";
+ break;
+ case FINAL_PLUTONIA:
+ INI_GetTable(plut_ini,INI_TAB_SIZE(plut_ini));
+ game_name="Final Doom - The Plutonia Experiment";
+ break;
+ case ZDOOM:
+ INI_GetTable(zdoom_ini,INI_TAB_SIZE(zdoom_ini));
+ game_name="ZDoom/BOOM extensions to Doom";
+ 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..8109dd6
--- /dev/null
+++ b/globals.h
@@ -0,0 +1,211 @@
+/*
+
+ 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 _GLOBALS_H
+
+#define _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;
+extern char *game_name;
+
+
+/* ------------------------------ 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;
+
+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 linedef_select;
+extern int tag_highlight;
+extern int show_full_linedef_info;
+
+
+/* ------------------------------ viDOOM configuration
+*/
+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;
+
+/* ------------------------------ 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;
+
+
+/* ------------------------------ 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..a7e41dc
--- /dev/null
+++ b/gui.c
@@ -0,0 +1,391 @@
+/*
+
+ 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"
+
+/* ---------------------------------------- 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};
+
+
+/* ---------------------------------------- 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];
+ char *t[MAXLINES];
+ int no,x,y,w,h;
+ int f;
+ GFXEvent ev;
+ va_list va;
+
+ va_start(va,fmt);
+ vsprintf(p,fmt,va);
+ va_end(va);
+
+ CalcBounding(p,&no,t,MAX(strlen(ANY_CLOSE),strlen(title)),&x,&y,&w,&h);
+ h+=GUI_TITLEH+GUI_FOOTERH;
+
+ DrawBox(x,y,w,h,GUI_BEVEL);
+
+ for(f=0;f<no;f++)
+ DrawText((SCRW-strlen(t[f])*fw)/2,y+GUI_BORDER+GUI_TITLEH+fh*f,t[f]);
+
+ DrawTitle(x,y,w,title);
+
+ GFX_print((SCRW-strlen(ANY_CLOSE)*fw)/2,y+h-GUI_FOOTERH+GUI_FOOTEROFF-1,
+ GUI_TEXTBOLD,"%s",ANY_CLOSE);
+
+ GFX_redraw();
+ GFX_bounce();
+ GFX_await_input(&ev);
+ GFX_bounce();
+}
+
+
+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];
+
+ switch(level_style)
+ {
+ case DOOM_LEVELS:
+ f=GUI_picklist(prompt,doom_levels);
+
+ if (f<0)
+ return(NULL);
+
+ strcpy(s,doom_levels[f]);
+ break;
+
+ case ULTIMATE_DOOM_LEVELS:
+ f=GUI_picklist(prompt,ult_doom_levels);
+
+ if (f<0)
+ return(NULL);
+
+ strcpy(s,ult_doom_levels[f]);
+ break;
+
+ case DOOM_2_LEVELS:
+ f=GUI_picklist(prompt,doom2_levels);
+
+ if (f<0)
+ return(NULL);
+
+ strcpy(s,doom2_levels[f]);
+ break;
+ }
+
+ return(s);
+}
+
+
+void ViewFile(char *title, char *file)
+{
+ FILE *fp;
+ int no;
+ char s[VIEW_LEN];
+ char **line;
+ int f;
+
+ if ((fp=fopen(file,"r")))
+ {
+ no=0;
+ fgets(s,VIEW_LEN,fp);
+
+ while(!feof(fp))
+ {
+ no++;
+ fgets(s,VIEW_LEN,fp);
+ }
+
+ no++;
+
+ fclose(fp);
+
+ line=Grab(sizeof(char *)*(no+1));
+
+ f=0;
+ fp=fopen(file,"r");
+
+ fgets(s,VIEW_LEN,fp);
+
+ while(!feof(fp))
+ {
+ line[f++]=Strdup(s);
+ fgets(s,VIEW_LEN,fp);
+ }
+
+ line[f++]=Strdup("<END OF FILE>");
+ line[f]=NULL;
+
+ GUI_picklist(title,line);
+
+ for(f=0;f<no;f++)
+ Release(line[f]);
+
+ Release(line);
+ }
+ else
+ GuiInfoBox("ERROR","ViewFile couldn't open:%s",file);
+}
+
+
+
+/* END OF FILE */
diff --git a/gui.h b/gui.h
new file mode 100644
index 0000000..87dbc1e
--- /dev/null
+++ b/gui.h
@@ -0,0 +1,88 @@
+/*
+
+ 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 _GUI_H
+#define _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);
+
+/* View a text file
+*/
+void ViewFile(char *title, char *file);
+
+
+#endif
diff --git a/ini.c b/ini.c
new file mode 100644
index 0000000..c4b37a1
--- /dev/null
+++ b/ini.c
@@ -0,0 +1,526 @@
+/*
+
+ 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"
+
+#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);
+}
+
+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..9ac4f0e
--- /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 _INI_H
+
+#define _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..bfeb6bc
--- /dev/null
+++ b/linedefs.c
@@ -0,0 +1,371 @@
+/*
+
+ 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 "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_flags[17]=
+ {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
+
+static char line_flags_sh[16]=
+ {'?','?','?','?','?','?','?','?',
+ '?','?','?','?','?','?','?','?'};
+
+static int line_flags_max=-1;
+
+
+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 LinedefFlag(int bit,char *s,char sh)
+{
+ line_flags[bit]=Strdup(s);
+ line_flags_sh[bit]=sh;
+
+ if (bit>line_flags_max)
+ line_flags_max=bit;
+}
+
+
+char **LinedefFlagArray(void)
+{
+ return(line_flags);
+}
+
+
+char *LinedefFlagText(int flags)
+{
+ static char s[32];
+ int b;
+ char *p;
+
+ s[0]=0;
+ p=s;
+
+ for(b=line_flags_max;b>=0;b--)
+ {
+ if (flags&(1<<b))
+ *p++=line_flags_sh[b];
+ else
+ *p++='-';
+
+ *p=0;
+ }
+
+ return(s);
+}
+
+
+void AddLinedefType(char *name,int flags,int two_sided)
+{
+ LineDefault l;
+
+ if (!line_def)
+ line_def=MapNew(sizeof(LineDefault));
+
+ l.name=Grab(strlen(name)+20);
+ strcpy(l.name,name);
+ strcat(l.name," [");
+ strcat(l.name,LinedefFlagText(flags));
+ strcat(l.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..9a0b044
--- /dev/null
+++ b/linedefs.h
@@ -0,0 +1,100 @@
+/*
+
+ 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 _LINEDEFS_H
+
+#define _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);
+
+
+/* Set the meaning of one of the bits in the options field for a linedef
+*/
+void LinedefFlag(int bit,char *name,char shorthand);
+
+
+/* Returns a GUI_multi_box() compatible text array for manipulating a LINEDEFs
+ flags.
+*/
+char **LinedefFlagArray(void);
+
+
+/* Return a static string representing the meaning of the passed in flags
+*/
+char *LinedefFlagText(int flag);
+
+
+/* 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/list.c b/list.c
new file mode 100644
index 0000000..ad23342
--- /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 _Elem
+ {
+ char *data;
+ struct _Elem *next;
+ struct _Elem *prev;
+ };
+
+struct _List
+ {
+ int no;
+ int size;
+ struct _Elem *head;
+ struct _Elem *tail;
+ };
+
+struct _Iterator
+ {
+ List l;
+ struct _Elem *e;
+ };
+
+/* ----------------------------------------
+*/
+List ListNew(int type_size)
+{
+ List nl;
+
+ nl=Grab(sizeof(struct _List));
+
+ nl->no=0;
+ nl->size=type_size;
+ nl->head=NULL;
+ nl->tail=NULL;
+
+ return(nl);
+}
+
+List ListClear(List l)
+{
+ struct _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 _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 _Iterator *i;
+
+ if (!l->head)
+ return(NULL);
+
+ i=Grab(sizeof(struct _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 _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 _Elem *e;
+
+ e=Grab(sizeof(struct _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 _Elem *e;
+
+ e=Grab(sizeof(struct _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 _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..929df83
--- /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 _LIST_H
+
+#define _LIST_H
+
+/* Opaque types for the list and iterator. NULL is the null list and iterator.
+*/
+struct _List;
+typedef struct _List *List;
+
+struct _Iterator;
+typedef struct _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/djgpp.cfg b/make/djgpp.cfg
new file mode 100644
index 0000000..e682b61
--- /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.6 2000/07/28 12:18:35 dosuser Exp dosuser $
+#
+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
+
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..036a787
--- /dev/null
+++ b/makefile
@@ -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
+#
+# -------------------------------------------------------------------------
+#
+
+#
+# START OF CONFIGURATION PART
+#
+# 1. The platform to compile for. Currently supported ones are:
+# djgpp
+#
+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)DEBUG
+
+
+CFLAGS= $(EXTRACF) $(DEFINEFLAG)TRACEFORM='$(TRACEFORM)' $(DEBUG) \
+ $(DEFINEFLAG)PLATFORM=$(PLATFORM) \
+ $(DEFINEFLAG)DIRSEP='$(DIRSEP)' $(INCFLAG).
+
+VIDOOM= vidoom
+WADDIR= waddir
+GFXTEST= gfxtest
+
+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) \
+ 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)
+
+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
+
+VIDOOM_OBJ= vidoom$(OBJ_EXT) $(COMMON_OBJ)
+WADDIR_OBJ= waddir$(OBJ_EXT) $(COMMON_OBJ)
+GFXTEST_OBJ= gfxtest$(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)
+
+all: $(VIDOOM)$(EXE_EXT) $(WADDIR)$(EXE_EXT) $(GFXTEST)$(EXE_EXT)
+
+
+$(PLATFORM)$(MKDS)main$(OBJ_EXT): $(PLATFORM)$(MKDS)main.c $(ALL_HEADERS)
+ cd $(PLATFORM); $(CC) $(CFLAGS) $(OBJFLAG) $(INCFLAG).. main.c ; cd ..
+
+$(PLATFORM)$(MKDS)file$(OBJ_EXT): $(PLATFORM)$(MKDS)file.c $(ALL_HEADERS)
+ cd $(PLATFORM); $(CC) $(CFLAGS) $(OBJFLAG) $(INCFLAG).. file.c ; cd ..
+
+$(PLATFORM)$(MKDS)gfx$(OBJ_EXT): $(PLATFORM)$(MKDS)gfx.c $(ALL_HEADERS)
+ cd $(PLATFORM); $(CC) $(CFLAGS) $(OBJFLAG) $(INCFLAG).. gfx.c ; cd ..
+
+$(PLATFORM)$(MKDS)mem$(OBJ_EXT): $(PLATFORM)$(MKDS)mem.c $(ALL_HEADERS)
+ cd $(PLATFORM); $(CC) $(CFLAGS) $(OBJFLAG) $(INCFLAG).. mem.c ; cd ..
+
+$(PLATFORM)$(MKDS)platgui$(OBJ_EXT): $(PLATFORM)$(MKDS)platgui.c $(ALL_HEADERS)
+ cd $(PLATFORM); $(CC) $(CFLAGS) $(OBJFLAG) $(INCFLAG).. platgui.c ; cd ..
+
+$(PLATFORM)$(MKDS)runcmd$(OBJ_EXT): $(PLATFORM)$(MKDS)runcmd.c $(ALL_HEADERS)
+ cd $(PLATFORM); $(CC) $(CFLAGS) $(OBJFLAG) $(INCFLAG).. runcmd.c ; cd ..
+
+$(PLATFORM)$(MKDS)vstring$(OBJ_EXT): $(PLATFORM)$(MKDS)vstring.c $(ALL_HEADERS)
+ cd $(PLATFORM); $(CC) $(CFLAGS) $(OBJFLAG) $(INCFLAG).. vstring.c ; cd ..
+
+
+debug$(OBJ_EXT): debug.c config.h debug.h
+ $(CC) $(CFLAGS) $(OBJFLAG) $<
+
+edit$(OBJ_EXT): edit.c config.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) $<
+
+editcord$(OBJ_EXT): editcord.c config.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) $<
+
+editdraw$(OBJ_EXT): editdraw.c config.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 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 globals.h editvar.h things.h gfx.h \
+ platgui.h linedefs.h wad.h map.h list.h gui.h mem.h texture.h
+ $(CC) $(CFLAGS) $(OBJFLAG) $<
+
+editline$(OBJ_EXT): editline.c config.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) $<
+
+editsect$(OBJ_EXT): editsect.c config.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) $<
+
+editsel$(OBJ_EXT): editsel.c config.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 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) $<
+
+editcrse$(OBJ_EXT): editcrse.c config.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 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) $<
+
+editvar$(OBJ_EXT): editvar.c config.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 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) $<
+
+editmrg$(OBJ_EXT): editmrg.c config.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 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) $<
+
+editilst$(OBJ_EXT): editilst.c config.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) $<
+
+gfxtest$(OBJ_EXT): gfxtest.c config.h globals.h gfx.h
+ $(CC) $(CFLAGS) $(OBJFLAG) $<
+
+globals$(OBJ_EXT): globals.c config.h globals.h ini.h gfx.h texture.h \
+ platgui.h things.h linedefs.h sectors.h wad.h map.h list.h
+ $(CC) $(CFLAGS) $(OBJFLAG) $<
+
+gui$(OBJ_EXT): gui.c config.h globals.h gui.h gfx.h platgui.h mem.h
+ $(CC) $(CFLAGS) $(OBJFLAG) $<
+
+ini$(OBJ_EXT): ini.c config.h ini.h mem.h file.h
+ $(CC) $(CFLAGS) $(OBJFLAG) $<
+
+linedefs$(OBJ_EXT): linedefs.c config.h globals.h linedefs.h wad.h map.h \
+ list.h gfx.h platgui.h mem.h
+ $(CC) $(CFLAGS) $(OBJFLAG) $<
+
+sectors$(OBJ_EXT): sectors.c config.h globals.h sectors.h wad.h map.h list.h \
+ gfx.h platgui.h mem.h
+ $(CC) $(CFLAGS) $(OBJFLAG) $<
+
+list$(OBJ_EXT): list.c config.h list.h mem.h
+ $(CC) $(CFLAGS) $(OBJFLAG) $<
+
+map$(OBJ_EXT): map.c config.h map.h mem.h
+ $(CC) $(CFLAGS) $(OBJFLAG) $<
+
+texture$(OBJ_EXT): texture.c config.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 globals.h things.h gfx.h platgui.h mem.h \
+ list.h
+ $(CC) $(CFLAGS) $(OBJFLAG) $<
+
+wad$(OBJ_EXT): wad.c config.h globals.h wad.h map.h list.h gfx.h mem.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
+
+
+# Rule for installation
+#
+install: $(VIDOOM)$(EXE_EXT) FORCE
+ cd $(PLATFORM) ; $(MAKEINSTALL)
+
+FORCE:
+
+
+#
+# $Id: makefile,v 1.28 2000/07/28 15:30:24 dosuser Exp dosuser $
+#
+# END OF FILE
diff --git a/map.c b/map.c
new file mode 100644
index 0000000..95421a8
--- /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 _Map
+ {
+ int top;
+ int alloc;
+ int size;
+ char *data;
+ };
+
+/* ----------------------------------------
+*/
+Map MapNew(int type_size)
+{
+ Map nm;
+
+ nm=Grab(sizeof(struct _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 _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..9878fe4
--- /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 _MAP_H
+
+#define _MAP_H
+
+/* Opaque types for the map. NULL is the null map.
+*/
+struct _Map;
+typedef struct _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..11fdfb7
--- /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 _MEM_H
+
+#define _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/platgui.h b/platgui.h
new file mode 100644
index 0000000..0719ef0
--- /dev/null
+++ b/platgui.h
@@ -0,0 +1,199 @@
+/*
+
+ 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 _PLATGUI_H
+#define _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
+ {
+ char *text; /* Text to display */
+ int client_index; /* Value returned by GUI_menu() */
+ } 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 simple dialog
+*/
+#define PLAT_DIAL_MAXSTRLEN 128
+
+#define PLAT_DIAL_STRING 0
+#define PLAT_DIAL_INTEGER 1
+#define PLAT_DIAL_DOUBLE 2
+
+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;
+ } 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.
+*/
+int GUI_yesno(char *question);
+
+
+/* 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 char pointers passed in, which terminates with a NULL pointer.
+
+ The passed in integer pointer points to a value which is used to enable/
+ disable the options dependent on the integers bit setting. The integer
+ pointed to is updated on exit of the multi-selection box is not cancelled.
+
+ Note that the bit patterns are matched bit number to opt index. ie.
+ val & 0x0001 corresponds to opts[0]
+ val & 0x0002 corresponds to opts[1]
+ ....
+ val & 0x0010 corresponds to opts[4]
+ ....
+ val & 0x0100 corresponds to opts[8]
+ ....
+ val & 0x8000 corresponds to opts[15]
+
+ A maximum of 16 bits should be all that needs supporting currently.
+
+ Returns TRUE if dialog accepted, otherwise FALSE.
+*/
+int GUI_multi_box(char *title,char *opts[],int *val);
+
+
+/* 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[]);
+
+
+#endif
+
+
+/* END OF FILE */
diff --git a/runcmd.h b/runcmd.h
new file mode 100644
index 0000000..e1ba406
--- /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 _RUNCMD_H
+
+#define _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..0c6ce40
--- /dev/null
+++ b/sectors.c
@@ -0,0 +1,318 @@
+/*
+
+ 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=0;
+
+static Map sect_class=NULL;
+static List sect_list=NULL;
+static Map sect_style=NULL;
+
+static PLAT_MENU *menu=NULL;
+static PLAT_PICKLIST **picklist=NULL;
+
+static char **sect_style_plist=NULL;
+
+
+/* ---------------------------------------- PRIVATE FUNCTIONS
+*/
+static PLAT_PICKLIST *SectorPicklist(int class)
+{
+ SectorInfo *si;
+ Iterator i;
+ int no;
+ int f;
+
+ if (picklist[class])
+ return(picklist[class]);
+
+ no=0;
+ i=ListIterator(sect_list);
+
+ while(i)
+ {
+ si=IteratorData(i);
+
+ if (si->class==class)
+ no++;
+
+ i=IteratorNext(i);
+ }
+
+ picklist[class]=Grab(sizeof(PLAT_PICKLIST)*(no+1));
+
+ i=ListIterator(sect_list);
+
+ f=0;
+ while(i)
+ {
+ si=IteratorData(i);
+
+ if (si->class==class)
+ {
+ picklist[class][f].text=si->long_name;
+ picklist[class][f].client_index=si->id;
+ f++;
+ }
+
+ i=IteratorNext(i);
+ }
+
+ picklist[class][f].text=NULL;
+
+ return(picklist[class]);
+}
+
+
+/* ---------------------------------------- EXPORTED FUNCTIONS
+*/
+void SectorNewClass(char *class)
+{
+ SectorClass c;
+
+ if (!sect_class)
+ sect_class=MapNew(sizeof(SectorClass));
+
+ c.name=Strdup(class);
+
+ MapAdd(sect_class,no_classes++,&c);
+}
+
+
+void SectorAdd(char *class,int id,char *long_name,char *short_name)
+{
+ SectorInfo *s;
+ int i_class;
+ int f;
+
+ if (!sect_list)
+ sect_list=ListNew(sizeof(SectorInfo));
+
+ s=Grab(sizeof(SectorInfo));
+
+ i_class=-1;
+
+ for(f=0;f<MapSize(sect_class);f++)
+ {
+ SectorClass *c;
+
+ c=MapElem(sect_class,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,s);
+}
+
+
+int SelectSector(void)
+{
+ int class;
+ int f;
+ int x,y;
+
+ if (!menu)
+ {
+ menu=Grab(sizeof(PLAT_MENU)*(no_classes+1));
+ picklist=Grab(sizeof(PLAT_IMG_PICKLIST *)*(no_classes+1));
+
+ for(f=0;f<no_classes;f++)
+ {
+ SectorClass *c;
+
+ c=MapElem(sect_class,f);
+ menu[f].text=c->name;
+ menu[f].client_index=f;
+ }
+
+ for(f=0;f<no_classes+1;f++)
+ picklist[f]=NULL;
+
+ menu[no_classes].text=NULL;
+ }
+
+ GFX_mouse(&x,&y);
+
+ if (no_classes<2)
+ class=0;
+ else
+ class=GUI_menu("Pick class of SECTOR",x,y,menu,SECTOR_NULLID);
+
+ if (class!=SECTOR_NULLID)
+ class=GUI_client_picklist("SECTOR",SectorPicklist(class),SECTOR_NULLID);
+
+ return(class);
+}
+
+
+char *SectorName(int id)
+{
+ SectorInfo *si;
+ Iterator i;
+
+ i=ListIterator(sect_list);
+
+ while(i)
+ {
+ si=IteratorData(i);
+
+ if (si->id==id)
+ {
+ IteratorClear(i);
+ return(si->short_name);
+ }
+
+ i=IteratorNext(i);
+ }
+
+ return("UNKNOWN");
+}
+
+
+void StartSectorStyles(void)
+{
+ SectorStyle *s;
+ int f;
+
+ if (!sect_style)
+ {
+ for(f=0;f<MapSize(sect_style);f++)
+ {
+ s=MapElem(sect_style,f);
+ Release(s->name);
+ }
+
+ sect_style=MapNew(sizeof(SectorStyle));
+ }
+ else
+ MapEmpty(sect_style);
+}
+
+
+void AddSectorStyle(char *name, int flags, DirName upper, DirName middle,
+ DirName lower, DirName floor, DirName ceiling)
+{
+ SectorStyle s;
+
+ 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..b59ec63
--- /dev/null
+++ b/sectors.h
@@ -0,0 +1,96 @@
+/*
+
+ 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 _SECTORS_H
+
+#define _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
+*/
+void SectorNewClass(char *class);
+
+
+/* Add the named type to the supplied class with the ID
+*/
+void SectorAdd(char *class,int id,char *long_name, char *short_name);
+
+
+/* Select the ID type for a sector. Returns SECTOR_NULLID if cancelled
+*/
+int SelectSector(void);
+
+
+/* Returns the short name of a sector type
+*/
+char *SectorName(int id);
+
+
+/* Start defining sector styles.
+*/
+void StartSectorStyles(void);
+
+/* 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/texture.c b/texture.c
new file mode 100644
index 0000000..eb29d01
--- /dev/null
+++ b/texture.c
@@ -0,0 +1,594 @@
+/*
+
+ 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 *GetShort(Byte *p,Short *s)
+{
+ *s=(Short)(*p++);
+ *s|=((Short)(*p++))<<8;
+ return(p);
+}
+
+
+static Byte *GetLong(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=GetShort(lump,&lw);
+ lump=GetShort(lump,&lh);
+ lump=GetShort(lump,&lox);
+ lump=GetShort(lump,&loy);
+
+ lox=MAX(lox,0);
+ loy=MAX(loy,0);
+
+ for(x=0;x<lw;x++)
+ {
+ lump=GetLong(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=GetShort(t_ent,&w);
+ bm->w=MIN(w,MAX_GFXBM_W);
+
+ t_ent=GetShort(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=GetShort(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=GetShort(t_ent,&xo);
+ t_ent=GetShort(t_ent,&yo);
+ t_ent=GetShort(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=GetLong(texture,&no);
+
+ /* Loop through all the textures and create the picklist entry
+ */
+ for(f=0;f<no;f++)
+ {
+ texture=GetLong(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");
+
+ if ((game==DOOM)||(game==ULTIMATE_DOOM))
+ if (!(t2=GetLump("TEXTURE2",NULL)))
+ GFX_exit(EXIT_FAILURE,
+ "There MUST be a TEXTURE2 lump in the WADs!!\n");
+
+ 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.
+ */
+ GetLong(t1,&t1_size);
+
+ if (t2)
+ GetLong(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);
+ 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=GetShort(obj,&w);
+ obj=GetShort(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..45959e3
--- /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 _TEXTURE_H
+
+#define _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..dc39546
--- /dev/null
+++ b/things.c
@@ -0,0 +1,331 @@
+/*
+
+ 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;
+
+static char *thing_flags[17]=
+ {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
+
+static char *thing_flags_sh[17]=
+ {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,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");
+}
+
+
+void ThingFlag(int bit,char *s,char *sh)
+{
+ thing_flags[bit]=Strdup(s);
+ thing_flags_sh[bit]=Strdup(sh);
+}
+
+
+char **ThingFlagArray(void)
+{
+ return(thing_flags);
+}
+
+
+char *ThingFlagText(int flags)
+{
+ static char s[512];
+ int b;
+
+ s[0]=0;
+
+ for(b=0;b<16;b++)
+ if (flags&(1<<b))
+ if (thing_flags_sh[b])
+ if (!s[0])
+ strcpy(s,thing_flags_sh[b]);
+ else
+ {
+ strcat(s,",");
+ strcat(s,thing_flags_sh[b]);
+ }
+ return(s);
+}
+
+
+
+/* END OF FILE */
diff --git a/things.h b/things.h
new file mode 100644
index 0000000..512de19
--- /dev/null
+++ b/things.h
@@ -0,0 +1,88 @@
+/*
+
+ 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 _THINGS_H
+
+#define _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);
+
+
+/* Set the meaning of one of the bits in the options field for a thing
+*/
+void ThingFlag(int bit,char *name,char *shorthand);
+
+
+/* Returns a GUI_multi_box() compatible text array for manipulating a THINGs
+ flags.
+*/
+char **ThingFlagArray(void);
+
+
+/* Return a static string representing the meaning of the passed in flags
+*/
+char *ThingFlagText(int flag);
+
+
+#endif
+
+
+/* END OF FILE */
diff --git a/vidoom.c b/vidoom.c
new file mode 100644
index 0000000..22a12e0
--- /dev/null
+++ b/vidoom.c
@@ -0,0 +1,723 @@
+/*
+
+ 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"
+
+/* ---------------------------------------- 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);
+
+
+/* ---------------------------------------- MAIN
+*/
+int viDOOM(int argc,char *argv[])
+{
+ char *load,*p;
+ int f;
+
+ GFX_init();
+ GFX_clear_XOR_mode();
+
+ 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;
+
+
+ /* 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();
+ 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.");
+}
+
+
+/* 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 -666
+
+ 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},
+ {"List entries in WAD directory", MENU_LISTDIR},
+ {"About viDOOM " VIDOOMVER, MENU_ABOUT},
+#ifdef DEBUG
+ {"DEBUG", MENU_DEBUG},
+#endif
+ {"Quit", MENU_QUIT},
+ {NULL,0}
+ };
+
+ int quit;
+ int editted;
+
+ quit=FALSE;
+ editted=FALSE;
+
+ while(!quit)
+ {
+ char *open;
+
+ GFX_clear(BLACK);
+
+ if (doom_image)
+ GFX_fill_screen(doom_image);
+
+ GuiDrawInfoBox(game_name,GUI_FLUSH_RIGHT,GUI_FLUSH_TOP,FALSE,
+ "Current map : %s| |Open WAD files :|%s",
+ 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);
+ editted=FALSE;
+ }
+
+ if (new)
+ {
+ strcpy(mapname,name);
+ wad=NewMap();
+ EditLoad(wad);
+ editted=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);
+ editted=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);
+ editted=FALSE;
+ }
+ }
+ }
+
+ break;
+ }
+
+ case MENU_EDIT:
+ if (!wad)
+ GuiInfoBox("ERROR","No map currently being editted");
+ else
+ {
+ EditLoop();
+ editted=TRUE;
+ }
+ break;
+
+ case MENU_SAVE:
+ {
+ char *wadf;
+
+ if (!wad)
+ GuiInfoBox("ERROR","No map currently being editted");
+ else
+ if ((wadf=GUI_fsel
+ ("Select WAD to save map as",PWAD_dir,".WAD")))
+ {
+ int ok;
+
+ 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;
+
+ editted=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]))
+ ViewFile("Node Builder",path);
+ }
+ else
+ if (path[0])
+ ViewFile("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 editted");
+ 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[1024];
+ WadDir *wd;
+ Iterator i;
+ char **list;
+ int f;
+
+ list=Grab(sizeof(char *)*(GetWadDirSize()+1));
+ 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));
+ list[f++]=Strdup(s);
+ }
+
+ list[f]=NULL;
+
+ GUI_picklist("WAD DIR (Name, Offset, Size, WAD file)",list);
+
+ f=0;
+ while(list[f])
+ Release(list[f++]);
+
+ Release(list);
+
+ 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_QUIT:
+ if ((map_exit_warn)&&(editted))
+ quit=GUI_yesno("Map may have been changed "
+ "- really quit?");
+ else
+ quit=TRUE;
+ break;
+
+#ifdef 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..2ba884b
--- /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_H
+
+#define _VIDOOM_H
+
+/* Main viDOOM entry point
+*/
+int viDOOM(int argc, char *argv[]);
+
+#endif
+
+/* END OF FILE */
+
diff --git a/vstring.h b/vstring.h
new file mode 100644
index 0000000..f2f7b04
--- /dev/null
+++ b/vstring.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
+
+ -------------------------------------------------------------------------
+
+ Provides portable versions of necessary string routines
+
+ $Id$
+
+
+*/
+
+#ifndef _VSTRING_H
+
+#define _VSTRING_H
+
+/* Does exactly the same as strcmp(), but ignores case.
+ The passed in strings should not be altered.
+*/
+int StrCaseCmp(char *a, char *b);
+
+/* Does exactly the same as strncmp(), but ignores case.
+ The passed in strings should not be altered.
+*/
+int StrNCaseCmp(char *a, char *b, int n);
+
+#endif
diff --git a/wad.c b/wad.c
new file mode 100644
index 0000000..ca7f28a
--- /dev/null
+++ b/wad.c
@@ -0,0 +1,806 @@
+/*
+
+ 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"
+
+/* ---------------------------------------- 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 IGNORE_LUMP 0
+#define THING_LUMP 1
+#define VERTEX_LUMP 2
+#define LINEDEF_LUMP 3
+#define SIDEDEF_LUMP 4
+#define SECTOR_LUMP 5
+
+#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",
+ __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 Word GetShort(FILE *fp)
+{
+ return ((Short)fgetc(fp)|((Short)fgetc(fp))<<8);
+}
+
+
+static Long GetLong(FILE *fp)
+{
+ return ((Long)fgetc(fp)|
+ ((Long)fgetc(fp))<<8|
+ ((Long)fgetc(fp))<<16|
+ ((Long)fgetc(fp))<<24);
+}
+
+
+static char *GetName(FILE *fp)
+{
+ static DirName d;
+
+ fread(d,1,8,fp);
+ d[sizeof(d)-1]=0;
+ return(d);
+}
+
+
+static void PutShort(FILE *fp,Short s)
+{
+ fputc(s&0xff,fp);
+ fputc((s>>8)&0xff,fp);
+}
+
+
+static 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);
+}
+
+
+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 IsMapLump(char *n)
+{
+ return(STREQ("THINGS",n)||
+ STREQ("LINEDEFS",n)||
+ STREQ("SIDEDEFS",n)||
+ STREQ("VERTEXES",n)||
+ STREQ("SEGS",n)||
+ STREQ("SSECTORS",n)||
+ STREQ("NODES",n)||
+ STREQ("SECTORS",n)||
+ STREQ("REJECT",n)||
+ STREQ("BLOCKMAP",n));
+}
+
+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},
+ {NULL,IGNORE_LUMP},
+ };
+
+ int f;
+
+ f=0;
+ while(lump[f].name)
+ {
+ if (STREQ(lump[f].name,n))
+ return(lump[f].lump);
+
+ f++;
+ }
+
+ return(IGNORE_LUMP);
+}
+
+
+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(ret,1,d->size,wf->fp);
+
+ 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(type,1,4,fp);
+
+ if (STRNEQ("IWAD",expect)&&(!STRNEQ("IWAD",type)))
+ ERR(WAD_NOT_IWAD,NULL);
+ else if (STRNEQ("PWAD",expect)&&(!STRNEQ("PWAD",type)))
+ ERR(WAD_NOT_PWAD,NULL);
+
+ 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));
+ }
+
+ 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
+ {
+ wf=Grab(sizeof(WadFile));
+
+ if (!(wf->fp=fopen(wad,"rb")))
+ GFX_exit(EXIT_FAILURE,"BIZARRE: WAD opening failed after "
+ "first open succeeded on\nt%s\n",wad);
+
+ strcpy(wf->path,wad);
+ wadname=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);
+}
+
+
+/* ---------------------------------------- EXPORTED FUNCTIONS
+*/
+
+int AddIWAD(char *wad)
+{
+ return (AddWAD(wad,"IWAD"));
+}
+
+
+int AddPWAD(char *wad)
+{
+ return (AddWAD(wad,"PWAD"));
+}
+
+
+int CloseWad(char *wad)
+{
+ WadDir *w;
+ Iterator i;
+
+ if (!(i=ListFindElem(wadlist,FindWAD,wad)))
+ ERR(WAD_FILE_NOT_FOUND,WAD_FILE_NOT_FOUND);
+
+ i=IteratorDelete(i);
+ IteratorClear(i);
+
+ i=ListIterator(waddir);
+
+ while(i)
+ {
+ w=IteratorData(i);
+
+ if (strcmp(w->wad,wad)==0)
+ i=IteratorDelete(i);
+ else
+ i=IteratorNext(i);
+ }
+
+ 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);
+}
+
+
+WadMap *LoadMap(char *name)
+{
+ Iterator i;
+ WadMap *map;
+ WadDir *dir;
+ WadFile *wf;
+ Thing thing;
+ Vertex vertex;
+ Sidedef sidedef;
+ Linedef linedef;
+ Sector sector;
+ int f;
+
+ 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);
+
+ 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));
+
+ /* Step through the items following the map in the dir to load the
+ lumps for the level in.
+ */
+ while(TRUE)
+ {
+ i=IteratorNext(i);
+ dir=IteratorData(i);
+
+ if (!IsMapLump(dir->name))
+ ERR(WAD_OK,map);
+
+ switch (HandledMapLump(dir->name))
+ {
+ case THING_LUMP:
+ fseek(wf->fp,(long)dir->off,SEEK_SET);
+
+ 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.opt=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);
+
+ 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;
+
+ default:
+ break;
+ }
+ }
+
+ ERR(WAD_OK,map);
+}
+
+
+WadMap *NewMap(void)
+{
+ WadMap *map;
+
+ 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));
+
+ return(map);
+}
+
+
+WadMap *ClearMap(WadMap *map)
+{
+ MapClear(map->thing);
+ MapClear(map->vertex);
+ MapClear(map->sidedef);
+ MapClear(map->linedef);
+ MapClear(map->sector);
+ Release(map);
+ return(NULL);
+}
+
+
+int SaveMap(WadMap *map, char *name, char *wad)
+{
+ FILE *fp;
+ Long off;
+ int f;
+
+ if (!(fp=fopen(wad,"wb")))
+ ERR(WAD_COULD_NOT_CREATE,WAD_COULD_NOT_CREATE);
+
+ off=MapSize(map->vertex)*VERTEX_SIZE+
+ MapSize(map->sidedef)*SIDEDEF_SIZE+
+ MapSize(map->linedef)*LINEDEF_SIZE+
+ MapSize(map->sector)*SECTOR_SIZE+
+ MapSize(map->thing)*THING_SIZE+12;
+
+ /* Put PWAD header
+ */
+ fputs("PWAD",fp);
+ PutLong(fp,10);
+ PutLong(fp,off);
+
+ /* Put THINGS
+ */
+ for(f=0;f<MapSize(map->thing);f++)
+ {
+ Thing *t;
+
+ t=MapElem(map->thing,f);
+ PutShort(fp,t->x);
+ PutShort(fp,t->y);
+ PutShort(fp,t->ang);
+ PutShort(fp,t->type);
+ PutShort(fp,t->opt);
+ }
+
+ /* Put LINEDEFS
+ */
+ for(f=0;f<MapSize(map->linedef);f++)
+ {
+ Linedef *l;
+
+ l=MapElem(map->linedef,f);
+ 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
+ */
+
+ /* Create directory
+ */
+ off=12;
+
+ PutDirEnt(fp,off,0,name);
+
+ PutDirEnt(fp,off,MapSize(map->thing)*THING_SIZE,"THINGS");
+ off+=MapSize(map->thing)*THING_SIZE;
+
+ 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");
+
+ fclose(fp);
+
+ ERR(WAD_OK,WAD_OK);
+}
+
+
+int WadError(void)
+{
+ return(wad_err);
+}
+
+
+const char *WadErrorString(void)
+{
+ return(wad_errstr[wad_err]);
+}
+
+/* END OF FILE */
diff --git a/wad.h b/wad.h
new file mode 100644
index 0000000..7d9a9f5
--- /dev/null
+++ b/wad.h
@@ -0,0 +1,242 @@
+/*
+
+ 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 _WAD_H
+
+#define _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)
+
+typedef struct Thing
+ {
+ Short x;
+ Short y;
+ Short ang;
+ Short type;
+ Short opt;
+ } 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)
+
+typedef struct
+ {
+ Short from;
+ Short to;
+ Short flags;
+ Short type;
+ Short tag;
+ Short right;
+ Short left;
+ } 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
+ {
+ Map thing;
+ Map vertex;
+ Map linedef;
+ Map sidedef;
+ Map sector;
+ } 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_BROKEN 8 /* Must be last error code */
+
+#define WAD_NOERROR (WAD_BROKEN+1)
+
+
+/* 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);
+
+
+/* Load a MAPxx or ExMy map from the open wads. Returns NULL on error.
+*/
+WadMap *LoadMap(char *name);
+
+
+/* Create a new empty map
+*/
+WadMap *NewMap(void);
+
+
+/* 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);
+
+
+#endif
+
+/* END OF FILE */
diff --git a/waddir.c b/waddir.c
new file mode 100644
index 0000000..d7bf4ae
--- /dev/null
+++ b/waddir.c
@@ -0,0 +1,75 @@
+/*
+
+ 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]);
+
+ LoadGlobals();
+
+ 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%.8x 0x%.8x %s\n",
+ w->name,w->off,w->size,Basename(w->wad));
+
+ i=IteratorNext(i);
+ }
+
+ return(0);
+}
+
+
+/* END OF FILE */