/*----------------------------------------------------------------------------*
 | This file is part of DEU (Doom Editing Utilities), created by the DEU team:|
 | Raphael Quinet, Brendon Wyber, Ted Vessenes and others.  See README.1ST or |
 | the "about" dialog box for full credits.                                   |
 |                                                                            |
 | DEU is an open project: if you think that you can contribute, please join  |
 | the DEU team.  You will be credited for any code (or ideas) included in    |
 | the next version of the program.                                           |
 |                                                                            |
 | If you want to make any modifications and re-distribute them on your own,  |
 | you must follow the conditions of the DEU license.  Read the file LICENSE  |
 | in this directory or README.1ST in the top directory.  If do not have a    |
 | copy of these files, you can request them from any member of the DEU team, |
 | or by mail: Raphael Quinet, Rue des Martyrs 9, B-4550 Nandrin (Belgium).   |
 |                                                                            |
 | This program comes with absolutely no warranty.  Use it at your own risks! |
 *----------------------------------------------------------------------------*

 M_EDMENU.C - Menu bar for the map editor

*/

/* the includes */
#include "deu.h"
#include <math.h>
#include "d_main.h"
#include "d_misc.h"
#include "d_wads.h"
#include "d_config.h"
#include "g_gfx.h"
#include "g_mouse.h"
#include "w_levels.h"
#include "w_names.h"
#include "w_things.h"
#include "w_object.h"
#include "w_select.h"
#include "i_dialog.h"
#include "i_menus.h"
#include "i_textur.h"
#include "m_object.h"
#include "m_checks.h"
/* #include "m_info.h" */
#include "m_align.h"
#include "m_edmenu.h"


/*
   Check the level consistency.
*/

void CheckLevel(LevelPtr level, Int16 x0, Int16 y0)
{
  char *lastline;

  if ((DoomVersion & 15) != 0)
    lastline = "Check texture names";
  else
    lastline = NULL;
  switch (DisplayMenu(x0, y0, ((x0 == -1) ? "Check level consistency" : NULL),
                      "Statistics",
                      "Run all checks",
                      "Check all cross-references",
                      "Check if all Sectors are closed",
                      "Check for missing textures",
                      "Check Things and tags",
                      lastline,
                      NULL))
    {
    case 1:
      Statistics(level, -1, -1);
      break;
    case 2:
      CheckEverything(level);
      break;
    case 3:
      CheckCrossReferences(level);
      break;
    case 4:
      CheckSectors(level);
      break;
    case 5:
      CheckTextures(level);
      break;
    case 6:
      CheckThings(level);
      break;
    case 7:
      CheckTextureNames(level);
      break;
   }
}


/*
   Insert the vertices of a new polygon.
*/
/*! see if the angle could be changed easily to have horiz. and vert. sides */
static void InsertPolygonVertices(LevelPtr level, Int16 centerx, Int16 centery, Int16 sides, Int16 radius)
{
  Int16 n;

  for (n = 0; n < sides; n++)
    InsertObject(level, OBJ_VERTEXES, -1,
                 centerx + (Int16) ((double) radius *
                                    cos(6.2832 * (double)n / (double)sides)),
                 centery + (Int16) ((double) radius *
                                    sin(6.2832 * (double)n / (double)sides)));
}


/*
   Yuck!  Dirty piece of code... -Raphael
   No longer!                    -Ted
*/

Bool Input2Numbers(Int16 x0, Int16 y0, char *name1, char *name2,
                   Int16 v1max, Int16 v2max, Int16 *v1, Int16 *v2)
{
  char prompt[80];
  char range1[11], range2[11];

  sprintf(prompt, "Give the %s and %s for the object:", name1, name2);
  sprintf(range1, "(0-%d)", v1max);
  sprintf(range2, "(0-%d)", v2max);

  return(DrawDialogBox(x0, y0, 25 + TEXT_W * strlen(prompt), 100, NULL, 9,
                        DBSC_TEXT,      10,  8,     '\0', prompt, 0, WHITE,
                        DBSC_TEXT,      10, 26, name1[0],  name1, 0, BLACK,
                        DBSC_TEXT,     180, 26, name2[0],  name2, 0, BLACK,
                        DBSC_TEXT,      10, 58,     '\0', range1, 0, BLACK,
                        DBSC_TEXT,     180, 58,     '\0', range2, 0, BLACK,
                        DBSC_INPUTBOX,  10, 38, name1[0], v1, 0, v1max,
                        DBSC_INPUTBOX, 180, 38, name2[0], v2, 0, v2max,
                        DBSC_OK,      10, 75, 'O',
                        DBSC_CANCEL, 180, 75, 'C'));
}



/*
   Insert a standard object at given position.
*/

