Files
dustinspace217andDustinspace217 499b3fb38b P4: qtFRED: rotate around the centre of a multi-selection (#7843)
* qtFRED: rotate around the centre of a multi-selection

Rotate Around Object (Alt+R) and the orbit pivot both used the current
object's position, so with several ships marked the camera still
circled whichever one happened to be current. Add
EditorViewport::selectionCenter(), which returns the centroid of the
marked objects when more than one is marked and the given object's
position otherwise, and use it at the three places that chose the
point: the look-at camera update, orbitCameraGetPivot(), and the Alt+R
handler that turns the camera toward the target when the mode is
switched on.

orbitCameraGetPivot() also serves middle-mouse orbit and wheel zoom in
normal mode, so those pivot on the selection centre as well. Every
marked type counts at full weight, including waypoints and jump nodes.
With a single object marked nothing changes.

Fixes #7724

* Make EditorViewport::selectionCenter const

It only reads the marked-object list and the editor's marked count;
clang-tidy (readability-make-member-function-const) flagged it in CI.

* Mirror the multi-selection orbit pivot to FRED, trim comments

FRED gets the same selection_center() as qtFRED's selectionCenter(),
used at the same three places: the look-at camera update, the orbit
pivot, and the Rotate Around Object toggle. Also removes the qtFRED
comments flagged in review.

* Make the selection-center pivot optional and share object bounds

Look-at and orbit use the current object again unless the new option is
on (qtFRED: Preferences > Controls; FRED: View menu), and the selection
center is now the bounding-box midpoint. view_universe in both editors,
the pivot, and qtFRED's selection radius all use get_object_bounds() in
missioneditor/common.

* Reset the cached orbit pivot when the selection-center option changes

Wheel zoom reuses the orbit pivot until the camera moves, so without this
the option only took effect for scrolling after the next camera move.

* Address review: move and rename the selection-centroid option

- FRED: the option moves from the View menu to Settings (and Misc in the
  classic layout), under Move Ships When Undocking, with hover help, and is
  renamed Rotate Around Selection Centroid.
- qtFRED: the checkbox moves next to Move Ships When Undocking in the
  preferences dialog, with the same name.
- setOrbitSelectionCenter only resets the orbit when the value changes.
- get_object_bounds iterates with list_range.
- The bounds test resets the object system with obj_init and takes objects
  off the free list, so it leaves a valid state for other tests.

* Rename the option to Selection Midpoint, move it to qtFRED's Controls tab, and document it

* Fix overlapping Appearance rows in qtFRED Preferences, realign a settings line

The last commit renumbered the Viewport label size row of the Appearance
group from 4 to 3, which is the Long list menu style row, so the label
size pair fell out of the layout and its spin box drew over the group
title. Put it back at row 4.

---------

Co-authored-by: Dustinspace217 <[email protected]>
2026-11-04 01:17:00 -05:00

5236 lines
130 KiB
C++

/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include "stdafx.h"
#include "FRED.h"
#include "FREDDoc.h"
#include "FREDView.h"
#include "FredRender.h"
#include "cfile/cfile.h"
#include "Grid.h"
#include "MainFrm.h"
#include "MFCGraphicsOperations.h"
#include "Management.h"
#include "graphics/2d.h"
#include "render/3d.h"
#include "object/object.h"
#include "globalincs/linklist.h"
#include "globalincs/vmallocator.h"
#include "math/fvi.h" // For find_plane_line_intersection
#include "math/vecmat.h"
#include "io/key.h"
#include "ai/ai.h"
#include "ai/aigoals.h"
#include "ship/ship.h" // for ship names
#include "prop/prop.h" // for prop names
#include "missioneditor/common.h"
#include "MissionGoalsDlg.h"
#include "MissionCutscenesDlg.h"
#include "wing.h"
#include "ship_select.h"
#include "PlayerStartEditor.h"
#include "OrientEditor.h"
#include "EventEditor.h"
#include "MessageEditorDlg.h"
#include "starfield/starfield.h"
#include "StarfieldEditor.h"
#include "math/floating.h"
#include "ReinforcementEditorDlg.h"
#include "AsteroidEditorDlg.h"
#include "CampaignTreeWnd.h"
#include "DebriefingEditorDlg.h"
#include "AdjustGridDlg.h"
#include "ShieldSysDlg.h"
#include "CmdBrief.h"
#include "jumpnode/jumpnode.h"
#include "DumpStats.h"
#include "SetGlobalShipFlags.h"
#include "VoiceActingManager.h"
#include "FictionViewerDlg.h"
#include "cmdline/cmdline.h"
#include "object/objectdock.h"
#include "species_defs/species_defs.h"
#include "sound/audiostr.h"
#include "mission/missiongrid.h"
#include "calcrelativecoordsdlg.h"
#include "reorderdlg.h"
#include "musicplayerdlg.h"
#include "volumetricsdlg.h"
#include "customdatadlg.h"
#include "customstringsdlg.h"
#include "osapi/osapi.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
subsys_to_render Render_subsys;
#define SHIP_TYPES 8000
#define REDUCER 100.0f
#define DUP_DRAG_OF_WING 2
LOCAL int Duped_wing;
int Autosave_disabled = 0;
int Show_sexp_help = 1;
int Show_ships = 1;
int Show_starts = 1;
int Show_ship_info = 1;
int Show_ship_models = 1;
int Show_compass = 1;
int Show_dock_points = 0;
int Show_paths_fred = 0;
int Lighting_on = 0;
int FullDetail = 0;
int Selection_lock = 0;
int viewpoint = 0;
int view_obj;
int button_down = 0;
int Marked = 0, moved = 0;
int on_object = -1;
int Cursor_over = -1;
int Dup_drag = 0;
int physics_speed = 1;
int physics_rot = 20;
int box_marking = 0;
int last_mouse_x, last_mouse_y, mouse_dx, mouse_dy;
int Cur_bitmap = -1;
UINT_PTR Id_select_type_waypoint = 0;
UINT_PTR Id_select_type_jump_node = 0;
int Hide_ship_cues = 0, Hide_wing_cues = 0;
int Move_ships_when_undocking = 1; // true by default
int Highlight_selectable_subsys = 0;
int Point_using_uvec = 0;
vec3d original_pos, saved_cam_pos;
matrix bitmap_matrix_backup, saved_cam_orient = { 0.0f };
Marking_box marking_box;
object_orient_pos rotation_backup[MAX_OBJECTS];
// Goober5000 (currently, FS1 retail not implemented)
int Mission_save_format = FSO_FORMAT_STANDARD;
// used by error checker, but needed in more than just one function.
char *names[MAX_OBJECTS], flags[MAX_OBJECTS];
int obj_count = 0;
int g_err = 0;
SCP_vector<int> ID_SHOW_IFF;
void view_universe(int just_marked = 0);
void select_objects();
void drag_rotate_save_backup();
/////////////////////////////////////////////////////////////////////////////
// CFREDView
CFREDView *Fred_view_wnd = NULL;
IMPLEMENT_DYNCREATE(CFREDView, CView)
BEGIN_MESSAGE_MAP(CFREDView, CView)
ON_MESSAGE(WM_GOODBYE, OnGoodbye)
ON_MESSAGE(WM_MENU_POPUP_SHIPS, OnMenuPopupShips)
ON_MESSAGE(WM_MENU_POPUP_EDIT, OnMenuPopupEdit)
//{{AFX_MSG_MAP(CFREDView)
ON_COMMAND(ID_VIEW_GRID, OnViewGrid)
ON_UPDATE_COMMAND_UI(ID_VIEW_GRID, OnUpdateViewGrid)
ON_COMMAND(ID_SHOW_WAYPOINTS, OnViewWaypoints)
ON_UPDATE_COMMAND_UI(ID_SHOW_WAYPOINTS, OnUpdateViewWaypoints)
ON_WM_LBUTTONDOWN()
ON_COMMAND(ID_EDITORS_SHIPS, OnEditorsShips)
ON_COMMAND(ID_EDITORS_PROPS, OnEditorsProps)
ON_WM_KEYDOWN()
ON_WM_KEYUP()
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
ON_WM_SIZE()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONUP()
ON_WM_MBUTTONDOWN()
ON_WM_MBUTTONUP()
ON_WM_RBUTTONDOWN()
ON_WM_RBUTTONUP()
ON_WM_MOUSEWHEEL()
ON_COMMAND(ID_MISCSTUFF_SHOWSHIPSASICONS, OnMiscstuffShowshipsasicons)
ON_WM_CONTEXTMENU()
ON_COMMAND(ID_EDIT_POPUP_SHOW_SHIP_ICONS, OnEditPopupShowShipIcons)
ON_UPDATE_COMMAND_UI(ID_EDIT_POPUP_SHOW_SHIP_ICONS, OnUpdateEditPopupShowShipIcons)
ON_COMMAND(ID_EDIT_POPUP_SHOW_SHIP_MODELS, OnEditPopupShowShipModels)
ON_UPDATE_COMMAND_UI(ID_EDIT_POPUP_SHOW_SHIP_MODELS, OnUpdateEditPopupShowShipModels)
ON_COMMAND(ID_MISC_STATISTICS, OnMiscStatistics)
ON_COMMAND(ID_EDIT_POPUP_SHOW_COMPASS, OnEditPopupShowCompass)
ON_UPDATE_COMMAND_UI(ID_EDIT_POPUP_SHOW_COMPASS, OnUpdateEditPopupShowCompass)
ON_UPDATE_COMMAND_UI(ID_CHANGE_VIEWPOINT_EXTERNAL, OnUpdateChangeViewpointExternal)
ON_COMMAND(ID_CHANGE_VIEWPOINT_EXTERNAL, OnChangeViewpointExternal)
ON_UPDATE_COMMAND_UI(ID_CHANGE_VIEWPOINT_FOLLOW, OnUpdateChangeViewpointFollow)
ON_COMMAND(ID_CHANGE_VIEWPOINT_FOLLOW, OnChangeViewpointFollow)
ON_COMMAND(ID_OUTLINE_LOD_0, OnOutlineLod0)
ON_UPDATE_COMMAND_UI(ID_OUTLINE_LOD_0, OnUpdateOutlineLod0)
ON_COMMAND(ID_OUTLINE_LOD_1, OnOutlineLod1)
ON_UPDATE_COMMAND_UI(ID_OUTLINE_LOD_1, OnUpdateOutlineLod1)
ON_COMMAND(ID_OUTLINE_LOD_2, OnOutlineLod2)
ON_UPDATE_COMMAND_UI(ID_OUTLINE_LOD_2, OnUpdateOutlineLod2)
ON_COMMAND(ID_OUTLINE_LOD_3, OnOutlineLod3)
ON_UPDATE_COMMAND_UI(ID_OUTLINE_LOD_3, OnUpdateOutlineLod3)
ON_COMMAND(ID_OUTLINE_LOD_4, OnOutlineLod4)
ON_UPDATE_COMMAND_UI(ID_OUTLINE_LOD_4, OnUpdateOutlineLod4)
ON_COMMAND(ID_LABEL_FONT_SCALE_50, OnLabelFontScale50)
ON_UPDATE_COMMAND_UI(ID_LABEL_FONT_SCALE_50, OnUpdateLabelFontScale50)
ON_COMMAND(ID_LABEL_FONT_SCALE_75, OnLabelFontScale75)
ON_UPDATE_COMMAND_UI(ID_LABEL_FONT_SCALE_75, OnUpdateLabelFontScale75)
ON_COMMAND(ID_LABEL_FONT_SCALE_100, OnLabelFontScale100)
ON_UPDATE_COMMAND_UI(ID_LABEL_FONT_SCALE_100, OnUpdateLabelFontScale100)
ON_COMMAND(ID_LABEL_FONT_SCALE_125, OnLabelFontScale125)
ON_UPDATE_COMMAND_UI(ID_LABEL_FONT_SCALE_125, OnUpdateLabelFontScale125)
ON_COMMAND(ID_LABEL_FONT_SCALE_150, OnLabelFontScale150)
ON_UPDATE_COMMAND_UI(ID_LABEL_FONT_SCALE_150, OnUpdateLabelFontScale150)
ON_COMMAND(ID_LABEL_FONT_SCALE_200, OnLabelFontScale200)
ON_UPDATE_COMMAND_UI(ID_LABEL_FONT_SCALE_200, OnUpdateLabelFontScale200)
ON_COMMAND(ID_LABEL_FONT_SCALE_250, OnLabelFontScale250)
ON_UPDATE_COMMAND_UI(ID_LABEL_FONT_SCALE_250, OnUpdateLabelFontScale250)
ON_COMMAND(ID_LABEL_FONT_SCALE_300, OnLabelFontScale300)
ON_UPDATE_COMMAND_UI(ID_LABEL_FONT_SCALE_300, OnUpdateLabelFontScale300)
ON_COMMAND(ID_LABEL_FONT_SCALE_350, OnLabelFontScale350)
ON_UPDATE_COMMAND_UI(ID_LABEL_FONT_SCALE_350, OnUpdateLabelFontScale350)
ON_COMMAND(ID_LABEL_FONT_SCALE_400, OnLabelFontScale400)
ON_UPDATE_COMMAND_UI(ID_LABEL_FONT_SCALE_400, OnUpdateLabelFontScale400)
ON_COMMAND(ID_EDITORS_GOALS, OnEditorsGoals)
ON_COMMAND(ID_EDITORS_CUTSCENES, OnEditorsCutscenes)
ON_COMMAND(ID_SPEED1, OnSpeed1)
ON_COMMAND(ID_SPEED2, OnSpeed2)
ON_COMMAND(ID_SPEED5, OnSpeed5)
ON_COMMAND(ID_SPEED10, OnSpeed10)
ON_UPDATE_COMMAND_UI(ID_SPEED1, OnUpdateSpeed1)
ON_COMMAND(ID_SPEED3, OnSpeed3)
ON_COMMAND(ID_SPEED8, OnSpeed8)
ON_COMMAND(ID_ROT1, OnRot1)
ON_COMMAND(ID_ROT2, OnRot2)
ON_COMMAND(ID_ROT3, OnRot3)
ON_COMMAND(ID_ROT4, OnRot4)
ON_COMMAND(ID_ROT5, OnRot5)
ON_UPDATE_COMMAND_UI(ID_SPEED2, OnUpdateSpeed2)
ON_UPDATE_COMMAND_UI(ID_SPEED3, OnUpdateSpeed3)
ON_UPDATE_COMMAND_UI(ID_SPEED5, OnUpdateSpeed5)
ON_UPDATE_COMMAND_UI(ID_SPEED8, OnUpdateSpeed8)
ON_UPDATE_COMMAND_UI(ID_SPEED10, OnUpdateSpeed10)
ON_UPDATE_COMMAND_UI(ID_ROT1, OnUpdateRot1)
ON_UPDATE_COMMAND_UI(ID_ROT2, OnUpdateRot2)
ON_UPDATE_COMMAND_UI(ID_ROT3, OnUpdateRot3)
ON_UPDATE_COMMAND_UI(ID_ROT4, OnUpdateRot4)
ON_UPDATE_COMMAND_UI(ID_ROT5, OnUpdateRot5)
ON_COMMAND(ID_CONTROL_MODE_CAMERA, OnControlModeCamera)
ON_UPDATE_COMMAND_UI(ID_CONTROL_MODE_CAMERA, OnUpdateControlModeCamera)
ON_COMMAND(ID_CONTROL_MODE_SHIP, OnControlModeShip)
ON_UPDATE_COMMAND_UI(ID_CONTROL_MODE_SHIP, OnUpdateControlModeShip)
ON_COMMAND(ID_SHOW_GRID_POSITIONS, OnShowGridPositions)
ON_UPDATE_COMMAND_UI(ID_SHOW_GRID_POSITIONS, OnUpdateShowGridPositions)
ON_COMMAND(ID_SHOW_COORDINATES, OnShowCoordinates)
ON_UPDATE_COMMAND_UI(ID_SHOW_COORDINATES, OnUpdateShowCoordinates)
ON_COMMAND(ID_SPEED50, OnSpeed50)
ON_UPDATE_COMMAND_UI(ID_SPEED50, OnUpdateSpeed50)
ON_COMMAND(ID_SPEED100, OnSpeed100)
ON_UPDATE_COMMAND_UI(ID_SPEED100, OnUpdateSpeed100)
ON_COMMAND(ID_SPEED500, OnSpeed500)
ON_UPDATE_COMMAND_UI(ID_SPEED500, OnUpdateSpeed500)
ON_COMMAND(ID_SPEED1000, OnSpeed1000)
ON_UPDATE_COMMAND_UI(ID_SPEED1000, OnUpdateSpeed1000)
ON_COMMAND(ID_SELECT, OnSelect)
ON_UPDATE_COMMAND_UI(ID_SELECT, OnUpdateSelect)
ON_COMMAND(ID_SELECT_AND_MOVE, OnSelectAndMove)
ON_UPDATE_COMMAND_UI(ID_SELECT_AND_MOVE, OnUpdateSelectAndMove)
ON_COMMAND(ID_SELECT_AND_ROTATE, OnSelectAndRotate)
ON_UPDATE_COMMAND_UI(ID_SELECT_AND_ROTATE, OnUpdateSelectAndRotate)
ON_COMMAND(ID_CONSTRAIN_X, OnConstrainX)
ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_X, OnUpdateConstrainX)
ON_COMMAND(ID_CONSTRAIN_Y, OnConstrainY)
ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_Y, OnUpdateConstrainY)
ON_COMMAND(ID_CONSTRAIN_Z, OnConstrainZ)
ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_Z, OnUpdateConstrainZ)
ON_COMMAND(ID_CONSTRAIN_XZ, OnConstrainXz)
ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_XZ, OnUpdateConstrainXz)
ON_COMMAND(ID_SELECTION_LOCK, OnSelectionLock)
ON_UPDATE_COMMAND_UI(ID_SELECTION_LOCK, OnUpdateSelectionLock)
ON_WM_LBUTTONDBLCLK()
ON_COMMAND(ID_DOUBLE_FINE_GRIDLINES, OnDoubleFineGridlines)
ON_UPDATE_COMMAND_UI(ID_DOUBLE_FINE_GRIDLINES, OnUpdateDoubleFineGridlines)
ON_COMMAND(ID_SHOW_DISTANCES, OnShowDistances)
ON_UPDATE_COMMAND_UI(ID_SHOW_DISTANCES, OnUpdateShowDistances)
ON_COMMAND(ID_UNIVERSAL_HEADING, OnUniversalHeading)
ON_UPDATE_COMMAND_UI(ID_UNIVERSAL_HEADING, OnUpdateUniversalHeading)
ON_COMMAND(ID_FLYING_CONTROLS, OnFlyingControls)
ON_UPDATE_COMMAND_UI(ID_FLYING_CONTROLS, OnUpdateFlyingControls)
ON_COMMAND(ID_ROTATE_LOCALLY, OnRotateLocally)
ON_UPDATE_COMMAND_UI(ID_ROTATE_LOCALLY, OnUpdateRotateLocally)
ON_COMMAND(ID_CONSTRAIN_XY, OnConstrainXy)
ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_XY, OnUpdateConstrainXy)
ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_YZ, OnUpdateConstrainYz)
ON_COMMAND(ID_CONSTRAIN_YZ, OnConstrainYz)
ON_COMMAND(ID_SELECT_LIST, OnSelectList)
ON_COMMAND(ID_ZOOM_EXTENTS, OnZoomExtents)
ON_COMMAND(ID_ZOOM_SELECTED, OnZoomSelected)
ON_UPDATE_COMMAND_UI(ID_ZOOM_SELECTED, OnUpdateZoomSelected)
ON_COMMAND(ID_FORM_WING, OnFormWing)
ON_UPDATE_COMMAND_UI(ID_FORM_WING, OnUpdateFormWing)
ON_COMMAND(ID_DISBAND_WING, OnDisbandWing)
ON_UPDATE_COMMAND_UI(ID_DISBAND_WING, OnUpdateDisbandWing)
ON_COMMAND(ID_SHOW_HORIZON, OnShowHorizon)
ON_UPDATE_COMMAND_UI(ID_SHOW_HORIZON, OnUpdateShowHorizon)
ON_COMMAND(ID_EDITORS_WING, OnEditorsWing)
ON_COMMAND(ID_EDITORS_PLAYER, OnEditorsPlayer)
ON_COMMAND(ID_EDITORS_ORIENT, OnEditorsOrient)
ON_COMMAND(ID_EDITORS_EVENTS, OnEditorsEvents)
ON_UPDATE_COMMAND_UI(ID_EDITORS_ORIENT, OnUpdateEditorsOrient)
ON_COMMAND(ID_EDITORS_MESSAGE, OnEditorsMessage)
ON_COMMAND(ID_EDITORS_STARFIELD, OnEditorsStarfield)
ON_COMMAND(ID_EDITORS_BG_BITMAPS, OnEditorsBgBitmaps)
ON_COMMAND(ID_EDITORS_VOLUMETRICS, OnEditorsVolumetrics)
ON_COMMAND(ID_EDITORS_REINFORCEMENT, OnEditorsReinforcement)
ON_COMMAND(ID_ERROR_CHECKER, OnErrorChecker)
ON_COMMAND(ID_EDITORS_WAYPOINT, OnEditorsWaypoint)
ON_COMMAND(ID_EDITORS_JUMPNODE, OnEditorsJumpnode)
ON_COMMAND(ID_VIEW_OUTLINES, OnViewOutlines)
ON_UPDATE_COMMAND_UI(ID_VIEW_OUTLINES, OnUpdateViewOutlines)
ON_COMMAND(ID_VIEW_OUTLINES_ON_SELECTED, OnViewOutlinesOnSelected)
ON_UPDATE_COMMAND_UI(ID_VIEW_OUTLINES_ON_SELECTED, OnUpdateViewOutlinesOnSelected)
ON_COMMAND(ID_VIEW_OUTLINE_AT_WARPIN, OnViewOutlineAtWarpin)
ON_UPDATE_COMMAND_UI(ID_VIEW_OUTLINE_AT_WARPIN, OnUpdateViewOutlineAtWarpin)
ON_COMMAND(ID_MISC_ROTATE_SELECTION_MIDPOINT, OnMiscRotateSelectionMidpoint)
ON_UPDATE_COMMAND_UI(ID_MISC_ROTATE_SELECTION_MIDPOINT, OnUpdateMiscRotateSelectionMidpoint)
ON_COMMAND(ID_ALWAYS_SAVE_DISPLAY_NAMES, OnAlwaysSaveDisplayNames)
ON_UPDATE_COMMAND_UI(ID_ALWAYS_SAVE_DISPLAY_NAMES, OnUpdateAlwaysSaveDisplayNames)
ON_COMMAND(ID_ERROR_CHECKER_CHECKS_POTENTIAL_ISSUES, OnErrorCheckerChecksPotentialIssues)
ON_UPDATE_COMMAND_UI(ID_ERROR_CHECKER_CHECKS_POTENTIAL_ISSUES, OnUpdateErrorCheckerChecksPotentialIssues)
ON_COMMAND(ID_CLASSIC_MENU_LAYOUT, OnClassicMenuLayout)
ON_UPDATE_COMMAND_UI(ID_CLASSIC_MENU_LAYOUT, OnUpdateClassicMenuLayout)
ON_UPDATE_COMMAND_UI(ID_NEW_SHIP_TYPE, OnUpdateNewShipType)
ON_COMMAND(ID_SHOW_STARFIELD, OnShowStarfield)
ON_UPDATE_COMMAND_UI(ID_SHOW_STARFIELD, OnUpdateShowStarfield)
ON_COMMAND(ID_ASTEROID_EDITOR, OnAsteroidEditor)
ON_COMMAND(ID_RUN_FREESPACE, OnRunFreeSpace)
ON_COMMAND(ID_EDITOR_CAMPAIGN, OnEditorCampaign)
ON_COMMAND(ID_SHOW_SHIPS, OnShowShips)
ON_UPDATE_COMMAND_UI(ID_SHOW_SHIPS, OnUpdateShowShips)
ON_COMMAND(ID_SHOW_STARTS, OnShowStarts)
ON_UPDATE_COMMAND_UI(ID_SHOW_STARTS, OnUpdateShowStarts)
ON_COMMAND(ID_SHOW_IFF_0, OnShowIFF0)
ON_UPDATE_COMMAND_UI(ID_SHOW_IFF_0, OnUpdateShowIFF0)
ON_COMMAND(ID_SHOW_IFF_1, OnShowIFF1)
ON_UPDATE_COMMAND_UI(ID_SHOW_IFF_1, OnUpdateShowIFF1)
ON_COMMAND(ID_SHOW_IFF_2, OnShowIFF2)
ON_UPDATE_COMMAND_UI(ID_SHOW_IFF_2, OnUpdateShowIFF2)
ON_COMMAND(ID_SHOW_IFF_3, OnShowIFF3)
ON_UPDATE_COMMAND_UI(ID_SHOW_IFF_3, OnUpdateShowIFF3)
ON_COMMAND(ID_SHOW_IFF_4, OnShowIFF4)
ON_UPDATE_COMMAND_UI(ID_SHOW_IFF_4, OnUpdateShowIFF4)
ON_COMMAND(ID_SHOW_IFF_5, OnShowIFF5)
ON_UPDATE_COMMAND_UI(ID_SHOW_IFF_5, OnUpdateShowIFF5)
ON_COMMAND(ID_SHOW_IFF_6, OnShowIFF6)
ON_UPDATE_COMMAND_UI(ID_SHOW_IFF_6, OnUpdateShowIFF6)
ON_COMMAND(ID_SHOW_IFF_7, OnShowIFF7)
ON_UPDATE_COMMAND_UI(ID_SHOW_IFF_7, OnUpdateShowIFF7)
ON_COMMAND(ID_SHOW_IFF_8, OnShowIFF8)
ON_UPDATE_COMMAND_UI(ID_SHOW_IFF_8, OnUpdateShowIFF8)
ON_COMMAND(ID_SHOW_IFF_9, OnShowIFF9)
ON_UPDATE_COMMAND_UI(ID_SHOW_IFF_9, OnUpdateShowIFF9)
ON_COMMAND(ID_TOGGLE_VIEWPOINT, OnToggleViewpoint)
ON_COMMAND(ID_REVERT, OnRevert)
ON_UPDATE_COMMAND_UI(ID_REVERT, OnUpdateRevert)
ON_WM_SETCURSOR()
ON_COMMAND(ID_HIDE_MARKED_OBJECTS, OnHideMarkedObjects)
ON_COMMAND(ID_SHOW_HIDDEN_OBJECTS, OnShowHiddenObjects)
ON_COMMAND(ID_EDIT_LOCK_MARKED_OBJECTS, OnLockMarkedObjects)
ON_COMMAND(ID_EDIT_UNLOCK_ALL_OBJECTS, OnUnlockAllObjects)
ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
ON_COMMAND(ID_EDITORS_BRIEFING, OnEditorsBriefing)
ON_COMMAND(ID_EDITORS_DEBRIEFING, OnEditorsDebriefing)
ON_COMMAND(ID_SAVE_CAMERA, OnSaveCamera)
ON_COMMAND(ID_RESTORE_CAMERA, OnRestoreCamera)
ON_UPDATE_COMMAND_UI(ID_RESTORE_CAMERA, OnUpdateRestoreCamera)
ON_COMMAND(ID_SHOW_SEXP_HELP, OnShowSexpHelp)
ON_UPDATE_COMMAND_UI(ID_SHOW_SEXP_HELP, OnUpdateShowSexpHelp)
ON_COMMAND(ID_LOOKAT_OBJ, OnLookatObj)
ON_UPDATE_COMMAND_UI(ID_LOOKAT_OBJ, OnUpdateLookatObj)
ON_COMMAND(ID_EDITORS_ADJUST_GRID, OnEditorsAdjustGrid)
ON_COMMAND(ID_CALC_RELATIVE_COORDS, OnCalcRelativeCoords)
ON_COMMAND(ID_REORDER, OnReorder)
ON_COMMAND(ID_MUSIC_PLAYER, OnMusicPlayer)
ON_COMMAND(ID_EDITORS_SHIELD_SYS, OnEditorsShieldSys)
ON_COMMAND(ID_LEVEL_OBJ, OnLevelObj)
ON_COMMAND(ID_ALIGN_OBJ, OnAlignObj)
ON_COMMAND(ID_CONTROL_OBJ, OnControlObj)
ON_COMMAND(ID_NEXT_OBJ, OnNextObj)
ON_COMMAND(ID_PREV_OBJ, OnPrevObj)
ON_COMMAND(ID_EDIT_CLONEMARKEDOBJECTS, OnEditCloneMarkedObjects)
ON_COMMAND(ID_EDIT_DELETE_WING, OnEditDeleteWing)
ON_COMMAND(ID_MARK_WING, OnMarkWing)
ON_UPDATE_COMMAND_UI(ID_CONTROL_OBJ, OnUpdateControlObj)
ON_COMMAND(ID_EDIT_DELETE, OnEditDelete)
ON_COMMAND(ID_AA_GRIDLINES, OnAaGridlines)
ON_UPDATE_COMMAND_UI(ID_AA_GRIDLINES, OnUpdateAaGridlines)
ON_COMMAND(ID_EDITORS_CMD_BRIEF, OnCmdBrief)
ON_COMMAND(ID_DISABLE_UNDO, OnDisableUndo)
ON_UPDATE_COMMAND_UI(ID_DISABLE_UNDO, OnUpdateDisableUndo)
ON_UPDATE_COMMAND_UI(ID_EDITORS_CMD_BRIEF, OnUpdateCmdBrief)
ON_COMMAND(ID_NEXT_SUBSYS, OnNextSubsys)
ON_COMMAND(ID_PREV_SUBSYS, OnPrevSubsys)
ON_COMMAND(ID_CANCEL_SUBSYS, OnCancelSubsys)
ON_COMMAND(ID_DUMP_STATS, OnDumpStats)
ON_COMMAND(ID_SHOW_PATHS, OnShowPaths)
ON_UPDATE_COMMAND_UI(ID_SHOW_PATHS, OnUpdateShowPaths)
ON_COMMAND(ID_SHOW_DOCK_POINTS, OnShowDockPoints)
ON_UPDATE_COMMAND_UI(ID_SHOW_DOCK_POINTS, OnUpdateShowDockPoints)
ON_COMMAND(ID_FORMAT_FS2_OPEN, OnFormatFs2Open)
ON_UPDATE_COMMAND_UI(ID_FORMAT_FS2_OPEN, OnUpdateFormatFs2Open)
ON_COMMAND(ID_FORMAT_FS2_OPEN_COMP, OnFormatFs2OpenComp)
ON_UPDATE_COMMAND_UI(ID_FORMAT_FS2_OPEN_COMP, OnUpdateFormatFs2OpenComp)
ON_COMMAND(ID_FORMAT_FS2_RETAIL, OnFormatFs2Retail)
ON_UPDATE_COMMAND_UI(ID_FORMAT_FS2_RETAIL, OnUpdateFormatFs2Retail)
ON_COMMAND(ID_FORMAT_FS1_RETAIL, OnFormatFs1Retail)
ON_UPDATE_COMMAND_UI(ID_FORMAT_FS1_RETAIL, OnUpdateFormatFs1Retail)
ON_COMMAND(ID_MISC_MOVESHIPSWHENUNDOCKING, OnMoveShipsWhenUndocking)
ON_UPDATE_COMMAND_UI(ID_MISC_MOVESHIPSWHENUNDOCKING, OnUpdateMoveShipsWhenUndocking)
ON_COMMAND(ID_MISC_POINTUSINGUVEC, OnPointUsingUvec)
ON_UPDATE_COMMAND_UI(ID_MISC_POINTUSINGUVEC, OnUpdatePointUsingUvec)
ON_COMMAND(ID_HIGHLIGHT_SUBSYS, OnHighlightSubsys)
ON_UPDATE_COMMAND_UI(ID_HIGHLIGHT_SUBSYS, OnUpdateHighlightSubsys)
ON_COMMAND(ID_EDITORS_SET_GLOBAL_SHIP_FLAGS, OnEditorsSetGlobalShipFlags)
ON_COMMAND(ID_EDITORS_VOICE, OnEditorsVoiceManager)
ON_COMMAND(ID_EDITORS_FICTION, OnEditorsFiction)
ON_WM_DESTROY()
ON_WM_CREATE()
ON_WM_ERASEBKGND()
ON_COMMAND(ID_VIEW_LIGHTING, OnViewLighting)
ON_UPDATE_COMMAND_UI(ID_VIEW_LIGHTING, OnUpdateViewLighting)
ON_COMMAND(ID_VIEW_FULL_DETAIL, OnViewFullDetail)
ON_UPDATE_COMMAND_UI(ID_VIEW_FULL_DETAIL, OnUpdateViewFullDetail)
//}}AFX_MSG_MAP
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
ON_COMMAND_RANGE(ID_GROUP1, ID_GROUP9, OnGroup)
ON_COMMAND_RANGE(ID_SET_GROUP1, ID_SET_GROUP9, OnSetGroup)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CFREDView construction/destruction
CFREDView::CFREDView()
{
//m_ConfirmDeleting = TRUE;
//m_ShowCapitalShips = TRUE;
//m_ShowElevations = TRUE;
//m_ShowFighters = TRUE;
//m_ShowGrid = TRUE;
//m_ShowMiscObjects = TRUE;
//m_ShowPlanets = TRUE;
//m_ShowWaypoints = TRUE;
// this is stupid
ID_SHOW_IFF.push_back(ID_SHOW_IFF_0);
ID_SHOW_IFF.push_back(ID_SHOW_IFF_1);
ID_SHOW_IFF.push_back(ID_SHOW_IFF_2);
ID_SHOW_IFF.push_back(ID_SHOW_IFF_3);
ID_SHOW_IFF.push_back(ID_SHOW_IFF_4);
ID_SHOW_IFF.push_back(ID_SHOW_IFF_5);
ID_SHOW_IFF.push_back(ID_SHOW_IFF_6);
ID_SHOW_IFF.push_back(ID_SHOW_IFF_7);
ID_SHOW_IFF.push_back(ID_SHOW_IFF_8);
ID_SHOW_IFF.push_back(ID_SHOW_IFF_9);
m_pGDlg = new CGrid(this);
//if (!(int errno = gr_init(640, 480, 32)))
// Error(LOCATION, "Hey, gr_init failed! Error code = %i", errno);
Fred_view_wnd = this;
}
CFREDView::~CFREDView()
{
delete m_pGDlg;
}
BOOL CFREDView::PreCreateWindow(CREATESTRUCT& cs)
{
BOOL casperl;
casperl = CView::PreCreateWindow(cs);
cs.y = 0; // doesn't seem to do anything. :(
// other miscellaneous initializations
if (Cmdline_mod)
cfile_chdir(Cmdline_mod);
cfile_chdir("data\\missions");
set_physics_controls();
return casperl;
}
/////////////////////////////////////////////////////////////////////////////
// CFREDView drawing
void CFREDView::OnDraw(CDC* pDC)
{
CRect clip;
CFREDDoc* pDoc = GetDocument();
Assert(pDoc != nullptr);
Update_window = 1;
}
/////////////////////////////////////////////////////////////////////////////
// CFREDView printing
BOOL CFREDView::OnPreparePrinting(CPrintInfo* pInfo)
{
// default preparation
return DoPreparePrinting(pInfo);
}
void CFREDView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add extra initialization before printing
}
void CFREDView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add cleanup after printing
}
/////////////////////////////////////////////////////////////////////////////
// CFREDView diagnostics
#ifdef _DEBUG
void CFREDView::AssertValid() const
{
CView::AssertValid();
}
void CFREDView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CFREDDoc* CFREDView::GetDocument() // non-debug version is inline
{
Assert(m_pDocument->IsKindOf(RUNTIME_CLASS(CFREDDoc)));
return (CFREDDoc*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CFREDView message handlers
void CFREDView::OnViewGrid()
{
Show_grid = !Show_grid;
Update_window = 1;
}
void CFREDView::OnUpdateViewGrid(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_grid);
}
void CFREDView::OnViewWaypoints()
{
Show_waypoints = !Show_waypoints;
correct_marking();
Update_window = 1;
}
void CFREDView::OnUpdateViewWaypoints(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_waypoints);
}
int duplicate_marked_objects()
{
int z, cobj, flag;
object *objp, *ptr;
cobj = Duped_wing = -1;
flag = 0;
int duping_waypoint_list = -1;
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
Assert(objp->type != OBJ_NONE);
if (objp->flags[Object::Object_Flags::Marked]) {
if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
z = Ships[objp->instance].wingnum;
if (!flag)
Duped_wing = z;
else if (Duped_wing != z)
Duped_wing = -1;
} else {
Duped_wing = -1;
}
// make sure we dup as many waypoint lists as we have
if (objp->type == OBJ_WAYPOINT) {
int this_list = calc_waypoint_list_index(objp->instance);
if (duping_waypoint_list != this_list) {
dup_object(nullptr); // reset waypoint list
duping_waypoint_list = this_list;
}
}
flag = 1;
z = dup_object(objp);
if (z == -1) {
cobj = -1;
break;
}
if (cur_object_index == OBJ_INDEX(objp) )
cobj = z;
}
objp = GET_NEXT(objp);
}
obj_merge_created_list();
// I think this code is to catch the case where an object wasn't created for whatever reason;
// in this case just delete the remaining objects we just created
if (cobj == -1) {
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
ptr = GET_NEXT(objp);
if (objp->flags [Object::Object_Flags::Temp_marked])
delete_object(objp);
objp = ptr;
}
button_down = 0;
return -1;
}
unmark_all();
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
if (objp->flags [Object::Object_Flags::Temp_marked]) {
objp->flags.remove(Object::Object_Flags::Temp_marked);
mark_object(OBJ_INDEX(objp));
}
objp = GET_NEXT(objp);
}
set_cur_object_index(cobj);
return 0;
}
#define MAX_MOVE_DISTANCE 25.0f
// If cur_object_index references a valid object, drag it from its current
// location to the new cursor location specified by "point".
// It is dragged relative to the main grid. Its y coordinate is not changed.
// Return value: 0/1 = didn't/did move object all the way to goal.
int drag_objects()
{
int rval = 1;
float r;
float distance_moved = 0.0f;
vec3d cursor_dir, int_pnt;
vec3d movement_vector;
vec3d obj;
vec3d vec1, vec2;
object *objp;
// starfield_bitmaps *bmp;
/*
if (Bg_bitmap_dialog) {
if (Cur_bitmap < 0)
return -1;
bmp = &Starfield_bitmaps[Cur_bitmap];
if (Single_axis_constraint && Constraint.z) {
bmp->dist *= 1.0f + mouse_dx / -800.0f;
calculate_bitmap_points(bmp, 0.0f);
} else {
g3_point_to_vec_delayed(&bmp->m.fvec, marking_box.x2, marking_box.y2);
vm_orthogonalize_matrix(&bmp->m);
calculate_bitmap_points(bmp, 0.0f);
}
return rval;
}
*/
// Do not move ships that we are currently centered around (Lookat_mode). The vector math will start going haywire and return NAN
if (!query_valid_object() || Lookat_mode)
return -1;
if ((Dup_drag == 1) && (Briefing_dialog))
Dup_drag = 0;
if (Dup_drag == 1) {
if (duplicate_marked_objects() < 0)
return -1;
if (Duped_wing != -1)
Dup_drag = DUP_DRAG_OF_WING; // indication for later that we duped objects in a wing
else
Dup_drag = 0;
drag_rotate_save_backup();
set_modified();
Update_window = 1;
}
objp = &Objects[cur_object_index];
Assert(objp->type != OBJ_NONE);
obj = int_pnt = objp->pos;
// Get 3d vector specified by mouse cursor location.
g3_point_to_vec_delayed(&cursor_dir, marking_box.x2, marking_box.y2);
if (Single_axis_constraint) {
// if (fvi_ray_plane(&int_pnt, &obj, &view_orient.fvec, &view_pos, &cursor_dir, 0.0f) >= 0.0f ) {
// vm_vec_add(&p1, &obj, &Constraint);
// find_nearest_point_on_line(&nearest_point, &obj, &p1, &int_pnt);
// int_pnt = nearest_point;
// distance_moved = vm_vec_dist(&obj, &int_pnt);
// }
vec3d tmpAnticonstraint = Anticonstraint;
vec3d tmpObject = obj;
tmpAnticonstraint.xyz.x = 0.0f;
r = fvi_ray_plane(&int_pnt, &tmpObject, &tmpAnticonstraint, &view_pos, &cursor_dir, 0.0f);
// If intersected behind viewer, don't move. Too confusing, not what user wants.
vm_vec_sub(&vec1, &int_pnt, &view_pos);
vm_vec_sub(&vec2, &obj, &view_pos);
if ((r>=0.0f) && (vm_vec_dot(&vec1, &vec2) >= 0.0f)) {
vec3d tmp1;
vm_vec_sub( &tmp1, &int_pnt, &obj );
tmp1.xyz.x *= Constraint.xyz.x;
tmp1.xyz.y *= Constraint.xyz.y;
tmp1.xyz.z *= Constraint.xyz.z;
vm_vec_add( &int_pnt, &obj, &tmp1 );
distance_moved = vm_vec_dist(&obj, &int_pnt);
}
} else { // Move in x-z plane, defined by grid. Preserve height.
r = fvi_ray_plane(&int_pnt, &obj, &Anticonstraint, &view_pos, &cursor_dir, 0.0f);
// If intersected behind viewer, don't move. Too confusing, not what user wants.
vm_vec_sub(&vec1, &int_pnt, &view_pos);
vm_vec_sub(&vec2, &obj, &view_pos);
if ((r>=0.0f) && (vm_vec_dot(&vec1, &vec2) >= 0.0f))
distance_moved = vm_vec_dist(&obj, &int_pnt);
}
// If moved too far, then move max distance along vector.
vm_vec_sub(&movement_vector, &int_pnt, &obj);
/* if (distance_moved > MAX_MOVE_DISTANCE) {
vm_vec_normalize(&movement_vector);
vm_vec_scale(&movement_vector, MAX_MOVE_DISTANCE);
} */
if (distance_moved) {
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
Assert(objp->type != OBJ_NONE);
if (objp->flags[Object::Object_Flags::Marked]) {
vm_vec_add(&objp->pos, &objp->pos, &movement_vector);
if (objp->type == OBJ_WAYPOINT) {
waypoint *wpt = find_waypoint_with_instance(objp->instance);
Assert(wpt != NULL);
wpt->set_pos(&objp->pos);
}
}
objp = GET_NEXT(objp);
}
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
if (objp->flags[Object::Object_Flags::Marked])
object_moved(objp);
objp = GET_NEXT(objp);
}
}
if (Briefing_dialog)
Briefing_dialog->update_positions();
set_modified();
return rval;
}
void drag_rotate_save_backup()
{
object *objp;
/*
if (Cur_bitmap != -1)
bitmap_matrix_backup = Starfield_bitmaps[Cur_bitmap].m;
*/
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
Assert(objp->type != OBJ_NONE);
if (objp->flags[Object::Object_Flags::Marked]) {
rotation_backup[OBJ_INDEX(objp)].pos = objp->pos;
rotation_backup[OBJ_INDEX(objp)].orient = objp->orient;
}
objp = GET_NEXT(objp);
}
}
int drag_rotate_objects()
{
int rval = 1;
vec3d int_pnt, obj;
angles a;
matrix leader_orient, leader_transpose, tmp, newmat, rotmat;
object *leader, *objp;
// starfield_bitmaps *bmp;
Update_window = 1;
/*
if (Bg_bitmap_dialog) {
if (Cur_bitmap < 0)
return -1;
bmp = &Starfield_bitmaps[Cur_bitmap];
calculate_bitmap_points(bmp, mouse_dx / -300.0f);
return rval;
}
*/
if (!query_valid_object()){
return -1;
}
objp = &Objects[cur_object_index];
Assert(objp->type != OBJ_NONE);
obj = int_pnt = objp->pos;
memset(&a, 0, sizeof(angles));
if (Single_axis_constraint) {
if (Constraint.xyz.x)
a.p = mouse_dy / REDUCER;
else if (Constraint.xyz.y)
a.h = mouse_dx / REDUCER;
else if (Constraint.xyz.z)
a.b = -mouse_dx / REDUCER;
} else {
if (!Constraint.xyz.x) { // yz
a.b = -mouse_dx / REDUCER;
a.h = mouse_dy / REDUCER;
} else if (!Constraint.xyz.y) { // xz
a.p = mouse_dy / REDUCER;
a.b = -mouse_dx / REDUCER;
} else if (!Constraint.xyz.z) { // xy
a.p = mouse_dy / REDUCER;
a.h = mouse_dx / REDUCER;
}
}
leader = &Objects[cur_object_index];
leader_orient = leader->orient; // save original orientation
vm_copy_transpose(&leader_transpose, &leader_orient);
vm_angles_2_matrix(&rotmat, &a);
vm_matrix_x_matrix(&newmat, &leader->orient, &rotmat);
leader->orient = newmat;
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
Assert(objp->type != OBJ_NONE);
if ((objp->flags[Object::Object_Flags::Marked]) && (cur_object_index != OBJ_INDEX(objp) )) {
if (Group_rotate) {
matrix rot_trans;
vec3d tmpv1, tmpv2;
// change rotation matrix to rotate in opposite direction. This rotation
// matrix is what the leader ship has rotated by.
vm_copy_transpose(&rot_trans, &rotmat);
// get point relative to our point of rotation (make POR the origin).
vm_vec_sub(&tmpv1, &objp->pos, &leader->pos);
// convert point from real-world coordinates to leader's relative coordinate
// system (z=forward vec, y=up vec, x=right vec
vm_vec_rotate(&tmpv2, &tmpv1, &leader_orient);
// now rotate the point by the transpose from above.
vm_vec_rotate(&tmpv1, &tmpv2, &rot_trans);
// convert point back into real-world coordinates
vm_vec_rotate(&tmpv2, &tmpv1, &leader_transpose);
// and move origin back to real-world origin. Object is now at its correct
// position.
vm_vec_add(&objp->pos, &leader->pos, &tmpv2);
// Now fix the object's orientation to what it should be.
vm_matrix_x_matrix(&tmp, &objp->orient, &rotmat);
vm_orthogonalize_matrix(&tmp); // safety check
objp->orient = tmp;
} else {
vm_matrix_x_matrix(&tmp, &objp->orient, &rotmat);
objp->orient = tmp;
}
}
objp = GET_NEXT(objp);
}
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
if (objp->flags[Object::Object_Flags::Marked])
object_moved(objp);
objp = GET_NEXT(objp);
}
set_modified();
return rval;
}
void cancel_drag()
{
Update_window = 1;
/*
if (Bg_bitmap_dialog) {
Assert(!vm_check_matrix_for_zeros(&bitmap_matrix_backup));
Starfield_bitmaps[Cur_bitmap].m = bitmap_matrix_backup;
calculate_bitmap_points(&Starfield_bitmaps[Cur_bitmap], 0.0f);
button_down = box_marking = 0;
Update_window = 1;
return;
}
*/
if (button_down) {
if (Editing_mode == 1) {
vec3d movement_vector;
object *objp;
if (query_valid_object()) {
objp = &Objects[cur_object_index];
Assert(objp->type != OBJ_NONE);
vm_vec_sub(&movement_vector, &original_pos, &objp->pos);
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
Assert(objp->type != OBJ_NONE);
if (objp->flags[Object::Object_Flags::Marked])
vm_vec_add(&objp->pos, &objp->pos, &movement_vector);
objp = GET_NEXT(objp);
}
}
} else if (Editing_mode == 2) {
object *objp;
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
Assert(objp->type != OBJ_NONE);
if (objp->flags[Object::Object_Flags::Marked]) {
int obj_index = OBJ_INDEX(objp);
if(!IS_VEC_NULL(&rotation_backup[obj_index].orient.vec.rvec) &&
!IS_VEC_NULL(&rotation_backup[obj_index].orient.vec.uvec) &&
!IS_VEC_NULL(&rotation_backup[obj_index].orient.vec.fvec)){
objp->pos = rotation_backup[obj_index].pos;
objp->orient = rotation_backup[obj_index].orient;
}
}
objp = GET_NEXT(objp);
}
}
}
button_down = box_marking = 0;
if (Briefing_dialog)
Briefing_dialog->update_positions();
}
void CFREDView::OnLButtonDown(UINT nFlags, CPoint point)
{
// RT point
int waypoint_instance = -1;
if (!Fred_active) {
CView::OnLButtonDown(nFlags, point);
return;
}
if (cur_waypoint != NULL)
{
Assert(cur_waypoint_list != NULL);
waypoint_instance = Objects[cur_waypoint->get_objnum()].instance;
}
marking_box.x1 = point.x;
marking_box.y1 = point.y;
Dup_drag = 0;
on_object = select_object(point.x, point.y);
button_down = 1;
SetCapture();
drag_rotate_save_backup();
if (nFlags & MK_CONTROL) { // add a new object
bool shift_down = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
if (shift_down) {
Selection_lock = 0; // force off selection lock
on_object = create_object_on_grid(waypoint_instance, true);
} else {
if (!Bg_bitmap_dialog) {
if (on_object == -1) {
Selection_lock = 0; // force off selection lock
on_object = create_object_on_grid(waypoint_instance);
} else
Dup_drag = 1;
} else {
/*
Selection_lock = 0; // force off selection lock
on_object = Cur_bitmap = create_bg_bitmap();
Bg_bitmap_dialog->update_data();
Update_window = 1;
if (Cur_bitmap == -1)
MessageBox("Background bitmap limit reached.\nCan't add more.");
*/
}
}
} else if (!Selection_lock) {
if (Bg_bitmap_dialog) {
Cur_bitmap = on_object;
Bg_bitmap_dialog -> update_data();
} else if ((nFlags & MK_SHIFT) || (on_object == -1) || !(Objects[on_object].flags[Object::Object_Flags::Marked])) {
if (!(nFlags & MK_SHIFT))
unmark_all();
if (on_object != -1) {
if (Objects[on_object].flags[Object::Object_Flags::Marked])
unmark_object(on_object);
else
mark_object(on_object);
}
}
}
if (query_valid_object())
original_pos = Objects[cur_object_index].pos;
moved = 0;
if (Selection_lock) {
if (Editing_mode == 1)
drag_objects();
else if (Editing_mode == 2)
drag_rotate_objects();
Update_window = 1;
}
if (query_valid_object() && (Marked == 1) && (Objects[cur_object_index].type == OBJ_POINT)) {
Assert(Briefing_dialog);
Briefing_dialog->icon_select(Objects[cur_object_index].instance);
} else {
if (Briefing_dialog)
Briefing_dialog->icon_select(-1);
}
CView::OnLButtonDown(nFlags, point);
}
void CFREDView::OnMouseMove(UINT nFlags, CPoint point)
{
// RT point
mouse_dx = point.x - last_mouse_x;
mouse_dy = point.y - last_mouse_y;
last_mouse_x = marking_box.x2 = point.x;
last_mouse_y = marking_box.y2 = point.y;
Cursor_over = select_object(point.x, point.y);
// Orbit camera: middle button drag
if (m_orbit_dragging && (nFlags & MK_MBUTTON)) {
handle_orbit_drag(point, nFlags);
CView::OnMouseMove(nFlags, point);
return;
}
// Orbit camera: right button drag
if (m_rbutton_down && (nFlags & MK_RBUTTON) && viewpoint == 0 && Control_mode == 0) {
if (!m_rbutton_moved) {
if (abs(point.x - m_rbutton_down_point.x) > 2 || abs(point.y - m_rbutton_down_point.y) > 2)
m_rbutton_moved = true;
}
if (m_rbutton_moved) {
handle_orbit_drag(point, nFlags);
CView::OnMouseMove(nFlags, point);
return;
}
}
if (!(nFlags & MK_LBUTTON))
button_down = 0;
// The following will cancel a drag operation if another program running in memory
// happens to jump in and take over (such as new email has arrived popup boxes).
if (button_down && GetCapture() != this)
cancel_drag();
if (!button_down && !m_orbit_dragging && !m_rbutton_down && GetCapture() == this)
ReleaseCapture();
if (button_down) {
if (abs(marking_box.x1 - marking_box.x2) > 1 || abs(marking_box.y1 - marking_box.y2) > 1)
moved = 1;
if (moved) {
if (on_object != -1 || Selection_lock) {
if (Editing_mode == 1)
drag_objects();
else if (Editing_mode == 2)
drag_rotate_objects();
} else if (!Bg_bitmap_dialog)
box_marking = 1;
if (mouse_dx || mouse_dy)
Update_window = 1;
}
}
CView::OnMouseMove(nFlags, point);
}
void CFREDView::OnLButtonUp(UINT nFlags, CPoint point)
{
marking_box.x2 = point.x;
marking_box.y2 = point.y;
if (button_down && GetCapture() != this)
cancel_drag();
if (!m_orbit_dragging && !m_rbutton_down && GetCapture() == this)
ReleaseCapture();
if (button_down) {
if ((abs(marking_box.x1 - marking_box.x2) > 1) || (abs(marking_box.y1 - marking_box.y2) > 1))
moved = 1;
if (moved) {
if ((on_object != -1) || Selection_lock) {
if (Editing_mode == 1)
drag_objects();
else if ((Editing_mode == 2) || (Editing_mode == 3))
drag_rotate_objects();
set_modified();
FREDDoc_ptr->autosave("object move");
} else
box_marking = 1;
}
if (Bg_bitmap_dialog) {
box_marking = 0;
} else {
if (box_marking) {
select_objects();
box_marking = 0;
} else if ((!moved && on_object != -1) && !Selection_lock && !(nFlags & MK_SHIFT)) {
unmark_all();
mark_object(on_object);
}
}
button_down = 0;
Update_window = 1;
if (Dup_drag == DUP_DRAG_OF_WING) {
char msg[256];
int ship;
object *objp;
sprintf(msg, "Add cloned ships to wing %s?", Wings[Duped_wing].name);
if (MessageBox(msg, "Query", MB_YESNO) == IDYES) {
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
if (objp->flags[Object::Object_Flags::Marked]) {
if (Wings[Duped_wing].wave_count >= MAX_SHIPS_PER_WING) {
MessageBox("Max ships per wing limit reached");
break;
}
// Can't do player starts, since only player 1 is currently allowed to be in a wing
Assert(objp->type == OBJ_SHIP);
ship = objp->instance;
Assert(Ships[ship].wingnum == -1);
char new_name[NAME_LENGTH];
wing_bash_ship_name(new_name, Wings[Duped_wing].name, Wings[Duped_wing].wave_count + 1);
rename_ship(ship, new_name);
// bash it again for the display name
wing_bash_ship_name(&Ships[ship], &Wings[Duped_wing], Wings[Duped_wing].wave_count + 1, true);
Wings[Duped_wing].ship_index[Wings[Duped_wing].wave_count] = ship;
Ships[ship].wingnum = Duped_wing;
wing_objects[Duped_wing][Wings[Duped_wing].wave_count] = OBJ_INDEX(objp);
Wings[Duped_wing].wave_count++;
}
objp = GET_NEXT(objp);
}
}
}
}
if (query_valid_object() && (Marked == 1) && (Objects[cur_object_index].type == OBJ_POINT)) {
Assert(Briefing_dialog);
Briefing_dialog->icon_select(Objects[cur_object_index].instance);
} else {
if (Briefing_dialog)
Briefing_dialog->icon_select(-1);
}
CView::OnLButtonUp(nFlags, point);
}
// ---------- Orbit camera mouse handlers ----------
void CFREDView::handle_orbit_drag(CPoint point, UINT nFlags)
{
int dx = point.x - m_orbit_last_mouse.x;
int dy = point.y - m_orbit_last_mouse.y;
m_orbit_last_mouse = point;
if (nFlags & MK_SHIFT)
orbit_camera_pan(dx, dy);
else
orbit_camera_rotate(dx, dy);
Update_window = 1;
}
void CFREDView::OnMButtonDown(UINT nFlags, CPoint point)
{
if (viewpoint != 0 || Control_mode != 0)
return;
vec3d pivot = orbit_camera_get_pivot();
auto grid_orient = The_grid ? &The_grid->gmatrix : nullptr;
orbit_camera_init_from_current_view(&pivot, grid_orient);
m_orbit_dragging = true;
m_orbit_last_mouse = point;
SetCapture();
}
void CFREDView::OnMButtonUp(UINT nFlags, CPoint point)
{
if (m_orbit_dragging) {
m_orbit_dragging = false;
if (GetCapture() == this && !m_rbutton_down)
ReleaseCapture();
}
}
void CFREDView::OnRButtonDown(UINT nFlags, CPoint point)
{
m_rbutton_down = true;
m_rbutton_moved = false;
m_rbutton_down_point = point;
m_orbit_last_mouse = point;
if (viewpoint == 0 && Control_mode == 0) {
vec3d pivot = orbit_camera_get_pivot();
auto grid_orient = The_grid ? &The_grid->gmatrix : nullptr;
orbit_camera_init_from_current_view(&pivot, grid_orient);
SetCapture();
}
}
void CFREDView::OnRButtonUp(UINT nFlags, CPoint point)
{
bool was_dragging = m_rbutton_moved;
m_rbutton_down = false;
m_rbutton_moved = false;
if (GetCapture() == this && !m_orbit_dragging)
ReleaseCapture();
if (!was_dragging) {
// No drag occurred — show context menu as normal
CPoint screen_point = point;
ClientToScreen(&screen_point);
OnContextMenu(this, screen_point);
}
}
BOOL CFREDView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
if (viewpoint != 0 || Control_mode != 0)
return CView::OnMouseWheel(nFlags, zDelta, pt);
if (!Orbit_active) {
vec3d pivot = orbit_camera_get_pivot();
auto grid_orient = The_grid ? &The_grid->gmatrix : nullptr;
orbit_camera_init_from_current_view(&pivot, grid_orient);
}
orbit_camera_zoom(zDelta / -200.0f);
Update_window = 1;
return TRUE;
}
// ---------- End orbit camera mouse handlers ----------
// This function never gets called because nothing causes
// the WM_GOODBYE event to occur.
// False! When you close the Ship Dialog, this function is called! --MK, 8/30/96
LRESULT CFREDView::OnGoodbye(WPARAM wParam, LPARAM lParam)
{
Ship_editor_dialog.DestroyWindow();
Wing_editor_dialog.DestroyWindow();
return 0L;
}
void CFREDView::OnEditorsShips()
{
Assert(Ship_editor_dialog.GetSafeHwnd());
if (!theApp.init_window(&Ship_wnd_data, &Ship_editor_dialog, 0))
return;
Ship_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
Ship_editor_dialog.ShowWindow(SW_RESTORE);
}
void CFREDView::OnEditorsProps()
{
Assert(Prop_editor_dialog.GetSafeHwnd());
if (!theApp.init_window(&Prop_wnd_data, &Prop_editor_dialog, 0))
return;
Prop_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
Prop_editor_dialog.ShowWindow(SW_RESTORE);
}
void CFREDView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT lParam)
{
uint lKeyData;
lKeyData = lParam & 255; // key data
if (lParam & 256) lKeyData += 0x80;
key_mark(lKeyData, 1, 0);
CView::OnKeyDown(nChar, nRepCnt, lParam);
}
void CFREDView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT lParam)
{
uint lKeyData;
lKeyData = lParam & 255; // key data
if (lParam & 256) lKeyData += 0x80;
key_mark(lKeyData, 0, 0);
CView::OnKeyUp(nChar, nRepCnt, lParam);
}
void CFREDView::OnSetFocus(CWnd* pOldWnd)
{
static int flag = 0;
if (flag)
return;
flag = 1;
nprintf(("Fred routing", "OnSetFocus() called\n"));
if (Update_ship) {
Ship_editor_dialog.initialize_data(1);
Update_ship = 0;
}
if (Update_wing) {
Wing_editor_dialog.initialize_data(1);
Update_wing = 0;
}
if (Update_prop) {
Prop_editor_dialog.initialize_data(1);
Update_prop = 0;
}
if (update_dialog_boxes()) {
nprintf(("Fred routing", "OnSetFocus() returned (error occured)\n"));
flag = 0;
Ship_editor_dialog.bypass_errors = 0;
Wing_editor_dialog.bypass_errors = 0;
return; // abort
}
if (Local_modified) {
FREDDoc_ptr->autosave("Editing");
Local_modified = 0;
}
Fred_active = true;
CView::OnSetFocus(pOldWnd);
nprintf(("Fred routing", "Main window focus accepted\n"));
key_got_focus();
Cursor_over = -1;
if (GetActiveWindow() != Fred_main_wnd) {
// If you get this assert make sure code.lib is compiling under the flags FRED and FRED_OGL
Assert(Fred_main_wnd);
Fred_main_wnd->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
nprintf(("Fred routing", "OnSetFocus() had to put main window back on top\n"));
}
flag = 0;
}
void CFREDView::OnKillFocus(CWnd* pNewWnd)
{
nprintf(("Fred routing", "OnKillFocus() called\n"));
Fred_active = false;
Local_modified = 0;
CView::OnKillFocus(pNewWnd);
key_lost_focus();
Cursor_over = -1;
}
void CFREDView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
if (cy > 0) {
gr_screen_resize(cx, cy);
RedrawWindow();
}
}
void do_trackball_stuff(int nFlags, CPoint point)
{
int btn = 0;
if (nFlags & MK_LBUTTON){
btn |= 1;
}
if (nFlags & MK_RBUTTON){
btn |= 2;
}
move_mouse(btn, point.x, point.y);
}
// If add_flag != 0, then add found objects to current wing, else create new wing.
void select_objects()
{
int x, y, valid, icon_mode = 0;
vertex v;
object *ptr;
if (marking_box.x1 > marking_box.x2) {
x = marking_box.x1;
marking_box.x1 = marking_box.x2;
marking_box.x2 = x;
}
if (marking_box.y1 > marking_box.y2) {
y = marking_box.y1;
marking_box.y1 = marking_box.y2;
marking_box.y2 = y;
}
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
valid = 1;
if (ptr->flags.any_of(Object::Object_Flags::Hidden, Object::Object_Flags::Locked_from_editing))
valid = 0;
Assert(ptr->type != OBJ_NONE);
switch (ptr->type) {
case OBJ_WAYPOINT:
if (!Show_waypoints)
valid = 0;
break;
case OBJ_START:
if (!Show_starts || !Show_ships)
valid = 0;
break;
case OBJ_SHIP:
if (!Show_ships)
valid = 0;
if (!Show_iff[Ships[ptr->instance].team])
valid = 0;
break;
}
g3_rotate_vertex(&v, &ptr->pos);
if (!(v.codes & CC_BEHIND) && valid)
if (!(g3_project_vertex(&v) & PF_OVERFLOW)) {
x = (int) v.screen.xyw.x;
y = (int) v.screen.xyw.y;
if (x >= marking_box.x1 && x <= marking_box.x2 && y >= marking_box.y1 && y <= marking_box.y2) {
if (ptr->flags[Object::Object_Flags::Marked])
unmark_object(OBJ_INDEX(ptr));
else
mark_object(OBJ_INDEX(ptr));
if (ptr->type == OBJ_POINT)
icon_mode = 1;
}
}
ptr = GET_NEXT(ptr);
}
if (icon_mode) {
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if ((ptr->flags[Object::Object_Flags::Marked]) && (ptr->type != OBJ_POINT))
unmark_object(OBJ_INDEX(ptr));
ptr = GET_NEXT(ptr);
}
}
Update_ship = Update_wing = Update_prop = 1;
}
LRESULT CFREDView::OnMenuPopupShips(WPARAM wParam, LPARAM lParam)
{
CMenu menu;
CPoint point;
point = * ((CPoint*) lParam);
ClientToScreen(&point);
menu.LoadMenu(IDR_MENU_SHIP_POPUP);
menu.GetSubMenu(0)->TrackPopupMenu(
TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
return 0L;
}
LRESULT CFREDView::OnMenuPopupEdit(WPARAM wParam, LPARAM lParam)
{
CMenu menu;
CPoint point;
point = * ((CPoint*) lParam);
ClientToScreen(&point);
menu.LoadMenu(IDR_MENU_EDIT_POPUP);
menu.GetSubMenu(0)->TrackPopupMenu(
TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
return 0L;
}
int g_Ships_as_icons = 0;
void CFREDView::OnMiscstuffShowshipsasicons()
{
Update_window = 1;
if (g_Ships_as_icons == 0)
g_Ships_as_icons = 1;
else
g_Ships_as_icons = 0;
}
// right mouse button popup menu stuff
void CFREDView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
{
// make sure window is active
// GetParentFrame()->ActivateFrame();
CMenu menu;
int objnum;
CPoint local = point;
if (button_down) {
cancel_drag();
return;
}
ScreenToClient(&local);
objnum = select_object(local.x, local.y);
if (objnum != -1) {
set_cur_object_index(objnum);
if (menu.LoadMenu(IDR_MENU_SHIP_POPUP)) {
int id = ID_EDITORS_SHIPS;
CMenu* pPopup = menu.GetSubMenu(0);
Assert(pPopup != nullptr);
if (Marked > 1)
pPopup->ModifyMenu(ID_EDITORS_SHIPS, MF_BYCOMMAND | MF_STRING, ID_EDITORS_SHIPS, "Edit Marked Ships");
else {
CString str;
if ((Objects[objnum].type == OBJ_START) || (Objects[objnum].type == OBJ_SHIP)) {
str.Format("Edit %s", Ships[Objects[objnum].instance].ship_name);
} else if (Objects[objnum].type == OBJ_PROP) {
id = ID_EDITORS_PROPS;
str.Format("Edit %s", prop_id_lookup(Objects[objnum].instance)->prop_name);
} else if (Objects[objnum].type == OBJ_JUMP_NODE) {
auto jnp = jumpnode_get_by_objnum(objnum);
Assert(jnp != nullptr);
id = ID_EDITORS_JUMPNODE;
str.Format("Edit %s", jnp->GetName());
} else if (Objects[objnum].type == OBJ_WAYPOINT) {
char text[NAME_LENGTH];
waypoint_stuff_name(text, Objects[objnum].instance);
id = ID_EDITORS_WAYPOINT;
str.Format("Edit %s", text);
} else if (Objects[objnum].type == OBJ_POINT) {
return;
} else {
Int3();
str = _T("Unknown");
}
pPopup->ModifyMenu(ID_EDITORS_SHIPS, MF_BYCOMMAND | MF_STRING, id, str);
}
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd()); // use main window for cmds
}
} else {
if (menu.LoadMenu(IDR_MENU_EDIT_POPUP)) {
size_t i;
CMenu* pPopup = menu.GetSubMenu(0);
CMenu shipPopup, player_submenu;
CMenu *species_submenu = new CMenu[Species_info.size()];
Assert(pPopup != nullptr);
// create a popup menu based on the ship models read in ship.cpp.
// note: the offsets from SHIP_TYPES must correspond to the indexes in the ship type dropdown
shipPopup.CreatePopupMenu();
shipPopup.AppendMenu(MF_STRING | MF_ENABLED, SHIP_TYPES + Id_select_type_waypoint, "Waypoint");
shipPopup.AppendMenu(MF_STRING | MF_ENABLED, SHIP_TYPES + Id_select_type_jump_node, "Jump Node");
for (i=0; i<Species_info.size(); i++) {
species_submenu[i].CreatePopupMenu();
shipPopup.AppendMenu(MF_STRING | MF_POPUP | MF_ENABLED,
(UINT) species_submenu[i].m_hMenu, Species_info[i].species_name);
}
i = 0;
for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
if (it->flags[Ship::Info_Flags::No_fred])
continue; // because the dropdown skips "no_fred", we need to skip it here too
species_submenu[it->species].AppendMenu(MF_STRING |
MF_ENABLED, SHIP_TYPES + i, it->name);
i++;
}
pPopup->AppendMenu(MF_STRING | MF_POPUP | MF_ENABLED,
(UINT) shipPopup.m_hMenu, "New Object Type");
CWnd::DrawMenuBar(); // AppendMenu documentation says to do this.
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd());
}
}
}
void CFREDView::OnEditPopupShowShipIcons()
{
Show_ship_info = !Show_ship_info;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateEditPopupShowShipIcons(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_ship_info);
}
void CFREDView::OnEditPopupShowShipModels()
{
Show_ship_models = !Show_ship_models;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateEditPopupShowShipModels(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_ship_models);
}
void CFREDView::OnEditPopupShowCompass()
{
Show_compass = !Show_compass;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateEditPopupShowCompass(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_compass);
}
// View implementation file
CFREDView *CFREDView::GetView()
{
CFrameWnd *pFrame = (CFrameWnd *) (AfxGetApp()->m_pMainWnd);
CView *pView = pFrame->GetActiveView();
if (!pView)
return NULL;
// Fail if view is of wrong kind
// (this could occur with splitter windows, or additional
// views on a single document
if (! pView->IsKindOf(RUNTIME_CLASS(CFREDView)))
return NULL;
return (CFREDView *) pView;
}
// following code added by MWA 09/04/96
// Implements messages for popup menu built on the fly.
// not sure how stable the code is, but appears to work for now.
// id's for the menu items are simply the model numbers for
// the ships. Shouldn't conflict with any other ID_* thingys.
BOOL CFREDView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
if (!pHandlerInfo) {
if ((nID >= SHIP_TYPES) && (nID < SHIP_TYPES + ship_type_combo_box_size + 2)) { // the 2 are waypoint and jump node
int idx = static_cast<int>(nID - SHIP_TYPES);
if (nCode == CN_COMMAND) {
cur_ship_type_combo_index = idx;
m_new_ship_type_combo_box.SetCurSel(cur_ship_type_combo_index);
// now that we've updated the combo box, try to actually create something
// see also CFREDView::OnLButtonDown
int waypoint_instance = cur_waypoint ? Objects[cur_waypoint->get_objnum()].instance : -1;
Selection_lock = 0;
on_object = create_object_on_grid(waypoint_instance);
} else if (nCode == CN_UPDATE_COMMAND_UI) {
// Update UI element state
((CCmdUI*) pExtra)->SetCheck(cur_ship_type_combo_index == idx);
((CCmdUI*) pExtra)->Enable(TRUE);
}
return TRUE;
}
}
return CView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
void CFREDView::OnMiscStatistics()
{
char buf[2048];
sprintf(buf,
"Number of Objects: %d\n"
"Number of Ships: %d\n"
"Number of Wings: %d\n",
Num_objects, ship_get_num_ships(), Num_wings);
MessageBox(buf, "FRED Statistics");
}
void CFREDView::OnUpdateChangeViewpointExternal(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(!viewpoint);
}
void CFREDView::OnChangeViewpointExternal()
{
viewpoint = 0;
Update_window = 1;
}
void CFREDView::OnUpdateChangeViewpointFollow(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(viewpoint == 1);
}
void CFREDView::OnChangeViewpointFollow()
{
viewpoint = 1;
view_obj = cur_object_index;
Update_window = 1;
}
void CFREDView::OnOutlineLod(int lod)
{
Outline_lod = lod;
Update_window = 1;
}
void CFREDView::OnUpdateOutlineLod(int lod, CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Outline_lod == lod);
}
void CFREDView::OnOutlineLod0() { OnOutlineLod(0); }
void CFREDView::OnOutlineLod1() { OnOutlineLod(1); }
void CFREDView::OnOutlineLod2() { OnOutlineLod(2); }
void CFREDView::OnOutlineLod3() { OnOutlineLod(3); }
void CFREDView::OnOutlineLod4() { OnOutlineLod(4); }
void CFREDView::OnUpdateOutlineLod0(CCmdUI* pCmdUI) { OnUpdateOutlineLod(0, pCmdUI); }
void CFREDView::OnUpdateOutlineLod1(CCmdUI* pCmdUI) { OnUpdateOutlineLod(1, pCmdUI); }
void CFREDView::OnUpdateOutlineLod2(CCmdUI* pCmdUI) { OnUpdateOutlineLod(2, pCmdUI); }
void CFREDView::OnUpdateOutlineLod3(CCmdUI* pCmdUI) { OnUpdateOutlineLod(3, pCmdUI); }
void CFREDView::OnUpdateOutlineLod4(CCmdUI* pCmdUI) { OnUpdateOutlineLod(4, pCmdUI); }
void CFREDView::OnLabelFontScale(float scale)
{
Fred_label_font_scale = scale;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateLabelFontScale(float scale, CCmdUI* pCmdUI)
{
// tolerant compare, since the value is stored as a float derived from an int percentage
pCmdUI->SetCheck(fl_equal(Fred_label_font_scale, scale, 0.001f));
}
void CFREDView::OnLabelFontScale50() { OnLabelFontScale(0.5f); }
void CFREDView::OnLabelFontScale75() { OnLabelFontScale(0.75f); }
void CFREDView::OnLabelFontScale100() { OnLabelFontScale(1.0f); }
void CFREDView::OnLabelFontScale125() { OnLabelFontScale(1.25f); }
void CFREDView::OnLabelFontScale150() { OnLabelFontScale(1.5f); }
void CFREDView::OnLabelFontScale200() { OnLabelFontScale(2.0f); }
void CFREDView::OnLabelFontScale250() { OnLabelFontScale(2.5f); }
void CFREDView::OnLabelFontScale300() { OnLabelFontScale(3.0f); }
void CFREDView::OnLabelFontScale350() { OnLabelFontScale(3.5f); }
void CFREDView::OnLabelFontScale400() { OnLabelFontScale(4.0f); }
void CFREDView::OnUpdateLabelFontScale50(CCmdUI* pCmdUI) { OnUpdateLabelFontScale(0.5f, pCmdUI); }
void CFREDView::OnUpdateLabelFontScale75(CCmdUI* pCmdUI) { OnUpdateLabelFontScale(0.75f, pCmdUI); }
void CFREDView::OnUpdateLabelFontScale100(CCmdUI* pCmdUI) { OnUpdateLabelFontScale(1.0f, pCmdUI); }
void CFREDView::OnUpdateLabelFontScale125(CCmdUI* pCmdUI) { OnUpdateLabelFontScale(1.25f, pCmdUI); }
void CFREDView::OnUpdateLabelFontScale150(CCmdUI* pCmdUI) { OnUpdateLabelFontScale(1.5f, pCmdUI); }
void CFREDView::OnUpdateLabelFontScale200(CCmdUI* pCmdUI) { OnUpdateLabelFontScale(2.0f, pCmdUI); }
void CFREDView::OnUpdateLabelFontScale250(CCmdUI* pCmdUI) { OnUpdateLabelFontScale(2.5f, pCmdUI); }
void CFREDView::OnUpdateLabelFontScale300(CCmdUI* pCmdUI) { OnUpdateLabelFontScale(3.0f, pCmdUI); }
void CFREDView::OnUpdateLabelFontScale350(CCmdUI* pCmdUI) { OnUpdateLabelFontScale(3.5f, pCmdUI); }
void CFREDView::OnUpdateLabelFontScale400(CCmdUI* pCmdUI) { OnUpdateLabelFontScale(4.0f, pCmdUI); }
void CFREDView::OnEditorsGoals()
{
CMissionGoalsDlg dlg;
dlg.DoModal();
}
void CFREDView::OnEditorsCutscenes()
{
CMissionCutscenesDlg dlg;
dlg.DoModal();
}
void CFREDView::OnSpeed1()
{
physics_speed = 1;
set_physics_controls();
}
void CFREDView::OnSpeed2()
{
physics_speed = 2;
set_physics_controls();
}
void CFREDView::OnSpeed3()
{
physics_speed = 3;
set_physics_controls();
}
void CFREDView::OnSpeed5()
{
physics_speed = 5;
set_physics_controls();
}
void CFREDView::OnSpeed8()
{
physics_speed = 8;
set_physics_controls();
}
void CFREDView::OnSpeed10()
{
physics_speed = 10;
set_physics_controls();
}
void CFREDView::OnSpeed50()
{
physics_speed = 50;
set_physics_controls();
}
void CFREDView::OnSpeed100()
{
physics_speed = 100;
set_physics_controls();
}
void CFREDView::OnSpeed500()
{
physics_speed = 500;
set_physics_controls();
}
void CFREDView::OnSpeed1000()
{
physics_speed = 1000;
set_physics_controls();
}
void CFREDView::OnRot1()
{
physics_rot = 2;
set_physics_controls();
}
void CFREDView::OnRot2()
{
physics_rot = 10;
set_physics_controls();
}
void CFREDView::OnRot3()
{
physics_rot = 25;
set_physics_controls();
}
void CFREDView::OnRot4()
{
physics_rot = 50;
set_physics_controls();
}
void CFREDView::OnRot5()
{
physics_rot = 100;
set_physics_controls();
}
void CFREDView::OnUpdateSpeed1(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_speed == 1);
}
void CFREDView::OnUpdateSpeed2(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_speed == 2);
}
void CFREDView::OnUpdateSpeed3(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_speed == 3);
}
void CFREDView::OnUpdateSpeed5(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_speed == 5);
}
void CFREDView::OnUpdateSpeed8(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_speed == 8);
}
void CFREDView::OnUpdateSpeed10(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_speed == 10);
}
void CFREDView::OnUpdateSpeed50(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_speed == 50);
}
void CFREDView::OnUpdateSpeed100(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_speed == 100);
}
void CFREDView::OnUpdateSpeed500(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_speed == 500);
}
void CFREDView::OnUpdateSpeed1000(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_speed == 1000);
}
void CFREDView::OnUpdateRot1(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_rot == 2);
}
void CFREDView::OnUpdateRot2(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_rot == 10);
}
void CFREDView::OnUpdateRot3(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_rot == 25);
}
void CFREDView::OnUpdateRot4(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_rot == 50);
}
void CFREDView::OnUpdateRot5(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(physics_rot == 100);
}
void CFREDView::OnControlModeCamera()
{
Control_mode = 0;
}
void CFREDView::OnUpdateControlModeCamera(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(!Control_mode);
}
void CFREDView::OnControlModeShip()
{
Control_mode = 1;
}
void CFREDView::OnUpdateControlModeShip(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Control_mode == 1);
}
void CFREDView::OnShowGridPositions()
{
Show_grid_positions = !Show_grid_positions;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateShowGridPositions(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_grid_positions);
}
void CFREDView::OnShowCoordinates()
{
Show_coordinates = !Show_coordinates;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateShowCoordinates(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_coordinates);
}
void CFREDView::OnSelect()
{
Editing_mode = 0;
}
void CFREDView::OnUpdateSelect(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(!Editing_mode);
}
void CFREDView::OnSelectAndMove()
{
Editing_mode = 1;
}
void CFREDView::OnUpdateSelectAndMove(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Editing_mode == 1);
}
void CFREDView::OnSelectAndRotate()
{
Editing_mode = 2;
}
void CFREDView::OnUpdateSelectAndRotate(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Editing_mode == 2);
}
void CFREDView::OnConstrainX()
{
vm_vec_make(&Constraint, 1.0f, 0.0f, 0.0f);
vm_vec_make(&Anticonstraint, 0.0f, 1.0f, 1.0f);
Single_axis_constraint = 1;
}
void CFREDView::OnUpdateConstrainX(CCmdUI* pCmdUI)
{
pCmdUI->SetRadio(Constraint.xyz.x && !Constraint.xyz.y && !Constraint.xyz.z);
}
void CFREDView::OnConstrainY()
{
vm_vec_make(&Constraint, 0.0f, 1.0f, 0.0f);
vm_vec_make(&Anticonstraint, 1.0f, 0.0f, 1.0f);
Single_axis_constraint = 1;
}
void CFREDView::OnUpdateConstrainY(CCmdUI* pCmdUI)
{
pCmdUI->SetRadio(!Constraint.xyz.x && Constraint.xyz.y && !Constraint.xyz.z);
}
void CFREDView::OnConstrainZ()
{
vm_vec_make(&Constraint, 0.0f, 0.0f, 1.0f);
vm_vec_make(&Anticonstraint, 1.0f, 1.0f, 0.0f);
Single_axis_constraint = 1;
}
void CFREDView::OnUpdateConstrainZ(CCmdUI* pCmdUI)
{
pCmdUI->SetRadio(!Constraint.xyz.x && !Constraint.xyz.y && Constraint.xyz.z);
}
void CFREDView::OnConstrainXz()
{
vm_vec_make(&Constraint, 1.0f, 0.0f, 1.0f);
vm_vec_make(&Anticonstraint, 0.0f, 1.0f, 0.0f);
Single_axis_constraint = 0;
}
void CFREDView::OnUpdateConstrainXz(CCmdUI* pCmdUI)
{
pCmdUI->SetRadio(Constraint.xyz.x && !Constraint.xyz.y && Constraint.xyz.z);
}
void CFREDView::OnConstrainXy()
{
vm_vec_make(&Constraint, 1.0f, 1.0f, 0.0f);
vm_vec_make(&Anticonstraint, 0.0f, 0.0f, 1.0f);
Single_axis_constraint = 0;
}
void CFREDView::OnUpdateConstrainXy(CCmdUI* pCmdUI)
{
pCmdUI->SetRadio(Constraint.xyz.x && Constraint.xyz.y && !Constraint.xyz.z);
}
void CFREDView::OnConstrainYz()
{
vm_vec_make(&Constraint, 0.0f, 1.0f, 1.0f);
vm_vec_make(&Anticonstraint, 1.0f, 0.0f, 0.0f);
Single_axis_constraint = 0;
}
void CFREDView::OnUpdateConstrainYz(CCmdUI* pCmdUI)
{
pCmdUI->SetRadio(!Constraint.xyz.x && Constraint.xyz.y && Constraint.xyz.z);
}
void CFREDView::OnSelectionLock()
{
Selection_lock = !Selection_lock;
}
void CFREDView::OnUpdateSelectionLock(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Selection_lock);
}
void CFREDView::OnLButtonDblClk(UINT nFlags, CPoint point)
{
CView::OnLButtonDblClk(nFlags, point);
if (Cursor_over != -1) {
switch (Objects[Cursor_over].type) {
case OBJ_SHIP:
case OBJ_START:
OnEditorsShips();
break;
case OBJ_PROP:
OnEditorsProps();
break;
case OBJ_WAYPOINT:
OnEditorsWaypoint();
break;
case OBJ_JUMP_NODE:
OnEditorsJumpnode();
break;
}
} else if (Briefing_dialog)
Fixed_briefing_size = !Fixed_briefing_size;
}
void CFREDView::OnDoubleFineGridlines()
{
double_fine_gridlines = !double_fine_gridlines;
maybe_create_new_grid(The_grid, &eye_pos, &eye_orient, 1);
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateDoubleFineGridlines(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(double_fine_gridlines);
}
void CFREDView::OnShowDistances()
{
Show_distances = !Show_distances;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateShowDistances(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_distances);
}
void CFREDView::OnUniversalHeading()
{
Universal_heading = !Universal_heading;
}
void CFREDView::OnUpdateUniversalHeading(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Universal_heading);
}
void CFREDView::OnFlyingControls()
{
Flying_controls_mode = !Flying_controls_mode;
}
void CFREDView::OnUpdateFlyingControls(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Flying_controls_mode);
}
void CFREDView::OnRotateLocally()
{
Group_rotate = !Group_rotate;
}
void CFREDView::OnUpdateRotateLocally(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(!Group_rotate);
}
void CFREDView::OnSelectList()
{
ship_select dlg;
dlg.DoModal();
}
// position camera to view all objects on the screen at once. Doesn't change orientation.
void view_universe(int just_marked)
{
float dist, radius;
vec3d center;
vertex v;
object *ptr;
if (!get_object_bounds(just_marked != 0, &center, &radius))
return;
dist = std::max(radius, fl_sqrt(20.0f)) + 1.0f;
vm_vec_scale_add(&view_pos, &center, &view_orient.vec.fvec, -dist);
g3_set_view_matrix(&view_pos, &view_orient, 0.5f);
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (!just_marked || (ptr->flags[Object::Object_Flags::Marked])) {
g3_rotate_vertex(&v, &ptr->pos);
Assert(!(v.codes & CC_BEHIND));
if (g3_project_vertex(&v) & PF_OVERFLOW)
Int3();
while (v.codes & CC_OFF) { // is point off screen?
dist += 5.0f; // zoom out a little and check again.
vm_vec_scale_add(&view_pos, &center, &view_orient.vec.fvec, -dist);
g3_set_view_matrix(&view_pos, &view_orient, 0.5f);
g3_rotate_vertex(&v, &ptr->pos);
if (g3_project_vertex(&v) & PF_OVERFLOW)
Int3();
}
}
ptr = GET_NEXT(ptr);
}
dist *= 1.1f;
vm_vec_scale_add(&view_pos, &center, &view_orient.vec.fvec, -dist);
g3_set_view_matrix(&view_pos, &view_orient, 0.5f);
Update_window = 1;
}
void CFREDView::cycle_constraint()
{
if (Single_axis_constraint) {
if (Constraint.xyz.x)
OnConstrainY();
else if (Constraint.xyz.y)
OnConstrainZ();
else if (Constraint.xyz.z)
OnConstrainXz();
} else {
if (!Constraint.xyz.x)
OnConstrainXy();
else if (!Constraint.xyz.y)
OnConstrainYz();
else if (!Constraint.xyz.z)
OnConstrainX();
}
}
void CFREDView::OnZoomExtents()
{
view_universe();
}
void CFREDView::OnZoomSelected()
{
if (query_valid_object()) {
if (Marked > 1)
view_universe(1);
else
vm_vec_scale_add(&view_pos, &Objects[cur_object_index].pos, &view_orient.vec.fvec, Objects[cur_object_index].radius * -3.0f);
}
Update_window = 1;
}
void CFREDView::OnUpdateZoomSelected(CCmdUI* pCmdUI)
{
pCmdUI->Enable(query_valid_object());
}
void CFREDView::OnFormWing()
{
object *ptr = GET_FIRST(&obj_used_list);
bool found = false;
while (ptr != END_OF_LIST(&obj_used_list)) {
if (( (ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START) ) && (ptr->flags[Object::Object_Flags::Marked])) {
if(Ships[ptr->instance].flags[Ship::Ship_Flags::Reinforcement]) {
found = true;
break;
}
}
ptr = GET_NEXT(ptr);
}
if(found) {
int ok = MessageBox("Some of the ships you selected to create a wing are marked as reinforcements. Press Ok to clear the flag on all selected ships. Press Cancel to not create the wing.", "Reinforcement conflict", MB_ICONEXCLAMATION | MB_OKCANCEL);
if(ok == IDOK) {
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (( (ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START) ) && (ptr->flags[Object::Object_Flags::Marked])) {
set_reinforcement(Ships[ptr->instance].ship_name, 0);
}
ptr = GET_NEXT(ptr);
}
} else {
return;
}
}
if (!create_wing())
FREDDoc_ptr->autosave("form wing");
}
void CFREDView::OnUpdateFormWing(CCmdUI* pCmdUI)
{
int count = 0;
object *ptr;
if (query_valid_object()) {
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (ptr->flags[Object::Object_Flags::Marked]) {
if (ptr->type == OBJ_SHIP)
{
int ship_type = ship_query_general_type(ptr->instance);
if(ship_type > -1 && (Ship_types[ship_type].flags[Ship::Type_Info_Flags::AI_can_form_wing]))
{
count++;
}
}
if (ptr->type == OBJ_START)
count++;
}
ptr = GET_NEXT(ptr);
}
}
pCmdUI->Enable(count > 0);
}
int query_single_wing_marked()
{
int i, obj;
if (!query_valid_object())
return 0;
if (cur_wing == -1)
return 0;
i = Wings[cur_wing].wave_count;
if (Marked != i) // does marked object count match number of ships in wing?
return 0;
while (i--) {
obj = wing_objects[cur_wing][i];
if ((Objects[obj].type != OBJ_SHIP) && (Objects[obj].type != OBJ_START))
Error(LOCATION, "Invalid objects detected in wing \"%s\"", Wings[cur_wing].name);
// if (Ships[Objects[obj].instance].wingnum != cur_wing)
// return 0;
Assert(Ships[Objects[obj].instance].wingnum == cur_wing);
if (!(Objects[obj].flags[Object::Object_Flags::Marked])) // ensure all ships in wing.are marked
return 0;
}
return 1;
}
void CFREDView::OnDisbandWing()
{
if (query_single_wing_marked()) {
disband_wing(cur_wing);
FREDDoc_ptr->autosave("wing disband");
} else
MessageBox("One and only one wing must be selected for this operation");
}
void CFREDView::OnUpdateDisbandWing(CCmdUI* pCmdUI)
{
pCmdUI->Enable(query_single_wing_marked());
}
void CFREDView::OnShowHorizon()
{
Show_horizon = !Show_horizon;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateShowHorizon(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_horizon);
}
void CFREDView::OnEditorsWing()
{
Assert(Wing_editor_dialog.GetSafeHwnd());
if (!theApp.init_window(&Wing_wnd_data, &Wing_editor_dialog, 0))
return;
Wing_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
Wing_editor_dialog.ShowWindow(SW_RESTORE);
}
void CFREDView::OnEditorsPlayer()
{
player_start_editor dlg;
dlg.DoModal();
}
void CFREDView::OnEditorsOrient()
{
orient_editor dlg;
dlg.DoModal();
}
void CFREDView::OnEditorsEvents()
{
if (Message_editor_dlg) {
MessageBox("You must close the message editor before opening the event editor");
return;
}
if (!Event_editor_dlg) {
Event_editor_dlg = new event_editor;
Event_editor_dlg->Create(event_editor::IDD);
}
Event_editor_dlg->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
Event_editor_dlg->ShowWindow(SW_RESTORE);
}
void CFREDView::OnUpdateEditorsOrient(CCmdUI* pCmdUI)
{
pCmdUI->Enable(query_valid_object());
}
void CFREDView::OnEditorsMessage()
{
if (Event_editor_dlg) {
MessageBox("You must close the event editor before opening the message editor");
return;
}
if (!Message_editor_dlg) {
Message_editor_dlg = new CMessageEditorDlg;
Message_editor_dlg->Create(CMessageEditorDlg::IDD);
}
Message_editor_dlg->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
Message_editor_dlg->ShowWindow(SW_RESTORE);
}
void CFREDView::OnEditorsStarfield()
{
starfield_editor dlg;
dlg.DoModal();
}
void CFREDView::place_background_bitmap(vec3d v)
{
}
void CFREDView::OnEditorsBgBitmaps()
{
if (!Bg_bitmap_dialog) {
Bg_bitmap_dialog = new bg_bitmap_dlg;
Bg_bitmap_dialog->create();
}
Bg_bitmap_dialog->SetWindowPos(&wndTop, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
Bg_bitmap_dialog->ShowWindow(SW_RESTORE);
}
void CFREDView::OnEditorsVolumetrics()
{
volumetrics_dlg dlg;
dlg.DoModal();
}
void CFREDView::OnEditorsReinforcement()
{
reinforcement_editor_dlg dlg;
dlg.DoModal();
}
void CFREDView::OnErrorChecker()
{
int z;
z = global_error_check();
if (!z)
MessageBox("No errors were detected in this mission", "Woohoo!");
for (z=0; z<obj_count; z++)
if (flags[z])
delete [] names[z];
obj_count = 0;
}
int CFREDView::global_error_check()
{
char buf[256], *str;
int bs, i, j, n, s, t, z, ai, count, ship, wing, obj, team, point, multi;
object *ptr;
brief_stage *sp;
SCP_string anchor_message;
SCP_set<anchor_t> anchors_checked;
g_err = multi = 0;
if ( The_mission.game_type & MISSION_TYPE_MULTI )
multi = 1;
// cycle though all the objects and verify every possible aspect of them
obj_count = t = 0;
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
names[obj_count] = NULL;
flags[obj_count] = 0;
i = ptr->instance;
if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
if (i < 0 || i >= MAX_SHIPS){
return internal_error("An object has an illegal ship index");
}
z = Ships[i].ship_info_index;
if ((z < 0) || (z >= ship_info_size())){
return internal_error("A ship has an illegal class");
}
if (ptr->type == OBJ_START) {
t++;
if (!(Ship_info[z].flags[Ship::Info_Flags::Player_ship])) {
ptr->type = OBJ_SHIP;
Player_starts--;
t--;
ensure_valid_player_start_shipnum();
if (error("Invalid ship type for a player. Ship has been reset to non-player ship.")){
return 1;
}
}
for (n=count=0; n<MAX_SHIP_PRIMARY_BANKS; n++){
if (Ships[i].weapons.primary_bank_weapons[n] >= 0){
count++;
}
}
if (!count){
if (error("Player \"%s\" has no primary weapons. Should have at least 1", Ships[i].ship_name)){
return 1;
}
}
for (n=count=0; n<MAX_SHIP_SECONDARY_BANKS; n++){
if (Ships[i].weapons.secondary_bank_weapons[n] >= 0){
count++;
}
}
}
if (Ships[i].objnum != OBJ_INDEX(ptr)){
return internal_error("Object/ship references are corrupt");
}
names[obj_count] = Ships[i].ship_name;
wing = Ships[i].wingnum;
if (wing >= 0) { // ship is part of a wing, so check this
if (wing < 0 || wing >= MAX_WINGS){ // completely out of range?
return internal_error("A ship has an illegal wing index");
}
j = Wings[wing].wave_count;
if (!j){
return internal_error("A ship is in a non-existent wing");
}
if (j < 0 || j > MAX_SHIPS_PER_WING){
return internal_error("Invalid number of ships in wing \"%s\"", Wings[z].name);
}
while (j--){
if (wing_objects[wing][j] == OBJ_INDEX(ptr)){ // look for object in wing's table
break;
}
}
if (j < 0){
return internal_error("Ship/wing references are corrupt");
}
// wing squad logo check - Goober5000
if (strlen(Wings[wing].wing_squad_filename) > 0) //-V805
{
if (The_mission.game_type & MISSION_TYPE_MULTI)
{
if (error("Wing squad logos are not displayed in multiplayer games."))
{
return 1;
}
}
else
{
if (ptr->type == OBJ_START)
{
if (error("A squad logo was assigned to the player's wing. The player's squad logo will be displayed instead of the wing squad logo on ships in this wing."))
{
return 1;
}
}
}
}
}
if ( (Ships[i].flags[Ship::Ship_Flags::Kill_before_mission]) && (Ships[i].hotkey >= 0) ){
if (error("Ship flagged as \"destroy before mission start\" has a hotkey assignment")){
return 1;
}
}
if ( (Ships[i].flags[Ship::Ship_Flags::Kill_before_mission]) && (ptr->type == OBJ_START) ){
if (error("Player start flagged as \"destroy before mission start\"")){
return 1;
}
}
} else if (ptr->type == OBJ_WAYPOINT) {
int waypoint_num;
waypoint_list *wp_list = find_waypoint_list_with_instance(i, &waypoint_num);
if (wp_list == NULL) {
return internal_error("Object references an illegal waypoint path number");
}
if (waypoint_num < 0 || (uint) waypoint_num >= wp_list->get_waypoints().size()) {
return internal_error("Object references an illegal waypoint number in path");
}
waypoint_stuff_name(buf, i);
names[obj_count] = new char[strlen(buf) + 1];
strcpy(names[obj_count], buf);
flags[obj_count] = 1;
} else if (ptr->type == OBJ_POINT) {
if (!Briefing_dialog){
return internal_error("Briefing icon detected when not in briefing edit mode");
}
//Shouldn't be needed anymore.
//If we really do need it, call me and I'll write a is_valid function for jumpnodes -WMC
}
else if (ptr->type == OBJ_JUMP_NODE || ptr->type == OBJ_PROP)
{
//nothing needs to be done here, we just need to make sure the else doesn't occur
}
else {
return internal_error("An unknown object type (%d) was detected", ptr->type);
}
for (i=0; i<obj_count; i++){
if (names[i] && names[obj_count]){
if (!stricmp(names[i], names[obj_count])){
return internal_error("Duplicate object names (%s)", names[i]);
}
}
}
obj_count++;
ptr = GET_NEXT(ptr);
}
if (t != Player_starts){
return internal_error("Total number of player ships is incorrect");
}
if (obj_count != Num_objects){
return internal_error("Num_objects is incorrect");
}
count = 0;
for (i=0; i<MAX_SHIPS; i++) {
if (Ships[i].objnum >= 0) { // is ship being used?
count++;
if (!query_valid_object(Ships[i].objnum)){
return internal_error("Ship uses an unused object");
}
z = Objects[Ships[i].objnum].type;
if ((z != OBJ_SHIP) && (z != OBJ_START)){
return internal_error("Object should be a ship, but isn't");
}
if (fred_check_sexp(Ships[i].arrival_cue, OPR_BOOL, "arrival cue of ship \"%s\"", Ships[i].ship_name)){
return -1;
}
if (fred_check_sexp(Ships[i].departure_cue, OPR_BOOL, "departure cue of ship \"%s\"", Ships[i].ship_name)){
return -1;
}
if (Ships[i].arrival_location != ArrivalLocation::AT_LOCATION) {
if (!Ships[i].arrival_anchor.isValid()){
if (error("Ship \"%s\" requires a valid arrival target", Ships[i].ship_name)){
return 1;
}
}
if (Ships[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) {
check_anchor_for_hangar_bay(anchor_message, anchors_checked, Ships[i].arrival_anchor, Ships[i].ship_name, true, true);
if (!anchor_message.empty() && error("%s", anchor_message.c_str())) {
return 1;
}
}
}
if (Ships[i].departure_location != DepartureLocation::AT_LOCATION) {
if (!Ships[i].departure_anchor.isValid()){
if (error("Ship \"%s\" requires a valid departure target", Ships[i].ship_name)){
return 1;
}
}
if (Ships[i].departure_location == DepartureLocation::TO_DOCK_BAY) {
check_anchor_for_hangar_bay(anchor_message, anchors_checked, Ships[i].departure_anchor, Ships[i].ship_name, true, false);
if (!anchor_message.empty() && error("%s", anchor_message.c_str())) {
return 1;
}
}
}
ai = Ships[i].ai_index;
if (ai < 0 || ai >= MAX_AI_INFO){
return internal_error("AI index out of range for ship \"%s\"", Ships[i].ship_name);
}
if (Ai_info[ai].shipnum != i){
return internal_error("AI/ship references are corrupt");
}
if ((str = error_check_initial_orders(Ai_info[ai].goals, i, -1))>0) {
if (*str == '*')
return internal_error("Initial orders error for ship \"%s\"\n\n%s", Ships[i].ship_name, str + 1);
else if (*str == '!')
return 1;
else if (error("Initial orders error for ship \"%s\"\n\n%s", Ships[i].ship_name, str))
return 1;
}
for (dock_instance *dock_ptr = Objects[Ships[i].objnum].dock_list; dock_ptr != NULL; dock_ptr = dock_ptr->next)
{
obj = OBJ_INDEX(dock_ptr->docked_objp);
if (!query_valid_object(obj)){
return internal_error("Ship \"%s\" initially docked with non-existant ship", Ships[i].ship_name);
}
if (Objects[obj].type != OBJ_SHIP && Objects[obj].type != OBJ_START){
return internal_error("Ship \"%s\" initially docked with non-ship object", Ships[i].ship_name);
}
ship = get_ship_from_obj(obj);
if (!ship_docking_valid(i, ship) && !ship_docking_valid(ship, i)){
return internal_error("Docking illegal between \"%s\" and \"%s\" (initially docked)", Ships[i].ship_name, Ships[ship].ship_name);
}
z = get_docking_list(Ship_info[Ships[i].ship_info_index].model_num);
point = dock_ptr->dockpoint_used;
if (point < 0 || point >= z){
internal_error("Invalid docker point (\"%s\" initially docked with \"%s\")", Ships[i].ship_name, Ships[ship].ship_name);
}
z = get_docking_list(Ship_info[Ships[ship].ship_info_index].model_num);
point = dock_find_dockpoint_used_by_object(dock_ptr->docked_objp, &Objects[Ships[i].objnum]);
if (point < 0 || point >= z){
internal_error("Invalid dockee point (\"%s\" initially docked with \"%s\")", Ships[i].ship_name, Ships[ship].ship_name);
}
}
wing = Ships[i].wingnum;
bool is_in_loadout_screen = (ptr->type == OBJ_START);
if (!is_in_loadout_screen && wing >= 0) {
if ( multi && The_mission.game_type & MISSION_TYPE_MULTI_TEAMS )
{
for (n = 0; n < MAX_TVT_WINGS; n++) {
if (!strcmp(Wings[wing].name, TVT_wing_names[n])) {
is_in_loadout_screen = true;
break;
}
}
} else {
for (n = 0; n < MAX_STARTING_WINGS; n++) {
if (!strcmp(Wings[wing].name, Starting_wing_names[n])) {
is_in_loadout_screen = true;
break;
}
}
}
}
if (is_in_loadout_screen) {
int illegal = 0;
z = Ships[i].ship_info_index;
for (n = 0; n < MAX_SHIP_PRIMARY_BANKS; n++){
if (Ships[i].weapons.primary_bank_weapons[n] >= 0 && !Ship_info[z].allowed_weapons[Ships[i].weapons.primary_bank_weapons[n]]){
illegal++;
}
}
for (n = 0; n < MAX_SHIP_SECONDARY_BANKS; n++){
if (Ships[i].weapons.secondary_bank_weapons[n] >= 0 && !Ship_info[z].allowed_weapons[Ships[i].weapons.secondary_bank_weapons[n]]){
illegal++;
}
}
if (illegal && error("%d illegal weapon(s) found on ship \"%s\"", illegal, Ships[i].ship_name)) {
return 1;
}
}
}
}
if (count != ship_get_num_ships()){
return internal_error("num_ships is incorrect");
}
count = 0;
for (i=0; i<MAX_WINGS; i++) {
team = -1;
j = Wings[i].wave_count;
if (j) { // is wing being used?
count++;
if (j < 0 || j > MAX_SHIPS_PER_WING){
return internal_error("Invalid number of ships in wing \"%s\"", Wings[i].name);
}
while (j--) {
obj = wing_objects[i][j];
if (obj < 0 || obj >= MAX_OBJECTS){
return internal_error("Wing_objects has an illegal object index");
}
if (!query_valid_object(obj)){
return internal_error("Wing_objects references an unused object");
}
// Now, at this point, we can assume several things. We have a valid object because
// we passed query_valid_object(), and all valid objects were already checked above,
// so this object has valid information, such as the instance.
if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START)) {
ship = Objects[obj].instance;
wing_bash_ship_name(buf, Wings[i].name, j + 1);
if (stricmp(buf, Ships[ship].ship_name)){
return internal_error("Ship \"%s\" in wing should be called \"%s\"", Ships[ship].ship_name, buf);
}
int ship_type = ship_query_general_type(ship);
if(ship_type < 0 || !(Ship_types[ship_type].flags[Ship::Type_Info_Flags::AI_can_form_wing]))
{
if (error("Ship \"%s\" is an illegal type to be in a wing", Ships[ship].ship_name)){
return 1;
}
}
} else {
return internal_error("Wing_objects of \"%s\" references an illegal object type", Wings[i].name);
}
if (Ships[ship].wingnum != i){
return internal_error("Wing/ship references are corrupt");
}
if (ship != Wings[i].ship_index[j]){
return internal_error("Ship/wing references are corrupt");
}
if (team < 0){
team = Ships[ship].team;
} else if (team != Ships[ship].team && team < 999){
if (error("ship teams mixed within same wing (\"%s\")", Wings[i].name)){
return 1;
}
}
}
if ((Wings[i].special_ship < 0) || (Wings[i].special_ship >= Wings[i].wave_count)){
return internal_error("Special ship out of range for \"%s\"", Wings[i].name);
}
if (Wings[i].num_waves < 0){
return internal_error("Number of waves for \"%s\" is negative", Wings[i].name);
}
if (Wings[i].threshold < 0){
return internal_error("Threshold for \"%s\" is invalid", Wings[i].name);
}
if (Wings[i].threshold + Wings[i].wave_count > MAX_SHIPS_PER_WING) {
Wings[i].threshold = MAX_SHIPS_PER_WING - Wings[i].wave_count;
if(error("Threshold for wing \"%s\" is higher than allowed. Reset to %d", Wings[i].name, Wings[i].threshold)){
return 1;
}
}
for (j=0; j<obj_count; j++){
if (names[j]){
if (!stricmp(names[j], Wings[i].name)){
return internal_error("Wing name is also used by an object (%s)", names[j]);
}
}
}
if(fred_check_sexp(Wings[i].arrival_cue, OPR_BOOL, "arrival cue of wing \"%s\"", Wings[i].name)){
return -1;
}
if(fred_check_sexp(Wings[i].departure_cue, OPR_BOOL, "departure cue of wing \"%s\"", Wings[i].name)){
return -1;
}
if (Wings[i].arrival_location != ArrivalLocation::AT_LOCATION) {
if (!Wings[i].arrival_anchor.isValid())
if (error("Wing \"%s\" requires a valid arrival target", Wings[i].name))
return 1;
if (Wings[i].arrival_location == ArrivalLocation::FROM_DOCK_BAY) {
check_anchor_for_hangar_bay(anchor_message, anchors_checked, Wings[i].arrival_anchor, Wings[i].name, false, true);
if (!anchor_message.empty() && error("%s", anchor_message.c_str())) {
return 1;
}
}
}
if (Wings[i].departure_location != DepartureLocation::AT_LOCATION) {
if (!Wings[i].departure_anchor.isValid())
if (error("Wing \"%s\" requires a valid departure target", Wings[i].name))
return 1;
if (Wings[i].departure_location == DepartureLocation::TO_DOCK_BAY) {
check_anchor_for_hangar_bay(anchor_message, anchors_checked, Wings[i].departure_anchor, Wings[i].name, false, false);
if (!anchor_message.empty() && error("%s", anchor_message.c_str())) {
return 1;
}
}
}
if ((str = error_check_initial_orders(Wings[i].ai_goals, -1, i))>0) {
if (*str == '*')
return internal_error("Initial orders error for wing \"%s\"\n\n%s", Wings[i].name, str + 1);
else if (*str == '!')
return 1;
else if (error("Initial orders error for wing \"%s\"\n\n%s", Wings[i].name, str))
return 1;
}
}
}
if (count != Num_wings){
return internal_error("Num_wings is incorrect");
}
for (const auto &ii: Waypoint_lists) {
for (z=0; z<obj_count; z++){
if (names[z]){
if (!stricmp(names[z], ii.get_name())){
return internal_error("Waypoint path name is also used by an object (%s)", names[z]);
}
}
}
for (const auto &jj: ii.get_waypoints()) {
waypoint_stuff_name(buf, jj);
for (z=0; z<obj_count; z++){
if (names[z]){
if (!stricmp(names[z], buf)){
break;
}
}
}
if (z == obj_count){
return internal_error("Waypoint \"%s\" not linked to an object", buf);
}
}
}
if (Player_starts > MAX_PLAYERS){
return internal_error("Number of player starts exceeds max limit");
}
if (!multi && (Player_starts > 1)){
if (error("Multiple player starts exist, but this is a single player mission")){
return 1;
}
}
for (const auto &reinforcement: Reinforcements) {
z = 0;
for (ship=0; ship<MAX_SHIPS; ship++){
if ((Ships[ship].objnum >= 0) && !stricmp(Ships[ship].ship_name, reinforcement.name)) {
z = 1;
break;
}
}
for (wing=0; wing<MAX_WINGS; wing++){
if (Wings[wing].wave_count && !stricmp(Wings[wing].name, reinforcement.name)) {
z = 1;
break;
}
}
if (!z){
return internal_error("Reinforcement name not found in ships or wings");
}
}
/* for (i=0; i<num_messages; i++) {
if (Messages[i].num_times < 0)
return internal_error("Number of times to play message is negative");
z = Messages[i].who_from;
if (z < -1 || z >= MAX_SHIPS) // hacked! -1 should be illegal..
return internal_error("Message originator index is out of range");
if (Ships[z].objnum == -1)
return internal_error("Message originator points to nonexistant ship");
if (fred_check_sexp(Messages[i].sexp, OPR_BOOL,
"Message formula from \"%s\"", Ships[Messages[i].who_from].ship_name))
return -1;
}*/
if ((Player_start_shipnum < 0) || (Player_start_shipnum >= MAX_SHIPS) || (Ships[Player_start_shipnum].objnum < 0)){
return internal_error("Mission has no valid player start ship");
}
i = global_error_check_player_wings(multi);
if (i){
return i;
}
for (i=0; i<(int)Mission_events.size(); i++){
if (fred_check_sexp(Mission_events[i].formula, OPR_NULL, "mission event \"%s\"", Mission_events[i].name.c_str())){
return -1;
}
}
for (i=0; i<(int)Mission_goals.size(); i++){
if (fred_check_sexp(Mission_goals[i].formula, OPR_BOOL, "mission goal \"%s\"", Mission_goals[i].name.c_str())){
return -1;
}
}
for ( bs = 0; bs < Num_teams; bs++ ) {
for (s=0; s<Briefings[bs].num_stages; s++) {
sp = &Briefings[bs].stages[s];
t = sp->num_icons;
for (i=0; i<t-1; i++){
for (j=i+1; j<t; j++) {
if ((sp->icons[i].id > 0) && (sp->icons[i].id == sp->icons[j].id)){
if (error("Duplicate icon IDs %d in briefing stage %d", sp->icons[i].id, s + 1)){
return 1;
}
}
}
}
}
}
for ( j = 0; j < Num_teams; j++ ) {
for (i=0; i<Debriefings[j].num_stages; i++) {
if (fred_check_sexp(Debriefings[j].stages[i].formula, OPR_BOOL, "debriefing stage %d", i + 1)){
return -1;
}
}
}
// for all wings, be sure that the orders accepted for all ships are the same for all ships
// in the wing
//std::set<size_t> starting_orders { std::numeric_limits<size_t>::max() };
for (i=0; i<MAX_WINGS; i++) {
int starting_wing;
std::set<size_t> default_orders;
int default_orders_idx = -1;
if ( !Wings[i].wave_count ){
continue;
}
// determine if this wing is a starting wing of the player
starting_wing = (ship_starting_wing_lookup(Wings[i].name) != -1);
// first, be sure this isn't a reinforcement wing.
if ( starting_wing && (Wings[i].flags[Ship::Wing_Flags::Reinforcement]) ) {
if ( error("Starting Wing %s marked as reinforcement. This wing\nshould either be renamed, or unmarked as reinforcement.", Wings[i].name) ){
return 1;
}
}
for ( j = 0; j < Wings[i].wave_count; j++ ) {
std::set<size_t> orders;
// exclude players from the check
if (Objects[Ships[Wings[i].ship_index[j]].objnum].type == OBJ_START) {
continue;
}
orders = Ships[Wings[i].ship_index[j]].orders_accepted;
if ( default_orders_idx < 0 ) {
default_orders_idx = j;
default_orders = orders;
} else if ( default_orders != orders ) {
if (error("%s and %s will accept different orders. All ships in a wing must accept the same Player Orders.", Ships[Wings[i].ship_index[j]].ship_name, Ships[Wings[i].ship_index[default_orders_idx]].ship_name)) {
return 1;
}
}
}
/* Goober5000 - this is not necessary
// make sure that these ignored orders are the same for all starting wings of the player
if ( starting_wing ) {
if ( starting_orders == -1 ) {
starting_orders = default_orders;
} else {
if ( starting_orders != default_orders ) {
if ( error("Player starting wing %s has orders which don't match other starting wings\n", Wings[i].name) ){
return 1;
}
}
}
}
*/
}
//This should never ever be a problem -WMC
/*
if (Num_jump_nodes < 0){
return internal_error("Jump node count is illegal");
}*/
// do error checking for asteroid targets
for (const auto& name : Asteroid_field.target_names) {
if (ship_name_lookup(name.c_str(), 1) < 0) {
if (error("Asteroid target '%s' is not a valid ship", name.c_str())) {
return 1;
}
}
}
fred_check_message_personas();
return g_err;
}
int CFREDView::global_error_check_mixed_player_wing(int w)
{
int i, s, species = -1, mixed = 0;
for (i=0; i<Wings[w].wave_count; i++) {
s = Wings[w].ship_index[i];
if (species < 0)
species = Ship_info[Ships[s].ship_info_index].species;
else if (Ship_info[Ships[s].ship_info_index].species != species)
mixed = 1;
}
if (mixed)
if (error("%s wing must all be of the same species", Wings[w].name))
return 1;
return 0;
}
int CFREDView::global_error_check_player_wings(int multi)
{
int i, z, err;
int starting_wing_count[MAX_STARTING_WINGS];
int tvt_wing_count[MAX_TVT_WINGS];
object *ptr;
CString starting_wing_list = "";
CString tvt_wing_list = "";
// check team wings in tvt
if ( multi && The_mission.game_type & MISSION_TYPE_MULTI_TEAMS )
{
for (i=0; i<MAX_TVT_WINGS; i++)
{
if (ship_tvt_wing_lookup(TVT_wing_names[i]) == -1)
{
if (error("%s wing is required for multiplayer team vs. team missions"))
return 1;
}
}
}
// The wing containing the player start must be present at mission start, so a custom arrival cue
// is only useful for controlling the arrival of subsequent waves. Flag a single-wave wing with a
// custom cue as a potential issue, but never change the cue itself.
if (Error_checker_checks_potential_issues || Error_checker_checks_potential_issues_once)
{
for (i=0; i<MAX_WINGS; i++)
{
if (!Wings[i].wave_count || Wings[i].num_waves > 1)
continue;
if (Wings[i].arrival_cue == Locked_sexp_true)
continue;
bool contains_player_start = false;
for (z=0; z<Wings[i].wave_count; z++)
if (Objects[Ships[Wings[i].ship_index[z]].objnum].type == OBJ_START)
contains_player_start = true;
if (contains_player_start)
{
SCP_string issue_buf;
sprintf(issue_buf, "Potential issue detected in wing %s:\n\nThis wing contains a player start and a custom arrival cue, but only one wave. The arrival cue must evaluate to true at mission start, or the player will not be created.", Wings[i].name);
if (Fred_main_wnd->MessageBox(issue_buf.c_str(), "Warning", MB_OKCANCEL | MB_ICONINFORMATION) != IDOK)
return 1;
}
}
}
// Check to be sure that any player wing doesn't have > 1 wave for multiplayer
if ( multi )
{
if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS )
{
for (i=0; i<MAX_TVT_WINGS; i++)
{
if (TVT_wings[i] >= 0 && Wings[TVT_wings[i]].num_waves > 1)
{
Wings[TVT_wings[i]].num_waves = 1;
if (error("%s wing must contain only 1 wave.\nThis change has been made for you.", TVT_wing_names[i]))
return 1;
}
}
}
else
{
for (i=0; i<MAX_STARTING_WINGS; i++)
{
if (Starting_wings[i] >= 0 && Wings[Starting_wings[i]].num_waves > 1)
{
Wings[Starting_wings[i]].num_waves = 1;
if (error("%s wing must contain only 1 wave.\nThis change has been made for you.", Starting_wing_names[i]))
return 1;
}
}
}
}
// check number of ships in player wing
if ( multi && The_mission.game_type & MISSION_TYPE_MULTI_TEAMS )
{
for (i=0; i<MAX_TVT_WINGS; i++)
{
if (TVT_wings[i] >= 0 && Wings[TVT_wings[i]].wave_count > 4)
{
if (error("%s wing has too many ships. Should only have 4 max.", TVT_wing_names[i]))
return 1;
}
}
}
else
{
for (i=0; i<MAX_STARTING_WINGS; i++)
{
if (Starting_wings[i] >= 0 && Wings[Starting_wings[i]].wave_count > 4)
{
if (error("%s wing has too many ships. Should only have 4 max.", Starting_wing_names[i]))
return 1;
}
}
}
// check arrival delay in tvt
if ( multi && The_mission.game_type & MISSION_TYPE_MULTI_TEAMS )
{
for (i=0; i<MAX_TVT_WINGS; i++)
{
if (TVT_wings[i] >= 0 && Wings[TVT_wings[i]].arrival_delay > 0)
{
if (error("%s wing shouldn't have a non-zero arrival delay", TVT_wing_names[i]))
return 1;
}
}
}
// check mixed-species in a wing for multi missions
if (multi)
{
if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS )
{
for (i=0; i<MAX_TVT_WINGS; i++)
{
if (TVT_wings[i] >= 0)
{
if (global_error_check_mixed_player_wing(TVT_wings[i]))
return 1;
}
}
}
else
{
for (i=0; i<MAX_STARTING_WINGS; i++)
{
if (Starting_wings[i] >= 0)
{
if (global_error_check_mixed_player_wing(Starting_wings[i]))
return 1;
}
}
}
}
for (i=0; i<MAX_STARTING_WINGS; i++)
{
starting_wing_count[i] = 0;
if (i < MAX_STARTING_WINGS - 1)
{
starting_wing_list += Starting_wing_names[i];
if (MAX_STARTING_WINGS > 2)
starting_wing_list += ",";
starting_wing_list += " ";
}
else
{
starting_wing_list += "or ";
starting_wing_list += Starting_wing_names[i];
}
}
for (i=0; i<MAX_TVT_WINGS; i++)
{
tvt_wing_count[i] = 0;
if (i < MAX_TVT_WINGS - 1)
{
tvt_wing_list += TVT_wing_names[i];
if (MAX_TVT_WINGS > 2)
tvt_wing_list += ",";
tvt_wing_list += " ";
}
else
{
tvt_wing_list += "or ";
tvt_wing_list += TVT_wing_names[i];
}
}
// check players in wings
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list))
{
int ship_instance = ptr->instance;
err = 0;
// this ship is a player?
if (ptr->type == OBJ_START)
{
// check if this ship is in a wing
z = Ships[ship_instance].wingnum;
if (z < 0)
{
err = 1;
}
else
{
int in_starting_wing = 0;
int in_tvt_wing = 0;
// check which wing the player is in
for (i=0; i<MAX_STARTING_WINGS; i++)
{
if (Starting_wings[i] == z)
{
in_starting_wing = 1;
starting_wing_count[i]++;
}
}
for (i=0; i<MAX_TVT_WINGS; i++)
{
if (TVT_wings[i] == z)
{
in_tvt_wing = 1;
tvt_wing_count[i]++;
}
}
// make sure the player belongs to his proper wing
if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS)
{
if (!in_tvt_wing)
{
err = 1;
}
}
else
{
if (!in_starting_wing)
{
err = 1;
}
}
}
if (err)
{
if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS)
{
if (error("Player %s should be part of %s wing", Ships[ship_instance].ship_name, static_cast<LPCTSTR>(tvt_wing_list)))
return 1;
}
else
{
if (error("Player %s should be part of %s wing", Ships[ship_instance].ship_name, static_cast<LPCTSTR>(starting_wing_list)))
return 1;
}
}
}
ptr = GET_NEXT(ptr);
}
// check all wings in tvt have players
if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS)
{
for (i=0; i<MAX_TVT_WINGS; i++)
{
if (!tvt_wing_count[i])
{
if (error("%s wing doesn't contain any players (which it should)", TVT_wing_names[i]))
return 1;
}
}
}
return 0;
}
int CFREDView::error(const char *msg, ...)
{
char buf[2048];
va_list args;
va_start(args, msg);
vsnprintf(buf, sizeof(buf)-1, msg, args);
va_end(args);
buf[sizeof(buf)-1] = '\0';
g_err = 1;
if (MessageBox(buf, "Error", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDOK)
return 0;
return 1;
}
int CFREDView::internal_error(const char *msg, ...)
{
SCP_string buf;
va_list args;
va_start(args, msg);
vsprintf(buf, msg, args);
va_end(args);
g_err = 1;
#ifndef NDEBUG
buf += "\n\nThis is an internal error. Please notify a coder about this. Click cancel to debug.";
if (MessageBox(buf.c_str(), "Internal Error", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
Int3(); // drop to debugger so the problem can be analyzed.
#else
MessageBox(buf.c_str(), "Error", MB_OK | MB_ICONEXCLAMATION);
#endif
return -1;
}
int CFREDView::fred_check_sexp(int sexp, int type, const char *location, ...)
{
SCP_string location_buf, sexp_buf, error_buf, bad_node_str, issue_msg;
int err = 0, z, faulty_node;
va_list args;
va_start(args, location);
vsprintf(location_buf, location, args);
va_end(args);
if (sexp == -1)
return 0;
z = check_sexp_syntax(sexp, type, 1, &faulty_node);
if (z)
{
convert_sexp_to_string(sexp_buf, sexp, SEXP_ERROR_CHECK_MODE);
truncate_message_lines(sexp_buf, 30);
stuff_sexp_text_string(bad_node_str, faulty_node, SEXP_ERROR_CHECK_MODE);
if (!bad_node_str.empty()) // the previous function adds a space at the end
bad_node_str.pop_back();
sprintf(error_buf, "Error in %s: %s\n\n%s\n\n(Bad node appears to be: %s)", location_buf.c_str(), sexp_error_message(z), sexp_buf.c_str(), bad_node_str.c_str());
if (z < 0 && z > -100)
err = 1;
if (err)
return internal_error("%s", error_buf.c_str());
if (error("%s", error_buf.c_str()))
return 1;
}
z = 0;
if (Error_checker_checks_potential_issues || Error_checker_checks_potential_issues_once)
z = check_sexp_potential_issues(sexp, &faulty_node, issue_msg);
if (z)
{
convert_sexp_to_string(sexp_buf, sexp, SEXP_ERROR_CHECK_MODE);
truncate_message_lines(sexp_buf, 30);
stuff_sexp_text_string(bad_node_str, faulty_node, SEXP_ERROR_CHECK_MODE);
if (!bad_node_str.empty()) // the previous function adds a space at the end
bad_node_str.pop_back();
sprintf(error_buf, "Potential issue detected in %s:\n\n%s\n\n%s\n\n(Suspect node appears to be: %s)", location_buf.c_str(), issue_msg.c_str(), sexp_buf.c_str(), bad_node_str.c_str());
if (Fred_main_wnd->MessageBox(error_buf.c_str(), "Warning", MB_OKCANCEL | MB_ICONINFORMATION) != IDOK)
return 1;
}
Error_checker_checks_potential_issues_once = false;
return 0;
}
void CFREDView::OnEditorsWaypoint()
{
Assert(Waypoint_editor_dialog.GetSafeHwnd());
if (!theApp.init_window(&Waypoint_wnd_data, &Waypoint_editor_dialog))
return;
Waypoint_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
Waypoint_editor_dialog.ShowWindow(SW_RESTORE);
}
void CFREDView::OnEditorsJumpnode()
{
Assert(Jumpnode_editor_dialog.GetSafeHwnd());
if (!theApp.init_window(&Jumpnode_wnd_data, &Jumpnode_editor_dialog))
return;
Jumpnode_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
Jumpnode_editor_dialog.ShowWindow(SW_RESTORE);
}
char *error_check_initial_orders(ai_goal *goals, int ship, int wing)
{
char *source;
int i, j, num, flag, found, inst, team, team2;
object *ptr;
if (ship >= 0) {
source = Ships[ship].ship_name;
team = Ships[ship].team;
for (i=0; i<MAX_AI_GOALS; i++)
if (!ai_query_goal_valid(ship, goals[i].ai_mode)) {
if (Fred_view_wnd->error("Order \"%s\" isn't allowed for ship \"%s\"", get_order_name(goals[i].ai_mode), source))
return "!";
}
} else {
Assert(wing >= 0);
Assert(Wings[wing].wave_count > 0);
source = Wings[wing].name;
team = Ships[Objects[wing_objects[wing][0]].instance].team;
for (j=0; j<Wings[wing].wave_count; j++)
for (i=0; i<MAX_AI_GOALS; i++)
if (!ai_query_goal_valid(Wings[wing].ship_index[j], goals[i].ai_mode)) {
if (Fred_view_wnd->error("Order \"%s\" isn't allowed for ship \"%s\"", get_order_name(goals[i].ai_mode),
Ships[Wings[wing].ship_index[j]].ship_name))
return "!";
}
}
for (i=0; i<MAX_AI_GOALS; i++) {
switch (goals[i].ai_mode) {
case AI_GOAL_NONE:
case AI_GOAL_CHASE_ANY:
case AI_GOAL_CHASE_SHIP_CLASS:
case AI_GOAL_CHASE_SHIP_TYPE:
case AI_GOAL_UNDOCK:
case AI_GOAL_KEEP_SAFE_DISTANCE:
case AI_GOAL_PLAY_DEAD:
case AI_GOAL_PLAY_DEAD_PERSISTENT:
case AI_GOAL_WARP:
flag = 0;
break;
case AI_GOAL_WAYPOINTS:
case AI_GOAL_WAYPOINTS_ONCE:
flag = 1;
break;
case AI_GOAL_DOCK:
if (ship < 0)
return "Wings can't dock";
FALLTHROUGH;
case AI_GOAL_DESTROY_SUBSYSTEM:
case AI_GOAL_CHASE:
case AI_GOAL_GUARD:
case AI_GOAL_DISARM_SHIP:
case AI_GOAL_DISARM_SHIP_TACTICAL:
case AI_GOAL_DISABLE_SHIP:
case AI_GOAL_DISABLE_SHIP_TACTICAL:
case AI_GOAL_EVADE_SHIP:
case AI_GOAL_STAY_NEAR_SHIP:
case AI_GOAL_FORM_ON_WING:
case AI_GOAL_IGNORE:
case AI_GOAL_IGNORE_NEW:
flag = 2;
break;
case AI_GOAL_CHASE_WING:
case AI_GOAL_GUARD_WING:
flag = 3;
break;
case AI_GOAL_STAY_STILL:
flag = 4;
break;
default:
return "*Invalid goal type";
}
found = 0;
if (flag > 0) {
if (*goals[i].target_name == '<')
return "Invalid target";
if (!stricmp(goals[i].target_name, source))
if (ship >= 0)
return "Target of ship's goal is itself";
else
return "Target of wing's goal is itself";
}
inst = team2 = -1;
if (flag == 1) { // target waypoint required
if (find_matching_waypoint_list(goals[i].target_name) == NULL)
return "*Invalid target waypoint path name";
} else if (flag == 2) { // target ship required
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (ptr->type == OBJ_SHIP || ptr->type == OBJ_START) {
inst = ptr->instance;
if (!stricmp(goals[i].target_name, Ships[inst].ship_name)) {
found = 1;
break;
}
}
ptr = GET_NEXT(ptr);
}
if (!found)
return "*Invalid target ship name";
if (wing >= 0) { // check if target ship is in wing
if (Ships[inst].wingnum == wing && Objects[Ships[inst].objnum].type != OBJ_START)
return "Target ship of wing's goal is within said wing";
}
team2 = Ships[inst].team;
} else if (flag == 3) { // target wing required
for (j=0; j<MAX_WINGS; j++)
if (Wings[j].wave_count && !stricmp(Wings[j].name, goals[i].target_name))
break;
if (j >= MAX_WINGS)
return "*Invalid target wing name";
if (ship >= 0) { // check if ship is in target wing
if (Ships[ship].wingnum == j)
return "Target wing of ship's goal is same wing said ship is part of";
}
team2 = Ships[Objects[wing_objects[j][0]].instance].team;
} else if (flag == 4) {
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (ptr->type == OBJ_SHIP || ptr->type == OBJ_START) {
inst = ptr->instance;
if (!stricmp(goals[i].target_name, Ships[inst].ship_name)) {
found = 2;
break;
}
} else if (ptr->type == OBJ_WAYPOINT) {
if (!stricmp(goals[i].target_name, object_name(OBJ_INDEX(ptr)))) {
found = 1;
break;
}
}
ptr = GET_NEXT(ptr);
}
if (!found)
return "*Invalid target ship or waypoint name";
if (found == 2) {
if (wing >= 0) { // check if target ship is in wing
if (Ships[inst].wingnum == wing && Objects[Ships[inst].objnum].type != OBJ_START)
return "Target ship of wing's goal is within said wing";
}
team2 = Ships[inst].team;
}
}
switch (goals[i].ai_mode) {
case AI_GOAL_DESTROY_SUBSYSTEM:
Assert(flag == 2 && inst >= 0);
if (ship_find_subsys(&Ships[inst], goals[i].docker.name) < 0)
return "Unknown subsystem type";
break;
case AI_GOAL_DOCK: {
int dock1 = -1, dock2 = -1, model1, model2;
Assert(flag == 2 && inst >= 0);
if (!ship_docking_valid(ship, inst))
return "Docking illegal between given ship types";
model1 = Ship_info[Ships[ship].ship_info_index].model_num;
num = get_docking_list(model1);
for (j=0; j<num; j++) {
Assert(Docking_bay_list[j]);
if (!stricmp(goals[i].docker.name, Docking_bay_list[j])) {
dock1 = j;
break;
}
}
model2 = Ship_info[Ships[inst].ship_info_index].model_num;
num = get_docking_list(model2);
for (j=0; j<num; j++) {
Assert(Docking_bay_list[j]);
if (!stricmp(goals[i].dockee.name, Docking_bay_list[j])) {
dock2 = j;
break;
}
}
if (dock1 < 0)
return "Invalid docker point";
if (dock2 < 0)
return "Invalid dockee point";
if ((dock1 >= 0) && (dock2 >= 0)) {
if ( !(model_get_dock_index_type(model1, dock1) & model_get_dock_index_type(model2, dock2)) )
return "Dock points are incompatible";
}
break;
}
}
switch (goals[i].ai_mode) {
case AI_GOAL_GUARD:
case AI_GOAL_GUARD_WING:
if (team != team2) { // MK, added support for TEAM_NEUTRAL. Won't this work?
if (ship >= 0)
return "Ship assigned to guard a different team";
else
return "Wing assigned to guard a different team";
}
break;
case AI_GOAL_CHASE:
case AI_GOAL_CHASE_WING:
case AI_GOAL_DESTROY_SUBSYSTEM:
case AI_GOAL_DISARM_SHIP:
case AI_GOAL_DISARM_SHIP_TACTICAL:
case AI_GOAL_DISABLE_SHIP:
case AI_GOAL_DISABLE_SHIP_TACTICAL:
if (team == team2) {
if (ship >= 0)
return "Ship assigned to attack same team";
else
return "Wings assigned to attack same team";
}
break;
}
}
return NULL;
}
// function (which is called externally from message editor and event editor) so skip through
// the sexpression nodes to look for send-message commands to try to associate message personas
// to ships
void fred_check_message_personas()
{
/*
int i, op, j, ship_index;
char *mname, *who_from;
object *objp;
// this function is responsible for assigning personas to ships as well as error checking them.
// clear out the persona index on all ship objects
for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
if ( objp->type == OBJ_SHIP ) {
Ships[objp->instance].persona_index = -1;
}
}
for (i = 0; i < Num_sexp_nodes; i++ ) {
if ( Sexp_nodes[i].type == SEXP_NOT_USED )
continue;
// look for only operator nodes
if ( Sexp_nodes[i].subtype != SEXP_ATOM_OPERATOR )
continue;
// now look for the send-message opeator
op = get_operator_const( i );
if ( op != OP_SEND_MESSAGE )
continue;
// have the message. parse through the message to determine who is sending the message.
who_from = CTEXT(CDR(i));
// we can ignore messages from any wingman, and allied, or from God.
if ( !stricmp(who_from, "<Any wingman>") || !stricmp(who_from, "<Any allied>") || (who_from[0] == '#') )
continue;
mname = CTEXT(CDR(CDR(CDR(i))));
// check to see if who_from is a wing. Don't do processing if so.
if ( wing_name_lookup(who_from, 1) != -1 )
continue;
ship_index = ship_name_lookup( who_from );
if ( ship_index == -1 ) {
Int3(); // get allender. something funny is up with shipnames in send-message
continue;
}
for ( j = Num_builtin_messages; j < Num_messages; j++ ) {
if ( !stricmp(mname, Messages[j].name) ) {
// check to see if there is a persona for this message -- if not, bail
if ( Messages[j].persona_index == -1 )
break;
// if a ship isn't assigned a persona, and this message says that he is, assign it, and move on
if ( Ships[ship_index].persona_index == -1 ) {
Ships[ship_index].persona_index = Messages[j].persona_index;
continue;
}
// we must be sure of the following conditions:
// 1) a ship isn't assigned > 1 persona
if ( Ships[ship_index].persona_index != Messages[j].persona_index )
Fred_view_wnd->error("Ship %s has at least two personas attached to it:\n%s and %s", Ships[ship_index].ship_name, Personas[Ships[ship_index].persona_index].name, Personas[Messages[j].persona_index].name );
}
}
}
// check that two or more ships are not using the same persona
for (i = 0; i < Num_personas; i++ ) {
int persona_count;
object *objp;
// move through object list looking for number of shis using this persona
persona_count = 0;
for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
if ( objp->type != OBJ_SHIP )
continue;
if (Ships[objp->instance].persona_index == i )
persona_count++;
}
if ( persona_count > 1 )
Fred_view_wnd->error("Persona %s used by more than 1 ship", Personas[Messages[j].persona_index].name );
}
*/
}
void CFREDView::OnViewOutlines()
{
Show_outlines = !Show_outlines;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateViewOutlines(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_outlines);
}
void CFREDView::OnViewOutlinesOnSelected()
{
Draw_outlines_on_selected_ships = !Draw_outlines_on_selected_ships;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateViewOutlinesOnSelected(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Draw_outlines_on_selected_ships);
}
void CFREDView::OnViewOutlineAtWarpin()
{
Draw_outline_at_warpin_position = !Draw_outline_at_warpin_position;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateViewOutlineAtWarpin(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Draw_outline_at_warpin_position);
}
void CFREDView::OnMiscRotateSelectionMidpoint()
{
Orbit_selection_midpoint = !Orbit_selection_midpoint;
Orbit_active = false;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateMiscRotateSelectionMidpoint(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Orbit_selection_midpoint);
}
void CFREDView::OnAlwaysSaveDisplayNames()
{
Always_save_display_names = !Always_save_display_names;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateAlwaysSaveDisplayNames(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Always_save_display_names);
}
void CFREDView::OnErrorCheckerChecksPotentialIssues()
{
Error_checker_checks_potential_issues = !Error_checker_checks_potential_issues;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateErrorCheckerChecksPotentialIssues(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Error_checker_checks_potential_issues);
}
void CFREDView::OnClassicMenuLayout()
{
Classic_menu_layout = !Classic_menu_layout;
if (Fred_main_wnd != nullptr)
Fred_main_wnd->apply_menu_layout(Classic_menu_layout);
theApp.write_ini_file();
}
void CFREDView::OnUpdateClassicMenuLayout(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Classic_menu_layout);
}
void CFREDView::OnUpdateNewShipType(CCmdUI* pCmdUI)
{
int z;
CWnd *bar;
z = m_new_ship_type_combo_box.GetCurSel();
if (z == CB_ERR)
m_new_ship_type_combo_box.SetCurSel(cur_ship_type_combo_index);
else
cur_ship_type_combo_index = z;
bar = GetDlgItem(pCmdUI->m_nID);
if (!bar) {
pCmdUI -> ContinueRouting();
return; // not for us
}
pCmdUI -> SetCheck((bar->GetStyle() & WS_VISIBLE) != 0);
}
void CFREDView::OnShowStarfield()
{
Show_stars = !Show_stars;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateShowStarfield(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_stars);
}
void CFREDView::OnAsteroidEditor()
{
asteroid_editor dlg;
dlg.DoModal();
}
void CFREDView::OnRunFreeSpace()
{
BOOL r = FALSE;
STARTUPINFO si;
PROCESS_INFORMATION pi;
char *lpMsgBuf;
if (!FREDDoc_ptr->SaveModified())
return;
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(si);
si.lpReserved = nullptr;
si.lpDesktop = nullptr;
si.lpTitle = nullptr;
si.dwFlags = 0;
si.cbReserved2 = 0;
si.lpReserved2 = nullptr;
// get the filename of the app and replace FRED2_Open with FS2_Open
SCP_string processed_name(AfxGetApp()->m_pszExeName);
SCP_string processed_lowername = processed_name;
SCP_tolower(processed_lowername);
SCP_string::size_type fred_index = processed_lowername.find("fred2_open", 0);
// capitalisation!
bool capitalF = false;
if (fred_index != SCP_string::npos && processed_name[fred_index] == 'F') {
capitalF = true;
}
SCP_vector<SCP_string> nameAttempts;
if (fred_index != std::string::npos) {
// replace fred2_open with fs2_open
processed_name.erase(fred_index, 4);
processed_name.insert(fred_index, capitalF ? "FS" : "fs");
processed_name.append(".exe");
nameAttempts.push_back(processed_name);
}
nameAttempts.push_back("fs2_open.exe");
nameAttempts.push_back("fs2_open_r.exe");
// reconstruct command line
extern SCP_string cmdline_build_string();
auto commandArgs = cmdline_build_string();
for (const auto &nameAttempt: nameAttempts) {
// CreateProcess actually writes to the C-string, so we have to make it modifiable
std::unique_ptr<char[]> fullString(new char[nameAttempt.length() + commandArgs.length() + 1]);
strcpy(fullString.get(), nameAttempt.c_str());
strcat(fullString.get(), commandArgs.c_str());
// try to start FS2_open
r = CreateProcess(nullptr, fullString.get(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
if (r) {
break;
}
}
if (r) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
} else {
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
// Display the string.
MessageBox(lpMsgBuf);
// Free the buffer.
LocalFree( lpMsgBuf );
}
}
void CFREDView::OnEditorCampaign()
{
if (!FREDDoc_ptr->SaveModified())
return;
Assert(!Campaign_wnd);
Campaign_wnd = new campaign_tree_wnd;
if (Campaign_wnd->Create(NULL, "Campaign Editor", WS_OVERLAPPEDWINDOW | WS_MAXIMIZE,
CFrameWnd::rectDefault, NULL, "IDR_MENU_CAMPAIGN")) {
Campaign_wnd->ShowWindow(SW_SHOW);
Campaign_wnd->UpdateWindow();
}
}
void CFREDView::OnShowShips()
{
Show_ships = !Show_ships;
correct_marking();
Update_window = 1;
}
void CFREDView::OnUpdateShowShips(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_ships);
}
void CFREDView::OnShowStarts()
{
Show_starts = !Show_starts;
correct_marking();
Update_window = 1;
}
void CFREDView::OnUpdateShowStarts(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_starts);
}
void CFREDView::OnShowIFF(int iff)
{
Show_iff[iff] = !Show_iff[iff];
correct_marking();
Update_window = 1;
}
void CFREDView::OnUpdateShowIFF(int iff, CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_iff[iff]);
}
void CFREDView::OnToggleViewpoint()
{
if (viewpoint || !query_valid_object())
viewpoint = 0;
else {
viewpoint = 1;
view_obj = cur_object_index;
}
Update_window = 1;
}
void CFREDView::OnRevert()
{
if (!FREDDoc_ptr->SaveModified())
return;
FREDDoc_ptr->DeleteContents();
FREDDoc_ptr->OnOpenDocument(NULL);
}
void CFREDView::OnUpdateRevert(CCmdUI* pCmdUI)
{
pCmdUI->Enable(*Mission_filename);
}
BOOL CFREDView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if ((Cursor_over >= 0) || Selection_lock) {
if (Editing_mode == 1) {
SetCursor(h_cursor_move);
return TRUE;
} else if (Editing_mode == 2) {
SetCursor(h_cursor_rotate);
return TRUE;
}
}
return CView::OnSetCursor(pWnd, nHitTest, message);
}
void CFREDView::OnHideMarkedObjects()
{
object *ptr;
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (ptr->flags[Object::Object_Flags::Marked]) {
ptr->flags.set(Object::Object_Flags::Hidden);
unmark_object(OBJ_INDEX(ptr));
}
ptr = GET_NEXT(ptr);
}
}
void CFREDView::OnShowHiddenObjects()
{
object *ptr;
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
ptr->flags.remove(Object::Object_Flags::Hidden);
ptr = GET_NEXT(ptr);
}
Update_window = 1;
}
void CFREDView::OnLockMarkedObjects()
{
object *ptr;
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (ptr->flags[Object::Object_Flags::Marked]) {
ptr->flags.set(Object::Object_Flags::Locked_from_editing);
unmark_object(OBJ_INDEX(ptr));
}
ptr = GET_NEXT(ptr);
}
}
void CFREDView::OnUnlockAllObjects()
{
object *ptr;
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
ptr->flags.remove(Object::Object_Flags::Locked_from_editing);
ptr = GET_NEXT(ptr);
}
Update_window = 1;
}
void CFREDView::OnEditUndo()
{
vec3d viewer_pos;
matrix viewer_orient;
if (Undo_available) {
viewer_pos = view_pos;
viewer_orient = view_orient;
FREDDoc_ptr->autoload();
view_pos = viewer_pos;
view_orient = viewer_orient;
}
}
void CFREDView::OnUpdateEditUndo(CCmdUI* pCmdUI)
{
pCmdUI->Enable(Undo_available);
}
void CFREDView::OnEditorsBriefing()
{
if (!Briefing_dialog) {
Briefing_dialog = new briefing_editor_dlg;
Briefing_dialog->create();
}
Briefing_dialog->SetWindowPos(&wndTop, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
Briefing_dialog->ShowWindow(SW_RESTORE);
}
void CFREDView::OnEditorsDebriefing()
{
if (!Debriefing_dialog) {
Debriefing_dialog = new debriefing_editor_dlg;
Debriefing_dialog->create();
}
Debriefing_dialog->SetWindowPos(&wndTop, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
Debriefing_dialog->ShowWindow(SW_RESTORE);
}
void CFREDView::OnSaveCamera()
{
saved_cam_pos = view_pos;
saved_cam_orient = view_orient;
}
void CFREDView::OnRestoreCamera()
{
view_pos = saved_cam_pos;
view_orient = saved_cam_orient;
Update_window = 1;
}
void CFREDView::OnUpdateRestoreCamera(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!IS_VEC_NULL(&saved_cam_orient.vec.fvec));
}
void CFREDView::OnShowSexpHelp()
{
CRect rect;
Show_sexp_help = !Show_sexp_help;
Ship_editor_dialog.show_hide_sexp_help();
Wing_editor_dialog.show_hide_sexp_help();
}
void CFREDView::OnUpdateShowSexpHelp(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_sexp_help);
}
void CFREDView::OnLookatObj()
{
Lookat_mode = !Lookat_mode;
if (Lookat_mode && query_valid_object()) {
vec3d v, loc;
matrix m;
loc = selection_midpoint();
vm_vec_sub(&v, &loc, &view_pos);
if (v.xyz.x || v.xyz.y || v.xyz.z) {
vm_vector_2_matrix(&m, &v, NULL, NULL);
view_orient = m;
}
}
}
void CFREDView::OnUpdateLookatObj(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Lookat_mode);
}
void CFREDView::OnGroup(UINT nID)
{
int n = 1 << (nID - ID_GROUP1);
object *objp;
unmark_all();
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
if (objp->type == OBJ_SHIP) {
if (Ships[objp->instance].group & n)
mark_object(OBJ_INDEX(objp));
}
objp = GET_NEXT(objp);
}
Update_window = 1;
}
void CFREDView::OnSetGroup(UINT nID)
{
int i, err = 0, n = 1 << (nID - ID_SET_GROUP1);
object *objp;
for (i=0; i<MAX_SHIPS; i++)
Ships[i].group &= ~n;
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
if (objp->flags[Object::Object_Flags::Marked]) {
if (objp->type == OBJ_SHIP) {
Ships[objp->instance].group |= n;
} else
err = 1;
}
objp = GET_NEXT(objp);
}
if (err)
Fred_main_wnd->MessageBox("Only ships can be in groups, and not players or waypoints, etc.\n"
"These illegal objects you marked were not placed in the group");
Update_window = 1;
}
void CFREDView::OnEditorsAdjustGrid()
{
adjust_grid_dlg dlg;
dlg.DoModal();
Update_window = 1;
}
void CFREDView::OnCalcRelativeCoords()
{
calc_relative_coords_dlg dlg;
dlg.DoModal();
}
void CFREDView::OnReorder()
{
reorder_dlg dlg;
dlg.DoModal();
}
void CFREDView::OnMusicPlayer()
{
Assertion(Music_player_dialog.GetSafeHwnd(), "Unable to create music player window!");
if (!theApp.init_window(&MusPlayer_wnd_data, &Music_player_dialog))
return;
Music_player_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
Music_player_dialog.ShowWindow(SW_RESTORE);
}
void CFREDView::OnEditorsShieldSys()
{
shield_sys_dlg dlg;
dlg.DoModal();
}
void CFREDView::OnLevelObj()
{
level_controlled();
Update_window = 1;
}
void CFREDView::OnAlignObj()
{
verticalize_controlled();
Update_window = 1;
}
void CFREDView::OnControlObj()
{
Control_mode = (Control_mode + 1) % 2;
}
void CFREDView::OnNextObj()
{
object *ptr;
if (Bg_bitmap_dialog) {
if (Cur_bitmap == -1)
{
if (stars_get_num_bitmaps())
{
Cur_bitmap = 0;
Bg_bitmap_dialog -> update_data();
}
return;
}
Cur_bitmap++;
if (Cur_bitmap >= stars_get_num_bitmaps())
Cur_bitmap = 0;
Bg_bitmap_dialog -> update_data();
return;
}
if (EMPTY(&obj_used_list))
return;
if (query_valid_object()) {
ptr = Objects[cur_object_index].next;
if (ptr == END_OF_LIST(&obj_used_list))
ptr = GET_NEXT(ptr);
} else
ptr = GET_FIRST(&obj_used_list);
if (Marked > 1) { // cycle through marked list
while (!(ptr->flags[Object::Object_Flags::Marked]))
{
ptr = GET_NEXT(ptr);
if (ptr == END_OF_LIST(&obj_used_list))
ptr = GET_NEXT(ptr);
}
set_cur_object_index(OBJ_INDEX(ptr));
} else {
if (Marked)
unmark_object(cur_object_index);
mark_object(OBJ_INDEX(ptr));
}
}
void CFREDView::OnPrevObj()
{
int arr[MAX_OBJECTS], i = 0, n = 0;
object *ptr;
if (Bg_bitmap_dialog) {
if (Cur_bitmap == -1)
{
if (stars_get_num_bitmaps())
{
Cur_bitmap = stars_get_num_bitmaps() - 1;
Bg_bitmap_dialog -> update_data();
}
return;
}
Cur_bitmap--;
if (Cur_bitmap < 0)
Cur_bitmap = stars_get_num_bitmaps() - 1;
Bg_bitmap_dialog -> update_data();
return;
}
if (EMPTY(&obj_used_list))
return;
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (cur_object_index == OBJ_INDEX(ptr))
i = n;
arr[n++] = OBJ_INDEX(ptr);
ptr = GET_NEXT(ptr);
}
Assert(n);
if (query_valid_object()) {
i--;
if (i < 0)
i = n - 1;
} else
i = n - 1;
if (Marked > 1) { // cycle through marked list
while (!(Objects[i].flags[Object::Object_Flags::Marked]))
{
i--;
if (i < 0)
i = n - 1;
}
set_cur_object_index(i);
} else {
if (Marked)
unmark_object(cur_object_index);
mark_object(i);
}
}
void CFREDView::OnEditDelete()
{
if (!button_down && Marked) {
delete_marked();
FREDDoc_ptr->autosave("object delete");
}
Update_window = 2; // For some strange reason, need to redraw twice for it to take.
}
void CFREDView::OnEditCloneMarkedObjects()
{
if (!button_down && Marked) {
duplicate_marked_objects();
FREDDoc_ptr->autosave("clone marked");
}
Update_window = 1;
}
void CFREDView::OnEditDeleteWing()
{
if (!button_down && (cur_wing >= 0)) {
delete_wing();
FREDDoc_ptr->autosave("wing delete");
cur_wing = -1;
if (!Marked)
Ship_editor_dialog.initialize_data(1);
Wing_editor_dialog.initialize_data(1);
}
Update_window = 2; // For some strange reason, need to redraw twice for it to take.
}
void CFREDView::OnMarkWing()
{
int i, wing = cur_wing;
if (wing != -1)
{
unmark_all();
for (i=0; i<Wings[wing].wave_count; i++)
mark_object(wing_objects[wing][i]);
Assert(Wings[wing].special_ship >= 0 && Wings[wing].special_ship < Wings[wing].wave_count);
set_cur_object_index(wing_objects[wing][Wings[wing].special_ship]);
}
}
void CFREDView::OnUpdateControlObj(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Control_mode != 0);
}
void CFREDView::OnAaGridlines()
{
Aa_gridlines = !Aa_gridlines;
}
void CFREDView::OnUpdateAaGridlines(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Aa_gridlines);
Update_window = 1;
}
void CFREDView::OnCmdBrief()
{
cmd_brief_dlg dlg;
dlg.DoModal();
Update_window = 1;
}
void CFREDView::OnDisableUndo()
{
Autosave_disabled = !Autosave_disabled;
}
void CFREDView::OnUpdateDisableUndo(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Autosave_disabled);
}
void CFREDView::OnUpdateCmdBrief(CCmdUI* pCmdUI)
{
pCmdUI->Enable(true);
}
int get_visible_sub_system_count(ship *shipp)
{
int count = 0;
ship_subsys *cur_subsys;
for (cur_subsys = GET_FIRST(&shipp->subsys_list); cur_subsys != END_OF_LIST(&shipp->subsys_list); cur_subsys = GET_NEXT(cur_subsys)) {
if (cur_subsys->system_info->subobj_num != -1) {
count++;
}
}
return count;
}
int get_next_visible_subsys(ship *shipp, ship_subsys **next_subsys)
{
int count = get_visible_sub_system_count(shipp);
// return don't try to display
if (count == 0) {
return 0;
}
// first timer
if (*next_subsys == NULL) {
*next_subsys = &shipp->subsys_list;
}
// look before wrap
for (*next_subsys = GET_NEXT(*next_subsys); *next_subsys != END_OF_LIST(&shipp->subsys_list); *next_subsys = GET_NEXT(*next_subsys)) {
if ((*next_subsys)->system_info->subobj_num != -1) {
Update_window = 1;
return 1;
}
}
// look for first after wrap
for (*next_subsys = GET_FIRST(&shipp->subsys_list); *next_subsys != END_OF_LIST(&shipp->subsys_list); *next_subsys = GET_NEXT(*next_subsys)) {
if ((*next_subsys)->system_info->subobj_num != -1) {
Update_window = 1;
return 1;
}
}
Int3(); // should be impossible to miss
return 0;
}
int get_prev_visible_subsys(ship *shipp, ship_subsys **prev_subsys)
{
int count = get_visible_sub_system_count(shipp);
// return don't try to display
if (count == 0) {
return 0;
}
// first timer
Assert(*prev_subsys != NULL);
// look before wrap
for (*prev_subsys = GET_PREV(*prev_subsys); *prev_subsys != END_OF_LIST(&shipp->subsys_list); *prev_subsys = GET_PREV(*prev_subsys)) {
if ((*prev_subsys)->system_info->subobj_num != -1) {
Update_window = 1;
return 1;
}
}
// look for first after wrap
for (*prev_subsys = GET_LAST(&shipp->subsys_list); *prev_subsys != END_OF_LIST(&shipp->subsys_list); *prev_subsys = GET_PREV(*prev_subsys)) {
if ((*prev_subsys)->system_info->subobj_num != -1) {
Update_window = 1;
return 1;
}
}
Int3(); // should be impossible to miss
return 0;
}
// update next subsystem to view
void CFREDView::OnNextSubsys()
{
object *objp;
if (cur_object_index < 0) {
OnCancelSubsys();
}
objp = &Objects[cur_object_index];
// check if cur object is ship type
if (objp->type == OBJ_SHIP) {
// check if same ship
if (Render_subsys.ship_obj == objp) {
// if already on, advance to next
if (Render_subsys.do_render) {
if ( !get_next_visible_subsys(&Ships[objp->instance], &Render_subsys.cur_subsys) ) {
OnCancelSubsys();
}
} else {
Int3();
}
} else {
// clean up
OnCancelSubsys();
// set up new and advance to first
Render_subsys.do_render = true;
Render_subsys.ship_obj = objp;
if ( !get_next_visible_subsys(&Ships[objp->instance], &Render_subsys.cur_subsys) ) {
OnCancelSubsys();
}
}
} else {
// not ship type
OnCancelSubsys();
}
}
void CFREDView::OnPrevSubsys()
{
if (!Render_subsys.do_render) {
return;
}
if ( (cur_object_index < 0) || (Objects[cur_object_index].type != OBJ_SHIP) || (&Objects[cur_object_index] != Render_subsys.ship_obj) ) {
OnCancelSubsys();
return;
}
if ( !get_prev_visible_subsys(&Ships[Objects[cur_object_index].instance], &Render_subsys.cur_subsys) ) {
OnCancelSubsys();
}
}
void CFREDView::OnCancelSubsys()
{
Render_subsys.do_render = false;
Render_subsys.ship_obj = NULL;
Render_subsys.cur_subsys = NULL;
Update_window = 1;
}
void CFREDView::OnShowPaths()
{
Show_paths_fred = !Show_paths_fred;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateShowPaths(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_paths_fred);
}
void CFREDView::OnShowDockPoints()
{
Show_dock_points = !Show_dock_points;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateShowDockPoints(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Show_dock_points);
}
void CFREDView::OnMoveShipsWhenUndocking()
{
Move_ships_when_undocking = !Move_ships_when_undocking;
theApp.write_ini_file();
}
void CFREDView::OnUpdateMoveShipsWhenUndocking(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Move_ships_when_undocking);
}
void CFREDView::OnPointUsingUvec()
{
Point_using_uvec = !Point_using_uvec;
theApp.write_ini_file();
}
void CFREDView::OnUpdatePointUsingUvec(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Point_using_uvec);
}
void CFREDView::OnHighlightSubsys()
{
Highlight_selectable_subsys = !Highlight_selectable_subsys;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateHighlightSubsys(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Highlight_selectable_subsys);
}
void CFREDView::OnDumpStats()
{
DumpStats dlg;
dlg.DoModal();
}
void CFREDView::OnFormatFs2Open()
{
Mission_save_format = FSO_FORMAT_STANDARD;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateFormatFs2Open(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Mission_save_format == FSO_FORMAT_STANDARD);
}
void CFREDView::OnFormatFs2OpenComp()
{
Mission_save_format = FSO_FORMAT_COMPATIBILITY_MODE;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateFormatFs2OpenComp(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Mission_save_format == FSO_FORMAT_COMPATIBILITY_MODE);
}
void CFREDView::OnFormatFs2Retail()
{
Mission_save_format = FSO_FORMAT_RETAIL;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateFormatFs2Retail(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Mission_save_format == FSO_FORMAT_RETAIL);
}
void CFREDView::OnFormatFs1Retail()
{
Mission_save_format = FSO_FORMAT_RETAIL;
theApp.write_ini_file();
Update_window = 1;
}
void CFREDView::OnUpdateFormatFs1Retail(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Mission_save_format == FSO_FORMAT_RETAIL);
}
void CFREDView::OnEditorsSetGlobalShipFlags()
{
SetGlobalShipFlags dlg;
dlg.DoModal();
}
void CFREDView::OnEditorsVoiceManager()
{
VoiceActingManager dlg;
dlg.DoModal();
}
void CFREDView::OnEditorsFiction()
{
FictionViewerDlg dlg;
dlg.DoModal();
}
void CFREDView::OnDestroy()
{
audiostream_close();
snd_close();
CView::OnDestroy();
}
int CFREDView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
MoveWindow(0,0,200,300,1);
std::unique_ptr<os::GraphicsOperations> graphicsOperations(new MFCGraphicsOperations(this->GetSafeHwnd()));
if(fred_init(std::move(graphicsOperations)) == false)
return -1;
return 0;
}
BOOL CFREDView::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
void CFREDView::OnViewLighting()
{
Lighting_on = !Lighting_on;
}
void CFREDView::OnUpdateViewLighting(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Lighting_on);
}
void CFREDView::OnViewFullDetail()
{
FullDetail = !FullDetail;
}
void CFREDView::OnUpdateViewFullDetail(CCmdUI *pCmdUI)
{
pCmdUI->SetCheck(FullDetail);
}
BOOL CFREDView::DestroyWindow()
{
Fred_view_wnd = nullptr;
return CView::DestroyWindow();
}
void CFREDView::OnShowIFF0()
{
OnShowIFF(0);
}
void CFREDView::OnUpdateShowIFF0(CCmdUI* pCmdUI)
{
OnUpdateShowIFF(0, pCmdUI);
}
void CFREDView::OnShowIFF1()
{
OnShowIFF(1);
}
void CFREDView::OnUpdateShowIFF1(CCmdUI* pCmdUI)
{
OnUpdateShowIFF(1, pCmdUI);
}
void CFREDView::OnShowIFF2()
{
OnShowIFF(2);
}
void CFREDView::OnUpdateShowIFF2(CCmdUI* pCmdUI)
{
OnUpdateShowIFF(2, pCmdUI);
}
void CFREDView::OnShowIFF3()
{
OnShowIFF(3);
}
void CFREDView::OnUpdateShowIFF3(CCmdUI* pCmdUI)
{
OnUpdateShowIFF(3, pCmdUI);
}
void CFREDView::OnShowIFF4()
{
OnShowIFF(4);
}
void CFREDView::OnUpdateShowIFF4(CCmdUI* pCmdUI)
{
OnUpdateShowIFF(4, pCmdUI);
}
void CFREDView::OnShowIFF5()
{
OnShowIFF(5);
}
void CFREDView::OnUpdateShowIFF5(CCmdUI* pCmdUI)
{
OnUpdateShowIFF(5, pCmdUI);
}
void CFREDView::OnShowIFF6()
{
OnShowIFF(6);
}
void CFREDView::OnUpdateShowIFF6(CCmdUI* pCmdUI)
{
OnUpdateShowIFF(6, pCmdUI);
}
void CFREDView::OnShowIFF7()
{
OnShowIFF(7);
}
void CFREDView::OnUpdateShowIFF7(CCmdUI* pCmdUI)
{
OnUpdateShowIFF(7, pCmdUI);
}
void CFREDView::OnShowIFF8()
{
OnShowIFF(8);
}
void CFREDView::OnUpdateShowIFF8(CCmdUI* pCmdUI)
{
OnUpdateShowIFF(8, pCmdUI);
}
void CFREDView::OnShowIFF9()
{
OnShowIFF(9);
}
void CFREDView::OnUpdateShowIFF9(CCmdUI* pCmdUI)
{
OnUpdateShowIFF(9, pCmdUI);
}