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