/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* 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 3 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, see <http://www.gnu.org/licenses/>.

--- HISTORICAL COMMENTS FOLLOW ---

 * $Logfile: /DescentIII/Main/editor/TriggerDialog.cpp $
 * $Revision: 1.1.1.1 $
 * $Date: 2003-08-26 03:57:39 $
 * $Author: kevinb $
 *
 * Trigger keypad dialog.
 *
 * $Log: not supported by cvs2svn $
 *
 * 31    6/15/99 5:33p Matt
 * Fixed small stupid bug.
 *
 * 30    4/03/99 7:55p Matt
 * Use new keypad update system instead of timer to update.
 *
 * 29    4/03/99 3:41p Kevin
 * Put in code for "Edit Script" button
 *
 * 28    3/30/99 4:11p Matt
 * Set world changed flag when trigger deleted.
 *
 * 27    3/27/99 7:10p Matt
 * Strip leading & trailing spaces before check for duplicate names.
 *
 * 26    3/23/99 5:52p Matt
 * Added clutter activators for triggers
 *
 * 25    3/22/99 6:59p Matt
 * Fixed stupid bug:  was assigning UVs to floating trigger face before
 * computing normal.
 *
 * 24    3/01/99 10:39a Matt
 * Strip leading and trailing spaces from object, trigger, and room names
 * on level load and when the names are entered.
 *
 * 23    2/28/99 10:40p Matt
 * Check for name already in use when entering a name.
 *
 * 22    2/11/99 8:30p Matt
 *
 * 21    2/02/99 11:54p Luke
 * Fixed small bug (MattT on Luke's machine)
 *
 * 20    1/21/99 11:34a Matt
 * Got rid of portal triggers.  Since we don't have multi-face portals, a
 * face trigger works fine for a portal.  Also fixed a few editor/trigger
 * bugs.
 *
 * 19    1/15/99 10:51a Matt
 * Disable rename button if no current trigger
 *
 * 18    1/14/99 11:06a Matt
 * Added names to triggers
 *
 * 17    1/08/99 2:56p Samir
 * Ripped out OSIRIS1.
 *
 * 16    10/13/98 3:23p Matt
 * Fixed a problem with the add portal trigger button being enabled when
 * there was already a trigger on the portal's face.  Also, fixed and
 * added some asserts.
 *
 * 15    9/01/98 12:04p Matt
 * Ripped out multi-face portal code
 *
 * 14    6/23/98 2:43p Matt
 * Changed calls to OutrageMessageBox() & Debug_MessageBox() to deal with
 * int return value (instead of bool).
 *
 * 13    2/04/98 6:23p Matt
 * Changed object room number to indicate a terrain cell via a flag.  Got
 * rid of the object flag which used to indicate terrain.
 *
 * 12    1/23/98 5:54p Samir
 * Added new style script support to triggers.
 *
 * 11    1/20/98 4:12p Samir
 * New script housekeeping system.
 *
 * 10    1/15/98 7:34p Matt
 * Revamped error checking when computing face normals
 *
 * 9     9/24/97 6:18p Samir
 * Use script names instead of script id values to identify scripts.
 *
 * 8     9/24/97 2:58p Samir
 * New ScriptSelect.
 *
 * 7     9/15/97 11:51a Matt
 * Disable 'select by number' if no triggers
 *
 * 6     9/15/97 11:38a Matt
 * Added better trigger selection functions
 *
 * 5     9/14/97 11:07p Matt
 * Use new InputNumber() function
 *
 * 4     9/12/97 3:59p Matt
 * Lots of changes to trigger dialog
 *
 * 3     9/08/97 10:01a Matt
 * Ripped out old trigger code
 *
 * $NoKeywords: $
 */

#include <stdlib.h>

#include "stdafx.h"
#include "editor.h"
#include "TriggerDialog.h"
#include "trigger.h"
#include "EditLineDialog.h"
#include "render.h"
#include "trigger.h"
#include "HView.h"
#include "FindIntersection.h"
#include "erooms.h"
//@@#include "d3x.h"

#include "DallasMainDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

int Current_trigger = -1; // current editing trigger

/////////////////////////////////////////////////////////////////////////////
// CTriggerDialog dialog

CTriggerDialog::CTriggerDialog(CWnd *pParent /*=NULL*/) : CKeypadDialog(CTriggerDialog::IDD, pParent) {
  //{{AFX_DATA_INIT(CTriggerDialog)
  // NOTE: the ClassWizard will add member initialization here
  //}}AFX_DATA_INIT
}

void CTriggerDialog::DoDataExchange(CDataExchange *pDX) {
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CTriggerDialog)
  // NOTE: the ClassWizard will add DDX and DDV calls here
  //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CTriggerDialog, CDialog)
//{{AFX_MSG_MAP(CTriggerDialog)
ON_WM_SIZE()
ON_WM_VSCROLL()
ON_WM_HSCROLL()
ON_WM_PAINT()
ON_WM_HELPINFO()
ON_BN_CLICKED(IDC_TRIG_ADD_TO_CURFACE, OnTrigAddToCurface)
ON_BN_CLICKED(IDC_TRIG_GOTO, OnTrigGoto)
ON_BN_CLICKED(IDC_TRIG_DELETE, OnTrigDelete)
ON_BN_CLICKED(IDC_TRIG_ADD_TO_CURPORTAL, OnTrigAddToCurportal)
ON_BN_CLICKED(IDC_TRIG_ADD_FLOATING, OnTrigAddFloating)
ON_BN_CLICKED(IDC_TRIG_RENDER_FLOATING, OnTrigRenderFloating)
ON_BN_CLICKED(IDC_TRIG_ACTIV_PLAYER, OnTrigActivPlayer)
ON_BN_CLICKED(IDC_TRIG_ACTIV_PLAYER_WEAPONS, OnTrigActivPlayerWeapons)
ON_BN_CLICKED(IDC_TRIG_ACTIV_ROBOT_WEAPONS, OnTrigActivRobotWeapons)
ON_BN_CLICKED(IDC_TRIG_ACTIV_ROBOTS, OnTrigActivRobots)
ON_BN_CLICKED(IDC_TRIG_FLOAT_NARROWER, OnTrigFloatNarrower)
ON_BN_CLICKED(IDC_TRIG_FLOAT_SHORTER, OnTrigFloatShorter)
ON_BN_CLICKED(IDC_TRIG_FLOAT_TALLER, OnTrigFloatTaller)
ON_BN_CLICKED(IDC_TRIG_FLOAT_WIDER, OnTrigFloatWider)
ON_BN_CLICKED(IDC_TRIG_ONESHOT, OnTrigOneShot)
ON_BN_CLICKED(IDC_TRIG_NEXT_PORTAL, OnTrigNextPortal)
ON_BN_CLICKED(IDC_TRIG_NEXT_IN_MINE, OnTrigNextInMine)
ON_BN_CLICKED(IDC_TRIG_NEXT_IN_ROOM, OnTrigNextInRoom)
ON_BN_CLICKED(IDC_TRIG_PREV_IN_MINE, OnTrigPrevInMine)
ON_BN_CLICKED(IDC_TRIG_PREV_IN_ROOM, OnTrigPrevInRoom)
ON_BN_CLICKED(IDC_TRIG_EDIT_NAME, OnTrigEditName)
ON_BN_CLICKED(IDC_TRIG_EDIT_SCRIPT, OnTrigEditScript)
ON_BN_CLICKED(IDC_TRIG_ACTIV_CLUTTER, OnTrigActivClutter)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTriggerDialog message handlers

void CTriggerDialog::OnSize(UINT nType, int cx, int cy) {
  CKeypadDialog::OnSize(nType, cx, cy);

  //	make sure we can see a few items in the combo box when we drop it down.
  RECT rect;

  CComboBox *box = (CComboBox *)GetDlgItem(IDC_TRIGGERTYPE);
  if (box) {
    box->GetClientRect(&rect);
    box->SetWindowPos(NULL, 0, 0, rect.right - rect.left, box->GetItemHeight(0) * 10, SWP_NOMOVE | SWP_NOZORDER);
  }
}

void CTriggerDialog::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar) {
  // TODO: Add your message handler code here and/or call default

  CKeypadDialog::OnVScroll(nSBCode, nPos, pScrollBar);
}

