/*----------------------------------------------------------------------------*
 | 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_OBJECT.C - Split, join and transform objects.

*/

/* the includes */
#include "deu.h"
#include <math.h>
#include "d_misc.h"
#include "d_config.h"
#include "w_levels.h"
#include "w_select.h"
#include "w_object.h"
#include "i_menus.h"
#include "i_textur.h"
#include "m_checks.h"
#include "m_edmenu.h" /* Input2Numbers */
#include "m_object.h"


/*
   Flip one or several LineDefs.
*/

void FlipLineDefs(LevelPtr level, SelPtr list, Bool swapvertices)
{
  SelPtr cur;
  Int16  tmp;

  for (cur = list; cur; cur = cur->next)
    {
      if (swapvertices)
        {
          /* swap starting and ending Vertices */
          tmp = level->linedefs[cur->objnum].end;
          level->linedefs[cur->objnum].end = level->linedefs[cur->objnum].start;
          level->linedefs[cur->objnum].start = tmp;
        }
      /* swap first and second SideDefs */
      tmp = level->linedefs[cur->objnum].sidedef1;
      level->linedefs[cur->objnum].sidedef1 = level->linedefs[cur->objnum].sidedef2;
      level->linedefs[cur->objnum].sidedef2 = tmp;
    }
  level->made_changes = TRUE;
  level->made_map_changes = TRUE;
}


/*
   Delete a Vertex and join the two LineDefs.
*/

void DeleteVerticesJoinLineDefs(LevelPtr level, SelPtr list)
{
  Int16  ldstart, ldend, ld;
  SelPtr cur;
  char   msg[80];
  
  while (list != NULL)
    {
      cur = list;
      list = list->next;
      ldstart = -1;
      ldend = -1;
      for (ld = 0; ld < level->num_linedefs; ld++)
        {
          if (level->linedefs[ld].start == cur->objnum)
            {
              if (ldstart < 0)
                ldstart = ld;
              else
                break; /* more than one LineDef starts at this Vertex */
            }
          if (level->linedefs[ld].end == cur->objnum)
            {
              if (ldend < 0)
                ldend = ld;
              else
                break; /* more than one LineDef ends at this Vertex */
            }
        }
      if (ld < level->num_linedefs || ldstart < 0 || ldend < 0)
        {
          Beep();
          sprintf(msg, "Cannot delete Vertex #%d and join the LineDefs", cur->objnum);
          Notify(-1, -1, msg, "The Vertex must be the start of one LineDef and the end of another one");
          continue;
        }
      /* join the two LineDefs */
      level->linedefs[ldend].end = level->linedefs[ldstart].end;
      DeleteObject(level, OBJ_LINEDEFS, ldstart);
      /* delete the Vertex */
      DeleteObject(level, OBJ_VERTEXES, cur->objnum);
      level->made_changes = TRUE;
      level->made_map_changes = TRUE;
    }
}



/*
   Merge several vertices into one.
*/
/*! The menu entry for this function is not very useful: it is easier and
    more intuitive to use the "AutoMerge" feature. */
void MergeVertices(LevelPtr level, SelPtr *listp)
{
  Int16 v, ld;

  v = (*listp)->objnum;
  UnSelectObject(listp, v);
  if (*listp == NULL)
    {
      Beep();
      Notify(-1, -1, "You must select at least two vertices", NULL);
      return;
    }
  /* change the LineDefs starts & ends */
  for (ld = 0; ld < level->num_linedefs; ld++)
    {
      if (IsSelected(*listp, level->linedefs[ld].start))
        {
          /* don't change a LineDef that has both ends on the same spot */
          if (!IsSelected(*listp, level->linedefs[ld].end) && level->linedefs[ld].end != v)
            level->linedefs[ld].start = v;
        }
      else if (IsSelected(*listp, level->linedefs[ld].end))
        {
          /* idem */
          if (level->linedefs[ld].start != v)
            level->linedefs[ld].end = v;
        }
    }
  /* delete the Vertices (and some LineDefs too) */
  DeleteObjects(level, OBJ_VERTEXES, listp);
  level->made_changes = TRUE;
  level->made_map_changes = TRUE;
}



/*
   Check if some vertices should be merged into one.  Return TRUE if the map
   has to be redrawn.
*/

