]> icculus.org git repositories - taylor/freespace2.git/blob - src/fred2/fredview.cpp
fix issue with looping audio streams
[taylor/freespace2.git] / src / fred2 / fredview.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Fred2/FREDView.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * View class for a document/view architechure design program, which we don't
16  * want or need, but MFC forces us to use.  This is the main place we handle
17  * MFC messages, events, etc.  Sort of the interface between our code and MFC.
18  * There is also a lot of our code in here related to these things.
19  *
20  * $Log$
21  * Revision 1.3  2002/06/09 04:41:16  relnev
22  * added copyright header
23  *
24  * Revision 1.2  2002/05/07 03:16:44  theoddone33
25  * The Great Newline Fix
26  *
27  * Revision 1.1.1.1  2002/05/03 03:28:08  root
28  * Initial import.
29  *
30  * 
31  * 17    7/15/99 3:07p Dave
32  * 32 bit detection support. Mouse coord commandline.
33  * 
34  * 16    7/09/99 5:54p Dave
35  * Seperated cruiser types into individual types. Added tons of new
36  * briefing icons. Campaign screen.
37  * 
38  * 15    6/04/99 2:20p Andsager
39  * Add dump stats basic functionality
40  * 
41  * 14    4/07/99 6:21p Dave
42  * Fred and Freespace support for multiple background bitmaps and suns.
43  * Fixed link errors on all subprojects. Moved encrypt_init() to
44  * cfile_init() and lcl_init(), since its safe to call twice.
45  * 
46  * 13    3/26/99 4:49p Dave
47  * Made cruisers able to dock with stuff. Made docking points and paths
48  * visible in fred.
49  * 
50  * 12    3/20/99 5:09p Dave
51  * Fixed release build fred warnings and unhandled exception.
52  * 
53  * 11    3/04/99 11:56a Johnson
54  * Fixed an undo-rotation bug.
55  * 
56  * 10    3/02/99 9:25p Dave
57  * Added a bunch of model rendering debug code. Started work on fixing
58  * beam weapon wacky firing.
59  * 
60  * 9     3/01/99 10:00a Dave
61  * Fxied several dogfight related stats bugs.
62  * 
63  * 8     2/23/99 2:32p Dave
64  * First run of oldschool dogfight mode.
65  * 
66  * 7     1/27/99 4:09p Andsager
67  * Added highlight to ship subsystems
68  * 
69  * 6     1/07/99 1:52p Andsager
70  * Initial check in of Sexp_variables
71  * 
72  * 5     12/18/98 1:49a Dave
73  * Fixed Fred initialization problem resulting from hi-res mode changes.
74  * 
75  * 4     10/13/98 9:27a Dave
76  * Started neatening up freespace.h
77  * 
78  * 3     10/12/98 1:01p Dave
79  * Fixed object rotation bug (uninitialized data). Changed a few stubs to
80  * correspond to new var names.
81  * 
82  * 2     10/07/98 6:28p Dave
83  * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
84  * Fred. Globalized mission and campaign file extensions. Removed Silent
85  * Threat specific code.
86  * 
87  * 1     10/07/98 3:00p Dave
88  * 
89  * 248   9/16/98 6:54p Dave
90  * Upped  max sexpression nodes to 1800 (from 1600). Changed FRED to sort
91  * the ship list box. Added code so that tracker stats are not stored with
92  * only 1 player.
93  * 
94  * 247   9/14/98 3:40p Allender
95  * better error checking for invalid number of waves for player wings in a
96  * multiplayer game.  Better popup message in FreeSpace side.
97  * 
98  * 246   6/18/98 4:28p Hoffoss
99  * Made invalid dock points in an initial order flag an error in Fred.
100  * 
101  * 245   6/17/98 4:50p Hoffoss
102  * Added error checking for arrival delays used on wing player is in.
103  * 
104  * 244   6/16/98 10:24a Hoffoss
105  * Switched internal errors over to normal looking errors for release
106  * build.
107  * 
108  * 243   5/21/98 11:48a Hoffoss
109  * Removed check in and check out options.
110  * 
111  * 242   5/21/98 12:58a Hoffoss
112  * Fixed warnings optimized build turned up.
113  * 
114  * 241   5/19/98 1:19p Allender
115  * new low level reliable socket reading code.  Make all missions/campaign
116  * load/save to data missions folder (i.e. we are rid of the player
117  * missions folder)
118  * 
119  * 240   5/14/98 5:31p Hoffoss
120  * Added some more error checking.
121  * 
122  * 239   5/10/98 10:05p Allender
123  * only show cutscenes which have been seen before.  Made Fred able to
124  * write missions anywhere, defaulting to player misison folder, not data
125  * mission folder.  Fix FreeSpace code to properly read missions from
126  * correct locations
127  * 
128  * 238   4/28/98 2:13p Hoffoss
129  * Added code to help keep invalid player ship types from existing in
130  * mission.
131  * 
132  * 237   4/26/98 6:05p Hoffoss
133  * Added multiplayer error checks requested by Dave B.
134  * 
135  * 236   4/17/98 2:37p Duncan
136  * Added check to error checker for arrival/depature targets valid.
137  * 
138  * 235   4/14/98 4:35p Hoffoss
139  * Fixed bug with launching FreeSpace from Fred.  Current working
140  * directory wasn't being set properly.
141  * 
142  * 234   4/14/98 3:38p Hoffoss
143  * Fixed the FreeSpace launch code in Fred.
144  * 
145  * 233   4/07/98 9:42a Allender
146  * put in persona combo box into ship editor.  Removed code to assign
147  * personas based on message
148  * 
149  * 232   4/01/98 10:48a Hoffoss
150  * Changed Fred to not allow command briefings in multiplayer missions.
151  * 
152  * 231   3/26/98 3:01p Hoffoss
153  * Put in check for alpha wing num ships < 4 for furball multiplayer
154  * missions.
155  * 
156  * 230   3/25/98 4:14p Hoffoss
157  * Split ship editor up into ship editor and a misc dialog, which tracks
158  * flags and such.
159  * 
160  * 229   3/23/98 4:04p Hoffoss
161  * Fixed dialog window initialization so it looks better at startup (they
162  * don't flash on for a second).
163  * 
164  * 228   3/21/98 7:36p Lawrance
165  * Move jump nodes to own lib.
166  * 
167  * 227   3/19/98 3:37p Adam
168  * Fixed Int3() when you try to right click on a briefing icon.
169  * 
170  * 226   3/16/98 5:05p Hoffoss
171  * Fixed viewpoint bug.
172  * 
173  * 225   3/12/98 2:21p Johnson
174  * Fixed some Fred bugs related to jump nodes.
175  * 
176  * 224   3/10/98 6:11p Hoffoss
177  * Added jump node renaming abilities to Fred.
178  * 
179  * 223   3/10/98 4:26p Hoffoss
180  * Changed jump node structure to include a name.  Position is now taken
181  * from the object (each jump node has an associated object now).
182  * 
183  * 222   3/09/98 10:03p Hoffoss
184  * Added support for loading/saving jump nodes to mission files.
185  * 
186  * 221   3/09/98 10:56a Hoffoss
187  * Added jump node objects to Fred.
188  * 
189  * 220   3/05/98 3:59p Hoffoss
190  * Added a bunch of new command brief stuff, and asteroid initialization
191  * to Fred.
192  * 
193  * 219   2/26/98 4:59p Allender
194  * groundwork for team vs team briefings.  Moved weaponry pool into the
195  * Team_data structure.  Added team field into the p_info structure.
196  * Allow for mutliple structures in the briefing code.
197  * 
198  * 218   2/09/98 9:25p Allender
199  * team v team support.  multiple pools and breifings
200  * 
201  * 217   2/07/98 9:13p Hoffoss
202  * Added some more error checking to global error checker.
203  * 
204  * 216   2/06/98 4:39p Hoffoss
205  * Added better checking for whether Fred is in the foreground or
206  * background.
207  * 
208  * 215   2/04/98 4:32p Allender
209  * support for multiple briefings and debriefings.  Changes to mission
210  * type (now a bitfield).  Bitfield defs for multiplayer modes
211  * 
212  * 214   2/02/98 4:36p Hoffoss
213  * Made no mission title given flag an error.
214  * 
215  * 213   1/29/98 5:14p Hoffoss
216  * Added error checking for more than 4 ships in a player starting wing.
217  * 
218  * 212   1/21/98 5:22p Hoffoss
219  * Added check to global error checker to make sure Alpha wing exists for
220  * multiplayer missions.
221  * 
222  * 211   1/14/98 8:40p Allender
223  * don't check builtin messages when checking persona stuff
224  * 
225  * 210   1/07/98 11:24p Allender
226  * add latency of 0 to key_mark function calls
227  * 
228  * 209   12/31/97 3:56p Hoffoss
229  * Forced alpha wing to always have a true arrival cue.
230  * 
231  * 208   12/09/97 8:11a Allender
232  * have Fred warn on certain starting wing conditions
233  * 
234  * 207   11/24/97 10:14p Allender
235  * removed num_ships from use!!!!  it was creating problems
236  * 
237  * 206   11/24/97 11:07a Allender
238  * error checking on wings ignoring player orders
239  * 
240  * 205   11/17/97 4:57p Allender
241  * added persona support in FreeSpace.  A couple of new messages for
242  * message.tbl which Dan didn't have
243  * 
244  * 204   11/14/97 5:21p Johnson
245  * Added debriefing formulas to error checker.
246  * 
247  * 203   11/05/97 4:43p Allender
248  * reworked medal/rank system to read all data from tables.  Made Fred
249  * read medals.tbl.  Changed ai-warp to ai-warp-out which doesn't require
250  * waypoint for activation
251  * 
252  * 202   10/30/97 3:30p Hoffoss
253  * Made anti-aliased gridlines an option in Fred.
254  * 
255  * 201   10/28/97 9:54a Jasen
256  * don't check for personas when wing is sending message
257  * 
258  * 200   10/22/97 3:15p Hoffoss
259  * Fixed ai-stay-still initial order to use waypoints instead of waypoint
260  * paths.
261  * 
262  * 199   10/22/97 1:58p Hoffoss
263  * Added support for AI_GOALS_PLAY_DEAD and changed AI_GOALS_STAY_STILL
264  * for Fred.
265  * 
266  * 198   10/14/97 10:59a Allender
267  * more persona work.  Made global error checker call funciton to assign
268  * and check personas
269  * 
270  * 197   10/10/97 5:03p Allender
271  * started work on ai-stay-still
272  * 
273  * 196   10/01/97 12:37p Hoffoss
274  * Changed Fred (and FreeSpace) to utilize alpha, beta and gamma as player
275  * starting wing names.
276  * 
277  * 195   9/16/97 9:41p Hoffoss
278  * Changed Fred code around to stop using Parse_player structure for
279  * player information, and use actual ships instead.
280  * 
281  * 194   9/10/97 3:48p Duncan
282  * Added a case that was never added in the past (probably overlooked).
283  * 
284  * 193   9/09/97 3:39p Sandeep
285  * warning level 4 bugs
286  * 
287  * 192   9/09/97 2:12p Hoffoss
288  * Added code to allow briefing editor view to be a 1:1 pixel size fixed
289  * as the FreeSpace view will have.
290  * 
291  * 191   9/06/97 2:13p Mike
292  * Replace support for TEAM_NEUTRAL
293  * 
294  * 190   9/03/97 4:32p Hoffoss
295  * Added error number range error checking to Fred, and defaulted numbers
296  * for # of times docked to 1 in sexp trees.
297  * 
298  * 189   9/02/97 4:32p Hoffoss
299  * Made minimized child windows restore if the window is activated.
300  * 
301  * 188   9/02/97 1:34p Johnson
302  * Fixed bug I swear wasn't there when I checked this in originally.
303  * Strange.
304  * 
305  * 187   9/01/97 6:59p Hoffoss
306  * Added source safe checkin and checkout capabilities to Fred.
307  * 
308  * 186   8/28/97 8:56a Hoffoss
309  * Added more checking to sexp error checker, fixed some bugs.
310  * 
311  * 185   8/26/97 4:18p Hoffoss
312  * Added error checking to initial orders dialog when ok is clicked.
313  * 
314  * 184   8/25/97 5:58p Hoffoss
315  * Created menu items for keypress functions in Fred, and fixed bug this
316  * uncovered with wing_delete function.
317  * 
318  * 183   8/22/97 4:16p Hoffoss
319  * added support for arrival and departure info in ship editor using
320  * wing's info if editing marked ships in a wing instead of using ship's.
321  * 
322  * 182   8/18/97 9:31p Hoffoss
323  * Added grid adjustment dialog and shield system editor dialog.
324  * 
325  * 181   8/17/97 10:22p Hoffoss
326  * Fixed several bugs in Fred with Undo feature.  In the process, recoded
327  * a lot of CFile.cpp.
328  * 
329  * 180   8/16/97 5:23p Hoffoss
330  * Added restrictions for single player mission type to allow only 1
331  * player start.
332  * 
333  * 179   8/16/97 2:02a Hoffoss
334  * Made docked objects move together in Fred.
335  * 
336  * 178   8/15/97 11:08a Hoffoss
337  * Created a list of order types that can be used for several things, and
338  * yet easily changable.  Added order error checking against ship types.
339  * 
340  * 177   8/15/97 1:07a Hoffoss
341  * Changed code to disallow some ship types from being in a wing, and
342  * disallowing some ship types from being able to have initial orders.
343  * 
344  * 176   8/14/97 6:37p Hoffoss
345  * Added briefing icon id support to Fred.
346  * 
347  * 175   8/14/97 2:32p Hoffoss
348  * fixed bug where controlling an object doesn't cause screen updates, and
349  * added a number of cool features to viewpoint/control object code.
350  * 
351  * 174   8/14/97 9:30a Hoffoss
352  * Added support for new ai goals "stay near ship" and "keep safe
353  * distance".
354  * 
355  * 173   8/13/97 6:23p Hoffoss
356  * Fixed initially docked code in Fred.
357  * 
358  * 172   8/13/97 5:40p Hoffoss
359  * Fixed typo.
360  * 
361  * 171   8/12/97 10:42p Hoffoss
362  * Changed code to campaign editor load an accelerator table when created.
363  * 
364  * 170   8/12/97 6:32p Hoffoss
365  * Added code to allow hiding of arrival and departure cues in editors.
366  * 
367  * 169   8/12/97 5:56p Johnson
368  * Fixed bug with global error checker.  Initially docked ships flagged as
369  * error when they aren't really.
370  * 
371  * 168   8/12/97 1:29p Duncan
372  * Fixed bug with ai_undock initial order.
373  * 
374  * 167   8/11/97 7:11p Hoffoss
375  * Somehow SS didn't merge the files, so I've reconstructed the changes
376  * Allender made.
377  * 
378  * 166   8/11/97 6:54p Hoffoss
379  * Groups now supported in Fred.
380  *
381  * 165   8/11/97 11:51a Allender
382  * added stamp stuff to Fred
383  * 
384  * 164   8/07/97 6:01p Hoffoss
385  * Added a rotate about selected object button to toolbar and
386  * functionality, as requested by Comet.
387  * 
388  * 163   8/07/97 2:07p Hoffoss
389  * Added duplicate icon label error checking to global error checker, and
390  * made briefign mode boxmark only icons if at least one icon is in the
391  * selection box.
392  * 
393  * 162   8/06/97 10:20a Hoffoss
394  * Changed rotation speeds to be less linear and hopefully more useful.
395  * 
396  * 161   8/05/97 2:27p Jasen
397  * Fixed bug in global error checker that caused crashes when in briefing
398  * mode.
399  * 
400  * 160   8/05/97 1:31p Hoffoss
401  * Removed excessive source control info from header.
402  * 
403  * 159   8/01/97 5:52p Hoffoss
404  * Changed initially docked to disallow illegal docking combinations, and
405  * changed error checker to check for this as well.
406  * 
407  * 158   8/01/97 3:10p Hoffoss
408  * Made Sexp help hidable.
409  * 
410  * 157   8/01/97 12:52p Hoffoss
411  * Added variable, fixed bug with global error check.
412  * 
413  * 156   7/31/97 6:41p Hoffoss
414  * Added checks to global error checker.
415  * 
416  * 155   7/30/97 4:30p Hoffoss
417  * Made Fred sexp error checker more explicit.
418  * 
419  * 154   7/29/97 5:16p Hoffoss
420  * Fixed bug in ship_query_general_type() and added docking checking to
421  * Fred's error checking.
422  * 
423  * 153   7/29/97 1:44p Hoffoss
424  * Make player ships exception to ship target within same wing error
425  * traping.
426  *
427  * $NoKeywords: $
428  */
429
430 #include "stdafx.h"
431 #include "fred.h"
432
433 #include "freddoc.h"
434 #include "fredview.h"
435 #include "fredrender.h"
436 #include "cfile.h"
437 #include "grid.h"
438 #include "mainfrm.h"
439 #include "editor.h"
440 #include "management.h"
441 #include "2d.h"
442 #include "3d.h"
443 #include "object.h"
444 #include "linklist.h"
445 #include "fvi.h"        //      For find_plane_line_intersection
446 #include "vecmat.h"
447 #include "key.h"
448 #include "ailocal.h"
449 #include "ai.h"
450 #include "aigoals.h"
451 #include "ship.h"       // for ship names
452 #include "missiongoalsdlg.h"
453 #include "wing.h"
454 #include "ship_select.h"
455 #include "playerstarteditor.h"
456 #include "orienteditor.h"
457 #include "eventeditor.h"
458 #include "messageeditordlg.h"
459 #include "starfield.h"
460 #include "starfieldeditor.h"
461 #include "floating.h"
462 #include "reinforcementeditordlg.h"
463 #include "asteroideditordlg.h"
464 #include "campaigntreewnd.h"
465 #include "debriefingeditordlg.h"
466 #include "adjustgriddlg.h"
467 #include "shieldsysdlg.h"
468 #include "cmdbrief.h"
469 #include "jumpnode.h"
470 #include "dumpstats.h"
471
472 #ifdef _DEBUG
473 #define new DEBUG_NEW
474 #undef THIS_FILE
475 static char THIS_FILE[] = __FILE__;
476 #endif
477
478 subsys_to_render Render_subsys;
479
480 // the next variable is used for executable stamping -- please leave it alone!!!
481 #define FRED_EXPIRE_TIME        (7 * 1000)
482 char stamp[STAMP_STRING_LENGTH] = { STAMP_STRING };
483 int expire_game;
484
485 #define EXPIRE_BAD_CHECKSUM                     1
486 #define EXPIRE_BAD_TIME                                 2
487
488 #define SHIP_TYPES                      8000
489 #define REDUCER                         100.0f
490 #define DUP_DRAG_OF_WING        2
491
492 LOCAL int Duped_wing;
493
494 int Autosave_disabled = 0;
495 int Show_sexp_help = 1;
496 int Show_ships = 1;
497 int Show_starts = 1;
498 int Show_friendly = 1;
499 int Show_hostile = 1;
500 int Show_neutral = 1;
501 int Show_ship_info = 1;
502 int Show_ship_models = 0;
503 int Show_compass = 1;
504 int Show_dock_points = 0;
505 int Show_paths_fred = 0;
506 int Selection_lock = 0;
507 int viewpoint = 0;
508 int view_obj;
509 int button_down = 0;
510 int Marked = 0, moved = 0;
511 int on_object = -1;
512 int Cursor_over = -1;
513 int Dup_drag = 0;
514 int physics_speed = 1;
515 int physics_rot = 20;
516 int box_marking = 0;
517 int last_mouse_x, last_mouse_y, mouse_dx, mouse_dy;
518 int Cur_bitmap = -1;
519 int Id_select_type_jump_node;
520 int Id_select_type_start = 0;
521 int Id_select_type_waypoint = 0;
522 int Hide_ship_cues = 0, Hide_wing_cues = 0;
523 vector original_pos, saved_cam_pos;
524 matrix bitmap_matrix_backup, saved_cam_orient = { 0.0f };
525 Marking_box     marking_box;
526 object_orient_pos       rotation_backup[MAX_OBJECTS];
527
528 // used by error checker, but needed in more than just one function.
529 char *names[MAX_OBJECTS], flags[MAX_OBJECTS];
530 int obj_count = 0;
531 int g_err = 0;
532
533 void view_universe(int just_marked = 0);
534 void select_objects();
535 void drag_rotate_save_backup();
536
537 /////////////////////////////////////////////////////////////////////////////
538 // CFREDView
539
540 CFREDView *Fred_view_wnd = NULL;
541
542 IMPLEMENT_DYNCREATE(CFREDView, CView)
543
544 BEGIN_MESSAGE_MAP(CFREDView, CView)
545         ON_MESSAGE(WM_GOODBYE, OnGoodbye)
546         ON_MESSAGE(WM_MENU_POPUP_SHIPS, OnMenuPopupShips)
547         ON_MESSAGE(WM_MENU_POPUP_EDIT, OnMenuPopupEdit)
548
549         //{{AFX_MSG_MAP(CFREDView)
550         ON_COMMAND(ID_VIEW_GRID, OnViewGrid)
551         ON_UPDATE_COMMAND_UI(ID_VIEW_GRID, OnUpdateViewGrid)
552         ON_COMMAND(ID_SHOW_WAYPOINTS, OnViewWaypoints)
553         ON_UPDATE_COMMAND_UI(ID_SHOW_WAYPOINTS, OnUpdateViewWaypoints)
554         ON_WM_LBUTTONDOWN()
555         ON_COMMAND(ID_EDITORS_SHIPS, OnEditorsShips)
556         ON_WM_KEYDOWN()
557         ON_WM_KEYUP()
558         ON_WM_SETFOCUS()
559         ON_WM_KILLFOCUS()
560         ON_WM_SIZE()
561         ON_WM_MOUSEMOVE()
562         ON_WM_LBUTTONUP()
563         ON_COMMAND(ID_MISCSTUFF_SHOWSHIPSASICONS, OnMiscstuffShowshipsasicons)
564         ON_WM_CONTEXTMENU()
565         ON_COMMAND(ID_EDIT_POPUP_SHOW_SHIP_ICONS, OnEditPopupShowShipIcons)
566         ON_UPDATE_COMMAND_UI(ID_EDIT_POPUP_SHOW_SHIP_ICONS, OnUpdateEditPopupShowShipIcons)
567         ON_COMMAND(ID_EDIT_POPUP_SHOW_SHIP_MODELS, OnEditPopupShowShipModels)
568         ON_UPDATE_COMMAND_UI(ID_EDIT_POPUP_SHOW_SHIP_MODELS, OnUpdateEditPopupShowShipModels)
569
570         ON_COMMAND(ID_SHOW_PATHS, OnShowPaths)
571         ON_UPDATE_COMMAND_UI(ID_SHOW_PATHS, OnUpdateShowPaths)
572         ON_COMMAND(ID_SHOW_DOCK_POINTS, OnShowDockPoints)
573         ON_UPDATE_COMMAND_UI(ID_SHOW_DOCK_POINTS, OnUpdateShowDockPoints)
574
575         ON_COMMAND(ID_MISC_STATISTICS, OnMiscStatistics)
576         ON_COMMAND(ID_EDIT_POPUP_SHOW_COMPASS, OnEditPopupShowCompass)
577         ON_UPDATE_COMMAND_UI(ID_EDIT_POPUP_SHOW_COMPASS, OnUpdateEditPopupShowCompass)
578         ON_UPDATE_COMMAND_UI(ID_CHANGE_VIEWPOINT_EXTERNAL, OnUpdateChangeViewpointExternal)
579         ON_COMMAND(ID_CHANGE_VIEWPOINT_EXTERNAL, OnChangeViewpointExternal)
580         ON_UPDATE_COMMAND_UI(ID_CHANGE_VIEWPOINT_FOLLOW, OnUpdateChangeViewpointFollow)
581         ON_COMMAND(ID_CHANGE_VIEWPOINT_FOLLOW, OnChangeViewpointFollow)
582         ON_COMMAND(ID_EDITORS_GOALS, OnEditorsGoals)
583         ON_COMMAND(ID_SPEED1, OnSpeed1)
584         ON_COMMAND(ID_SPEED2, OnSpeed2)
585         ON_COMMAND(ID_SPEED5, OnSpeed5)
586         ON_COMMAND(ID_SPEED10, OnSpeed10)
587         ON_UPDATE_COMMAND_UI(ID_SPEED1, OnUpdateSpeed1)
588         ON_COMMAND(ID_SPEED3, OnSpeed3)
589         ON_COMMAND(ID_SPEED8, OnSpeed8)
590         ON_COMMAND(ID_ROT1, OnRot1)
591         ON_COMMAND(ID_ROT2, OnRot2)
592         ON_COMMAND(ID_ROT3, OnRot3)
593         ON_COMMAND(ID_ROT4, OnRot4)
594         ON_COMMAND(ID_ROT5, OnRot5)
595         ON_UPDATE_COMMAND_UI(ID_SPEED2, OnUpdateSpeed2)
596         ON_UPDATE_COMMAND_UI(ID_SPEED3, OnUpdateSpeed3)
597         ON_UPDATE_COMMAND_UI(ID_SPEED5, OnUpdateSpeed5)
598         ON_UPDATE_COMMAND_UI(ID_SPEED8, OnUpdateSpeed8)
599         ON_UPDATE_COMMAND_UI(ID_SPEED10, OnUpdateSpeed10)
600         ON_UPDATE_COMMAND_UI(ID_ROT1, OnUpdateRot1)
601         ON_UPDATE_COMMAND_UI(ID_ROT2, OnUpdateRot2)
602         ON_UPDATE_COMMAND_UI(ID_ROT3, OnUpdateRot3)
603         ON_UPDATE_COMMAND_UI(ID_ROT4, OnUpdateRot4)
604         ON_UPDATE_COMMAND_UI(ID_ROT5, OnUpdateRot5)
605         ON_COMMAND(ID_CONTROL_MODE_CAMERA, OnControlModeCamera)
606         ON_UPDATE_COMMAND_UI(ID_CONTROL_MODE_CAMERA, OnUpdateControlModeCamera)
607         ON_COMMAND(ID_CONTROL_MODE_SHIP, OnControlModeShip)
608         ON_UPDATE_COMMAND_UI(ID_CONTROL_MODE_SHIP, OnUpdateControlModeShip)
609         ON_COMMAND(ID_SHOW_GRID_POSITIONS, OnShowGridPositions)
610         ON_UPDATE_COMMAND_UI(ID_SHOW_GRID_POSITIONS, OnUpdateShowGridPositions)
611         ON_COMMAND(ID_SHOW_COORDINATES, OnShowCoordinates)
612         ON_UPDATE_COMMAND_UI(ID_SHOW_COORDINATES, OnUpdateShowCoordinates)
613         ON_COMMAND(ID_SPEED50, OnSpeed50)
614         ON_UPDATE_COMMAND_UI(ID_SPEED50, OnUpdateSpeed50)
615         ON_COMMAND(ID_SPEED100, OnSpeed100)
616         ON_UPDATE_COMMAND_UI(ID_SPEED100, OnUpdateSpeed100)
617         ON_COMMAND(ID_SELECT, OnSelect)
618         ON_UPDATE_COMMAND_UI(ID_SELECT, OnUpdateSelect)
619         ON_COMMAND(ID_SELECT_AND_MOVE, OnSelectAndMove)
620         ON_UPDATE_COMMAND_UI(ID_SELECT_AND_MOVE, OnUpdateSelectAndMove)
621         ON_COMMAND(ID_SELECT_AND_ROTATE, OnSelectAndRotate)
622         ON_UPDATE_COMMAND_UI(ID_SELECT_AND_ROTATE, OnUpdateSelectAndRotate)
623         ON_COMMAND(ID_CONSTRAIN_X, OnConstrainX)
624         ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_X, OnUpdateConstrainX)
625         ON_COMMAND(ID_CONSTRAIN_Y, OnConstrainY)
626         ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_Y, OnUpdateConstrainY)
627         ON_COMMAND(ID_CONSTRAIN_Z, OnConstrainZ)
628         ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_Z, OnUpdateConstrainZ)
629         ON_COMMAND(ID_CONSTRAIN_XZ, OnConstrainXz)
630         ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_XZ, OnUpdateConstrainXz)
631         ON_COMMAND(ID_SELECTION_LOCK, OnSelectionLock)
632         ON_UPDATE_COMMAND_UI(ID_SELECTION_LOCK, OnUpdateSelectionLock)
633         ON_WM_LBUTTONDBLCLK()
634         ON_COMMAND(ID_DOUBLE_FINE_GRIDLINES, OnDoubleFineGridlines)
635         ON_UPDATE_COMMAND_UI(ID_DOUBLE_FINE_GRIDLINES, OnUpdateDoubleFineGridlines)
636         ON_COMMAND(ID_SHOW_DISTANCES, OnShowDistances)
637         ON_UPDATE_COMMAND_UI(ID_SHOW_DISTANCES, OnUpdateShowDistances)
638         ON_COMMAND(ID_UNIVERSAL_HEADING, OnUniversalHeading)
639         ON_UPDATE_COMMAND_UI(ID_UNIVERSAL_HEADING, OnUpdateUniversalHeading)
640         ON_COMMAND(ID_FLYING_CONTROLS, OnFlyingControls)
641         ON_UPDATE_COMMAND_UI(ID_FLYING_CONTROLS, OnUpdateFlyingControls)
642         ON_COMMAND(ID_ROTATE_LOCALLY, OnRotateLocally)
643         ON_UPDATE_COMMAND_UI(ID_ROTATE_LOCALLY, OnUpdateRotateLocally)
644         ON_COMMAND(ID_CONSTRAIN_XY, OnConstrainXy)
645         ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_XY, OnUpdateConstrainXy)
646         ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_YZ, OnUpdateConstrainYz)
647         ON_COMMAND(ID_CONSTRAIN_YZ, OnConstrainYz)
648         ON_COMMAND(ID_SELECT_LIST, OnSelectList)
649         ON_COMMAND(ID_ZOOM_EXTENTS, OnZoomExtents)
650         ON_COMMAND(ID_ZOOM_SELECTED, OnZoomSelected)
651         ON_UPDATE_COMMAND_UI(ID_ZOOM_SELECTED, OnUpdateZoomSelected)
652         ON_COMMAND(ID_FORM_WING, OnFormWing)
653         ON_UPDATE_COMMAND_UI(ID_FORM_WING, OnUpdateFormWing)
654         ON_COMMAND(ID_DISBAND_WING, OnDisbandWing)
655         ON_UPDATE_COMMAND_UI(ID_DISBAND_WING, OnUpdateDisbandWing)
656         ON_COMMAND(ID_SHOW_HORIZON, OnShowHorizon)
657         ON_UPDATE_COMMAND_UI(ID_SHOW_HORIZON, OnUpdateShowHorizon)
658         ON_COMMAND(ID_EDITORS_WING, OnEditorsWing)
659         ON_COMMAND(ID_EDITORS_PLAYER, OnEditorsPlayer)
660         ON_COMMAND(ID_EDITORS_ORIENT, OnEditorsOrient)
661         ON_COMMAND(ID_EDITORS_EVENTS, OnEditorsEvents)
662         ON_UPDATE_COMMAND_UI(ID_EDITORS_ORIENT, OnUpdateEditorsOrient)
663         ON_COMMAND(ID_EDITORS_MESSAGE, OnEditorsMessage)
664         ON_COMMAND(ID_EDITORS_STARFIELD, OnEditorsStarfield)
665         ON_COMMAND(ID_EDITORS_BG_BITMAPS, OnEditorsBgBitmaps)
666         ON_COMMAND(ID_EDITORS_REINFORCEMENT, OnEditorsReinforcement)
667         ON_COMMAND(ID_ERROR_CHECKER, OnErrorChecker)
668         ON_COMMAND(ID_EDITORS_WAYPOINT, OnEditorsWaypoint)
669         ON_COMMAND(ID_VIEW_OUTLINES, OnViewOutlines)
670         ON_UPDATE_COMMAND_UI(ID_VIEW_OUTLINES, OnUpdateViewOutlines)
671         ON_UPDATE_COMMAND_UI(ID_NEW_SHIP_TYPE, OnUpdateNewShipType)
672         ON_COMMAND(ID_SHOW_STARFIELD, OnShowStarfield)
673         ON_UPDATE_COMMAND_UI(ID_SHOW_STARFIELD, OnUpdateShowStarfield)
674         ON_COMMAND(ID_ASTEROID_EDITOR, OnAsteroidEditor)
675         ON_COMMAND(ID_RUN_FREESPACE, OnRunFreespace)
676         ON_COMMAND(ID_EDITOR_CAMPAIGN, OnEditorCampaign)
677         ON_COMMAND(ID_SHOW_SHIPS, OnShowShips)
678         ON_UPDATE_COMMAND_UI(ID_SHOW_SHIPS, OnUpdateShowShips)
679         ON_COMMAND(ID_SHOW_STARTS, OnShowStarts)
680         ON_UPDATE_COMMAND_UI(ID_SHOW_STARTS, OnUpdateShowStarts)
681         ON_COMMAND(ID_SHOW_FRIENDLY, OnShowFriendly)
682         ON_UPDATE_COMMAND_UI(ID_SHOW_FRIENDLY, OnUpdateShowFriendly)
683         ON_COMMAND(ID_SHOW_HOSTILE, OnShowHostile)
684         ON_UPDATE_COMMAND_UI(ID_SHOW_HOSTILE, OnUpdateShowHostile)
685         ON_COMMAND(ID_TOGGLE_VIEWPOINT, OnToggleViewpoint)
686         ON_COMMAND(ID_REVERT, OnRevert)
687         ON_UPDATE_COMMAND_UI(ID_REVERT, OnUpdateRevert)
688         ON_WM_SETCURSOR()
689         ON_COMMAND(ID_HIDE_OBJECTS, OnHideObjects)
690         ON_COMMAND(ID_SHOW_HIDDEN_OBJECTS, OnShowHiddenObjects)
691         ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
692         ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
693         ON_COMMAND(ID_EDITORS_BRIEFING, OnEditorsBriefing)
694         ON_COMMAND(ID_EDITORS_DEBRIEFING, OnEditorsDebriefing)
695         ON_COMMAND(ID_SAVE_CAMERA, OnSaveCamera)
696         ON_COMMAND(ID_RESTORE_CAMERA, OnRestoreCamera)
697         ON_UPDATE_COMMAND_UI(ID_RESTORE_CAMERA, OnUpdateRestoreCamera)
698         ON_COMMAND(ID_SHOW_SEXP_HELP, OnShowSexpHelp)
699         ON_UPDATE_COMMAND_UI(ID_SHOW_SEXP_HELP, OnUpdateShowSexpHelp)
700         ON_COMMAND(ID_LOOKAT_OBJ, OnLookatObj)
701         ON_UPDATE_COMMAND_UI(ID_LOOKAT_OBJ, OnUpdateLookatObj)
702         ON_COMMAND(ID_EDITORS_ADJUST_GRID, OnEditorsAdjustGrid)
703         ON_COMMAND(ID_EDITORS_SHIELD_SYS, OnEditorsShieldSys)
704         ON_COMMAND(ID_LEVEL_OBJ, OnLevelObj)
705         ON_COMMAND(ID_ALIGN_OBJ, OnAlignObj)
706         ON_COMMAND(ID_CONTROL_OBJ, OnControlObj)
707         ON_COMMAND(ID_NEXT_OBJ, OnNextObj)
708         ON_COMMAND(ID_PREV_OBJ, OnPrevObj)
709         ON_COMMAND(ID_EDIT_DELETE_WING, OnEditDeleteWing)
710         ON_COMMAND(ID_MARK_WING, OnMarkWing)
711         ON_UPDATE_COMMAND_UI(ID_CONTROL_OBJ, OnUpdateControlObj)
712         ON_COMMAND(ID_EDIT_DELETE, OnEditDelete)
713         ON_COMMAND(ID_AA_GRIDLINES, OnAaGridlines)
714         ON_UPDATE_COMMAND_UI(ID_AA_GRIDLINES, OnUpdateAaGridlines)
715         ON_COMMAND(ID_CMD_BRIEF, OnCmdBrief)
716         ON_COMMAND(ID_DISABLE_UNDO, OnDisableUndo)
717         ON_UPDATE_COMMAND_UI(ID_DISABLE_UNDO, OnUpdateDisableUndo)
718         ON_UPDATE_COMMAND_UI(ID_CMD_BRIEF, OnUpdateCmdBrief)
719         ON_COMMAND(ID_NEXT_SUBSYS, OnNextSubsys)
720         ON_COMMAND(ID_PREV_SUBSYS, OnPrevSubsys)
721         ON_COMMAND(ID_CANCEL_SUBSYS, OnCancelSubsys)
722         ON_COMMAND(ID_DUMP_STATS, OnDumpStats)
723         //}}AFX_MSG_MAP
724         ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
725         ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
726         ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
727         ON_COMMAND_RANGE(ID_GROUP1, ID_GROUP9, OnGroup)
728         ON_COMMAND_RANGE(ID_SET_GROUP1, ID_SET_GROUP9, OnSetGroup)
729 END_MESSAGE_MAP()
730
731 /////////////////////////////////////////////////////////////////////////////
732 // CFREDView construction/destruction
733
734 CFREDView::CFREDView()
735 {
736         //m_ConfirmDeleting = TRUE;
737         //m_ShowCapitalShips = TRUE;
738         //m_ShowElevations = TRUE;
739         //m_ShowFighters = TRUE;
740         //m_ShowGrid = TRUE;
741         //m_ShowMiscObjects = TRUE;
742         //m_ShowPlanets = TRUE;
743         //m_ShowWaypoints = TRUE;
744         
745         m_pGDlg = new CGrid(this);
746
747         fred_init();
748
749         //if (!(int errno = gr_init(640, 480, 32)))
750         //      Error(LOCATION, "Hey, gr_init failed! Error code = %i", errno);
751         Fred_view_wnd = this;
752 }
753
754 CFREDView::~CFREDView()
755 {
756         delete m_pGDlg;
757
758         gr_close();
759 }
760
761 void CALLBACK expire_game_proc( HWND wnd, UINT uMsg, UINT idEvent, DWORD dwTime)
762 {
763         KillTimer(wnd, 1);
764         if ( expire_game == EXPIRE_BAD_CHECKSUM )
765                 MessageBox (wnd, "Fred can no longer run due to internal overlay error", NULL, MB_OK | MB_ICONERROR |MB_TASKMODAL|MB_SETFOREGROUND);
766         else
767                 MessageBox (wnd, "Error: cannot enter DOS mode for 80x40 color text mode display.", NULL, MB_OK | MB_ICONERROR|MB_TASKMODAL|MB_SETFOREGROUND);
768         exit(1);
769 }
770
771 BOOL CFREDView::PreCreateWindow(CREATESTRUCT& cs)
772 {
773         BOOL casperl;
774
775         casperl = CView::PreCreateWindow(cs);
776         cs.y = 0;  // doesn't seem to do anything. :(
777
778 // other miscellaneous initializations
779         cfile_chdir("data\\missions");
780         set_physics_controls();
781         return casperl;
782 }
783
784 /////////////////////////////////////////////////////////////////////////////
785 // CFREDView drawing
786
787 void CFREDView::OnDraw(CDC* pDC)
788 {
789         CRect clip;
790
791         CFREDDoc* pDoc = GetDocument();
792         ASSERT_VALID(pDoc);
793
794         Update_window = 1;
795         if (Fred_active)
796                 return;
797
798         pDC->GetClipBox(&clip);
799         gr_set_clip(clip.left, clip.top, clip.right - clip.left + 1, clip.bottom - clip.top + 1);
800         SDL_assert(clip.left <= clip.right);
801         SDL_assert(clip.top <= clip.bottom);
802         gr_flip_window((uint) pDC->m_hDC, clip.left, clip.top,
803                 clip.right - clip.left + 1, clip.bottom - clip.top + 1);
804 }
805
806 /////////////////////////////////////////////////////////////////////////////
807 // CFREDView printing
808
809 BOOL CFREDView::OnPreparePrinting(CPrintInfo* pInfo)
810 {
811         // default preparation
812         return DoPreparePrinting(pInfo);
813 }
814
815 void CFREDView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
816 {
817         // TODO: add extra initialization before printing
818 }
819
820 void CFREDView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
821 {
822         // TODO: add cleanup after printing
823 }
824
825 /////////////////////////////////////////////////////////////////////////////
826 // CFREDView diagnostics
827
828 #ifdef _DEBUG
829 void CFREDView::AssertValid() const
830 {
831         CView::AssertValid();
832 }
833
834 void CFREDView::Dump(CDumpContext& dc) const
835 {
836         CView::Dump(dc);
837 }
838
839 CFREDDoc* CFREDView::GetDocument() // non-debug version is inline
840 {
841         ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CFREDDoc)));
842         return (CFREDDoc*)m_pDocument;
843 }
844 #endif //_DEBUG
845
846 /////////////////////////////////////////////////////////////////////////////
847 // CFREDView message handlers
848
849 void CFREDView::OnViewGrid() 
850 {
851         Show_grid = !Show_grid;
852         Update_window = 1;
853 }
854
855 void CFREDView::OnUpdateViewGrid(CCmdUI* pCmdUI) 
856 {
857         pCmdUI->SetCheck(Show_grid);
858 }
859
860 void CFREDView::OnViewWaypoints() 
861 {
862         Show_waypoints = !Show_waypoints;
863         Update_window = 1;
864 }
865
866 void CFREDView::OnUpdateViewWaypoints(CCmdUI* pCmdUI) 
867 {
868         pCmdUI->SetCheck(Show_waypoints);
869 }
870
871 #define MAX_MOVE_DISTANCE 25.0f
872
873 //      If cur_object_index references a valid object, drag it from its current
874 //      location to the new cursor location specified by "point".
875 //      It is dragged relative to the main grid.  It's y coordinate is not changed.
876 //      Return value: 0/1 = didn't/did move object all the way to goal.
877 int drag_objects()
878 {
879         int z, cobj, flag, rval = 1;
880         float r;
881         float   distance_moved = 0.0f;
882         vector cursor_dir, int_pnt;
883         vector movement_vector;
884         vector obj;
885         vector vec1, vec2;
886         object *objp, *ptr;
887         // starfield_bitmaps *bmp;
888
889         /*
890         if (Bg_bitmap_dialog) {
891                 if (Cur_bitmap < 0)
892                         return -1;
893
894                 bmp = &Starfield_bitmaps[Cur_bitmap];
895                 if (Single_axis_constraint && Constraint.z) {
896                         bmp->dist *= 1.0f + mouse_dx / -800.0f;
897                         calculate_bitmap_points(bmp, 0.0f);
898
899                 } else {
900                         g3_point_to_vec_delayed(&bmp->m.fvec, marking_box.x2, marking_box.y2);
901                         vm_orthogonalize_matrix(&bmp->m);
902                         calculate_bitmap_points(bmp, 0.0f);
903                 }
904                 return rval;
905         }
906         */
907
908         if (!query_valid_object())
909                 return -1;
910
911         if ((Dup_drag == 1) && (Briefing_dialog))
912                 Dup_drag = 0;
913
914         if (Dup_drag == 1) {
915                 dup_object(NULL);  // reset waypoint list
916                 cobj = Duped_wing = -1;
917                 flag = 0;
918                 objp = GET_FIRST(&obj_used_list);
919                 while (objp != END_OF_LIST(&obj_used_list))     {
920                         SDL_assert(objp->type != OBJ_NONE);
921                         if (objp->flags & OF_MARKED) {
922                                 if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
923                                         z = Ships[objp->instance].wingnum;
924                                         if (!flag)
925                                                 Duped_wing = z;
926                                         else if (Duped_wing != z)
927                                                 Duped_wing = -1;
928
929                                 } else
930                                         Duped_wing = -1;
931
932                                 flag = 1;
933                                 z = dup_object(objp);
934                                 if (z == -1) {
935                                         cobj = -1;
936                                         break;
937                                 }
938
939                                 if (cur_object_index == OBJ_INDEX(objp) )
940                                         cobj = z;
941                         }
942
943                         objp = GET_NEXT(objp);
944                 }
945
946                 obj_merge_created_list();
947                 if (cobj == -1) {
948                         objp = GET_FIRST(&obj_used_list);
949                         while (objp != END_OF_LIST(&obj_used_list))     {
950                                 ptr = GET_NEXT(objp);
951                                 if (objp->flags & OF_TEMP_MARKED)
952                                         delete_object(objp);
953
954                                 objp = ptr;
955                         }
956
957                         button_down = 0;
958                         return -1;
959                 }
960
961                 unmark_all();
962
963                 objp = GET_FIRST(&obj_used_list);
964                 while (objp != END_OF_LIST(&obj_used_list))     {
965                         if (objp->flags & OF_TEMP_MARKED) {
966                                 objp->flags &= ~OF_TEMP_MARKED;
967                                 mark_object(OBJ_INDEX(objp));
968                         }
969
970                         objp = GET_NEXT(objp);
971                 }
972
973                 set_cur_object_index(cobj);
974                 if (Duped_wing != -1)
975                         Dup_drag = DUP_DRAG_OF_WING;  // indication for later that we duped objects in a wing
976                 else
977                         Dup_drag = 0;
978
979                 drag_rotate_save_backup();
980                 set_modified();
981                 Update_window = 1;
982         }
983
984         objp = &Objects[cur_object_index];
985         SDL_assert(objp->type != OBJ_NONE);
986         obj = int_pnt = objp->pos;
987
988         //      Get 3d vector specified by mouse cursor location.
989         g3_point_to_vec_delayed(&cursor_dir, marking_box.x2, marking_box.y2);
990         if (Single_axis_constraint)     {
991 //              if (fvi_ray_plane(&int_pnt, &obj, &view_orient.fvec, &view_pos, &cursor_dir, 0.0f) >= 0.0f )    {
992 //                      vm_vec_add(&p1, &obj, &Constraint);
993 //                      find_nearest_point_on_line(&nearest_point, &obj, &p1, &int_pnt);
994 //                      int_pnt = nearest_point;
995 //                      distance_moved = vm_vec_dist(&obj, &int_pnt);
996 //              }
997
998                 vector tmpAnticonstraint = Anticonstraint;
999                 vector tmpObject = obj;
1000
1001                 tmpAnticonstraint.x = 0.0f;
1002                 r = fvi_ray_plane(&int_pnt, &tmpObject, &tmpAnticonstraint, &view_pos, &cursor_dir, 0.0f);
1003
1004                 //      If intersected behind viewer, don't move.  Too confusing, not what user wants.
1005                 vm_vec_sub(&vec1, &int_pnt, &view_pos);
1006                 vm_vec_sub(&vec2, &obj, &view_pos);
1007                 if ((r>=0.0f) && (vm_vec_dot(&vec1, &vec2) >= 0.0f))    {
1008                         vector tmp1;
1009                         vm_vec_sub( &tmp1, &int_pnt, &obj );
1010                         tmp1.x *= Constraint.x;
1011                         tmp1.y *= Constraint.y;
1012                         tmp1.z *= Constraint.z;
1013                         vm_vec_add( &int_pnt, &obj, &tmp1 );
1014                                 
1015                         distance_moved = vm_vec_dist(&obj, &int_pnt);
1016                 }
1017
1018
1019         } else {  // Move in x-z plane, defined by grid.  Preserve height.
1020                 r = fvi_ray_plane(&int_pnt, &obj, &Anticonstraint, &view_pos, &cursor_dir, 0.0f);
1021
1022                 //      If intersected behind viewer, don't move.  Too confusing, not what user wants.
1023                 vm_vec_sub(&vec1, &int_pnt, &view_pos);
1024                 vm_vec_sub(&vec2, &obj, &view_pos);
1025                 if ((r>=0.0f) && (vm_vec_dot(&vec1, &vec2) >= 0.0f))
1026                         distance_moved = vm_vec_dist(&obj, &int_pnt);
1027         }
1028
1029         //      If moved too far, then move max distance along vector.
1030         vm_vec_sub(&movement_vector, &int_pnt, &obj);
1031 /*      if (distance_moved > MAX_MOVE_DISTANCE) {
1032                 vm_vec_normalize(&movement_vector);
1033                 vm_vec_scale(&movement_vector, MAX_MOVE_DISTANCE);
1034         } */
1035
1036         if (distance_moved) {
1037                 objp = GET_FIRST(&obj_used_list);
1038                 while (objp != END_OF_LIST(&obj_used_list))     {
1039                         SDL_assert(objp->type != OBJ_NONE);
1040                         if (objp->flags & OF_MARKED) {
1041                                 vm_vec_add(&objp->pos, &objp->pos, &movement_vector);
1042                                 if (objp->type == OBJ_WAYPOINT) {
1043                                         Waypoint_lists[objp->instance / 65536].waypoints[objp->instance & 0xffff] =
1044                                                 objp->pos;
1045                                 }
1046                         }
1047
1048                         objp = GET_NEXT(objp);
1049                 }
1050
1051                 objp = GET_FIRST(&obj_used_list);
1052                 while (objp != END_OF_LIST(&obj_used_list)) {
1053                         if (objp->flags & OF_MARKED)
1054                                 object_moved(objp);
1055
1056                         objp = GET_NEXT(objp);
1057                 }
1058         }
1059
1060         if (Briefing_dialog)
1061                 Briefing_dialog->update_positions();
1062
1063         set_modified();
1064         return rval;
1065 }
1066
1067 void drag_rotate_save_backup()
1068 {
1069         object *objp;
1070
1071         /*
1072         if (Cur_bitmap != -1)
1073                 bitmap_matrix_backup = Starfield_bitmaps[Cur_bitmap].m;
1074                 */
1075
1076         objp = GET_FIRST(&obj_used_list);
1077         while (objp != END_OF_LIST(&obj_used_list))                     {
1078                 SDL_assert(objp->type != OBJ_NONE);
1079                 if (objp->flags & OF_MARKED)    {
1080                         rotation_backup[OBJ_INDEX(objp)].pos = objp->pos;
1081                         rotation_backup[OBJ_INDEX(objp)].orient = objp->orient;
1082                 }
1083
1084                 objp = GET_NEXT(objp);
1085         }
1086 }
1087
1088 int drag_rotate_objects()
1089 {
1090         int rval = 1;   
1091         vector int_pnt, obj;
1092         angles a;
1093         matrix leader_orient, leader_transpose, tmp, newmat, rotmat;
1094         object *leader, *objp;
1095         // starfield_bitmaps *bmp;
1096
1097         Update_window = 1;
1098         /*
1099         if (Bg_bitmap_dialog) {
1100                 if (Cur_bitmap < 0)
1101                         return -1;
1102
1103                 bmp = &Starfield_bitmaps[Cur_bitmap];
1104                 calculate_bitmap_points(bmp, mouse_dx / -300.0f);
1105                 return rval;
1106         }
1107         */
1108
1109         if (!query_valid_object()){
1110                 return -1;
1111         }
1112
1113         objp = &Objects[cur_object_index];
1114         SDL_assert(objp->type != OBJ_NONE);
1115         obj = int_pnt = objp->pos;
1116
1117         memset(&a, 0, sizeof(angles));
1118         if (Single_axis_constraint) {
1119                 if (Constraint.x)
1120                         a.p = mouse_dy / REDUCER;
1121                 else if (Constraint.y)
1122                         a.h = mouse_dx / REDUCER;
1123                 else if (Constraint.z)
1124                         a.b = -mouse_dx / REDUCER;
1125
1126         } else {
1127                 if (!Constraint.x) {                            // yz
1128                         a.b = -mouse_dx / REDUCER;
1129                         a.h = mouse_dy / REDUCER;
1130                 } else if (!Constraint.y) {     // xz
1131                         a.p = mouse_dy / REDUCER;
1132                         a.b = -mouse_dx / REDUCER;
1133                 } else if (!Constraint.z) {     // xy
1134                         a.p = mouse_dy / REDUCER;
1135                         a.h = mouse_dx / REDUCER;
1136                 }
1137         }
1138
1139         leader = &Objects[cur_object_index];
1140         leader_orient = leader->orient;                 // save original orientation
1141         vm_copy_transpose_matrix(&leader_transpose, &leader_orient);
1142
1143         vm_angles_2_matrix(&rotmat, &a);
1144         vm_matrix_x_matrix(&newmat, &leader->orient, &rotmat);
1145         leader->orient = newmat;
1146
1147         objp = GET_FIRST(&obj_used_list);
1148         while (objp != END_OF_LIST(&obj_used_list))                     {
1149                 SDL_assert(objp->type != OBJ_NONE);
1150                 if ((objp->flags & OF_MARKED) && (cur_object_index != OBJ_INDEX(objp) )) {
1151                         if (Group_rotate) {
1152                                 matrix rot_trans;
1153                                 vector tmpv1, tmpv2;
1154
1155                                 // change rotation matrix to rotate in opposite direction.  This rotation
1156                                 // matrix is what the leader ship has rotated by.
1157                                 vm_copy_transpose_matrix(&rot_trans, &rotmat);
1158
1159                                 // get point relative to our point of rotation (make POR the origin).
1160                                 vm_vec_sub(&tmpv1, &objp->pos, &leader->pos);
1161
1162                                 // convert point from real-world coordinates to leader's relative coordinate
1163                                 // system (z=forward vec, y=up vec, x=right vec
1164                                 vm_vec_rotate(&tmpv2, &tmpv1, &leader_orient);
1165
1166                                 // now rotate the point by the transpose from above.
1167                                 vm_vec_rotate(&tmpv1, &tmpv2, &rot_trans);
1168
1169                                 // convert point back into real-world coordinates
1170                                 vm_vec_rotate(&tmpv2, &tmpv1, &leader_transpose);
1171
1172                                 // and move origin back to real-world origin.  Object is now at it's correct
1173                                 // position.
1174                                 vm_vec_add(&objp->pos, &leader->pos, &tmpv2);
1175
1176                                 // Now fix the object's orientation to what it should be.
1177                                 vm_matrix_x_matrix(&tmp, &objp->orient, &rotmat);
1178                                 vm_orthogonalize_matrix(&tmp);  // safety check
1179                                 objp->orient = tmp;
1180
1181                         } else {
1182                                 vm_matrix_x_matrix(&tmp, &objp->orient, &rotmat);
1183                                 objp->orient = tmp;
1184                         }
1185                 }
1186                 
1187                 objp = GET_NEXT(objp);
1188         }
1189
1190         objp = GET_FIRST(&obj_used_list);
1191         while (objp != END_OF_LIST(&obj_used_list)) {
1192                 if (objp->flags & OF_MARKED)
1193                         object_moved(objp);
1194
1195                 objp = GET_NEXT(objp);
1196         }
1197
1198         set_modified();
1199         return rval;
1200 }
1201
1202 void cancel_drag()
1203 {
1204         Update_window = 1;
1205
1206         /*
1207         if (Bg_bitmap_dialog) {
1208                 SDL_assert(!vm_check_matrix_for_zeros(&bitmap_matrix_backup));
1209                 Starfield_bitmaps[Cur_bitmap].m = bitmap_matrix_backup;
1210                 calculate_bitmap_points(&Starfield_bitmaps[Cur_bitmap], 0.0f);
1211                 button_down = box_marking = 0;
1212                 Update_window = 1;
1213                 return;
1214         }
1215         */
1216
1217         if (button_down) {
1218                 if (Editing_mode == 1) {
1219                         vector movement_vector;
1220                         object *objp;
1221
1222                         if (query_valid_object()) {
1223                                 objp = &Objects[cur_object_index];
1224                                 SDL_assert(objp->type != OBJ_NONE);
1225                                 vm_vec_sub(&movement_vector, &original_pos, &objp->pos);
1226
1227                                 objp = GET_FIRST(&obj_used_list);
1228                                 while (objp != END_OF_LIST(&obj_used_list))     {
1229                                         SDL_assert(objp->type != OBJ_NONE);
1230                                         if (objp->flags & OF_MARKED)
1231                                                 vm_vec_add(&objp->pos, &objp->pos, &movement_vector);
1232
1233                                         objp = GET_NEXT(objp);
1234                                 }
1235                         }
1236
1237                 } else if (Editing_mode == 2) {
1238                         object *objp;
1239
1240                         objp = GET_FIRST(&obj_used_list);
1241                         while (objp != END_OF_LIST(&obj_used_list))     {
1242                                 SDL_assert(objp->type != OBJ_NONE);
1243                                 if (objp->flags & OF_MARKED) {
1244                                         int obj_index = OBJ_INDEX(objp);
1245
1246                                         if(!IS_VEC_NULL(&rotation_backup[obj_index].orient.rvec) && 
1247                                                 !IS_VEC_NULL(&rotation_backup[obj_index].orient.uvec) && 
1248                                                 !IS_VEC_NULL(&rotation_backup[obj_index].orient.fvec)){
1249
1250                                                 objp->pos = rotation_backup[obj_index].pos;
1251                                                 objp->orient = rotation_backup[obj_index].orient;
1252                                         }
1253                                 }
1254
1255                                 objp = GET_NEXT(objp);
1256                         }
1257                 }
1258         }
1259
1260         button_down = box_marking = 0;
1261         if (Briefing_dialog)
1262                 Briefing_dialog->update_positions();
1263 }
1264
1265 void CFREDView::OnLButtonDown(UINT nFlags, CPoint point) 
1266 {
1267         int list = -1;
1268         
1269         if (!Fred_active) {
1270                 CView::OnLButtonDown(nFlags, point);
1271                 return;
1272         }
1273
1274         if (cur_waypoint != -1)
1275                 list = cur_waypoint_list * 65536 + cur_waypoint;
1276
1277         marking_box.x1 = point.x;
1278         marking_box.y1 = point.y;
1279         Dup_drag = 0;
1280         
1281         on_object = select_object(point.x, point.y);
1282         button_down = 1;
1283         SetCapture();
1284         drag_rotate_save_backup();
1285         
1286         if (nFlags & MK_CONTROL)        {  // add a new object
1287                 if (!Bg_bitmap_dialog) {
1288                         if (on_object == -1) {
1289                                 Selection_lock = 0;  // force off selection lock
1290                                 on_object = create_object_on_grid(list);
1291
1292                         } else
1293                                 Dup_drag = 1;
1294
1295                 } else {
1296                         /*
1297                         Selection_lock = 0;  // force off selection lock
1298                         on_object = Cur_bitmap = create_bg_bitmap();
1299                         Bg_bitmap_dialog->update_data();
1300                         Update_window = 1;
1301                         if (Cur_bitmap == -1)
1302                                 MessageBox("Background bitmap limit reached.\nCan't add more.");
1303                         */
1304                 }
1305
1306         } else if (!Selection_lock) {
1307                 if (Bg_bitmap_dialog) {
1308                         Cur_bitmap = on_object;
1309                         Bg_bitmap_dialog -> update_data();
1310
1311                 } else if ((nFlags & MK_SHIFT) || (on_object == -1) || !(Objects[on_object].flags & OF_MARKED)) {
1312                         if (!(nFlags & MK_SHIFT))
1313                                 unmark_all();
1314
1315                         if (on_object != -1) {
1316                                 if (Objects[on_object].flags & OF_MARKED)
1317                                         unmark_object(on_object);
1318                                 else
1319                                         mark_object(on_object);
1320                         }
1321                 }
1322         }
1323
1324         if (query_valid_object())
1325                 original_pos = Objects[cur_object_index].pos;
1326
1327         moved = 0;
1328         if (Selection_lock) {
1329                 if (Editing_mode == 1)
1330                         drag_objects();
1331                 else if (Editing_mode == 2)
1332                         drag_rotate_objects();
1333
1334                 Update_window = 1;
1335         }
1336
1337         if (query_valid_object() && (Marked == 1) && (Objects[cur_object_index].type == OBJ_POINT)) {
1338                 SDL_assert(Briefing_dialog);
1339                 Briefing_dialog->icon_select(Objects[cur_object_index].instance);
1340
1341         } else {
1342                 if (Briefing_dialog)
1343                         Briefing_dialog->icon_select(-1);
1344         }
1345
1346         CView::OnLButtonDown(nFlags, point);
1347 }
1348
1349 void CFREDView::OnMouseMove(UINT nFlags, CPoint point) 
1350 {
1351         mouse_dx = point.x - last_mouse_x;
1352         mouse_dy = point.y - last_mouse_y;
1353         last_mouse_x = marking_box.x2 = point.x;
1354         last_mouse_y = marking_box.y2 = point.y;
1355         Cursor_over = select_object(point.x, point.y);
1356
1357         if (!(nFlags & MK_LBUTTON))
1358                 button_down = 0;
1359
1360         // The following will cancel a drag operation if another program running in memory
1361         // happens to jump in and take over (such as new email has arrived popup boxes).
1362         if (button_down && GetCapture() != this)
1363                 cancel_drag();
1364
1365         if (!button_down && GetCapture() == this)
1366                 ReleaseCapture();
1367
1368         if (button_down) {
1369                 if (abs(marking_box.x1 - marking_box.x2) > 1 || abs(marking_box.y1 - marking_box.y2) > 1)
1370                         moved = 1;
1371
1372                 if (moved) {
1373                         if (on_object != -1 || Selection_lock) {
1374                                 if (Editing_mode == 1)
1375                                         drag_objects();
1376                                 else if (Editing_mode == 2)
1377                                         drag_rotate_objects();
1378
1379                         } else if (!Bg_bitmap_dialog)
1380                                 box_marking = 1;
1381
1382                         if (mouse_dx || mouse_dy)
1383                                 Update_window = 1;
1384                 }
1385         }
1386
1387         CView::OnMouseMove(nFlags, point);
1388 }
1389
1390 void CFREDView::OnLButtonUp(UINT nFlags, CPoint point) 
1391 {
1392         marking_box.x2 = point.x;
1393         marking_box.y2 = point.y;
1394
1395         if (button_down && GetCapture() != this)
1396                 cancel_drag();
1397
1398         if (GetCapture() == this)
1399                 ReleaseCapture();
1400
1401         if (button_down) {
1402                 if ((abs(marking_box.x1 - marking_box.x2) > 1) || (abs(marking_box.y1 - marking_box.y2) > 1))
1403                         moved = 1;
1404
1405                 if (moved) {
1406                         if ((on_object != -1) || Selection_lock) {
1407                                 if (Editing_mode == 1)
1408                                         drag_objects();
1409                                 else if ((Editing_mode == 2) || (Editing_mode == 3))
1410                                         drag_rotate_objects();
1411
1412                                 set_modified();
1413                                 FREDDoc_ptr->autosave("object move");
1414
1415                         } else
1416                                 box_marking = 1;
1417                 }
1418
1419                 if (Bg_bitmap_dialog) {
1420                         box_marking = 0;
1421
1422                 } else {
1423                         if (box_marking) {
1424                                 select_objects();
1425                                 box_marking = 0;
1426
1427                         } else if ((!moved && on_object != -1) && !Selection_lock && !(nFlags & MK_SHIFT)) {
1428                                 unmark_all();
1429                                 mark_object(on_object);
1430                         }
1431                 }
1432         
1433                 button_down = 0;
1434                 Update_window = 1;
1435                 if (Dup_drag == DUP_DRAG_OF_WING) {
1436                         char msg[256];
1437                         int ship;
1438                         object *objp;
1439
1440                         sprintf(msg, "Add cloned ships to wing %s?", Wings[Duped_wing].name);
1441                         if (MessageBox(msg, "Query", MB_YESNO) == IDYES) {
1442                                 objp = GET_FIRST(&obj_used_list);
1443                                 while (objp != END_OF_LIST(&obj_used_list))     {
1444                                         if (objp->flags & OF_MARKED) {
1445                                                 if (Wings[Duped_wing].wave_count >= MAX_SHIPS_PER_WING) {
1446                                                         MessageBox("Max ships per wing limit reached");
1447                                                         break;
1448                                                 }
1449
1450 // Can't do player starts, since only player 1 is currently allowed to be in a wing
1451
1452                                                 SDL_assert(objp->type == OBJ_SHIP);
1453                                                 ship = objp->instance;
1454                                                 SDL_assert(Ships[ship].wingnum == -1);
1455                                                 sprintf(Ships[ship].ship_name, "%s %d", Wings[Duped_wing].name,
1456                                                         Wings[Duped_wing].wave_count + 1);
1457
1458                                                 Wings[Duped_wing].ship_index[Wings[Duped_wing].wave_count] = ship;
1459                                                 Ships[ship].wingnum = Duped_wing;
1460
1461                                                 wing_objects[Duped_wing][Wings[Duped_wing].wave_count] = OBJ_INDEX(objp);
1462                                                 Wings[Duped_wing].wave_count++;
1463                                         }
1464
1465                                         objp = GET_NEXT(objp);
1466                                 }
1467                         }
1468                 }
1469         }
1470
1471         if (query_valid_object() && (Marked == 1) && (Objects[cur_object_index].type == OBJ_POINT)) {
1472                 SDL_assert(Briefing_dialog);
1473                 Briefing_dialog->icon_select(Objects[cur_object_index].instance);
1474
1475         } else {
1476                 if (Briefing_dialog)
1477                         Briefing_dialog->icon_select(-1);
1478         }
1479
1480         CView::OnLButtonUp(nFlags, point);
1481 }
1482
1483 //      This function never gets called because nothing causes
1484 //      the WM_GOODBYE event to occur.
1485 // False! When you close the Ship Dialog, this function is called! --MK, 8/30/96
1486 LONG CFREDView::OnGoodbye(UINT wParam, LONG lParam)
1487 {
1488         Ship_editor_dialog.DestroyWindow();
1489         Wing_editor_dialog.DestroyWindow();
1490
1491         return 0L;
1492 }
1493
1494 void CFREDView::OnEditorsShips() 
1495 {
1496         int adjust = 0;
1497
1498         SDL_assert(Ship_editor_dialog.GetSafeHwnd());
1499         if (!Show_sexp_help)
1500                 adjust = -SEXP_HELP_BOX_SIZE;
1501
1502         if (!theApp.init_window(&Ship_wnd_data, &Ship_editor_dialog, adjust))
1503                 return;
1504
1505         Ship_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0,
1506                 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1507         Ship_editor_dialog.ShowWindow(SW_RESTORE);
1508 }
1509
1510 void CFREDView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT lParam)
1511 {
1512         uint lKeyData;
1513
1514         lKeyData = lParam & 255;          // key data 
1515         if (lParam & 256) lKeyData += 0x80;
1516         key_mark(lKeyData, 1, 0);
1517         
1518         CView::OnKeyDown(nChar, nRepCnt, lParam);
1519 }
1520
1521 void CFREDView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT lParam) 
1522 {
1523         uint lKeyData;
1524
1525         lKeyData = lParam & 255;          // key data 
1526         if (lParam & 256) lKeyData += 0x80;
1527         key_mark(lKeyData, 0, 0);
1528
1529         CView::OnKeyUp(nChar, nRepCnt, lParam);
1530 }
1531
1532 void CFREDView::OnSetFocus(CWnd* pOldWnd) 
1533 {
1534         static int flag = 0;
1535
1536         if (flag)
1537                 return;
1538
1539         flag = 1;
1540         nprintf(("Fred routing", "OnSetFocus() called\n"));
1541         if (Update_ship) {
1542                 Ship_editor_dialog.initialize_data(1);
1543                 Update_ship = 0;
1544         }
1545
1546         if (Update_wing) {
1547                 Wing_editor_dialog.initialize_data(1);
1548                 Update_wing = 0;
1549         }
1550
1551 /*      if (Wing_editor_dialog.verify() == -1)
1552                 return;  // abort
1553
1554         if (Ship_editor_dialog.verify() == -1)
1555                 return;  // abort*/
1556
1557         if (update_dialog_boxes()) {
1558                 nprintf(("Fred routing", "OnSetFocus() returned (error occured)\n"));
1559                 flag = 0;
1560                 Ship_editor_dialog.bypass_errors = 0;
1561                 Wing_editor_dialog.bypass_errors = 0;
1562                 return;  // abort
1563         }
1564
1565         if (Local_modified) {
1566                 FREDDoc_ptr->autosave("Editing");
1567                 Local_modified = 0;
1568         }
1569
1570         Fred_active = 1;
1571         CView::OnSetFocus(pOldWnd);
1572         nprintf(("Fred routing", "Main window focus accepted\n"));
1573         key_got_focus();
1574
1575         Cursor_over = -1;
1576         if (GetActiveWindow() != Fred_main_wnd) {
1577                 Fred_main_wnd->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
1578                 nprintf(("Fred routing", "OnSetFocus() had to put main window back on top\n"));
1579         }
1580
1581         flag = 0;
1582 }
1583
1584 void CFREDView::OnKillFocus(CWnd* pNewWnd) 
1585 {
1586         nprintf(("Fred routing", "OnKillFocus() called\n"));
1587         Fred_active = 0;
1588         Local_modified = 0;
1589         CView::OnKillFocus(pNewWnd);
1590         key_lost_focus();
1591         Cursor_over = -1;
1592 }
1593
1594 void CFREDView::OnSize(UINT nType, int cx, int cy) 
1595 {
1596         CView::OnSize(nType, cx, cy);
1597         
1598         if ((cx > 0) && (cy > 0)) {
1599                 gr_init(GR_640, GR_SOFTWARE, 8, cx, cy);
1600         }
1601 }
1602
1603 void do_trackball_stuff(int nFlags, CPoint point)
1604 {
1605         int     btn = 0;
1606
1607         if (nFlags & MK_LBUTTON){
1608                 btn |= 1;
1609         }
1610         if (nFlags & MK_RBUTTON){
1611                 btn |= 2;
1612         }
1613
1614         move_mouse(btn, point.x, point.y);
1615 }
1616
1617 //      If add_flag != 0, then add found objects to current wing, else create new wing.
1618 void select_objects()
1619 {
1620         int     x, y, valid, icon_mode = 0;             
1621         vertex  v;
1622         object  *ptr;
1623
1624         if (marking_box.x1 > marking_box.x2) {
1625                 x = marking_box.x1;
1626                 marking_box.x1 = marking_box.x2;
1627                 marking_box.x2 = x;
1628         }
1629
1630         if (marking_box.y1 > marking_box.y2) {
1631                 y = marking_box.y1;
1632                 marking_box.y1 = marking_box.y2;
1633                 marking_box.y2 = y;
1634         }
1635
1636         ptr = GET_FIRST(&obj_used_list);
1637         while (ptr != END_OF_LIST(&obj_used_list)) {
1638                 valid = 1;
1639                 if (ptr->flags & OF_HIDDEN)
1640                         valid = 0;
1641
1642                 SDL_assert(ptr->type != OBJ_NONE);
1643                 switch (ptr->type) {
1644                         case OBJ_WAYPOINT:
1645                                 if (!Show_waypoints)
1646                                         valid = 0;
1647                                 break;
1648
1649                         case OBJ_START:
1650                                 if (!Show_starts)
1651                                         valid = 0;
1652                                 break;
1653
1654                         case OBJ_SHIP:
1655                                 if (!Show_ships)
1656                                         valid = 0;
1657                                 switch (Ships[ptr->instance].team) {
1658                                         case TEAM_FRIENDLY:
1659                                                 if (!Show_friendly)
1660                                                         valid = 0;
1661                                                 break;
1662
1663                                         case TEAM_HOSTILE:
1664                                                 if (!Show_hostile)
1665                                                         valid = 0;
1666                                                 break;
1667
1668                                         case TEAM_NEUTRAL:
1669                                                 if (!Show_neutral)
1670                                                         valid = 0;
1671                                                 break;
1672                                 }
1673
1674                                 break;
1675                 }
1676
1677                 g3_rotate_vertex(&v, &ptr->pos);
1678                 if (!(v.codes & CC_BEHIND) && valid)
1679                         if (!(g3_project_vertex(&v) & PF_OVERFLOW)) {
1680                                 x = (int) v.sx;
1681                                 y = (int) v.sy;
1682
1683                                 if (x >= marking_box.x1 && x <= marking_box.x2 && y >= marking_box.y1 && y <= marking_box.y2) {
1684                                         if (ptr->flags & OF_MARKED)
1685                                                 unmark_object(OBJ_INDEX(ptr));
1686                                         else
1687                                                 mark_object(OBJ_INDEX(ptr));
1688
1689                                         if (ptr->type == OBJ_POINT)
1690                                                 icon_mode = 1;
1691                                 }
1692                         }
1693                 
1694                 ptr = GET_NEXT(ptr);
1695         }
1696
1697         if (icon_mode) {
1698                 ptr = GET_FIRST(&obj_used_list);
1699                 while (ptr != END_OF_LIST(&obj_used_list)) {
1700                         if ((ptr->flags & OF_MARKED) && (ptr->type != OBJ_POINT))
1701                                 unmark_object(OBJ_INDEX(ptr));
1702
1703                         ptr = GET_NEXT(ptr);
1704                 }
1705         }
1706
1707         Update_ship = Update_wing = 1;
1708 }
1709
1710 LONG CFREDView::OnMenuPopupShips(UINT wParam, LONG lParam)
1711 {
1712         CMenu   menu;
1713         CPoint  point;
1714
1715         point = * ((CPoint*) lParam);
1716         
1717         ClientToScreen(&point);
1718
1719         menu.LoadMenu(IDR_MENU_SHIP_POPUP);
1720         menu.GetSubMenu(0)->TrackPopupMenu(
1721                 TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
1722
1723         return 0L;
1724 }
1725
1726 LONG CFREDView::OnMenuPopupEdit(UINT wParam, LONG lParam)
1727 {
1728         CMenu   menu;
1729         CPoint  point;
1730
1731         point = * ((CPoint*) lParam);
1732         
1733         ClientToScreen(&point);
1734
1735         menu.LoadMenu(IDR_MENU_EDIT_POPUP);
1736         menu.GetSubMenu(0)->TrackPopupMenu(
1737                 TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
1738
1739         return 0L;
1740 }
1741
1742 int     g_Ships_as_icons = 0;
1743
1744 void CFREDView::OnMiscstuffShowshipsasicons() 
1745 {
1746         Update_window = 1;
1747         if (g_Ships_as_icons == 0)
1748                 g_Ships_as_icons = 1;
1749         else
1750                 g_Ships_as_icons = 0;
1751 }
1752
1753 // right mouse button popup menu stuff
1754 void CFREDView::OnContextMenu(CWnd* /*pWnd*/, CPoint point) 
1755 {
1756         // make sure window is active
1757 //      GetParentFrame()->ActivateFrame();
1758
1759         CMenu menu;
1760         int     objnum;
1761         CPoint local = point;
1762
1763         if (button_down) {
1764                 cancel_drag();
1765                 return;
1766         }
1767
1768         ScreenToClient(&local);
1769         objnum = select_object(local.x, local.y);
1770
1771         if (objnum != -1) {
1772                 set_cur_object_index(objnum);
1773                 if (menu.LoadMenu(IDR_MENU_SHIP_POPUP)) {
1774                         int id = ID_EDITORS_SHIPS;
1775                         CMenu* pPopup = menu.GetSubMenu(0);
1776
1777                         ASSERT(pPopup != NULL);
1778                         if (Marked > 1)
1779                                 pPopup->ModifyMenu(ID_EDITORS_SHIPS, MF_BYCOMMAND | MF_STRING, ID_EDITORS_SHIPS, "Edit Marked Ships");
1780                         else {
1781                                 CString str;
1782
1783                                 if ((Objects[objnum].type == OBJ_START) || (Objects[objnum].type == OBJ_SHIP))
1784                                         str.Format("Edit %s", Ships[Objects[objnum].instance].ship_name);
1785
1786                                 else if (Objects[objnum].type == OBJ_JUMP_NODE) {
1787                                         id = ID_EDITORS_WAYPOINT;
1788                                         str.Format("Edit %s", Jump_nodes[Objects[objnum].instance].name);
1789
1790                                 } else if (Objects[objnum].type == OBJ_WAYPOINT) {
1791                                         id = ID_EDITORS_WAYPOINT;
1792                                         str.Format("Edit %s:%d", Waypoint_lists[Objects[objnum].instance / 65536].name,
1793                                                 (Objects[objnum].instance & 0xffff) + 1);
1794
1795                                 } else if (Objects[objnum].type == OBJ_POINT) {
1796                                         return;
1797
1798                                 } else {
1799                                         Int3();
1800                                         str = _T("Unknown");
1801                                 }
1802
1803                                 pPopup->ModifyMenu(ID_EDITORS_SHIPS, MF_BYCOMMAND | MF_STRING, id, str);
1804                         }
1805
1806                         pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd()); // use main window for cmds
1807                 }
1808
1809         } else {
1810                 if (menu.LoadMenu(IDR_MENU_EDIT_POPUP)) {
1811                         int i;
1812                         CMenu* pPopup = menu.GetSubMenu(0);
1813                         CMenu shipPopup, player_submenu, species_submenu[MAX_SPECIES_NAMES];
1814                         ASSERT(pPopup != NULL);
1815
1816                         // create a popup menu based on the ship models read in ship.cpp.
1817                         shipPopup.CreatePopupMenu();
1818                         shipPopup.AppendMenu(MF_STRING | MF_ENABLED, SHIP_TYPES + Id_select_type_waypoint, "Waypoint");
1819                         shipPopup.AppendMenu(MF_STRING | MF_ENABLED, SHIP_TYPES + Id_select_type_start, "Player Start");
1820                         shipPopup.AppendMenu(MF_STRING | MF_ENABLED, SHIP_TYPES + Id_select_type_jump_node, "Jump Node");
1821                         for (i=0; i<MAX_SPECIES_NAMES; i++) {
1822                                 species_submenu[i].CreatePopupMenu();
1823                                 shipPopup.AppendMenu(MF_STRING | MF_POPUP | MF_ENABLED,
1824                                         (UINT) species_submenu[i].m_hMenu, Species_names[i]);
1825                         }
1826
1827                         for (i=0; i<Num_ship_types; i++)
1828                                 species_submenu[Ship_info[i].species].AppendMenu(MF_STRING |
1829                                         MF_ENABLED, SHIP_TYPES + i, Ship_info[i].name);
1830
1831                         pPopup->AppendMenu(MF_STRING | MF_POPUP | MF_ENABLED,
1832                                 (UINT) shipPopup.m_hMenu, "New Object Type");
1833
1834                         CWnd::DrawMenuBar();    // AppendMenu documentation says to do this.
1835                         pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd());
1836                 }
1837         }
1838 }
1839
1840 void CFREDView::OnEditPopupShowShipIcons() 
1841 {
1842         Show_ship_info = !Show_ship_info;
1843         theApp.write_ini_file();
1844         Update_window = 1;
1845 }
1846
1847 void CFREDView::OnUpdateEditPopupShowShipIcons(CCmdUI* pCmdUI) 
1848 {
1849         pCmdUI->SetCheck(Show_ship_info);
1850 }
1851
1852 void CFREDView::OnEditPopupShowShipModels() 
1853 {
1854         Show_ship_models = !Show_ship_models;
1855         theApp.write_ini_file();
1856         Update_window = 1;
1857 }
1858
1859 void CFREDView::OnUpdateEditPopupShowShipModels(CCmdUI* pCmdUI) 
1860 {
1861         pCmdUI->SetCheck(Show_ship_models);
1862 }
1863
1864 void CFREDView::OnEditPopupShowCompass() 
1865 {
1866         Show_compass = !Show_compass;
1867         theApp.write_ini_file();
1868         Update_window = 1;
1869 }
1870
1871 void CFREDView::OnUpdateEditPopupShowCompass(CCmdUI* pCmdUI) 
1872 {
1873         pCmdUI->SetCheck(Show_compass);
1874 }
1875
1876 // View implementation file
1877 CFREDView *CFREDView::GetView()
1878 {
1879   CFrameWnd *pFrame = (CFrameWnd *) (AfxGetApp()->m_pMainWnd);
1880
1881   CView *pView = pFrame->GetActiveView();
1882
1883   if (!pView)
1884      return NULL;
1885
1886   // Fail if view is of wrong kind
1887   // (this could occur with splitter windows, or additional
1888   // views on a single document
1889   if (! pView->IsKindOf(RUNTIME_CLASS(CFREDView)))
1890      return NULL;
1891
1892   return (CFREDView *) pView;
1893 }
1894
1895 /*void CFREDView::OnShipType0() 
1896 {
1897         cur_model_index = 1;
1898 }
1899
1900 void CFREDView::OnShipType1() 
1901 {
1902         cur_model_index = 2;
1903 }
1904
1905 void CFREDView::OnShipType2() 
1906 {
1907         cur_model_index = 3;
1908 }
1909
1910 void CFREDView::OnShipType3() 
1911 {
1912         cur_model_index = 4;
1913 }
1914
1915 void CFREDView::OnShipType4() 
1916 {
1917         cur_model_index = 5;
1918 }
1919
1920 void CFREDView::OnShipType5() 
1921 {
1922         cur_model_index = 6;
1923 }
1924
1925 void CFREDView::OnUpdateShipType1(CCmdUI* pCmdUI) 
1926 {
1927         pCmdUI->SetCheck(cur_model_index == 2);
1928 }
1929
1930 void CFREDView::OnUpdateShipType2(CCmdUI* pCmdUI) 
1931 {
1932         pCmdUI->SetCheck(cur_model_index == 3);
1933 }
1934
1935 void CFREDView::OnUpdateShipType3(CCmdUI* pCmdUI) 
1936 {
1937         pCmdUI->SetCheck(cur_model_index == 4);
1938 }
1939
1940 void CFREDView::OnUpdateShipType4(CCmdUI* pCmdUI) 
1941 {
1942         pCmdUI->SetCheck(cur_model_index == 5);
1943 }
1944
1945 void CFREDView::OnUpdateShipType5(CCmdUI* pCmdUI) 
1946 {
1947         pCmdUI->SetCheck(cur_model_index == 6);
1948 }
1949
1950
1951 void CFREDView::OnUpdateShipType0(CCmdUI* pCmdUI) 
1952 {
1953         pCmdUI->SetCheck(cur_model_index == 1);
1954         
1955 }
1956
1957 void CFREDView::OnEditShipType6() 
1958 {
1959         cur_model_index = 7;
1960         
1961 }
1962
1963 void CFREDView::OnUpdateEditShipType6(CCmdUI* pCmdUI) 
1964 {
1965         pCmdUI->SetCheck(cur_model_index == 7);
1966 } */
1967
1968
1969 // following code added by MWA 09/04/96
1970 // Implements messages for popup menu built on the fly.
1971 // not sure how stable the code is, but appears to work for now.
1972 // id's for the menu items are simply the model numbers for
1973 // the ships.  Shouldn't conflict with any other ID_* thingys.
1974 BOOL CFREDView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) 
1975 {
1976         int id = (int) nID;
1977
1978         if (!pHandlerInfo) {
1979                 if ((id >= SHIP_TYPES) && (id < SHIP_TYPES + Num_ship_types + 3)) {
1980                         if (nCode == CN_COMMAND) {
1981                                 cur_model_index = id - SHIP_TYPES;
1982                                 m_new_ship_type_combo_box.SetCurSelNEW(cur_model_index);
1983
1984                         } else if (nCode == CN_UPDATE_COMMAND_UI)       {
1985                                 // Update UI element state
1986                                 ((CCmdUI*) pExtra)->SetCheck(cur_model_index + SHIP_TYPES == id);
1987                                 ((CCmdUI*) pExtra)->Enable(TRUE);
1988                         }
1989
1990                         return TRUE;
1991                 }
1992         }
1993
1994         return CView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
1995 }
1996
1997 void CFREDView::OnMiscStatistics() 
1998 {
1999         char buf[2048];
2000
2001         sprintf(buf,
2002                 "Number of Objects: %d\n"
2003                 "Number of Ships:   %d\n"
2004                 "Number of Wings:   %d\n",
2005                 num_objects, ship_get_num_ships(), num_wings);
2006
2007         MessageBox(buf, "FRED Statistics");
2008 }
2009
2010 void CFREDView::OnUpdateChangeViewpointExternal(CCmdUI* pCmdUI) 
2011 {
2012         pCmdUI->SetCheck(!viewpoint);
2013 }
2014
2015 void CFREDView::OnChangeViewpointExternal() 
2016 {
2017         viewpoint = 0;
2018         Update_window = 1;
2019 }
2020
2021 void CFREDView::OnUpdateChangeViewpointFollow(CCmdUI* pCmdUI) 
2022 {
2023         pCmdUI->SetCheck(viewpoint == 1);
2024 }
2025
2026 void CFREDView::OnChangeViewpointFollow() 
2027 {
2028         viewpoint = 1;
2029         view_obj = cur_object_index;
2030         Update_window = 1;
2031 }
2032
2033 void CFREDView::OnEditorsGoals()
2034 {
2035         CMissionGoalsDlg dlg;
2036
2037         dlg.DoModal();
2038 }
2039
2040 void CFREDView::OnSpeed1() 
2041 {
2042         physics_speed = 1;
2043         set_physics_controls();
2044 }
2045
2046 void CFREDView::OnSpeed2() 
2047 {
2048         physics_speed = 2;
2049         set_physics_controls();
2050 }
2051
2052 void CFREDView::OnSpeed3() 
2053 {
2054         physics_speed = 3;
2055         set_physics_controls();
2056 }
2057
2058 void CFREDView::OnSpeed5() 
2059 {
2060         physics_speed = 5;
2061         set_physics_controls();
2062 }
2063
2064 void CFREDView::OnSpeed8() 
2065 {
2066         physics_speed = 8;
2067         set_physics_controls();
2068 }
2069
2070 void CFREDView::OnSpeed10() 
2071 {
2072         physics_speed = 10;
2073         set_physics_controls();
2074 }
2075
2076 void CFREDView::OnSpeed50() 
2077 {
2078         physics_speed = 50;
2079         set_physics_controls();
2080 }
2081
2082 void CFREDView::OnSpeed100() 
2083 {
2084         physics_speed = 100;
2085         set_physics_controls();
2086 }
2087
2088 void CFREDView::OnRot1() 
2089 {
2090         physics_rot = 2;
2091         set_physics_controls();
2092 }
2093
2094 void CFREDView::OnRot2() 
2095 {
2096         physics_rot = 10;
2097         set_physics_controls();
2098 }
2099
2100 void CFREDView::OnRot3() 
2101 {
2102         physics_rot = 25;
2103         set_physics_controls();
2104 }
2105
2106 void CFREDView::OnRot4() 
2107 {
2108         physics_rot = 50;
2109         set_physics_controls();
2110 }
2111
2112 void CFREDView::OnRot5() 
2113 {
2114         physics_rot = 100;
2115         set_physics_controls();
2116 }
2117
2118 void CFREDView::OnUpdateSpeed1(CCmdUI* pCmdUI) 
2119 {
2120         pCmdUI->SetCheck(physics_speed == 1);
2121 }
2122
2123 void CFREDView::OnUpdateSpeed2(CCmdUI* pCmdUI) 
2124 {
2125         pCmdUI->SetCheck(physics_speed == 2);
2126 }
2127
2128 void CFREDView::OnUpdateSpeed3(CCmdUI* pCmdUI) 
2129 {
2130         pCmdUI->SetCheck(physics_speed == 3);
2131 }
2132
2133 void CFREDView::OnUpdateSpeed5(CCmdUI* pCmdUI) 
2134 {
2135         pCmdUI->SetCheck(physics_speed == 5);
2136 }
2137
2138 void CFREDView::OnUpdateSpeed8(CCmdUI* pCmdUI) 
2139 {
2140         pCmdUI->SetCheck(physics_speed == 8);
2141 }
2142
2143 void CFREDView::OnUpdateSpeed10(CCmdUI* pCmdUI) 
2144 {
2145         pCmdUI->SetCheck(physics_speed == 10);
2146 }
2147
2148 void CFREDView::OnUpdateSpeed50(CCmdUI* pCmdUI) 
2149 {
2150         pCmdUI->SetCheck(physics_speed == 50);
2151 }
2152
2153 void CFREDView::OnUpdateSpeed100(CCmdUI* pCmdUI) 
2154 {
2155         pCmdUI->SetCheck(physics_speed == 100);
2156 }
2157
2158 void CFREDView::OnUpdateRot1(CCmdUI* pCmdUI) 
2159 {
2160         pCmdUI->SetCheck(physics_rot == 2);
2161 }
2162
2163 void CFREDView::OnUpdateRot2(CCmdUI* pCmdUI) 
2164 {
2165         pCmdUI->SetCheck(physics_rot == 10);
2166 }
2167
2168 void CFREDView::OnUpdateRot3(CCmdUI* pCmdUI) 
2169 {
2170         pCmdUI->SetCheck(physics_rot == 25);
2171 }
2172
2173 void CFREDView::OnUpdateRot4(CCmdUI* pCmdUI) 
2174 {
2175         pCmdUI->SetCheck(physics_rot == 50);
2176 }
2177
2178 void CFREDView::OnUpdateRot5(CCmdUI* pCmdUI) 
2179 {
2180         pCmdUI->SetCheck(physics_rot == 100);
2181 }
2182
2183 void CFREDView::OnControlModeCamera() 
2184 {
2185         Control_mode = 0;
2186 }
2187
2188 void CFREDView::OnUpdateControlModeCamera(CCmdUI* pCmdUI) 
2189 {
2190         pCmdUI->SetCheck(!Control_mode);
2191 }
2192
2193 void CFREDView::OnControlModeShip() 
2194 {
2195         Control_mode = 1;
2196 }
2197
2198 void CFREDView::OnUpdateControlModeShip(CCmdUI* pCmdUI) 
2199 {
2200         pCmdUI->SetCheck(Control_mode == 1);
2201 }
2202
2203 void CFREDView::OnShowGridPositions() 
2204 {
2205         Show_grid_positions = !Show_grid_positions;
2206         theApp.write_ini_file();
2207         Update_window = 1;
2208 }
2209
2210 void CFREDView::OnUpdateShowGridPositions(CCmdUI* pCmdUI) 
2211 {
2212         pCmdUI->SetCheck(Show_grid_positions);
2213 }
2214
2215 void CFREDView::OnShowCoordinates() 
2216 {
2217         Show_coordinates = !Show_coordinates;
2218         theApp.write_ini_file();
2219         Update_window = 1;
2220 }
2221
2222 void CFREDView::OnUpdateShowCoordinates(CCmdUI* pCmdUI) 
2223 {
2224         pCmdUI->SetCheck(Show_coordinates);
2225 }
2226
2227 void CFREDView::OnSelect() 
2228 {
2229         Editing_mode = 0;
2230 }
2231
2232 void CFREDView::OnUpdateSelect(CCmdUI* pCmdUI) 
2233 {
2234         pCmdUI->SetCheck(!Editing_mode);
2235 }
2236
2237 void CFREDView::OnSelectAndMove() 
2238 {
2239         Editing_mode = 1;
2240 }
2241
2242 void CFREDView::OnUpdateSelectAndMove(CCmdUI* pCmdUI) 
2243 {
2244         pCmdUI->SetCheck(Editing_mode == 1);
2245 }
2246
2247 void CFREDView::OnSelectAndRotate() 
2248 {
2249         Editing_mode = 2;
2250 }
2251
2252 void CFREDView::OnUpdateSelectAndRotate(CCmdUI* pCmdUI) 
2253 {
2254         pCmdUI->SetCheck(Editing_mode == 2);
2255 }
2256
2257 void CFREDView::OnConstrainX() 
2258 {
2259         vm_vec_make(&Constraint, 1.0f, 0.0f, 0.0f);
2260         vm_vec_make(&Anticonstraint, 0.0f, 1.0f, 1.0f);
2261         Single_axis_constraint = 1;
2262 }
2263
2264 void CFREDView::OnUpdateConstrainX(CCmdUI* pCmdUI) 
2265 {
2266         pCmdUI->SetRadio(Constraint.x && !Constraint.y && !Constraint.z);
2267 }
2268
2269 void CFREDView::OnConstrainY() 
2270 {
2271         vm_vec_make(&Constraint, 0.0f, 1.0f, 0.0f);
2272         vm_vec_make(&Anticonstraint, 1.0f, 0.0f, 1.0f);
2273         Single_axis_constraint = 1;
2274 }
2275
2276 void CFREDView::OnUpdateConstrainY(CCmdUI* pCmdUI) 
2277 {
2278         pCmdUI->SetRadio(!Constraint.x && Constraint.y && !Constraint.z);
2279 }
2280
2281 void CFREDView::OnConstrainZ() 
2282 {
2283         vm_vec_make(&Constraint, 0.0f, 0.0f, 1.0f);
2284         vm_vec_make(&Anticonstraint, 1.0f, 1.0f, 0.0f);
2285         Single_axis_constraint = 1;
2286 }
2287
2288 void CFREDView::OnUpdateConstrainZ(CCmdUI* pCmdUI) 
2289 {
2290         pCmdUI->SetRadio(!Constraint.x && !Constraint.y && Constraint.z);
2291 }
2292
2293 void CFREDView::OnConstrainXz() 
2294 {
2295         vm_vec_make(&Constraint, 1.0f, 0.0f, 1.0f);
2296         vm_vec_make(&Anticonstraint, 0.0f, 1.0f, 0.0f);
2297         Single_axis_constraint = 0;
2298 }
2299
2300 void CFREDView::OnUpdateConstrainXz(CCmdUI* pCmdUI) 
2301 {
2302         pCmdUI->SetRadio(Constraint.x && !Constraint.y && Constraint.z);
2303 }
2304
2305 void CFREDView::OnConstrainXy()
2306 {
2307         vm_vec_make(&Constraint, 1.0f, 1.0f, 0.0f);
2308         vm_vec_make(&Anticonstraint, 0.0f, 0.0f, 1.0f);
2309         Single_axis_constraint = 0;
2310 }
2311
2312 void CFREDView::OnUpdateConstrainXy(CCmdUI* pCmdUI) 
2313 {
2314         pCmdUI->SetRadio(Constraint.x && Constraint.y && !Constraint.z);
2315 }
2316
2317 void CFREDView::OnConstrainYz() 
2318 {
2319         vm_vec_make(&Constraint, 0.0f, 1.0f, 1.0f);
2320         vm_vec_make(&Anticonstraint, 1.0f, 0.0f, 0.0f);
2321         Single_axis_constraint = 0;
2322 }
2323
2324 void CFREDView::OnUpdateConstrainYz(CCmdUI* pCmdUI) 
2325 {
2326         pCmdUI->SetRadio(!Constraint.x && Constraint.y && Constraint.z);
2327 }
2328
2329 void CFREDView::OnSelectionLock() 
2330 {
2331         Selection_lock = !Selection_lock;
2332 }
2333
2334 void CFREDView::OnUpdateSelectionLock(CCmdUI* pCmdUI) 
2335 {
2336         pCmdUI->SetCheck(Selection_lock);
2337 }
2338
2339 void CFREDView::OnLButtonDblClk(UINT nFlags, CPoint point) 
2340 {
2341         CView::OnLButtonDblClk(nFlags, point);
2342         if (Cursor_over != -1) {
2343                 switch (Objects[Cursor_over].type) {
2344                         case OBJ_SHIP:
2345                         case OBJ_START:
2346                                 OnEditorsShips();
2347                                 break;
2348
2349                         case OBJ_WAYPOINT:
2350                         case OBJ_JUMP_NODE:
2351                                 OnEditorsWaypoint();
2352                                 break;
2353                 }
2354
2355         } else if (Briefing_dialog)
2356                 Fixed_briefing_size = !Fixed_briefing_size;
2357 }
2358
2359 void CFREDView::OnDoubleFineGridlines() 
2360 {
2361         double_fine_gridlines = !double_fine_gridlines; 
2362         maybe_create_new_grid(The_grid, &eye_pos, &eye_orient, 1);
2363         theApp.write_ini_file();
2364         Update_window = 1;
2365 }
2366
2367 void CFREDView::OnUpdateDoubleFineGridlines(CCmdUI* pCmdUI) 
2368 {
2369         pCmdUI->SetCheck(double_fine_gridlines);        
2370 }
2371
2372 void CFREDView::OnShowDistances() 
2373 {
2374         Show_distances = !Show_distances;
2375         theApp.write_ini_file();
2376         Update_window = 1;
2377 }
2378
2379 void CFREDView::OnUpdateShowDistances(CCmdUI* pCmdUI) 
2380 {
2381         pCmdUI->SetCheck(Show_distances);       
2382 }
2383
2384 void CFREDView::OnUniversalHeading() 
2385 {
2386         Universal_heading = !Universal_heading; 
2387 }
2388
2389 void CFREDView::OnUpdateUniversalHeading(CCmdUI* pCmdUI) 
2390 {
2391         pCmdUI->SetCheck(Universal_heading);    
2392 }
2393
2394 void CFREDView::OnFlyingControls() 
2395 {
2396         Flying_controls_mode = !Flying_controls_mode;   
2397 }
2398
2399 void CFREDView::OnUpdateFlyingControls(CCmdUI* pCmdUI) 
2400 {
2401         pCmdUI->SetCheck(Flying_controls_mode); 
2402 }
2403
2404 void CFREDView::OnRotateLocally() 
2405 {
2406         Group_rotate = !Group_rotate;   
2407 }
2408
2409 void CFREDView::OnUpdateRotateLocally(CCmdUI* pCmdUI) 
2410 {
2411         pCmdUI->SetCheck(!Group_rotate);        
2412 }
2413
2414 void CFREDView::OnSelectList() 
2415 {
2416         ship_select dlg;
2417
2418         dlg.DoModal();
2419 }
2420
2421 // position camera to view all objects on the screen at once.  Doesn't change orientation.
2422 void view_universe(int just_marked)
2423 {
2424         int i, max = 0, flags[MAX_OBJECTS];
2425         float dist, largest = 20.0f;
2426         vector center, p1, p2;          // center of all the objects collectively
2427         vertex v;
2428         object *ptr;
2429
2430         for (i=0; i<MAX_OBJECTS; i++)
2431                 flags[i] = 0;
2432
2433         if (just_marked)
2434                 ptr = &Objects[cur_object_index];
2435         else
2436                 ptr = GET_FIRST(&obj_used_list);
2437
2438         p1.x = p2.x = ptr->pos.x;
2439         p1.y = p2.y = ptr->pos.y;
2440         p1.z = p2.z = ptr->pos.z;
2441
2442         ptr = GET_FIRST(&obj_used_list);
2443         while (ptr != END_OF_LIST(&obj_used_list)) {
2444                 if (!just_marked || (ptr->flags & OF_MARKED)) {
2445                         center = ptr->pos;
2446                         if (center.x < p1.x)
2447                                 p1.x = center.x;
2448                         if (center.x > p2.x)
2449                                 p2.x = center.x;
2450                         if (center.y < p1.y)
2451                                 p1.y = center.y;
2452                         if (center.y > p2.y)
2453                                 p2.y = center.y;
2454                         if (center.z < p1.z)
2455                                 p1.z = center.z;
2456                         if (center.z > p2.z)
2457                                 p2.z = center.z;
2458                 }
2459                 
2460                 ptr = GET_NEXT(ptr);
2461         }
2462
2463         vm_vec_avg(&center, &p1, &p2);
2464         ptr = GET_FIRST(&obj_used_list);
2465         while (ptr != END_OF_LIST(&obj_used_list)) {
2466                 if (!just_marked || (ptr->flags & OF_MARKED)) {
2467                         dist = vm_vec_dist_squared(&center, &ptr->pos);
2468                         if (dist > largest)
2469                                 largest = dist;
2470
2471                         flags[OBJ_INDEX(ptr)] = 1;  // flag object as needing on-screen check
2472                         if (OBJ_INDEX(ptr) > max)
2473                                 max = OBJ_INDEX(ptr);
2474                 }
2475
2476                 ptr = GET_NEXT(ptr);
2477         }
2478
2479         dist = fl_sqrt(largest) + 1.0f;
2480         vm_vec_scale_add(&view_pos, &center, &view_orient.fvec, -dist);
2481         g3_set_view_matrix(&view_pos, &view_orient, 0.5f);
2482
2483         ptr = GET_FIRST(&obj_used_list);
2484         while (ptr != END_OF_LIST(&obj_used_list)) {
2485                 if (!just_marked || (ptr->flags & OF_MARKED)) {
2486                         g3_rotate_vertex(&v, &ptr->pos);
2487                         SDL_assert(!(v.codes & CC_BEHIND));
2488                         if (g3_project_vertex(&v) & PF_OVERFLOW)
2489                                 Int3();
2490
2491                         while (v.codes & CC_OFF) {  // is point off screen?
2492                                 dist += 5.0f;  // zoom out a little and check again.
2493                                 vm_vec_scale_add(&view_pos, &center, &view_orient.fvec, -dist);
2494                                 g3_set_view_matrix(&view_pos, &view_orient, 0.5f);
2495                                 g3_rotate_vertex(&v, &ptr->pos);
2496                                 if (g3_project_vertex(&v) & PF_OVERFLOW)
2497                                         Int3();
2498                         }
2499                 }
2500
2501                 ptr = GET_NEXT(ptr);
2502         }
2503
2504         dist *= 1.1f;
2505         vm_vec_scale_add(&view_pos, &center, &view_orient.fvec, -dist);
2506         g3_set_view_matrix(&view_pos, &view_orient, 0.5f);
2507         Update_window = 1;
2508 }
2509
2510 void CFREDView::cycle_constraint()
2511 {
2512         if (Single_axis_constraint) {
2513                 if (Constraint.x)
2514                         OnConstrainY();
2515                 else if (Constraint.y)
2516                         OnConstrainZ();
2517                 else if (Constraint.z)
2518                         OnConstrainXz();
2519
2520         } else {
2521                 if (!Constraint.x)
2522                         OnConstrainXy();
2523                 else if (!Constraint.y)
2524                         OnConstrainYz();
2525                 else if (!Constraint.z)
2526                         OnConstrainX();
2527         }
2528 }
2529
2530 void CFREDView::OnZoomExtents() 
2531 {
2532         view_universe();
2533 }
2534
2535 void CFREDView::OnZoomSelected() 
2536 {
2537         if (query_valid_object()) {
2538                 if (Marked > 1)
2539                         view_universe(1);
2540                 else
2541                         vm_vec_scale_add(&view_pos, &Objects[cur_object_index].pos, &view_orient.fvec, Objects[cur_object_index].radius * -3.0f);
2542         }
2543
2544         Update_window = 1;
2545 }
2546
2547 void CFREDView::OnUpdateZoomSelected(CCmdUI* pCmdUI) 
2548 {
2549         pCmdUI->Enable(query_valid_object());
2550 }
2551
2552 void CFREDView::OnFormWing() 
2553 {
2554         if (!create_wing())
2555                 FREDDoc_ptr->autosave("form wing");
2556 }
2557
2558 void CFREDView::OnUpdateFormWing(CCmdUI* pCmdUI) 
2559 {
2560         int count = 0;
2561         object *ptr;
2562
2563         if (query_valid_object()) {
2564                 ptr = GET_FIRST(&obj_used_list);
2565                 while (ptr != END_OF_LIST(&obj_used_list)) {
2566                         if (ptr->flags & OF_MARKED) {
2567                                 if (ptr->type == OBJ_SHIP)
2568                                         switch (ship_query_general_type(ptr->instance))
2569                                         {
2570                                                 case SHIP_TYPE_FIGHTER_BOMBER:
2571                                                 case SHIP_TYPE_CRUISER:
2572                                                 case SHIP_TYPE_AWACS:
2573                                                 case SHIP_TYPE_GAS_MINER:
2574                                                 case SHIP_TYPE_CORVETTE:
2575                                                 case SHIP_TYPE_FREIGHTER:
2576                                                 case SHIP_TYPE_CAPITAL:
2577                                                 case SHIP_TYPE_TRANSPORT:
2578                                                         count++;
2579                                         }
2580
2581                                 if (ptr->type == OBJ_START)
2582                                         count++;
2583                         }
2584
2585                         ptr = GET_NEXT(ptr);
2586                 }
2587         }
2588
2589         pCmdUI->Enable(count > 0);
2590 }
2591
2592 int query_single_wing_marked()
2593 {
2594         int i, obj;
2595
2596         if (!query_valid_object())
2597                 return 0;
2598
2599         if (cur_wing == -1)
2600                 return 0;
2601
2602         i = Wings[cur_wing].wave_count;
2603         if (Marked != i)  // does marked object count match number of ships in wing?
2604                 return 0;
2605
2606         while (i--) {
2607                 obj = wing_objects[cur_wing][i];
2608                 if ((Objects[obj].type != OBJ_SHIP) && (Objects[obj].type != OBJ_START))
2609                         Error(LOCATION, "Invalid objects detected in wing \"%s\"", Wings[cur_wing].name);
2610
2611 //              if (Ships[Objects[obj].instance].wingnum != cur_wing)
2612 //                      return 0;
2613                 SDL_assert(Ships[Objects[obj].instance].wingnum == cur_wing);
2614                 if (!(Objects[obj].flags & OF_MARKED))  // ensure all ships in wing.are marked
2615                         return 0;
2616         }
2617
2618         return 1;
2619 }
2620
2621 void CFREDView::OnDisbandWing() 
2622 {
2623         if (query_single_wing_marked()) {
2624                 remove_wing(cur_wing);
2625                 FREDDoc_ptr->autosave("wing disband");
2626
2627         } else
2628                 MessageBox("One and only one wing must be selected for this operation");
2629 }
2630
2631 void CFREDView::OnUpdateDisbandWing(CCmdUI* pCmdUI) 
2632 {
2633         pCmdUI->Enable(query_single_wing_marked());
2634 }
2635
2636 void CFREDView::OnShowHorizon() 
2637 {
2638         Show_horizon = !Show_horizon;
2639         theApp.write_ini_file();
2640         Update_window = 1;
2641 }
2642
2643 void CFREDView::OnUpdateShowHorizon(CCmdUI* pCmdUI) 
2644 {
2645         pCmdUI->SetCheck(Show_horizon);
2646 }
2647
2648 void CFREDView::OnEditorsWing() 
2649 {
2650         int adjust = 0;
2651
2652         SDL_assert(Wing_editor_dialog.GetSafeHwnd());
2653         if (!Show_sexp_help)
2654                 adjust = -SEXP_HELP_BOX_SIZE;
2655
2656         if (!theApp.init_window(&Wing_wnd_data, &Wing_editor_dialog, adjust))
2657                 return;
2658
2659         Wing_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0,
2660                 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2661         Wing_editor_dialog.ShowWindow(SW_RESTORE);
2662 }
2663
2664 void CFREDView::OnEditorsPlayer() 
2665 {
2666         player_start_editor dlg;
2667
2668         dlg.DoModal();
2669 }
2670
2671 void CFREDView::OnEditorsOrient() 
2672 {
2673         orient_editor dlg;
2674
2675         dlg.DoModal();
2676 }
2677
2678 void CFREDView::OnEditorsEvents() 
2679 {
2680         if (Message_editor_dlg) {
2681                 MessageBox("You must close the message editor before opening the event editor");
2682                 return;
2683         }
2684
2685         if (!Event_editor_dlg) {
2686                 Event_editor_dlg = new event_editor;
2687                 Event_editor_dlg->Create(event_editor::IDD);
2688         }
2689
2690         Event_editor_dlg->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2691         Event_editor_dlg->ShowWindow(SW_RESTORE);
2692 }
2693
2694 void CFREDView::OnUpdateEditorsOrient(CCmdUI* pCmdUI) 
2695 {
2696         pCmdUI->Enable(query_valid_object());
2697 }
2698
2699 void CFREDView::OnEditorsMessage()
2700 {
2701         if (Event_editor_dlg) {
2702                 MessageBox("You must close the event editor before opening the message editor");
2703                 return;
2704         }
2705
2706         if (!Message_editor_dlg) {
2707                 Message_editor_dlg = new CMessageEditorDlg;
2708                 Message_editor_dlg->Create(CMessageEditorDlg::IDD);
2709         }
2710
2711         Message_editor_dlg->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2712         Message_editor_dlg->ShowWindow(SW_RESTORE);
2713 }
2714
2715 void CFREDView::OnEditorsStarfield() 
2716 {
2717         starfield_editor dlg;
2718
2719         dlg.DoModal();
2720 }
2721
2722 void CFREDView::place_background_bitmap(vector v)
2723 {
2724 }
2725
2726 void CFREDView::OnEditorsBgBitmaps()
2727 {
2728         if (!Bg_bitmap_dialog) {
2729                 Bg_bitmap_dialog = new bg_bitmap_dlg;
2730                 Bg_bitmap_dialog->create();
2731         }
2732
2733         Bg_bitmap_dialog->SetWindowPos(&wndTop, 0, 0, 0, 0,
2734                 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2735         Bg_bitmap_dialog->ShowWindow(SW_RESTORE);
2736 }
2737
2738 void CFREDView::OnEditorsReinforcement()
2739 {
2740         reinforcement_editor_dlg dlg;
2741
2742         dlg.DoModal();
2743 }
2744
2745 void CFREDView::OnErrorChecker()
2746 {
2747         int z;
2748
2749         z = global_error_check();
2750         if (!z)
2751                 MessageBox("No errors were detected in this mission", "Woohoo!");
2752
2753         for (z=0; z<obj_count; z++)
2754                 if (flags[z])
2755                         delete [] names[z];
2756
2757         obj_count = 0;
2758 }
2759
2760 int CFREDView::global_error_check()
2761 {
2762         char buf[256], *str;
2763         int bs, i, j, n, s, t, z, ai, count, ship, wing, obj, team, point, multi;
2764         object *ptr;
2765         brief_stage *sp;
2766         int starting_orders;
2767
2768         g_err = multi = 0;
2769         if ( The_mission.game_type & MISSION_TYPE_MULTI )
2770                 multi = 1;
2771
2772 //      if (!stricmp(The_mission.name, "Untitled"))
2773 //              if (error("You haven't given this mission a title yet.\nThis is done from the Mission Specs Editor (Shift-N)."))
2774 //                      return 1;
2775
2776         // cycle though all the objects and verify every possible aspect of them
2777         obj_count = t = 0;
2778         ptr = GET_FIRST(&obj_used_list);
2779         while (ptr != END_OF_LIST(&obj_used_list)) {
2780                 names[obj_count] = NULL;
2781                 flags[obj_count] = 0;
2782                 i = ptr->instance;
2783                 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
2784                         if (i < 0 || i >= MAX_SHIPS){
2785                                 return internal_error("An object has an illegal ship index");
2786                         }
2787
2788                         z = Ships[i].ship_info_index;
2789                         if ((z < 0) || (z >= Num_ship_types)){
2790                                 return internal_error("A ship has an illegal class");
2791                         }
2792
2793                         if (ptr->type == OBJ_START) {
2794                                 t++;
2795                                 if (!(Ship_info[z].flags & SIF_PLAYER_SHIP)) {
2796                                         ptr->type = OBJ_SHIP;
2797                                         Player_starts--;
2798                                         t--;
2799                                         if (error("Invalid ship type for a player.  Ship has been reset to non-player ship.")){
2800                                                 return 1;
2801                                         }
2802                                 }
2803
2804                                 for (n=count=0; n<MAX_PRIMARY_BANKS; n++){
2805                                         if (Ships[i].weapons.primary_bank_weapons[n] >= 0){
2806                                                 count++;
2807                                         }
2808                                 }
2809
2810                                 if (!count){
2811                                         if (error("Player \"%s\" has no primary weapons.  Should have at least 1", Ships[i].ship_name)){
2812                                                 return 1;
2813                                         }
2814                                 }
2815
2816                                 for (n=count=0; n<MAX_SECONDARY_BANKS; n++){
2817                                         if (Ships[i].weapons.secondary_bank_weapons[n] >= 0){
2818                                                 count++;
2819                                         }
2820                                 }
2821
2822                                 if (!count){
2823                                         if (error("Player \"%s\" has no secondary weapons.  Should have at least 1", Ships[i].ship_name)){
2824                                                 return 1;
2825                                         }
2826                                 }
2827                         }
2828
2829                         if (Ships[i].objnum != OBJ_INDEX(ptr)){
2830                                 return internal_error("Object/ship references are corrupt");
2831                         }
2832
2833                         names[obj_count] = Ships[i].ship_name;
2834                         wing = Ships[i].wingnum;
2835                         if (wing >= 0) {  // ship is part of a wing, so check this
2836                                 if (wing < 0 || wing >= MAX_WINGS){  // completely out of range?
2837                                         return internal_error("A ship has an illegal wing index");
2838                                 }
2839
2840                                 j = Wings[wing].wave_count;
2841                                 if (!j){
2842                                         return internal_error("A ship is in a non-existant wing");
2843                                 }
2844
2845                                 if (j < 0 || j > MAX_SHIPS_PER_WING){
2846                                         return internal_error("Invalid number of ships in wing \"%s\"", Wings[z].name);
2847                                 }
2848
2849                                 while (j--){
2850                                         if (wing_objects[wing][j] == OBJ_INDEX(ptr)){  // look for object in wing's table
2851                                                 break;
2852                                         }
2853                                 }
2854
2855                                 if (j < 0){
2856                                         return internal_error("Ship/wing references are corrupt");
2857                                 }
2858                         }
2859
2860                         if ( (Ships[i].flags & SF_KILL_BEFORE_MISSION) && (Ships[i].hotkey >= 0) ){
2861                                 if (error("Ship flagged as \"destroy before mission start\" has a hotkey assignment")){
2862                                         return 1;
2863                                 }
2864                         }
2865
2866                         if ( (Ships[i].flags & SF_KILL_BEFORE_MISSION) && (ptr->type == OBJ_START) ){
2867                                 if (error("Player start flagged as \"destroy before mission start\"")){
2868                                         return 1;
2869                                 }
2870                         }
2871                 } else if (ptr->type == OBJ_WAYPOINT) {
2872                         j = i / 65536;  // waypoint path number
2873                         z = i & 0xffff;  // waypoint number in path
2874                         if (j < 0 || j >= Num_waypoint_lists){
2875                                 return internal_error("Object references an illegal waypoint path number");
2876                         }
2877
2878                         if (z >= Waypoint_lists[j].count){
2879                                 return internal_error("Object references an illegal waypoint number in path");
2880                         }
2881
2882                         sprintf(buf, "%s:%d", Waypoint_lists[j].name, z + 1);
2883                         names[obj_count] = new char[strlen(buf) + 1];
2884                         strcpy(names[obj_count], buf);
2885                         flags[obj_count] = 1;
2886                 } else if (ptr->type == OBJ_POINT) {
2887                         if (!Briefing_dialog){
2888                                 return internal_error("Briefing icon detected when not in briefing edit mode");
2889                         }
2890                 } else if (ptr->type == OBJ_JUMP_NODE) {
2891                         if (i < 0 || i >= Num_jump_nodes){
2892                                 return internal_error("Object has illegal jump node index");
2893                         }
2894                 } else {
2895                         return internal_error("An unknown object type (%d) was detected", ptr->type);
2896                 }
2897
2898                 for (i=0; i<obj_count; i++){
2899                         if (names[i] && names[obj_count]){
2900                                 if (!stricmp(names[i], names[obj_count])){
2901                                         return internal_error("Duplicate object names (%s)", names[i]);
2902                                 }
2903                         }
2904                 }
2905                 
2906                 obj_count++;
2907                 ptr = GET_NEXT(ptr);
2908         }
2909
2910         if (t != Player_starts){
2911                 return internal_error("Total number of player ships is incorrect");
2912         }
2913
2914         if (obj_count != num_objects){
2915                 return internal_error("num_objects is incorrect");
2916         }
2917
2918         count = 0;
2919         for (i=0; i<MAX_SHIPS; i++) {
2920                 if (Ships[i].objnum >= 0) {  // is ship being used?
2921                         count++;
2922                         if (!query_valid_object(Ships[i].objnum)){
2923                                 return internal_error("Ship uses an unused object");
2924                         }
2925
2926                         z = Objects[Ships[i].objnum].type;
2927                         if ((z != OBJ_SHIP) && (z != OBJ_START)){
2928                                 return internal_error("Object should be a ship, but isn't");
2929                         }
2930
2931                         if (fred_check_sexp(Ships[i].arrival_cue, OPR_BOOL, "arrival cue of ship \"%s\"", Ships[i].ship_name)){
2932                                 return -1;
2933                         }
2934
2935                         if (fred_check_sexp(Ships[i].departure_cue, OPR_BOOL, "departure cue of ship \"%s\"", Ships[i].ship_name)){
2936                                 return -1;
2937                         }
2938
2939                         if (Ships[i].arrival_location != ARRIVE_AT_LOCATION) {
2940                                 if (Ships[i].arrival_anchor < 0){
2941                                         if (error("Ship \"%s\" requires a valid arrival target", Ships[i].ship_name)){
2942                                                 return 1;
2943                                         }
2944                                 }
2945                         }
2946
2947                         if (Ships[i].departure_location != DEPART_AT_LOCATION) {
2948                                 if (Ships[i].departure_anchor < 0){
2949                                         if (error("Ship \"%s\" requires a valid departure target", Ships[i].ship_name)){
2950                                                 return 1;
2951                                         }
2952                                 }
2953                         }
2954
2955                         ai = Ships[i].ai_index;
2956                         if (ai < 0 || ai >= MAX_AI_INFO){
2957                                 return internal_error("AI index out of range for ship \"%s\"", Ships[i].ship_name);
2958                         }
2959
2960                         if (Ai_info[ai].shipnum != i){
2961                                 return internal_error("AI/ship references are corrupt");
2962                         }
2963
2964                         if ((str = error_check_initial_orders(Ai_info[ai].goals, i, -1))>0) {
2965                                 if (*str == '*')
2966                                         return internal_error("Initial orders error for ship \"%s\"\n\n%s", Ships[i].ship_name, str + 1);
2967                                 else if (*str == '!')
2968                                         return 1;
2969                                 else if (error("Initial orders error for ship \"%s\"\n\n%s", Ships[i].ship_name, str))
2970                                         return 1;
2971                         }
2972
2973                         obj = Ai_info[ai].dock_objnum;
2974                         if (obj >= 0) {
2975                                 if (!query_valid_object(obj)){
2976                                         return internal_error("Ship \"%s\" initially docked with non-existant ship", Ships[i].ship_name);
2977                                 }
2978
2979                                 if (Objects[obj].type != OBJ_SHIP && Objects[obj].type != OBJ_START){
2980                                         return internal_error("Ship \"%s\" initially docked with non-ship object", Ships[i].ship_name);
2981                                 }
2982
2983                                 ship = get_ship_from_obj(obj);
2984                                 if (!ship_docking_valid(i, ship) && !ship_docking_valid(ship, i)){
2985                                         return internal_error("Docking illegal between \"%s\" and \"%s\" (initially docked)", Ships[i].ship_name, Ships[ship].ship_name);
2986                                 }
2987
2988                                 z = get_docking_list(Ships[i].modelnum);
2989                                 point = Ai_info[ai].dock_index;
2990                                 if (point < 0 || point >= z){
2991                                         internal_error("Invalid docker point (\"%s\" initially docked with \"%s\")", Ships[i].ship_name, Ships[ship].ship_name);
2992                                 }
2993
2994                                 z = get_docking_list(Ships[ship].modelnum);
2995                                 point = Ai_info[ai].dockee_index;
2996                                 if (point < 0 || point >= z){
2997                                         internal_error("Invalid dockee point (\"%s\" initially docked with \"%s\")", Ships[i].ship_name, Ships[ship].ship_name);
2998                                 }
2999                         }
3000                 }
3001         }
3002
3003         if (count != ship_get_num_ships()){
3004                 return internal_error("num_ships is incorrect");
3005         }
3006
3007         count = 0;
3008         for (i=0; i<MAX_WINGS; i++) {
3009                 team = -1;
3010                 j = Wings[i].wave_count;
3011                 if (j) {  // is wing being used?
3012                         count++;
3013                         if (j < 0 || j > MAX_SHIPS_PER_WING){
3014                                 return internal_error("Invalid number of ships in wing \"%s\"", Wings[i].name);
3015                         }
3016
3017                         while (j--) {
3018                                 obj = wing_objects[i][j];
3019                                 if (obj < 0 || obj >= MAX_OBJECTS){
3020                                         return internal_error("Wing_objects has an illegal object index");
3021                                 }
3022
3023                                 if (!query_valid_object(obj)){
3024                                         return internal_error("Wing_objects references an unused object");
3025                                 }
3026
3027 // Now, at this point, we can assume several things.  We have a valid object because
3028 // we passed query_valid_object(), and all valid objects were already checked above,
3029 // so this object has valid information, such as the instance.
3030                                 
3031                                 if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START)) {
3032                                         ship = Objects[obj].instance;
3033                                         sprintf(buf, "%s %d", Wings[i].name, j + 1);
3034                                         if (stricmp(buf, Ships[ship].ship_name)){
3035                                                 return internal_error("Ship \"%s\" in wing should be called \"%s\"", Ships[ship].ship_name, buf);
3036                                         }
3037
3038                                         switch (ship_query_general_type(ship))
3039                                         {
3040                                                 case SHIP_TYPE_FIGHTER_BOMBER:
3041                                                 case SHIP_TYPE_CRUISER:
3042                                                 case SHIP_TYPE_AWACS:
3043                                                 case SHIP_TYPE_GAS_MINER:
3044                                                 case SHIP_TYPE_CORVETTE:
3045                                                 case SHIP_TYPE_FREIGHTER:
3046                                                 case SHIP_TYPE_CAPITAL:
3047                                                 case SHIP_TYPE_TRANSPORT:
3048                                                 case SHIP_TYPE_SUPERCAP:
3049                                                         break;
3050
3051                                                 default:
3052                                                         if (error("Ship \"%s\" is an illegal type to be in a wing", Ships[ship].ship_name)){
3053                                                                 return 1;
3054                                                         }
3055                                         }
3056                                 } else {
3057                                         return internal_error("Wing_objects of \"%s\" references an illegal object type", Wings[i].name);
3058                                 }
3059
3060                                 if (Ships[ship].wingnum != i){
3061                                         return internal_error("Wing/ship references are corrupt");
3062                                 }
3063
3064                                 if (ship != Wings[i].ship_index[j]){
3065                                         return internal_error("Ship/wing references are corrupt");
3066                                 }
3067
3068                                 if (team < 0){
3069                                         team = Ships[ship].team;
3070                                 } else if (team != Ships[ship].team && team < 999){
3071                                         if (error("ship teams mixed within same wing (\"%s\")", Wings[i].name)){
3072                                                 return 1;
3073                                         }
3074                                 }
3075                         }
3076
3077                         if ((Wings[i].special_ship < 0) || (Wings[i].special_ship >= Wings[i].wave_count)){
3078                                 return internal_error("Special ship out of range for \"%s\"", Wings[i].name);
3079                         }
3080
3081                         if (Wings[i].num_waves < 0){
3082                                 return internal_error("Number of waves for \"%s\" is negative", Wings[i].name);
3083                         }
3084
3085                         if ((Wings[i].threshold < 0) || (Wings[i].threshold >= Wings[i].wave_count)){
3086                                 return internal_error("Threshold for \"%s\" is invalid", Wings[i].name);
3087                         }
3088
3089                         if (Wings[i].threshold + Wings[i].wave_count > MAX_SHIPS_PER_WING) {
3090                                 Wings[i].threshold = MAX_SHIPS_PER_WING - Wings[i].wave_count;
3091                                 if(error("Threshold for wing \"%s\" is higher than allowed.  Reset to %d", Wings[i].name, Wings[i].threshold)){
3092                                         return 1;
3093                                 }
3094                         }
3095
3096                         for (j=0; j<obj_count; j++){
3097                                 if (names[j]){
3098                                         if (!stricmp(names[j], Wings[i].name)){
3099                                                 return internal_error("Wing name is also used by an object (%s)", names[j]);
3100                                         }
3101                                 }
3102                         }
3103
3104                         if(fred_check_sexp(Wings[i].arrival_cue, OPR_BOOL, "arrival cue of wing \"%s\"", Wings[i].name)){
3105                                 return -1;
3106                         }
3107
3108                         if(fred_check_sexp(Wings[i].departure_cue, OPR_BOOL, "departure cue of wing \"%s\"", Wings[i].name)){
3109                                 return -1;
3110                         }
3111
3112                         if (Wings[i].arrival_location != ARRIVE_AT_LOCATION) {
3113                                 if (Wings[i].arrival_anchor < 0)
3114                                         if (error("Wing \"%s\" requires a valid arrival target", Wings[i].name))
3115                                                 return 1;
3116                         }
3117
3118                         if (Wings[i].departure_location != DEPART_AT_LOCATION) {
3119                                 if (Wings[i].departure_anchor < 0)
3120                                         if (error("Wing \"%s\" requires a valid departure target", Wings[i].name))
3121                                                 return 1;
3122                         }
3123
3124                         if ((str = error_check_initial_orders(Wings[i].ai_goals, -1, i))>0) {
3125                                 if (*str == '*')
3126                                         return internal_error("Initial orders error for wing \"%s\"\n\n%s", Wings[i].name, str + 1);
3127                                 else if (*str == '!')
3128                                         return 1;
3129                                 else if (error("Initial orders error for wing \"%s\"\n\n%s", Wings[i].name, str))
3130                                         return 1;
3131                         }
3132
3133                 }
3134         }
3135
3136         if (count != num_wings){
3137                 return internal_error("num_wings is incorrect");
3138         }
3139
3140         for (i=0; i<Num_waypoint_lists; i++) {
3141                 for (z=0; z<obj_count; z++){
3142                         if (names[z]){
3143                                 if (!stricmp(names[z], Waypoint_lists[i].name)){
3144                                         return internal_error("Waypoint path name is also used by an object (%s)", names[z]);
3145                                 }
3146                         }
3147                 }
3148
3149                 j = Waypoint_lists[i].count;
3150                 while (j--) {
3151                         sprintf(buf, "%s:%d", Waypoint_lists[i].name, j + 1);
3152                         for (z=0; z<obj_count; z++){
3153                                 if (names[z]){
3154                                         if (!stricmp(names[z], buf)){
3155                                                 break;
3156                                         }
3157                                 }
3158                         }
3159                         
3160                         if (z == obj_count){
3161                                 return internal_error("Waypoint \"%s\" not linked to an object", buf);
3162                         }
3163                 }
3164         }
3165
3166         if (Player_starts > MAX_PLAYERS){
3167                 return internal_error("Number of player starts exceeds max limit");
3168         }
3169
3170         if (!multi && (Player_starts > 1)){
3171                 if (error("Multiple player starts exist, but this is a single player mission")){
3172                         return 1;
3173                 }
3174         }
3175
3176         if (Num_reinforcements > MAX_REINFORCEMENTS){
3177                 return internal_error("Number of reinforcements exceeds max limit");
3178         }
3179
3180         for (i=0; i<Num_reinforcements; i++) {
3181                 z = 0;
3182                 for (ship=0; ship<MAX_SHIPS; ship++){
3183                         if ((Ships[ship].objnum >= 0) && !stricmp(Ships[ship].ship_name, Reinforcements[i].name)) {
3184                                 z = 1;
3185                                 break;
3186                         }
3187                 }
3188
3189                 for (wing=0; wing<MAX_WINGS; wing++){
3190                         if (Wings[wing].wave_count && !stricmp(Wings[wing].name, Reinforcements[i].name)) {
3191                                 z = 1;
3192                                 break;
3193                         }
3194                 }
3195
3196                 if (!z){
3197                         return internal_error("Reinforcement name not found in ships or wings");
3198                 }
3199         }
3200
3201 /*      for (i=0; i<num_messages; i++) {
3202                 if (Messages[i].num_times < 0)
3203                         return internal_error("Number of times to play message is negative");
3204
3205                 z = Messages[i].who_from;
3206                 if (z < -1 || z >= MAX_SHIPS)  // hacked!  -1 should be illegal..
3207                         return internal_error("Message originator index is out of range");
3208
3209                 if (Ships[z].objnum == -1)
3210                         return internal_error("Message originator points to nonexistant ship");
3211
3212                 if (fred_check_sexp(Messages[i].sexp, OPR_BOOL,
3213                         "Message formula from \"%s\"", Ships[Messages[i].who_from].ship_name))
3214                                 return -1;
3215         }*/
3216
3217         SDL_assert((Player_start_shipnum >= 0) && (Player_start_shipnum < MAX_SHIPS) && (Ships[Player_start_shipnum].objnum >= 0));
3218         i = global_error_check_player_wings(multi);
3219         if (i){
3220                 return i;
3221         }
3222
3223         for (i=0; i<Num_mission_events; i++){
3224                 if (fred_check_sexp(Mission_events[i].formula, OPR_NULL, "mission event \"%s\"", Mission_events[i].name)){
3225                         return -1;
3226                 }
3227         }
3228
3229         for (i=0; i<Num_goals; i++){
3230                 if (fred_check_sexp(Mission_goals[i].formula, OPR_BOOL, "mission goal \"%s\"", Mission_goals[i].name)){
3231                         return -1;
3232                 }
3233         }
3234
3235         for ( bs = 0; bs < Num_teams; bs++ ) {
3236                 for (s=0; s<Briefings[bs].num_stages; s++) {
3237                         sp = &Briefings[bs].stages[s];
3238                         t = sp->num_icons;
3239                         for (i=0; i<t-1; i++){
3240                                 for (j=i+1; j<t; j++) {
3241                                         if ((sp->icons[i].id > 0) && (sp->icons[i].id == sp->icons[j].id)){
3242                                                 if (error("Duplicate icon IDs %d in briefing stage %d", sp->icons[i].id, s + 1)){
3243                                                         return 1;
3244                                                 }
3245                                         }
3246                                 }
3247                         }
3248                 }
3249         }
3250
3251         for ( j = 0; j < Num_teams; j++ ) {
3252                 for (i=0; i<Debriefings[j].num_stages; i++) {
3253                         if (fred_check_sexp(Debriefings[j].stages[i].formula, OPR_BOOL, "debriefing stage %d", i + 1)){
3254                                 return -1;
3255                         }
3256                 }
3257         }
3258
3259         // for all wings, be sure that the orders accepted for all ships are the same for all ships
3260         // in the wing
3261         starting_orders = -1;
3262         for (i=0; i<MAX_WINGS; i++) {
3263                 int default_orders, starting_wing;
3264
3265                 if ( !Wings[i].wave_count ){
3266                         continue;
3267                 }
3268                 
3269                 // determine if this wing is a starting wing of the player
3270                 starting_wing = 1;
3271                 for ( j = 0; j < MAX_STARTING_WINGS; j++ ) {
3272                         if ( !stricmp( Wings[i].name, Starting_wing_names[j]) ){
3273                                 break;
3274                         }
3275                 }
3276                 if ( j == MAX_STARTING_WINGS ){
3277                         starting_wing = 0;
3278                 }
3279
3280                 // first, be sure this isn't a reinforcement wing.
3281                 if ( starting_wing && (Wings[i].flags & WF_REINFORCEMENT) ) {
3282                         if ( error("Starting Wing %s marked as reinforcement.  This wing\nshould either be renamed, or unmarked as reinforcement.", Wings[i].name) ){
3283                                 return 1;
3284                         }
3285                 }
3286
3287                 default_orders = 0;
3288                 for ( j = 0; j < Wings[i].wave_count; j++ ) {
3289                         int orders;
3290
3291                         orders = Ships[Wings[i].ship_index[j]].orders_accepted;
3292                         if ( j == 0 ) {
3293                                 default_orders = orders;
3294                         } else if ( default_orders != orders ) {
3295                                 if (error("Wing %s has ships with different player orders which\nare ignored.  They must all be the same", Wings[i].name ) ){
3296                                         return 1;
3297                                 }
3298                         }
3299                 }
3300
3301                 // make sure that these ignored orders are the same for all starting wings of the player
3302                 if ( starting_wing ) {
3303                         if ( starting_orders == -1 ) {
3304                                 starting_orders = default_orders;
3305                         } else {
3306                                 if ( starting_orders != default_orders ) {
3307                                         if ( error("Player starting wing %s has orders which don't match other starting wings\n", Wings[i].name) ){
3308                                                 return 1;
3309                                         }
3310                                 }
3311                         }
3312                 }
3313         }
3314
3315         if (Num_jump_nodes < 0 || Num_jump_nodes > MAX_JUMP_NODES){
3316                 return internal_error("Jump node count is illegal");
3317         }
3318
3319         fred_check_message_personas();
3320
3321         return g_err;
3322 }
3323
3324 int CFREDView::global_error_check_mixed_player_wing(int w)
3325 {
3326         int i, s, species = -1, mixed = 0;
3327
3328         for (i=0; i<Wings[w].wave_count; i++) {
3329                 s = Wings[w].ship_index[i];
3330                 if (species < 0)
3331                         species = Ship_info[Ships[s].ship_info_index].species;
3332                 else if (Ship_info[Ships[s].ship_info_index].species != species)
3333                         mixed = 1;
3334         }
3335
3336         if (mixed)
3337                 if (error("%s wing must all be of the same species", Wings[w].name))
3338                         return 1;
3339
3340         return 0;
3341 }
3342
3343 int CFREDView::global_error_check_player_wings(int multi)
3344 {
3345         int i, z, alpha, beta, gamma, zeta, err, alpha_count, zeta_count;
3346         object *ptr;
3347
3348         z = Ships[Player_start_shipnum].wingnum;
3349         alpha = wing_name_lookup("alpha", 1);
3350         beta = wing_name_lookup("beta", 1);
3351         gamma = wing_name_lookup("gamma", 1);
3352         zeta = wing_name_lookup( "zeta", 1 );
3353
3354         if (alpha >= 0) {
3355                 free_sexp2(Wings[alpha].arrival_cue);
3356                 Wings[alpha].arrival_cue = Locked_sexp_true;
3357         }
3358
3359         if (multi && (alpha < 0)){
3360                 if (error("Alpha wing is required for multiplayer missions")){
3361                         return 1;
3362                 }
3363         }
3364
3365         // Check to be sure that any player starting wing doesn't have > 1 wave for multiplayer
3366         if ( multi ) {
3367                 if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ) {
3368                         if ( alpha && (Wings[alpha].num_waves > 1) ) {
3369                                 error("Alpha wing must contain only 1 wave.\nThis change has been made for you.");
3370                                 Wings[alpha].num_waves = 1;
3371                                 return 1;
3372                         }
3373                         if ( zeta && (Wings[zeta].num_waves > 1) ) {
3374                                 error("Zeta wing must contain only 1 wave.\nThis change has been made for you.");
3375                                 Wings[zeta].num_waves = 1;
3376                                 return 1;
3377                         }
3378
3379                 } else {
3380                         if ( alpha && (Wings[alpha].num_waves > 1) ) {
3381                                 error("Alpha wing must contain only 1 wave.\nThis change has been made for you.");
3382                                 Wings[alpha].num_waves = 1;
3383                                 return 1;
3384                         }
3385                         if ( beta && (Wings[beta].num_waves > 1) ) {
3386                                 error("Beta wing must contain only 1 wave.\nThis change has been made for you.");
3387                                 Wings[beta].num_waves = 1;
3388                                 return 1;
3389                         }
3390                         if ( gamma && (Wings[gamma].num_waves > 1) ) {
3391                                 error("Gamma wing must contain only 1 wave.\nThis change has been made for you.");
3392                                 Wings[gamma].num_waves = 1;
3393                                 return 1;
3394                         }
3395                 }
3396         }
3397
3398         // if not a multiplayer misison, or a coop multiplayer mission, then do "normal"
3399         // wing name checking.
3400         if ( !multi || (The_mission.game_type & MISSION_TYPE_MULTI_COOP) ) {
3401                 if (((alpha >= 0) || (z >= 0)) && (alpha != z)){
3402                         if (error("Player start is not in Alpha wing")){
3403                                 return 1;
3404                         }
3405                 }
3406
3407                 if ((beta >= 0) && (alpha < 0)){
3408                         if (error("Alpha wing required, but not present")){
3409                                 return 1;
3410                         }
3411                 }
3412
3413                 if ((gamma >= 0) && (beta < 0)){
3414                         if (error("Beta wing required, but not present")){
3415                                 return 1;
3416                         }
3417                 }
3418
3419                 if ((alpha >= 0) && (Wings[alpha].wave_count > 4)){
3420                         if (error("Alpha wing has too many ships.  Should only have 4 max.")){
3421                                 return 1;
3422                         }
3423                 }
3424
3425                 if ((beta >= 0) && (Wings[beta].wave_count > 4)){
3426                         if (error("Beta wing has too many ships.  Should only have 4 max.")){
3427                                 return 1;
3428                         }
3429                 }
3430
3431                 if ((gamma >= 0) && (Wings[gamma].wave_count > 4)){
3432                         if (error("Gamma wing has too many ships.  Should only have 4 max.")){
3433                                 return 1;
3434                         }
3435                 }
3436
3437                 if ((alpha >= 0) && Wings[alpha].arrival_delay){
3438                         if (error("Alpha wing shouldn't have a non-zero arrival delay")){
3439                                 return 1;
3440                         }
3441                 }
3442         } else if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ) {
3443                 if ( zeta == -1 ){
3444                         if (error("Zeta wing is required for multiplayer team vs. team missions")){
3445                                 return 1;
3446                         }
3447                 }
3448
3449                 if ( Wings[alpha].wave_count > 4 ){
3450                         if (error("Alpha wing has too many ships.  Should only have 4 max.")){
3451                                 return 1;
3452                         }
3453                 }
3454
3455                 if ( Wings[zeta].wave_count > 4 ) {
3456                         if (error("Zeta wing has too many ships.  Should only have 4 max.")){
3457                                 return 1;
3458                         }
3459                 }
3460
3461                 if ((alpha >= 0) && Wings[alpha].arrival_delay){
3462                         if (error("Alpha wing shouldn't have a non-zero arrival delay")){
3463                                 return 1;
3464                         }
3465                 }
3466
3467                 if ((zeta >= 0) && Wings[zeta].arrival_delay){
3468                         if (error("Zeta wing shouldn't have a non-zero arrival delay")){
3469                                 return 1;
3470                         }
3471                 }
3472         } else if ( The_mission.game_type & MISSION_TYPE_MULTI_DOGFIGHT ) {
3473                 if ( Wings[alpha].wave_count > 4 ) {
3474                         if (error("Alpha wing has too many ships.  Should only have 4 max.")){
3475                                 return 1;
3476                         }
3477                 }
3478
3479                 if ((alpha >= 0) && Wings[alpha].arrival_delay){
3480                         if (error("Alpha wing shouldn't have a non-zero arrival delay")){
3481                                 return 1;
3482                         }
3483                 }
3484         } else {
3485                 error("Unknown game type: %d", The_mission.game_type);
3486                 return 1;
3487         }
3488
3489         if (multi) {
3490                 if (alpha >= 0){
3491                         if (global_error_check_mixed_player_wing(alpha)){
3492                                 return 1;
3493                         }
3494                 }
3495
3496                 if (beta >= 0){
3497                         if (global_error_check_mixed_player_wing(beta)){
3498                                 return 1;
3499                         }
3500                 }
3501
3502                 if (gamma >= 0){
3503                         if (global_error_check_mixed_player_wing(gamma)){
3504                                 return 1;
3505                         }
3506                 }
3507
3508                 if (zeta >= 0){
3509                         if (global_error_check_mixed_player_wing(zeta)){
3510                                 return 1;
3511                         }
3512                 }
3513         }
3514
3515         alpha_count = zeta_count = 0;
3516         ptr = GET_FIRST(&obj_used_list);
3517         while (ptr != END_OF_LIST(&obj_used_list)) {
3518                 i = ptr->instance;
3519                 err = 0;
3520                 if (ptr->type == OBJ_START) {
3521                         z = Ships[i].wingnum;
3522                         if (z < 0) {
3523                                 err = 1;
3524                         } else {
3525                                 if (z == alpha){
3526                                         alpha_count++;
3527                                 } else if (z == zeta){
3528                                         zeta_count++;
3529                                 }
3530
3531                                 if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) {
3532                                         if ((z != alpha) && (z != zeta)){
3533                                                 err = 1;
3534                                         }
3535                                 } else {
3536                                         if ((z != alpha) && (z != beta) && (z != gamma)){
3537                                                 err = 1;
3538                                         }
3539                                 }
3540                         }
3541
3542                         if (err) {
3543                                 if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) {
3544                                         if (error("Player \"%s\" should be part Alpha or Zeta wing", Ships[i].ship_name)){
3545                                                 return 1;
3546                                         }
3547                                 } else {
3548                                         if (error("Player \"%s\" should be part Alpha, Beta or Gamma wing", Ships[i].ship_name)){
3549                                                 return 1;
3550                                         }
3551                                 }
3552                         }
3553                 }
3554
3555                 ptr = GET_NEXT(ptr);
3556         }
3557
3558         if ((alpha >= 0) && !alpha_count){
3559                 if (error("Alpha wing doesn't contain any players, which it should.")){
3560                         return 1;
3561                 }
3562         }
3563
3564         if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS){
3565                 if ((zeta >= 0) && !zeta_count){
3566                         if (error("Zeta wing doesn't contain any players, which it should.")){
3567                                 return 1;
3568                         }
3569                 }
3570         }
3571
3572         return 0;
3573 }
3574
3575 int CFREDView::error(char *msg, ...)
3576 {
3577         char buf[2048];
3578         va_list args;
3579
3580         va_start(args, msg);
3581         vsprintf(buf, msg, args);
3582         va_end(args);
3583
3584         g_err = 1;
3585         if (MessageBox(buf, "Error", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDOK)
3586                 return 0;
3587
3588         return 1;
3589 }
3590
3591 int CFREDView::internal_error(char *msg, ...)
3592 {
3593         char buf[2048];
3594         va_list args;
3595
3596         va_start(args, msg);
3597         vsprintf(buf, msg, args);
3598         va_end(args);
3599
3600         g_err = 1;
3601
3602 #ifndef NDEBUG
3603         char buf2[2048];
3604
3605         sprintf(buf2, "%s\n\nThis is an internal error.  Please let Jason\n"
3606                 "know about this so he can fix it.  Click cancel to debug.", buf);
3607
3608         if (MessageBox(buf2, "Internal Error", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
3609                 Int3();  // drop to debugger so the problem can be analyzed.
3610
3611 #else
3612         MessageBox(buf, "Error", MB_OK | MB_ICONEXCLAMATION);
3613 #endif
3614
3615         return -1;
3616 }
3617
3618 int CFREDView::fred_check_sexp(int sexp, int type, char *msg, ...)
3619 {
3620         char buf[512], buf2[2048], buf3[4096];
3621         int err = 0, z, faulty_node;
3622         va_list args;
3623
3624         va_start(args, msg);
3625         vsprintf(buf, msg, args);
3626         va_end(args);
3627
3628         if (sexp == -1)
3629                 return 0;
3630
3631         z = check_sexp_syntax(sexp, type, 1, &faulty_node);
3632         if (!z)
3633                 return 0;
3634
3635         convert_sexp_to_string(sexp, buf2, SEXP_ERROR_CHECK_MODE);
3636         sprintf(buf3, "Error in %s: %s\n\nIn sexpression: %s\n(Error appears to be: %s)",
3637                 buf, sexp_error_message(z), buf2, Sexp_nodes[faulty_node].text);
3638
3639         if (z < 0 && z > -100)
3640                 err = 1;
3641
3642         if (err)
3643                 return internal_error(buf3);
3644
3645         if (error(buf3))
3646                 return 1;
3647
3648         return 0;
3649 }
3650
3651 void CFREDView::OnEditorsWaypoint() 
3652 {
3653         int adjust = 0;
3654
3655         SDL_assert(Waypoint_editor_dialog.GetSafeHwnd());
3656         if (!Show_sexp_help)
3657                 adjust = -SEXP_HELP_BOX_SIZE;
3658
3659         if (!theApp.init_window(&Waypoint_wnd_data, &Waypoint_editor_dialog))
3660                 return;
3661
3662         Waypoint_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0,
3663                 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
3664         Waypoint_editor_dialog.ShowWindow(SW_RESTORE);
3665 }
3666
3667 char *error_check_initial_orders(ai_goal *goals, int ship, int wing)
3668 {
3669         char *source;
3670         int i, j, num, flag, found, inst, team, team2;
3671         object *ptr;
3672
3673         if (ship >= 0) {
3674                 source = Ships[ship].ship_name;
3675                 team = Ships[ship].team;
3676                 for (i=0; i<MAX_AI_GOALS; i++)
3677                         if (!ai_query_goal_valid(ship, goals[i].ai_mode)) {
3678                                 if (Fred_view_wnd->error("Order \"%s\" isn't allowed for ship \"%s\"", get_order_name(goals[i].ai_mode), source))
3679                                         return "!";
3680                         }
3681
3682         } else {
3683                 SDL_assert(wing >= 0);
3684                 SDL_assert(Wings[wing].wave_count > 0);
3685                 source = Wings[wing].name;
3686                 team = Ships[Objects[wing_objects[wing][0]].instance].team;
3687                 for (j=0; j<Wings[wing].wave_count; j++)
3688                         for (i=0; i<MAX_AI_GOALS; i++)
3689                                 if (!ai_query_goal_valid(Wings[wing].ship_index[j], goals[i].ai_mode)) {
3690                                         if (Fred_view_wnd->error("Order \"%s\" isn't allowed for ship \"%s\"", get_order_name(goals[i].ai_mode),
3691                                                 Ships[Wings[wing].ship_index[j]].ship_name))
3692                                                         return "!";
3693                                 }
3694         }
3695
3696         for (i=0; i<MAX_AI_GOALS; i++) {
3697                 switch (goals[i].ai_mode) {
3698                         case AI_GOAL_NONE:
3699                         case AI_GOAL_CHASE_ANY:
3700                         case AI_GOAL_UNDOCK:
3701                         case AI_GOAL_KEEP_SAFE_DISTANCE:
3702                         case AI_GOAL_PLAY_DEAD:
3703                         case AI_GOAL_WARP:
3704                                 flag = 0;
3705                                 break;
3706
3707                         case AI_GOAL_WAYPOINTS:
3708                         case AI_GOAL_WAYPOINTS_ONCE:
3709                                 flag = 1;
3710                                 break;
3711
3712                         case AI_GOAL_DOCK:
3713                                 if (ship < 0)
3714                                         return "Wings can't dock";
3715                                 // fall through..
3716
3717                         case AI_GOAL_DESTROY_SUBSYSTEM:
3718                         case AI_GOAL_CHASE:
3719                         case AI_GOAL_GUARD:
3720                         case AI_GOAL_DISARM_SHIP:
3721                         case AI_GOAL_DISABLE_SHIP:
3722                         case AI_GOAL_EVADE_SHIP:
3723                         case AI_GOAL_STAY_NEAR_SHIP:
3724                         case AI_GOAL_IGNORE:
3725                                 flag = 2;
3726                                 break;
3727
3728                         case AI_GOAL_CHASE_WING:
3729                         case AI_GOAL_GUARD_WING:
3730                                 flag = 3;
3731                                 break;
3732
3733                         case AI_GOAL_STAY_STILL:
3734                                 flag = 4;
3735                                 break;
3736
3737                         default:
3738                                 return "*Invalid goal type";
3739                 }
3740
3741                 found = 0;
3742                 if (flag > 0) {
3743                         if (*goals[i].ship_name == '<')
3744                                 return "Invalid target";
3745
3746                         if (!stricmp(goals[i].ship_name, source))
3747                                 if (ship >= 0)
3748                                         return "Target of ship's goal is itself";
3749                                 else
3750                                         return "Target of wing's goal is itself";
3751                 }
3752
3753                 inst = team2 = -1;
3754                 if (flag == 1) {  // target waypoint required
3755                         for (j=0; j<Num_waypoint_lists; j++)
3756                                 if (!stricmp(goals[i].ship_name, Waypoint_lists[j].name))
3757                                         found = 1;
3758
3759                         if (!found)
3760                                 return "*Invalid target waypoint path name";
3761
3762                 } else if (flag == 2) {  // target ship required
3763                         ptr = GET_FIRST(&obj_used_list);
3764                         while (ptr != END_OF_LIST(&obj_used_list)) {
3765                                 if (ptr->type == OBJ_SHIP || ptr->type == OBJ_START) {
3766                                         inst = ptr->instance;
3767                                         if (!stricmp(goals[i].ship_name, Ships[inst].ship_name)) {
3768                                                 found = 1;
3769                                                 break;
3770                                         }
3771                                 }
3772
3773                                 ptr = GET_NEXT(ptr);
3774                         }
3775
3776                         if (!found)
3777                                 return "*Invalid target ship name";
3778
3779                         if (wing >= 0) {  // check if target ship is in wing
3780                                 if (Ships[inst].wingnum == wing && Objects[Ships[inst].objnum].type != OBJ_START)
3781                                         return "Target ship of wing's goal is within said wing";
3782                         }
3783
3784                         team2 = Ships[inst].team;
3785
3786                 } else if (flag == 3) {  // target wing required
3787                         for (j=0; j<MAX_WINGS; j++)
3788                                 if (Wings[j].wave_count && !stricmp(Wings[j].name, goals[i].ship_name))
3789                                         break;
3790
3791                         if (j >= MAX_WINGS)
3792                                 return "*Invalid target wing name";
3793
3794                         if (ship >= 0) {  // check if ship is in target wing
3795                                 if (Ships[ship].wingnum == j)
3796                                         return "Target wing of ship's goal is same wing said ship is part of";
3797                         }
3798
3799                         team2 = Ships[Objects[wing_objects[j][0]].instance].team;
3800
3801                 } else if (flag == 4) {
3802                         ptr = GET_FIRST(&obj_used_list);
3803                         while (ptr != END_OF_LIST(&obj_used_list)) {
3804                                 if (ptr->type == OBJ_SHIP || ptr->type == OBJ_START) {
3805                                         inst = ptr->instance;
3806                                         if (!stricmp(goals[i].ship_name, Ships[inst].ship_name)) {
3807                                                 found = 2;
3808                                                 break;
3809                                         }
3810
3811                                 } else if (ptr->type == OBJ_WAYPOINT) {
3812                                         if (!stricmp(goals[i].ship_name, object_name(OBJ_INDEX(ptr)))) {
3813                                                 found = 1;
3814                                                 break;
3815                                         }
3816                                 }
3817
3818                                 ptr = GET_NEXT(ptr);
3819                         }
3820
3821                         if (!found)
3822                                 return "*Invalid target ship or waypoint name";
3823
3824                         if (found == 2) {
3825                                 if (wing >= 0) {  // check if target ship is in wing
3826                                         if (Ships[inst].wingnum == wing && Objects[Ships[inst].objnum].type != OBJ_START)
3827                                                 return "Target ship of wing's goal is within said wing";
3828                                 }
3829
3830                                 team2 = Ships[inst].team;
3831                         }
3832                 }
3833
3834                 switch (goals[i].ai_mode) {
3835                         case AI_GOAL_DESTROY_SUBSYSTEM:
3836                                 SDL_assert(flag == 2 && inst >= 0);
3837                                 if (ship_get_subsys_index(&Ships[inst], goals[i].docker.name, 1) < 0)
3838                                         return "Unknown subsystem type";
3839
3840                                 break;
3841
3842                         case AI_GOAL_DOCK: {
3843                                 int dock1 = -1, dock2 = -1, model1, model2;
3844
3845                                 SDL_assert(flag == 2 && inst >= 0);
3846                                 if (!ship_docking_valid(ship, inst))
3847                                         return "Docking illegal between given ship types";
3848
3849                                 model1 = Ships[ship].modelnum;
3850                                 num = get_docking_list(model1);
3851                                 for (j=0; j<num; j++) {
3852                                         SDL_assert(Docking_bay_list[j]);
3853                                         if (!stricmp(goals[i].docker.name, Docking_bay_list[j])) {
3854                                                 dock1 = j;
3855                                                 break;
3856                                         }
3857                                 }
3858
3859                                 model2 = Ships[inst].modelnum;
3860                                 num = get_docking_list(model2);
3861                                 for (j=0; j<num; j++) {
3862                                         SDL_assert(Docking_bay_list[j]);
3863                                         if (!stricmp(goals[i].dockee.name, Docking_bay_list[j])) {
3864                                                 dock2 = j;
3865                                                 break;
3866                                         }
3867                                 }
3868
3869                                 if (dock1 < 0)
3870                                         return "Invalid docker point";
3871
3872                                 if (dock2 < 0)
3873                                         return "Invalid dockee point";
3874
3875                                 if ((dock1 >= 0) && (dock2 >= 0)) {
3876                                         if ( !(model_get_dock_index_type(model1, dock1) & model_get_dock_index_type(model2, dock2)) )
3877                                                 return "Dock points are incompatible";
3878                                 }
3879
3880                                 break;
3881                         }
3882                 }
3883
3884                 switch (goals[i].ai_mode) {
3885                         case AI_GOAL_GUARD:
3886                         case AI_GOAL_GUARD_WING:
3887                                 // if ((team == TEAM_HOSTILE && team2 == TEAM_FRIENDLY) || (team == TEAM_FRIENDLY && team2 == TEAM_HOSTILE)) {
3888                                 if (team != team2) {    //      MK, added support for TEAM_NEUTRAL.  Won't this work?
3889                                         if (ship >= 0)
3890                                                 return "Ship assigned to guard opposite team";
3891                                         else
3892                                                 return "Wing assigned to guard opposite team";
3893                                 }
3894
3895                                 break;
3896
3897                         case AI_GOAL_CHASE:
3898                         case AI_GOAL_CHASE_WING:
3899                         case AI_GOAL_DESTROY_SUBSYSTEM:
3900                         case AI_GOAL_DISARM_SHIP:
3901                         case AI_GOAL_DISABLE_SHIP:
3902                                 if (team == team2) {
3903                                         if (ship >= 0)
3904                                                 return "Ship assigned to attack same team";
3905                                         else
3906                                                 return "Wings assigned to attack same team";
3907                                 }
3908                                         
3909                                 break;
3910                 }
3911         }
3912
3913         return NULL;
3914 }
3915
3916 // function (which is called externally from message editor and event editor) so skip through
3917 // the sexpression nodes to look for send-message commands to try to associate message personas
3918 // to ships
3919 void fred_check_message_personas()
3920 {
3921 /*
3922         int i, op, j, ship_index;
3923         char *mname, *who_from;
3924         object *objp;
3925
3926         // this function is responsible for assigning personas to ships as well as error checking them.
3927         // clear out the persona index on all ship objects
3928         for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
3929                 if ( objp->type == OBJ_SHIP ) {
3930                         Ships[objp->instance].persona_index = -1;
3931                 }
3932         }
3933
3934
3935         for (i = 0; i < MAX_SEXP_NODES; i++ ) {
3936                 if ( Sexp_nodes[i].type == SEXP_NOT_USED )
3937                         continue;
3938
3939                 // look for only operator nodes
3940                 if ( Sexp_nodes[i].subtype != SEXP_ATOM_OPERATOR )
3941                         continue;
3942
3943                 // now look for the send-message opeator
3944                 op = find_operator( Sexp_nodes[i].text );
3945                 if ( op != OP_SEND_MESSAGE )
3946                         continue;
3947
3948                 // have the message.  parse through the message to determine who is sending the message.
3949                 who_from = CTEXT(CDR(i));
3950
3951                 // we can ignore messages from any wingman, and allied, or from God.
3952                 if ( !stricmp(who_from, "<Any wingman>") || !stricmp(who_from, "<Any allied>") || (who_from[0] == '#') )
3953                         continue;
3954
3955                 mname = CTEXT(CDR(CDR(CDR(i))));
3956
3957                 // check to see if who_from is a wing.  Don't do processing if so.
3958                 if ( wing_name_lookup(who_from, 1) != -1 )
3959                         continue;
3960
3961                 ship_index = ship_name_lookup( who_from );
3962                 if ( ship_index == -1 ) {
3963                         Int3();                 // get allender.  something funny is up with shipnames in send-message
3964                         continue;
3965                 }
3966
3967                 for ( j = Num_builtin_messages; j < Num_messages; j++ ) {
3968                         if ( !stricmp(mname, Messages[j].name) ) {
3969
3970                                 // check to see if there is a persona for this message -- if not, bail
3971                                 if ( Messages[j].persona_index == -1 )
3972                                         break;
3973
3974                                 // if a ship isn't assigned a persona, and this message says that he is, assign it, and move on
3975                                 if ( Ships[ship_index].persona_index == -1 ) {
3976                                         Ships[ship_index].persona_index = Messages[j].persona_index;
3977                                         continue;
3978                                 }
3979
3980                                 // we must be sure of the following conditions:
3981                                 // 1) a ship isn't assigned > 1 persona
3982                                 
3983                                 if ( Ships[ship_index].persona_index != Messages[j].persona_index )
3984                                         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 );
3985                         }
3986                 }
3987         }
3988
3989         // check that two or more ships are not using the same persona
3990         for (i = 0; i < Num_personas; i++ ) {
3991                 int persona_count;
3992                 object *objp;
3993
3994                 // move through object list looking for number of shis using this persona
3995                 persona_count = 0;
3996                 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
3997                         if ( objp->type != OBJ_SHIP )
3998                                 continue;
3999                         if (Ships[objp->instance].persona_index == i )
4000                                 persona_count++;
4001                 }
4002
4003                 if ( persona_count > 1 )
4004                         Fred_view_wnd->error("Persona %s used by more than 1 ship", Personas[Messages[j].persona_index].name );
4005         }
4006 */
4007
4008 }
4009
4010 void CFREDView::OnViewOutlines() 
4011 {
4012         Show_outlines = !Show_outlines;
4013         theApp.write_ini_file();
4014         Update_window = 1;
4015 }
4016
4017 void CFREDView::OnUpdateViewOutlines(CCmdUI* pCmdUI) 
4018 {
4019         pCmdUI->SetCheck(Show_outlines);
4020 }
4021
4022 void CFREDView::OnUpdateNewShipType(CCmdUI* pCmdUI) 
4023 {
4024         int z;
4025         CWnd *bar;
4026
4027         z = m_new_ship_type_combo_box.GetCurSelNEW();
4028         if (z == CB_ERR)
4029                 m_new_ship_type_combo_box.SetCurSelNEW(cur_model_index);
4030         else
4031                 cur_model_index = z;
4032
4033         bar = GetDlgItem(pCmdUI->m_nID);
4034         if (!bar) {
4035                 pCmdUI -> ContinueRouting();
4036                 return; // not for us
4037         }
4038
4039         pCmdUI -> SetCheck((bar->GetStyle() & WS_VISIBLE) != 0);
4040 }
4041
4042 void CFREDView::OnShowStarfield() 
4043 {
4044         Show_stars = !Show_stars;
4045         theApp.write_ini_file();
4046         Update_window = 1;
4047 }
4048
4049 void CFREDView::OnUpdateShowStarfield(CCmdUI* pCmdUI) 
4050 {
4051         pCmdUI->SetCheck(Show_stars);
4052 }
4053
4054 void CFREDView::OnAsteroidEditor() 
4055 {
4056         asteroid_editor dlg;
4057
4058         dlg.DoModal();
4059 }
4060
4061 void CFREDView::OnRunFreespace() 
4062 {
4063         BOOL r;
4064         STARTUPINFO si;
4065         PROCESS_INFORMATION pi;
4066         char *lpMsgBuf;
4067
4068         if (!FREDDoc_ptr->SaveModified())
4069                 return;
4070
4071         si.cb = sizeof(si);
4072         si.lpReserved = NULL;
4073         si.lpDesktop = NULL;
4074         si.lpTitle = NULL;
4075         si.dwFlags = 0;
4076         si.cbReserved2 = 0;
4077         si.lpReserved2 = NULL;
4078
4079         r = CreateProcess("..\\..\\Fs.exe", NULL, NULL, NULL, FALSE, 0, NULL, "..\\..", &si, &pi);
4080         if (!r) {
4081                 FormatMessage(
4082                          FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
4083                          NULL,
4084                          GetLastError(),
4085                          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
4086                          (LPTSTR) &lpMsgBuf,
4087                          0,
4088                          NULL
4089                 );
4090
4091                 // Display the string.
4092                 MessageBox(lpMsgBuf);
4093
4094                 // Free the buffer.
4095                 LocalFree( lpMsgBuf );
4096         }
4097 }
4098
4099 void CFREDView::OnEditorCampaign() 
4100 {
4101         if (!FREDDoc_ptr->SaveModified())
4102                 return;
4103
4104         SDL_assert(!Campaign_wnd);
4105         Campaign_wnd = new campaign_tree_wnd;
4106         if (Campaign_wnd->Create(NULL, "Campaign Editor", WS_OVERLAPPEDWINDOW | WS_MAXIMIZE,
4107                 CFrameWnd::rectDefault, NULL, "IDR_MENU_CAMPAIGN")) {
4108                 Campaign_wnd->ShowWindow(SW_SHOW);
4109                 Campaign_wnd->UpdateWindow();
4110         }
4111 }
4112
4113 void CFREDView::OnShowShips() 
4114 {
4115         Show_ships = !Show_ships;       
4116         correct_marking();
4117         Update_window = 1;
4118 }
4119
4120 void CFREDView::OnUpdateShowShips(CCmdUI* pCmdUI) 
4121 {
4122         pCmdUI->SetCheck(Show_ships);
4123 }
4124
4125 void CFREDView::OnShowStarts() 
4126 {
4127         Show_starts = !Show_starts;
4128         correct_marking();
4129         Update_window = 1;
4130 }
4131
4132 void CFREDView::OnUpdateShowStarts(CCmdUI* pCmdUI) 
4133 {
4134         pCmdUI->SetCheck(Show_starts);
4135 }
4136
4137 void CFREDView::OnShowFriendly() 
4138 {
4139         Show_friendly = !Show_friendly;
4140         correct_marking();
4141         Update_window = 1;
4142 }
4143
4144 void CFREDView::OnUpdateShowFriendly(CCmdUI* pCmdUI) 
4145 {
4146         pCmdUI->SetCheck(Show_friendly);
4147 }
4148
4149 void CFREDView::OnShowHostile() 
4150 {
4151         Show_hostile = !Show_hostile;
4152         correct_marking();
4153         Update_window = 1;
4154 }
4155
4156 void CFREDView::OnUpdateShowHostile(CCmdUI* pCmdUI) 
4157 {
4158         pCmdUI->SetCheck(Show_hostile);
4159 }
4160
4161 void CFREDView::OnToggleViewpoint() 
4162 {
4163         if (viewpoint || !query_valid_object())
4164                 viewpoint = 0;
4165
4166         else {
4167                 viewpoint = 1;
4168                 view_obj = cur_object_index;
4169         }
4170
4171         Update_window = 1;
4172 }
4173
4174 void CFREDView::OnRevert() 
4175 {
4176         if (!FREDDoc_ptr->SaveModified())
4177                 return;
4178
4179         FREDDoc_ptr->DeleteContents();
4180         FREDDoc_ptr->OnOpenDocument(NULL);
4181 }
4182
4183 void CFREDView::OnUpdateRevert(CCmdUI* pCmdUI) 
4184 {
4185         pCmdUI->Enable(*Mission_filename);
4186 }
4187
4188 BOOL CFREDView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
4189 {
4190         if ((Cursor_over >= 0) || Selection_lock) {
4191                 if (Editing_mode == 1) {
4192                         SetCursor(h_cursor_move);
4193                         return TRUE;
4194
4195                 } else if (Editing_mode == 2) {
4196                         SetCursor(h_cursor_rotate);
4197                         return TRUE;
4198                 }
4199         }
4200
4201         return CView::OnSetCursor(pWnd, nHitTest, message);
4202 }
4203
4204 void CFREDView::OnHideObjects() 
4205 {
4206         object *ptr;
4207
4208         ptr = GET_FIRST(&obj_used_list);
4209         while (ptr != END_OF_LIST(&obj_used_list)) {
4210                 if (ptr->flags & OF_MARKED) {
4211                         ptr->flags |= OF_HIDDEN;
4212                         unmark_object(OBJ_INDEX(ptr));
4213                 }
4214
4215                 ptr = GET_NEXT(ptr);
4216         }
4217 }
4218
4219 void CFREDView::OnShowHiddenObjects() 
4220 {
4221         object *ptr;
4222
4223         ptr = GET_FIRST(&obj_used_list);
4224         while (ptr != END_OF_LIST(&obj_used_list)) {
4225                 ptr->flags &= ~OF_HIDDEN;
4226                 ptr = GET_NEXT(ptr);
4227         }
4228
4229         Update_window = 1;
4230 }
4231
4232 void CFREDView::OnEditUndo() 
4233 {
4234         vector viewer_pos;
4235         matrix viewer_orient;
4236
4237         if (Undo_available) {
4238                 viewer_pos = view_pos;
4239                 viewer_orient = view_orient;
4240                 FREDDoc_ptr->autoload();
4241                 view_pos = viewer_pos;
4242                 view_orient = viewer_orient;
4243         }
4244 }
4245
4246 void CFREDView::OnUpdateEditUndo(CCmdUI* pCmdUI) 
4247 {
4248         pCmdUI->Enable(Undo_available);
4249 }
4250
4251 void CFREDView::OnEditorsBriefing() 
4252 {
4253         if (!Briefing_dialog) {
4254                 Briefing_dialog = new briefing_editor_dlg;
4255                 Briefing_dialog->create();
4256         }
4257
4258         Briefing_dialog->SetWindowPos(&wndTop, 0, 0, 0, 0,
4259                 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
4260         Briefing_dialog->ShowWindow(SW_RESTORE);
4261 }
4262
4263 void CFREDView::OnEditorsDebriefing() 
4264 {
4265         debriefing_editor_dlg dlg;
4266
4267         dlg.DoModal();
4268 }
4269
4270 void CFREDView::OnSaveCamera() 
4271 {
4272         saved_cam_pos = view_pos;
4273         saved_cam_orient = view_orient;
4274 }
4275
4276 void CFREDView::OnRestoreCamera() 
4277 {
4278         view_pos = saved_cam_pos;
4279         view_orient = saved_cam_orient;
4280         Update_window = 1;
4281 }
4282
4283 void CFREDView::OnUpdateRestoreCamera(CCmdUI* pCmdUI) 
4284 {
4285         pCmdUI->Enable(!IS_VEC_NULL(&saved_cam_orient.fvec));
4286 }
4287
4288 void CFREDView::OnShowSexpHelp() 
4289 {
4290         CRect rect;
4291
4292         Show_sexp_help = !Show_sexp_help;
4293         Ship_editor_dialog.show_hide_sexp_help();
4294         Wing_editor_dialog.show_hide_sexp_help();
4295
4296         if (Event_editor_dlg) {
4297                 Event_editor_dlg->GetWindowRect(rect);
4298                 if (Show_sexp_help)
4299                         rect.bottom += SEXP_HELP_BOX_SIZE;
4300                 else
4301                         rect.bottom -= SEXP_HELP_BOX_SIZE;
4302
4303                 Event_editor_dlg->MoveWindow(rect);
4304         }
4305 }
4306
4307 void CFREDView::OnUpdateShowSexpHelp(CCmdUI* pCmdUI) 
4308 {
4309         pCmdUI->SetCheck(Show_sexp_help);
4310 }
4311
4312 void CFREDView::OnLookatObj() 
4313 {
4314         Lookat_mode = !Lookat_mode;
4315         if (Lookat_mode && query_valid_object()) {
4316                 vector v, loc;
4317                 matrix m;
4318
4319                 loc = Objects[cur_object_index].pos;
4320                 vm_vec_sub(&v, &loc, &view_pos);
4321
4322                 if (v.x || v.y || v.z) {
4323                         vm_vector_2_matrix(&m, &v, NULL, NULL);
4324                         view_orient = m;
4325                 }
4326         }
4327 }
4328
4329 void CFREDView::OnUpdateLookatObj(CCmdUI* pCmdUI) 
4330 {
4331         pCmdUI->SetCheck(Lookat_mode);
4332 }
4333
4334 void CFREDView::OnGroup(UINT nID)
4335 {
4336         int n = 1 << (nID - ID_GROUP1);
4337         object *objp;
4338
4339         unmark_all();
4340         objp = GET_FIRST(&obj_used_list);
4341         while (objp != END_OF_LIST(&obj_used_list))     {
4342                 if (objp->type == OBJ_SHIP) {
4343                         if (Ships[objp->instance].group & n)
4344                                 mark_object(OBJ_INDEX(objp));
4345                 }
4346
4347                 objp = GET_NEXT(objp);
4348         }
4349
4350         Update_window = 1;
4351 }
4352
4353 void CFREDView::OnSetGroup(UINT nID)
4354 {
4355         int i, err = 0, n = 1 << (nID - ID_SET_GROUP1);
4356         object *objp;
4357
4358         for (i=0; i<MAX_SHIPS; i++)
4359                 Ships[i].group &= ~n;
4360
4361         objp = GET_FIRST(&obj_used_list);
4362         while (objp != END_OF_LIST(&obj_used_list)) {
4363                 if (objp->flags & OF_MARKED) {
4364                         if (objp->type == OBJ_SHIP) {
4365                                 Ships[objp->instance].group |= n;
4366
4367                         } else
4368                                 err = 1;
4369                 }
4370
4371                 objp = GET_NEXT(objp);
4372         }
4373
4374         if (err)
4375                 Fred_main_wnd->MessageBox("Only ships can be in groups, and not players or waypoints, etc.\n"
4376                         "These illegal objects you marked were not placed in the group");
4377
4378         Update_window = 1;
4379 }
4380
4381 void CFREDView::OnInitialUpdate() 
4382 {
4383         char *ptr, text[512];
4384
4385         CView::OnInitialUpdate();
4386         
4387         // check the time/checksum strings.
4388         expire_game = 0;
4389         ptr = &stamp[0];
4390         if ( memcmp(ptr, DEFAULT_CHECKSUM_STRING, strlen(DEFAULT_CHECKSUM_STRING)) ) {
4391                 int stamped_checksum, checksum;
4392
4393                 // the checksum is not the default checksum.  Calculate the checksum of the string
4394                 // and compare it.
4395                 memcpy(&stamped_checksum, ptr, sizeof(stamped_checksum) );
4396                 ptr = &stamp[0];
4397                 ptr += 8;                       // get us to the actual string to calculate the checksum
4398                 CALCULATE_STAMP_CHECKSUM();
4399
4400                 if ( checksum != stamped_checksum ){
4401                         expire_game = EXPIRE_BAD_CHECKSUM;
4402                 }
4403
4404                 // now check the time
4405                 ptr = &stamp[0];
4406                 ptr += 4;
4407                 if ( memcmp( ptr, DEFAULT_TIME_STRING, strlen(DEFAULT_TIME_STRING)) ) {
4408                         int expire_time, current_time;
4409
4410                         // not the default time -- check against the current time
4411                         memcpy( &expire_time, ptr, sizeof(expire_time) );
4412                         time( (long *)&current_time );
4413                         if ( current_time > expire_time )
4414                                 expire_game = EXPIRE_BAD_TIME;
4415                 }
4416
4417                 // since the default checksum has changed -- put up a message which shows who the program
4418                 // is stamped for
4419                 ptr = &stamp[0];
4420                 ptr += 8;
4421                 sprintf(text, "This version of Fred has been compiled for %s", ptr);
4422                 MessageBox(text, NULL, MB_OK);
4423
4424                 if ( expire_game )
4425                         SetTimer(1, FRED_EXPIRE_TIME, expire_game_proc);
4426         }
4427 }
4428
4429 void CFREDView::OnEditorsAdjustGrid() 
4430 {
4431         adjust_grid_dlg dlg;
4432
4433         dlg.DoModal();
4434         Update_window = 1;
4435 }
4436
4437 void CFREDView::OnEditorsShieldSys() 
4438 {
4439         shield_sys_dlg dlg;
4440
4441         dlg.DoModal();
4442 }
4443
4444 void CFREDView::OnLevelObj() 
4445 {
4446         level_controlled();
4447         Update_window = 1;
4448 }
4449
4450 void CFREDView::OnAlignObj() 
4451 {
4452         verticalize_controlled();
4453         Update_window = 1;
4454 }
4455
4456 void CFREDView::OnControlObj() 
4457 {
4458         Control_mode = (Control_mode + 1) % 2;
4459 }
4460
4461 void CFREDView::OnNextObj() 
4462 {       
4463         object *ptr;
4464
4465         if (Bg_bitmap_dialog) {
4466                 if (Cur_bitmap == -1)
4467                 {
4468                         if (Num_starfield_bitmaps)
4469                         {
4470                                 Cur_bitmap = 0;
4471                                 Bg_bitmap_dialog -> update_data();
4472                         }
4473
4474                         return;
4475                 }
4476
4477                 Cur_bitmap++;
4478                 if (Cur_bitmap >= Num_starfield_bitmaps)
4479                         Cur_bitmap = 0;
4480
4481                 Bg_bitmap_dialog -> update_data();
4482                 return;
4483         }
4484
4485         if (EMPTY(&obj_used_list))
4486                 return;
4487
4488         if (query_valid_object())       {
4489                 ptr = Objects[cur_object_index].next;
4490                 if (ptr == END_OF_LIST(&obj_used_list))
4491                         ptr = GET_NEXT(ptr);
4492         
4493         } else
4494                 ptr = GET_FIRST(&obj_used_list);
4495
4496         if (Marked > 1) {  // cycle through marked list
4497                 while (!(ptr->flags & OF_MARKED))
4498                 {
4499                         ptr = GET_NEXT(ptr);
4500                         if (ptr == END_OF_LIST(&obj_used_list))
4501                                 ptr = GET_NEXT(ptr);
4502                 }
4503
4504                 set_cur_object_index(OBJ_INDEX(ptr));
4505
4506         } else {
4507                 if (Marked)
4508                         unmark_object(cur_object_index);
4509
4510                 mark_object(OBJ_INDEX(ptr));
4511         }
4512 }
4513
4514 void CFREDView::OnPrevObj() 
4515 {
4516         int arr[MAX_OBJECTS], i = 0, n = 0;
4517         object *ptr;
4518
4519         if (Bg_bitmap_dialog) {
4520                 if (Cur_bitmap == -1)
4521                 {
4522                         if (Num_starfield_bitmaps)
4523                         {
4524                                 Cur_bitmap = Num_starfield_bitmaps - 1;
4525                                 Bg_bitmap_dialog -> update_data();
4526                         }
4527
4528                         return;
4529                 }
4530
4531                 Cur_bitmap--;
4532                 if (Cur_bitmap < 0)
4533                         Cur_bitmap = Num_starfield_bitmaps - 1;
4534
4535                 Bg_bitmap_dialog -> update_data();
4536                 return;
4537         }
4538
4539         if (EMPTY(&obj_used_list))
4540                 return;
4541
4542         ptr = GET_FIRST(&obj_used_list);
4543         while (ptr != END_OF_LIST(&obj_used_list)) {
4544                 if (cur_object_index == OBJ_INDEX(ptr))
4545                         i = n;
4546
4547                 arr[n++] = OBJ_INDEX(ptr);
4548                 ptr = GET_NEXT(ptr);
4549         }
4550
4551         SDL_assert(n);
4552         if (query_valid_object()) {
4553                 i--;
4554                 if (i < 0)
4555                         i = n - 1;
4556         
4557         } else
4558                 i = n - 1;
4559
4560         if (Marked > 1) {  // cycle through marked list
4561                 while (!(Objects[i].flags & OF_MARKED))
4562                 {
4563                         i--;
4564                         if (i < 0)
4565                                 i = n - 1;
4566                 }
4567
4568                 set_cur_object_index(i);
4569
4570         } else {
4571                 if (Marked)
4572                         unmark_object(cur_object_index);
4573
4574                 mark_object(i);
4575         }
4576 }
4577
4578 void CFREDView::OnEditDelete() 
4579 {
4580         if (!button_down && Marked) {
4581                 delete_marked();
4582                 FREDDoc_ptr->autosave("object delete");
4583         }
4584
4585         Update_window = 2;  // For some strange reason, need to redraw twice for it to take.
4586 }
4587
4588 void CFREDView::OnEditDeleteWing() 
4589 {
4590         if (!button_down && (cur_wing >= 0)) {
4591                 delete_wing();
4592                 FREDDoc_ptr->autosave("wing delete");
4593                 cur_wing = -1;
4594                 if (!Marked)
4595                         Ship_editor_dialog.initialize_data(1);
4596
4597                 Wing_editor_dialog.initialize_data(1);
4598         }
4599
4600         Update_window = 2;  // For some strange reason, need to redraw twice for it to take.
4601 }
4602
4603 void CFREDView::OnMarkWing() 
4604 {
4605         int i, wing = cur_wing;
4606
4607         if (wing != -1)
4608         {
4609                 unmark_all();
4610                 for (i=0; i<Wings[wing].wave_count; i++)
4611                         mark_object(wing_objects[wing][i]);
4612
4613                 SDL_assert(Wings[wing].special_ship >= 0 && Wings[wing].special_ship < Wings[wing].wave_count);
4614                 set_cur_object_index(wing_objects[wing][Wings[wing].special_ship]);
4615         }
4616 }
4617
4618 void CFREDView::OnUpdateControlObj(CCmdUI* pCmdUI) 
4619 {
4620         pCmdUI->SetCheck(Control_mode != 0);
4621 }
4622
4623 void CFREDView::OnAaGridlines()
4624 {
4625         Aa_gridlines = !Aa_gridlines;
4626 }
4627
4628 void CFREDView::OnUpdateAaGridlines(CCmdUI* pCmdUI)
4629 {
4630         pCmdUI->SetCheck(Aa_gridlines);
4631         Update_window = 1;
4632 }
4633
4634 void CFREDView::OnCmdBrief() 
4635 {
4636         cmd_brief_dlg dlg;
4637
4638         dlg.DoModal();
4639         Update_window = 1;
4640 }
4641
4642 void CFREDView::OnDisableUndo() 
4643 {
4644         Autosave_disabled = !Autosave_disabled;
4645 }
4646
4647 void CFREDView::OnUpdateDisableUndo(CCmdUI* pCmdUI) 
4648 {
4649         pCmdUI->SetCheck(Autosave_disabled);
4650 }
4651
4652 void CFREDView::OnUpdateCmdBrief(CCmdUI* pCmdUI) 
4653 {
4654         pCmdUI->Enable(!(The_mission.game_type & MISSION_TYPE_MULTI));
4655 }
4656
4657 int get_visible_sub_system_count(ship *shipp)
4658 {
4659         int count = 0;
4660         ship_subsys *cur_subsys;
4661
4662         for (cur_subsys = GET_FIRST(&shipp->subsys_list); cur_subsys != END_OF_LIST(&shipp->subsys_list); cur_subsys = GET_NEXT(cur_subsys)) {
4663                 if (cur_subsys->system_info->subobj_num != -1) {
4664                         count++;
4665                 }
4666         }
4667
4668         return count;
4669 }
4670
4671 int get_next_visible_subsys(ship *shipp, ship_subsys **next_subsys)
4672 {
4673         int count = get_visible_sub_system_count(shipp);
4674
4675         // return don't try to display
4676         if (count == 0) {
4677                 return 0;
4678         }
4679
4680         // first timer
4681         if (*next_subsys == NULL) {
4682                 *next_subsys = &shipp->subsys_list;
4683         }
4684
4685         // look before wrap
4686         for (*next_subsys = GET_NEXT(*next_subsys); *next_subsys != END_OF_LIST(&shipp->subsys_list); *next_subsys = GET_NEXT(*next_subsys)) {
4687                 if ((*next_subsys)->system_info->subobj_num != -1) {
4688                         Update_window = 1;
4689                         return 1;
4690                 }
4691         }
4692
4693         // look for first after wrap
4694         for (*next_subsys = GET_FIRST(&shipp->subsys_list); *next_subsys != END_OF_LIST(&shipp->subsys_list); *next_subsys = GET_NEXT(*next_subsys)) {
4695                 if ((*next_subsys)->system_info->subobj_num != -1) {
4696                         Update_window = 1;
4697                         return 1;
4698                 }
4699         }
4700
4701         Int3(); // should be impossible to miss
4702         return 0;
4703 }
4704
4705 int get_prev_visible_subsys(ship *shipp, ship_subsys **prev_subsys)
4706 {
4707         int count = get_visible_sub_system_count(shipp);
4708
4709         // return don't try to display
4710         if (count == 0) {
4711                 return 0;
4712         }
4713
4714         // first timer
4715         SDL_assert(*prev_subsys != NULL);
4716
4717         // look before wrap
4718         for (*prev_subsys = GET_PREV(*prev_subsys); *prev_subsys != END_OF_LIST(&shipp->subsys_list); *prev_subsys = GET_PREV(*prev_subsys)) {
4719                 if ((*prev_subsys)->system_info->subobj_num != -1) {
4720                         Update_window = 1;
4721                         return 1;
4722                 }
4723         }
4724
4725         // look for first after wrap
4726         for (*prev_subsys = GET_LAST(&shipp->subsys_list); *prev_subsys != END_OF_LIST(&shipp->subsys_list); *prev_subsys = GET_PREV(*prev_subsys)) {
4727                 if ((*prev_subsys)->system_info->subobj_num != -1) {
4728                         Update_window = 1;
4729                         return 1;
4730                 }
4731         }
4732
4733         Int3(); // should be impossible to miss
4734         return 0;
4735 }
4736
4737 // update next subsystem to view
4738 void CFREDView::OnNextSubsys() 
4739 {
4740         object *objp;
4741
4742         if (cur_object_index < 0) {
4743                 OnCancelSubsys();
4744         }
4745
4746         objp = &Objects[cur_object_index];
4747
4748         // check if cur object is ship type
4749         if (objp->type == OBJ_SHIP) {
4750
4751                 // check if same ship
4752                 if (Render_subsys.ship_obj == objp) {
4753
4754                         // if already on, advance to next
4755                         if (Render_subsys.do_render) {
4756                                 if ( !get_next_visible_subsys(&Ships[objp->instance], &Render_subsys.cur_subsys) ) {
4757                                         OnCancelSubsys();
4758                                 }
4759                         } else {
4760                                 Int3();
4761                         }
4762                 } else {
4763                         // clean up
4764                         OnCancelSubsys();
4765
4766                         // set up new and advance to first
4767                         Render_subsys.do_render = true;
4768                         Render_subsys.ship_obj = objp;
4769                         if ( !get_next_visible_subsys(&Ships[objp->instance], &Render_subsys.cur_subsys) ) {
4770                                 OnCancelSubsys();
4771                         }
4772                 }
4773         } else {
4774                 // not ship type
4775                 OnCancelSubsys();
4776         }
4777 }
4778
4779 void CFREDView::OnPrevSubsys() 
4780 {
4781         if (!Render_subsys.do_render) {
4782                 return;
4783         }
4784
4785         if ( (cur_object_index < 0)  || (Objects[cur_object_index].type != OBJ_SHIP) || (&Objects[cur_object_index] != Render_subsys.ship_obj) ) {
4786                 OnCancelSubsys();
4787                 return;
4788         }
4789
4790         if ( !get_prev_visible_subsys(&Ships[Objects[cur_object_index].instance], &Render_subsys.cur_subsys) ) {
4791                 OnCancelSubsys();
4792         }
4793
4794 }
4795
4796 void CFREDView::OnCancelSubsys()
4797 {
4798         Render_subsys.do_render = false;
4799         Render_subsys.ship_obj = NULL;
4800         Render_subsys.cur_subsys = NULL;
4801         Update_window = 1;
4802 }
4803
4804 void CFREDView::OnShowPaths()
4805 {
4806         Show_paths_fred = !Show_paths_fred;
4807         theApp.write_ini_file();
4808         Update_window = 1;
4809 }
4810
4811 void CFREDView::OnUpdateShowPaths(CCmdUI* pCmdUI)
4812 {
4813         pCmdUI->SetCheck(Show_paths_fred);
4814 }
4815
4816 void CFREDView::OnShowDockPoints()
4817 {
4818         Show_dock_points = !Show_dock_points;
4819         theApp.write_ini_file();
4820         Update_window = 1;
4821 }
4822
4823 void CFREDView::OnUpdateShowDockPoints(CCmdUI* pCmdUI)
4824 {       
4825         pCmdUI->SetCheck(Show_dock_points);
4826 }
4827
4828 void CFREDView::OnDumpStats()
4829 {
4830         DumpStats dlg;
4831
4832         dlg.DoModal();
4833 }