void CTriggerDialog::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar) {
  // TODO: Add your message handler code here and/or call default

  CKeypadDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}

trigger *FindTrigger(int roomnum, int facenum);

int FindNextTrigInRoom(int roomnum, int start) {
  if ((start == -1) || (Triggers[start].roomnum != roomnum))
    start = 0;
  else
    start++;

  for (int i = 0; i < Num_triggers; i++) {
    int n = (start + i) % Num_triggers;

    if (Triggers[n].roomnum == roomnum)
      return n;
  }

  // Couldn't find one
  return -1;
}

int FindPrevTrigInRoom(int roomnum, int start) {
  if (Triggers[start].roomnum != roomnum)
    start = 0;
  else
    start--;

  for (int i = Num_triggers; i > 0; i--) {
    int n = (start + i) % Num_triggers;

    if (Triggers[n].roomnum == roomnum)
      return n;
  }

  // Couldn't find one
  return -1;
}

void CTriggerDialog::UpdateDialog() {
  bool portal = 0, floating = 0;
  static int last_curroom = -1, last_curface = -1, last_curportal = -1;

  if (Current_trigger >= Num_triggers)
    Current_trigger--;

  // If current room/face/portal change, select current trigger from current room/face/portal
  if ((ROOMNUM(Curroomp) != last_curroom) || (Curface != last_curface)) {
    if (Curroomp->faces[Curface].flags & FF_HAS_TRIGGER)
      Current_trigger = (FindTrigger(ROOMNUM(Curroomp), Curface) - Triggers);
  } else if ((Curportal != -1) && (Curportal != last_curportal))
    if (Curroomp->portals[Curportal].flags & FF_HAS_TRIGGER)
      Current_trigger = (FindTrigger(ROOMNUM(Curroomp), Curroomp->portals[Curportal].portal_face) - Triggers);
  last_curroom = ROOMNUM(Curroomp);
  last_curface = Curface, last_curportal = Curportal;

  // Enable/Disable some buttons, etc.
  ((CButton *)GetDlgItem(IDC_TRIG_NEXT_IN_MINE))->EnableWindow(Num_triggers > 1);
  ((CButton *)GetDlgItem(IDC_TRIG_PREV_IN_MINE))->EnableWindow(Num_triggers > 1);
  ((CButton *)GetDlgItem(IDC_TRIG_GOTO))->EnableWindow(Num_triggers > 0);
  ((CButton *)GetDlgItem(IDC_TRIG_DELETE))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_EDIT_SCRIPT))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_EDIT_NAME))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_ADD_TO_CURFACE))->EnableWindow(!(Curroomp->faces[Curface].flags & FF_HAS_TRIGGER));
  ((CButton *)GetDlgItem(IDC_TRIG_ADD_TO_CURPORTAL))
      ->EnableWindow((Curportal != -1) &&
                     !(Curroomp->faces[Curroomp->portals[Curportal].portal_face].flags & FF_HAS_TRIGGER) &&
                     !(Curroomp->faces[Curroomp->portals[Curportal].portal_face].flags & FF_HAS_TRIGGER));
  ((CButton *)GetDlgItem(IDC_TRIG_CURRENT_NUM))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_CURRENT_TYPE))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_CURRENT_ROOM))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_CURRENT_FACE))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_CURRENT_FACE))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_CURRENT_TRIG_PORTAL))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_ACTIVATORS))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_PLAYER))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_PLAYER_WEAPONS))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_ROBOT_WEAPONS))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_ROBOTS))->EnableWindow(Current_trigger != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_ONESHOT))->EnableWindow(Current_trigger != -1);

  int n = FindNextTrigInRoom(ROOMNUM(Curroomp), Current_trigger);
  ((CButton *)GetDlgItem(IDC_TRIG_NEXT_IN_ROOM))->EnableWindow((n != -1) && (n != Current_trigger));
  ((CButton *)GetDlgItem(IDC_TRIG_PREV_IN_ROOM))->EnableWindow((n != -1) && (n != Current_trigger));

  // Update current trigger info
  if (Current_trigger > -1) {
    trigger *tp = &Triggers[Current_trigger];
    room *rp = &Rooms[tp->roomnum];
    face *fp = &rp->faces[tp->facenum];

    portal = (fp->portal_num != -1);
    floating = ((fp->flags & FF_FLOATING_TRIG) != 0);
    ASSERT(!(portal && floating)); // Shouldn't be both portal and floating

    PrintToDlgItem(this, IDC_TRIG_CURRENT_NAME, "Name:\t %s", tp->name[0] ? tp->name : "<none>");
    PrintToDlgItem(this, IDC_TRIG_CURRENT_NUM, "Number:\t %d", Current_trigger);
    PrintToDlgItem(this, IDC_TRIG_CURRENT_TYPE, "Type:\t %s", portal ? "PORTAL" : (floating ? "FLOATING" : "FACE"));
    PrintToDlgItem(this, IDC_TRIG_CURRENT_ROOM, "Room:\t%d", tp->roomnum);
    PrintToDlgItem(this, IDC_TRIG_CURRENT_FACE, "Face:\t%d", tp->facenum);
    PrintToDlgItem(this, IDC_TRIG_CURRENT_TRIG_PORTAL, (portal) ? "Portal:\t%d" : "", fp->portal_num);

    ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_PLAYER))->SetCheck(tp->activator & AF_PLAYER);
    ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_PLAYER_WEAPONS))->SetCheck(tp->activator & AF_PLAYER_WEAPON);
    ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_ROBOT_WEAPONS))->SetCheck(tp->activator & AF_ROBOT_WEAPON);
    ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_ROBOTS))->SetCheck(tp->activator & AF_ROBOT);
    ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_CLUTTER))->SetCheck(tp->activator & AF_CLUTTER);

    ((CButton *)GetDlgItem(IDC_TRIG_ONESHOT))->SetCheck(tp->flags & TF_ONESHOT);
  } else {
    PrintToDlgItem(this, IDC_TRIG_CURRENT_NAME, "Name:");
    PrintToDlgItem(this, IDC_TRIG_CURRENT_NUM, "Number:");
    PrintToDlgItem(this, IDC_TRIG_CURRENT_TYPE, "Type:");
    PrintToDlgItem(this, IDC_TRIG_CURRENT_ROOM, "Room:");
    PrintToDlgItem(this, IDC_TRIG_CURRENT_FACE, "Face:");
    PrintToDlgItem(this, IDC_TRIG_CURRENT_TRIG_PORTAL, "");

    ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_PLAYER))->SetCheck(0);
    ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_PLAYER_WEAPONS))->SetCheck(0);
    ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_ROBOT_WEAPONS))->SetCheck(0);
    ((CButton *)GetDlgItem(IDC_TRIG_ACTIV_ROBOTS))->SetCheck(0);

    ((CButton *)GetDlgItem(IDC_TRIG_ONESHOT))->SetCheck(0);
  }

  // Enable/disable floating trig items
  ((CButton *)GetDlgItem(IDC_TRIG_FLOAT_BORDER))->EnableWindow(floating);
  ((CButton *)GetDlgItem(IDC_TRIG_FLOAT_WIDER))->EnableWindow(floating);
  ((CButton *)GetDlgItem(IDC_TRIG_FLOAT_NARROWER))->EnableWindow(floating);
  ((CButton *)GetDlgItem(IDC_TRIG_FLOAT_TALLER))->EnableWindow(floating);
  ((CButton *)GetDlgItem(IDC_TRIG_FLOAT_SHORTER))->EnableWindow(floating);

  // Enable/disable the portal items
  ((CButton *)GetDlgItem(IDC_TRIG_CURRENT_PORTAL))->EnableWindow(Curportal != -1);
  ((CButton *)GetDlgItem(IDC_TRIG_NEXT_PORTAL))
      ->EnableWindow(((Curportal == -1) && (Curroomp->num_portals > 0)) || (Curroomp->num_portals > 1));

  // Update the portal number
  if (Curportal != -1)
    PrintToDlgItem(this, IDC_TRIG_CURRENT_PORTAL, "Current Portal:\t%d", Curportal);
  else
    PrintToDlgItem(this, IDC_TRIG_CURRENT_PORTAL, "Current Portal:");
}