Bool AutoMergeVertices(LevelPtr level, SelPtr *listp)
{
  SelPtr ref, cur;
  Bool   confirmed, redraw, flipped, mergedone, isldend;
  Int16  v, refv, ld, sd, oldnumld;
  
  confirmed = FALSE;
  redraw = FALSE;
  mergedone = FALSE;
  isldend = FALSE;
  ref = *listp;
  while (ref)
    {
      refv = ref->objnum;
      ref = ref->next;
      /* check if there is a Vertex at the same position (same X and Y) */
      for (v = 0; v < level->num_vertexes; v++)
        if (v != refv && level->vertexes[refv].x == level->vertexes[v].x && level->vertexes[refv].y == level->vertexes[v].y)
          {
            redraw = TRUE;
            if (confirmed || Config.expert || Confirm(-1, -1, "Some Vertices occupy the same position", "Do you want to merge them into one?"))
              {
                /* don't ask for confirmation twice */
                confirmed = TRUE;
                /* merge the two vertices */
                mergedone = TRUE;
                cur = NULL;
                SelectObject(&cur, refv);
                SelectObject(&cur, v);
                MergeVertices(level, &cur);
                /* update the references in the selection list */
                for (cur = *listp; cur; cur = cur->next)
                  if (cur->objnum > refv)
                    cur->objnum = cur->objnum - 1;
                if (v > refv)
                  v--;
                /* the old Vertex has been deleted */
                UnSelectObject(listp, refv);
                /* select the new Vertex instead */
               if (!IsSelected(*listp, v))
                 SelectObject(listp, v);
                break;
              }
            else
              return redraw;
          }
    }
  confirmed = FALSE;
  ref = *listp;
  while (ref)
    {
      refv = ref->objnum;
      ref = ref->next;
      oldnumld = level->num_linedefs;
      /* check if this Vertex is on a LineDef */
      for (ld = 0; ld < oldnumld; ld++)
        {
          if (level->linedefs[ld].start == refv || level->linedefs[ld].end == refv)
            {
              /* one Vertex had a LineDef bound to it -- check it later */
              isldend = TRUE;
            }
          else if (IsLineDefInside(level, ld, level->vertexes[refv].x - 3, level->vertexes[refv].y - 3, level->vertexes[refv].x + 3, level->vertexes[refv].y + 3))
            {
              redraw = TRUE;
              if (confirmed || Config.expert || Confirm(-1, -1, "Some Vertices are on a LineDef", "Do you want to split the LineDef there?"))
                {
                  /* don't ask for confirmation twice */
                  confirmed = TRUE;
                  /* split the LineDef */
                  mergedone = TRUE;
                  InsertObject(level, OBJ_LINEDEFS, ld, 0, 0);
                  level->linedefs[ld].end = refv;
                  level->linedefs[level->num_linedefs - 1].start = refv;
                  sd = level->linedefs[ld].sidedef1;
                  if (sd >= 0)
                    {
                      InsertObject(level, OBJ_SIDEDEFS, sd, 0, 0);
                  level->linedefs[level->num_linedefs - 1].sidedef1 = level->num_sidedefs - 1;
                    }
                  sd = level->linedefs[ld].sidedef2;
                  if (sd >= 0)
                    {
                      InsertObject(level, OBJ_SIDEDEFS, sd, 0, 0);
                      level->linedefs[level->num_linedefs - 1].sidedef2 = level->num_sidedefs - 1;
                    }
                  level->made_changes = TRUE;
                  level->made_map_changes = TRUE;
                }
              else
                return redraw;
            }
        }
    }
  /* don't continue if this isn't necessary */
  if (isldend == FALSE || mergedone == FALSE)
    return redraw;
  
  confirmed = FALSE;
  /* test if two LineDefs are between the same pair of Vertices */
  for (v = 0; v < level->num_linedefs - 1; v++)
    for (ld = v + 1; ld < level->num_linedefs; ld++)
      if ((level->linedefs[v].start == level->linedefs[ld].start && level->linedefs[v].end == level->linedefs[ld].end)
          || (level->linedefs[v].start == level->linedefs[ld].end && level->linedefs[v].end == level->linedefs[ld].start))
        {
          redraw = TRUE;
            if (confirmed || Config.expert || Confirm(-1, -1, "Some LineDefs are superimposed", "Do you want to merge them into one?"))
              {
                /* don't ask for confirmation twice */
                confirmed = TRUE;
                /* test if the LineDefs have the same orientation */
                if (level->linedefs[v].start == level->linedefs[ld].end)
                  flipped = TRUE;
                else
                  flipped = FALSE;
                /* merge the two LineDefs */
                if (level->linedefs[v].sidedef1 < 0)
                  {
                  if (flipped)
                    {
                      level->linedefs[v].sidedef1 = level->linedefs[ld].sidedef2;
                      level->linedefs[ld].sidedef2 = -1;
                    }
                  else
                    {
                      level->linedefs[v].sidedef1 = level->linedefs[ld].sidedef1;
                      level->linedefs[ld].sidedef1 = -1;
                  }
                }
                if (level->linedefs[v].sidedef2 < 0)
                  {
                    if (flipped)
                      {
                        level->linedefs[v].sidedef2 = level->linedefs[ld].sidedef1;
                        level->linedefs[ld].sidedef1 = -1;
                      }
                  else
                    {
                      level->linedefs[v].sidedef2 = level->linedefs[ld].sidedef2;
                      level->linedefs[ld].sidedef2 = -1;
                    }
                  }
                if (level->linedefs[v].sidedef1 >= 0 && level->linedefs[v].sidedef2 >= 0 && (level->linedefs[v].flags & 0x04) == 0)
                  level->linedefs[v].flags = 0x04;
                DeleteObject(level, OBJ_LINEDEFS, ld);
              }
        }
  return redraw;
}


/*
   Split one or more LineDefs in two, adding new Vertices in the middle.
*/