void InsertStandardObject(LevelPtr level, Int16 x0, Int16 y0, Int16 xpos, Int16 ypos)
{
  char   texside[9], textop[9];
  Int16  sector, n;
  int    choice, stair;
  Int16  l, h, xw, yh, m, cw, ch, ct, radius, increment, angle;
  Int16  a, b, f, c;
  Int16  x1, y1, x2, y2;
  double astep, astart;

  /* show where the object will be inserted */
  if (UseMouse)
    HideMousePointer();
  DrawPointer(TRUE);
  if (UseMouse)
    ShowMousePointer();
  /* are we inside a Sector? */
  sector = GetCurObject(level, OBJ_SECTORS, xpos, ypos, xpos, ypos);
  if (sector >= 0)
    choice = DisplayMenu(x0, y0, ((x0 == -1) ? "Insert a pre-defined object (inside a Sector)" : NULL),
                         "Rectangle",
                         "Polygon (N sides)",
                         "Stairs",
                         "Hidden stairs",
                         "Teleport in",
                         "Teleport out",
                         "Crates...",
                         "Extrusions",
                         NULL);
  else
    choice = DisplayMenu(x0, y0, ((x0 == -1) ? "Insert a pre-defined object (outside)" : NULL),
                         "Rectangle",
                         "Polygon (N sides)",
                         "Stairs",
                         "Hidden stairs",
                         NULL);
  /*! Should also check for overlapping objects (sectors, crossing lines) */
  switch (choice)
    {
    case 1:
      a = 256;
      b = 256;
      if (Input2Numbers(-1, -1, "Width", "Height", 2000, 2000, &a, &b))
        {
          if (a < 8)
            a = 8;
          if (b < 8)
            b = 8;
          xpos = xpos - a / 2;
          ypos = ypos - b / 2;
          InsertObject(level, OBJ_VERTEXES, -1, xpos, ypos);
          InsertObject(level, OBJ_VERTEXES, -1, xpos + a, ypos);
          InsertObject(level, OBJ_VERTEXES, -1, xpos + a, ypos + b);
          InsertObject(level, OBJ_VERTEXES, -1, xpos, ypos + b);
          if (sector < 0)
            InsertObject(level, OBJ_SECTORS, -1, 0, 0);
          for (n = 0; n < 4; n++)
            {
              InsertObject(level, OBJ_LINEDEFS, -1, 0, 0);
              level->linedefs[level->num_linedefs - 1].sidedef1 = level->num_sidedefs;
              InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
              if (sector >= 0)
                level->sidedefs[level->num_sidedefs - 1].sector = sector;
            }
          if (sector >= 0)
            {
              level->linedefs[level->num_linedefs - 4].start = level->num_vertexes - 4;
              level->linedefs[level->num_linedefs - 4].end = level->num_vertexes - 3;
              level->linedefs[level->num_linedefs - 3].start = level->num_vertexes - 3;
              level->linedefs[level->num_linedefs - 3].end = level->num_vertexes - 2;
              level->linedefs[level->num_linedefs - 2].start = level->num_vertexes - 2;
              level->linedefs[level->num_linedefs - 2].end = level->num_vertexes - 1;
              level->linedefs[level->num_linedefs - 1].start = level->num_vertexes - 1;
              level->linedefs[level->num_linedefs - 1].end = level->num_vertexes - 4;
            }
          else
            {
              level->linedefs[level->num_linedefs - 4].start = level->num_vertexes - 1;
              level->linedefs[level->num_linedefs - 4].end = level->num_vertexes - 2;
              level->linedefs[level->num_linedefs - 3].start = level->num_vertexes - 2;
              level->linedefs[level->num_linedefs - 3].end = level->num_vertexes - 3;
              level->linedefs[level->num_linedefs - 2].start = level->num_vertexes - 3;
              level->linedefs[level->num_linedefs - 2].end = level->num_vertexes - 4;
              level->linedefs[level->num_linedefs - 1].start = level->num_vertexes - 4;
              level->linedefs[level->num_linedefs - 1].end = level->num_vertexes - 1;
            }
        }
      break;
    case 2:
      a = 8;
      b = 128;
      if (Input2Numbers(-1, -1, "Number of sides", "Radius", 32, 2000, &a, &b))
        {
          if (a < 3)
            a = 3;
          if (b < 8)
            b = 8;
          InsertPolygonVertices(level, xpos, ypos, a, b);
          if (sector < 0)
            InsertObject(level, OBJ_SECTORS, -1, 0, 0);
          for (n = 0; n < a; n++)
            {
              InsertObject(level, OBJ_LINEDEFS, -1, 0, 0);
              level->linedefs[level->num_linedefs - 1].sidedef1 = level->num_sidedefs;
              InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
              if (sector >= 0)
                level->sidedefs[level->num_sidedefs - 1].sector = sector;
            }
          if (sector >= 0)
            {
              level->linedefs[level->num_linedefs - 1].start = level->num_vertexes - 1;
              level->linedefs[level->num_linedefs - 1].end = level->num_vertexes - a;
              for (n = 2; n <= a; n++)
                {
                  level->linedefs[level->num_linedefs - n].start = level->num_vertexes - n;
                  level->linedefs[level->num_linedefs - n].end = level->num_vertexes - n + 1;
                }
            }
          else
            {
              level->linedefs[level->num_linedefs - 1].start = level->num_vertexes - a;
              level->linedefs[level->num_linedefs - 1].end = level->num_vertexes - 1;
              for (n = 2; n <= a; n++)
                {
                  level->linedefs[level->num_linedefs - n].start = level->num_vertexes - n + 1;
                  level->linedefs[level->num_linedefs - n].end = level->num_vertexes - n;
                }
            }
        }
      break;

    case 3:
    case 4:
      /* Stairs */
      
      a = 6;
      b = 8;
      xw = 64;
      yh = 32;
      stair = DisplayMenu(x0, y0, "Choose stairs type",
                          "Line",
                          "Curve",
                          NULL);
      
      if (!Input2Numbers(-1, -1, "Number of steps", "Step thick", 256, 48, &a, &b))
        return;
      
      if (b < 1)
        b = 1;
      if (b > 16)
        b = 16;
      if (b & 0x1)
        b++;
      if (a < 2)
        a = 2;
      
      if (stair == 1)
        {
          if (!Input2Numbers(-1, -1, "Step width", "Step height", 256, 256, &xw, &yh))
            return;
          if (xw < 8)
            xw = 8;
          if (yh < 8)
            yh = 8;
          if (xw & 0x1)
            xw++;
          if (yh & 0x1)
            yh++;
          xpos = xpos - (xw >> 1);
          ypos = ypos - yh * a;
        }
      else
        {
          angle = 360;
          radius = 128;
          increment = 8;
          if (!Input2Numbers(-1, -1, "Step width", "Angle", 256, 720, &xw, &angle))
            return;
          if (!Input2Numbers(-1, -1, "Radius", "Increment", 512, 128, &radius, &increment))
            return;
          astart = 0.0;
          astep = (double)angle * 0.0174532925 / (double)a ;
        }

      if (sector >= 0)
        {
          l = level->sectors[sector].ceilh;
          h = level->sectors[sector].floorh;
        }
      else
        {
          l = 256;
          h = 0;
          if (!Input2Numbers(-1, -1, "Floor width", "Ceiling height", 1024, 1024, &h, &l))
            return;
        }
      
      if ((a * b > l - h) && (sector >= 0))
        {
          Beep();
          Notify(-1, -1, "The stairs are too high for this Sector", NULL);
          return;
        }

      for (n = 0; n < a; n++)
        {
          /* Line stairs */
          if (stair == 1)
            {
              if (n == 0)
                {
                  InsertObject(level, OBJ_VERTEXES, -1, xpos, ypos);
                  InsertObject(level, OBJ_VERTEXES, -1, xpos + xw, ypos);
                }
              InsertObject(level, OBJ_VERTEXES, -1, xpos, ypos + yh);
              InsertObject(level, OBJ_VERTEXES, -1, xpos + xw, ypos + yh);
              ypos += yh;
            }
          /* Curve stairs */
          else
            {
              x1 = xpos + (int)(cos(astart) * (double)radius);
              y1 = ypos + (int)(sin(astart) * (double)radius);
              x2 = xpos + (int)(cos(astart) * (double)(radius + xw));
              y2 = ypos + (int)(sin(astart) * (double)(radius + xw));
              if (n == 0)
                {
                  InsertObject(level, OBJ_VERTEXES, -1, x1, y1);
                  InsertObject(level, OBJ_VERTEXES, -1, x2, y2);
                  astart += astep;
                  radius += increment;
                  x1 = xpos + (int)(cos(astart) * (double)radius);
                  y1 = ypos + (int)(sin(astart) * (double)radius);
                  x2 = xpos + (int)(cos(astart) * (double)(radius + xw));
                  y2 = ypos + (int)(sin(astart) * (double)(radius + xw));
                }
              InsertObject(level, OBJ_VERTEXES, -1, x1, y1);
              InsertObject(level, OBJ_VERTEXES, -1, x2, y2);
              astart += astep;
              radius += increment;
            }
          InsertObject(level, OBJ_SECTORS, -1, 0, 0);
          /* growing stairs */
          if (choice == 3)
            {
              /* new stairs */
              if (sector < 0)
                {
                  if (n != 0)
                    {
                      l += b;
                      h += b;
                    }
                }
              else h += b;
            }
          level->sectors[level->num_sectors - 1].floorh = h;
          /* Stair = New Sector */
          if (sector < 0)
            {
              level->sectors[level->num_sectors - 1].ceilh = l;
              for (m = 0; m < 4; m++)
                {
                  if (!((m == 0) && (n > 0)))
                    {
                      InsertObject(level, OBJ_LINEDEFS, -1, 0, 0);
                      
                      if ((m == 3) && (n != a-1))
                        {
                          level->linedefs[level->num_linedefs - 1].sidedef1 = level->num_sidedefs;
                          level->linedefs[level->num_linedefs - 1].sidedef2 = level->num_sidedefs+1;
                          level->linedefs[level->num_linedefs - 1].flags = 4;
                          InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
                          strcpy(level->sidedefs[level->num_sidedefs - 1].tex2, Config.stepFaceTexture);
                          strcpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-");
                        }
                      else
                        level->linedefs[level->num_linedefs - 1].sidedef1 = level->num_sidedefs;
                      InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
                      if ((m == 3) && (n != a-1))
                        {
                          level->sidedefs[level->num_sidedefs - 1].sector = level->num_sectors;
                          strcpy(level->sidedefs[level->num_sidedefs - 1].tex1, Config.stepFaceTexture);
                          strcpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-");
                        }
                      else
                        strncpy(level->sidedefs[level->num_sidedefs - 1].tex3, Config.wallTexture, 8);
                    }
                }
            }
          /* Stair Inside sector */
          else
            {
              for (m = 0; m < 4; m++)
                {
                  if (!((m == 0) && (n > 0)))
                    {
                      InsertObject(level, OBJ_LINEDEFS, -1, 0, 0);
                      level->linedefs[level->num_linedefs - 1].sidedef1 = level->num_sidedefs;
                      level->linedefs[level->num_linedefs - 1].sidedef2 = level->num_sidedefs + 1;
                      level->linedefs[level->num_linedefs - 1].flags = 4;
                      InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
                      /* Back-Front face */
                      if (m == 3)
                        {
                          level->sidedefs[level->num_sidedefs - 1].sector = level->num_sectors - 1;
                          strcpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-");
                          if (n != a-1)
                            strcpy(level->sidedefs[level->num_sidedefs - 1].tex2, Config.stepFaceTexture);
                          InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
                          if (n == a-1)
                            {
                              level->sidedefs[level->num_sidedefs - 1].sector = sector;
                              strncpy(level->sidedefs[level->num_sidedefs - 1].tex2, Config.wallTexture, 8);
                            }
                          else
                            level->sidedefs[level->num_sidedefs - 1].sector = level->num_sectors;
                        }
                      else
                        {
                          level->sidedefs[level->num_sidedefs - 1].sector = sector;
                          strcpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-");
                          if (m == 0)
                            strcpy(level->sidedefs[level->num_sidedefs - 1].tex2, Config.stepFaceTexture);
                          else
                            strncpy(level->sidedefs[level->num_sidedefs - 1].tex2, Config.wallTexture, 8);
                          InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
                          level->sidedefs[level->num_sidedefs - 1].sector = level->num_sectors - 1;
                        }
                      strcpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-");
                    }
                }
            }
          
          if (sector >= 0)
            {
              level->linedefs[level->num_linedefs - 4].start = level->num_vertexes - 4;
              level->linedefs[level->num_linedefs - 4].end = level->num_vertexes - 3;
              level->linedefs[level->num_linedefs - 3].start = level->num_vertexes - 3;
              level->linedefs[level->num_linedefs - 3].end = level->num_vertexes - 1;
              level->linedefs[level->num_linedefs - 2].start = level->num_vertexes - 2;
              level->linedefs[level->num_linedefs - 2].end = level->num_vertexes - 4;
            }
          else
            {
              if (n == 0)
                {
                  level->linedefs[level->num_linedefs - 4].start = level->num_vertexes - 3;
                  level->linedefs[level->num_linedefs - 4].end = level->num_vertexes - 4;
                }
              else
                {
                  level->linedefs[level->num_linedefs - 4].start = level->num_vertexes - 4;
                  level->linedefs[level->num_linedefs - 4].end = level->num_vertexes - 3;
                }
              level->linedefs[level->num_linedefs - 3].start = level->num_vertexes - 1;
              level->linedefs[level->num_linedefs - 3].end = level->num_vertexes - 3;
              level->linedefs[level->num_linedefs - 2].start = level->num_vertexes - 4;
              level->linedefs[level->num_linedefs - 2].end = level->num_vertexes - 2;
            }
          level->linedefs[level->num_linedefs - 1].start = level->num_vertexes - 2;
          level->linedefs[level->num_linedefs - 1].end = level->num_vertexes - 1;
        }
      break;

    case 5:
    case 6:
      /* Teleports */
      
      /* Align on a 64 boundarie for texture */
      xpos = (xpos - 32) & 0xffc0;
      ypos = (ypos - 32) & 0xffc0;

      l = level->sectors[sector].ceilh - 8;
      h = level->sectors[sector].floorh + 8;
      if ((l - h) < 64)
        {
          Beep();
          Notify(-1, -1, "Room is too low!!", NULL);
          return;
        }
      
      if (choice == 6)
        {
          InsertObject(level, OBJ_THINGS, -1, xpos + 32, ypos + 32);
          level->things[level->num_things -1 ].type = THING_TELEPORT;
        }
      InsertObject(level, OBJ_VERTEXES, -1, xpos, ypos);
      InsertObject(level, OBJ_VERTEXES, -1, xpos + 64, ypos);
      InsertObject(level, OBJ_VERTEXES, -1, xpos + 64, ypos + 64);
      InsertObject(level, OBJ_VERTEXES, -1, xpos, ypos + 64);
      InsertObject(level, OBJ_SECTORS, -1, 0, 0);
      level->sectors[level->num_sectors - 1].ceilh  = l;
      level->sectors[level->num_sectors - 1].floorh = h;
      
      if (choice == 5)
        {
          strcpy(level->sectors[level->num_sectors -1].floort, "GATE1"); /*! texture */
          strncpy(level->sectors[level->num_sectors -1].ceilt, "TLITE6_5", 8); /*! texture */
        }
      else
        {
          strcpy(level->sectors[level->num_sectors -1].floort, "FLAT22"); /*! texture */
          strncpy(level->sectors[level->num_sectors -1].ceilt, "FLAT22", 8); /*! texture */
        }
      
      for (n = 0; n < 4; n++)
        {
          InsertObject(level, OBJ_LINEDEFS, -1, 0, 0);
          level->linedefs[level->num_linedefs - 1].sidedef1 = level->num_sidedefs;
          level->linedefs[level->num_linedefs - 1].sidedef2 = level->num_sidedefs + 1;
          level->linedefs[level->num_linedefs - 1].flags = 4;
          if (choice == 5)
            level->linedefs[level->num_linedefs -1].type = 97;
          InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
          level->sidedefs[level->num_sidedefs - 1].sector = sector;
          strcpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-");
          strcpy(level->sidedefs[level->num_sidedefs - 1].tex2, Config.stepFaceTexture);
          strncpy(level->sidedefs[level->num_sidedefs - 1].tex1, "LITEBLU4", 8); /*! texture */
          InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
          level->sidedefs[level->num_sidedefs - 1].sector = level->num_sectors - 1;
          strcpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-");
        }
      level->linedefs[level->num_linedefs - 4].start = level->num_vertexes - 4;
      level->linedefs[level->num_linedefs - 4].end = level->num_vertexes - 3;
      level->linedefs[level->num_linedefs - 3].start = level->num_vertexes - 3;
      level->linedefs[level->num_linedefs - 3].end = level->num_vertexes - 2;
      level->linedefs[level->num_linedefs - 2].start = level->num_vertexes - 2;
      level->linedefs[level->num_linedefs - 2].end = level->num_vertexes - 1;
      level->linedefs[level->num_linedefs - 1].start = level->num_vertexes - 1;
      level->linedefs[level->num_linedefs - 1].end = level->num_vertexes - 4;
      break;
      
    case 7:
      /* Crates */
      switch(DisplayMenu(x0, y0, ((x0 == -1) ? "Insert a Crate" : NULL),
                         " 16x16x16    Gray ",
                         " 32x32x16    Gray ",
                         " 64x64x16    Gray ",
                         " 16x32x16    Gray ",
                         " 16x64x16    Gray ",
                         " 32x64x16    Gray ",
                         " 64x64x64    Brown ",
                         " 64x64x64    Gray ",
                         " 64x64x128   Brown ",
                         " 64x64x128   Gray ",
                         " 128x128x64  Brown ",
                         " 128x128x128 Gray ",
                         NULL))
        {
          /*! replace all textures by something configurable */
        case 1:
          cw = ch = ct = 16;
          strcpy(texside, "CRATINY");
          strcpy(textop, "CRATOP1");
          break;
          
        case 2:
          cw = ch = 32;
          ct = 16;
          strcpy(texside, "CRATINY");
          strcpy(textop, "CRATOP1");
          break;
          
        case 3:
          cw = ch = 64;
          ct = 16;
          strcpy(texside, "CRATINY");
          strcpy(textop, "CRATOP1");
          break;
        case 4:
          
          cw = 16;
          ch = 32;
          ct = 16;
          strcpy(texside, "CRATINY");
          strcpy(textop, "CRATOP1");
          break;

        case 5:
          cw = 16;
          ch = 64;
          ct = 16;
          strcpy(texside, "CRATINY");
          strcpy(textop, "CRATOP1");
          break;
          
        case 6:
          cw = 32;
          ch = 64;
          ct = 16;
          strcpy(texside, "CRATINY");
          strcpy(textop, "CRATOP1");
          break;
          
        case 7:
          cw = ch = ct = 64;
          strcpy(texside, "CRATE1");
          strcpy(textop, "CRATOP2");
          break;
          
        case 8:
          cw = ch = ct = 64;
          strcpy(texside, "CRATE2");
          strcpy(textop, "CRATOP1");
          break;
          
        case 9:
          cw = ch = 64;
          ct = 128;
          strcpy(texside, "CRATE1");
          strcpy(textop, "CRATOP2");
          break;
          
        case 10:
          cw = ch = 64;
          ct = 128;
          strcpy(texside, "CRATE2");
          strcpy(textop, "CRATOP1");
          break;

        case 11:
          cw = ch = 128;
          ct = 64;
          strcpy(texside, "CRATWIDE");
          strcpy(textop, "CRATOP2");
          break;
          
        case 12:
          cw = ch = ct = 128;
          strcpy(texside, "CRATWIDE");
          strcpy(textop, "CRATOP2");
          break;
        }
      
      l = level->sectors[sector].ceilh;
      h = level->sectors[sector].floorh;
      if ((l - h) < ct)
        {
          Beep();
          Notify(-1, -1, "Room is too low!!", NULL);
          return;
        }
      
      InsertObject(level, OBJ_VERTEXES, -1, xpos, ypos);
      InsertObject(level, OBJ_VERTEXES, -1, xpos + cw, ypos);
      InsertObject(level, OBJ_VERTEXES, -1, xpos + cw, ypos + ch);
      InsertObject(level, OBJ_VERTEXES, -1, xpos, ypos + ch);
      if ((l-h) > ct)
        {
          InsertObject(level, OBJ_SECTORS, -1, 0, 0);
          level->sectors[level->num_sectors - 1].floorh = h + ct;
          strncpy(level->sectors[level->num_sectors -1].floort, textop, 8);
        }
      for (n = 0; n < 4; n++)
        {
          InsertObject(level, OBJ_LINEDEFS, -1, 0, 0);
          level->linedefs[level->num_linedefs - 1].sidedef1 = level->num_sidedefs;
          if ((l-h) > ct)
            {
              level->linedefs[level->num_linedefs - 1].sidedef2 = level->num_sidedefs + 1;
              level->linedefs[level->num_linedefs - 1].flags = 4;
            }
          InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
          level->sidedefs[level->num_sidedefs - 1].sector = sector;
          if ((l-h) > ct)
            {
              strcpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-");
              strncpy(level->sidedefs[level->num_sidedefs - 1].tex2, texside, 8);
              InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
              
              level->sidedefs[level->num_sidedefs - 1].sector = level->num_sectors - 1;
              strcpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-");
            }
          else
            strncpy(level->sidedefs[level->num_sidedefs - 1].tex3, texside, 8);
        }
      level->linedefs[level->num_linedefs - 4].start = level->num_vertexes - 4;
      level->linedefs[level->num_linedefs - 4].end = level->num_vertexes - 3;
      level->linedefs[level->num_linedefs - 3].start = level->num_vertexes - 3;
      level->linedefs[level->num_linedefs - 3].end = level->num_vertexes - 2;
      level->linedefs[level->num_linedefs - 2].start = level->num_vertexes - 2;
      level->linedefs[level->num_linedefs - 2].end = level->num_vertexes - 1;
      level->linedefs[level->num_linedefs - 1].start = level->num_vertexes - 1;
      level->linedefs[level->num_linedefs - 1].end = level->num_vertexes - 4;
      break;

    case 8:
      /* Extrusions */
      a = 4;
      b = 128;
      f = c = 0;
      
      if (Input2Numbers(-1, -1, "Number of sides", "Radius", 32, 2000, &a, &b)
          && Input2Numbers(-1, -1, "Floor", "Ceiling", 32, 2000, &f, &c))
        {
          l = level->sectors[sector].ceilh;
          h = level->sectors[sector].floorh;
          if (((l-c) - (h-f)) < 4)
            {
              Beep();
              Notify(-1, -1, "Room is too low!!", NULL);
              return;
            }
          
          if (a < 3)
            a = 3;
          if (b < 8)
            b = 8;

          InsertPolygonVertices(level, xpos, ypos, a, b);
          InsertObject(level, OBJ_SECTORS, -1, 0, 0);
          level->sectors[level->num_sectors - 1].floorh = h + f;
          level->sectors[level->num_sectors - 1].ceilh = l - c;
          for (n = 0; n < a; n++)
            {
              InsertObject(level, OBJ_LINEDEFS, -1, 0, 0);
              level->linedefs[level->num_linedefs - 1].sidedef1 = level->num_sidedefs;
              level->linedefs[level->num_linedefs - 1].sidedef2 = level->num_sidedefs + 1;
              level->linedefs[level->num_linedefs - 1].flags = 4;
              InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
              level->sidedefs[level->num_sidedefs - 1].sector = sector;
              if ((f != 0) || (c != 0))
                {
                  strcpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-");
                  if (f != 0)
                    {
                      strncpy(level->sidedefs[level->num_sidedefs - 1].tex2, Config.wallTexture, 8);
                      level->linedefs[level->num_linedefs - 1].flags |= 0x10;
                    }
                  if (c != 0)
                    {
                      strncpy(level->sidedefs[level->num_sidedefs - 1].tex1, Config.wallTexture, 8);
                      level->linedefs[level->num_linedefs - 1].flags |= 0x8;
                    }
                }
              else
                strncpy(level->sidedefs[level->num_sidedefs - 1].tex3, Config.wallTexture, 8);
              InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
              level->sidedefs[level->num_sidedefs - 1].sector = level->num_sectors - 1;
              strcpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-");
            }
          level->linedefs[level->num_linedefs - 1].start = level->num_vertexes - 1;
          level->linedefs[level->num_linedefs - 1].end = level->num_vertexes - a;
          for (n = 2; n <= a; n++)
            {
              level->linedefs[level->num_linedefs - n].start = level->num_vertexes - n;
              level->linedefs[level->num_linedefs - n].end = level->num_vertexes - n + 1;
            }
        }
      break;
    }
}