void CTriggerDialog::OnPaint() {
  CPaintDC dc(this); // device context for painting

  UpdateDialog();
}

BOOL CTriggerDialog::OnInitDialog() {
  CDialog::OnInitDialog();

  // Check current trigger
  if (Current_trigger >= Num_triggers)
    Current_trigger = Num_triggers - 1;

  // Update global checkboxes
  (((CButton *)GetDlgItem(IDC_TRIG_RENDER_FLOATING))->SetCheck(Render_floating_triggers));

  return TRUE; // return TRUE unless you set the focus to a control
               // EXCEPTION: OCX Property Pages should return FALSE
}

void CTriggerDialog::OnOK() {
  CWnd *focus = CWnd::GetFocus();
  if (focus)
    focus->GetNextWindow()->SetFocus();
}

BOOL CTriggerDialog::OnHelpInfo(HELPINFO *pHelpInfo) {
  // TODO: Add your message handler code here and/or call default
  WinHelp(HID_TRIGGERTAB, HELP_CONTEXT);
  return TRUE;
  // return CDialog::OnHelpInfo(pHelpInfo);
}

void CTriggerDialog::OnTrigAddToCurface() {
  ASSERT(!(Curroomp->faces[Curface].flags & FF_HAS_TRIGGER));

  char name[TRIG_NAME_LEN + 1] = "";

  if (!InputString(name, sizeof(name), "Trigger Name", "Enter a name for this trigger"))
    return;

  int activator = (Curroomp->faces[Curface].portal_num != -1) ? AF_PLAYER : AF_PLAYER_WEAPON;

  Current_trigger = AddTrigger(name, ROOMNUM(Curroomp), Curface, activator, NULL);
  UpdateDialog();
}