void SplitLineDefs(LevelPtr level, SelPtr list)
{
  SelPtr cur;
  Int16  vstart, vend, sd;
  
  for (cur = list; cur; cur = cur->next)
    {
      vstart = level->linedefs[cur->objnum].start;
      vend = level->linedefs[cur->objnum].end;
      InsertObject(level, OBJ_VERTEXES, -1, (level->vertexes[vstart].x + level->vertexes[vend].x) / 2, (level->vertexes[vstart].y + level->vertexes[vend].y) / 2);
      InsertObject(level, OBJ_LINEDEFS, cur->objnum, 0, 0);
      level->linedefs[cur->objnum].end = level->num_vertexes - 1;
      level->linedefs[level->num_linedefs - 1].start = level->num_vertexes - 1;
      sd = level->linedefs[cur->objnum].sidedef1;
      if (sd >= 0)
        {
          InsertObject(level, OBJ_SIDEDEFS, sd, 0, 0);
          level->linedefs[level->num_linedefs - 1].sidedef1 = level->num_sidedefs - 1;
        }
      sd = level->linedefs[cur->objnum].sidedef2;
      if (sd >= 0)
        {
          InsertObject(level, OBJ_SIDEDEFS, sd, 0, 0);
          level->linedefs[level->num_linedefs - 1].sidedef2 = level->num_sidedefs - 1;
        }
    }
  level->made_changes = TRUE;
  level->made_map_changes = TRUE;
}


/*
   Split a Sector in two, adding a new LineDef between the two Vertices.

   Note from RQ:
      This function could (and should) be improved.  Sometimes it will
      refuse to split a sector although it is possible to do it.
   Note 2 from RQ:
      It will always fail if the user tries to split a "donut" Sector,
      but this is perfectly normal: the correct way to split such a
      sector is to insert a new sector inside it, then merge two lines
      of this new sector with the inner an outer edges of the donut.
      Errr... maybe this should be explained clearly in the docs.
*/

void SplitSector(LevelPtr level, Int16 vertex1, Int16 vertex2)
{
  SelPtr ldlist;
  Int16  curv, s, ld, sd;
  char   msg1[80], msg2[80];
  
  /* check if there is a Sector between the two Vertices (in the middle) */
  s = GetCurObject(level, OBJ_SECTORS, level->vertexes[vertex1].x, level->vertexes[vertex1].y, level->vertexes[vertex2].x, level->vertexes[vertex2].y);
  if (s < 0)
    {
      Beep();
      sprintf(msg1, "There is no Sector between Vertex #%d and Vertex #%d", vertex1, vertex2);
      Notify(-1, -1, msg1, NULL);
      return;
    }
  /* check if there is a closed path from vertex1 to vertex2, along the edge of the Sector s */
  ldlist = NULL;
  curv = vertex1;
  while (curv != vertex2)
    {
      for (ld = 0; ld < level->num_linedefs; ld++)
        {
          sd = level->linedefs[ld].sidedef1;
          if (sd >= 0 && level->sidedefs[sd].sector == s && level->linedefs[ld].start == curv)
            {
              curv = level->linedefs[ld].end;
              SelectObject(&ldlist, ld);
              break;
            }
          sd = level->linedefs[ld].sidedef2;
          if (sd >= 0 && level->sidedefs[sd].sector == s && level->linedefs[ld].end == curv)
            {
              curv = level->linedefs[ld].start;
              SelectObject(&ldlist, ld);
              break;
            }
        }
      if (ld >= level->num_linedefs)
        {
          Beep();
          sprintf(msg1, "Cannot find a closed path from Vertex #%d to Vertex #%d", vertex1, vertex2);
          if (curv == vertex1)
            sprintf(msg2, "There is no SideDef starting from Vertex #%d on Sector #%d", vertex1, s);
          else
            sprintf(msg2, "Check if Sector #%d is closed (cannot go past Vertex #%d)", s, curv);
          Notify(-1, -1, msg1, msg2);
          ForgetSelection(&ldlist);
          return;
        }
      if (curv == vertex1)
        {
          Beep();
          sprintf(msg1, "Vertex #%d is not on the same Sector (#%d) as Vertex #%d", vertex2, s, vertex1);
          Notify(-1, -1, msg1, NULL);
          ForgetSelection(&ldlist);
          return;
        }
    }
  /* now, the list of LineDefs for the new Sector is in ldlist */
  
  /* add the new Sector, LineDef and SideDefs */
  InsertObject(level, OBJ_SECTORS, s, 0, 0);
  InsertObject(level, OBJ_LINEDEFS, -1, 0, 0);
  level->linedefs[level->num_linedefs - 1].start = vertex1;
  level->linedefs[level->num_linedefs - 1].end = vertex2;
  level->linedefs[level->num_linedefs - 1].flags = 4;
  InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
  level->sidedefs[level->num_sidedefs - 1].sector = s;
  strncpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-", 8);
  InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
  strncpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-", 8);
  level->linedefs[level->num_linedefs - 1].sidedef1 = level->num_sidedefs - 2;
  level->linedefs[level->num_linedefs - 1].sidedef2 = level->num_sidedefs - 1;
  
  /* bind all LineDefs in ldlist to the new Sector */
  while (ldlist)
    {
      sd = level->linedefs[ldlist->objnum].sidedef1;
      if (sd < 0 || level->sidedefs[sd].sector != s)
        sd = level->linedefs[ldlist->objnum].sidedef2;
      level->sidedefs[sd].sector = level->num_sectors - 1;
      UnSelectObject(&ldlist, ldlist->objnum);
    }
  
  /* second check... useful for Sectors within Sectors */
  /*! Will miss some Sector references if they are facing each other */
  for (ld = 0; ld < level->num_linedefs; ld++)
    {
      sd = level->linedefs[ld].sidedef1;
      if (sd >= 0 && level->sidedefs[sd].sector == s)
        {
          curv = GetOppositeSector(level, ld, TRUE);
          if (curv == level->num_sectors - 1)
            level->sidedefs[sd].sector = level->num_sectors - 1;
        }
      sd = level->linedefs[ld].sidedef2;
      if (sd >= 0 && level->sidedefs[sd].sector == s)
        {
          curv = GetOppositeSector(level, ld, FALSE);
          if (curv == level->num_sectors - 1)
            level->sidedefs[sd].sector = level->num_sectors - 1;
        }
    }
  level->made_changes = TRUE;
  level->made_map_changes = TRUE;
}



