2 * $Logfile: /Freespace2/code/Fred2/FREDView.cpp $
7 * View class for a document/view architechure design program, which we don't
8 * want or need, but MFC forces us to use. This is the main place we handle
9 * MFC messages, events, etc. Sort of the interface between our code and MFC.
10 * There is also a lot of our code in here related to these things.
13 * Revision 1.1 2002/05/03 03:28:08 root
17 * 17 7/15/99 3:07p Dave
18 * 32 bit detection support. Mouse coord commandline.
20 * 16 7/09/99 5:54p Dave
21 * Seperated cruiser types into individual types. Added tons of new
22 * briefing icons. Campaign screen.
24 * 15 6/04/99 2:20p Andsager
25 * Add dump stats basic functionality
27 * 14 4/07/99 6:21p Dave
28 * Fred and Freespace support for multiple background bitmaps and suns.
29 * Fixed link errors on all subprojects. Moved encrypt_init() to
30 * cfile_init() and lcl_init(), since its safe to call twice.
32 * 13 3/26/99 4:49p Dave
33 * Made cruisers able to dock with stuff. Made docking points and paths
36 * 12 3/20/99 5:09p Dave
37 * Fixed release build fred warnings and unhandled exception.
39 * 11 3/04/99 11:56a Johnson
40 * Fixed an undo-rotation bug.
42 * 10 3/02/99 9:25p Dave
43 * Added a bunch of model rendering debug code. Started work on fixing
44 * beam weapon wacky firing.
46 * 9 3/01/99 10:00a Dave
47 * Fxied several dogfight related stats bugs.
49 * 8 2/23/99 2:32p Dave
50 * First run of oldschool dogfight mode.
52 * 7 1/27/99 4:09p Andsager
53 * Added highlight to ship subsystems
55 * 6 1/07/99 1:52p Andsager
56 * Initial check in of Sexp_variables
58 * 5 12/18/98 1:49a Dave
59 * Fixed Fred initialization problem resulting from hi-res mode changes.
61 * 4 10/13/98 9:27a Dave
62 * Started neatening up freespace.h
64 * 3 10/12/98 1:01p Dave
65 * Fixed object rotation bug (uninitialized data). Changed a few stubs to
66 * correspond to new var names.
68 * 2 10/07/98 6:28p Dave
69 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
70 * Fred. Globalized mission and campaign file extensions. Removed Silent
71 * Threat specific code.
73 * 1 10/07/98 3:00p Dave
75 * 248 9/16/98 6:54p Dave
76 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
77 * the ship list box. Added code so that tracker stats are not stored with
80 * 247 9/14/98 3:40p Allender
81 * better error checking for invalid number of waves for player wings in a
82 * multiplayer game. Better popup message in FreeSpace side.
84 * 246 6/18/98 4:28p Hoffoss
85 * Made invalid dock points in an initial order flag an error in Fred.
87 * 245 6/17/98 4:50p Hoffoss
88 * Added error checking for arrival delays used on wing player is in.
90 * 244 6/16/98 10:24a Hoffoss
91 * Switched internal errors over to normal looking errors for release
94 * 243 5/21/98 11:48a Hoffoss
95 * Removed check in and check out options.
97 * 242 5/21/98 12:58a Hoffoss
98 * Fixed warnings optimized build turned up.
100 * 241 5/19/98 1:19p Allender
101 * new low level reliable socket reading code. Make all missions/campaign
102 * load/save to data missions folder (i.e. we are rid of the player
105 * 240 5/14/98 5:31p Hoffoss
106 * Added some more error checking.
108 * 239 5/10/98 10:05p Allender
109 * only show cutscenes which have been seen before. Made Fred able to
110 * write missions anywhere, defaulting to player misison folder, not data
111 * mission folder. Fix FreeSpace code to properly read missions from
114 * 238 4/28/98 2:13p Hoffoss
115 * Added code to help keep invalid player ship types from existing in
118 * 237 4/26/98 6:05p Hoffoss
119 * Added multiplayer error checks requested by Dave B.
121 * 236 4/17/98 2:37p Duncan
122 * Added check to error checker for arrival/depature targets valid.
124 * 235 4/14/98 4:35p Hoffoss
125 * Fixed bug with launching FreeSpace from Fred. Current working
126 * directory wasn't being set properly.
128 * 234 4/14/98 3:38p Hoffoss
129 * Fixed the FreeSpace launch code in Fred.
131 * 233 4/07/98 9:42a Allender
132 * put in persona combo box into ship editor. Removed code to assign
133 * personas based on message
135 * 232 4/01/98 10:48a Hoffoss
136 * Changed Fred to not allow command briefings in multiplayer missions.
138 * 231 3/26/98 3:01p Hoffoss
139 * Put in check for alpha wing num ships < 4 for furball multiplayer
142 * 230 3/25/98 4:14p Hoffoss
143 * Split ship editor up into ship editor and a misc dialog, which tracks
146 * 229 3/23/98 4:04p Hoffoss
147 * Fixed dialog window initialization so it looks better at startup (they
148 * don't flash on for a second).
150 * 228 3/21/98 7:36p Lawrance
151 * Move jump nodes to own lib.
153 * 227 3/19/98 3:37p Adam
154 * Fixed Int3() when you try to right click on a briefing icon.
156 * 226 3/16/98 5:05p Hoffoss
157 * Fixed viewpoint bug.
159 * 225 3/12/98 2:21p Johnson
160 * Fixed some Fred bugs related to jump nodes.
162 * 224 3/10/98 6:11p Hoffoss
163 * Added jump node renaming abilities to Fred.
165 * 223 3/10/98 4:26p Hoffoss
166 * Changed jump node structure to include a name. Position is now taken
167 * from the object (each jump node has an associated object now).
169 * 222 3/09/98 10:03p Hoffoss
170 * Added support for loading/saving jump nodes to mission files.
172 * 221 3/09/98 10:56a Hoffoss
173 * Added jump node objects to Fred.
175 * 220 3/05/98 3:59p Hoffoss
176 * Added a bunch of new command brief stuff, and asteroid initialization
179 * 219 2/26/98 4:59p Allender
180 * groundwork for team vs team briefings. Moved weaponry pool into the
181 * Team_data structure. Added team field into the p_info structure.
182 * Allow for mutliple structures in the briefing code.
184 * 218 2/09/98 9:25p Allender
185 * team v team support. multiple pools and breifings
187 * 217 2/07/98 9:13p Hoffoss
188 * Added some more error checking to global error checker.
190 * 216 2/06/98 4:39p Hoffoss
191 * Added better checking for whether Fred is in the foreground or
194 * 215 2/04/98 4:32p Allender
195 * support for multiple briefings and debriefings. Changes to mission
196 * type (now a bitfield). Bitfield defs for multiplayer modes
198 * 214 2/02/98 4:36p Hoffoss
199 * Made no mission title given flag an error.
201 * 213 1/29/98 5:14p Hoffoss
202 * Added error checking for more than 4 ships in a player starting wing.
204 * 212 1/21/98 5:22p Hoffoss
205 * Added check to global error checker to make sure Alpha wing exists for
206 * multiplayer missions.
208 * 211 1/14/98 8:40p Allender
209 * don't check builtin messages when checking persona stuff
211 * 210 1/07/98 11:24p Allender
212 * add latency of 0 to key_mark function calls
214 * 209 12/31/97 3:56p Hoffoss
215 * Forced alpha wing to always have a true arrival cue.
217 * 208 12/09/97 8:11a Allender
218 * have Fred warn on certain starting wing conditions
220 * 207 11/24/97 10:14p Allender
221 * removed num_ships from use!!!! it was creating problems
223 * 206 11/24/97 11:07a Allender
224 * error checking on wings ignoring player orders
226 * 205 11/17/97 4:57p Allender
227 * added persona support in FreeSpace. A couple of new messages for
228 * message.tbl which Dan didn't have
230 * 204 11/14/97 5:21p Johnson
231 * Added debriefing formulas to error checker.
233 * 203 11/05/97 4:43p Allender
234 * reworked medal/rank system to read all data from tables. Made Fred
235 * read medals.tbl. Changed ai-warp to ai-warp-out which doesn't require
236 * waypoint for activation
238 * 202 10/30/97 3:30p Hoffoss
239 * Made anti-aliased gridlines an option in Fred.
241 * 201 10/28/97 9:54a Jasen
242 * don't check for personas when wing is sending message
244 * 200 10/22/97 3:15p Hoffoss
245 * Fixed ai-stay-still initial order to use waypoints instead of waypoint
248 * 199 10/22/97 1:58p Hoffoss
249 * Added support for AI_GOALS_PLAY_DEAD and changed AI_GOALS_STAY_STILL
252 * 198 10/14/97 10:59a Allender
253 * more persona work. Made global error checker call funciton to assign
256 * 197 10/10/97 5:03p Allender
257 * started work on ai-stay-still
259 * 196 10/01/97 12:37p Hoffoss
260 * Changed Fred (and FreeSpace) to utilize alpha, beta and gamma as player
261 * starting wing names.
263 * 195 9/16/97 9:41p Hoffoss
264 * Changed Fred code around to stop using Parse_player structure for
265 * player information, and use actual ships instead.
267 * 194 9/10/97 3:48p Duncan
268 * Added a case that was never added in the past (probably overlooked).
270 * 193 9/09/97 3:39p Sandeep
271 * warning level 4 bugs
273 * 192 9/09/97 2:12p Hoffoss
274 * Added code to allow briefing editor view to be a 1:1 pixel size fixed
275 * as the FreeSpace view will have.
277 * 191 9/06/97 2:13p Mike
278 * Replace support for TEAM_NEUTRAL
280 * 190 9/03/97 4:32p Hoffoss
281 * Added error number range error checking to Fred, and defaulted numbers
282 * for # of times docked to 1 in sexp trees.
284 * 189 9/02/97 4:32p Hoffoss
285 * Made minimized child windows restore if the window is activated.
287 * 188 9/02/97 1:34p Johnson
288 * Fixed bug I swear wasn't there when I checked this in originally.
291 * 187 9/01/97 6:59p Hoffoss
292 * Added source safe checkin and checkout capabilities to Fred.
294 * 186 8/28/97 8:56a Hoffoss
295 * Added more checking to sexp error checker, fixed some bugs.
297 * 185 8/26/97 4:18p Hoffoss
298 * Added error checking to initial orders dialog when ok is clicked.
300 * 184 8/25/97 5:58p Hoffoss
301 * Created menu items for keypress functions in Fred, and fixed bug this
302 * uncovered with wing_delete function.
304 * 183 8/22/97 4:16p Hoffoss
305 * added support for arrival and departure info in ship editor using
306 * wing's info if editing marked ships in a wing instead of using ship's.
308 * 182 8/18/97 9:31p Hoffoss
309 * Added grid adjustment dialog and shield system editor dialog.
311 * 181 8/17/97 10:22p Hoffoss
312 * Fixed several bugs in Fred with Undo feature. In the process, recoded
313 * a lot of CFile.cpp.
315 * 180 8/16/97 5:23p Hoffoss
316 * Added restrictions for single player mission type to allow only 1
319 * 179 8/16/97 2:02a Hoffoss
320 * Made docked objects move together in Fred.
322 * 178 8/15/97 11:08a Hoffoss
323 * Created a list of order types that can be used for several things, and
324 * yet easily changable. Added order error checking against ship types.
326 * 177 8/15/97 1:07a Hoffoss
327 * Changed code to disallow some ship types from being in a wing, and
328 * disallowing some ship types from being able to have initial orders.
330 * 176 8/14/97 6:37p Hoffoss
331 * Added briefing icon id support to Fred.
333 * 175 8/14/97 2:32p Hoffoss
334 * fixed bug where controlling an object doesn't cause screen updates, and
335 * added a number of cool features to viewpoint/control object code.
337 * 174 8/14/97 9:30a Hoffoss
338 * Added support for new ai goals "stay near ship" and "keep safe
341 * 173 8/13/97 6:23p Hoffoss
342 * Fixed initially docked code in Fred.
344 * 172 8/13/97 5:40p Hoffoss
347 * 171 8/12/97 10:42p Hoffoss
348 * Changed code to campaign editor load an accelerator table when created.
350 * 170 8/12/97 6:32p Hoffoss
351 * Added code to allow hiding of arrival and departure cues in editors.
353 * 169 8/12/97 5:56p Johnson
354 * Fixed bug with global error checker. Initially docked ships flagged as
355 * error when they aren't really.
357 * 168 8/12/97 1:29p Duncan
358 * Fixed bug with ai_undock initial order.
360 * 167 8/11/97 7:11p Hoffoss
361 * Somehow SS didn't merge the files, so I've reconstructed the changes
364 * 166 8/11/97 6:54p Hoffoss
365 * Groups now supported in Fred.
367 * 165 8/11/97 11:51a Allender
368 * added stamp stuff to Fred
370 * 164 8/07/97 6:01p Hoffoss
371 * Added a rotate about selected object button to toolbar and
372 * functionality, as requested by Comet.
374 * 163 8/07/97 2:07p Hoffoss
375 * Added duplicate icon label error checking to global error checker, and
376 * made briefign mode boxmark only icons if at least one icon is in the
379 * 162 8/06/97 10:20a Hoffoss
380 * Changed rotation speeds to be less linear and hopefully more useful.
382 * 161 8/05/97 2:27p Jasen
383 * Fixed bug in global error checker that caused crashes when in briefing
386 * 160 8/05/97 1:31p Hoffoss
387 * Removed excessive source control info from header.
389 * 159 8/01/97 5:52p Hoffoss
390 * Changed initially docked to disallow illegal docking combinations, and
391 * changed error checker to check for this as well.
393 * 158 8/01/97 3:10p Hoffoss
394 * Made Sexp help hidable.
396 * 157 8/01/97 12:52p Hoffoss
397 * Added variable, fixed bug with global error check.
399 * 156 7/31/97 6:41p Hoffoss
400 * Added checks to global error checker.
402 * 155 7/30/97 4:30p Hoffoss
403 * Made Fred sexp error checker more explicit.
405 * 154 7/29/97 5:16p Hoffoss
406 * Fixed bug in ship_query_general_type() and added docking checking to
407 * Fred's error checking.
409 * 153 7/29/97 1:44p Hoffoss
410 * Make player ships exception to ship target within same wing error
420 #include "fredview.h"
421 #include "fredrender.h"
426 #include "management.h"
430 #include "linklist.h"
431 #include "fvi.h" // For find_plane_line_intersection
437 #include "ship.h" // for ship names
438 #include "missiongoalsdlg.h"
440 #include "ship_select.h"
441 #include "playerstarteditor.h"
442 #include "orienteditor.h"
443 #include "eventeditor.h"
444 #include "messageeditordlg.h"
445 #include "starfield.h"
446 #include "starfieldeditor.h"
447 #include "floating.h"
448 #include "reinforcementeditordlg.h"
449 #include "asteroideditordlg.h"
450 #include "campaigntreewnd.h"
451 #include "debriefingeditordlg.h"
452 #include "adjustgriddlg.h"
453 #include "shieldsysdlg.h"
454 #include "cmdbrief.h"
455 #include "jumpnode.h"
456 #include "dumpstats.h"
459 #define new DEBUG_NEW
461 static char THIS_FILE[] = __FILE__;
464 subsys_to_render Render_subsys;
466 // the next variable is used for executable stamping -- please leave it alone!!!
467 #define FRED_EXPIRE_TIME (7 * 1000)
468 char stamp[STAMP_STRING_LENGTH] = { STAMP_STRING };
471 #define EXPIRE_BAD_CHECKSUM 1
472 #define EXPIRE_BAD_TIME 2
474 #define SHIP_TYPES 8000
475 #define REDUCER 100.0f
476 #define DUP_DRAG_OF_WING 2
478 LOCAL int Duped_wing;
480 int Autosave_disabled = 0;
481 int Show_sexp_help = 1;
484 int Show_friendly = 1;
485 int Show_hostile = 1;
486 int Show_neutral = 1;
487 int Show_ship_info = 1;
488 int Show_ship_models = 0;
489 int Show_compass = 1;
490 int Show_dock_points = 0;
491 int Show_paths_fred = 0;
492 int Selection_lock = 0;
496 int Marked = 0, moved = 0;
498 int Cursor_over = -1;
500 int physics_speed = 1;
501 int physics_rot = 20;
503 int last_mouse_x, last_mouse_y, mouse_dx, mouse_dy;
505 int Id_select_type_jump_node;
506 int Id_select_type_start = 0;
507 int Id_select_type_waypoint = 0;
508 int Hide_ship_cues = 0, Hide_wing_cues = 0;
509 vector original_pos, saved_cam_pos;
510 matrix bitmap_matrix_backup, saved_cam_orient = { 0.0f };
511 Marking_box marking_box;
512 object_orient_pos rotation_backup[MAX_OBJECTS];
514 // used by error checker, but needed in more than just one function.
515 char *names[MAX_OBJECTS], flags[MAX_OBJECTS];
519 void view_universe(int just_marked = 0);
520 void select_objects();
521 void drag_rotate_save_backup();
523 /////////////////////////////////////////////////////////////////////////////
526 CFREDView *Fred_view_wnd = NULL;
528 IMPLEMENT_DYNCREATE(CFREDView, CView)
530 BEGIN_MESSAGE_MAP(CFREDView, CView)
531 ON_MESSAGE(WM_GOODBYE, OnGoodbye)
532 ON_MESSAGE(WM_MENU_POPUP_SHIPS, OnMenuPopupShips)
533 ON_MESSAGE(WM_MENU_POPUP_EDIT, OnMenuPopupEdit)
535 //{{AFX_MSG_MAP(CFREDView)
536 ON_COMMAND(ID_VIEW_GRID, OnViewGrid)
537 ON_UPDATE_COMMAND_UI(ID_VIEW_GRID, OnUpdateViewGrid)
538 ON_COMMAND(ID_SHOW_WAYPOINTS, OnViewWaypoints)
539 ON_UPDATE_COMMAND_UI(ID_SHOW_WAYPOINTS, OnUpdateViewWaypoints)
541 ON_COMMAND(ID_EDITORS_SHIPS, OnEditorsShips)
549 ON_COMMAND(ID_MISCSTUFF_SHOWSHIPSASICONS, OnMiscstuffShowshipsasicons)
551 ON_COMMAND(ID_EDIT_POPUP_SHOW_SHIP_ICONS, OnEditPopupShowShipIcons)
552 ON_UPDATE_COMMAND_UI(ID_EDIT_POPUP_SHOW_SHIP_ICONS, OnUpdateEditPopupShowShipIcons)
553 ON_COMMAND(ID_EDIT_POPUP_SHOW_SHIP_MODELS, OnEditPopupShowShipModels)
554 ON_UPDATE_COMMAND_UI(ID_EDIT_POPUP_SHOW_SHIP_MODELS, OnUpdateEditPopupShowShipModels)
556 ON_COMMAND(ID_SHOW_PATHS, OnShowPaths)
557 ON_UPDATE_COMMAND_UI(ID_SHOW_PATHS, OnUpdateShowPaths)
558 ON_COMMAND(ID_SHOW_DOCK_POINTS, OnShowDockPoints)
559 ON_UPDATE_COMMAND_UI(ID_SHOW_DOCK_POINTS, OnUpdateShowDockPoints)
561 ON_COMMAND(ID_MISC_STATISTICS, OnMiscStatistics)
562 ON_COMMAND(ID_EDIT_POPUP_SHOW_COMPASS, OnEditPopupShowCompass)
563 ON_UPDATE_COMMAND_UI(ID_EDIT_POPUP_SHOW_COMPASS, OnUpdateEditPopupShowCompass)
564 ON_UPDATE_COMMAND_UI(ID_CHANGE_VIEWPOINT_EXTERNAL, OnUpdateChangeViewpointExternal)
565 ON_COMMAND(ID_CHANGE_VIEWPOINT_EXTERNAL, OnChangeViewpointExternal)
566 ON_UPDATE_COMMAND_UI(ID_CHANGE_VIEWPOINT_FOLLOW, OnUpdateChangeViewpointFollow)
567 ON_COMMAND(ID_CHANGE_VIEWPOINT_FOLLOW, OnChangeViewpointFollow)
568 ON_COMMAND(ID_EDITORS_GOALS, OnEditorsGoals)
569 ON_COMMAND(ID_SPEED1, OnSpeed1)
570 ON_COMMAND(ID_SPEED2, OnSpeed2)
571 ON_COMMAND(ID_SPEED5, OnSpeed5)
572 ON_COMMAND(ID_SPEED10, OnSpeed10)
573 ON_UPDATE_COMMAND_UI(ID_SPEED1, OnUpdateSpeed1)
574 ON_COMMAND(ID_SPEED3, OnSpeed3)
575 ON_COMMAND(ID_SPEED8, OnSpeed8)
576 ON_COMMAND(ID_ROT1, OnRot1)
577 ON_COMMAND(ID_ROT2, OnRot2)
578 ON_COMMAND(ID_ROT3, OnRot3)
579 ON_COMMAND(ID_ROT4, OnRot4)
580 ON_COMMAND(ID_ROT5, OnRot5)
581 ON_UPDATE_COMMAND_UI(ID_SPEED2, OnUpdateSpeed2)
582 ON_UPDATE_COMMAND_UI(ID_SPEED3, OnUpdateSpeed3)
583 ON_UPDATE_COMMAND_UI(ID_SPEED5, OnUpdateSpeed5)
584 ON_UPDATE_COMMAND_UI(ID_SPEED8, OnUpdateSpeed8)
585 ON_UPDATE_COMMAND_UI(ID_SPEED10, OnUpdateSpeed10)
586 ON_UPDATE_COMMAND_UI(ID_ROT1, OnUpdateRot1)
587 ON_UPDATE_COMMAND_UI(ID_ROT2, OnUpdateRot2)
588 ON_UPDATE_COMMAND_UI(ID_ROT3, OnUpdateRot3)
589 ON_UPDATE_COMMAND_UI(ID_ROT4, OnUpdateRot4)
590 ON_UPDATE_COMMAND_UI(ID_ROT5, OnUpdateRot5)
591 ON_COMMAND(ID_CONTROL_MODE_CAMERA, OnControlModeCamera)
592 ON_UPDATE_COMMAND_UI(ID_CONTROL_MODE_CAMERA, OnUpdateControlModeCamera)
593 ON_COMMAND(ID_CONTROL_MODE_SHIP, OnControlModeShip)
594 ON_UPDATE_COMMAND_UI(ID_CONTROL_MODE_SHIP, OnUpdateControlModeShip)
595 ON_COMMAND(ID_SHOW_GRID_POSITIONS, OnShowGridPositions)
596 ON_UPDATE_COMMAND_UI(ID_SHOW_GRID_POSITIONS, OnUpdateShowGridPositions)
597 ON_COMMAND(ID_SHOW_COORDINATES, OnShowCoordinates)
598 ON_UPDATE_COMMAND_UI(ID_SHOW_COORDINATES, OnUpdateShowCoordinates)
599 ON_COMMAND(ID_SPEED50, OnSpeed50)
600 ON_UPDATE_COMMAND_UI(ID_SPEED50, OnUpdateSpeed50)
601 ON_COMMAND(ID_SPEED100, OnSpeed100)
602 ON_UPDATE_COMMAND_UI(ID_SPEED100, OnUpdateSpeed100)
603 ON_COMMAND(ID_SELECT, OnSelect)
604 ON_UPDATE_COMMAND_UI(ID_SELECT, OnUpdateSelect)
605 ON_COMMAND(ID_SELECT_AND_MOVE, OnSelectAndMove)
606 ON_UPDATE_COMMAND_UI(ID_SELECT_AND_MOVE, OnUpdateSelectAndMove)
607 ON_COMMAND(ID_SELECT_AND_ROTATE, OnSelectAndRotate)
608 ON_UPDATE_COMMAND_UI(ID_SELECT_AND_ROTATE, OnUpdateSelectAndRotate)
609 ON_COMMAND(ID_CONSTRAIN_X, OnConstrainX)
610 ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_X, OnUpdateConstrainX)
611 ON_COMMAND(ID_CONSTRAIN_Y, OnConstrainY)
612 ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_Y, OnUpdateConstrainY)
613 ON_COMMAND(ID_CONSTRAIN_Z, OnConstrainZ)
614 ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_Z, OnUpdateConstrainZ)
615 ON_COMMAND(ID_CONSTRAIN_XZ, OnConstrainXz)
616 ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_XZ, OnUpdateConstrainXz)
617 ON_COMMAND(ID_SELECTION_LOCK, OnSelectionLock)
618 ON_UPDATE_COMMAND_UI(ID_SELECTION_LOCK, OnUpdateSelectionLock)
619 ON_WM_LBUTTONDBLCLK()
620 ON_COMMAND(ID_DOUBLE_FINE_GRIDLINES, OnDoubleFineGridlines)
621 ON_UPDATE_COMMAND_UI(ID_DOUBLE_FINE_GRIDLINES, OnUpdateDoubleFineGridlines)
622 ON_COMMAND(ID_SHOW_DISTANCES, OnShowDistances)
623 ON_UPDATE_COMMAND_UI(ID_SHOW_DISTANCES, OnUpdateShowDistances)
624 ON_COMMAND(ID_UNIVERSAL_HEADING, OnUniversalHeading)
625 ON_UPDATE_COMMAND_UI(ID_UNIVERSAL_HEADING, OnUpdateUniversalHeading)
626 ON_COMMAND(ID_FLYING_CONTROLS, OnFlyingControls)
627 ON_UPDATE_COMMAND_UI(ID_FLYING_CONTROLS, OnUpdateFlyingControls)
628 ON_COMMAND(ID_ROTATE_LOCALLY, OnRotateLocally)
629 ON_UPDATE_COMMAND_UI(ID_ROTATE_LOCALLY, OnUpdateRotateLocally)
630 ON_COMMAND(ID_CONSTRAIN_XY, OnConstrainXy)
631 ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_XY, OnUpdateConstrainXy)
632 ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_YZ, OnUpdateConstrainYz)
633 ON_COMMAND(ID_CONSTRAIN_YZ, OnConstrainYz)
634 ON_COMMAND(ID_SELECT_LIST, OnSelectList)
635 ON_COMMAND(ID_ZOOM_EXTENTS, OnZoomExtents)
636 ON_COMMAND(ID_ZOOM_SELECTED, OnZoomSelected)
637 ON_UPDATE_COMMAND_UI(ID_ZOOM_SELECTED, OnUpdateZoomSelected)
638 ON_COMMAND(ID_FORM_WING, OnFormWing)
639 ON_UPDATE_COMMAND_UI(ID_FORM_WING, OnUpdateFormWing)
640 ON_COMMAND(ID_DISBAND_WING, OnDisbandWing)
641 ON_UPDATE_COMMAND_UI(ID_DISBAND_WING, OnUpdateDisbandWing)
642 ON_COMMAND(ID_SHOW_HORIZON, OnShowHorizon)
643 ON_UPDATE_COMMAND_UI(ID_SHOW_HORIZON, OnUpdateShowHorizon)
644 ON_COMMAND(ID_EDITORS_WING, OnEditorsWing)
645 ON_COMMAND(ID_EDITORS_PLAYER, OnEditorsPlayer)
646 ON_COMMAND(ID_EDITORS_ORIENT, OnEditorsOrient)
647 ON_COMMAND(ID_EDITORS_EVENTS, OnEditorsEvents)
648 ON_UPDATE_COMMAND_UI(ID_EDITORS_ORIENT, OnUpdateEditorsOrient)
649 ON_COMMAND(ID_EDITORS_MESSAGE, OnEditorsMessage)
650 ON_COMMAND(ID_EDITORS_STARFIELD, OnEditorsStarfield)
651 ON_COMMAND(ID_EDITORS_BG_BITMAPS, OnEditorsBgBitmaps)
652 ON_COMMAND(ID_EDITORS_REINFORCEMENT, OnEditorsReinforcement)
653 ON_COMMAND(ID_ERROR_CHECKER, OnErrorChecker)
654 ON_COMMAND(ID_EDITORS_WAYPOINT, OnEditorsWaypoint)
655 ON_COMMAND(ID_VIEW_OUTLINES, OnViewOutlines)
656 ON_UPDATE_COMMAND_UI(ID_VIEW_OUTLINES, OnUpdateViewOutlines)
657 ON_UPDATE_COMMAND_UI(ID_NEW_SHIP_TYPE, OnUpdateNewShipType)
658 ON_COMMAND(ID_SHOW_STARFIELD, OnShowStarfield)
659 ON_UPDATE_COMMAND_UI(ID_SHOW_STARFIELD, OnUpdateShowStarfield)
660 ON_COMMAND(ID_ASTEROID_EDITOR, OnAsteroidEditor)
661 ON_COMMAND(ID_RUN_FREESPACE, OnRunFreespace)
662 ON_COMMAND(ID_EDITOR_CAMPAIGN, OnEditorCampaign)
663 ON_COMMAND(ID_SHOW_SHIPS, OnShowShips)
664 ON_UPDATE_COMMAND_UI(ID_SHOW_SHIPS, OnUpdateShowShips)
665 ON_COMMAND(ID_SHOW_STARTS, OnShowStarts)
666 ON_UPDATE_COMMAND_UI(ID_SHOW_STARTS, OnUpdateShowStarts)
667 ON_COMMAND(ID_SHOW_FRIENDLY, OnShowFriendly)
668 ON_UPDATE_COMMAND_UI(ID_SHOW_FRIENDLY, OnUpdateShowFriendly)
669 ON_COMMAND(ID_SHOW_HOSTILE, OnShowHostile)
670 ON_UPDATE_COMMAND_UI(ID_SHOW_HOSTILE, OnUpdateShowHostile)
671 ON_COMMAND(ID_TOGGLE_VIEWPOINT, OnToggleViewpoint)
672 ON_COMMAND(ID_REVERT, OnRevert)
673 ON_UPDATE_COMMAND_UI(ID_REVERT, OnUpdateRevert)
675 ON_COMMAND(ID_HIDE_OBJECTS, OnHideObjects)
676 ON_COMMAND(ID_SHOW_HIDDEN_OBJECTS, OnShowHiddenObjects)
677 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
678 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
679 ON_COMMAND(ID_EDITORS_BRIEFING, OnEditorsBriefing)
680 ON_COMMAND(ID_EDITORS_DEBRIEFING, OnEditorsDebriefing)
681 ON_COMMAND(ID_SAVE_CAMERA, OnSaveCamera)
682 ON_COMMAND(ID_RESTORE_CAMERA, OnRestoreCamera)
683 ON_UPDATE_COMMAND_UI(ID_RESTORE_CAMERA, OnUpdateRestoreCamera)
684 ON_COMMAND(ID_SHOW_SEXP_HELP, OnShowSexpHelp)
685 ON_UPDATE_COMMAND_UI(ID_SHOW_SEXP_HELP, OnUpdateShowSexpHelp)
686 ON_COMMAND(ID_LOOKAT_OBJ, OnLookatObj)
687 ON_UPDATE_COMMAND_UI(ID_LOOKAT_OBJ, OnUpdateLookatObj)
688 ON_COMMAND(ID_EDITORS_ADJUST_GRID, OnEditorsAdjustGrid)
689 ON_COMMAND(ID_EDITORS_SHIELD_SYS, OnEditorsShieldSys)
690 ON_COMMAND(ID_LEVEL_OBJ, OnLevelObj)
691 ON_COMMAND(ID_ALIGN_OBJ, OnAlignObj)
692 ON_COMMAND(ID_CONTROL_OBJ, OnControlObj)
693 ON_COMMAND(ID_NEXT_OBJ, OnNextObj)
694 ON_COMMAND(ID_PREV_OBJ, OnPrevObj)
695 ON_COMMAND(ID_EDIT_DELETE_WING, OnEditDeleteWing)
696 ON_COMMAND(ID_MARK_WING, OnMarkWing)
697 ON_UPDATE_COMMAND_UI(ID_CONTROL_OBJ, OnUpdateControlObj)
698 ON_COMMAND(ID_EDIT_DELETE, OnEditDelete)
699 ON_COMMAND(ID_AA_GRIDLINES, OnAaGridlines)
700 ON_UPDATE_COMMAND_UI(ID_AA_GRIDLINES, OnUpdateAaGridlines)
701 ON_COMMAND(ID_CMD_BRIEF, OnCmdBrief)
702 ON_COMMAND(ID_DISABLE_UNDO, OnDisableUndo)
703 ON_UPDATE_COMMAND_UI(ID_DISABLE_UNDO, OnUpdateDisableUndo)
704 ON_UPDATE_COMMAND_UI(ID_CMD_BRIEF, OnUpdateCmdBrief)
705 ON_COMMAND(ID_NEXT_SUBSYS, OnNextSubsys)
706 ON_COMMAND(ID_PREV_SUBSYS, OnPrevSubsys)
707 ON_COMMAND(ID_CANCEL_SUBSYS, OnCancelSubsys)
708 ON_COMMAND(ID_DUMP_STATS, OnDumpStats)
710 ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
711 ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
712 ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
713 ON_COMMAND_RANGE(ID_GROUP1, ID_GROUP9, OnGroup)
714 ON_COMMAND_RANGE(ID_SET_GROUP1, ID_SET_GROUP9, OnSetGroup)
717 /////////////////////////////////////////////////////////////////////////////
718 // CFREDView construction/destruction
720 CFREDView::CFREDView()
722 //m_ConfirmDeleting = TRUE;
723 //m_ShowCapitalShips = TRUE;
724 //m_ShowElevations = TRUE;
725 //m_ShowFighters = TRUE;
727 //m_ShowMiscObjects = TRUE;
728 //m_ShowPlanets = TRUE;
729 //m_ShowWaypoints = TRUE;
731 m_pGDlg = new CGrid(this);
735 //if (!(int errno = gr_init(640, 480, 32)))
736 // Error(LOCATION, "Hey, gr_init failed! Error code = %i", errno);
737 Fred_view_wnd = this;
740 CFREDView::~CFREDView()
747 void CALLBACK expire_game_proc( HWND wnd, UINT uMsg, UINT idEvent, DWORD dwTime)
750 if ( expire_game == EXPIRE_BAD_CHECKSUM )
751 MessageBox (wnd, "Fred can no longer run due to internal overlay error", NULL, MB_OK | MB_ICONERROR |MB_TASKMODAL|MB_SETFOREGROUND);
753 MessageBox (wnd, "Error: cannot enter DOS mode for 80x40 color text mode display.", NULL, MB_OK | MB_ICONERROR|MB_TASKMODAL|MB_SETFOREGROUND);
757 BOOL CFREDView::PreCreateWindow(CREATESTRUCT& cs)
761 casperl = CView::PreCreateWindow(cs);
762 cs.y = 0; // doesn't seem to do anything. :(
764 // other miscellaneous initializations
765 cfile_chdir("data\\missions");
766 set_physics_controls();
770 /////////////////////////////////////////////////////////////////////////////
773 void CFREDView::OnDraw(CDC* pDC)
777 CFREDDoc* pDoc = GetDocument();
784 pDC->GetClipBox(&clip);
785 gr_set_clip(clip.left, clip.top, clip.right - clip.left + 1, clip.bottom - clip.top + 1);
786 Assert(clip.left <= clip.right);
787 Assert(clip.top <= clip.bottom);
788 gr_flip_window((uint) pDC->m_hDC, clip.left, clip.top,
789 clip.right - clip.left + 1, clip.bottom - clip.top + 1);
792 /////////////////////////////////////////////////////////////////////////////
793 // CFREDView printing
795 BOOL CFREDView::OnPreparePrinting(CPrintInfo* pInfo)
797 // default preparation
798 return DoPreparePrinting(pInfo);
801 void CFREDView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
803 // TODO: add extra initialization before printing
806 void CFREDView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
808 // TODO: add cleanup after printing
811 /////////////////////////////////////////////////////////////////////////////
812 // CFREDView diagnostics
815 void CFREDView::AssertValid() const
817 CView::AssertValid();
820 void CFREDView::Dump(CDumpContext& dc) const
825 CFREDDoc* CFREDView::GetDocument() // non-debug version is inline
827 ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CFREDDoc)));
828 return (CFREDDoc*)m_pDocument;
832 /////////////////////////////////////////////////////////////////////////////
833 // CFREDView message handlers
835 void CFREDView::OnViewGrid()
837 Show_grid = !Show_grid;
841 void CFREDView::OnUpdateViewGrid(CCmdUI* pCmdUI)
843 pCmdUI->SetCheck(Show_grid);
846 void CFREDView::OnViewWaypoints()
848 Show_waypoints = !Show_waypoints;
852 void CFREDView::OnUpdateViewWaypoints(CCmdUI* pCmdUI)
854 pCmdUI->SetCheck(Show_waypoints);
857 #define MAX_MOVE_DISTANCE 25.0f
859 // If cur_object_index references a valid object, drag it from its current
860 // location to the new cursor location specified by "point".
861 // It is dragged relative to the main grid. It's y coordinate is not changed.
862 // Return value: 0/1 = didn't/did move object all the way to goal.
865 int z, cobj, flag, rval = 1;
867 float distance_moved = 0.0f;
868 vector cursor_dir, int_pnt;
869 vector movement_vector;
873 // starfield_bitmaps *bmp;
876 if (Bg_bitmap_dialog) {
880 bmp = &Starfield_bitmaps[Cur_bitmap];
881 if (Single_axis_constraint && Constraint.z) {
882 bmp->dist *= 1.0f + mouse_dx / -800.0f;
883 calculate_bitmap_points(bmp, 0.0f);
886 g3_point_to_vec_delayed(&bmp->m.fvec, marking_box.x2, marking_box.y2);
887 vm_orthogonalize_matrix(&bmp->m);
888 calculate_bitmap_points(bmp, 0.0f);
894 if (!query_valid_object())
897 if ((Dup_drag == 1) && (Briefing_dialog))
901 dup_object(NULL); // reset waypoint list
902 cobj = Duped_wing = -1;
904 objp = GET_FIRST(&obj_used_list);
905 while (objp != END_OF_LIST(&obj_used_list)) {
906 Assert(objp->type != OBJ_NONE);
907 if (objp->flags & OF_MARKED) {
908 if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
909 z = Ships[objp->instance].wingnum;
912 else if (Duped_wing != z)
919 z = dup_object(objp);
925 if (cur_object_index == OBJ_INDEX(objp) )
929 objp = GET_NEXT(objp);
932 obj_merge_created_list();
934 objp = GET_FIRST(&obj_used_list);
935 while (objp != END_OF_LIST(&obj_used_list)) {
936 ptr = GET_NEXT(objp);
937 if (objp->flags & OF_TEMP_MARKED)
949 objp = GET_FIRST(&obj_used_list);
950 while (objp != END_OF_LIST(&obj_used_list)) {
951 if (objp->flags & OF_TEMP_MARKED) {
952 objp->flags &= ~OF_TEMP_MARKED;
953 mark_object(OBJ_INDEX(objp));
956 objp = GET_NEXT(objp);
959 set_cur_object_index(cobj);
960 if (Duped_wing != -1)
961 Dup_drag = DUP_DRAG_OF_WING; // indication for later that we duped objects in a wing
965 drag_rotate_save_backup();
970 objp = &Objects[cur_object_index];
971 Assert(objp->type != OBJ_NONE);
972 obj = int_pnt = objp->pos;
974 // Get 3d vector specified by mouse cursor location.
975 g3_point_to_vec_delayed(&cursor_dir, marking_box.x2, marking_box.y2);
976 if (Single_axis_constraint) {
977 // if (fvi_ray_plane(&int_pnt, &obj, &view_orient.fvec, &view_pos, &cursor_dir, 0.0f) >= 0.0f ) {
978 // vm_vec_add(&p1, &obj, &Constraint);
979 // find_nearest_point_on_line(&nearest_point, &obj, &p1, &int_pnt);
980 // int_pnt = nearest_point;
981 // distance_moved = vm_vec_dist(&obj, &int_pnt);
984 vector tmpAnticonstraint = Anticonstraint;
985 vector tmpObject = obj;
987 tmpAnticonstraint.x = 0.0f;
988 r = fvi_ray_plane(&int_pnt, &tmpObject, &tmpAnticonstraint, &view_pos, &cursor_dir, 0.0f);
990 // If intersected behind viewer, don't move. Too confusing, not what user wants.
991 vm_vec_sub(&vec1, &int_pnt, &view_pos);
992 vm_vec_sub(&vec2, &obj, &view_pos);
993 if ((r>=0.0f) && (vm_vec_dot(&vec1, &vec2) >= 0.0f)) {
995 vm_vec_sub( &tmp1, &int_pnt, &obj );
996 tmp1.x *= Constraint.x;
997 tmp1.y *= Constraint.y;
998 tmp1.z *= Constraint.z;
999 vm_vec_add( &int_pnt, &obj, &tmp1 );
1001 distance_moved = vm_vec_dist(&obj, &int_pnt);
1005 } else { // Move in x-z plane, defined by grid. Preserve height.
1006 r = fvi_ray_plane(&int_pnt, &obj, &Anticonstraint, &view_pos, &cursor_dir, 0.0f);
1008 // If intersected behind viewer, don't move. Too confusing, not what user wants.
1009 vm_vec_sub(&vec1, &int_pnt, &view_pos);
1010 vm_vec_sub(&vec2, &obj, &view_pos);
1011 if ((r>=0.0f) && (vm_vec_dot(&vec1, &vec2) >= 0.0f))
1012 distance_moved = vm_vec_dist(&obj, &int_pnt);
1015 // If moved too far, then move max distance along vector.
1016 vm_vec_sub(&movement_vector, &int_pnt, &obj);
1017 /* if (distance_moved > MAX_MOVE_DISTANCE) {
1018 vm_vec_normalize(&movement_vector);
1019 vm_vec_scale(&movement_vector, MAX_MOVE_DISTANCE);
1022 if (distance_moved) {
1023 objp = GET_FIRST(&obj_used_list);
1024 while (objp != END_OF_LIST(&obj_used_list)) {
1025 Assert(objp->type != OBJ_NONE);
1026 if (objp->flags & OF_MARKED) {
1027 vm_vec_add(&objp->pos, &objp->pos, &movement_vector);
1028 if (objp->type == OBJ_WAYPOINT) {
1029 Waypoint_lists[objp->instance / 65536].waypoints[objp->instance & 0xffff] =
1034 objp = GET_NEXT(objp);
1037 objp = GET_FIRST(&obj_used_list);
1038 while (objp != END_OF_LIST(&obj_used_list)) {
1039 if (objp->flags & OF_MARKED)
1042 objp = GET_NEXT(objp);
1046 if (Briefing_dialog)
1047 Briefing_dialog->update_positions();
1053 void drag_rotate_save_backup()
1058 if (Cur_bitmap != -1)
1059 bitmap_matrix_backup = Starfield_bitmaps[Cur_bitmap].m;
1062 objp = GET_FIRST(&obj_used_list);
1063 while (objp != END_OF_LIST(&obj_used_list)) {
1064 Assert(objp->type != OBJ_NONE);
1065 if (objp->flags & OF_MARKED) {
1066 rotation_backup[OBJ_INDEX(objp)].pos = objp->pos;
1067 rotation_backup[OBJ_INDEX(objp)].orient = objp->orient;
1070 objp = GET_NEXT(objp);
1074 int drag_rotate_objects()
1077 vector int_pnt, obj;
1079 matrix leader_orient, leader_transpose, tmp, newmat, rotmat;
1080 object *leader, *objp;
1081 // starfield_bitmaps *bmp;
1085 if (Bg_bitmap_dialog) {
1089 bmp = &Starfield_bitmaps[Cur_bitmap];
1090 calculate_bitmap_points(bmp, mouse_dx / -300.0f);
1095 if (!query_valid_object()){
1099 objp = &Objects[cur_object_index];
1100 Assert(objp->type != OBJ_NONE);
1101 obj = int_pnt = objp->pos;
1103 memset(&a, 0, sizeof(angles));
1104 if (Single_axis_constraint) {
1106 a.p = mouse_dy / REDUCER;
1107 else if (Constraint.y)
1108 a.h = mouse_dx / REDUCER;
1109 else if (Constraint.z)
1110 a.b = -mouse_dx / REDUCER;
1113 if (!Constraint.x) { // yz
1114 a.b = -mouse_dx / REDUCER;
1115 a.h = mouse_dy / REDUCER;
1116 } else if (!Constraint.y) { // xz
1117 a.p = mouse_dy / REDUCER;
1118 a.b = -mouse_dx / REDUCER;
1119 } else if (!Constraint.z) { // xy
1120 a.p = mouse_dy / REDUCER;
1121 a.h = mouse_dx / REDUCER;
1125 leader = &Objects[cur_object_index];
1126 leader_orient = leader->orient; // save original orientation
1127 vm_copy_transpose_matrix(&leader_transpose, &leader_orient);
1129 vm_angles_2_matrix(&rotmat, &a);
1130 vm_matrix_x_matrix(&newmat, &leader->orient, &rotmat);
1131 leader->orient = newmat;
1133 objp = GET_FIRST(&obj_used_list);
1134 while (objp != END_OF_LIST(&obj_used_list)) {
1135 Assert(objp->type != OBJ_NONE);
1136 if ((objp->flags & OF_MARKED) && (cur_object_index != OBJ_INDEX(objp) )) {
1139 vector tmpv1, tmpv2;
1141 // change rotation matrix to rotate in opposite direction. This rotation
1142 // matrix is what the leader ship has rotated by.
1143 vm_copy_transpose_matrix(&rot_trans, &rotmat);
1145 // get point relative to our point of rotation (make POR the origin).
1146 vm_vec_sub(&tmpv1, &objp->pos, &leader->pos);
1148 // convert point from real-world coordinates to leader's relative coordinate
1149 // system (z=forward vec, y=up vec, x=right vec
1150 vm_vec_rotate(&tmpv2, &tmpv1, &leader_orient);
1152 // now rotate the point by the transpose from above.
1153 vm_vec_rotate(&tmpv1, &tmpv2, &rot_trans);
1155 // convert point back into real-world coordinates
1156 vm_vec_rotate(&tmpv2, &tmpv1, &leader_transpose);
1158 // and move origin back to real-world origin. Object is now at it's correct
1160 vm_vec_add(&objp->pos, &leader->pos, &tmpv2);
1162 // Now fix the object's orientation to what it should be.
1163 vm_matrix_x_matrix(&tmp, &objp->orient, &rotmat);
1164 vm_orthogonalize_matrix(&tmp); // safety check
1168 vm_matrix_x_matrix(&tmp, &objp->orient, &rotmat);
1173 objp = GET_NEXT(objp);
1176 objp = GET_FIRST(&obj_used_list);
1177 while (objp != END_OF_LIST(&obj_used_list)) {
1178 if (objp->flags & OF_MARKED)
1181 objp = GET_NEXT(objp);
1193 if (Bg_bitmap_dialog) {
1194 Assert(!vm_check_matrix_for_zeros(&bitmap_matrix_backup));
1195 Starfield_bitmaps[Cur_bitmap].m = bitmap_matrix_backup;
1196 calculate_bitmap_points(&Starfield_bitmaps[Cur_bitmap], 0.0f);
1197 button_down = box_marking = 0;
1204 if (Editing_mode == 1) {
1205 vector movement_vector;
1208 if (query_valid_object()) {
1209 objp = &Objects[cur_object_index];
1210 Assert(objp->type != OBJ_NONE);
1211 vm_vec_sub(&movement_vector, &original_pos, &objp->pos);
1213 objp = GET_FIRST(&obj_used_list);
1214 while (objp != END_OF_LIST(&obj_used_list)) {
1215 Assert(objp->type != OBJ_NONE);
1216 if (objp->flags & OF_MARKED)
1217 vm_vec_add(&objp->pos, &objp->pos, &movement_vector);
1219 objp = GET_NEXT(objp);
1223 } else if (Editing_mode == 2) {
1226 objp = GET_FIRST(&obj_used_list);
1227 while (objp != END_OF_LIST(&obj_used_list)) {
1228 Assert(objp->type != OBJ_NONE);
1229 if (objp->flags & OF_MARKED) {
1230 int obj_index = OBJ_INDEX(objp);
1232 if(!IS_VEC_NULL(&rotation_backup[obj_index].orient.rvec) &&
1233 !IS_VEC_NULL(&rotation_backup[obj_index].orient.uvec) &&
1234 !IS_VEC_NULL(&rotation_backup[obj_index].orient.fvec)){
1236 objp->pos = rotation_backup[obj_index].pos;
1237 objp->orient = rotation_backup[obj_index].orient;
1241 objp = GET_NEXT(objp);
1246 button_down = box_marking = 0;
1247 if (Briefing_dialog)
1248 Briefing_dialog->update_positions();
1251 void CFREDView::OnLButtonDown(UINT nFlags, CPoint point)
1256 CView::OnLButtonDown(nFlags, point);
1260 if (cur_waypoint != -1)
1261 list = cur_waypoint_list * 65536 + cur_waypoint;
1263 marking_box.x1 = point.x;
1264 marking_box.y1 = point.y;
1267 on_object = select_object(point.x, point.y);
1270 drag_rotate_save_backup();
1272 if (nFlags & MK_CONTROL) { // add a new object
1273 if (!Bg_bitmap_dialog) {
1274 if (on_object == -1) {
1275 Selection_lock = 0; // force off selection lock
1276 on_object = create_object_on_grid(list);
1283 Selection_lock = 0; // force off selection lock
1284 on_object = Cur_bitmap = create_bg_bitmap();
1285 Bg_bitmap_dialog->update_data();
1287 if (Cur_bitmap == -1)
1288 MessageBox("Background bitmap limit reached.\nCan't add more.");
1292 } else if (!Selection_lock) {
1293 if (Bg_bitmap_dialog) {
1294 Cur_bitmap = on_object;
1295 Bg_bitmap_dialog -> update_data();
1297 } else if ((nFlags & MK_SHIFT) || (on_object == -1) || !(Objects[on_object].flags & OF_MARKED)) {
1298 if (!(nFlags & MK_SHIFT))
1301 if (on_object != -1) {
1302 if (Objects[on_object].flags & OF_MARKED)
1303 unmark_object(on_object);
1305 mark_object(on_object);
1310 if (query_valid_object())
1311 original_pos = Objects[cur_object_index].pos;
1314 if (Selection_lock) {
1315 if (Editing_mode == 1)
1317 else if (Editing_mode == 2)
1318 drag_rotate_objects();
1323 if (query_valid_object() && (Marked == 1) && (Objects[cur_object_index].type == OBJ_POINT)) {
1324 Assert(Briefing_dialog);
1325 Briefing_dialog->icon_select(Objects[cur_object_index].instance);
1328 if (Briefing_dialog)
1329 Briefing_dialog->icon_select(-1);
1332 CView::OnLButtonDown(nFlags, point);
1335 void CFREDView::OnMouseMove(UINT nFlags, CPoint point)
1337 mouse_dx = point.x - last_mouse_x;
1338 mouse_dy = point.y - last_mouse_y;
1339 last_mouse_x = marking_box.x2 = point.x;
1340 last_mouse_y = marking_box.y2 = point.y;
1341 Cursor_over = select_object(point.x, point.y);
1343 if (!(nFlags & MK_LBUTTON))
1346 // The following will cancel a drag operation if another program running in memory
1347 // happens to jump in and take over (such as new email has arrived popup boxes).
1348 if (button_down && GetCapture() != this)
1351 if (!button_down && GetCapture() == this)
1355 if (abs(marking_box.x1 - marking_box.x2) > 1 || abs(marking_box.y1 - marking_box.y2) > 1)
1359 if (on_object != -1 || Selection_lock) {
1360 if (Editing_mode == 1)
1362 else if (Editing_mode == 2)
1363 drag_rotate_objects();
1365 } else if (!Bg_bitmap_dialog)
1368 if (mouse_dx || mouse_dy)
1373 CView::OnMouseMove(nFlags, point);
1376 void CFREDView::OnLButtonUp(UINT nFlags, CPoint point)
1378 marking_box.x2 = point.x;
1379 marking_box.y2 = point.y;
1381 if (button_down && GetCapture() != this)
1384 if (GetCapture() == this)
1388 if ((abs(marking_box.x1 - marking_box.x2) > 1) || (abs(marking_box.y1 - marking_box.y2) > 1))
1392 if ((on_object != -1) || Selection_lock) {
1393 if (Editing_mode == 1)
1395 else if ((Editing_mode == 2) || (Editing_mode == 3))
1396 drag_rotate_objects();
1399 FREDDoc_ptr->autosave("object move");
1405 if (Bg_bitmap_dialog) {
1413 } else if ((!moved && on_object != -1) && !Selection_lock && !(nFlags & MK_SHIFT)) {
1415 mark_object(on_object);
1421 if (Dup_drag == DUP_DRAG_OF_WING) {
1426 sprintf(msg, "Add cloned ships to wing %s?", Wings[Duped_wing].name);
1427 if (MessageBox(msg, "Query", MB_YESNO) == IDYES) {
1428 objp = GET_FIRST(&obj_used_list);
1429 while (objp != END_OF_LIST(&obj_used_list)) {
1430 if (objp->flags & OF_MARKED) {
1431 if (Wings[Duped_wing].wave_count >= MAX_SHIPS_PER_WING) {
1432 MessageBox("Max ships per wing limit reached");
1436 // Can't do player starts, since only player 1 is currently allowed to be in a wing
1438 Assert(objp->type == OBJ_SHIP);
1439 ship = objp->instance;
1440 Assert(Ships[ship].wingnum == -1);
1441 sprintf(Ships[ship].ship_name, "%s %d", Wings[Duped_wing].name,
1442 Wings[Duped_wing].wave_count + 1);
1444 Wings[Duped_wing].ship_index[Wings[Duped_wing].wave_count] = ship;
1445 Ships[ship].wingnum = Duped_wing;
1447 wing_objects[Duped_wing][Wings[Duped_wing].wave_count] = OBJ_INDEX(objp);
1448 Wings[Duped_wing].wave_count++;
1451 objp = GET_NEXT(objp);
1457 if (query_valid_object() && (Marked == 1) && (Objects[cur_object_index].type == OBJ_POINT)) {
1458 Assert(Briefing_dialog);
1459 Briefing_dialog->icon_select(Objects[cur_object_index].instance);
1462 if (Briefing_dialog)
1463 Briefing_dialog->icon_select(-1);
1466 CView::OnLButtonUp(nFlags, point);
1469 // This function never gets called because nothing causes
1470 // the WM_GOODBYE event to occur.
1471 // False! When you close the Ship Dialog, this function is called! --MK, 8/30/96
1472 LONG CFREDView::OnGoodbye(UINT wParam, LONG lParam)
1474 Ship_editor_dialog.DestroyWindow();
1475 Wing_editor_dialog.DestroyWindow();
1480 void CFREDView::OnEditorsShips()
1484 Assert(Ship_editor_dialog.GetSafeHwnd());
1485 if (!Show_sexp_help)
1486 adjust = -SEXP_HELP_BOX_SIZE;
1488 if (!theApp.init_window(&Ship_wnd_data, &Ship_editor_dialog, adjust))
1491 Ship_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0,
1492 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1493 Ship_editor_dialog.ShowWindow(SW_RESTORE);
1496 void CFREDView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT lParam)
1500 lKeyData = lParam & 255; // key data
1501 if (lParam & 256) lKeyData += 0x80;
1502 key_mark(lKeyData, 1, 0);
1504 CView::OnKeyDown(nChar, nRepCnt, lParam);
1507 void CFREDView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT lParam)
1511 lKeyData = lParam & 255; // key data
1512 if (lParam & 256) lKeyData += 0x80;
1513 key_mark(lKeyData, 0, 0);
1515 CView::OnKeyUp(nChar, nRepCnt, lParam);
1518 void CFREDView::OnSetFocus(CWnd* pOldWnd)
1520 static int flag = 0;
1526 nprintf(("Fred routing", "OnSetFocus() called\n"));
1528 Ship_editor_dialog.initialize_data(1);
1533 Wing_editor_dialog.initialize_data(1);
1537 /* if (Wing_editor_dialog.verify() == -1)
1540 if (Ship_editor_dialog.verify() == -1)
1543 if (update_dialog_boxes()) {
1544 nprintf(("Fred routing", "OnSetFocus() returned (error occured)\n"));
1546 Ship_editor_dialog.bypass_errors = 0;
1547 Wing_editor_dialog.bypass_errors = 0;
1551 if (Local_modified) {
1552 FREDDoc_ptr->autosave("Editing");
1557 CView::OnSetFocus(pOldWnd);
1558 nprintf(("Fred routing", "Main window focus accepted\n"));
1562 if (GetActiveWindow() != Fred_main_wnd) {
1563 Fred_main_wnd->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
1564 nprintf(("Fred routing", "OnSetFocus() had to put main window back on top\n"));
1570 void CFREDView::OnKillFocus(CWnd* pNewWnd)
1572 nprintf(("Fred routing", "OnKillFocus() called\n"));
1575 CView::OnKillFocus(pNewWnd);
1580 void CFREDView::OnSize(UINT nType, int cx, int cy)
1582 CView::OnSize(nType, cx, cy);
1584 if ((cx > 0) && (cy > 0)) {
1585 gr_init(GR_640, GR_SOFTWARE, 8, cx, cy);
1589 void do_trackball_stuff(int nFlags, CPoint point)
1593 if (nFlags & MK_LBUTTON){
1596 if (nFlags & MK_RBUTTON){
1600 move_mouse(btn, point.x, point.y);
1603 // If add_flag != 0, then add found objects to current wing, else create new wing.
1604 void select_objects()
1606 int x, y, valid, icon_mode = 0;
1610 if (marking_box.x1 > marking_box.x2) {
1612 marking_box.x1 = marking_box.x2;
1616 if (marking_box.y1 > marking_box.y2) {
1618 marking_box.y1 = marking_box.y2;
1622 ptr = GET_FIRST(&obj_used_list);
1623 while (ptr != END_OF_LIST(&obj_used_list)) {
1625 if (ptr->flags & OF_HIDDEN)
1628 Assert(ptr->type != OBJ_NONE);
1629 switch (ptr->type) {
1631 if (!Show_waypoints)
1643 switch (Ships[ptr->instance].team) {
1663 g3_rotate_vertex(&v, &ptr->pos);
1664 if (!(v.codes & CC_BEHIND) && valid)
1665 if (!(g3_project_vertex(&v) & PF_OVERFLOW)) {
1669 if (x >= marking_box.x1 && x <= marking_box.x2 && y >= marking_box.y1 && y <= marking_box.y2) {
1670 if (ptr->flags & OF_MARKED)
1671 unmark_object(OBJ_INDEX(ptr));
1673 mark_object(OBJ_INDEX(ptr));
1675 if (ptr->type == OBJ_POINT)
1680 ptr = GET_NEXT(ptr);
1684 ptr = GET_FIRST(&obj_used_list);
1685 while (ptr != END_OF_LIST(&obj_used_list)) {
1686 if ((ptr->flags & OF_MARKED) && (ptr->type != OBJ_POINT))
1687 unmark_object(OBJ_INDEX(ptr));
1689 ptr = GET_NEXT(ptr);
1693 Update_ship = Update_wing = 1;
1696 LONG CFREDView::OnMenuPopupShips(UINT wParam, LONG lParam)
1701 point = * ((CPoint*) lParam);
1703 ClientToScreen(&point);
1705 menu.LoadMenu(IDR_MENU_SHIP_POPUP);
1706 menu.GetSubMenu(0)->TrackPopupMenu(
1707 TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
1712 LONG CFREDView::OnMenuPopupEdit(UINT wParam, LONG lParam)
1717 point = * ((CPoint*) lParam);
1719 ClientToScreen(&point);
1721 menu.LoadMenu(IDR_MENU_EDIT_POPUP);
1722 menu.GetSubMenu(0)->TrackPopupMenu(
1723 TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
1728 int g_Ships_as_icons = 0;
1730 void CFREDView::OnMiscstuffShowshipsasicons()
1733 if (g_Ships_as_icons == 0)
1734 g_Ships_as_icons = 1;
1736 g_Ships_as_icons = 0;
1739 // right mouse button popup menu stuff
1740 void CFREDView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
1742 // make sure window is active
1743 // GetParentFrame()->ActivateFrame();
1747 CPoint local = point;
1754 ScreenToClient(&local);
1755 objnum = select_object(local.x, local.y);
1758 set_cur_object_index(objnum);
1759 if (menu.LoadMenu(IDR_MENU_SHIP_POPUP)) {
1760 int id = ID_EDITORS_SHIPS;
1761 CMenu* pPopup = menu.GetSubMenu(0);
1763 ASSERT(pPopup != NULL);
1765 pPopup->ModifyMenu(ID_EDITORS_SHIPS, MF_BYCOMMAND | MF_STRING, ID_EDITORS_SHIPS, "Edit Marked Ships");
1769 if ((Objects[objnum].type == OBJ_START) || (Objects[objnum].type == OBJ_SHIP))
1770 str.Format("Edit %s", Ships[Objects[objnum].instance].ship_name);
1772 else if (Objects[objnum].type == OBJ_JUMP_NODE) {
1773 id = ID_EDITORS_WAYPOINT;
1774 str.Format("Edit %s", Jump_nodes[Objects[objnum].instance].name);
1776 } else if (Objects[objnum].type == OBJ_WAYPOINT) {
1777 id = ID_EDITORS_WAYPOINT;
1778 str.Format("Edit %s:%d", Waypoint_lists[Objects[objnum].instance / 65536].name,
1779 (Objects[objnum].instance & 0xffff) + 1);
1781 } else if (Objects[objnum].type == OBJ_POINT) {
1786 str = _T("Unknown");
1789 pPopup->ModifyMenu(ID_EDITORS_SHIPS, MF_BYCOMMAND | MF_STRING, id, str);
1792 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd()); // use main window for cmds
1796 if (menu.LoadMenu(IDR_MENU_EDIT_POPUP)) {
1798 CMenu* pPopup = menu.GetSubMenu(0);
1799 CMenu shipPopup, player_submenu, species_submenu[MAX_SPECIES_NAMES];
1800 ASSERT(pPopup != NULL);
1802 // create a popup menu based on the ship models read in ship.cpp.
1803 shipPopup.CreatePopupMenu();
1804 shipPopup.AppendMenu(MF_STRING | MF_ENABLED, SHIP_TYPES + Id_select_type_waypoint, "Waypoint");
1805 shipPopup.AppendMenu(MF_STRING | MF_ENABLED, SHIP_TYPES + Id_select_type_start, "Player Start");
1806 shipPopup.AppendMenu(MF_STRING | MF_ENABLED, SHIP_TYPES + Id_select_type_jump_node, "Jump Node");
1807 for (i=0; i<MAX_SPECIES_NAMES; i++) {
1808 species_submenu[i].CreatePopupMenu();
1809 shipPopup.AppendMenu(MF_STRING | MF_POPUP | MF_ENABLED,
1810 (UINT) species_submenu[i].m_hMenu, Species_names[i]);
1813 for (i=0; i<Num_ship_types; i++)
1814 species_submenu[Ship_info[i].species].AppendMenu(MF_STRING |
1815 MF_ENABLED, SHIP_TYPES + i, Ship_info[i].name);
1817 pPopup->AppendMenu(MF_STRING | MF_POPUP | MF_ENABLED,
1818 (UINT) shipPopup.m_hMenu, "New Object Type");
1820 CWnd::DrawMenuBar(); // AppendMenu documentation says to do this.
1821 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd());
1826 void CFREDView::OnEditPopupShowShipIcons()
1828 Show_ship_info = !Show_ship_info;
1829 theApp.write_ini_file();
1833 void CFREDView::OnUpdateEditPopupShowShipIcons(CCmdUI* pCmdUI)
1835 pCmdUI->SetCheck(Show_ship_info);
1838 void CFREDView::OnEditPopupShowShipModels()
1840 Show_ship_models = !Show_ship_models;
1841 theApp.write_ini_file();
1845 void CFREDView::OnUpdateEditPopupShowShipModels(CCmdUI* pCmdUI)
1847 pCmdUI->SetCheck(Show_ship_models);
1850 void CFREDView::OnEditPopupShowCompass()
1852 Show_compass = !Show_compass;
1853 theApp.write_ini_file();
1857 void CFREDView::OnUpdateEditPopupShowCompass(CCmdUI* pCmdUI)
1859 pCmdUI->SetCheck(Show_compass);
1862 // View implementation file
1863 CFREDView *CFREDView::GetView()
1865 CFrameWnd *pFrame = (CFrameWnd *) (AfxGetApp()->m_pMainWnd);
1867 CView *pView = pFrame->GetActiveView();
1872 // Fail if view is of wrong kind
1873 // (this could occur with splitter windows, or additional
1874 // views on a single document
1875 if (! pView->IsKindOf(RUNTIME_CLASS(CFREDView)))
1878 return (CFREDView *) pView;
1881 /*void CFREDView::OnShipType0()
1883 cur_model_index = 1;
1886 void CFREDView::OnShipType1()
1888 cur_model_index = 2;
1891 void CFREDView::OnShipType2()
1893 cur_model_index = 3;
1896 void CFREDView::OnShipType3()
1898 cur_model_index = 4;
1901 void CFREDView::OnShipType4()
1903 cur_model_index = 5;
1906 void CFREDView::OnShipType5()
1908 cur_model_index = 6;
1911 void CFREDView::OnUpdateShipType1(CCmdUI* pCmdUI)
1913 pCmdUI->SetCheck(cur_model_index == 2);
1916 void CFREDView::OnUpdateShipType2(CCmdUI* pCmdUI)
1918 pCmdUI->SetCheck(cur_model_index == 3);
1921 void CFREDView::OnUpdateShipType3(CCmdUI* pCmdUI)
1923 pCmdUI->SetCheck(cur_model_index == 4);
1926 void CFREDView::OnUpdateShipType4(CCmdUI* pCmdUI)
1928 pCmdUI->SetCheck(cur_model_index == 5);
1931 void CFREDView::OnUpdateShipType5(CCmdUI* pCmdUI)
1933 pCmdUI->SetCheck(cur_model_index == 6);
1937 void CFREDView::OnUpdateShipType0(CCmdUI* pCmdUI)
1939 pCmdUI->SetCheck(cur_model_index == 1);
1943 void CFREDView::OnEditShipType6()
1945 cur_model_index = 7;
1949 void CFREDView::OnUpdateEditShipType6(CCmdUI* pCmdUI)
1951 pCmdUI->SetCheck(cur_model_index == 7);
1955 // following code added by MWA 09/04/96
1956 // Implements messages for popup menu built on the fly.
1957 // not sure how stable the code is, but appears to work for now.
1958 // id's for the menu items are simply the model numbers for
1959 // the ships. Shouldn't conflict with any other ID_* thingys.
1960 BOOL CFREDView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
1964 if (!pHandlerInfo) {
1965 if ((id >= SHIP_TYPES) && (id < SHIP_TYPES + Num_ship_types + 3)) {
1966 if (nCode == CN_COMMAND) {
1967 cur_model_index = id - SHIP_TYPES;
1968 m_new_ship_type_combo_box.SetCurSelNEW(cur_model_index);
1970 } else if (nCode == CN_UPDATE_COMMAND_UI) {
1971 // Update UI element state
1972 ((CCmdUI*) pExtra)->SetCheck(cur_model_index + SHIP_TYPES == id);
1973 ((CCmdUI*) pExtra)->Enable(TRUE);
1980 return CView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
1983 void CFREDView::OnMiscStatistics()
1988 "Number of Objects: %d\n"
1989 "Number of Ships: %d\n"
1990 "Number of Wings: %d\n",
1991 num_objects, ship_get_num_ships(), num_wings);
1993 MessageBox(buf, "FRED Statistics");
1996 void CFREDView::OnUpdateChangeViewpointExternal(CCmdUI* pCmdUI)
1998 pCmdUI->SetCheck(!viewpoint);
2001 void CFREDView::OnChangeViewpointExternal()
2007 void CFREDView::OnUpdateChangeViewpointFollow(CCmdUI* pCmdUI)
2009 pCmdUI->SetCheck(viewpoint == 1);
2012 void CFREDView::OnChangeViewpointFollow()
2015 view_obj = cur_object_index;
2019 void CFREDView::OnEditorsGoals()
2021 CMissionGoalsDlg dlg;
2026 void CFREDView::OnSpeed1()
2029 set_physics_controls();
2032 void CFREDView::OnSpeed2()
2035 set_physics_controls();
2038 void CFREDView::OnSpeed3()
2041 set_physics_controls();
2044 void CFREDView::OnSpeed5()
2047 set_physics_controls();
2050 void CFREDView::OnSpeed8()
2053 set_physics_controls();
2056 void CFREDView::OnSpeed10()
2059 set_physics_controls();
2062 void CFREDView::OnSpeed50()
2065 set_physics_controls();
2068 void CFREDView::OnSpeed100()
2070 physics_speed = 100;
2071 set_physics_controls();
2074 void CFREDView::OnRot1()
2077 set_physics_controls();
2080 void CFREDView::OnRot2()
2083 set_physics_controls();
2086 void CFREDView::OnRot3()
2089 set_physics_controls();
2092 void CFREDView::OnRot4()
2095 set_physics_controls();
2098 void CFREDView::OnRot5()
2101 set_physics_controls();
2104 void CFREDView::OnUpdateSpeed1(CCmdUI* pCmdUI)
2106 pCmdUI->SetCheck(physics_speed == 1);
2109 void CFREDView::OnUpdateSpeed2(CCmdUI* pCmdUI)
2111 pCmdUI->SetCheck(physics_speed == 2);
2114 void CFREDView::OnUpdateSpeed3(CCmdUI* pCmdUI)
2116 pCmdUI->SetCheck(physics_speed == 3);
2119 void CFREDView::OnUpdateSpeed5(CCmdUI* pCmdUI)
2121 pCmdUI->SetCheck(physics_speed == 5);
2124 void CFREDView::OnUpdateSpeed8(CCmdUI* pCmdUI)
2126 pCmdUI->SetCheck(physics_speed == 8);
2129 void CFREDView::OnUpdateSpeed10(CCmdUI* pCmdUI)
2131 pCmdUI->SetCheck(physics_speed == 10);
2134 void CFREDView::OnUpdateSpeed50(CCmdUI* pCmdUI)
2136 pCmdUI->SetCheck(physics_speed == 50);
2139 void CFREDView::OnUpdateSpeed100(CCmdUI* pCmdUI)
2141 pCmdUI->SetCheck(physics_speed == 100);
2144 void CFREDView::OnUpdateRot1(CCmdUI* pCmdUI)
2146 pCmdUI->SetCheck(physics_rot == 2);
2149 void CFREDView::OnUpdateRot2(CCmdUI* pCmdUI)
2151 pCmdUI->SetCheck(physics_rot == 10);
2154 void CFREDView::OnUpdateRot3(CCmdUI* pCmdUI)
2156 pCmdUI->SetCheck(physics_rot == 25);
2159 void CFREDView::OnUpdateRot4(CCmdUI* pCmdUI)
2161 pCmdUI->SetCheck(physics_rot == 50);
2164 void CFREDView::OnUpdateRot5(CCmdUI* pCmdUI)
2166 pCmdUI->SetCheck(physics_rot == 100);
2169 void CFREDView::OnControlModeCamera()
2174 void CFREDView::OnUpdateControlModeCamera(CCmdUI* pCmdUI)
2176 pCmdUI->SetCheck(!Control_mode);
2179 void CFREDView::OnControlModeShip()
2184 void CFREDView::OnUpdateControlModeShip(CCmdUI* pCmdUI)
2186 pCmdUI->SetCheck(Control_mode == 1);
2189 void CFREDView::OnShowGridPositions()
2191 Show_grid_positions = !Show_grid_positions;
2192 theApp.write_ini_file();
2196 void CFREDView::OnUpdateShowGridPositions(CCmdUI* pCmdUI)
2198 pCmdUI->SetCheck(Show_grid_positions);
2201 void CFREDView::OnShowCoordinates()
2203 Show_coordinates = !Show_coordinates;
2204 theApp.write_ini_file();
2208 void CFREDView::OnUpdateShowCoordinates(CCmdUI* pCmdUI)
2210 pCmdUI->SetCheck(Show_coordinates);
2213 void CFREDView::OnSelect()
2218 void CFREDView::OnUpdateSelect(CCmdUI* pCmdUI)
2220 pCmdUI->SetCheck(!Editing_mode);
2223 void CFREDView::OnSelectAndMove()
2228 void CFREDView::OnUpdateSelectAndMove(CCmdUI* pCmdUI)
2230 pCmdUI->SetCheck(Editing_mode == 1);
2233 void CFREDView::OnSelectAndRotate()
2238 void CFREDView::OnUpdateSelectAndRotate(CCmdUI* pCmdUI)
2240 pCmdUI->SetCheck(Editing_mode == 2);
2243 void CFREDView::OnConstrainX()
2245 vm_vec_make(&Constraint, 1.0f, 0.0f, 0.0f);
2246 vm_vec_make(&Anticonstraint, 0.0f, 1.0f, 1.0f);
2247 Single_axis_constraint = 1;
2250 void CFREDView::OnUpdateConstrainX(CCmdUI* pCmdUI)
2252 pCmdUI->SetRadio(Constraint.x && !Constraint.y && !Constraint.z);
2255 void CFREDView::OnConstrainY()
2257 vm_vec_make(&Constraint, 0.0f, 1.0f, 0.0f);
2258 vm_vec_make(&Anticonstraint, 1.0f, 0.0f, 1.0f);
2259 Single_axis_constraint = 1;
2262 void CFREDView::OnUpdateConstrainY(CCmdUI* pCmdUI)
2264 pCmdUI->SetRadio(!Constraint.x && Constraint.y && !Constraint.z);
2267 void CFREDView::OnConstrainZ()
2269 vm_vec_make(&Constraint, 0.0f, 0.0f, 1.0f);
2270 vm_vec_make(&Anticonstraint, 1.0f, 1.0f, 0.0f);
2271 Single_axis_constraint = 1;
2274 void CFREDView::OnUpdateConstrainZ(CCmdUI* pCmdUI)
2276 pCmdUI->SetRadio(!Constraint.x && !Constraint.y && Constraint.z);
2279 void CFREDView::OnConstrainXz()
2281 vm_vec_make(&Constraint, 1.0f, 0.0f, 1.0f);
2282 vm_vec_make(&Anticonstraint, 0.0f, 1.0f, 0.0f);
2283 Single_axis_constraint = 0;
2286 void CFREDView::OnUpdateConstrainXz(CCmdUI* pCmdUI)
2288 pCmdUI->SetRadio(Constraint.x && !Constraint.y && Constraint.z);
2291 void CFREDView::OnConstrainXy()
2293 vm_vec_make(&Constraint, 1.0f, 1.0f, 0.0f);
2294 vm_vec_make(&Anticonstraint, 0.0f, 0.0f, 1.0f);
2295 Single_axis_constraint = 0;
2298 void CFREDView::OnUpdateConstrainXy(CCmdUI* pCmdUI)
2300 pCmdUI->SetRadio(Constraint.x && Constraint.y && !Constraint.z);
2303 void CFREDView::OnConstrainYz()
2305 vm_vec_make(&Constraint, 0.0f, 1.0f, 1.0f);
2306 vm_vec_make(&Anticonstraint, 1.0f, 0.0f, 0.0f);
2307 Single_axis_constraint = 0;
2310 void CFREDView::OnUpdateConstrainYz(CCmdUI* pCmdUI)
2312 pCmdUI->SetRadio(!Constraint.x && Constraint.y && Constraint.z);
2315 void CFREDView::OnSelectionLock()
2317 Selection_lock = !Selection_lock;
2320 void CFREDView::OnUpdateSelectionLock(CCmdUI* pCmdUI)
2322 pCmdUI->SetCheck(Selection_lock);
2325 void CFREDView::OnLButtonDblClk(UINT nFlags, CPoint point)
2327 CView::OnLButtonDblClk(nFlags, point);
2328 if (Cursor_over != -1) {
2329 switch (Objects[Cursor_over].type) {
2337 OnEditorsWaypoint();
2341 } else if (Briefing_dialog)
2342 Fixed_briefing_size = !Fixed_briefing_size;
2345 void CFREDView::OnDoubleFineGridlines()
2347 double_fine_gridlines = !double_fine_gridlines;
2348 maybe_create_new_grid(The_grid, &eye_pos, &eye_orient, 1);
2349 theApp.write_ini_file();
2353 void CFREDView::OnUpdateDoubleFineGridlines(CCmdUI* pCmdUI)
2355 pCmdUI->SetCheck(double_fine_gridlines);
2358 void CFREDView::OnShowDistances()
2360 Show_distances = !Show_distances;
2361 theApp.write_ini_file();
2365 void CFREDView::OnUpdateShowDistances(CCmdUI* pCmdUI)
2367 pCmdUI->SetCheck(Show_distances);
2370 void CFREDView::OnUniversalHeading()
2372 Universal_heading = !Universal_heading;
2375 void CFREDView::OnUpdateUniversalHeading(CCmdUI* pCmdUI)
2377 pCmdUI->SetCheck(Universal_heading);
2380 void CFREDView::OnFlyingControls()
2382 Flying_controls_mode = !Flying_controls_mode;
2385 void CFREDView::OnUpdateFlyingControls(CCmdUI* pCmdUI)
2387 pCmdUI->SetCheck(Flying_controls_mode);
2390 void CFREDView::OnRotateLocally()
2392 Group_rotate = !Group_rotate;
2395 void CFREDView::OnUpdateRotateLocally(CCmdUI* pCmdUI)
2397 pCmdUI->SetCheck(!Group_rotate);
2400 void CFREDView::OnSelectList()
2407 // position camera to view all objects on the screen at once. Doesn't change orientation.
2408 void view_universe(int just_marked)
2410 int i, max = 0, flags[MAX_OBJECTS];
2411 float dist, largest = 20.0f;
2412 vector center, p1, p2; // center of all the objects collectively
2416 for (i=0; i<MAX_OBJECTS; i++)
2420 ptr = &Objects[cur_object_index];
2422 ptr = GET_FIRST(&obj_used_list);
2424 p1.x = p2.x = ptr->pos.x;
2425 p1.y = p2.y = ptr->pos.y;
2426 p1.z = p2.z = ptr->pos.z;
2428 ptr = GET_FIRST(&obj_used_list);
2429 while (ptr != END_OF_LIST(&obj_used_list)) {
2430 if (!just_marked || (ptr->flags & OF_MARKED)) {
2432 if (center.x < p1.x)
2434 if (center.x > p2.x)
2436 if (center.y < p1.y)
2438 if (center.y > p2.y)
2440 if (center.z < p1.z)
2442 if (center.z > p2.z)
2446 ptr = GET_NEXT(ptr);
2449 vm_vec_avg(¢er, &p1, &p2);
2450 ptr = GET_FIRST(&obj_used_list);
2451 while (ptr != END_OF_LIST(&obj_used_list)) {
2452 if (!just_marked || (ptr->flags & OF_MARKED)) {
2453 dist = vm_vec_dist_squared(¢er, &ptr->pos);
2457 flags[OBJ_INDEX(ptr)] = 1; // flag object as needing on-screen check
2458 if (OBJ_INDEX(ptr) > max)
2459 max = OBJ_INDEX(ptr);
2462 ptr = GET_NEXT(ptr);
2465 dist = fl_sqrt(largest) + 1.0f;
2466 vm_vec_scale_add(&view_pos, ¢er, &view_orient.fvec, -dist);
2467 g3_set_view_matrix(&view_pos, &view_orient, 0.5f);
2469 ptr = GET_FIRST(&obj_used_list);
2470 while (ptr != END_OF_LIST(&obj_used_list)) {
2471 if (!just_marked || (ptr->flags & OF_MARKED)) {
2472 g3_rotate_vertex(&v, &ptr->pos);
2473 Assert(!(v.codes & CC_BEHIND));
2474 if (g3_project_vertex(&v) & PF_OVERFLOW)
2477 while (v.codes & CC_OFF) { // is point off screen?
2478 dist += 5.0f; // zoom out a little and check again.
2479 vm_vec_scale_add(&view_pos, ¢er, &view_orient.fvec, -dist);
2480 g3_set_view_matrix(&view_pos, &view_orient, 0.5f);
2481 g3_rotate_vertex(&v, &ptr->pos);
2482 if (g3_project_vertex(&v) & PF_OVERFLOW)
2487 ptr = GET_NEXT(ptr);
2491 vm_vec_scale_add(&view_pos, ¢er, &view_orient.fvec, -dist);
2492 g3_set_view_matrix(&view_pos, &view_orient, 0.5f);
2496 void CFREDView::cycle_constraint()
2498 if (Single_axis_constraint) {
2501 else if (Constraint.y)
2503 else if (Constraint.z)
2509 else if (!Constraint.y)
2511 else if (!Constraint.z)
2516 void CFREDView::OnZoomExtents()
2521 void CFREDView::OnZoomSelected()
2523 if (query_valid_object()) {
2527 vm_vec_scale_add(&view_pos, &Objects[cur_object_index].pos, &view_orient.fvec, Objects[cur_object_index].radius * -3.0f);
2533 void CFREDView::OnUpdateZoomSelected(CCmdUI* pCmdUI)
2535 pCmdUI->Enable(query_valid_object());
2538 void CFREDView::OnFormWing()
2541 FREDDoc_ptr->autosave("form wing");
2544 void CFREDView::OnUpdateFormWing(CCmdUI* pCmdUI)
2549 if (query_valid_object()) {
2550 ptr = GET_FIRST(&obj_used_list);
2551 while (ptr != END_OF_LIST(&obj_used_list)) {
2552 if (ptr->flags & OF_MARKED) {
2553 if (ptr->type == OBJ_SHIP)
2554 switch (ship_query_general_type(ptr->instance))
2556 case SHIP_TYPE_FIGHTER_BOMBER:
2557 case SHIP_TYPE_CRUISER:
2558 case SHIP_TYPE_AWACS:
2559 case SHIP_TYPE_GAS_MINER:
2560 case SHIP_TYPE_CORVETTE:
2561 case SHIP_TYPE_FREIGHTER:
2562 case SHIP_TYPE_CAPITAL:
2563 case SHIP_TYPE_TRANSPORT:
2567 if (ptr->type == OBJ_START)
2571 ptr = GET_NEXT(ptr);
2575 pCmdUI->Enable(count > 0);
2578 int query_single_wing_marked()
2582 if (!query_valid_object())
2588 i = Wings[cur_wing].wave_count;
2589 if (Marked != i) // does marked object count match number of ships in wing?
2593 obj = wing_objects[cur_wing][i];
2594 if ((Objects[obj].type != OBJ_SHIP) && (Objects[obj].type != OBJ_START))
2595 Error(LOCATION, "Invalid objects detected in wing \"%s\"", Wings[cur_wing].name);
2597 // if (Ships[Objects[obj].instance].wingnum != cur_wing)
2599 Assert(Ships[Objects[obj].instance].wingnum == cur_wing);
2600 if (!(Objects[obj].flags & OF_MARKED)) // ensure all ships in wing.are marked
2607 void CFREDView::OnDisbandWing()
2609 if (query_single_wing_marked()) {
2610 remove_wing(cur_wing);
2611 FREDDoc_ptr->autosave("wing disband");
2614 MessageBox("One and only one wing must be selected for this operation");
2617 void CFREDView::OnUpdateDisbandWing(CCmdUI* pCmdUI)
2619 pCmdUI->Enable(query_single_wing_marked());
2622 void CFREDView::OnShowHorizon()
2624 Show_horizon = !Show_horizon;
2625 theApp.write_ini_file();
2629 void CFREDView::OnUpdateShowHorizon(CCmdUI* pCmdUI)
2631 pCmdUI->SetCheck(Show_horizon);
2634 void CFREDView::OnEditorsWing()
2638 Assert(Wing_editor_dialog.GetSafeHwnd());
2639 if (!Show_sexp_help)
2640 adjust = -SEXP_HELP_BOX_SIZE;
2642 if (!theApp.init_window(&Wing_wnd_data, &Wing_editor_dialog, adjust))
2645 Wing_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0,
2646 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2647 Wing_editor_dialog.ShowWindow(SW_RESTORE);
2650 void CFREDView::OnEditorsPlayer()
2652 player_start_editor dlg;
2657 void CFREDView::OnEditorsOrient()
2664 void CFREDView::OnEditorsEvents()
2666 if (Message_editor_dlg) {
2667 MessageBox("You must close the message editor before opening the event editor");
2671 if (!Event_editor_dlg) {
2672 Event_editor_dlg = new event_editor;
2673 Event_editor_dlg->Create(event_editor::IDD);
2676 Event_editor_dlg->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2677 Event_editor_dlg->ShowWindow(SW_RESTORE);
2680 void CFREDView::OnUpdateEditorsOrient(CCmdUI* pCmdUI)
2682 pCmdUI->Enable(query_valid_object());
2685 void CFREDView::OnEditorsMessage()
2687 if (Event_editor_dlg) {
2688 MessageBox("You must close the event editor before opening the message editor");
2692 if (!Message_editor_dlg) {
2693 Message_editor_dlg = new CMessageEditorDlg;
2694 Message_editor_dlg->Create(CMessageEditorDlg::IDD);
2697 Message_editor_dlg->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2698 Message_editor_dlg->ShowWindow(SW_RESTORE);
2701 void CFREDView::OnEditorsStarfield()
2703 starfield_editor dlg;
2708 void CFREDView::place_background_bitmap(vector v)
2712 void CFREDView::OnEditorsBgBitmaps()
2714 if (!Bg_bitmap_dialog) {
2715 Bg_bitmap_dialog = new bg_bitmap_dlg;
2716 Bg_bitmap_dialog->create();
2719 Bg_bitmap_dialog->SetWindowPos(&wndTop, 0, 0, 0, 0,
2720 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2721 Bg_bitmap_dialog->ShowWindow(SW_RESTORE);
2724 void CFREDView::OnEditorsReinforcement()
2726 reinforcement_editor_dlg dlg;
2731 void CFREDView::OnErrorChecker()
2735 z = global_error_check();
2737 MessageBox("No errors were detected in this mission", "Woohoo!");
2739 for (z=0; z<obj_count; z++)
2746 int CFREDView::global_error_check()
2748 char buf[256], *str;
2749 int bs, i, j, n, s, t, z, ai, count, ship, wing, obj, team, point, multi;
2752 int starting_orders;
2755 if ( The_mission.game_type & MISSION_TYPE_MULTI )
2758 // if (!stricmp(The_mission.name, "Untitled"))
2759 // if (error("You haven't given this mission a title yet.\nThis is done from the Mission Specs Editor (Shift-N)."))
2762 // cycle though all the objects and verify every possible aspect of them
2764 ptr = GET_FIRST(&obj_used_list);
2765 while (ptr != END_OF_LIST(&obj_used_list)) {
2766 names[obj_count] = NULL;
2767 flags[obj_count] = 0;
2769 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
2770 if (i < 0 || i >= MAX_SHIPS){
2771 return internal_error("An object has an illegal ship index");
2774 z = Ships[i].ship_info_index;
2775 if ((z < 0) || (z >= Num_ship_types)){
2776 return internal_error("A ship has an illegal class");
2779 if (ptr->type == OBJ_START) {
2781 if (!(Ship_info[z].flags & SIF_PLAYER_SHIP)) {
2782 ptr->type = OBJ_SHIP;
2785 if (error("Invalid ship type for a player. Ship has been reset to non-player ship.")){
2790 for (n=count=0; n<MAX_PRIMARY_BANKS; n++){
2791 if (Ships[i].weapons.primary_bank_weapons[n] >= 0){
2797 if (error("Player \"%s\" has no primary weapons. Should have at least 1", Ships[i].ship_name)){
2802 for (n=count=0; n<MAX_SECONDARY_BANKS; n++){
2803 if (Ships[i].weapons.secondary_bank_weapons[n] >= 0){
2809 if (error("Player \"%s\" has no secondary weapons. Should have at least 1", Ships[i].ship_name)){
2815 if (Ships[i].objnum != OBJ_INDEX(ptr)){
2816 return internal_error("Object/ship references are corrupt");
2819 names[obj_count] = Ships[i].ship_name;
2820 wing = Ships[i].wingnum;
2821 if (wing >= 0) { // ship is part of a wing, so check this
2822 if (wing < 0 || wing >= MAX_WINGS){ // completely out of range?
2823 return internal_error("A ship has an illegal wing index");
2826 j = Wings[wing].wave_count;
2828 return internal_error("A ship is in a non-existant wing");
2831 if (j < 0 || j > MAX_SHIPS_PER_WING){
2832 return internal_error("Invalid number of ships in wing \"%s\"", Wings[z].name);
2836 if (wing_objects[wing][j] == OBJ_INDEX(ptr)){ // look for object in wing's table
2842 return internal_error("Ship/wing references are corrupt");
2846 if ( (Ships[i].flags & SF_KILL_BEFORE_MISSION) && (Ships[i].hotkey >= 0) ){
2847 if (error("Ship flagged as \"destroy before mission start\" has a hotkey assignment")){
2852 if ( (Ships[i].flags & SF_KILL_BEFORE_MISSION) && (ptr->type == OBJ_START) ){
2853 if (error("Player start flagged as \"destroy before mission start\"")){
2857 } else if (ptr->type == OBJ_WAYPOINT) {
2858 j = i / 65536; // waypoint path number
2859 z = i & 0xffff; // waypoint number in path
2860 if (j < 0 || j >= Num_waypoint_lists){
2861 return internal_error("Object references an illegal waypoint path number");
2864 if (z >= Waypoint_lists[j].count){
2865 return internal_error("Object references an illegal waypoint number in path");
2868 sprintf(buf, "%s:%d", Waypoint_lists[j].name, z + 1);
2869 names[obj_count] = new char[strlen(buf) + 1];
2870 strcpy(names[obj_count], buf);
2871 flags[obj_count] = 1;
2872 } else if (ptr->type == OBJ_POINT) {
2873 if (!Briefing_dialog){
2874 return internal_error("Briefing icon detected when not in briefing edit mode");
2876 } else if (ptr->type == OBJ_JUMP_NODE) {
2877 if (i < 0 || i >= Num_jump_nodes){
2878 return internal_error("Object has illegal jump node index");
2881 return internal_error("An unknown object type (%d) was detected", ptr->type);
2884 for (i=0; i<obj_count; i++){
2885 if (names[i] && names[obj_count]){
2886 if (!stricmp(names[i], names[obj_count])){
2887 return internal_error("Duplicate object names (%s)", names[i]);
2893 ptr = GET_NEXT(ptr);
2896 if (t != Player_starts){
2897 return internal_error("Total number of player ships is incorrect");
2900 if (obj_count != num_objects){
2901 return internal_error("num_objects is incorrect");
2905 for (i=0; i<MAX_SHIPS; i++) {
2906 if (Ships[i].objnum >= 0) { // is ship being used?
2908 if (!query_valid_object(Ships[i].objnum)){
2909 return internal_error("Ship uses an unused object");
2912 z = Objects[Ships[i].objnum].type;
2913 if ((z != OBJ_SHIP) && (z != OBJ_START)){
2914 return internal_error("Object should be a ship, but isn't");
2917 if (fred_check_sexp(Ships[i].arrival_cue, OPR_BOOL, "arrival cue of ship \"%s\"", Ships[i].ship_name)){
2921 if (fred_check_sexp(Ships[i].departure_cue, OPR_BOOL, "departure cue of ship \"%s\"", Ships[i].ship_name)){
2925 if (Ships[i].arrival_location != ARRIVE_AT_LOCATION) {
2926 if (Ships[i].arrival_anchor < 0){
2927 if (error("Ship \"%s\" requires a valid arrival target", Ships[i].ship_name)){
2933 if (Ships[i].departure_location != DEPART_AT_LOCATION) {
2934 if (Ships[i].departure_anchor < 0){
2935 if (error("Ship \"%s\" requires a valid departure target", Ships[i].ship_name)){
2941 ai = Ships[i].ai_index;
2942 if (ai < 0 || ai >= MAX_AI_INFO){
2943 return internal_error("AI index out of range for ship \"%s\"", Ships[i].ship_name);
2946 if (Ai_info[ai].shipnum != i){
2947 return internal_error("AI/ship references are corrupt");
2950 if ((str = error_check_initial_orders(Ai_info[ai].goals, i, -1))>0) {
2952 return internal_error("Initial orders error for ship \"%s\"\n\n%s", Ships[i].ship_name, str + 1);
2953 else if (*str == '!')
2955 else if (error("Initial orders error for ship \"%s\"\n\n%s", Ships[i].ship_name, str))
2959 obj = Ai_info[ai].dock_objnum;
2961 if (!query_valid_object(obj)){
2962 return internal_error("Ship \"%s\" initially docked with non-existant ship", Ships[i].ship_name);
2965 if (Objects[obj].type != OBJ_SHIP && Objects[obj].type != OBJ_START){
2966 return internal_error("Ship \"%s\" initially docked with non-ship object", Ships[i].ship_name);
2969 ship = get_ship_from_obj(obj);
2970 if (!ship_docking_valid(i, ship) && !ship_docking_valid(ship, i)){
2971 return internal_error("Docking illegal between \"%s\" and \"%s\" (initially docked)", Ships[i].ship_name, Ships[ship].ship_name);
2974 z = get_docking_list(Ships[i].modelnum);
2975 point = Ai_info[ai].dock_index;
2976 if (point < 0 || point >= z){
2977 internal_error("Invalid docker point (\"%s\" initially docked with \"%s\")", Ships[i].ship_name, Ships[ship].ship_name);
2980 z = get_docking_list(Ships[ship].modelnum);
2981 point = Ai_info[ai].dockee_index;
2982 if (point < 0 || point >= z){
2983 internal_error("Invalid dockee point (\"%s\" initially docked with \"%s\")", Ships[i].ship_name, Ships[ship].ship_name);
2989 if (count != ship_get_num_ships()){
2990 return internal_error("num_ships is incorrect");
2994 for (i=0; i<MAX_WINGS; i++) {
2996 j = Wings[i].wave_count;
2997 if (j) { // is wing being used?
2999 if (j < 0 || j > MAX_SHIPS_PER_WING){
3000 return internal_error("Invalid number of ships in wing \"%s\"", Wings[i].name);
3004 obj = wing_objects[i][j];
3005 if (obj < 0 || obj >= MAX_OBJECTS){
3006 return internal_error("Wing_objects has an illegal object index");
3009 if (!query_valid_object(obj)){
3010 return internal_error("Wing_objects references an unused object");
3013 // Now, at this point, we can assume several things. We have a valid object because
3014 // we passed query_valid_object(), and all valid objects were already checked above,
3015 // so this object has valid information, such as the instance.
3017 if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START)) {
3018 ship = Objects[obj].instance;
3019 sprintf(buf, "%s %d", Wings[i].name, j + 1);
3020 if (stricmp(buf, Ships[ship].ship_name)){
3021 return internal_error("Ship \"%s\" in wing should be called \"%s\"", Ships[ship].ship_name, buf);
3024 switch (ship_query_general_type(ship))
3026 case SHIP_TYPE_FIGHTER_BOMBER:
3027 case SHIP_TYPE_CRUISER:
3028 case SHIP_TYPE_AWACS:
3029 case SHIP_TYPE_GAS_MINER:
3030 case SHIP_TYPE_CORVETTE:
3031 case SHIP_TYPE_FREIGHTER:
3032 case SHIP_TYPE_CAPITAL:
3033 case SHIP_TYPE_TRANSPORT:
3034 case SHIP_TYPE_SUPERCAP:
3038 if (error("Ship \"%s\" is an illegal type to be in a wing", Ships[ship].ship_name)){
3043 return internal_error("Wing_objects of \"%s\" references an illegal object type", Wings[i].name);
3046 if (Ships[ship].wingnum != i){
3047 return internal_error("Wing/ship references are corrupt");
3050 if (ship != Wings[i].ship_index[j]){
3051 return internal_error("Ship/wing references are corrupt");
3055 team = Ships[ship].team;
3056 } else if (team != Ships[ship].team && team < 999){
3057 if (error("ship teams mixed within same wing (\"%s\")", Wings[i].name)){
3063 if ((Wings[i].special_ship < 0) || (Wings[i].special_ship >= Wings[i].wave_count)){
3064 return internal_error("Special ship out of range for \"%s\"", Wings[i].name);
3067 if (Wings[i].num_waves < 0){
3068 return internal_error("Number of waves for \"%s\" is negative", Wings[i].name);
3071 if ((Wings[i].threshold < 0) || (Wings[i].threshold >= Wings[i].wave_count)){
3072 return internal_error("Threshold for \"%s\" is invalid", Wings[i].name);
3075 if (Wings[i].threshold + Wings[i].wave_count > MAX_SHIPS_PER_WING) {
3076 Wings[i].threshold = MAX_SHIPS_PER_WING - Wings[i].wave_count;
3077 if(error("Threshold for wing \"%s\" is higher than allowed. Reset to %d", Wings[i].name, Wings[i].threshold)){
3082 for (j=0; j<obj_count; j++){
3084 if (!stricmp(names[j], Wings[i].name)){
3085 return internal_error("Wing name is also used by an object (%s)", names[j]);
3090 if(fred_check_sexp(Wings[i].arrival_cue, OPR_BOOL, "arrival cue of wing \"%s\"", Wings[i].name)){
3094 if(fred_check_sexp(Wings[i].departure_cue, OPR_BOOL, "departure cue of wing \"%s\"", Wings[i].name)){
3098 if (Wings[i].arrival_location != ARRIVE_AT_LOCATION) {
3099 if (Wings[i].arrival_anchor < 0)
3100 if (error("Wing \"%s\" requires a valid arrival target", Wings[i].name))
3104 if (Wings[i].departure_location != DEPART_AT_LOCATION) {
3105 if (Wings[i].departure_anchor < 0)
3106 if (error("Wing \"%s\" requires a valid departure target", Wings[i].name))
3110 if ((str = error_check_initial_orders(Wings[i].ai_goals, -1, i))>0) {
3112 return internal_error("Initial orders error for wing \"%s\"\n\n%s", Wings[i].name, str + 1);
3113 else if (*str == '!')
3115 else if (error("Initial orders error for wing \"%s\"\n\n%s", Wings[i].name, str))
3122 if (count != num_wings){
3123 return internal_error("num_wings is incorrect");
3126 for (i=0; i<Num_waypoint_lists; i++) {
3127 for (z=0; z<obj_count; z++){
3129 if (!stricmp(names[z], Waypoint_lists[i].name)){
3130 return internal_error("Waypoint path name is also used by an object (%s)", names[z]);
3135 j = Waypoint_lists[i].count;
3137 sprintf(buf, "%s:%d", Waypoint_lists[i].name, j + 1);
3138 for (z=0; z<obj_count; z++){
3140 if (!stricmp(names[z], buf)){
3146 if (z == obj_count){
3147 return internal_error("Waypoint \"%s\" not linked to an object", buf);
3152 if (Player_starts > MAX_PLAYERS){
3153 return internal_error("Number of player starts exceeds max limit");
3156 if (!multi && (Player_starts > 1)){
3157 if (error("Multiple player starts exist, but this is a single player mission")){
3162 if (Num_reinforcements > MAX_REINFORCEMENTS){
3163 return internal_error("Number of reinforcements exceeds max limit");
3166 for (i=0; i<Num_reinforcements; i++) {
3168 for (ship=0; ship<MAX_SHIPS; ship++){
3169 if ((Ships[ship].objnum >= 0) && !stricmp(Ships[ship].ship_name, Reinforcements[i].name)) {
3175 for (wing=0; wing<MAX_WINGS; wing++){
3176 if (Wings[wing].wave_count && !stricmp(Wings[wing].name, Reinforcements[i].name)) {
3183 return internal_error("Reinforcement name not found in ships or wings");
3187 /* for (i=0; i<num_messages; i++) {
3188 if (Messages[i].num_times < 0)
3189 return internal_error("Number of times to play message is negative");
3191 z = Messages[i].who_from;
3192 if (z < -1 || z >= MAX_SHIPS) // hacked! -1 should be illegal..
3193 return internal_error("Message originator index is out of range");
3195 if (Ships[z].objnum == -1)
3196 return internal_error("Message originator points to nonexistant ship");
3198 if (fred_check_sexp(Messages[i].sexp, OPR_BOOL,
3199 "Message formula from \"%s\"", Ships[Messages[i].who_from].ship_name))
3203 Assert((Player_start_shipnum >= 0) && (Player_start_shipnum < MAX_SHIPS) && (Ships[Player_start_shipnum].objnum >= 0));
3204 i = global_error_check_player_wings(multi);
3209 for (i=0; i<Num_mission_events; i++){
3210 if (fred_check_sexp(Mission_events[i].formula, OPR_NULL, "mission event \"%s\"", Mission_events[i].name)){
3215 for (i=0; i<Num_goals; i++){
3216 if (fred_check_sexp(Mission_goals[i].formula, OPR_BOOL, "mission goal \"%s\"", Mission_goals[i].name)){
3221 for ( bs = 0; bs < Num_teams; bs++ ) {
3222 for (s=0; s<Briefings[bs].num_stages; s++) {
3223 sp = &Briefings[bs].stages[s];
3225 for (i=0; i<t-1; i++){
3226 for (j=i+1; j<t; j++) {
3227 if ((sp->icons[i].id > 0) && (sp->icons[i].id == sp->icons[j].id)){
3228 if (error("Duplicate icon IDs %d in briefing stage %d", sp->icons[i].id, s + 1)){
3237 for ( j = 0; j < Num_teams; j++ ) {
3238 for (i=0; i<Debriefings[j].num_stages; i++) {
3239 if (fred_check_sexp(Debriefings[j].stages[i].formula, OPR_BOOL, "debriefing stage %d", i + 1)){
3245 // for all wings, be sure that the orders accepted for all ships are the same for all ships
3247 starting_orders = -1;
3248 for (i=0; i<MAX_WINGS; i++) {
3249 int default_orders, starting_wing;
3251 if ( !Wings[i].wave_count ){
3255 // determine if this wing is a starting wing of the player
3257 for ( j = 0; j < MAX_STARTING_WINGS; j++ ) {
3258 if ( !stricmp( Wings[i].name, Starting_wing_names[j]) ){
3262 if ( j == MAX_STARTING_WINGS ){
3266 // first, be sure this isn't a reinforcement wing.
3267 if ( starting_wing && (Wings[i].flags & WF_REINFORCEMENT) ) {
3268 if ( error("Starting Wing %s marked as reinforcement. This wing\nshould either be renamed, or unmarked as reinforcement.", Wings[i].name) ){
3274 for ( j = 0; j < Wings[i].wave_count; j++ ) {
3277 orders = Ships[Wings[i].ship_index[j]].orders_accepted;
3279 default_orders = orders;
3280 } else if ( default_orders != orders ) {
3281 if (error("Wing %s has ships with different player orders which\nare ignored. They must all be the same", Wings[i].name ) ){
3287 // make sure that these ignored orders are the same for all starting wings of the player
3288 if ( starting_wing ) {
3289 if ( starting_orders == -1 ) {
3290 starting_orders = default_orders;
3292 if ( starting_orders != default_orders ) {
3293 if ( error("Player starting wing %s has orders which don't match other starting wings\n", Wings[i].name) ){
3301 if (Num_jump_nodes < 0 || Num_jump_nodes > MAX_JUMP_NODES){
3302 return internal_error("Jump node count is illegal");
3305 fred_check_message_personas();
3310 int CFREDView::global_error_check_mixed_player_wing(int w)
3312 int i, s, species = -1, mixed = 0;
3314 for (i=0; i<Wings[w].wave_count; i++) {
3315 s = Wings[w].ship_index[i];
3317 species = Ship_info[Ships[s].ship_info_index].species;
3318 else if (Ship_info[Ships[s].ship_info_index].species != species)
3323 if (error("%s wing must all be of the same species", Wings[w].name))
3329 int CFREDView::global_error_check_player_wings(int multi)
3331 int i, z, alpha, beta, gamma, zeta, err, alpha_count, zeta_count;
3334 z = Ships[Player_start_shipnum].wingnum;
3335 alpha = wing_name_lookup("alpha", 1);
3336 beta = wing_name_lookup("beta", 1);
3337 gamma = wing_name_lookup("gamma", 1);
3338 zeta = wing_name_lookup( "zeta", 1 );
3341 free_sexp2(Wings[alpha].arrival_cue);
3342 Wings[alpha].arrival_cue = Locked_sexp_true;
3345 if (multi && (alpha < 0)){
3346 if (error("Alpha wing is required for multiplayer missions")){
3351 // Check to be sure that any player starting wing doesn't have > 1 wave for multiplayer
3353 if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ) {
3354 if ( alpha && (Wings[alpha].num_waves > 1) ) {
3355 error("Alpha wing must contain only 1 wave.\nThis change has been made for you.");
3356 Wings[alpha].num_waves = 1;
3359 if ( zeta && (Wings[zeta].num_waves > 1) ) {
3360 error("Zeta wing must contain only 1 wave.\nThis change has been made for you.");
3361 Wings[zeta].num_waves = 1;
3366 if ( alpha && (Wings[alpha].num_waves > 1) ) {
3367 error("Alpha wing must contain only 1 wave.\nThis change has been made for you.");
3368 Wings[alpha].num_waves = 1;
3371 if ( beta && (Wings[beta].num_waves > 1) ) {
3372 error("Beta wing must contain only 1 wave.\nThis change has been made for you.");
3373 Wings[beta].num_waves = 1;
3376 if ( gamma && (Wings[gamma].num_waves > 1) ) {
3377 error("Gamma wing must contain only 1 wave.\nThis change has been made for you.");
3378 Wings[gamma].num_waves = 1;
3384 // if not a multiplayer misison, or a coop multiplayer mission, then do "normal"
3385 // wing name checking.
3386 if ( !multi || (The_mission.game_type & MISSION_TYPE_MULTI_COOP) ) {
3387 if (((alpha >= 0) || (z >= 0)) && (alpha != z)){
3388 if (error("Player start is not in Alpha wing")){
3393 if ((beta >= 0) && (alpha < 0)){
3394 if (error("Alpha wing required, but not present")){
3399 if ((gamma >= 0) && (beta < 0)){
3400 if (error("Beta wing required, but not present")){
3405 if ((alpha >= 0) && (Wings[alpha].wave_count > 4)){
3406 if (error("Alpha wing has too many ships. Should only have 4 max.")){
3411 if ((beta >= 0) && (Wings[beta].wave_count > 4)){
3412 if (error("Beta wing has too many ships. Should only have 4 max.")){
3417 if ((gamma >= 0) && (Wings[gamma].wave_count > 4)){
3418 if (error("Gamma wing has too many ships. Should only have 4 max.")){
3423 if ((alpha >= 0) && Wings[alpha].arrival_delay){
3424 if (error("Alpha wing shouldn't have a non-zero arrival delay")){
3428 } else if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ) {
3430 if (error("Zeta wing is required for multiplayer team vs. team missions")){
3435 if ( Wings[alpha].wave_count > 4 ){
3436 if (error("Alpha wing has too many ships. Should only have 4 max.")){
3441 if ( Wings[zeta].wave_count > 4 ) {
3442 if (error("Zeta wing has too many ships. Should only have 4 max.")){
3447 if ((alpha >= 0) && Wings[alpha].arrival_delay){
3448 if (error("Alpha wing shouldn't have a non-zero arrival delay")){
3453 if ((zeta >= 0) && Wings[zeta].arrival_delay){
3454 if (error("Zeta wing shouldn't have a non-zero arrival delay")){
3458 } else if ( The_mission.game_type & MISSION_TYPE_MULTI_DOGFIGHT ) {
3459 if ( Wings[alpha].wave_count > 4 ) {
3460 if (error("Alpha wing has too many ships. Should only have 4 max.")){
3465 if ((alpha >= 0) && Wings[alpha].arrival_delay){
3466 if (error("Alpha wing shouldn't have a non-zero arrival delay")){
3471 error("Unknown game type: %d", The_mission.game_type);
3477 if (global_error_check_mixed_player_wing(alpha)){
3483 if (global_error_check_mixed_player_wing(beta)){
3489 if (global_error_check_mixed_player_wing(gamma)){
3495 if (global_error_check_mixed_player_wing(zeta)){
3501 alpha_count = zeta_count = 0;
3502 ptr = GET_FIRST(&obj_used_list);
3503 while (ptr != END_OF_LIST(&obj_used_list)) {
3506 if (ptr->type == OBJ_START) {
3507 z = Ships[i].wingnum;
3513 } else if (z == zeta){
3517 if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) {
3518 if ((z != alpha) && (z != zeta)){
3522 if ((z != alpha) && (z != beta) && (z != gamma)){
3529 if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) {
3530 if (error("Player \"%s\" should be part Alpha or Zeta wing", Ships[i].ship_name)){
3534 if (error("Player \"%s\" should be part Alpha, Beta or Gamma wing", Ships[i].ship_name)){
3541 ptr = GET_NEXT(ptr);
3544 if ((alpha >= 0) && !alpha_count){
3545 if (error("Alpha wing doesn't contain any players, which it should.")){
3550 if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS){
3551 if ((zeta >= 0) && !zeta_count){
3552 if (error("Zeta wing doesn't contain any players, which it should.")){
3561 int CFREDView::error(char *msg, ...)
3566 va_start(args, msg);
3567 vsprintf(buf, msg, args);
3571 if (MessageBox(buf, "Error", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDOK)
3577 int CFREDView::internal_error(char *msg, ...)
3582 va_start(args, msg);
3583 vsprintf(buf, msg, args);
3591 sprintf(buf2, "%s\n\nThis is an internal error. Please let Jason\n"
3592 "know about this so he can fix it. Click cancel to debug.", buf);
3594 if (MessageBox(buf2, "Internal Error", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
3595 Int3(); // drop to debugger so the problem can be analyzed.
3598 MessageBox(buf, "Error", MB_OK | MB_ICONEXCLAMATION);
3604 int CFREDView::fred_check_sexp(int sexp, int type, char *msg, ...)
3606 char buf[512], buf2[2048], buf3[4096];
3607 int err = 0, z, faulty_node;
3610 va_start(args, msg);
3611 vsprintf(buf, msg, args);
3617 z = check_sexp_syntax(sexp, type, 1, &faulty_node);
3621 convert_sexp_to_string(sexp, buf2, SEXP_ERROR_CHECK_MODE);
3622 sprintf(buf3, "Error in %s: %s\n\nIn sexpression: %s\n(Error appears to be: %s)",
3623 buf, sexp_error_message(z), buf2, Sexp_nodes[faulty_node].text);
3625 if (z < 0 && z > -100)
3629 return internal_error(buf3);
3637 void CFREDView::OnEditorsWaypoint()
3641 Assert(Waypoint_editor_dialog.GetSafeHwnd());
3642 if (!Show_sexp_help)
3643 adjust = -SEXP_HELP_BOX_SIZE;
3645 if (!theApp.init_window(&Waypoint_wnd_data, &Waypoint_editor_dialog))
3648 Waypoint_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0,
3649 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
3650 Waypoint_editor_dialog.ShowWindow(SW_RESTORE);
3653 char *error_check_initial_orders(ai_goal *goals, int ship, int wing)
3656 int i, j, num, flag, found, inst, team, team2;
3660 source = Ships[ship].ship_name;
3661 team = Ships[ship].team;
3662 for (i=0; i<MAX_AI_GOALS; i++)
3663 if (!ai_query_goal_valid(ship, goals[i].ai_mode)) {
3664 if (Fred_view_wnd->error("Order \"%s\" isn't allowed for ship \"%s\"", get_order_name(goals[i].ai_mode), source))
3670 Assert(Wings[wing].wave_count > 0);
3671 source = Wings[wing].name;
3672 team = Ships[Objects[wing_objects[wing][0]].instance].team;
3673 for (j=0; j<Wings[wing].wave_count; j++)
3674 for (i=0; i<MAX_AI_GOALS; i++)
3675 if (!ai_query_goal_valid(Wings[wing].ship_index[j], goals[i].ai_mode)) {
3676 if (Fred_view_wnd->error("Order \"%s\" isn't allowed for ship \"%s\"", get_order_name(goals[i].ai_mode),
3677 Ships[Wings[wing].ship_index[j]].ship_name))
3682 for (i=0; i<MAX_AI_GOALS; i++) {
3683 switch (goals[i].ai_mode) {
3685 case AI_GOAL_CHASE_ANY:
3686 case AI_GOAL_UNDOCK:
3687 case AI_GOAL_KEEP_SAFE_DISTANCE:
3688 case AI_GOAL_PLAY_DEAD:
3693 case AI_GOAL_WAYPOINTS:
3694 case AI_GOAL_WAYPOINTS_ONCE:
3700 return "Wings can't dock";
3703 case AI_GOAL_DESTROY_SUBSYSTEM:
3706 case AI_GOAL_DISARM_SHIP:
3707 case AI_GOAL_DISABLE_SHIP:
3708 case AI_GOAL_EVADE_SHIP:
3709 case AI_GOAL_STAY_NEAR_SHIP:
3710 case AI_GOAL_IGNORE:
3714 case AI_GOAL_CHASE_WING:
3715 case AI_GOAL_GUARD_WING:
3719 case AI_GOAL_STAY_STILL:
3724 return "*Invalid goal type";
3729 if (*goals[i].ship_name == '<')
3730 return "Invalid target";
3732 if (!stricmp(goals[i].ship_name, source))
3734 return "Target of ship's goal is itself";
3736 return "Target of wing's goal is itself";
3740 if (flag == 1) { // target waypoint required
3741 for (j=0; j<Num_waypoint_lists; j++)
3742 if (!stricmp(goals[i].ship_name, Waypoint_lists[j].name))
3746 return "*Invalid target waypoint path name";
3748 } else if (flag == 2) { // target ship required
3749 ptr = GET_FIRST(&obj_used_list);
3750 while (ptr != END_OF_LIST(&obj_used_list)) {
3751 if (ptr->type == OBJ_SHIP || ptr->type == OBJ_START) {
3752 inst = ptr->instance;
3753 if (!stricmp(goals[i].ship_name, Ships[inst].ship_name)) {
3759 ptr = GET_NEXT(ptr);
3763 return "*Invalid target ship name";
3765 if (wing >= 0) { // check if target ship is in wing
3766 if (Ships[inst].wingnum == wing && Objects[Ships[inst].objnum].type != OBJ_START)
3767 return "Target ship of wing's goal is within said wing";
3770 team2 = Ships[inst].team;
3772 } else if (flag == 3) { // target wing required
3773 for (j=0; j<MAX_WINGS; j++)
3774 if (Wings[j].wave_count && !stricmp(Wings[j].name, goals[i].ship_name))
3778 return "*Invalid target wing name";
3780 if (ship >= 0) { // check if ship is in target wing
3781 if (Ships[ship].wingnum == j)
3782 return "Target wing of ship's goal is same wing said ship is part of";
3785 team2 = Ships[Objects[wing_objects[j][0]].instance].team;
3787 } else if (flag == 4) {
3788 ptr = GET_FIRST(&obj_used_list);
3789 while (ptr != END_OF_LIST(&obj_used_list)) {
3790 if (ptr->type == OBJ_SHIP || ptr->type == OBJ_START) {
3791 inst = ptr->instance;
3792 if (!stricmp(goals[i].ship_name, Ships[inst].ship_name)) {
3797 } else if (ptr->type == OBJ_WAYPOINT) {
3798 if (!stricmp(goals[i].ship_name, object_name(OBJ_INDEX(ptr)))) {
3804 ptr = GET_NEXT(ptr);
3808 return "*Invalid target ship or waypoint name";
3811 if (wing >= 0) { // check if target ship is in wing
3812 if (Ships[inst].wingnum == wing && Objects[Ships[inst].objnum].type != OBJ_START)
3813 return "Target ship of wing's goal is within said wing";
3816 team2 = Ships[inst].team;
3820 switch (goals[i].ai_mode) {
3821 case AI_GOAL_DESTROY_SUBSYSTEM:
3822 Assert(flag == 2 && inst >= 0);
3823 if (ship_get_subsys_index(&Ships[inst], goals[i].docker.name, 1) < 0)
3824 return "Unknown subsystem type";
3828 case AI_GOAL_DOCK: {
3829 int dock1 = -1, dock2 = -1, model1, model2;
3831 Assert(flag == 2 && inst >= 0);
3832 if (!ship_docking_valid(ship, inst))
3833 return "Docking illegal between given ship types";
3835 model1 = Ships[ship].modelnum;
3836 num = get_docking_list(model1);
3837 for (j=0; j<num; j++) {
3838 Assert(Docking_bay_list[j]);
3839 if (!stricmp(goals[i].docker.name, Docking_bay_list[j])) {
3845 model2 = Ships[inst].modelnum;
3846 num = get_docking_list(model2);
3847 for (j=0; j<num; j++) {
3848 Assert(Docking_bay_list[j]);
3849 if (!stricmp(goals[i].dockee.name, Docking_bay_list[j])) {
3856 return "Invalid docker point";
3859 return "Invalid dockee point";
3861 if ((dock1 >= 0) && (dock2 >= 0)) {
3862 if ( !(model_get_dock_index_type(model1, dock1) & model_get_dock_index_type(model2, dock2)) )
3863 return "Dock points are incompatible";
3870 switch (goals[i].ai_mode) {
3872 case AI_GOAL_GUARD_WING:
3873 // if ((team == TEAM_HOSTILE && team2 == TEAM_FRIENDLY) || (team == TEAM_FRIENDLY && team2 == TEAM_HOSTILE)) {
3874 if (team != team2) { // MK, added support for TEAM_NEUTRAL. Won't this work?
3876 return "Ship assigned to guard opposite team";
3878 return "Wing assigned to guard opposite team";
3884 case AI_GOAL_CHASE_WING:
3885 case AI_GOAL_DESTROY_SUBSYSTEM:
3886 case AI_GOAL_DISARM_SHIP:
3887 case AI_GOAL_DISABLE_SHIP:
3888 if (team == team2) {
3890 return "Ship assigned to attack same team";
3892 return "Wings assigned to attack same team";
3902 // function (which is called externally from message editor and event editor) so skip through
3903 // the sexpression nodes to look for send-message commands to try to associate message personas
3905 void fred_check_message_personas()
3908 int i, op, j, ship_index;
3909 char *mname, *who_from;
3912 // this function is responsible for assigning personas to ships as well as error checking them.
3913 // clear out the persona index on all ship objects
3914 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
3915 if ( objp->type == OBJ_SHIP ) {
3916 Ships[objp->instance].persona_index = -1;
3921 for (i = 0; i < MAX_SEXP_NODES; i++ ) {
3922 if ( Sexp_nodes[i].type == SEXP_NOT_USED )
3925 // look for only operator nodes
3926 if ( Sexp_nodes[i].subtype != SEXP_ATOM_OPERATOR )
3929 // now look for the send-message opeator
3930 op = find_operator( Sexp_nodes[i].text );
3931 if ( op != OP_SEND_MESSAGE )
3934 // have the message. parse through the message to determine who is sending the message.
3935 who_from = CTEXT(CDR(i));
3937 // we can ignore messages from any wingman, and allied, or from God.
3938 if ( !stricmp(who_from, "<Any wingman>") || !stricmp(who_from, "<Any allied>") || (who_from[0] == '#') )
3941 mname = CTEXT(CDR(CDR(CDR(i))));
3943 // check to see if who_from is a wing. Don't do processing if so.
3944 if ( wing_name_lookup(who_from, 1) != -1 )
3947 ship_index = ship_name_lookup( who_from );
3948 if ( ship_index == -1 ) {
3949 Int3(); // get allender. something funny is up with shipnames in send-message
3953 for ( j = Num_builtin_messages; j < Num_messages; j++ ) {
3954 if ( !stricmp(mname, Messages[j].name) ) {
3956 // check to see if there is a persona for this message -- if not, bail
3957 if ( Messages[j].persona_index == -1 )
3960 // if a ship isn't assigned a persona, and this message says that he is, assign it, and move on
3961 if ( Ships[ship_index].persona_index == -1 ) {
3962 Ships[ship_index].persona_index = Messages[j].persona_index;
3966 // we must be sure of the following conditions:
3967 // 1) a ship isn't assigned > 1 persona
3969 if ( Ships[ship_index].persona_index != Messages[j].persona_index )
3970 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 );
3975 // check that two or more ships are not using the same persona
3976 for (i = 0; i < Num_personas; i++ ) {
3980 // move through object list looking for number of shis using this persona
3982 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
3983 if ( objp->type != OBJ_SHIP )
3985 if (Ships[objp->instance].persona_index == i )
3989 if ( persona_count > 1 )
3990 Fred_view_wnd->error("Persona %s used by more than 1 ship", Personas[Messages[j].persona_index].name );
3996 void CFREDView::OnViewOutlines()
3998 Show_outlines = !Show_outlines;
3999 theApp.write_ini_file();
4003 void CFREDView::OnUpdateViewOutlines(CCmdUI* pCmdUI)
4005 pCmdUI->SetCheck(Show_outlines);
4008 void CFREDView::OnUpdateNewShipType(CCmdUI* pCmdUI)
4013 z = m_new_ship_type_combo_box.GetCurSelNEW();
4015 m_new_ship_type_combo_box.SetCurSelNEW(cur_model_index);
4017 cur_model_index = z;
4019 bar = GetDlgItem(pCmdUI->m_nID);
4021 pCmdUI -> ContinueRouting();
4022 return; // not for us
4025 pCmdUI -> SetCheck((bar->GetStyle() & WS_VISIBLE) != 0);
4028 void CFREDView::OnShowStarfield()
4030 Show_stars = !Show_stars;
4031 theApp.write_ini_file();
4035 void CFREDView::OnUpdateShowStarfield(CCmdUI* pCmdUI)
4037 pCmdUI->SetCheck(Show_stars);
4040 void CFREDView::OnAsteroidEditor()
4042 asteroid_editor dlg;
4047 void CFREDView::OnRunFreespace()
4051 PROCESS_INFORMATION pi;
4054 if (!FREDDoc_ptr->SaveModified())
4058 si.lpReserved = NULL;
4059 si.lpDesktop = NULL;
4063 si.lpReserved2 = NULL;
4065 r = CreateProcess("..\\..\\Fs.exe", NULL, NULL, NULL, FALSE, 0, NULL, "..\\..", &si, &pi);
4068 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
4071 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
4077 // Display the string.
4078 MessageBox(lpMsgBuf);
4081 LocalFree( lpMsgBuf );
4085 void CFREDView::OnEditorCampaign()
4087 if (!FREDDoc_ptr->SaveModified())
4090 Assert(!Campaign_wnd);
4091 Campaign_wnd = new campaign_tree_wnd;
4092 if (Campaign_wnd->Create(NULL, "Campaign Editor", WS_OVERLAPPEDWINDOW | WS_MAXIMIZE,
4093 CFrameWnd::rectDefault, NULL, "IDR_MENU_CAMPAIGN")) {
4094 Campaign_wnd->ShowWindow(SW_SHOW);
4095 Campaign_wnd->UpdateWindow();
4099 void CFREDView::OnShowShips()
4101 Show_ships = !Show_ships;
4106 void CFREDView::OnUpdateShowShips(CCmdUI* pCmdUI)
4108 pCmdUI->SetCheck(Show_ships);
4111 void CFREDView::OnShowStarts()
4113 Show_starts = !Show_starts;
4118 void CFREDView::OnUpdateShowStarts(CCmdUI* pCmdUI)
4120 pCmdUI->SetCheck(Show_starts);
4123 void CFREDView::OnShowFriendly()
4125 Show_friendly = !Show_friendly;
4130 void CFREDView::OnUpdateShowFriendly(CCmdUI* pCmdUI)
4132 pCmdUI->SetCheck(Show_friendly);
4135 void CFREDView::OnShowHostile()
4137 Show_hostile = !Show_hostile;
4142 void CFREDView::OnUpdateShowHostile(CCmdUI* pCmdUI)
4144 pCmdUI->SetCheck(Show_hostile);
4147 void CFREDView::OnToggleViewpoint()
4149 if (viewpoint || !query_valid_object())
4154 view_obj = cur_object_index;
4160 void CFREDView::OnRevert()
4162 if (!FREDDoc_ptr->SaveModified())
4165 FREDDoc_ptr->DeleteContents();
4166 FREDDoc_ptr->OnOpenDocument(NULL);
4169 void CFREDView::OnUpdateRevert(CCmdUI* pCmdUI)
4171 pCmdUI->Enable(*Mission_filename);
4174 BOOL CFREDView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
4176 if ((Cursor_over >= 0) || Selection_lock) {
4177 if (Editing_mode == 1) {
4178 SetCursor(h_cursor_move);
4181 } else if (Editing_mode == 2) {
4182 SetCursor(h_cursor_rotate);
4187 return CView::OnSetCursor(pWnd, nHitTest, message);
4190 void CFREDView::OnHideObjects()
4194 ptr = GET_FIRST(&obj_used_list);
4195 while (ptr != END_OF_LIST(&obj_used_list)) {
4196 if (ptr->flags & OF_MARKED) {
4197 ptr->flags |= OF_HIDDEN;
4198 unmark_object(OBJ_INDEX(ptr));
4201 ptr = GET_NEXT(ptr);
4205 void CFREDView::OnShowHiddenObjects()
4209 ptr = GET_FIRST(&obj_used_list);
4210 while (ptr != END_OF_LIST(&obj_used_list)) {
4211 ptr->flags &= ~OF_HIDDEN;
4212 ptr = GET_NEXT(ptr);
4218 void CFREDView::OnEditUndo()
4221 matrix viewer_orient;
4223 if (Undo_available) {
4224 viewer_pos = view_pos;
4225 viewer_orient = view_orient;
4226 FREDDoc_ptr->autoload();
4227 view_pos = viewer_pos;
4228 view_orient = viewer_orient;
4232 void CFREDView::OnUpdateEditUndo(CCmdUI* pCmdUI)
4234 pCmdUI->Enable(Undo_available);
4237 void CFREDView::OnEditorsBriefing()
4239 if (!Briefing_dialog) {
4240 Briefing_dialog = new briefing_editor_dlg;
4241 Briefing_dialog->create();
4244 Briefing_dialog->SetWindowPos(&wndTop, 0, 0, 0, 0,
4245 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
4246 Briefing_dialog->ShowWindow(SW_RESTORE);
4249 void CFREDView::OnEditorsDebriefing()
4251 debriefing_editor_dlg dlg;
4256 void CFREDView::OnSaveCamera()
4258 saved_cam_pos = view_pos;
4259 saved_cam_orient = view_orient;
4262 void CFREDView::OnRestoreCamera()
4264 view_pos = saved_cam_pos;
4265 view_orient = saved_cam_orient;
4269 void CFREDView::OnUpdateRestoreCamera(CCmdUI* pCmdUI)
4271 pCmdUI->Enable(!IS_VEC_NULL(&saved_cam_orient.fvec));
4274 void CFREDView::OnShowSexpHelp()
4278 Show_sexp_help = !Show_sexp_help;
4279 Ship_editor_dialog.show_hide_sexp_help();
4280 Wing_editor_dialog.show_hide_sexp_help();
4282 if (Event_editor_dlg) {
4283 Event_editor_dlg->GetWindowRect(rect);
4285 rect.bottom += SEXP_HELP_BOX_SIZE;
4287 rect.bottom -= SEXP_HELP_BOX_SIZE;
4289 Event_editor_dlg->MoveWindow(rect);
4293 void CFREDView::OnUpdateShowSexpHelp(CCmdUI* pCmdUI)
4295 pCmdUI->SetCheck(Show_sexp_help);
4298 void CFREDView::OnLookatObj()
4300 Lookat_mode = !Lookat_mode;
4301 if (Lookat_mode && query_valid_object()) {
4305 loc = Objects[cur_object_index].pos;
4306 vm_vec_sub(&v, &loc, &view_pos);
4308 if (v.x || v.y || v.z) {
4309 vm_vector_2_matrix(&m, &v, NULL, NULL);
4315 void CFREDView::OnUpdateLookatObj(CCmdUI* pCmdUI)
4317 pCmdUI->SetCheck(Lookat_mode);
4320 void CFREDView::OnGroup(UINT nID)
4322 int n = 1 << (nID - ID_GROUP1);
4326 objp = GET_FIRST(&obj_used_list);
4327 while (objp != END_OF_LIST(&obj_used_list)) {
4328 if (objp->type == OBJ_SHIP) {
4329 if (Ships[objp->instance].group & n)
4330 mark_object(OBJ_INDEX(objp));
4333 objp = GET_NEXT(objp);
4339 void CFREDView::OnSetGroup(UINT nID)
4341 int i, err = 0, n = 1 << (nID - ID_SET_GROUP1);
4344 for (i=0; i<MAX_SHIPS; i++)
4345 Ships[i].group &= ~n;
4347 objp = GET_FIRST(&obj_used_list);
4348 while (objp != END_OF_LIST(&obj_used_list)) {
4349 if (objp->flags & OF_MARKED) {
4350 if (objp->type == OBJ_SHIP) {
4351 Ships[objp->instance].group |= n;
4357 objp = GET_NEXT(objp);
4361 Fred_main_wnd->MessageBox("Only ships can be in groups, and not players or waypoints, etc.\n"
4362 "These illegal objects you marked were not placed in the group");
4367 void CFREDView::OnInitialUpdate()
4369 char *ptr, text[512];
4371 CView::OnInitialUpdate();
4373 // check the time/checksum strings.
4376 if ( memcmp(ptr, DEFAULT_CHECKSUM_STRING, strlen(DEFAULT_CHECKSUM_STRING)) ) {
4377 int stamped_checksum, checksum;
4379 // the checksum is not the default checksum. Calculate the checksum of the string
4381 memcpy(&stamped_checksum, ptr, sizeof(stamped_checksum) );
4383 ptr += 8; // get us to the actual string to calculate the checksum
4384 CALCULATE_STAMP_CHECKSUM();
4386 if ( checksum != stamped_checksum ){
4387 expire_game = EXPIRE_BAD_CHECKSUM;
4390 // now check the time
4393 if ( memcmp( ptr, DEFAULT_TIME_STRING, strlen(DEFAULT_TIME_STRING)) ) {
4394 int expire_time, current_time;
4396 // not the default time -- check against the current time
4397 memcpy( &expire_time, ptr, sizeof(expire_time) );
4398 time( (long *)¤t_time );
4399 if ( current_time > expire_time )
4400 expire_game = EXPIRE_BAD_TIME;
4403 // since the default checksum has changed -- put up a message which shows who the program
4407 sprintf(text, "This version of Fred has been compiled for %s", ptr);
4408 MessageBox(text, NULL, MB_OK);
4411 SetTimer(1, FRED_EXPIRE_TIME, expire_game_proc);
4415 void CFREDView::OnEditorsAdjustGrid()
4417 adjust_grid_dlg dlg;
4423 void CFREDView::OnEditorsShieldSys()
4430 void CFREDView::OnLevelObj()
4436 void CFREDView::OnAlignObj()
4438 verticalize_controlled();
4442 void CFREDView::OnControlObj()
4444 Control_mode = (Control_mode + 1) % 2;
4447 void CFREDView::OnNextObj()
4451 if (Bg_bitmap_dialog) {
4452 if (Cur_bitmap == -1)
4454 if (Num_starfield_bitmaps)
4457 Bg_bitmap_dialog -> update_data();
4464 if (Cur_bitmap >= Num_starfield_bitmaps)
4467 Bg_bitmap_dialog -> update_data();
4471 if (EMPTY(&obj_used_list))
4474 if (query_valid_object()) {
4475 ptr = Objects[cur_object_index].next;
4476 if (ptr == END_OF_LIST(&obj_used_list))
4477 ptr = GET_NEXT(ptr);
4480 ptr = GET_FIRST(&obj_used_list);
4482 if (Marked > 1) { // cycle through marked list
4483 while (!(ptr->flags & OF_MARKED))
4485 ptr = GET_NEXT(ptr);
4486 if (ptr == END_OF_LIST(&obj_used_list))
4487 ptr = GET_NEXT(ptr);
4490 set_cur_object_index(OBJ_INDEX(ptr));
4494 unmark_object(cur_object_index);
4496 mark_object(OBJ_INDEX(ptr));
4500 void CFREDView::OnPrevObj()
4502 int arr[MAX_OBJECTS], i = 0, n = 0;
4505 if (Bg_bitmap_dialog) {
4506 if (Cur_bitmap == -1)
4508 if (Num_starfield_bitmaps)
4510 Cur_bitmap = Num_starfield_bitmaps - 1;
4511 Bg_bitmap_dialog -> update_data();
4519 Cur_bitmap = Num_starfield_bitmaps - 1;
4521 Bg_bitmap_dialog -> update_data();
4525 if (EMPTY(&obj_used_list))
4528 ptr = GET_FIRST(&obj_used_list);
4529 while (ptr != END_OF_LIST(&obj_used_list)) {
4530 if (cur_object_index == OBJ_INDEX(ptr))
4533 arr[n++] = OBJ_INDEX(ptr);
4534 ptr = GET_NEXT(ptr);
4538 if (query_valid_object()) {
4546 if (Marked > 1) { // cycle through marked list
4547 while (!(Objects[i].flags & OF_MARKED))
4554 set_cur_object_index(i);
4558 unmark_object(cur_object_index);
4564 void CFREDView::OnEditDelete()
4566 if (!button_down && Marked) {
4568 FREDDoc_ptr->autosave("object delete");
4571 Update_window = 2; // For some strange reason, need to redraw twice for it to take.
4574 void CFREDView::OnEditDeleteWing()
4576 if (!button_down && (cur_wing >= 0)) {
4578 FREDDoc_ptr->autosave("wing delete");
4581 Ship_editor_dialog.initialize_data(1);
4583 Wing_editor_dialog.initialize_data(1);
4586 Update_window = 2; // For some strange reason, need to redraw twice for it to take.
4589 void CFREDView::OnMarkWing()
4591 int i, wing = cur_wing;
4596 for (i=0; i<Wings[wing].wave_count; i++)
4597 mark_object(wing_objects[wing][i]);
4599 Assert(Wings[wing].special_ship >= 0 && Wings[wing].special_ship < Wings[wing].wave_count);
4600 set_cur_object_index(wing_objects[wing][Wings[wing].special_ship]);
4604 void CFREDView::OnUpdateControlObj(CCmdUI* pCmdUI)
4606 pCmdUI->SetCheck(Control_mode != 0);
4609 void CFREDView::OnAaGridlines()
4611 Aa_gridlines = !Aa_gridlines;
4614 void CFREDView::OnUpdateAaGridlines(CCmdUI* pCmdUI)
4616 pCmdUI->SetCheck(Aa_gridlines);
4620 void CFREDView::OnCmdBrief()
4628 void CFREDView::OnDisableUndo()
4630 Autosave_disabled = !Autosave_disabled;
4633 void CFREDView::OnUpdateDisableUndo(CCmdUI* pCmdUI)
4635 pCmdUI->SetCheck(Autosave_disabled);
4638 void CFREDView::OnUpdateCmdBrief(CCmdUI* pCmdUI)
4640 pCmdUI->Enable(!(The_mission.game_type & MISSION_TYPE_MULTI));
4643 int get_visible_sub_system_count(ship *shipp)
4646 ship_subsys *cur_subsys;
4648 for (cur_subsys = GET_FIRST(&shipp->subsys_list); cur_subsys != END_OF_LIST(&shipp->subsys_list); cur_subsys = GET_NEXT(cur_subsys)) {
4649 if (cur_subsys->system_info->subobj_num != -1) {
4657 int get_next_visible_subsys(ship *shipp, ship_subsys **next_subsys)
4659 int count = get_visible_sub_system_count(shipp);
4661 // return don't try to display
4667 if (*next_subsys == NULL) {
4668 *next_subsys = &shipp->subsys_list;
4672 for (*next_subsys = GET_NEXT(*next_subsys); *next_subsys != END_OF_LIST(&shipp->subsys_list); *next_subsys = GET_NEXT(*next_subsys)) {
4673 if ((*next_subsys)->system_info->subobj_num != -1) {
4679 // look for first after wrap
4680 for (*next_subsys = GET_FIRST(&shipp->subsys_list); *next_subsys != END_OF_LIST(&shipp->subsys_list); *next_subsys = GET_NEXT(*next_subsys)) {
4681 if ((*next_subsys)->system_info->subobj_num != -1) {
4687 Int3(); // should be impossible to miss
4691 int get_prev_visible_subsys(ship *shipp, ship_subsys **prev_subsys)
4693 int count = get_visible_sub_system_count(shipp);
4695 // return don't try to display
4701 Assert(*prev_subsys != NULL);
4704 for (*prev_subsys = GET_PREV(*prev_subsys); *prev_subsys != END_OF_LIST(&shipp->subsys_list); *prev_subsys = GET_PREV(*prev_subsys)) {
4705 if ((*prev_subsys)->system_info->subobj_num != -1) {
4711 // look for first after wrap
4712 for (*prev_subsys = GET_LAST(&shipp->subsys_list); *prev_subsys != END_OF_LIST(&shipp->subsys_list); *prev_subsys = GET_PREV(*prev_subsys)) {
4713 if ((*prev_subsys)->system_info->subobj_num != -1) {
4719 Int3(); // should be impossible to miss
4723 // update next subsystem to view
4724 void CFREDView::OnNextSubsys()
4728 if (cur_object_index < 0) {
4732 objp = &Objects[cur_object_index];
4734 // check if cur object is ship type
4735 if (objp->type == OBJ_SHIP) {
4737 // check if same ship
4738 if (Render_subsys.ship_obj == objp) {
4740 // if already on, advance to next
4741 if (Render_subsys.do_render) {
4742 if ( !get_next_visible_subsys(&Ships[objp->instance], &Render_subsys.cur_subsys) ) {
4752 // set up new and advance to first
4753 Render_subsys.do_render = true;
4754 Render_subsys.ship_obj = objp;
4755 if ( !get_next_visible_subsys(&Ships[objp->instance], &Render_subsys.cur_subsys) ) {
4765 void CFREDView::OnPrevSubsys()
4767 if (!Render_subsys.do_render) {
4771 if ( (cur_object_index < 0) || (Objects[cur_object_index].type != OBJ_SHIP) || (&Objects[cur_object_index] != Render_subsys.ship_obj) ) {
4776 if ( !get_prev_visible_subsys(&Ships[Objects[cur_object_index].instance], &Render_subsys.cur_subsys) ) {
4782 void CFREDView::OnCancelSubsys()
4784 Render_subsys.do_render = false;
4785 Render_subsys.ship_obj = NULL;
4786 Render_subsys.cur_subsys = NULL;
4790 void CFREDView::OnShowPaths()
4792 Show_paths_fred = !Show_paths_fred;
4793 theApp.write_ini_file();
4797 void CFREDView::OnUpdateShowPaths(CCmdUI* pCmdUI)
4799 pCmdUI->SetCheck(Show_paths_fred);
4802 void CFREDView::OnShowDockPoints()
4804 Show_dock_points = !Show_dock_points;
4805 theApp.write_ini_file();
4809 void CFREDView::OnUpdateShowDockPoints(CCmdUI* pCmdUI)
4811 pCmdUI->SetCheck(Show_dock_points);
4814 void CFREDView::OnDumpStats()