void SetCurroomFromTrigger() {
  if (ROOMNUM(Curroomp) != Triggers[Current_trigger].roomnum) {

    Curroomp = &Rooms[Triggers[Current_trigger].roomnum];

    Curface = Curedge = Curvert = 0;
    Curportal = -1;
  }

  Curface = Triggers[Current_trigger].facenum;

  if (Curroomp->faces[Curface].portal_num != -1)
    Curportal = Curroomp->faces[Curface].portal_num;

  State_changed = 1;
}

void CTriggerDialog::OnTrigGoto() {
  int n;

  if (InputNumber(&n, "Select Trigger", "Enter trigger number to select", this)) {

    if (n >= Num_triggers) {
      OutrageMessageBox("Invalid trigger number.");
      return;
    }

    Current_trigger = n;

    SetCurroomFromTrigger();

    UpdateDialog();
  }
}

void CTriggerDialog::OnTrigDelete() {
  ASSERT(Current_trigger != -1);

  if (OutrageMessageBox(MBOX_YESNO, "Are you sure you want to delete the current trigger?") != IDYES)
    return;

  trigger *tp = &Triggers[Current_trigger];
  room *rp = &Rooms[tp->roomnum];
  int facenum = tp->facenum;

  // Delete the trigger
  DeleteTrigger(Current_trigger);

  // Delete face if this was a floating trigger
  if (rp->faces[facenum].flags & FF_FLOATING_TRIG) {
    DeleteRoomFace(rp, facenum);
    if (Curface == facenum)
      Curface = (facenum + 1) % rp->num_faces;
  }

  if (Current_trigger == Num_triggers)
    Current_trigger--;

  UpdateDialog();

  World_changed = 1;
}