/*
   Split two LineDefs, then split the Sector and add a new LineDef between the new Vertices.
*/

void SplitLineDefsAndSector(LevelPtr level, Int16 linedef1, Int16 linedef2)
{
   SelPtr ldlist;
   Int16  s1, s2, s3, s4;
   char   msg[80];

   /* check if the two LineDefs are adjacent to the same Sector */
   s1 = level->linedefs[linedef1].sidedef1;
   s2 = level->linedefs[linedef1].sidedef2;
   s3 = level->linedefs[linedef2].sidedef1;
   s4 = level->linedefs[linedef2].sidedef2;
   if (s1 >= 0)
      s1 = level->sidedefs[s1].sector;
   if (s2 >= 0)
      s2 = level->sidedefs[s2].sector;
   if (s3 >= 0)
      s3 = level->sidedefs[s3].sector;
   if (s4 >= 0)
      s4 = level->sidedefs[s4].sector;
   if ((s1 < 0 || (s1 != s3 && s1 != s4)) && (s2 < 0 || (s2 != s3 && s2 != s4)))
   {
      Beep();
      sprintf(msg, "LineDefs #%d and #%d are not adjacent to the same Sector", linedef1, linedef2);
      Notify(-1, -1, msg, NULL);
      return;
   }
   /* split the two LineDefs and create two new Vertices */
   ldlist = NULL;
   SelectObject(&ldlist, linedef1);
   SelectObject(&ldlist, linedef2);
   SplitLineDefs(level, ldlist);
   ForgetSelection(&ldlist);
   /* split the Sector and create a LineDef between the two Vertices */
   SplitSector(level, level->num_vertexes - 1, level->num_vertexes - 2);
}


/*
   Merge two or more Sectors into one.
*/

void MergeSectors(LevelPtr level, SelPtr *listp)
{
  SelPtr cur;
  Int16  n, olds, news;
  
  /* save the first Sector number */
  news = (*listp)->objnum;
  UnSelectObject(listp, news);
  
  /* change all SideDefs references to the other Sectors */
  for (cur = *listp; cur; cur = cur->next)
    {
      olds = cur->objnum;
      for (n = 0; n < level->num_sidedefs; n++)
        if (level->sidedefs[n].sector == olds)
          level->sidedefs[n].sector = news;
    }
  
  /* delete the Sectors */
  DeleteObjects(level, OBJ_SECTORS, listp);
  
  /* the returned list contains only the first Sector */
  SelectObject(listp, news);
}


/*
   Delete one or several two-sided LineDefs and join the two Sectors.
*/

void DeleteLineDefsJoinSectors(LevelPtr level, SelPtr *ldlistp)
{
  SelPtr cur, slist;
  Int16  sd1, sd2, s1, s2;
  char   msg[80];
  
  /* first, do the tests for all LineDefs */
  for (cur = *ldlistp; cur; cur = cur->next)
    {
      sd1 = level->linedefs[cur->objnum].sidedef1;
      sd2 = level->linedefs[cur->objnum].sidedef2;
      if (sd1 < 0 || sd2 < 0)
        {
          Beep();
          sprintf(msg, "ERROR: LineDef #%d has only one side", cur->objnum);
          Notify(-1, -1, msg, NULL);
          return;
        }
      s1 = level->sidedefs[sd1].sector;
      s2 = level->sidedefs[sd2].sector;
      if (s1 < 0 || s2 < 0)
        {
          Beep();
          sprintf(msg, "ERROR: LineDef #%d has two sides, but one", cur->objnum);
          Notify(-1, -1, msg, "side is not bound to any Sector");
          return;
        }
    }
  
  /* then join the Sectors and delete the LineDefs */
  for (cur = *ldlistp; cur; cur = cur->next)
    {
      sd1 = level->linedefs[cur->objnum].sidedef1;
      sd2 = level->linedefs[cur->objnum].sidedef2;
      s1 = level->sidedefs[sd1].sector;
      s2 = level->sidedefs[sd2].sector;
      slist = NULL;
      SelectObject(&slist, s2);
      SelectObject(&slist, s1);
      MergeSectors(level, &slist);
      ForgetSelection(&slist);
    }
  DeleteObjects(level, OBJ_LINEDEFS, ldlistp);
}



/*
   Create LineDefs between the selected Vertices (ordered).
   On exit, the LineDefs are selected (assuming that the edit mode changes).
*/

