Files
fs2open.github.com/fred2/fredview.cpp
T
Goober5000andClaude Fable 5 5fa6674688 send-message-list arg-count forgiveness, plus better error checking
Due to a long-standing bug, the groups-of-4 argument count check for
send-message-list and send-message-chain never actually worked, so
missions through 25.0 could includes sexps with truncated argument lists
that recovered gracefully at runtime.  Now that the check works, don't
punish those missions: introduce SEXP_CHECK_BAD_ARG_COUNT_BENIGN, which
warns but does not prevent the mission from loading.

To make sexp tree validation more robust, check_sexp_syntax now defers
recoverable errors rather than returning them immediately; otherwise
a recoverable error could mask a fatal error deeper in the tree.

Also fix a stale-variable bug in FRED's fred_check_sexp that could
produce a bogus empty dialog.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-30 01:03:06 -04:00

5172 lines
126 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_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_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_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_VALID(pDoc);
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);
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 != NULL);
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 != NULL);
// 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::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)
{
int i, max = 0, obj_flags[MAX_OBJECTS];
float dist, largest = 20.0f;
vec3d center, p1, p2; // center of all the objects collectively
vertex v;
object *ptr;
for (i=0; i<MAX_OBJECTS; i++)
obj_flags[i] = 0;
if (just_marked)
ptr = &Objects[cur_object_index];
else
ptr = GET_FIRST(&obj_used_list);
p1.xyz.x = p2.xyz.x = ptr->pos.xyz.x;
p1.xyz.y = p2.xyz.y = ptr->pos.xyz.y;
p1.xyz.z = p2.xyz.z = ptr->pos.xyz.z;
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (!just_marked || (ptr->flags[Object::Object_Flags::Marked])) {
center = ptr->pos;
if (center.xyz.x < p1.xyz.x)
p1.xyz.x = center.xyz.x;
if (center.xyz.x > p2.xyz.x)
p2.xyz.x = center.xyz.x;
if (center.xyz.y < p1.xyz.y)
p1.xyz.y = center.xyz.y;
if (center.xyz.y > p2.xyz.y)
p2.xyz.y = center.xyz.y;
if (center.xyz.z < p1.xyz.z)
p1.xyz.z = center.xyz.z;
if (center.xyz.z > p2.xyz.z)
p2.xyz.z = center.xyz.z;
}
ptr = GET_NEXT(ptr);
}
vm_vec_avg(&center, &p1, &p2);
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (!just_marked || (ptr->flags[Object::Object_Flags::Marked])) {
dist = vm_vec_dist_squared(&center, &ptr->pos);
if (dist > largest)
largest = dist;
obj_flags[OBJ_INDEX(ptr)] = 1; // flag object as needing on-screen check
if (OBJ_INDEX(ptr) > max)
max = OBJ_INDEX(ptr);
}
ptr = GET_NEXT(ptr);
}
dist = fl_sqrt(largest) + 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;
}
}
}
// // player's wing must have a true arrival
// free_sexp2(Wings[z].arrival_cue);
// Wings[z].arrival_cue = Locked_sexp_true;
// 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::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::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 = Objects[cur_object_index].pos;
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);
}