void CTriggerDialog::OnTrigAddToCurportal() {
  ASSERT(Curportal != -1);
  ASSERT(!(Curroomp->faces[Curroomp->portals[Curportal].portal_face].flags & FF_HAS_TRIGGER));

  char name[TRIG_NAME_LEN + 1] = "";

  if (!InputString(name, sizeof(name), "Trigger Name", "Enter a name for this trigger"))
    return;

  Current_trigger = AddTrigger(name, ROOMNUM(Curroomp), Curroomp->portals[Curportal].portal_face, AF_PLAYER, NULL);
  UpdateDialog();
}

void CTriggerDialog::UpdateKeypad(int mask) {
  if (mask & (KUF_CURROOM_CHANGED + KUF_CURFACE_CHANGED + KUF_CURPORTAL_CHANGED))
    UpdateDialog();
}

#include "erooms.h"

#define TRIG_PLACE_DIST 5.0
#define TRIG_DEFAULT_SIZE 10.0

void CTriggerDialog::OnTrigAddFloating() {
  int facenum, vertnum;
  room *rp;
  face *fp;
  vector center;
  fvi_query fq;
  fvi_info hit_info;
  int fate;

  if (OBJECT_OUTSIDE(Viewer_object)) {
    OutrageMessageBox("Sorry, this operation does not yet work in the terrain.");
    return;
  }

  char name[TRIG_NAME_LEN + 1] = "";

  if (!InputString(name, sizeof(name), "Trigger Name", "Enter a name for this trigger"))
    return;

  // Get center point of new face
  center = Viewer_object->pos + (Viewer_object->orient.fvec * TRIG_PLACE_DIST);

  // Make sure the viewer in in the mine
  if (Viewer_object->flags & OF_OUTSIDE_MINE)
    goto bad_place;

  // Check if position ok
  fq.p0 = &Viewer_object->pos;
  fq.startroom = Viewer_object->roomnum;
  fq.p1 = &center;
  fq.rad = 0;
  fq.thisobjnum = -1;
  fq.ignore_obj_list = NULL;
  fq.flags = 0;
  fate = fvi_FindIntersection(&fq, &hit_info);

  // Bail if there's something in the way, or if the center point is outside
  if ((fate != HIT_NONE) || ROOMNUM_OUTSIDE(hit_info.hit_room)) {

  bad_place:;
    OutrageMessageBox("You cannot place a floating trigger here.");
    return;
  }

  // Get pointer to room the trigger is in
  rp = &Rooms[hit_info.hit_room];

  // Create a new face & new verts
  facenum = RoomAddFaces(rp, 1);
  vertnum = RoomAddVertices(rp, 4);
  fp = &rp->faces[facenum];
  InitRoomFace(fp, 4);

  // Set the vertices for the new face
  rp->verts[vertnum] =
      center - (Viewer_object->orient.rvec * TRIG_DEFAULT_SIZE) + (Viewer_object->orient.uvec * TRIG_DEFAULT_SIZE);
  rp->verts[vertnum + 1] =
      center + (Viewer_object->orient.rvec * TRIG_DEFAULT_SIZE) + (Viewer_object->orient.uvec * TRIG_DEFAULT_SIZE);
  rp->verts[vertnum + 2] =
      center + (Viewer_object->orient.rvec * TRIG_DEFAULT_SIZE) - (Viewer_object->orient.uvec * TRIG_DEFAULT_SIZE);
  rp->verts[vertnum + 3] =
      center - (Viewer_object->orient.rvec * TRIG_DEFAULT_SIZE) - (Viewer_object->orient.uvec * TRIG_DEFAULT_SIZE);

  // Set vertnums for new face
  for (int i = 0; i < 4; i++)
    fp->face_verts[i] = vertnum + i;

  // Assign UVs and normal to new face
  if (!ComputeFaceNormal(rp, facenum))
    Int3(); // Get Matt
  AssignDefaultUVsToRoomFace(rp, facenum);

  // Set floating trig flag
  fp->flags |= FF_FLOATING_TRIG;

  // Add the trigger for the new face
  Current_trigger = AddTrigger(name, ROOMNUM(rp), facenum, AF_PLAYER, NULL);

  // Set the current room:face to the new face
  SetCurroomFromTrigger();

  // We're done
  UpdateDialog();
}