void CreateLineDefs(LevelPtr level, SelPtr *vlist)
{
  SelPtr cur;
  Int16  ld;
  Int16  firstv;

  /* if there are 3 Vertices or more, remember the first one (for closing) */
  if ((*vlist)->next->next != NULL)
    firstv = (*vlist)->objnum;
  else
    firstv = -1;

  /* create LineDefs between the Vertices */
  for (cur = *vlist; cur->next; cur = cur->next)
    {
      /* check if there is already a LineDef between the two Vertices */
      for (ld = 0; ld < level->num_linedefs; ld++)
        if ((level->linedefs[ld].start == cur->next->objnum && level->linedefs[ld].end == cur->objnum)
            || (level->linedefs[ld].end == cur->next->objnum && level->linedefs[ld].start == cur->objnum))
          break;
      if (ld < level->num_linedefs)
        cur->objnum = ld;
      else
        {
          InsertObject(level, OBJ_LINEDEFS, -1, 0, 0);
          ld = level->num_linedefs - 1;
          level->linedefs[ld].start = cur->next->objnum;
          level->linedefs[ld].end = cur->objnum;
          cur->objnum = ld;
        }
    }

  /* close the polygon if there are more than 2 Vertices */
  if (firstv >= 0)
    {
      for (ld = 0; ld < level->num_linedefs; ld++)
        if ((level->linedefs[ld].start == firstv && level->linedefs[ld].end == cur->objnum)
            || (level->linedefs[ld].end == firstv && level->linedefs[ld].start == cur->objnum))
          break;
      if (ld < level->num_linedefs)
        cur->objnum = ld;
      else
        {
          InsertObject(level, OBJ_LINEDEFS, -1, 0, 0);
          ld = level->num_linedefs - 1;
          level->linedefs[ld].start = firstv;
          level->linedefs[ld].end = cur->objnum;
          cur->objnum = ld;
        }
    }
  else
    UnSelectObject(vlist, cur->objnum);
}


/*
   Create a Sector using all the selected LineDefs.
   On exit, only the sector is selected (assuming that the edit mode changes).
*/

void CreateSector(LevelPtr level, SelPtr *ldlist)
{
  SelPtr cur;
  Int16  s0;

  for (cur = *ldlist; cur; cur = cur->next)
    if (level->linedefs[cur->objnum].sidedef1 >= 0 && level->linedefs[cur->objnum].sidedef2 >= 0)
      {
        char msg[80];

        Beep();
        sprintf(msg, "LineDef #%d already has two SideDefs", cur->objnum);
        Notify(-1, -1, "Error: cannot add the new Sector", msg);
        break;
      }
  if (cur == NULL)
    {
      InsertObject(level, OBJ_SECTORS, -1, 0, 0);
      s0 = level->num_sectors - 1;
      for (cur = *ldlist; cur; cur = cur->next)
        {
          Int16 sd;

          InsertObject(level, OBJ_SIDEDEFS, -1, 0, 0);
          level->sidedefs[level->num_sidedefs - 1].sector = s0;
          sd = level->linedefs[cur->objnum].sidedef1;
          if (sd >= 0)
            {
              Int16 s;

              s = level->sidedefs[sd].sector;
              if (s >= 0)
                {
                  level->sectors[s0].floorh = level->sectors[s].floorh;
                  level->sectors[s0].ceilh = level->sectors[s].ceilh;
                  strncpy(level->sectors[s0].floort, level->sectors[s].floort, 8);
                  strncpy(level->sectors[s0].ceilt, level->sectors[s].ceilt, 8);
                  level->sectors[s0].light = level->sectors[s].light;
                }
              level->linedefs[cur->objnum].sidedef2 = level->num_sidedefs - 1;
              level->linedefs[cur->objnum].flags = 4;
              strncpy(level->sidedefs[level->num_sidedefs - 1].tex3, "-", 8);
              strncpy(level->sidedefs[sd].tex3, "-", 8);
            }
          else
            {
              /*! ... */
/* #error  */

              level->linedefs[cur->objnum].sidedef1 = level->num_sidedefs - 1;
            }
        }
      ForgetSelection(ldlist);
      SelectObject(ldlist, s0);
    }
}



/*
   Turn a Sector into a door: change the LineDefs and SideDefs.
*/