/*
   Menu of miscellaneous operations.
*/
/*! This should be re-organised: one menu for each editing mode. */
void MiscOperations(LevelPtr level, Int16 x0, Int16 y0, int objtype, SelPtr *list)
{
  char   msg[80];
  Int16  val;
  Int16  angle, scale;
  
  sprintf(msg, "Rotate and scale %s", GetEditModeName(objtype));
  if (objtype == OBJ_VERTEXES)
    {
      val = DisplayMenu(x0, y0, ((x0 == -1) ? "Misc. Operations" : NULL),
                        "Find first free tag number",
                        msg,
                        "Delete Vertex and join LineDefs",
                        "Merge several Vertices into one", /*! not needed? */
                        "Add a LineDef and split Sector",
                        NULL);
    }
  else if (objtype == OBJ_LINEDEFS)
    {
      val = DisplayMenu(x0, y0, ((x0 == -1) ? "Misc. Operations" : NULL),
                        "Find first free tag number",
                        msg,
                        "Split LineDef (add new Vertex)",
                        "Split LineDefs and Sector",
                        "Delete LineDefs and join Sectors",
                        "Flip LineDef",
                        "Swap SideDefs",
                        "Align textures (X or Y offset)",
                        NULL);
    }
  else if (objtype == OBJ_SECTORS)
    {
      val = DisplayMenu(x0, y0, ((x0 == -1) ? "Misc. Operations" : NULL),
                        "Find first free tag number",
                        msg,
                        "Make door from Sector",
                        "Make window from Sector",
                        "Make lift from Sector",
                        "Distribute Sector floor heights",
                        "Distribute Sector ceiling heights",
                        "Distribute Sector light levels",
                        NULL);
    }                                                         
  else
    {
      val = DisplayMenu(x0, y0, ((x0 == -1) ? "Misc. Operations" : NULL),
                        "Find first free tag number",
                        msg,
                        NULL);
    }
  if (val > 1 && *list == NULL)
    {
      Beep();
      sprintf(msg, "You must select at least one %s", GetObjectTypeName(objtype));
      Notify(-1, -1, msg, NULL);
      return;
    }
  switch (val)
    {
    case 1:
      /* find first tag */
      sprintf(msg, "First free tag number: %d", FindFreeTag(level));
      Notify(-1, -1, msg, NULL);
      break;

    case 2:
      /* rotate and scale */
      if ((objtype == OBJ_THINGS || objtype == OBJ_VERTEXES) && (*list)->next == NULL)
        {
          Beep();
          sprintf(msg, "You must select more than one %s", GetObjectTypeName(objtype));
          Notify(-1, -1, msg, NULL);
          return;
        }
      angle = 0;
      scale = 100;
      if (Input2Numbers(-1, -1, "Rotation angle (ø)", "Scale (%)", 360, 1000, &angle, &scale))
        RotateAndScaleObjects(level, objtype, *list, (double) angle * 0.0174533, (double) scale * 0.01);
      break;

    case 3:
      if (objtype == OBJ_VERTEXES)
        {
          /* delete Vertices and join LineDefs */
          DeleteVerticesJoinLineDefs(level, *list);
          ForgetSelection(list);
        }
      else if (objtype == OBJ_LINEDEFS)
        {
          /* split LineDefs */
          SplitLineDefs(level, *list);
        }
      else if (objtype == OBJ_SECTORS)
        {
          /* make door from Sector */
          if ((*list)->next != NULL)
            {
              Beep();
              Notify(-1, -1, "You must select exactly one Sector", NULL);
            }
          else
            {
              MakeDoorFromSector(level, (*list)->objnum);
            }
        }
      break;

    case 4:
      if (objtype == OBJ_VERTEXES)
        {
          /* merge Vertices */
          MergeVertices(level, list);
        }
      else if (objtype == OBJ_LINEDEFS)
        {
          /* split LineDefs and Sector */
          if ((*list)->next == NULL || (*list)->next->next != NULL)
            {
              Beep();
              Notify(-1, -1, "You must select exactly two LineDefs", NULL);
            }
          else
            {
              SplitLineDefsAndSector(level, (*list)->objnum, (*list)->next->objnum);
              ForgetSelection(list);
            }
        }
      else if (objtype == OBJ_SECTORS)
        {
          /* make window from Sector */
          if ((*list)->next != NULL)
            {
              Beep();
              Notify(-1, -1, "You must select exactly one Sector", NULL);
            }
          else
            {
              MakeWindowFromSector(level, (*list)->objnum);
            }
        }
      break;

    case 5:
      if (objtype == OBJ_VERTEXES)
        {
          /* split Sector */
          if ((*list)->next == NULL || (*list)->next->next != NULL)
            {
              Beep();
              Notify(-1, -1, "You must select exactly two Vertices", NULL);
            }
          else
            {
              SplitSector(level, (*list)->objnum, (*list)->next->objnum);
              ForgetSelection(list);
            }
        }
      else if (objtype == OBJ_LINEDEFS)
        {
          /* join Sectors */
          DeleteLineDefsJoinSectors(level, list);
        }
      else if (objtype == OBJ_SECTORS)
        {
          /* make lift from Sector */
          if ((*list)->next != NULL)
            {
              Beep();
              Notify(-1, -1, "You must select exactly one Sector", NULL);
            }
          else
            {
              MakeLiftFromSector(level, (*list)->objnum);
            }
        }
      break;

    case 6:
      if (objtype == OBJ_LINEDEFS)
        {
          /* flip LineDefs */
          FlipLineDefs(level, *list, TRUE);
        }
      else if (objtype == OBJ_SECTORS)
        {
          /* distribute floors */
          if ((*list)->next == NULL || (*list)->next->next == NULL)
            {
              Beep();
              Notify(-1, -1, "You must select three or more Sectors", NULL);
            }
          else
            {
              DistributeSectorFloors(level, *list);
            }
        }
      break;

    case 7:
      if (objtype == OBJ_LINEDEFS)
        {
          /* flip SideDefs */
          if (Config.expert || Confirm(-1, -1, "Warning: the Sector references are also swapped", "You may get strange results if you don't know what you are doing..."))
            FlipLineDefs(level, *list, FALSE);
        }
      else if (objtype == OBJ_SECTORS)
        {
          /* distribute ceilings */
          if ((*list)->next == NULL || (*list)->next->next == NULL)
            {
              Beep();
              Notify(-1, -1, "You must select three or more Sectors", NULL);
            }
          else
            {
              DistributeSectorCeilings(level, *list);
            }
        }
      break;

    case 8:
      if (objtype == OBJ_LINEDEFS)
        {
          /* align textures */
          AlignTextures(level, *list);
        }
      else if (objtype == OBJ_SECTORS)
        {
          /* distribute lights */
          if ((*list)->next == NULL || (*list)->next->next == NULL)
            {
              Beep();
              Notify(-1, -1, "You must select three or more Sectors", NULL);
            }
          else
            {
              DistributeSectorLights(level, *list);
            }
        }
      break;
    }
}