void CTriggerDialog::OnTrigRenderFloating() {
  Render_floating_triggers = (((CButton *)GetDlgItem(IDC_TRIG_RENDER_FLOATING))->GetCheck() != 0);
  State_changed = 1;
}

void CTriggerDialog::OnTrigActivPlayer() {
  trigger *tp = &Triggers[Current_trigger];

  if (((CButton *)GetDlgItem(IDC_TRIG_ACTIV_PLAYER))->GetCheck())
    tp->activator |= AF_PLAYER;
  else
    tp->activator &= ~AF_PLAYER;

  World_changed = 1;
}

void CTriggerDialog::OnTrigActivPlayerWeapons() {
  trigger *tp = &Triggers[Current_trigger];

  if (((CButton *)GetDlgItem(IDC_TRIG_ACTIV_PLAYER_WEAPONS))->GetCheck())
    tp->activator |= AF_PLAYER_WEAPON;
  else
    tp->activator &= ~AF_PLAYER_WEAPON;

  World_changed = 1;
}

void CTriggerDialog::OnTrigActivRobotWeapons() {
  trigger *tp = &Triggers[Current_trigger];

  if (((CButton *)GetDlgItem(IDC_TRIG_ACTIV_ROBOT_WEAPONS))->GetCheck())
    tp->activator |= AF_ROBOT_WEAPON;
  else
    tp->activator &= ~AF_ROBOT_WEAPON;

  World_changed = 1;
}