void MakeDoorFromSector(LevelPtr level, Int16 sector)
{
  Int16  sd1, sd2;
  Int16  n, s;
  SelPtr ldok, ldflip, ld1s;
  
  ldok = NULL;
  ldflip = NULL;
  ld1s = NULL;
  s = 0;
  /* build lists of LineDefs that border the Sector */
  for (n = 0; n < level->num_linedefs; n++)
    {
      sd1 = level->linedefs[n].sidedef1;
      sd2 = level->linedefs[n].sidedef2;
      if (sd1 >= 0 && sd2 >= 0)
        {
          if (level->sidedefs[sd2].sector == sector)
            {
              SelectObject(&ldok, n); /* already ok */
              s++;
            }
          if (level->sidedefs[sd1].sector == sector)
            {
              SelectObject(&ldflip, n); /* must be flipped */
              s++;
            }
        }
      else if (sd1 >= 0 && sd2 < 0)
        {
          if (level->sidedefs[sd1].sector == sector)
            SelectObject(&ld1s, n); /* wall (one-sided) */
        }
    }
  /* a normal door has two sides... */
  if (s < 2)
    {
      Beep();
      Notify(-1, -1, "The door must be connected to two other Sectors.", NULL);
      ForgetSelection(&ldok);
      ForgetSelection(&ldflip);
      ForgetSelection(&ld1s);
      return;
    }
  if ((s > 2) && !(Config.expert || Confirm(-1, -1, "The door will have more than two sides.", "Do you still want to create it?")))
    {
      ForgetSelection(&ldok);
      ForgetSelection(&ldflip);
      ForgetSelection(&ld1s);
      return;
    }
  /* flip the LineDefs that have the wrong orientation */
  if (ldflip != NULL)
    FlipLineDefs(level, ldflip, TRUE);
  /* merge the two selection lists */
  while (ldflip != NULL)
    {
      if (!IsSelected(ldok, ldflip->objnum))
        SelectObject(&ldok, ldflip->objnum);
      UnSelectObject(&ldflip, ldflip->objnum);
    }
  /* change the LineDefs and SideDefs */
  while (ldok != NULL)
    {
      /* give the "normal door" type and flags to the LineDef */
      n = ldok->objnum;
      level->linedefs[n].type = 1;
      level->linedefs[n].flags = 0x04;
      sd1 = level->linedefs[n].sidedef1;
      sd2 = level->linedefs[n].sidedef2;
      /* adjust the textures for the SideDefs */
      if (strncmp(level->sidedefs[sd1].tex3, "-", 8))
        {
          if (!strncmp(level->sidedefs[sd1].tex1, "-", 8))
            strncpy(level->sidedefs[sd1].tex1, level->sidedefs[sd1].tex3, 8);
          strncpy(level->sidedefs[sd1].tex3, "-", 8);
        }
      if (!strncmp(level->sidedefs[sd1].tex1, "-", 8))
        strncpy(level->sidedefs[sd1].tex1, Config.doorFaceTexture, 8);
      strncpy(level->sidedefs[sd2].tex3, "-", 8);
      UnSelectObject(&ldok, n);
    }
  while (ld1s != NULL)
    {
      /* give the "door side" flags to the LineDef */
      n = ld1s->objnum;
      level->linedefs[n].flags = 0x11;
      sd1 = level->linedefs[n].sidedef1;
      /* adjust the textures for the SideDef */
      if (!strncmp(level->sidedefs[sd1].tex3, "-", 8))
        strncpy(level->sidedefs[sd1].tex3, Config.doorTrakTexture, 8);
      strncpy(level->sidedefs[sd1].tex1, "-", 8);
      strncpy(level->sidedefs[sd1].tex2, "-", 8);
      UnSelectObject(&ld1s, n);
    }
  /* adjust the ceiling height */
  level->sectors[sector].ceilh = level->sectors[sector].floorh;
}


/*
   Turn a Sector into a window: change the LineDefs and SideDefs.
*/

void MakeWindowFromSector(LevelPtr level, Int16 sector)
{
  Int16  sd1, sd2, fl, cl;
  Int16  n, s;
  SelPtr ldok, ldflip, ld1s;
  
  ldok = NULL;
  ldflip = NULL;
  ld1s = NULL;
  s = 0;
  /* build lists of LineDefs that border the Sector */
  for (n = 0; n < level->num_linedefs; n++)
    {
      sd1 = level->linedefs[n].sidedef1;
      sd2 = level->linedefs[n].sidedef2;
      if (sd1 >= 0 && sd2 >= 0)
        {
          if (level->sidedefs[sd2].sector == sector)
            {
              SelectObject(&ldok, n); /* already ok */
              s++;
            }
          if (level->sidedefs[sd1].sector == sector)
            {
              SelectObject(&ldflip, n); /* must be flipped */
              s++;
            }
        }
      else if (sd1 >= 0 && sd2 < 0)
        {
          if (level->sidedefs[sd1].sector == sector)
            SelectObject(&ld1s, n); /* wall (one-sided) */
        }
    }
  /* a normal window has two sides... */
  if (s < 2)
    {
      Beep();
      Notify(-1, -1, "The window must be connected to two other Sectors.", NULL);
      ForgetSelection(&ldok);
      ForgetSelection(&ldflip);
      ForgetSelection(&ld1s);
      return;
    }
  if ((s > 2) && !(Config.expert || Confirm(-1, -1, "The window will have more than two sides.", "Do you still want to create it?")))
    {
      ForgetSelection(&ldok);
      ForgetSelection(&ldflip);
      ForgetSelection(&ld1s);
      return;
    }
  
  fl = cl = 32;

  if (!Input2Numbers(-1, -1, "Floor height", "Ceiling height", 512, 512, &fl, &cl))
    {
      ForgetSelection(&ldok);
      ForgetSelection(&ldflip);
      ForgetSelection(&ld1s);
      return;
    }
  
  if (((level->sectors[sector].ceilh + cl) - (level->sectors[sector].floorh + fl)) < 4)
    {
      Beep();
      Notify(-1, -1, "The size of the window must >= 4.", NULL);
      ForgetSelection(&ldok);
      ForgetSelection(&ldflip);
      ForgetSelection(&ld1s);
      return;
    }
  
  level->sectors[sector].floorh += fl;
  level->sectors[sector].ceilh -= cl;
  
  /* flip the LineDefs that have the wrong orientation */
  if (ldflip != NULL)
    FlipLineDefs(level, ldflip, TRUE);
  /* merge the two selection lists */
  while (ldflip != NULL)
    {
      if (!IsSelected(ldok, ldflip->objnum))
        SelectObject(&ldok, ldflip->objnum);
      UnSelectObject(&ldflip, ldflip->objnum);
    }
  /* change the LineDefs and SideDefs */
  while (ldok != NULL)
    {
      /* give the "normal unpegged low & hi" type and flags to the LineDef */
      n = ldok->objnum;
      level->linedefs[n].type = 0;
      level->linedefs[n].flags = 0x1d; /* impassable low pegged up pegged 2 sided */
      sd1 = level->linedefs[n].sidedef1; /* outside */
      sd2 = level->linedefs[n].sidedef2; /* inside */
      /* adjust the textures for the SideDefs */
      if (strncmp(level->sidedefs[sd1].tex3, "-", 8))
        {
          strncpy(level->sidedefs[sd1].tex1, level->sidedefs[sd1].tex3, 8);
          strncpy(level->sidedefs[sd1].tex2, level->sidedefs[sd1].tex3, 8);
          strncpy(level->sidedefs[sd1].tex3, "-", 8);
        }
      else
        {
          if (!strncmp(level->sidedefs[sd1].tex1, "-", 8))
            strncpy(level->sidedefs[sd1].tex1, Config.wallTexture, 8);
          if (!strncmp(level->sidedefs[sd1].tex2, "-", 8))
            strncpy(level->sidedefs[sd1].tex2, Config.wallTexture, 8);
        }
      
      strncpy(level->sidedefs[sd2].tex3, "-", 8);
      UnSelectObject(&ldok, n);
    }
  
  while (ld1s != NULL)
    {
      /* give the "Window side" flags to the LineDef */
      n = ld1s->objnum;
      level->linedefs[n].flags = 0x1;
      sd1 = level->linedefs[n].sidedef1;
      /* adjust the textures for the SideDef */
      if (!strncmp(level->sidedefs[sd1].tex3, "-", 8))
        strncpy(level->sidedefs[sd1].tex3, Config.wallTexture, 8);
      strncpy(level->sidedefs[sd1].tex1, "-", 8);
      strncpy(level->sidedefs[sd1].tex2, "-", 8);
      UnSelectObject(&ld1s, n);
    }
}


