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