void CTriggerDialog::OnTrigActivRobots() {
  trigger *tp = &Triggers[Current_trigger];

  if (((CButton *)GetDlgItem(IDC_TRIG_ACTIV_ROBOTS))->GetCheck())
    tp->activator |= AF_ROBOT;
  else
    tp->activator &= ~AF_ROBOT;

  World_changed = 1;
}

void ScaleFloatingTrigEdge(int roomnum, int facenum, int v0, int v1, float delta) {
  room *rp = &Rooms[roomnum];
  face *fp = &rp->faces[facenum];
  vector *vv0, *vv1;
  vector deltav;
  float mag;

  // Get pointers to verts
  vv0 = &rp->verts[fp->face_verts[v0]];
  vv1 = &rp->verts[fp->face_verts[v1]];

  // Get delta vec
  deltav = *vv1 - *vv0;
  mag = vm_NormalizeVector(&deltav);

  // Check for min size
  if (mag <= delta)
    return;

  // Now add
  *vv0 -= deltav * delta;
  *vv1 += deltav * delta;
}

#define SCALE_DELTA 1.0

void CTriggerDialog::OnTrigFloatNarrower() {
  ASSERT(Current_trigger != -1);

  trigger *tp = &Triggers[Current_trigger];

  ScaleFloatingTrigEdge(tp->roomnum, tp->facenum, 0, 1, -SCALE_DELTA);
  ScaleFloatingTrigEdge(tp->roomnum, tp->facenum, 2, 3, -SCALE_DELTA);

  World_changed = 1;
}

void CTriggerDialog::OnTrigFloatShorter() {
  ASSERT(Current_trigger != -1);

  trigger *tp = &Triggers[Current_trigger];

  ScaleFloatingTrigEdge(tp->roomnum, tp->facenum, 1, 2, -SCALE_DELTA);
  ScaleFloatingTrigEdge(tp->roomnum, tp->facenum, 3, 0, -SCALE_DELTA);

  World_changed = 1;
}

void CTriggerDialog::OnTrigFloatTaller() {
  ASSERT(Current_trigger != -1);

  trigger *tp = &Triggers[Current_trigger];

  ScaleFloatingTrigEdge(tp->roomnum, tp->facenum, 1, 2, SCALE_DELTA);
  ScaleFloatingTrigEdge(tp->roomnum, tp->facenum, 3, 0, SCALE_DELTA);

  World_changed = 1;
}

void CTriggerDialog::OnTrigFloatWider() {
  ASSERT(Current_trigger != -1);

  trigger *tp = &Triggers[Current_trigger];

  ScaleFloatingTrigEdge(tp->roomnum, tp->facenum, 0, 1, SCALE_DELTA);
  ScaleFloatingTrigEdge(tp->roomnum, tp->facenum, 2, 3, SCALE_DELTA);

  World_changed = 1;
}