/*
   Turn a Sector into a lift: change the LineDefs and SideDefs.
*/

void MakeLiftFromSector(LevelPtr level, Int16 sector)
{
  Int16  sd1, sd2;
  Int16  n, s, tag;
  SelPtr ldok, ldflip, ld1s;
  SelPtr sect, curs;
  Int16  minh, maxh;

  ldok = NULL;
  ldflip = NULL;
  ld1s = NULL;
  sect = NULL;
  /* build lists of LineDefs that border the Sector */
  for (n = 0; n < level->num_linedefs; n++)
    {
      sd1 = level->linedefs[n].sidedef1;
      sd2 = level->linedefs[n].sidedef2;
      if (sd1 >= 0 && sd2 >= 0)
        {
          if (level->sidedefs[sd2].sector == sector)
            {
              SelectObject(&ldok, n); /* already ok */
              s = level->sidedefs[sd1].sector;
              if (s != sector && !IsSelected(sect, s))
                SelectObject(&sect, s);
            }
          if (level->sidedefs[sd1].sector == sector)
            {
              SelectObject(&ldflip, n); /* will be flipped */
              s = level->sidedefs[sd2].sector;
              if (s != sector && !IsSelected(sect, s))
                SelectObject(&sect, s);
            }
        }
      else if (sd1 >= 0 && sd2 < 0)
        {
          if (level->sidedefs[sd1].sector == sector)
            SelectObject(&ld1s, n); /* wall (one-sided) */
        }
    }
  /* there must be a way to go on the lift... */
  if (sect == NULL)
    {
      Beep();
      Notify(-1, -1, "The lift must be connected to at least one other Sector.", NULL);
      ForgetSelection(&ldok);
      ForgetSelection(&ldflip);
      ForgetSelection(&ld1s);
      return;
    }
  /* flip the LineDefs that have the wrong orientation */
  if (ldflip != NULL)
    FlipLineDefs(level, ldflip, TRUE);
  /* merge the two selection lists */
  while (ldflip != NULL)
    {
      if (!IsSelected(ldok, ldflip->objnum))
        SelectObject(&ldok, ldflip->objnum);
      UnSelectObject(&ldflip, ldflip->objnum);
    }
  /* find a free tag number */
  tag = FindFreeTag(level);
  /* find the minimum altitude */
  minh = 32767;
  maxh = -32767;
  for (curs = sect; curs; curs = curs->next)
    {
      if (level->sectors[curs->objnum].floorh < minh)
        minh = level->sectors[curs->objnum].floorh;
      if (level->sectors[curs->objnum].floorh > maxh)
        maxh = level->sectors[curs->objnum].floorh;
    }
  ForgetSelection(&sect);

  /* change the Sector altitude if necessary */
  if (level->sectors[sector].floorh < maxh)
    level->sectors[sector].floorh = maxh;

  /* change the lift's ceiling height if necessary */
  if (level->sectors[sector].ceilh < maxh + 56)
    level->sectors[sector].ceilh = maxh + 56;

  /* assign the new tag number to the lift */
  level->sectors[sector].tag = tag;

  /* change the LineDefs and SideDefs */
  while (ldok != NULL)
    {
      /* give the "lower lift" type and flags to the LineDef */
      n = ldok->objnum;
      level->linedefs[n].type = 62; /* lower lift (switch) */
      level->linedefs[n].flags = 0x04;
      level->linedefs[n].tag = tag;
      sd1 = level->linedefs[n].sidedef1;
      sd2 = level->linedefs[n].sidedef2;
      /* adjust the textures for the SideDefs visible from the outside */
      if (strncmp(level->sidedefs[sd1].tex3, "-", 8))
        {
          if (!strncmp(level->sidedefs[sd1].tex2, "-", 8))
            strncpy(level->sidedefs[sd1].tex2, level->sidedefs[sd1].tex3, 8);
          strncpy(level->sidedefs[sd1].tex3, "-", 8);
        }
      if (!strncmp(level->sidedefs[sd1].tex2, "-", 8))
        strncpy(level->sidedefs[sd1].tex2, Config.liftFaceTexture, 8);
      /* adjust the textures for the SideDef visible from the lift */
      strncpy(level->sidedefs[sd2].tex3, "-", 8);
      s = level->sidedefs[sd1].sector;
      if (level->sectors[s].floorh > minh)
        {
          if (strncmp(level->sidedefs[sd2].tex3, "-", 8))
            {
              if (!strncmp(level->sidedefs[sd2].tex2, "-", 8))
                strncpy(level->sidedefs[sd2].tex2, level->sidedefs[sd1].tex3, 8);
              strncpy(level->sidedefs[sd2].tex3, "-", 8);
            }
          if (!strncmp(level->sidedefs[sd2].tex2, "-", 8))
            strncpy(level->sidedefs[sd2].tex2, Config.liftFaceTexture, 8);
        }
      else
        strncpy(level->sidedefs[sd2].tex2, "-", 8);
      strncpy(level->sidedefs[sd2].tex3, "-", 8);

      /* if the ceiling of the Sector is lower than that of the lift */
      if (level->sectors[s].ceilh < level->sectors[sector].ceilh)
        {
          if (strncmp(level->sidedefs[sd2].tex1, "-", 8))
            strncpy(level->sidedefs[sd2].tex1, Config.upperTexture, 8);
        }

      /* if the floor of the Sector is above the lift */
      if (level->sectors[s].floorh >= level->sectors[sector].floorh)
        {
          level->linedefs[n].type = 88; /* lower lift (walk through) */
          /* flip it, just for fun */
          curs = NULL;
          SelectObject(&curs, n);
          FlipLineDefs(level, curs, TRUE);
          ForgetSelection(&curs);
        }
      /* done with this LineDef */
      UnSelectObject(&ldok, n);
    }
  while (ld1s != NULL)
    {
      /* these are the lift walls (one-sided) */
      n = ld1s->objnum;
      level->linedefs[n].flags = 0x01;
      sd1 = level->linedefs[n].sidedef1;
      /* adjust the textures for the SideDef */
      if (!strncmp(level->sidedefs[sd1].tex3, "-", 8))
        strncpy(level->sidedefs[sd1].tex3, Config.wallTexture, 8);
      strncpy(level->sidedefs[sd1].tex1, "-", 8);
      strncpy(level->sidedefs[sd1].tex2, "-", 8);
      UnSelectObject(&ld1s, n);
    }
}