/*
   Display a "Preferences" menu (change default textures, etc.).
*/

void Preferences(Int16 x0, Int16 y0)
{
  char  *menustr[13];
  Int16  n, val;
  char   texname[9];

  if (x0 < 0)
    x0 = (ScrMaxX - 50 * 8 - 19) / 2;
  if (y0 < 0)
    y0 = (ScrMaxY - 16 * 10 - 28) / 2;
  for (n = 0; n < 13; n++)
    menustr[n] = (char *)GetMemory(80);
  sprintf(menustr[0],  "Change default wall texture       (Current: %s)", Config.wallTexture);
  sprintf(menustr[1],  "Change default \"upper\" texture    (Current: %s)", Config.upperTexture);
  sprintf(menustr[2],  "Change default \"lower\" texture    (Current: %s)", Config.lowerTexture);
  sprintf(menustr[3],  "Change default door face texture  (Current: %s)", Config.doorFaceTexture);
  sprintf(menustr[4],  "Change default door track texture (Current: %s)", Config.doorTrakTexture);
  sprintf(menustr[5],  "Change default lift face texture  (Current: %s)", Config.liftFaceTexture);
  sprintf(menustr[6],  "Change default step face texture  (Current: %s)", Config.stepFaceTexture);
  sprintf(menustr[7],  "Change default switch texture     (Current: %s)", Config.switchTexture);
  sprintf(menustr[8],  "Change default floor texture      (Current: %s)", Config.floorTexture);
  sprintf(menustr[9],  "Change default ceiling texture    (Current: %s)", Config.ceilingTexture);
  sprintf(menustr[10], "Change default floor height       (Current: %d)", Config.floorHeight);
  sprintf(menustr[11], "Change default ceiling height     (Current: %d)", Config.ceilingHeight);
  sprintf(menustr[12], "Change default light level        (Current: %d)", Config.lightLevel);
  val = DisplayMenu(x0, y0, "Preferences",
                            menustr[0],
                            menustr[1],
                            menustr[2],
                            menustr[3],
                            menustr[4],
                            menustr[5],
                            menustr[6],
                            menustr[7],
                            menustr[8],
                            menustr[9],
                            menustr[10],
                            menustr[11],
                            menustr[12],
                            "Save preferences in DEU.INI",
                            "Load preferences from theme...",
                            "Save preferences as theme...",
                            NULL);
  for (n = 0; n < 13; n++)
    FreeMemory(menustr[n]);
  switch (val)
    {
    case 1:
      strcpy(texname, Config.wallTexture);
      ChooseWallTexture(x0 + 42, y0 + 34, "Choose a wall texture", num_wtexture, wtexture, texname);
      if (strlen(texname) > 0)
        strcpy(Config.wallTexture, texname);
      break;
    case 2:
      strcpy(texname, Config.upperTexture);
      ChooseWallTexture(x0 + 42, y0 + 44, "Choose a wall texture", num_wtexture, wtexture, texname);
      if (strlen(texname) > 0)
        strcpy(Config.upperTexture, texname);
      break;
    case 3:
      strcpy(texname, Config.lowerTexture);
      ChooseWallTexture(x0 + 42, y0 + 54, "Choose a wall texture", num_wtexture, wtexture, texname);
      if (strlen(texname) > 0)
        strcpy(Config.lowerTexture, texname);
      break;
    case 4:
      strcpy(texname, Config.doorFaceTexture);
      ChooseWallTexture(x0 + 42, y0 + 64, "Choose a wall texture", num_wtexture, wtexture, texname);
      if (strlen(texname) > 0)
        strcpy(Config.doorFaceTexture, texname);
      break;
    case 5:
      strcpy(texname, Config.doorTrakTexture);
      ChooseWallTexture(x0 + 42, y0 + 74, "Choose a wall texture", num_wtexture, wtexture, texname);
      if (strlen(texname) > 0)
        strcpy(Config.lowerTexture, texname);
      break;
    case 6:
      strcpy(texname, Config.liftFaceTexture);
      ChooseWallTexture(x0 + 42, y0 + 84, "Choose a wall texture", num_wtexture, wtexture, texname);
      if (strlen(texname) > 0)
        strcpy(Config.liftFaceTexture, texname);
      break;
    case 7:
      strcpy(texname, Config.stepFaceTexture);
      ChooseWallTexture(x0 + 42, y0 + 94, "Choose a wall texture", num_wtexture, wtexture, texname);
      if (strlen(texname) > 0)
        strcpy(Config.stepFaceTexture, texname);
      break;
    case 8:
      strcpy(texname, Config.switchTexture);
      ChooseWallTexture(x0 + 42, y0 + 104, "Choose a wall texture", num_wtexture, wtexture, texname);
      if (strlen(texname) > 0)
        strcpy(Config.switchTexture, texname);
      break;
    case 9:
      strcpy(texname, Config.floorTexture);
      ChooseFloorTexture(x0 + 42, y0 + 114, "Choose a floor texture", num_ftexture, ftexture, texname);
      if (strlen(texname) > 0)
        strcpy(Config.floorTexture, texname);
      break;
    case 10:
      strcpy(texname, Config.ceilingTexture);
      ChooseFloorTexture(x0 + 42, y0 + 124, "Choose a ceiling texture", num_ftexture, ftexture, texname);
      if (strlen(texname) > 0)
        strcpy(Config.ceilingTexture, texname);
      break;
    case 11:
      val = InputIntegerValue(x0 + 42, y0 + 134, NULL, -512, 511, Config.floorHeight);
      if (val >= -512)
        Config.floorHeight = val;
      break;
    case 12:
      val = InputIntegerValue(x0 + 42, y0 + 144, NULL, -512, 511, Config.ceilingHeight);
      if (val >= -512)
        Config.ceilingHeight = val;
      break;
    case 13:
      val = InputIntegerValue(x0 + 42, y0 + 154, NULL, 0, 255, Config.lightLevel);
      if (val >= 0)
        Config.lightLevel = val;
      break;
    case 14:
      NotImplemented(); /*!*/
    case 15:
      NotImplemented(); /*!*/
    case 16:
      NotImplemented(); /*!*/
    }
}


/* end of file */