void CTriggerDialog::OnTrigOneShot() {
  trigger *tp = &Triggers[Current_trigger];

  if (((CButton *)GetDlgItem(IDC_TRIG_ONESHOT))->GetCheck())
    tp->flags |= TF_ONESHOT;
  else
    tp->flags &= ~TF_ONESHOT;

  World_changed = 1;
}

void CTriggerDialog::OnTrigNextPortal() {
  if (Curroomp->num_portals) {
    Curportal = (Curportal + 1) % Curroomp->num_portals;
    State_changed = 1;
    UpdateDialog();
  }
}

void CTriggerDialog::OnTrigNextInMine() {
  ASSERT(Current_trigger != -1);

  Current_trigger = (Current_trigger + 1) % Num_triggers;

  SetCurroomFromTrigger();

  UpdateDialog();
}

void CTriggerDialog::OnTrigNextInRoom() {
  int n = FindNextTrigInRoom(ROOMNUM(Curroomp), Current_trigger);

  ASSERT(n != -1);

  Current_trigger = n;

  SetCurroomFromTrigger();
}

void CTriggerDialog::OnTrigPrevInMine() {
  ASSERT(Current_trigger != -1);

  if (--Current_trigger < 0)
    Current_trigger += Num_triggers;

  SetCurroomFromTrigger();

  UpdateDialog();
}

void CTriggerDialog::OnTrigPrevInRoom() {
  int n = FindPrevTrigInRoom(ROOMNUM(Curroomp), Current_trigger);

  ASSERT(n != -1);

  Current_trigger = n;

  SetCurroomFromTrigger();
}

#include "osiris_predefs.h"
extern bool StripLeadingTrailingSpaces(char *s);

void CTriggerDialog::OnTrigEditName() {
  ASSERT(Current_trigger != -1);

  trigger *tp = &Triggers[Current_trigger];

  char name[TRIG_NAME_LEN + 1];

  strcpy(name, tp->name);

try_again:;
  if (!InputString(name, sizeof(name), "Trigger Name", "Enter a new name:"))
    return;

  if (StripLeadingTrailingSpaces(name))
    EditorMessageBox("Note: Leading and/or trailing spaces have been removed from this name (\"%s\")", name);

  int n = osipf_FindTriggerName(name);
  if ((n != -1) && (n != Current_trigger)) {
    EditorMessageBox("Trigger %d already has this name.", n);
    goto try_again;
  }

  strcpy(tp->name, name);

  UpdateDialog();
}

void CTriggerDialog::OnTrigEditScript() {

  mprintf(0, "Edit script for trigger %d\n", Current_trigger);

  // Make sure Dallas is open
  if (theApp.m_DallasModelessDlgPtr == NULL) {
    theApp.m_DallasModelessDlgPtr = new CDallasMainDlg;
    theApp.m_DallasModelessDlgPtr->Create(IDD_DALLAS_MAIN_DIALOG, this);
    theApp.m_DallasModelessDlgPtr->ShowWindow(SW_SHOW);
  } else
    theApp.m_DallasModelessDlgPtr->ShowWindow(SW_RESTORE);

  // make sure a trigger was selected
  if (Current_trigger == -1)
    return;

  // Tell Dallas to add a new script with this trigger as the owner
  theApp.m_DallasModelessDlgPtr->m_ScriptOwnerType = TRIGGER_TYPE;
  theApp.m_DallasModelessDlgPtr->m_ScriptOwnerHandle = Current_trigger;
  theApp.m_DallasModelessDlgPtr->PostMessage(WM_HIGHLIGHT_SCRIPTS);
}

void CTriggerDialog::OnTrigActivClutter() {
  trigger *tp = &Triggers[Current_trigger];

  if (((CButton *)GetDlgItem(IDC_TRIG_ACTIV_CLUTTER))->GetCheck())
    tp->activator |= AF_CLUTTER;
  else
    tp->activator &= ~AF_CLUTTER;

  World_changed = 1;
}