/*
   Distribute sector floor heights.
*/

void DistributeSectorFloors(LevelPtr level, SelPtr obj)
{
  SelPtr cur;
  Int16  n, num, floor1h, floor2h;

  num = 0;
  for (cur = obj; cur->next; cur = cur->next)
    num++;

  floor1h = level->sectors[obj->objnum].floorh;
  floor2h = level->sectors[cur->objnum].floorh;

  n = 0;
  for (cur = obj; cur; cur = cur->next)
    {
      level->sectors[cur->objnum].floorh = floor1h + n * (floor2h - floor1h) / num;
      n++;
    }
  level->made_changes = TRUE;
}


/*
   Distribute sector ceiling heights.
*/

void DistributeSectorCeilings(LevelPtr level, SelPtr obj)
{
  SelPtr cur;
  Int16  n, num, ceil1h, ceil2h;
  
  num = 0;
  for (cur = obj; cur->next; cur = cur->next)
    num++;
  
  ceil1h = level->sectors[obj->objnum].ceilh;
  ceil2h = level->sectors[cur->objnum].ceilh;
  
  n = 0;
  for (cur = obj; cur; cur = cur->next)
    {
      level->sectors[cur->objnum].ceilh = ceil1h + n * (ceil2h - ceil1h) / num;
      n++;
    }
  level->made_changes = TRUE;
}



/*
   Distribute sector light levels (brightness).
*/

void DistributeSectorLights(LevelPtr level, SelPtr obj)
{
  SelPtr cur;
  Int16  n, num, light1, light2;
  
  num = 0;
  for (cur = obj; cur->next; cur = cur->next)
    num++;
  
  light1 = level->sectors[obj->objnum].light;
  light2 = level->sectors[cur->objnum].light;
  
  n = 0;
  for (cur = obj; cur; cur = cur->next)
    {
      level->sectors[cur->objnum].light = light1 + n * (light2 - light1) / num;
      n++;
    }
  level->made_changes = TRUE;
}


/* end of file */
