/*
* 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 .
--- HISTORICAL COMMENTS FOLLOW ---
* $Logfile: /DescentIII/Main/editor/ObjectDialog.cpp $
* $Revision: 1.1.1.1 $
* $Date: 2003-08-26 03:57:38 $
* $Author: kevinb $
*
* Object keypad dialog
*
* $Log: not supported by cvs2svn $
*
* 46 5/08/99 1:39a Matt
* Added a function to delete all objects of a certain type, and support
* for placing and attaching groups to the terrain.
*
* 45 4/05/99 3:34p Matt
* Cleaned up player start flags
*
* 44 4/02/99 12:58p Chris
* Change object now preserves names
*
* 43 3/27/99 12:52p Chris
* The editor object placement code now allows objects to be pushed into
* wall (when a toggle box is checked)
*
* 42 2/18/99 2:09p Luke
* Checking in for Matt
*
* 41 2/05/99 1:25p Matt
* Show more info on the status line when an object is selected
*
* 40 2/03/99 1:58a Matt
* Fixed small bug
*
* 39 2/03/99 1:20a Matt
* Made the object combo box on the object tab sort, and replaced the
* listbox of pictures with a big picture of the current item.
*
* 38 1/27/99 11:03a Samir
* added rotate 90 button to object dialog.
*
* 37 1/19/99 6:53p Matt
* Fixed a problem when the viewer was instantaneously moved from inside
* to ourside or vise-versa.
*
* 36 1/15/99 7:52p Chris
* Updated ObjSetPos() to include a f_update_attach_children flag
*
* 35 10/22/98 11:01a Chris
* Added object regrounding
*
* 34 10/08/98 7:17p Chris
* Added object swap types
*
* 33 8/24/98 12:24p Jason
* added waypoints and player start position flags
*
* 32 6/15/98 4:00p Jason
* replaced monochromatic polymodel lighting with rgb lighting
*
* 31 5/18/98 2:56p Matt
* Added code to readjust all ground object (for after the terrain has
* moved).
*
* 30 5/06/98 4:02p Sean
*
* 29 5/06/98 1:55p Sean
*
* 28 5/06/98 1:49p Sean
* fixed dumb bug with my last rev
*
* 27 5/06/98 12:34p Jason
* Added next object in terrain stuff
*
* 26 4/02/98 3:54p Jason
* first pass in getting polymodel paging to work
*
* 25 3/30/98 6:20p Matt
* Renamed ResetObject() to be ObjReInitAll()
*
* 24 2/24/98 11:19a Samir
* objects should look better in listbox.
*
* 23 2/05/98 11:25a Samir
* Maybe this will work to fix ListNextItem problems.
*
* 22 1/30/98 6:05p Matt
* Got Next Object in object keypad working, and made a routine to set the
* current editor object.
*
* 21 1/18/98 9:07p Matt
* New parms for ResetObjects()
*
* 20 1/07/98 6:39p Jason
* Fixed player object number stuff
*
* 19 12/19/97 11:26a Samir
* Oops.
*
* 18 12/19/97 11:25a Samir
* g3_StartFrame and g3_EndFrame replaced by EditorStartFrame and
* EditorEndFrame
*
* 17 12/01/97 3:20p Samir
* No Int3 when trying to place objects outside the mine.
*
* 16 9/17/97 1:26p Samir
* BIG SEGMENT RIPOUT
*
* 15 9/15/97 11:55a Samir
* Fixed playership adding.
*
* 14 9/02/97 3:02p Samir
* fixed bug. accidentally set wrong variable for axis movement.
*
* 13 8/29/97 2:49p Samir
* Added new object movement features.
*
* 12 8/25/97 7:39p Samir
* Using new GrListBox from old editorPictListBox
*
* 11 8/18/97 12:09p Matt
* Fixed a stupid bug, and added some error checking and handling
*
* 10 8/13/97 9:50a Matt
* Changed highlighted object to draw with magenta box around it
*
* 9 8/12/97 10:57p Matt
* Made object page work for the generic object types (robot, powerup,
* building, clutter)
*
* 8 8/11/97 1:53p Matt
* Ripped out robot & powerup pages, and added generic page
*
* 7 8/06/97 11:11a Mark
* Took out Int3 for 'untested' draw code in DrawItem. - Samir
*
* 6 7/24/97 7:18p Matt
* Cleaned up code that draws robots & powerups
*
* 5 7/24/97 6:11p Matt
* Created symbolic constant for default zoom, and used it every place
* that specifies zoom
*
* 4 7/24/97 4:21p Matt
* On object keypad, only list this object types the the designer can
* place
*
* 3 7/24/97 2:54p Matt
* Got rid of some member variables that kept track of current robot,
* powerup, & object type, and used variables in D3EditState instead.
*
* 2 7/22/97 7:08p Matt
* Cleaned up D3EditState, moving some vars in and some out, and renaming
* and changing a few others
*
* 35 6/04/97 11:56a Samir
* Added PowerupProp Dialog.
*
* 34 6/03/97 4:55p Mark
*
* 34 6/03/97 4:50p Jeff
* added Context Sensitive Help
*
* 33 5/01/97 12:33p Jason
* added reset objects function
*
* 32 5/01/97 11:16a Samir
* No Int3 in Object type selection
*
* 26 4/02/97 6:25p Samir
* Fixed screwup with decrease heading function.
*
* 25 4/02/97 12:22p Samir
* Fixed object box scroll problems by keeping an array of item starts for
* each obj_type and fixed problem of placing a bad robot (door) when
* first placing objects.
*
* 14 3/13/97 6:17p Jason
* changed door/polymodel prototype
*
* 13 3/03/97 5:56p Jason
* changed function name to be more generic
* GetPowerupBitmap -> GetPowerupImage
*
* 12 2/26/97 3:35p Mark
*
* 11 2/25/97 4:35p Samir
* Selecting from combo box sets pictoral list box selection.
*
* 10 2/21/97 6:25p Samir
* Object palette works (shitty scaling of powerups.)
*
* 9 2/20/97 4:26p Mark
*
* 8 2/19/97 5:26p Samir
* When setting focus on objID listbox, update its contents.
*
* 7 2/19/97 2:16p Samir
* Added RunKeypadFunction handler.
*
* 6 2/18/97 12:47p Samir
* Able to select object types and ids from keypad.
*
* 5 2/17/97 6:19p Samir
* Added place object and move left,right,for, back functionality.
*
* 4 1/27/97 11:38a Samir
* Added horizontal scrolling of keypad modeless dialog.
*
* $NoKeywords: $
*/
#include "stdafx.h"
#include "editor.h"
#include "ObjectDialog.h"
#include "ObjMoveManager.h"
#include "HObject.h"
#include "polymodel.h"
#include "object.h"
#include "ship.h"
#include "objinfo.h"
#include "multi.h"
#include "objinit.h"
#include "room.h"
#include "d3edit.h"
#include "physics.h"
#include "door.h"
#include "mem.h"
#include "mono.h"
#include "PowerupPropDialog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// List of object types & names for the editor
int edit_object_types[] = {OBJ_ROBOT, OBJ_POWERUP, OBJ_BUILDING, OBJ_CLUTTER, OBJ_PLAYER};
char *edit_objtype_names[] = {"Robot", "Powerup", "Building", "Clutter", "Player ship"};
int Swap_source_id = -1;
int Swap_dest_id = -1;
int Reground_id = -1;
bool f_allow_objects_to_be_pushed_through_walls = false;
void DrawItemModel(grHardwareSurface *surf, int model_num);
#define NUM_EDIT_OBJTYPES (sizeof(edit_object_types) / sizeof(*edit_object_types))
/////////////////////////////////////////////////////////////////////////////
// CObjectDialog dialog
CObjectDialog::CObjectDialog(CWnd *pParent /*=NULL*/) : CKeypadDialog(CObjectDialog::IDD, pParent) {
//{{AFX_DATA_INIT(CObjectDialog)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
void CObjectDialog::DoDataExchange(CDataExchange *pDX) {
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CObjectDialog)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
void CObjectDialog::UpdateIDList() { InitObjIDComboBox((CComboBox *)GetDlgItem(IDC_COMBO_OBJID)); }
void CObjectDialog::PlaceObject() { OnObjectPlaceObject(); }
void CObjectDialog::DeleteObject() { OnObjPadDelobj(); }
BEGIN_MESSAGE_MAP(CObjectDialog, CDialog)
//{{AFX_MSG_MAP(CObjectDialog)
ON_WM_SIZE()
ON_WM_VSCROLL()
ON_WM_HSCROLL()
ON_BN_CLICKED(IDC_OBJPAD_PLACEOBJ, OnObjectPlaceObject)
ON_CBN_SELCHANGE(IDC_COMBO_OBJTYPE, OnSelchangeComboObjType)
ON_CBN_SELCHANGE(IDC_COMBO_OBJID, OnSelchangeComboObjID)
ON_BN_CLICKED(IDC_OBJ_DELOBJ, OnObjPadDelobj)
ON_BN_CLICKED(IDC_OBJPAD_NEXTOBJ, OnObjpadNextobj)
ON_CBN_SETFOCUS(IDC_COMBO_OBJID, OnSetfocusComboObjID)
ON_BN_CLICKED(IDC_OBJECTPAD_GROUP, OnObjectpadGroup)
ON_BN_CLICKED(IDC_OBJ_CONT_PREVIEW, OnObjContPreview)
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_BN_CLICKED(IDC_OBJPAD_FLAGOBJMULTI, OnObjpadFlagobjmulti)
ON_BN_CLICKED(IDC_OBJPAD_FLIPOBJ, OnObjpadFlipobj)
ON_BN_CLICKED(IDC_OBJPAD_SETDEFAULT, OnObjpadSetdefault)
ON_WM_DESTROY()
ON_BN_CLICKED(IDC_RESET_OBJECTS, OnResetObjects)
ON_WM_HELPINFO()
ON_BN_CLICKED(IDC_OBJPAD_PROPERTIES, OnObjpadProperties)
ON_CBN_SELCHANGE(IDC_COORDSYS_SELECT, OnSelChangeCoordSysSelect)
ON_BN_CLICKED(IDC_OBJMOVEX, OnObjmoveX)
ON_BN_CLICKED(IDC_OBJMOVEY, OnObjmoveY)
ON_BN_CLICKED(IDC_OBJMOVEXY, OnObjmoveXY)
ON_BN_CLICKED(IDC_OBJMOVEZ, OnObjmoveZ)
ON_BN_CLICKED(IDC_OBJMOVEP, OnObjmoveP)
ON_BN_CLICKED(IDC_OBJMOVEH, OnObjmoveH)
ON_BN_CLICKED(IDC_OBJMOVEB, OnObjmoveB)
ON_BN_CLICKED(IDC_OBJMOVEPH, OnObjmovePH)
ON_BN_CLICKED(IDC_OBJPAD_RESET_ALL_OBJ_HEIGHTS, OnObjpadResetAllObjHeights)
ON_BN_CLICKED(IDC_OBJPAD_RESET_CUROBJ_HEIGHT, OnObjpadResetCurobjHeight)
ON_BN_CLICKED(IDC_PREV_START_POSITON, OnPrevStartPositon)
ON_BN_CLICKED(IDC_NEXT_START_POS, OnNextStartPos)
ON_BN_CLICKED(IDC_JUMP_TO_START_POS, OnJumpToStartPos)
ON_BN_CLICKED(IDC_RED_CHECK, OnRedCheck)
ON_BN_CLICKED(IDC_BLUE_CHECK, OnBlueCheck)
ON_BN_CLICKED(IDC_GREEN_CHECK, OnGreenCheck)
ON_BN_CLICKED(IDC_YELLOW_CHECK, OnYellowCheck)
ON_CBN_SELENDOK(IDC_SWAP_SOURCE_COMBO, OnSelendokSwapSourceCombo)
ON_CBN_SELENDOK(IDC_SWAP_DEST_COMBO, OnSelendokSwapDestCombo)
ON_BN_CLICKED(IDC_OBJECT_SWAP_BUTTON, OnObjectSwapButton)
ON_CBN_SELENDOK(IDC_REGROUND_COMBO, OnSelendokRegroundCombo)
ON_BN_CLICKED(IDC_REGROUND_BUTTON, OnRegroundButton)
ON_BN_CLICKED(IDC_OBJ_ROT90, OnObjRot90)
ON_BN_CLICKED(IDC_OBJECT_PUSHTHROUGHWALLS, OnObjectPushthroughwalls)
ON_BN_CLICKED(IDC_OBJPAD_DELETEALL, OnObjpadDeleteAll)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// Current player isn't saved, so just keep it locally
int current_player = 0;
// Sets the current index of the given type
void SetCurrentIndex(int index) {
switch (D3EditState.current_obj_type) {
case OBJ_ROBOT:
D3EditState.current_robot = index;
break;
case OBJ_POWERUP:
D3EditState.current_powerup = index;
break;
case OBJ_CLUTTER:
D3EditState.current_clutter = index;
break;
case OBJ_BUILDING:
D3EditState.current_building = index;
break;
case OBJ_PLAYER:
current_player = index;
break;
default:
Int3();
}
}
// Returns the current index of the given type
int GetCurrentIndex() {
int type = D3EditState.current_obj_type;
int current;
switch (type) {
case OBJ_ROBOT:
current = D3EditState.current_robot;
break;
case OBJ_POWERUP:
current = D3EditState.current_powerup;
break;
case OBJ_CLUTTER:
current = D3EditState.current_clutter;
break;
case OBJ_BUILDING:
current = D3EditState.current_building;
break;
case OBJ_PLAYER:
return current_player;
break;
default:
Int3();
}
if (Object_info[current].type != type) { // current is of wrong type
current = GetObjectID(type); //...so get a new current
SetCurrentIndex(current);
}
return current;
}
/////////////////////////////////////////////////////////////////////////////
// CObjectDialog message handlers
void CObjectDialog::InitObjIDComboBox(CComboBox *box) {
int i, first, n;
box->ResetContent();
// Add names of object IDs of current object type
switch (D3EditState.current_obj_type) {
case OBJ_ROBOT:
case OBJ_POWERUP:
case OBJ_CLUTTER:
case OBJ_BUILDING:
first = GetObjectID(D3EditState.current_obj_type);
if (first == -1)
break;
if (GetCurrentIndex() == -1)
SetCurrentIndex(first);
n = first;
do {
int index = box->AddString(Object_info[n].name);
box->SetItemData(index, n);
if (n == GetCurrentIndex())
box->SetCurSel(index);
n = GetNextObjectID(n);
} while (n != first);
break;
case OBJ_PLAYER:
for (i = 0; i < MAX_SHIPS; i++) {
if (Ships[i].used) {
int index = box->AddString(Ships[i].name);
box->SetItemData(index, i);
if (i == D3EditState.current_ship)
box->SetCurSel(index);
}
}
break;
}
}
void CObjectDialog::InitObjTypeComboBox(CComboBox *box) {
box->ResetContent();
for (int i = 0; i < NUM_EDIT_OBJTYPES; i++) {
if (box->AddString(edit_objtype_names[i]) < 0)
Int3();
if (edit_object_types[i] == D3EditState.current_obj_type)
box->SetCurSel(i);
}
}
BOOL CObjectDialog::OnInitDialog() {
CComboBox *box;
int current;
CDialog::OnInitDialog();
// Init types combo box
InitObjTypeComboBox((CComboBox *)GetDlgItem(IDC_COMBO_OBJTYPE));
// Init IDs combo box
InitObjIDComboBox((CComboBox *)GetDlgItem(IDC_COMBO_OBJID));
// Get index of current item
current = GetCurrentIndex();
ASSERT(current != -1);
// set selection in object movement combobox and move axis.
box = (CComboBox *)GetDlgItem(IDC_COORDSYS_SELECT);
if (D3EditState.object_move_mode == REL_OBJECT)
box->SelectString(-1, "Local");
else if (D3EditState.object_move_mode == REL_VIEWER)
box->SelectString(-1, "Viewer");
else {
D3EditState.object_move_mode = REL_OBJECT;
box->SelectString(-1, "Local");
}
ObjMoveManager.SetMoveAxis(D3EditState.object_move_axis);
CButton *btn = NULL;
switch (D3EditState.object_move_axis) {
case OBJMOVEAXIS_X:
btn = (CButton *)GetDlgItem(IDC_OBJMOVEX);
break;
case OBJMOVEAXIS_Y:
btn = (CButton *)GetDlgItem(IDC_OBJMOVEY);
break;
case OBJMOVEAXIS_Z:
btn = (CButton *)GetDlgItem(IDC_OBJMOVEZ);
break;
case OBJMOVEAXIS_XY:
btn = (CButton *)GetDlgItem(IDC_OBJMOVEXY);
break;
case OBJMOVEAXIS_P:
btn = (CButton *)GetDlgItem(IDC_OBJMOVEP);
break;
case OBJMOVEAXIS_H:
btn = (CButton *)GetDlgItem(IDC_OBJMOVEH);
break;
case OBJMOVEAXIS_B:
btn = (CButton *)GetDlgItem(IDC_OBJMOVEB);
break;
case OBJMOVEAXIS_PH:
btn = (CButton *)GetDlgItem(IDC_OBJMOVEPH);
break;
default:
Int3(); // THIS SHOULD NEVER HAPPEN - samir
}
btn->SetCheck(1);
m_current_start_pos = 0;
UpdateDialog();
// Done.
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CObjectDialog::OnDestroy() { CDialog::OnDestroy(); }
void CObjectDialog::OnSize(UINT nType, int cx, int cy) { CKeypadDialog::OnSize(nType, cx, cy); }
void CObjectDialog::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar) {
CKeypadDialog::OnVScroll(nSBCode, nPos, pScrollBar);
}
void CObjectDialog::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar) {
CKeypadDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
void CObjectDialog::OnPaint() {
CPaintDC dc(this); // device context for painting
if (!m_Active)
return;
UpdateIDList(); // this is here to update the object ids in case of any changes
ASSERT(GetCurrentIndex() != -1);
// Draw picture of current item
DrawPicture(GetDlgItem(IDC_OBJ_PALETTE));
}
void CObjectDialog::OnLButtonDown(UINT nFlags, CPoint point) { CDialog::OnLButtonDown(nFlags, point); }
void CObjectDialog::UpdateDialog() {
CEdit *box;
char str[255];
int cur = m_current_start_pos;
int i;
sprintf(str, "Current start position: %d", cur);
box = (CEdit *)GetDlgItem(IDC_START_POS_TEXT);
box->SetWindowText(str);
CheckDlgButton(IDC_RED_CHECK, Players[cur].startpos_flags & PSPF_RED);
CheckDlgButton(IDC_BLUE_CHECK, Players[cur].startpos_flags & PSPF_BLUE);
CheckDlgButton(IDC_GREEN_CHECK, Players[cur].startpos_flags & PSPF_GREEN);
CheckDlgButton(IDC_YELLOW_CHECK, Players[cur].startpos_flags & PSPF_YELLOW);
SendDlgItemMessage(IDC_SWAP_SOURCE_COMBO, CB_RESETCONTENT, 0, 0);
SendDlgItemMessage(IDC_SWAP_DEST_COMBO, CB_RESETCONTENT, 0, 0);
SendDlgItemMessage(IDC_REGROUND_COMBO, CB_RESETCONTENT, 0, 0);
for (i = 0; i < MAX_OBJECT_IDS; i++) {
if (Object_info[i].type != OBJ_NONE) {
SendDlgItemMessage(IDC_SWAP_SOURCE_COMBO, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)Object_info[i].name);
SendDlgItemMessage(IDC_SWAP_DEST_COMBO, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)Object_info[i].name);
SendDlgItemMessage(IDC_REGROUND_COMBO, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)Object_info[i].name);
}
}
if (Swap_source_id >= 0 && Swap_source_id < MAX_OBJECT_IDS && Object_info[Swap_source_id].type != OBJ_NONE)
SendDlgItemMessage(IDC_SWAP_SOURCE_COMBO, CB_SELECTSTRING, 0, (LPARAM)(LPCTSTR)Object_info[Swap_source_id].name);
else
SendDlgItemMessage(IDC_SWAP_SOURCE_COMBO, CB_SELECTSTRING, 0, (LPARAM)(LPCTSTR) "None");
if (Swap_dest_id >= 0 && Swap_dest_id < MAX_OBJECT_IDS && Object_info[Swap_dest_id].type != OBJ_NONE)
SendDlgItemMessage(IDC_SWAP_DEST_COMBO, CB_SELECTSTRING, 0, (LPARAM)(LPCTSTR)Object_info[Swap_dest_id].name);
else
SendDlgItemMessage(IDC_SWAP_DEST_COMBO, CB_SELECTSTRING, 0, (LPARAM)(LPCTSTR) "None");
if (Reground_id >= 0 && Reground_id < MAX_OBJECT_IDS && Object_info[Reground_id].type != OBJ_NONE)
SendDlgItemMessage(IDC_REGROUND_COMBO, CB_SELECTSTRING, 0, (LPARAM)(LPCTSTR)Object_info[Reground_id].name);
else
SendDlgItemMessage(IDC_REGROUND_COMBO, CB_SELECTSTRING, 0, (LPARAM)(LPCTSTR) "None");
}
void CObjectDialog::DrawPicture(CWnd *wnd) {
RECT box_rect;
int width, height, model_num;
grHardwareSurface hw_surf;
if (!Editor_active)
return;
// initialize graphic objects
wnd->GetClientRect(&box_rect);
width = box_rect.right - box_rect.left;
height = box_rect.bottom - box_rect.top;
Desktop_surf->attach_to_window((unsigned)wnd->m_hWnd);
hw_surf.create(width, height, BPP_16);
switch (D3EditState.current_obj_type) {
case OBJ_CLUTTER:
case OBJ_BUILDING:
case OBJ_POWERUP:
case OBJ_ROBOT:
model_num = GetObjectImage(GetCurrentIndex());
break;
case OBJ_PLAYER:
model_num = GetShipImage(GetCurrentIndex());
break;
default:
Int3();
}
DrawItemModel(&hw_surf, model_num);
}
// Keypad functions!
void CObjectDialog::OnObjmoveX() {
D3EditState.object_move_axis = OBJMOVEAXIS_X;
ObjMoveManager.SetMoveAxis(D3EditState.object_move_axis);
}
void CObjectDialog::OnObjmoveY() {
D3EditState.object_move_axis = OBJMOVEAXIS_Y;
ObjMoveManager.SetMoveAxis(D3EditState.object_move_axis);
}
void CObjectDialog::OnObjmoveXY() {
D3EditState.object_move_axis = OBJMOVEAXIS_XY;
ObjMoveManager.SetMoveAxis(D3EditState.object_move_axis);
}
void CObjectDialog::OnObjmoveZ() {
D3EditState.object_move_axis = OBJMOVEAXIS_Z;
ObjMoveManager.SetMoveAxis(D3EditState.object_move_axis);
}
void CObjectDialog::OnObjmoveP() {
D3EditState.object_move_axis = OBJMOVEAXIS_P;
ObjMoveManager.SetMoveAxis(D3EditState.object_move_axis);
}
void CObjectDialog::OnObjmoveH() {
D3EditState.object_move_axis = OBJMOVEAXIS_H;
ObjMoveManager.SetMoveAxis(D3EditState.object_move_axis);
}
void CObjectDialog::OnObjmoveB() {
D3EditState.object_move_axis = OBJMOVEAXIS_B;
ObjMoveManager.SetMoveAxis(D3EditState.object_move_axis);
}
void CObjectDialog::OnObjmovePH() {
D3EditState.object_move_axis = OBJMOVEAXIS_PH;
ObjMoveManager.SetMoveAxis(D3EditState.object_move_axis);
}
int CObjectDialog::GetFreePlayerIndex() {
uint8_t slots[MAX_NET_PLAYERS];
memset(slots, 0, MAX_NET_PLAYERS);
for (int i = 0; i <= Highest_object_index; i++) {
if (Objects[i].type == OBJ_PLAYER) {
// Get Jason if these asserts fail
ASSERT(slots[Objects[i].id] == 0);
ASSERT(Objects[i].id < MAX_NET_PLAYERS);
slots[Objects[i].id] = 1;
}
}
for (int i = 0; i < MAX_NET_PLAYERS; i++) {
if (slots[i] == 0) {
mprintf(0, "Making new ship with id of %d...\n", i);
return i;
}
}
return -1;
}
void CObjectDialog::OnObjectPlaceObject() {
int objid = -1;
if (D3EditState.current_obj_type == OBJ_PLAYER) {
objid = GetFreePlayerIndex();
if (objid == -1) {
mprintf(0, "No more player slots! You already have %d players in this level!\n", MAX_NET_PLAYERS);
return;
}
} else
objid = GetCurrentIndex();
ASSERT(objid > -1);
if (!HObjectPlace(D3EditState.current_obj_type, objid)) {
mprintf(0, "Attempt to place object outside mine failed!\n");
}
}
void CObjectDialog::OnSelchangeComboObjType() {
// make sure to save the old item start value.
int old_obj_type = D3EditState.current_obj_type;
D3EditState.current_obj_type = edit_object_types[((CComboBox *)GetDlgItem(IDC_COMBO_OBJTYPE))->GetCurSel()];
InitObjIDComboBox((CComboBox *)GetDlgItem(IDC_COMBO_OBJID));
ASSERT(GetCurrentIndex() != -1);
// Draw picture of current item
DrawPicture(GetDlgItem(IDC_OBJ_PALETTE));
}
void CObjectDialog::OnSelchangeComboObjID() {
CComboBox *box = (CComboBox *)GetDlgItem(IDC_COMBO_OBJID);
switch (D3EditState.current_obj_type) {
case OBJ_ROBOT:
case OBJ_POWERUP:
case OBJ_BUILDING:
case OBJ_CLUTTER: {
int id = box->GetItemData(box->GetCurSel());
ASSERT(Object_info[id].type == D3EditState.current_obj_type);
SetCurrentIndex(id);
break;
}
case OBJ_PLAYER: {
char text[32];
int i, j;
box->GetLBText(box->GetCurSel(), text);
j = 0;
for (i = 0; i < MAX_SHIPS; i++)
if (Ships[i].used && (stricmp(text, Ships[i].name) == 0)) {
j++;
break;
}
ASSERT(i < MAX_SHIPS);
SetCurrentIndex(i);
break;
}
}
// Draw picture of current item
DrawPicture(GetDlgItem(IDC_OBJ_PALETTE));
}
void CObjectDialog::OnObjPadDelobj() { HObjectDelete(); }
// Set the current object
void SelectObject(int objnum) {
char *type_name, *obj_name;
char *id_name = NULL;
ASSERT(objnum != -1);
Cur_object_index = objnum;
object *objp = &Objects[objnum];
type_name = Object_type_names[objp->type];
obj_name = objp->name ? objp->name : "";
if (IS_GENERIC(objp->type))
id_name = Object_info[objp->id].name;
else if (objp->type == OBJ_DOOR)
id_name = Doors[objp->id].name;
if (id_name)
EditorStatus("Object %d selected. Type = %s:%s, Name = %s", objnum, type_name, id_name, obj_name);
else
EditorStatus("Object %d selected. Type = %s, Name = %s", objnum, type_name, obj_name);
State_changed = 1;
}
void CObjectDialog::OnObjpadNextobj() {
int next;
// If on the terrain, advance to the next object
if (Editor_view_mode == VM_TERRAIN && OBJECT_OUTSIDE(&Objects[Cur_object_index])) {
next = -1;
int done = 0;
for (int i = Cur_object_index + 1; i <= Highest_object_index && !done; i++) {
object *obj = &Objects[i];
if (obj->type != OBJ_NONE && OBJECT_OUTSIDE(obj) && obj->type != OBJ_ROOM) {
next = i;
done = 1;
}
}
if (!done) {
// Start counting from zero
for (int i = 0; i <= Cur_object_index && !done; i++) {
object *obj = &Objects[i];
if (obj->type != OBJ_NONE && OBJECT_OUTSIDE(obj) && obj->type != OBJ_ROOM) {
next = i;
done = 1;
}
}
if (next == -1) {
EditorStatus("There are no other objects on the terrain");
return;
}
}
SelectObject(next);
return;
}
if (Objects[Cur_object_index].roomnum != ROOMNUM(Curroomp)) {
next = Curroomp->objects;
if (next == -1) {
EditorStatus("There are no objects in the current room");
return;
}
} else {
next = Objects[Cur_object_index].next;
if (next == -1) {
next = Curroomp->objects;
if (next == Cur_object_index) {
EditorStatus("There are no other objects in this room");
return;
}
}
}
SelectObject(next);
}
void CObjectDialog::OnSetfocusComboObjID() { InitObjIDComboBox((CComboBox *)GetDlgItem(IDC_COMBO_OBJID)); }
void CObjectDialog::OnObjectpadGroup() {
// TODO: Add your control notification handler code here
}
void CObjectDialog::OnObjContPreview() {
// TODO: Add your control notification handler code here
}
void CObjectDialog::OnObjpadFlagobjmulti() {
// TODO: Add your control notification handler code here
}
void CObjectDialog::OnObjpadFlipobj() {
// TODO: Add your control notification handler code here
HObjectFlip();
}
void CObjectDialog::OnObjpadSetdefault() {
// TODO: Add your control notification handler code here
HObjectSetDefault();
}
void CObjectDialog::OnResetObjects() {
ObjReInitAll();
OutrageMessageBox("All objects reset!");
}
BOOL CObjectDialog::OnHelpInfo(HELPINFO *pHelpInfo) {
// TODO: Add your message handler code here and/or call default
WinHelp(HID_OBJECTTAB, HELP_CONTEXT);
return TRUE;
// return CDialog::OnHelpInfo(pHelpInfo);
}
// This function will allow you to edit individual properties for the current selected object.
void CObjectDialog::OnObjpadProperties() {
if (Cur_object_index == -1) {
OutrageMessageBox("Must select an object before editing it's properties.");
return;
}
theApp.pause();
switch (Objects[Cur_object_index].type) {
case OBJ_POWERUP: {
CPowerupPropDialog dlg;
dlg.DoModal();
break;
}
}
theApp.resume();
}
/////////////////////////////////////////////////////////////////////
// New Object Movement Control implementation
void CObjectDialog::OnSelChangeCoordSysSelect() {
CComboBox *cbox = (CComboBox *)GetDlgItem(IDC_COORDSYS_SELECT);
char seltext[8];
cbox->GetLBText(cbox->GetCurSel(), seltext);
if (stricmp(seltext, "local") == 0) {
D3EditState.object_move_mode = REL_OBJECT;
} else if (stricmp(seltext, "viewer") == 0) {
D3EditState.object_move_mode = REL_VIEWER;
} else if (stricmp(seltext, "world") == 0) {
D3EditState.object_move_mode = REL_VIEWER;
}
}
void DrawItemModel(grHardwareSurface *surf, int model_num) {
vector zero_vector = {0, 0, 0};
vector view_pos = {0, 0, 0};
matrix id_matrix, rot_matrix;
poly_model *pm = GetPolymodelPointer(model_num);
// Calculate viewer position
vector maxs = pm->maxs, mins = pm->mins;
view_pos.x = -(mins.x + maxs.x) / 2;
view_pos.y = (mins.y + maxs.y) / 2;
maxs.x += view_pos.x;
maxs.y -= view_pos.y;
maxs.z = 0;
view_pos.z = -2.5 * vm_GetMagnitude(&maxs);
// Set viewer and object orientations
vm_MakeIdentity(&id_matrix);
vm_AnglesToMatrix(&rot_matrix, 0, 32768, 0);
// Set up surface & vuewport
surf->clear();
grViewport *vport = new grViewport(surf);
vport->clear();
// Draw to our surface
StartEditorFrame(vport, &view_pos, &id_matrix, D3_DEFAULT_ZOOM);
DrawPolygonModel(&zero_vector, &rot_matrix, model_num, NULL, 0, 1.0, 1.0, 1.0);
EndEditorFrame();
// Copy to the screen
Desktop_surf->blt(0, 0, surf);
// Delete our viewport
delete vport;
}
void CObjectDialog::OnObjpadResetCurobjHeight() {
if (Cur_object_index == -1) {
OutrageMessageBox("You must have a current object for this operation.");
return;
}
object *objp = &Objects[Cur_object_index];
// Make sure object is outside
if (!OBJECT_OUTSIDE(objp)) {
OutrageMessageBox("The object must be on the terrain this operation.");
return;
}
// Make sure object has a ground plane
if (!((objp->render_type == RT_POLYOBJ) && ((GetPolymodelPointer(objp->rtype.pobj_info.model_num))->n_ground))) {
OutrageMessageBox("The object must have a ground plane for this operation.");
return;
}
ResetGroundObject(objp);
}
void CObjectDialog::OnObjpadResetAllObjHeights() {
object *objp;
int i;
int n_moved = 0;
for (i = 0, objp = Objects; i <= Highest_object_index; i++, objp++) {
// Make sure object is outside
if (!OBJECT_OUTSIDE(objp))
continue;
// Make sure object has a ground plane
if (!((objp->render_type == RT_POLYOBJ) && ((GetPolymodelPointer(objp->rtype.pobj_info.model_num))->n_ground)))
continue;
// Do it
ResetGroundObject(objp);
n_moved++;
}
EditorStatus("%d objects adjusted.", n_moved);
}
void CObjectDialog::OnPrevStartPositon() {
if (m_current_start_pos == 0)
m_current_start_pos = MAX_PLAYERS - 1;
else
m_current_start_pos--;
UpdateDialog();
}
void CObjectDialog::OnNextStartPos() {
m_current_start_pos++;
if (m_current_start_pos >= MAX_PLAYERS)
m_current_start_pos = 0;
UpdateDialog();
}
void CObjectDialog::OnJumpToStartPos() {
char str[255];
int found_obj = -1;
int highest = -1;
for (int i = 0; i <= Highest_object_index && found_obj == -1; i++) {
if (Objects[i].type == OBJ_PLAYER) {
if (Objects[i].id > highest)
highest = Objects[i].id;
if (Objects[i].id == m_current_start_pos) {
found_obj = i;
}
}
}
if (found_obj == -1) {
sprintf(str, "There was no player defined for this start position. The highest numbered player is #%d.", highest);
OutrageMessageBox(str);
} else {
MoveViewer(&Objects[found_obj].pos, Objects[found_obj].roomnum, &Objects[found_obj].orient);
TV_changed = 1;
}
}
void CObjectDialog::OnRedCheck() {
int cur = m_current_start_pos;
int c = IsDlgButtonChecked(IDC_RED_CHECK);
if (c)
Players[cur].startpos_flags |= PSPF_RED;
else
Players[cur].startpos_flags &= ~PSPF_RED;
}
void CObjectDialog::OnBlueCheck() {
int cur = m_current_start_pos;
int c = IsDlgButtonChecked(IDC_BLUE_CHECK);
if (c)
Players[cur].startpos_flags |= PSPF_BLUE;
else
Players[cur].startpos_flags &= ~PSPF_BLUE;
}
void CObjectDialog::OnGreenCheck() {
int cur = m_current_start_pos;
int c = IsDlgButtonChecked(IDC_GREEN_CHECK);
if (c)
Players[cur].startpos_flags |= PSPF_GREEN;
else
Players[cur].startpos_flags &= ~PSPF_GREEN;
}
void CObjectDialog::OnYellowCheck() {
int cur = m_current_start_pos;
int c = IsDlgButtonChecked(IDC_YELLOW_CHECK);
if (c)
Players[cur].startpos_flags |= PSPF_YELLOW;
else
Players[cur].startpos_flags &= ~PSPF_YELLOW;
}
void CObjectDialog::OnSelendokSwapSourceCombo() {
int cur;
char name[200];
cur = SendDlgItemMessage(IDC_SWAP_SOURCE_COMBO, CB_GETCURSEL, 0, 0);
SendDlgItemMessage(IDC_SWAP_SOURCE_COMBO, CB_GETLBTEXT, cur, (LPARAM)(LPCTSTR)name);
Swap_source_id = FindObjectIDName(name);
UpdateDialog();
}
void CObjectDialog::OnSelendokSwapDestCombo() {
int cur;
char name[200];
cur = SendDlgItemMessage(IDC_SWAP_DEST_COMBO, CB_GETCURSEL, 0, 0);
SendDlgItemMessage(IDC_SWAP_DEST_COMBO, CB_GETLBTEXT, cur, (LPARAM)(LPCTSTR)name);
Swap_dest_id = FindObjectIDName(name);
UpdateDialog();
}
void CObjectDialog::OnObjectSwapButton() {
int i;
int cur;
char name[200];
cur = SendDlgItemMessage(IDC_SWAP_SOURCE_COMBO, CB_GETCURSEL, 0, 0);
SendDlgItemMessage(IDC_SWAP_SOURCE_COMBO, CB_GETLBTEXT, cur, (LPARAM)(LPCTSTR)name);
int s_id = FindObjectIDName(name);
cur = SendDlgItemMessage(IDC_SWAP_DEST_COMBO, CB_GETCURSEL, 0, 0);
SendDlgItemMessage(IDC_SWAP_DEST_COMBO, CB_GETLBTEXT, cur, (LPARAM)(LPCTSTR)name);
int d_id = FindObjectIDName(name);
if (s_id >= 0 && d_id >= 0) {
for (i = 0; i <= Highest_object_index; i++) {
if (Objects[i].type == OBJ_ROBOT || Objects[i].type == OBJ_CLUTTER || Objects[i].type == OBJ_POWERUP ||
Objects[i].type == OBJ_BUILDING) {
if (Objects[i].id == s_id) {
vector pos = Objects[i].pos;
matrix orient = Objects[i].orient;
int room = Objects[i].roomnum;
int parent = Objects[i].parent_handle;
char temp_name[256];
temp_name[0] = '\0';
if (Objects[i].name) {
strcpy(temp_name, Objects[i].name);
}
ObjDelete(i);
int o_index = ObjCreate(Object_info[d_id].type, d_id, room, &pos, &orient, parent);
if (temp_name[0] != '\0') {
Objects[o_index].name = (char *)mem_malloc(strlen(temp_name) + 1);
strcpy(Objects[o_index].name, temp_name);
}
}
}
}
}
World_changed = 1;
UpdateDialog();
}
void CObjectDialog::OnSelendokRegroundCombo() {
int cur;
char name[200];
cur = SendDlgItemMessage(IDC_REGROUND_COMBO, CB_GETCURSEL, 0, 0);
SendDlgItemMessage(IDC_REGROUND_COMBO, CB_GETLBTEXT, cur, (LPARAM)(LPCTSTR)name);
Reground_id = FindObjectIDName(name);
UpdateDialog();
}
void CObjectDialog::OnRegroundButton() {
if (Reground_id >= 0 && Object_info[Reground_id].type != OBJ_NONE) {
int i;
for (i = 0; i <= Highest_object_index; i++) {
vector gp;
object *obj = &Objects[i];
if (obj->type == OBJ_ROBOT || obj->type == OBJ_CLUTTER || obj->type == OBJ_POWERUP || obj->type == OBJ_BUILDING) {
if (obj->id == Reground_id && PhysCalcGround(&gp, NULL, &Objects[i], 0)) {
fvi_info hit_info;
fvi_query fq;
int fate;
vector start = obj->pos + obj->size * 0.8f * obj->orient.uvec;
vector end = obj->pos - obj->size * 2.0f * obj->orient.uvec;
fq.p0 = &start;
fq.p1 = &end;
fq.startroom = obj->roomnum;
fq.rad = 0.0f;
fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS | FQ_IGNORE_MOVING_OBJECTS;
fq.thisobjnum = OBJNUM(obj);
fq.ignore_obj_list = NULL;
fate = fvi_FindIntersection(&fq, &hit_info);
if (fate != HIT_NONE) {
float ps;
float pr;
float diff;
ps = (gp - obj->pos) * obj->orient.uvec;
pr = (hit_info.hit_pnt - obj->pos) * obj->orient.uvec;
if (ps != pr) {
diff = ps - pr;
obj->pos -= diff * obj->orient.uvec;
ObjSetPos(obj, &obj->pos, obj->roomnum, NULL, false);
World_changed = 1;
}
}
}
}
}
}
UpdateDialog();
}
extern bool RotateObject(int objnum, angle p, angle h, angle b);
void CObjectDialog::OnObjRot90() {
angle p = 0, b = 0, h = 0;
switch (D3EditState.object_move_axis) {
case OBJMOVEAXIS_P:
p = 0x4000;
break;
case OBJMOVEAXIS_H:
h = 0x4000;
break;
case OBJMOVEAXIS_B:
b = 0x4000;
break;
case OBJMOVEAXIS_PH:
p = 0x4000;
h = 0x4000;
break;
default:
return;
}
RotateObject(Cur_object_index, p, h, b);
}
void CObjectDialog::OnObjectPushthroughwalls() {
if (f_allow_objects_to_be_pushed_through_walls)
f_allow_objects_to_be_pushed_through_walls = false;
else
f_allow_objects_to_be_pushed_through_walls = true;
}
void CObjectDialog::OnObjpadDeleteAll() {
if (Cur_object_index == -1)
return;
object *objp = &Objects[Cur_object_index];
if (objp->type == OBJ_PLAYER) {
OutrageMessageBox("You cannot use this function on player objects.");
return;
}
int type = objp->type, id = objp->id;
if (type == OBJ_DOOR) {
if (OutrageMessageBox(MBOX_YESNO,
"It's very, very bad to delete a door object. Are you sure you want to delete all instances "
"of the \"%s\" door?",
Doors[id].name) != IDYES)
return;
} else { // Not a door
char *objid_name;
if (IS_GENERIC(type))
objid_name = Object_info[id].name;
else
objid_name = "";
if (OutrageMessageBox(MBOX_YESNO, "Are you sure you want to delete all %s objects of type \"%s\" in the mine?",
Object_type_names[type], objid_name) != IDYES)
return;
}
int count = 0;
for (int objnum = 0; objnum <= Highest_object_index; objnum++) {
if ((Objects[objnum].type == type) && (Objects[objnum].id == id)) {
ObjDelete(objnum);
count++;
}
}
EditorStatus("%d objects deleted.", count);
World_changed = 1;
}