Oolite 1.91.0.7604-240417-a536cbe
Loading...
Searching...
No Matches
GameController.m
Go to the documentation of this file.
1/*
2
3GameController.m
4
5Oolite
6Copyright (C) 2004-2013 Giles C Williams and contributors
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21MA 02110-1301, USA.
22
23*/
24
25#import "GameController.h"
26#import "Universe.h"
27#import "ResourceManager.h"
28#import "MyOpenGLView.h"
29#import "OOSound.h"
30#import "OOOpenGL.h"
32#include <stdlib.h>
34#import "OOOXPVerifier.h"
35#import "OOLoggingExtended.h"
38#import "OODebugFlags.h"
42#import "OODebugSupport.h"
43#import "legacy_random.h"
44#import "OOOXZManager.h"
46
47#if OOLITE_MAC_OS_X
49#import <Sparkle/Sparkle.h>
50#import "OoliteApp.h"
52
53static void SetUpSparkle(void);
54#elif (OOLITE_GNUSTEP && !defined(NDEBUG))
55#import "OODebugMonitor.h"
56#endif
57
58
60
61
62@interface GameController (OOPrivate)
63
64- (void)reportUnhandledStartupException:(NSException *)exception;
65
66- (void)doPerformGameTick;
67
68@end
69
70
71@implementation GameController
72
73+ (GameController *) sharedController
74{
76 {
77 sSharedController = [[self alloc] init];
78 }
79 return sSharedController;
80}
81
82
83- (id) init
84{
86 {
87 [self release];
88 [NSException raise:NSInternalInconsistencyException format:@"%s: expected only one GameController to exist at a time.", __PRETTY_FUNCTION__];
89 }
90
91 if ((self = [super init]))
92 {
93 last_timeInterval = [NSDate timeIntervalSinceReferenceDate];
94 delta_t = 0.01; // one hundredth of a second
95 _animationTimerInterval = [[NSUserDefaults standardUserDefaults] oo_doubleForKey:@"animation_timer_interval" defaultValue:0.005];
96
97 // rather than seeding this with the date repeatedly, seed it
98 // once here at startup
99 ranrot_srand((uint32_t)[[NSDate date] timeIntervalSince1970]); // reset randomiser with current time
100
101 _splashStart = [[NSDate alloc] init];
102 }
103
104 return self;
105}
106
107
108- (void) dealloc
109{
110#if OOLITE_MAC_OS_X
111 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:UNIVERSE];
112#endif
113
114 [timer release];
115 [gameView release];
116 [UNIVERSE release];
117
118 [playerFileToLoad release];
119 [playerFileDirectory release];
120 [expansionPathsToInclude release];
121
122 [super dealloc];
123}
124
125
126- (BOOL) isGamePaused
127{
128 return gameIsPaused;
129}
130
131
132- (void) setGamePaused:(BOOL)value
133{
134 if (value && !gameIsPaused)
135 {
136 _resumeMode = [self mouseInteractionMode];
137 [self setMouseInteractionModeForUIWithMouseInteraction:NO];
138 gameIsPaused = YES;
139 [PLAYER doScriptEvent:OOJSID("gamePaused")];
140 }
141 else if (!value && gameIsPaused)
142 {
143 [self setMouseInteractionMode:_resumeMode];
144 gameIsPaused = NO;
145 [PLAYER doScriptEvent:OOJSID("gameResumed")];
146 }
147}
148
149
150- (OOMouseInteractionMode) mouseInteractionMode
151{
152 return _mouseMode;
153}
154
155
156- (void) setMouseInteractionMode:(OOMouseInteractionMode)mode
157{
158 OOMouseInteractionMode oldMode = _mouseMode;
159 if (mode == oldMode) return;
160
161 _mouseMode = mode;
162 OOLog(@"input.mouseMode.changed", @"Mouse interaction mode changed from %@ to %@", OOStringFromMouseInteractionMode(oldMode), OOStringFromMouseInteractionMode(mode));
163
164#if OO_USE_FULLSCREEN_CONTROLLER
165 if ([self inFullScreenMode])
166 {
167 [_fullScreenController noteMouseInteractionModeChangedFrom:oldMode to:mode];
168 }
169 else
170#endif
171 {
172 [[self gameView] noteMouseInteractionModeChangedFrom:oldMode to:mode];
173 }
174}
175
176
177- (void) setMouseInteractionModeForFlight
178{
179 [self setMouseInteractionMode:[PLAYER isMouseControlOn] ? MOUSE_MODE_FLIGHT_WITH_MOUSE_CONTROL : MOUSE_MODE_FLIGHT_NO_MOUSE_CONTROL];
180}
181
182
183- (void) setMouseInteractionModeForUIWithMouseInteraction:(BOOL)interaction
184{
185 [self setMouseInteractionMode:interaction ? MOUSE_MODE_UI_SCREEN_WITH_INTERACTION : MOUSE_MODE_UI_SCREEN_NO_INTERACTION];
186}
187
188
189- (MyOpenGLView *) gameView
190{
191 return gameView;
192}
193
194
195- (void) setGameView:(MyOpenGLView *)view
196{
197 [gameView release];
198 gameView = [view retain];
199 [gameView setGameController:self];
200 [UNIVERSE setGameView:gameView];
201}
202
203
204- (void) applicationDidFinishLaunching:(NSNotification *)notification
205{
206 NSAutoreleasePool *pool = nil;
207 unsigned i;
208
209 pool = [[NSAutoreleasePool alloc] init];
210
211 @try
212 {
213 // if not verifying oxps, ensure that gameView is drawn to using beginSplashScreen
214 // OpenGL is initialised and that allows textures to initialise too.
215
216#if OO_OXP_VERIFIER_ENABLED
217
218 if ([OOOXPVerifier runVerificationIfRequested])
219 {
220 [self exitAppWithContext:@"OXP verifier run"];
221 }
222 else
223 {
224 [self beginSplashScreen];
225 }
226
227#else
228 [self beginSplashScreen];
229#endif
230
231#if OOLITE_MAC_OS_X
233 SetUpSparkle();
234#endif
235
236 [self setUpDisplayModes];
237
238 // moved to before the Universe is created
239 if (expansionPathsToInclude)
240 {
241 for (i = 0; i < [expansionPathsToInclude count]; i++)
242 {
243 [ResourceManager addExternalPath: (NSString*)[expansionPathsToInclude objectAtIndex: i]];
244 }
245 }
246
247 // initialise OXZ manager
249
250 // moved here to try to avoid initialising this before having an Open GL context
251 //[self logProgress:DESC(@"Initialising universe")]; // DESC expansions only possible after Universe init
252 [[Universe alloc] initWithGameView:gameView];
253
254 [self loadPlayerIfRequired];
255
256 [self logProgress:@""];
257
258 // get the run loop and add the call to performGameTick:
259 [self startAnimationTimer];
260
261 [self endSplashScreen];
262 }
263 @catch (NSException *exception)
264 {
265 [self reportUnhandledStartupException:exception];
266 exit(EXIT_FAILURE);
267 }
268
269 OOLog(@"startup.complete", @"========== Loading complete in %.2f seconds. ==========", -[_splashStart timeIntervalSinceNow]);
270
271#if OO_USE_FULLSCREEN_CONTROLLER
272 [self setFullScreenMode:[[NSUserDefaults standardUserDefaults] boolForKey:@"fullscreen"]];
273#endif
274
275 // Release anything allocated above that is not required.
276 [pool release];
277
278#if !OOLITE_MAC_OS_X
279 [[NSRunLoop currentRunLoop] run];
280#endif
281}
282
283
284- (void) loadPlayerIfRequired
285{
286 if (playerFileToLoad != nil)
287 {
288 [self logProgress:DESC(@"loading-player")];
289 // fix problem with non-shader lighting when starting skips
290 // the splash screen
291 [UNIVERSE useGUILightSource:YES];
292 [UNIVERSE useGUILightSource:NO];
293 [PLAYER loadPlayerFromFile:playerFileToLoad asNew:NO];
294 }
295}
296
297
298- (void) beginSplashScreen
299{
300#if !OOLITE_MAC_OS_X
301 if(!gameView)
302 {
303 gameView = [MyOpenGLView alloc];
304 [gameView init];
305 [gameView setGameController:self];
306 [gameView initSplashScreen];
307 }
308#else
309 [gameView updateScreen];
310#endif
311}
312
313
314#if OOLITE_MAC_OS_X
315
316- (void) performGameTick:(id)sender
317{
318 [gameView pollControls];
319 [self doPerformGameTick];
320}
321
322#else
323
324- (void) performGameTick:(id)sender
325{
326 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
327
328 [gameView pollControls];
329 [self doPerformGameTick];
330
331 [pool release];
332}
333
334#endif
335
336
337- (void) doPerformGameTick
338{
339 @try
340 {
341 if (gameIsPaused)
342 delta_t = 0.0; // no movement!
343 else
344 {
345 delta_t = [NSDate timeIntervalSinceReferenceDate] - last_timeInterval;
346 last_timeInterval += delta_t;
347 if (delta_t > MINIMUM_GAME_TICK)
348 delta_t = MINIMUM_GAME_TICK; // peg the maximum pause (at 0.5->1.0 seconds) to protect against when the machine sleeps
349 }
350
351 [UNIVERSE update:delta_t];
352 if (EXPECT_NOT([PLAYER status] == STATUS_RESTART_GAME))
353 {
354 [UNIVERSE reinitAndShowDemo:YES];
355 }
356 [OOSound update];
357 if (!gameIsPaused)
358 {
360 }
361 }
362 @catch (id exception)
363 {
364 OOLog(@"exception.backtrace",@"%@",[exception callStackSymbols]);
365 }
366
367 @try
368 {
369 [gameView display];
370 }
371 @catch (id exception) {}
372}
373
374
375- (void) startAnimationTimer
376{
377 if (timer == nil)
378 {
379 NSTimeInterval ti = _animationTimerInterval; // default one two-hundredth of a second (should be a fair bit faster than expected frame rate ~60Hz to avoid problems with phase differences)
380
381 timer = [[NSTimer timerWithTimeInterval:ti target:self selector:@selector(performGameTick:) userInfo:nil repeats:YES] retain];
382
383 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
384#if OOLITE_MAC_OS_X
385 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
386#endif
387
388 }
389}
390
391
392- (void) stopAnimationTimer
393{
394 if (timer != nil)
395 {
396 [timer invalidate];
397 [timer release];
398 timer = nil;
399 }
400}
401
402
403#if OOLITE_MAC_OS_X
404
405- (void) recenterVirtualJoystick
406{
407 // FIXME: does this really need to be spread across GameController and MyOpenGLView? -- Ahruman 2011-01-22
408 my_mouse_x = my_mouse_y = 0; // center mouse
409 [gameView setVirtualJoystick:0.0 :0.0];
410}
411
412
413- (IBAction) showLogAction:sender
414{
415 [[NSWorkspace sharedWorkspace] openFile:[OOLogHandlerGetLogBasePath() stringByAppendingPathComponent:@"Previous.log"]];
416}
417
418
419- (IBAction) showLogFolderAction:sender
420{
421 [[NSWorkspace sharedWorkspace] openFile:OOLogHandlerGetLogBasePath()];
422}
423
424
425// Helpers to allow -snapshotsURLCreatingIfNeeded: code to be identical here and in dock tile plug-in.
426static id GetPreference(NSString *key, Class expectedClass)
427{
428 id result = [[NSUserDefaults standardUserDefaults] objectForKey:key];
429 if (expectedClass != Nil && ![result isKindOfClass:expectedClass]) result = nil;
430
431 return result;
432}
433
434
435static void SetPreference(NSString *key, id value)
436{
437 [[NSUserDefaults standardUserDefaults] setObject:value forKey:key];
438}
439
440
441static void RemovePreference(NSString *key)
442{
443 [[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
444}
445
446
447#define kSnapshotsDirRefKey @"snapshots-directory-reference"
448#define kSnapshotsDirNameKey @"snapshots-directory-name"
449
450- (NSURL *) snapshotsURLCreatingIfNeeded:(BOOL)create
451{
452 BOOL stale = NO;
453 NSDictionary *snapshotDirDict = GetPreference(kSnapshotsDirRefKey, [NSDictionary class]);
454 NSURL *url = nil;
455 NSString *name = DESC(@"snapshots-directory-name-mac");
456
457 if (snapshotDirDict != nil)
458 {
460 if (url != nil)
461 {
462 NSString *existingName = [[url path] lastPathComponent];
463 if ([existingName compare:name options:NSCaseInsensitiveSearch] != 0)
464 {
465 // Check name from previous access, because we might have changed localizations.
466 NSString *originalOldName = GetPreference(kSnapshotsDirNameKey, [NSString class]);
467 if (originalOldName == nil || [existingName compare:originalOldName options:NSCaseInsensitiveSearch] != 0)
468 {
469 url = nil;
470 }
471 }
472
473 // did we put the old directory in the trash?
474 Boolean inTrash = false;
475 const UInt8 *utfPath = (const UInt8 *)[[url path] UTF8String];
476
477 OSStatus err = DetermineIfPathIsEnclosedByFolder(kOnAppropriateDisk, kTrashFolderType, utfPath, false, &inTrash);
478 // if so, create a new directory.
479 if (err == noErr && inTrash == true) url = nil;
480 }
481 }
482
483 if (url == nil)
484 {
485 NSString *path = nil;
486 NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);
487 if ([searchPaths count] > 0)
488 {
489 path = [[searchPaths objectAtIndex:0] stringByAppendingPathComponent:name];
490 }
491 url = [NSURL fileURLWithPath:path];
492
493 if (url != nil)
494 {
495 stale = YES;
496 if (create)
497 {
498 NSFileManager *fmgr = [NSFileManager defaultManager];
499 if (![fmgr fileExistsAtPath:path])
500 {
501 [fmgr oo_createDirectoryAtPath:path attributes:nil];
502 }
503 }
504 }
505 }
506
507 if (stale)
508 {
509 snapshotDirDict = JAPersistentFileReferenceFromURL(url);
510 if (snapshotDirDict != nil)
511 {
512 SetPreference(kSnapshotsDirRefKey, snapshotDirDict);
513 SetPreference(kSnapshotsDirNameKey, [[url path] lastPathComponent]);
514 }
515 else
516 {
517 RemovePreference(kSnapshotsDirRefKey);
518 }
519 }
520
521 return url;
522}
523
524
525- (IBAction) showSnapshotsAction:sender
526{
527 [[NSWorkspace sharedWorkspace] openURL:[self snapshotsURLCreatingIfNeeded:YES]];
528}
529
530
531- (IBAction) showAddOnsAction:sender
532{
533 NSArray *paths = ResourceManager.userRootPaths;
534
535 // Look for an AddOns directory that actually contains some AddOns.
536 for (NSString *path in paths) {
537 if ([self addOnsExistAtPath:path]) {
538 [self openPath:path];
539 return;
540 }
541 }
542
543 // If that failed, look for an AddOns directory that actually exists.
544 for (NSString *path in paths) {
545 if ([self isDirectoryAtPath:path]) {
546 [self openPath:path];
547 return;
548 }
549 }
550
551 // None found, create the default path.
552 [NSFileManager.defaultManager createDirectoryAtPath:[paths objectAtIndex:0]
553 withIntermediateDirectories:YES
554 attributes:nil
555 error:NULL];
556 [self openPath:[paths objectAtIndex:0]];
557}
558
559
560- (BOOL) isDirectoryAtPath:(NSString *)path
561{
562 BOOL isDirectory;
563 return [NSFileManager.defaultManager fileExistsAtPath:path isDirectory:&isDirectory] && isDirectory;
564}
565
566
567- (BOOL) addOnsExistAtPath:(NSString *)path
568{
569 if (![self isDirectoryAtPath:path]) return NO;
570
571 NSWorkspace *workspace = NSWorkspace.sharedWorkspace;
572 for (NSString *subPath in [NSFileManager.defaultManager enumeratorAtPath:path]) {
573 subPath = [path stringByAppendingPathComponent:subPath];
574 NSString *type = [workspace typeOfFile:subPath error:NULL];
575 if ([workspace type:type conformsToType:@"org.aegidian.oolite.expansion"]) return YES;
576 }
577
578 return NO;
579}
580
581
582- (void) openPath:(NSString *)path
583{
584 [NSWorkspace.sharedWorkspace openURL:[NSURL fileURLWithPath:path]];
585}
586
587
588- (BOOL) validateMenuItem:(NSMenuItem *)menuItem
589{
590 SEL action = menuItem.action;
591
592 if (action == @selector(showLogAction:))
593 {
594 // the first path is always Resources
595 return ([[NSFileManager defaultManager] fileExistsAtPath:[OOLogHandlerGetLogBasePath() stringByAppendingPathComponent:@"Previous.log"]]);
596 }
597
598 if (action == @selector(showAddOnsAction:))
599 {
600 // Always enabled in unrestricted mode, to allow users to add OXPs more easily.
601 return [ResourceManager useAddOns] != nil;
602 }
603
604 if (action == @selector(showSnapshotsAction:))
605 {
606 BOOL pathIsDirectory;
607 if(![[NSFileManager defaultManager] fileExistsAtPath:[self snapshotsURLCreatingIfNeeded:NO].path isDirectory:&pathIsDirectory])
608 {
609 return NO;
610 }
611 return pathIsDirectory;
612 }
613
614 if (action == @selector(toggleFullScreenAction:))
615 {
616 if (_fullScreenController.fullScreenMode)
617 {
618 // NOTE: not DESC, because menu titles are not generally localizable.
619 menuItem.title = NSLocalizedString(@"Exit Full Screen", NULL);
620 }
621 else
622 {
623 menuItem.title = NSLocalizedString(@"Enter Full Screen", NULL);
624 }
625 }
626
627 // default
628 return YES;
629}
630
631
632- (NSMenu *)applicationDockMenu:(NSApplication *)sender
633{
634 return dockMenu;
635}
636
637#elif OOLITE_SDL
638
639- (NSURL *) snapshotsURLCreatingIfNeeded:(BOOL)create
640{
641 NSURL *url = [NSURL fileURLWithPath:[NSHomeDirectory() stringByAppendingPathComponent:DESC(@"snapshots-directory-name")]];
642
643 if (create)
644 {
645 NSString *path = [url path];
646 NSFileManager *fmgr = [NSFileManager defaultManager];
647 if (![fmgr fileExistsAtPath:path])
648 {
649 [fmgr createDirectoryAtPath:path attributes:nil];
650 }
651 }
652 return url;
653}
654
655#else
656 #error Unknown environment!
657#endif
658
659- (void) logProgress:(NSString *)message
660{
661 if (![UNIVERSE doingStartUp]) return;
662
663#if OOLITE_MAC_OS_X
664 [splashProgressTextField setStringValue:message];
665 [splashProgressTextField display];
666#endif
667 if([message length] > 0)
668 {
669 OOLog(@"startup.progress", @"===== [%.2f s] %@", -[_splashStart timeIntervalSinceNow], message);
670 }
671}
672
673
674#if OO_DEBUG
675#if OOLITE_MAC_OS_X
676- (BOOL) debugMessageTrackingIsOn
677{
678 return splashProgressTextField != nil;
679}
680
681
682- (NSString *) debugMessageCurrentString
683{
684 return [splashProgressTextField stringValue];
685}
686#else
687- (BOOL) debugMessageTrackingIsOn
688{
689 return OOLogWillDisplayMessagesInClass(@"startup.progress");
690}
691
692
693- (NSString *) debugMessageCurrentString
694{
695 return @"";
696}
697#endif
698
699- (void) debugLogProgress:(NSString *)format, ...
700{
701 va_list args;
702 va_start(args, format);
703 [self debugLogProgress:format arguments:args];
704 va_end(args);
705}
706
707
708- (void) debugLogProgress:(NSString *)format arguments:(va_list)arguments
709{
710 NSString *message = [[[NSString alloc] initWithFormat:format arguments:arguments] autorelease];
711 [self logProgress:message];
712}
713
714
715static NSMutableArray *sMessageStack;
716
717- (void) debugPushProgressMessage:(NSString *)format, ...
718{
719 if ([self debugMessageTrackingIsOn])
720 {
721 if (sMessageStack == nil) sMessageStack = [[NSMutableArray alloc] init];
722 [sMessageStack addObject:[self debugMessageCurrentString]];
723
724 va_list args;
725 va_start(args, format);
726 [self debugLogProgress:format arguments:args];
727 va_end(args);
728 }
729
730 OOLogIndentIf(@"startup.progress");
731}
732
733
734- (void) debugPopProgressMessage
735{
736 OOLogOutdentIf(@"startup.progress");
737
738 if ([sMessageStack count] > 0)
739 {
740 NSString *message = [sMessageStack lastObject];
741 if ([message length] > 0) [self logProgress:message];
742 [sMessageStack removeLastObject];
743 }
744}
745
746#endif
747
748
749- (void) endSplashScreen
750{
751 OOLogSetDisplayMessagesInClass(@"startup.progress", NO);
752
753#if OOLITE_MAC_OS_X
754 // These views will be released when we replace the content view.
755 splashProgressTextField = nil;
756 splashView = nil;
757
758 [gameWindow setAcceptsMouseMovedEvents:YES];
759 [gameWindow setContentView:gameView];
760 [gameWindow makeFirstResponder:gameView];
761#elif OOLITE_SDL
762 [gameView endSplashScreen];
763#endif
764}
765
766
767#if OOLITE_MAC_OS_X
768
769// NIB methods
770- (void)awakeFromNib
771{
772 NSString *path = nil;
773
774 // Set contents of Help window
775 path = [[NSBundle mainBundle] pathForResource:@"OoliteReadMe" ofType:@"pdf"];
776 if (path != nil)
777 {
778 PDFDocument *document = [[PDFDocument alloc] initWithURL:[NSURL fileURLWithPath:path]];
779 [helpView setDocument:document];
780 [document release];
781 }
782 [helpView setBackgroundColor:[NSColor whiteColor]];
783}
784
785
786// delegate methods
787- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
788{
789 if ([[filename pathExtension] isEqual:@"oolite-save"])
790 {
791 [self setPlayerFileToLoad:filename];
792 [self setPlayerFileDirectory:filename];
793 return YES;
794 }
795 if ([[filename pathExtension] isEqualToString:@"oxp"])
796 {
797 BOOL dir_test;
798 [[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&dir_test];
799 if (dir_test)
800 {
801 if (expansionPathsToInclude == nil)
802 {
803 expansionPathsToInclude = [[NSMutableArray alloc] init];
804 }
805 [expansionPathsToInclude addObject:filename];
806 return YES;
807 }
808 }
809 return NO;
810}
811
812
813- (void) exitAppWithContext:(NSString *)context
814{
815 [gameView.window orderOut:nil];
816 [(OoliteApp *)NSApp setExitContext:context];
817 [NSApp terminate:self];
818}
819
820
821- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
822{
825 return NSTerminateNow;
826}
827
828#elif OOLITE_SDL
829
830- (void) exitAppWithContext:(NSString *)context
831{
832 OOLog(@"exit.context", @"Exiting: %@.", context);
833#if (OOLITE_GNUSTEP && !defined(NDEBUG))
834 [[OODebugMonitor sharedDebugMonitor] applicationWillTerminate];
835#endif
836#if OOLITE_WINDOWS
837 // This should not be required normally but we have to ensure that
838 // desktop resolution is restored also on some Intel cards on Win10
839 if (![gameView atDesktopResolution])
840 {
841 OOLog(@"gameController.exitApp", @"%@", @"Restoring desktop resolution.");
842 ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL);
843 }
844#endif
845 [[NSUserDefaults standardUserDefaults] synchronize];
846 OOLog(@"gameController.exitApp", @"%@", @".GNUstepDefaults synchronized.");
848 SDL_Quit();
850 exit(0);
851}
852
853#else
854 #error Unknown environment!
855#endif
856
857
858- (void) exitAppCommandQ
859{
860 [self exitAppWithContext:@"Command-Q"];
861}
862
863
864- (void)windowDidResize:(NSNotification *)aNotification
865{
866 [gameView updateScreen];
867}
868
869
870- (NSString *) playerFileToLoad
871{
872 return playerFileToLoad;
873}
874
875
876- (void) setPlayerFileToLoad:(NSString *)filename
877{
878 if (playerFileToLoad)
879 [playerFileToLoad autorelease];
880 playerFileToLoad = nil;
881 if ([[[filename pathExtension] lowercaseString] isEqual:@"oolite-save"])
882 playerFileToLoad = [filename copy];
883}
884
885
886- (NSString *) playerFileDirectory
887{
888 if (playerFileDirectory == nil)
889 {
890 playerFileDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"save-directory"];
891 if (playerFileDirectory != nil && ![[NSFileManager defaultManager] fileExistsAtPath:playerFileDirectory])
892 {
893 playerFileDirectory = nil;
894 }
895 if (playerFileDirectory == nil) playerFileDirectory = [[NSFileManager defaultManager] defaultCommanderPath];
896
897 [playerFileDirectory retain];
898 }
899
900 return playerFileDirectory;
901}
902
903
904- (void) setPlayerFileDirectory:(NSString *)filename
905{
906 if (playerFileDirectory != nil)
907 {
908 [playerFileDirectory autorelease];
909 playerFileDirectory = nil;
910 }
911
912 if ([[[filename pathExtension] lowercaseString] isEqual:@"oolite-save"])
913 {
914 filename = [filename stringByDeletingLastPathComponent];
915 }
916
917 playerFileDirectory = [filename retain];
918 [[NSUserDefaults standardUserDefaults] setObject:filename forKey:@"save-directory"];
919}
920
921
922- (void)reportUnhandledStartupException:(NSException *)exception
923{
924 OOLog(@"startup.exception", @"***** Unhandled exception during startup: %@ (%@).", [exception name], [exception reason]);
925
926 #if OOLITE_MAC_OS_X
927 // Display an error alert.
928 // TODO: provide better information on reporting bugs in the manual, and refer to it here.
929 NSRunCriticalAlertPanel(@"Oolite failed to start up, because an unhandled exception occurred.", @"An exception of type %@ occurred. If this problem persists, please file a bug report.", @"OK", NULL, NULL, [exception name]);
930 #endif
931}
932
933
934- (void)setUpBasicOpenGLStateWithSize:(NSSize)viewSize
935{
937
938 float ratio = 0.5;
939 float aspect = viewSize.height/viewSize.width;
940
941 OOGL(glClearColor(0.0, 0.0, 0.0, 0.0));
942 OOGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
943
944 OOGL(glClearDepth(1.0));
945 OOGL(glViewport(0, 0, viewSize.width, viewSize.height));
946
947 OOGLResetProjection(); // reset matrix
948 OOGLFrustum(-ratio, ratio, -aspect*ratio, aspect*ratio, 1.0, MAX_CLEAR_DEPTH); // set projection matrix
949
950 OOGL(glDepthFunc(GL_LESS)); // depth buffer
951
952 if (UNIVERSE)
953 {
954 [UNIVERSE setLighting];
955 }
956 else
957 {
958 GLfloat black[4] = {0.0, 0.0, 0.0, 1.0};
959 GLfloat white[] = {1.0, 1.0, 1.0, 1.0};
960 GLfloat stars_ambient[] = {0.25, 0.2, 0.25, 1.0};
961
962 OOGL(glLightfv(GL_LIGHT1, GL_AMBIENT, black));
963 OOGL(glLightfv(GL_LIGHT1, GL_SPECULAR, white));
964 OOGL(glLightfv(GL_LIGHT1, GL_DIFFUSE, white));
965 OOGL(glLightfv(GL_LIGHT1, GL_POSITION, black));
966 OOGL(glLightModelfv(GL_LIGHT_MODEL_AMBIENT, stars_ambient));
967
968 }
969
970 if ([extMgr usePointSmoothing]) OOGL(glEnable(GL_POINT_SMOOTH));
971 if ([extMgr useLineSmoothing]) OOGL(glEnable(GL_LINE_SMOOTH));
972
973 // world's simplest OpenGL optimisations...
974#if GL_APPLE_transform_hint
975 if ([extMgr haveExtension:@"GL_APPLE_transform_hint"])
976 {
977 OOGL(glHint(GL_TRANSFORM_HINT_APPLE, GL_FASTEST));
978 }
979#endif
980
981 OOGL(glDisable(GL_NORMALIZE));
982 OOGL(glDisable(GL_RESCALE_NORMAL));
983
984#if GL_VERSION_1_2
985 // For OpenGL 1.2 or later, we want GL_SEPARATE_SPECULAR_COLOR all the time.
986 if ([extMgr versionIsAtLeastMajor:1 minor:2])
987 {
988 OOGL(glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR));
989 }
990#endif
991}
992
993
994#ifndef NDEBUG
995/* This method exists purely to suppress Clang static analyzer warnings that
996 these ivars are unused (but may be used by categories, which they are).
997*/
998- (BOOL) suppressClangStuff
999{
1000 return pauseSelector &&
1001 pauseTarget;
1002}
1003#endif
1004
1005@end
1006
1007
1008#if OOLITE_MAC_OS_X
1009
1010static void SetUpSparkle(void)
1011{
1012#define FEED_URL_BASE "http://www.oolite.org/updates/"
1013#define TEST_RELEASE_FEED_NAME "oolite-mac-test-release-appcast.xml"
1014#define DEPLOYMENT_FEED_NAME "oolite-mac-appcast.xml"
1015
1016#define TEST_RELEASE_FEED_URL (@ FEED_URL_BASE TEST_RELEASE_FEED_NAME)
1017#define DEPLOYMENT_FEED_URL (@ FEED_URL_BASE DEPLOYMENT_FEED_NAME)
1018
1019// Default to test releases in test release or debug builds, and stable releases for deployment builds.
1020#ifdef NDEBUG
1021#define DEFAULT_TEST_RELEASE 0
1022#else
1023#define DEFAULT_TEST_RELEASE 1
1024#endif
1025
1026 BOOL useTestReleases = [[NSUserDefaults standardUserDefaults] oo_boolForKey:@"use-test-release-updates"
1027 defaultValue:DEFAULT_TEST_RELEASE];
1028
1029 SUUpdater *updater = [SUUpdater sharedUpdater];
1030 [updater setFeedURL:[NSURL URLWithString:useTestReleases ? TEST_RELEASE_FEED_URL : DEPLOYMENT_FEED_URL]];
1031}
1032
1033#endif
#define MAX_CLEAR_DEPTH
#define MINIMUM_GAME_TICK
static GameController * sSharedController
#define kSnapshotsDirNameKey
#define kSnapshotsDirRefKey
static void SetUpSparkle(void)
@ kJAPersistentFileReferenceWithoutUI
@ kJAPersistentFileReferenceWithoutMounting
NSURL * JAURLFromPersistentFileReference(NSDictionary *fileRef, JAPersistentFileReferenceResolveFlags flags, BOOL *isStale)
NSDictionary * JAPersistentFileReferenceFromURL(NSURL *url)
#define EXPECT_NOT(x)
void OOJSFrameCallbacksInvoke(OOTimeDelta delta)
NSString * OOLogHandlerGetLogBasePath(void)
BOOL OOLogWillDisplayMessagesInClass(NSString *inMessageClass)
Definition OOLogging.m:144
#define OOLogOutdentIf(class)
Definition OOLogging.h:102
#define OOLog(class, format,...)
Definition OOLogging.h:88
#define OOLogIndentIf(class)
Definition OOLogging.h:101
void OOLoggingTerminate(void)
Definition OOLogging.m:612
void OOLogSetDisplayMessagesInClass(NSString *inClass, BOOL inFlag)
Definition OOLogging.m:182
OOMouseInteractionMode
NSString * OOStringFromMouseInteractionMode(OOMouseInteractionMode mode)
void OOGLFrustum(double left, double right, double bottom, double top, double near, double far)
void OOGLResetProjection(void)
#define OOGL(statement)
Definition OOOpenGL.h:251
return self
unsigned count
return nil
#define PLAYER
#define UNIVERSE
Definition Universe.h:833
#define DESC(key)
Definition Universe.h:839
static id GetPreference(NSString *key, Class expectedClass)
static void SetPreference(NSString *key, id value)
OOCacheManager * sharedCache()
OODebugMonitor * sharedDebugMonitor()
BOOL setStickHandlerClass:(Class aClass)
OOOXZManager * sharedManager()
OOOpenALController * sharedController()
OOOpenGLExtensionManager * sharedManager()
void update()
Definition OOALSound.m:149
void addExternalPath:(NSString *fileName)
NSString * useAddOns()
NSArray * userRootPaths()
const char * filename
Definition ioapi.h:133
const char int mode
Definition ioapi.h:133
void ranrot_srand(uint32_t seed)