Oolite 1.93.0.7767-260207-f2a8cb5
Loading...
Searching...
No Matches
PlayerEntityLoadSave.m
Go to the documentation of this file.
1/*
2
3 PlayerEntityLoadSave.m
4
5 Oolite
6 Copyright (C) 2004-2013 Giles C Williams and contributors
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 MA 02110-1301, USA.
22
23 */
24
28#import "PlayerEntitySound.h"
29
31#import "GameController.h"
32#import "ResourceManager.h"
33#import "OOStringExpander.h"
35#import "ProxyPlayerEntity.h"
36#import "ShipEntityAI.h"
37#import "OOXMLExtensions.h"
38#import "OOSound.h"
39#import "OOColor.h"
40#import "OOStringParsing.h"
41#import "OOPListParsing.h"
42#import "StationEntity.h"
44#import "OOConstToString.h"
45#import "OOShipRegistry.h"
46#import "OOTexture.h"
50
51
52// Name of modifier key used to issue commands. See also -isCommandModifierKeyDown.
53#if OO_USE_CUSTOM_LOAD_SAVE
54#define COMMAND_MODIFIER_KEY "Ctrl"
55#endif
56
57
58static uint16_t PersonalityForCommanderDict(NSDictionary *dict);
59
60
61#if OO_USE_CUSTOM_LOAD_SAVE
62
63@interface MyOpenGLView (OOLoadSaveExtensions)
64
66
67@end
68
69#endif
70
71
72@interface PlayerEntity (OOLoadSavePrivate)
73
74#if OOLITE_USE_APPKIT_LOAD_SAVE
75
76- (BOOL) loadPlayerWithPanel;
77- (void) savePlayerWithPanel;
78
79#endif
80
81#if OO_USE_CUSTOM_LOAD_SAVE
82
84- (void) setGuiToSaveCommanderScreen: (NSString *)cdrName;
85- (void) setGuiToOverwriteScreen: (NSString *)cdrName;
86- (void) lsCommanders: (GuiDisplayGen *)gui directory: (NSString*)directory pageNumber: (int)page highlightName: (NSString *)highlightName;
87- (void) showCommanderShip: (int)cdrArrayIndex;
88- (int) findIndexOfCommander: (NSString *)cdrName;
89- (void) nativeSavePlayer: (NSString *)cdrName;
90- (BOOL) existingNativeSave: (NSString *)cdrName;
91
92#endif
93
94- (void) writePlayerToPath:(NSString *)path;
95
96@end
97
98
99@implementation PlayerEntity (LoadSave)
100
102{
103 BOOL OK = YES;
104
105#if OO_USE_APPKIT_LOAD_SAVE_ALWAYS
106 OK = [self loadPlayerWithPanel];
107#elif OOLITE_USE_APPKIT_LOAD_SAVE
108 // OS X: use system open/save dialogs in windowed mode, custom interface in full-screen.
109 if ([[UNIVERSE gameController] inFullScreenMode])
110 {
112 }
113 else
114 {
115 OK = [self loadPlayerWithPanel];
116 }
117#else
118 // Other platforms: use custom interface all the time.
120#endif
121 return OK;
122}
123
124
126{
127#if OO_USE_APPKIT_LOAD_SAVE_ALWAYS
128 [self savePlayerWithPanel];
129#elif OOLITE_USE_APPKIT_LOAD_SAVE
130 // OS X: use system open/save dialogs in windowed mode, custom interface in full-screen.
131 if ([[UNIVERSE gameController] inFullScreenMode])
132 {
133 [self setGuiToSaveCommanderScreen:self.lastsaveName];
134 }
135 else
136 {
137 [self savePlayerWithPanel];
138 }
139#else
140 // Other platforms: use custom interface all the time.
142#endif
143}
144
146{
147 NSString *tmp_path = nil;
148 NSString *tmp_name = nil;
149 NSString *dir = [[UNIVERSE gameController] playerFileDirectory];
150
151 tmp_name = [self lastsaveName];
152 tmp_path = save_path;
153
154 ShipScriptEventNoCx(self, "playerWillSaveGame", OOJSSTR("AUTO_SAVE"));
155
156 NSString *saveName = [self lastsaveName];
157 NSString *autosaveSuffix = DESC(@"autosave-commander-suffix");
158
159 if (![saveName hasSuffix:autosaveSuffix])
160 {
161 saveName = [saveName stringByAppendingString:autosaveSuffix];
162 }
163 NSString *savePath = [dir stringByAppendingPathComponent:[saveName stringByAppendingString:@".oolite-save"]];
164
165 [self setLastsaveName:saveName];
166
167 @try
168 {
169 [self writePlayerToPath:savePath];
170 }
171 @catch (id exception)
172 {
173 // Suppress exceptions silently. Warning the user about failed autosaves would be pretty unhelpful.
174 }
175
176 if (tmp_path != nil)
177 {
178 [save_path autorelease];
179 save_path = [tmp_path copy];
180 }
181 [self setLastsaveName:tmp_name];
182}
183
184
186{
187 MyOpenGLView *gameView = [UNIVERSE gameView];
188 NSString *path = nil;
189
190 path = save_path;
191 if (!path) path = [[gameView gameController] playerFileToLoad];
192 if (!path)
193 {
194 OOLog(@"quickSave.failed.noName", @"%@", @"ERROR no file name returned by [[gameView gameController] playerFileToLoad]");
195 [NSException raise:@"OoliteGameNotSavedException"
196 format:@"ERROR no file name returned by [[gameView gameController] playerFileToLoad]"];
197 }
198
199 ShipScriptEventNoCx(self, "playerWillSaveGame", OOJSSTR("QUICK_SAVE"));
200
201 [self writePlayerToPath:path];
202 [[UNIVERSE gameView] suppressKeysUntilKeyUp];
204}
205
206
207- (void) setGuiToScenarioScreen:(int)page
208{
209 NSArray *scenarios = [UNIVERSE scenarios];
210 [UNIVERSE removeDemoShips];
211 // GUI stuff
212 {
213 GuiDisplayGen *gui = [UNIVERSE gui];
215 OOGUIRow row = start_row;
216 BOOL guiChanged = (gui_screen != GUI_SCREEN_NEWGAME);
217
218 [gui clearAndKeepBackground:!guiChanged];
219 [gui setTitle:DESC(@"oolite-newgame-title")];
220
221 OOGUITabSettings tab_stops;
222 tab_stops[0] = 0;
223 tab_stops[1] = -480;
224 [gui setTabStops:tab_stops];
225
226 unsigned n_rows = GUI_MAX_ROWS_SCENARIOS;
227 NSUInteger i, count = [scenarios count];
228
229 NSDictionary *scenario = nil;
230
231 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-scenario-exit"), @" <----- ", nil] forRow:start_row - 2];
232 [gui setColor:[OOColor redColor] forRow:start_row - 2];
233 [gui setKey:@"exit" forRow:start_row - 2];
234
235
236 if (page > 0)
237 {
238 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @" <-- ", nil] forRow:start_row - 1];
239 [gui setColor:[OOColor greenColor] forRow:start_row - 1];
240 [gui setKey:[NSString stringWithFormat:@"__page:%i",page-1] forRow:start_row - 1];
241 }
242
243 [self setShowDemoShips:NO];
244
245 for (i = page*n_rows ; i < count && row < start_row + n_rows ; i++)
246 {
247 scenario = [[UNIVERSE scenarios] objectAtIndex:i];
248 NSString *scenarioName = [NSString stringWithFormat:@" %@ ",[scenario oo_stringForKey:@"name"]];
249 [gui setText:OOExpand(scenarioName) forRow:row];
250 [gui setKey:[NSString stringWithFormat:@"Scenario:%lu", (unsigned long)i] forRow:row];
251 ++row;
252 }
253
254 if ((page+1) * n_rows < count)
255 {
256 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @" --> ", nil] forRow:row];
257 [gui setColor:[OOColor greenColor] forRow:row];
258 [gui setKey:[NSString stringWithFormat:@"__page:%i",page+1] forRow:row];
259 ++row;
260 }
261
262 gui_screen = GUI_SCREEN_NEWGAME;
263
264 [gui setSelectableRange:NSMakeRange(start_row - 2,3 + row - start_row)];
265 [gui setSelectedRow:start_row];
266 [self showScenarioDetails];
267
268 if (guiChanged)
269 {
270 [gui setBackgroundTextureKey:@"newgame"];
271 [gui setForegroundTextureKey:@"newgame_overlay"];
272 }
273 }
274
275 [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
276}
277
278- (void) addScenarioModel:(NSString *)shipKey
279{
280 [self showShipModelWithKey:shipKey shipData:nil personality:0 factorX:1.2 factorY:0.8 factorZ:6.4 inContext:@"scenario"];
281}
282
283
285{
286 GuiDisplayGen* gui = [UNIVERSE gui];
287 NSString* key = [gui selectedRowKey];
288 [UNIVERSE removeDemoShips];
289
290 if ([key hasPrefix:@"Scenario"])
291 {
292 int item = [[key componentsSeparatedByString:@":"] oo_intAtIndex:1];
293 NSDictionary *scenario = [[UNIVERSE scenarios] objectAtIndex:item];
294 [self setShowDemoShips:NO];
295 for (NSUInteger i=GUI_ROW_SCENARIOS_DETAIL;i<=27;i++)
296 {
297 [gui setText:@"" forRow:i];
298 }
299 if (scenario)
300 {
301 [gui addLongText:OOExpand([scenario oo_stringForKey:@"description"]) startingAtRow:GUI_ROW_SCENARIOS_DETAIL align:GUI_ALIGN_LEFT];
302 NSString *shipKey = [scenario oo_stringForKey:@"model"];
303 if (shipKey != nil)
304 {
305 [self addScenarioModel:shipKey];
306 [self setShowDemoShips:YES];
307 }
308 }
309
310 }
311}
312
313
315{
316 GuiDisplayGen* gui = [UNIVERSE gui];
317 NSString* key = [gui selectedRowKey];
318
319 if ([key isEqualToString:@"exit"])
320 {
321 // intended to return to main menu
322 return NO;
323 }
324 if ([key hasPrefix:@"__page"])
325 {
326 int page = [[key componentsSeparatedByString:@":"] oo_intAtIndex:1];
327 [self setGuiToScenarioScreen:page];
328 return YES;
329 }
330 int selection = [[key componentsSeparatedByString:@":"] oo_intAtIndex:1];
331
332 NSDictionary *scenario = [[UNIVERSE scenarios] objectAtIndex:selection];
333 NSString *file = [scenario oo_stringForKey:@"file" defaultValue:nil];
334 if (file == nil)
335 {
336 OOLog(@"scenario.init.error", @"%@", @"No file entry found for scenario");
337 return NO;
338 }
339 NSString *path = [ResourceManager pathForFileNamed:file inFolder:@"Scenarios"];
340 if (path == nil)
341 {
342 OOLog(@"scenario.init.error", @"Game file not found for scenario %@",file);
343 return NO;
344 }
345 BOOL result = [self loadPlayerFromFile:path asNew:YES];
346 if (!result)
347 {
348 return NO;
349 }
350 [scenarioKey release];
351 scenarioKey = [[scenario oo_stringForKey:@"scenario" defaultValue:nil] retain];
352
353 // don't drop the save game directory in
354 return YES;
355}
356
357
358
359
360#if OO_USE_CUSTOM_LOAD_SAVE
361
362- (NSString *)commanderSelector
363{
364 MyOpenGLView *gameView = [UNIVERSE gameView];
365 GuiDisplayGen *gui = [UNIVERSE gui];
366 NSString *dir = [[UNIVERSE gameController] playerFileDirectory];
367
368 int idx;
369 if([self handleGUIUpDownArrowKeys])
370 {
371 int guiSelectedRow=[gui selectedRow];
372 idx=(guiSelectedRow - STARTROW) + (currentPage * NUMROWS);
373 if (guiSelectedRow != MOREROW && guiSelectedRow != BACKROW && guiSelectedRow != EXITROW)
374 {
375 [self showCommanderShip: idx];
376 }
377 else
378 {
379 [UNIVERSE removeDemoShips];
380 [gui setText:@"" forRow:CDRDESCROW align:GUI_ALIGN_LEFT];
381 [gui setText:@"" forRow:CDRDESCROW + 1 align:GUI_ALIGN_LEFT];
382 [gui setText:@"" forRow:CDRDESCROW + 2 align:GUI_ALIGN_LEFT];
383 }
384
385 }
386 else
387 {
388 idx=([gui selectedRow] - STARTROW) + (currentPage * NUMROWS);
389 }
390
391 // handle page <-- and page --> keys
392 if (([self checkKeyPress:n_key_gui_arrow_left] || [self checkKeyPress:n_key_gui_page_up]) && [[gui keyForRow:BACKROW] isEqual: GUI_KEY_OK])
393 {
394 currentPage--;
396 [self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
398 }
399 if (([self checkKeyPress:n_key_gui_arrow_right] || [self checkKeyPress:n_key_gui_page_down]) && [[gui keyForRow:MOREROW] isEqual: GUI_KEY_OK])
400 {
401 currentPage++;
402 [self playMenuPageNext];
403 [self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
405 }
406
407 // Enter pressed - find the commander name underneath.
408 // ignore Ctrl for the moment - we check for it explicitly later
409 if ([self checkKeyPress:n_key_gui_select ignore_ctrl:YES]||[gameView isDown:gvMouseDoubleClick])
410 {
411 NSDictionary *cdr;
412 switch ([gui selectedRow])
413 {
414 case EXITROW:
415 if ([self status] == STATUS_START_GAME)
416 {
417 [self setGuiToIntroFirstGo:YES];
418 return nil;
419 }
420 break;
421 case BACKROW:
422 currentPage--;
423 [self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
425 break;
426 case MOREROW:
427 currentPage++;
428 [self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
430 break;
431 default:
432 cdr=[cdrDetailArray objectAtIndex: idx];
433 if ([cdr oo_boolForKey:@"isSavedGame"])
434 return [cdr oo_stringForKey:@"saved_game_path"];
435 else
436 {
437 if ([gameView isCommandModifierKeyDown]||[gameView isDown:gvMouseDoubleClick])
438 {
439 // change directory to the selected path
440 NSString* newDir = [cdr oo_stringForKey:@"saved_game_path"];
441 [[UNIVERSE gameController] setPlayerFileDirectory: newDir];
442 dir = newDir;
443 currentPage = 0;
444 [self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
446 }
447 }
448 }
449 }
450
451 if([gameView isDown: 27]) // escape key
452 {
454 }
455 return nil;
456}
457
458
460{
461 MyOpenGLView *gameView = [UNIVERSE gameView];
462 GuiDisplayGen *gui = [UNIVERSE gui];
463 NSString *dir = [[UNIVERSE gameController] playerFileDirectory];
464
465 if ([self handleGUIUpDownArrowKeys])
466 {
467 int guiSelectedRow=[gui selectedRow];
468 int idx = (guiSelectedRow - STARTROW) + (currentPage * NUMROWS);
469 if (guiSelectedRow != MOREROW && guiSelectedRow != BACKROW)
470 {
471 [self showCommanderShip: idx];
472 if ([(NSDictionary *)[cdrDetailArray objectAtIndex:idx] oo_boolForKey:@"isSavedGame"]) // don't show things that aren't saved games
473 commanderNameString = [[cdrDetailArray oo_dictionaryAtIndex:idx] oo_stringForKey:@"player_save_name" defaultValue:[[cdrDetailArray oo_dictionaryAtIndex:idx] oo_stringForKey:@"player_name"]];
474 else
475 commanderNameString = [gameView typedString];
476 }
477 else
478 {
479 [UNIVERSE removeDemoShips];
480 [gui setText:@"" forRow:CDRDESCROW align:GUI_ALIGN_LEFT];
481 [gui setText:@"" forRow:CDRDESCROW + 1 align:GUI_ALIGN_LEFT];
482 [gui setText:@"" forRow:CDRDESCROW + 2 align:GUI_ALIGN_LEFT];
483 }
484 }
485 else
486 {
487 commanderNameString = [gameView typedString];
488 }
489
490 [gameView setTypedString: commanderNameString];
491
492 [gui setText:
493 [NSString stringWithFormat:DESC(@"savescreen-commander-name-@"), commanderNameString]
494 forRow: INPUTROW];
495 [gui setColor:[OOColor cyanColor] forRow:INPUTROW];
496
497 // handle page <-- and page --> keys, and on-screen buttons
498 if (((([gameView isDown:gvMouseDoubleClick] || [self checkKeyPress:n_key_gui_select]) && [gui selectedRow] == BACKROW) || ([self checkKeyPress:n_key_gui_arrow_left] || [self checkKeyPress:n_key_gui_page_up]))
499 && [[gui keyForRow:BACKROW] isEqual: GUI_KEY_OK])
500 {
501 currentPage--;
502 [self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
504 }
505 //
506 if (((([gameView isDown:gvMouseDoubleClick] || [self checkKeyPress:n_key_gui_select]) && [gui selectedRow] == MOREROW) || ([self checkKeyPress:n_key_gui_arrow_right] || [self checkKeyPress:n_key_gui_page_down]))
507 && [[gui keyForRow:MOREROW] isEqual: GUI_KEY_OK])
508 {
509 currentPage++;
510 [self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
512 }
513
514 // ignore Ctrl if pressed together with Enter for the moment - we check for it explicitly immediately after
515 if(([self checkKeyPress:n_key_gui_select ignore_ctrl:YES]||[gameView isDown:gvMouseDoubleClick]) && [commanderNameString length])
516 {
517 if ([gameView isCommandModifierKeyDown]||[gameView isDown:gvMouseDoubleClick])
518 {
519 int guiSelectedRow=[gui selectedRow];
520 int idx = (guiSelectedRow - STARTROW) + (currentPage * NUMROWS);
521 NSDictionary* cdr = [cdrDetailArray objectAtIndex:idx];
522
523 if (![cdr oo_boolForKey:@"isSavedGame"]) // don't open saved games
524 {
525 // change directory to the selected path
526 NSString* newDir = [cdr oo_stringForKey:@"saved_game_path"];
527 [[UNIVERSE gameController] setPlayerFileDirectory: newDir];
528 dir = newDir;
529 currentPage = 0;
530 [self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
532 }
533 }
534 else
535 {
536 pollControls = YES;
537 if ([self existingNativeSave: commanderNameString])
538 {
540 [self setGuiToOverwriteScreen: commanderNameString];
541 }
542 else
543 {
544 [self nativeSavePlayer: commanderNameString];
545 [[UNIVERSE gameView] suppressKeysUntilKeyUp];
547 }
548 }
549 }
550
551 if([gameView isDown: 27]) // escape key
552 {
553 // get out of here
554 pollControls = YES;
555 [[UNIVERSE gameView] resetTypedString];
557 }
558}
559
560
562{
563 MyOpenGLView *gameView = [UNIVERSE gameView];
564 GuiDisplayGen *gui = [UNIVERSE gui];
565
567
568 // Translation issue: we can't confidently use raw Y and N ascii as shortcuts. It's better to use the load-previous-commander keys.
569 id valueYes = [[[UNIVERSE descriptions] oo_stringForKey:@"load-previous-commander-yes" defaultValue:@"y"] lowercaseString];
570 id valueNo = [[[UNIVERSE descriptions] oo_stringForKey:@"load-previous-commander-no" defaultValue:@"n"] lowercaseString];
571 unsigned char cYes, cNo;
572
573 cYes = [valueYes characterAtIndex: 0] & 0x00ff; // Use lower byte of unichar.
574 cNo = [valueNo characterAtIndex: 0] & 0x00ff; // Use lower byte of unichar.
575
576 if (([self checkKeyPress:n_key_gui_select] && ([gui selectedRow] == SAVE_OVERWRITE_YES_ROW))||[gameView isDown:cYes]||[gameView isDown:cYes - 32])
577 {
578 pollControls=YES;
579 [self nativeSavePlayer: commanderNameString];
581 [[UNIVERSE gameView] suppressKeysUntilKeyUp];
583 }
584
585 if (([self checkKeyPress:n_key_gui_select] && ([gui selectedRow] == SAVE_OVERWRITE_NO_ROW))||[gameView isDown:27]||[gameView isDown:cNo]||[gameView isDown:cNo - 32])
586 {
587 // esc or NO was pressed - get out of here
588 pollControls=YES;
589 [self playSaveOverwriteNo];
591 }
592}
593
594#endif
595
596
597- (BOOL) loadPlayerFromFile:(NSString *)fileToOpen asNew:(BOOL)asNew
598{
599 /* TODO: it would probably be better to load by creating a new
600 PlayerEntity, verifying that's OK, then replacing the global player.
601
602 Actually, it'd be better to separate PlayerEntity into OOPlayer and
603 OOPlayerShipEntity. And then move most of OOPlayerShipEntity into
604 ShipEntity, and make NPC ships behave more like player ships.
605 -- Ahruman
606 */
607
608 BOOL loadedOK = YES;
609 NSDictionary *fileDic = nil;
610 NSString *fail_reason = nil;
611
612 if (fileToOpen == nil)
613 {
614 fail_reason = DESC(@"loadfailed-no-file-specified");
615 loadedOK = NO;
616 }
617
618 if (loadedOK)
619 {
620 OOLog(@"load.progress", @"%@", @"Reading file");
621 fileDic = OODictionaryFromFile(fileToOpen);
622 if (fileDic == nil)
623 {
624 fail_reason = DESC(@"loadfailed-could-not-load-file");
625 loadedOK = NO;
626 }
627 }
628
629 if (loadedOK)
630 {
631 OOLog(@"load.progress", @"%@", @"Restricting scenario");
632 NSString *scenarioRestrict = [fileDic oo_stringForKey:@"scenario_restriction" defaultValue:nil];
633 if (scenarioRestrict == nil)
634 {
635 // older save game - use the 'strict' key instead
636 BOOL strict = [fileDic oo_boolForKey:@"strict" defaultValue:NO];
637 if (strict)
638 {
639 scenarioRestrict = SCENARIO_OXP_DEFINITION_NONE;
640 }
641 else
642 {
643 scenarioRestrict = SCENARIO_OXP_DEFINITION_ALL;
644 }
645 }
646
647 if (![UNIVERSE setUseAddOns:scenarioRestrict fromSaveGame:YES forceReinit:YES])
648 {
649 fail_reason = DESC(@"loadfailed-saved-game-failed-to-load");
650 loadedOK = NO;
651 }
652 }
653
654
655 if (loadedOK)
656 {
657 OOLog(@"load.progress", @"%@", @"Creating player ship");
658 // Check that player ship exists
659 NSString *shipKey = nil;
660 NSDictionary *shipDict = nil;
661
662 shipKey = [fileDic oo_stringForKey:@"ship_desc"];
663 shipDict = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipKey];
664
665 if (shipDict == nil)
666 {
667 loadedOK = NO;
668 if (shipKey != nil) fail_reason = [NSString stringWithFormat:DESC(@"loadfailed-could-not-find-ship-type-@-please-reinstall-the-appropriate-OXP"), shipKey];
669 else fail_reason = DESC(@"loadfailed-invalid-saved-game-no-ship-specified");
670 }
671 }
672
673 if (loadedOK)
674 {
675 OOLog(@"load.progress", @"%@", @"Initialising player entity");
676 if (![self setUpAndConfirmOK:YES saveGame:YES])
677 {
678 fail_reason = DESC(@"loadfailed-could-not-reset-javascript");
679 loadedOK = NO;
680 }
681 }
682
683 if (loadedOK)
684 {
685 OOLog(@"load.progress", @"%@", @"Loading commander data");
686 if (![self setCommanderDataFromDictionary:fileDic])
687 {
688 // this could still be a reset js issue, if switching from strict / unrestricted
689 // TODO: use "could not reset js message" if that's the case.
690 fail_reason = DESC(@"loadfailed-could-not-set-up-player-ship");
691 loadedOK = NO;
692 }
693 }
694
695 if (loadedOK)
696 {
697 OOLog(@"load.progress", @"%@", @"Recording save path");
698 if (!asNew)
699 {
700 [save_path autorelease];
701 save_path = [fileToOpen retain];
702
703 [[[UNIVERSE gameView] gameController] setPlayerFileToLoad:fileToOpen];
704 [[[UNIVERSE gameView] gameController] setPlayerFileDirectory:fileToOpen];
705 }
706 }
707 else
708 {
709 OOLog(@"load.failed", @"***** Failed to load saved game \"%@\": %@", [fileToOpen lastPathComponent], fail_reason ? fail_reason : (NSString *)@"unknown error");
710 [[UNIVERSE gameController] setPlayerFileToLoad:nil];
711 [UNIVERSE handleGameOver];
712 [UNIVERSE clearPreviousMessage];
713 [UNIVERSE addMessage:DESC(@"loadfailed-saved-game-failed-to-load") forCount: 9.0];
714 if (fail_reason != nil) [UNIVERSE addMessage: fail_reason forCount: 9.0];
715 return NO;
716 }
717
718 OOLog(@"load.progress", @"%@", @"Creating system");
719 [UNIVERSE setTimeAccelerationFactor:TIME_ACCELERATION_FACTOR_DEFAULT];
720 [UNIVERSE setSystemTo:system_id];
721 [UNIVERSE removeAllEntitiesExceptPlayer];
722 [UNIVERSE setGalaxyTo: galaxy_number andReinit:YES]; // set overridden planet names on long range map
723 [UNIVERSE setUpSpace];
724 [UNIVERSE setAutoSaveNow:NO];
725
726 OOLog(@"load.progress", @"%@", @"Resetting player flight variables");
728 StationEntity *dockedStation = [self dockedStation];
729
730 [UNIVERSE enterGUIViewModeWithMouseInteraction:NO];
731
732 if (dockedStation)
733 {
734 position = [dockedStation position];
735 [self setOrientation: kIdentityQuaternion];
736 v_forward = vector_forward_from_quaternion(orientation);
737 v_right = vector_right_from_quaternion(orientation);
738 v_up = vector_up_from_quaternion(orientation);
739 }
740
741 flightRoll = 0.0;
742 flightPitch = 0.0;
743 flightYaw = 0.0;
744 flightSpeed = 0.0;
745
746 [self setEntityPersonalityInt:PersonalityForCommanderDict(fileDic)];
747
748 OOLog(@"load.progress", @"%@", @"Loading system market");
749 // dockedStation is always the main station at this point;
750 // "localMarket" save key always refers to the main station (system) market
751 NSArray *market = [fileDic oo_arrayForKey:@"localMarket"];
752 if (market != nil)
753 {
755 }
756 else
757 {
759 }
760
762
763 OOLog(@"load.progress", @"%@", @"Setting scenario key");
764 // set scenario key if the scenario allows saving and has one
765 NSString *scenario = [fileDic oo_stringForKey:@"scenario_key" defaultValue:nil];
766 DESTROY(scenarioKey);
767 if (scenario != nil)
768 {
769 scenarioKey = [scenario retain];
770 }
771
772 OOLog(@"load.progress", @"%@", @"Starting JS engine");
773 // Remember the savegame target, run js startUp.
775 // run initial system population
776 OOLog(@"load.progress", @"%@", @"Populating initial system");
777 [UNIVERSE populateNormalSpace];
778
779 // might as well start off with a collected JS environment
781
782 // read saved position vector and primary role, check for an
783 // appropriate station at those coordinates, if found, switch
784 // docked station to that one.
785 HPVector dockedPos = [fileDic oo_hpvectorForKey:@"docked_station_position"];
786 NSString *dockedRole = [fileDic oo_stringForKey:@"docked_station_role" defaultValue:@""];
787 StationEntity *saveStation = [UNIVERSE stationWithRole:dockedRole andPosition:dockedPos];
788 if (saveStation != nil && [saveStation allowsSaving])
789 {
790 [self setDockedStation:saveStation];
791 position = [saveStation position];
792 }
793 // and initialise markets for the secondary stations
794 [UNIVERSE loadStationMarkets:[fileDic oo_arrayForKey:@"station_markets"]];
795
796 OOLog(@"load.progress", @"%@", @"Completing JS startup");
797 [self startUpComplete];
798
799 // if the file was specified in the command line at startup, DO NOT suppress the keys!
800 if ([[UNIVERSE gameController] finishedLaunching]) [[UNIVERSE gameView] suppressKeysUntilKeyUp];
801 gui_screen = GUI_SCREEN_LOAD; // force evaluation of new gui screen on startup
803 if (loadedOK) [self doWorldEventUntilMissionScreen:OOJSID("missionScreenOpportunity")]; // trigger missionScreenOpportunity immediately after loading
804 OOLog(@"load.progress", @"%@", @"Loading complete");
805 return loadedOK;
806}
807
808@end
809
810
811@implementation PlayerEntity (OOLoadSavePrivate)
812
813#if OOLITE_USE_APPKIT_LOAD_SAVE
814
816{
817 NSOpenPanel *oPanel = [NSOpenPanel openPanel];
818
819 oPanel.allowsMultipleSelection = NO;
820 oPanel.allowedFileTypes = [NSArray arrayWithObject:@"oolite-save"];
821
822 if ([oPanel runModal] == NSOKButton)
823 {
824 NSURL *url = oPanel.URL;
825 if (url.isFileURL)
826 {
827 return [self loadPlayerFromFile:url.path asNew:NO];
828 }
829 }
830
831 return NO;
832}
833
834
836{
837 NSSavePanel *sPanel = [NSSavePanel savePanel];
838
839 sPanel.allowedFileTypes = [NSArray arrayWithObject:@"oolite-save"];
840 sPanel.canSelectHiddenExtension = YES;
841 sPanel.nameFieldStringValue = self.lastsaveName;
842
843 if ([sPanel runModal] == NSOKButton)
844 {
845 NSURL *url = sPanel.URL;
846 NSAssert(url.isFileURL, @"Save panel with default configuration should not provide non-file URLs.");
847
848 NSString *path = url.path;
849 NSString *newName = [path.lastPathComponent stringByDeletingPathExtension];
850
851 ShipScriptEventNoCx(self, "playerWillSaveGame", OOJSSTR("STANDARD_SAVE"));
852
853 self.lastsaveName = newName;
854 [self writePlayerToPath:path];
855 }
857}
858
859#endif
860
861
862- (void) writePlayerToPath:(NSString *)path
863{
864 NSString *errDesc = nil;
865 NSDictionary *dict = nil;
866 BOOL didSave = NO;
867 [[UNIVERSE gameView] resetTypedString];
868
869 if (!path)
870 {
871 OOLog(@"save.failed", @"***** SAVE ERROR: %s called with nil path.", __PRETTY_FUNCTION__);
872 return;
873 }
874
875 dict = [self commanderDataDictionary];
876 if (dict == nil) errDesc = @"could not construct commander data dictionary.";
877 else didSave = [dict writeOOXMLToFile:path atomically:YES errorDescription:&errDesc];
878 if (didSave)
879 {
880 [UNIVERSE clearPreviousMessage]; // allow this to be given time and again
881 [UNIVERSE addMessage:DESC(@"game-saved") forCount:2];
882 [save_path autorelease];
883 save_path = [path copy];
884 [[UNIVERSE gameController] setPlayerFileToLoad:save_path];
885 [[UNIVERSE gameController] setPlayerFileDirectory:save_path];
886 // no duplicated autosave immediately after a save.
887 [UNIVERSE setAutoSaveNow:NO];
888 }
889 else
890 {
891 OOLog(@"save.failed", @"***** SAVE ERROR: %@", errDesc);
892 [NSException raise:@"OoliteException"
893 format:@"Attempt to save game to file '%@' failed: %@", path, errDesc];
894 }
895 [[UNIVERSE gameView] suppressKeysUntilKeyUp];
897}
898
899
900- (void)nativeSavePlayer:(NSString *)cdrName
901{
902 NSString* dir = [[UNIVERSE gameController] playerFileDirectory];
903 NSString *savePath = [dir stringByAppendingPathComponent:[cdrName stringByAppendingPathExtension:@"oolite-save"]];
904
905 ShipScriptEventNoCx(self, "playerWillSaveGame", OOJSSTR("STANDARD_SAVE"));
906
907 [self setLastsaveName:cdrName];
908
909 [self writePlayerToPath:savePath];
910}
911
912
913#if OO_USE_CUSTOM_LOAD_SAVE
914
916{
917 GuiDisplayGen *gui=[UNIVERSE gui];
918 NSString* dir = [[UNIVERSE gameController] playerFileDirectory];
919
920 gui_screen = GUI_SCREEN_LOAD;
921
922 [gui clear];
923 [gui setTitle:DESC(@"loadscreen-title")];
924
925 currentPage = 0;
926 [self lsCommanders:gui directory:dir pageNumber: currentPage highlightName:nil];
927
928 [gui setForegroundTextureKey:@"docked_overlay"];
929 [gui setBackgroundTextureKey:@"load_save"];
930
931 [[UNIVERSE gameView] suppressKeysUntilKeyUp];
932
933 [self setShowDemoShips:YES];
934 [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
935}
936
937
938- (void) setGuiToSaveCommanderScreen:(NSString *)cdrName
939{
940 GuiDisplayGen *gui=[UNIVERSE gui];
941 MyOpenGLView *gameView = [UNIVERSE gameView];
942 NSString *dir = [[UNIVERSE gameController] playerFileDirectory];
943
944 pollControls = NO;
945 gui_screen = GUI_SCREEN_SAVE;
946
947 [gui clear];
948 [gui setTitle:DESC(@"savescreen-title")];
949
950 currentPage = 0;
951 [self lsCommanders:gui directory:dir pageNumber: currentPage highlightName:nil];
952
953 [gui setText:DESC(@"savescreen-commander-name") forRow: INPUTROW];
954 [gui setColor:[OOColor cyanColor] forRow:INPUTROW];
955 [gui setShowTextCursor: YES];
956 [gui setCurrentRow: INPUTROW];
957
958 [gui setForegroundTextureKey:@"docked_overlay"];
959 [gui setBackgroundTextureKey:@"load_save"];
960
961 [gameView setTypedString:cdrName];
963
964 [self setShowDemoShips:YES];
965 [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
966}
967
968
969- (void) setGuiToOverwriteScreen:(NSString *)cdrName
970{
971 GuiDisplayGen *gui=[UNIVERSE gui];
972 MyOpenGLView* gameView = [UNIVERSE gameView];
973
974 // Don't poll controls
975 pollControls=NO;
976
977 gui_screen = GUI_SCREEN_SAVE_OVERWRITE;
978
979 [gui clear];
980 [gui setTitle:[NSString stringWithFormat:DESC(@"overwrite-save-commander-@"), cdrName]];
981
982 [gui setText:[NSString stringWithFormat:DESC(@"overwritescreen-commander-@-already-exists-overwrite-query"), cdrName]
983 forRow:SAVE_OVERWRITE_WARN_ROW align: GUI_ALIGN_CENTER];
984
985 [gui setText:DESC(@"overwritescreen-yes") forRow: SAVE_OVERWRITE_YES_ROW align: GUI_ALIGN_CENTER];
986 [gui setKey:GUI_KEY_OK forRow: SAVE_OVERWRITE_YES_ROW];
987
988 [gui setText:DESC(@"overwritescreen-no") forRow: SAVE_OVERWRITE_NO_ROW align: GUI_ALIGN_CENTER];
989 [gui setKey:GUI_KEY_OK forRow: SAVE_OVERWRITE_NO_ROW];
990
991 [gui setSelectableRange: NSMakeRange(SAVE_OVERWRITE_YES_ROW, 2)];
992 [gui setSelectedRow: SAVE_OVERWRITE_NO_ROW];
993
994 // We can only leave this screen by answering yes or no, or esc. Therefore
995 // use a specific overlay, to allow visual reminders of the available options.
996 [gui setForegroundTextureKey:@"overwrite_overlay"];
997 [gui setBackgroundTextureKey:@"load_save"];
998
999 [self setShowDemoShips:NO];
1000 [gameView setStringInput:gvStringInputNo];
1001 [UNIVERSE enterGUIViewModeWithMouseInteraction:NO]; // FIXME: should be YES, but was NO before introducing new mouse mode stuff. If set to YES, choices can be selected but not activated.
1002}
1003
1004NSComparisonResult sortCommanders(id cdr1, id cdr2, void *context)
1005{
1006 return [[cdr1 objectForKey:@"saved_game_path"] localizedCompare:[cdr2 objectForKey:@"saved_game_path"]];
1007}
1008
1009- (void) lsCommanders: (GuiDisplayGen *)gui
1010 directory: (NSString*) directory
1011 pageNumber: (int)page
1012 highlightName: (NSString *)highlightName
1013{
1014 NSFileManager *cdrFileManager=[NSFileManager defaultManager];
1015 int rangeStart=STARTROW;
1016 unsigned lastIndex;
1017 unsigned i;
1018 int row=STARTROW;
1019
1020 // cdrArray defined in PlayerEntity.h
1021 NSArray *cdrArray=[cdrFileManager commanderContentsOfPath: directory];
1022
1023 // get commander details so a brief rundown of the commander's details may
1024 // be displayed.
1025 if (!cdrDetailArray)
1026 cdrDetailArray=[[NSMutableArray alloc] init]; // alloc retains this so the retain further on in the code was unnecessary
1027 else
1028 [cdrDetailArray removeAllObjects];
1029
1030 [cdrDetailArray addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:
1031 @"YES", @"isParentFolder",
1032 [directory stringByDeletingLastPathComponent], @"saved_game_path", nil]];
1033
1034 for(i = 0; i < [cdrArray count]; i++)
1035 {
1036 NSString* path = [cdrArray objectAtIndex:i];
1037 BOOL exists, isDirectory = NO;
1038
1039 exists = [cdrFileManager fileExistsAtPath:path isDirectory:&isDirectory];
1040
1041 if (exists)
1042 {
1043 if (!isDirectory && [[[path pathExtension] lowercaseString] isEqualToString:@"oolite-save"])
1044 {
1045 NSDictionary *cdr = OODictionaryFromFile(path);
1046 if(cdr)
1047 {
1048 // okay use the same dictionary but add a 'saved_game_path' attribute
1049 NSMutableDictionary* cdr1 = [NSMutableDictionary dictionaryWithDictionary:cdr];
1050 [cdr1 setObject: @"YES" forKey:@"isSavedGame"];
1051 [cdr1 setObject: path forKey:@"saved_game_path"];
1052 [cdrDetailArray addObject: cdr1];
1053 }
1054 }
1055 if (isDirectory && ![[path lastPathComponent] hasPrefix:@"."])
1056 {
1057 [cdrDetailArray addObject: [NSDictionary dictionaryWithObjectsAndKeys: @"YES", @"isFolder", path, @"saved_game_path", nil]];
1058 }
1059 }
1060 }
1061
1062 if(![cdrDetailArray count])
1063 {
1064 // Empty directory; tell the user and exit immediately.
1065 [gui setText:DESC(@"loadsavescreen-no-commanders-found") forRow:STARTROW align:GUI_ALIGN_CENTER];
1066 return;
1067 }
1068
1069 [cdrDetailArray sortUsingFunction:sortCommanders context:NULL];
1070
1071 // Do we need to highlight a name?
1072 int highlightRowOnPage=STARTROW;
1073 int highlightIdx=0;
1074 if(highlightName)
1075 {
1076 highlightIdx=[self findIndexOfCommander: highlightName];
1077 if(highlightIdx < 0)
1078 {
1079 OOLog(@"save.list.commanders.commanderNotFound", @"Commander %@ doesn't exist, very bad", highlightName);
1080 highlightIdx=0;
1081 }
1082
1083 // figure out what page we need to be on
1084 page=highlightIdx/NUMROWS;
1085 highlightRowOnPage=highlightIdx % NUMROWS + STARTROW;
1086 }
1087
1088 // We now know for certain what page we're on -
1089 // set the first index of the first commander on this page.
1090 unsigned firstIndex=page * NUMROWS;
1091
1092 // Set up the GUI.
1093 OOGUITabSettings tabStop;
1094 tabStop[0]=0;
1095 tabStop[1]=160;
1096 tabStop[2]=270;
1097 [gui setTabStops: tabStop];
1098
1099 // clear text lines here
1100 for (i = EXITROW ; i < ENDROW + 1; i++)
1101 {
1102 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
1103 [gui setColor: [OOColor yellowColor] forRow: i];
1104 [gui setKey:GUI_KEY_SKIP forRow:i];
1105 }
1106
1107 [gui setColor: [OOColor greenColor] forRow: LABELROW];
1108 [gui setArray: [NSArray arrayWithObjects: DESC(@"loadsavescreen-commander-name"), DESC(@"loadsavescreen-rating"), nil]
1109 forRow:LABELROW];
1110
1111 if (page)
1112 {
1113 [gui setColor:[OOColor greenColor] forRow:STARTROW-1];
1114 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @" <-- ", nil]
1115 forRow:STARTROW-1];
1116 [gui setKey:GUI_KEY_OK forRow:STARTROW-1];
1117 rangeStart=STARTROW-1;
1118 }
1119
1120 if ([self status] == STATUS_START_GAME)
1121 {
1122 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-loadsave-exit"), @" <----- ", nil] forRow:EXITROW];
1123 [gui setColor:[OOColor redColor] forRow:EXITROW];
1124 [gui setKey:GUI_KEY_OK forRow:EXITROW];
1125 rangeStart = EXITROW;
1126 }
1127
1128
1129 if (firstIndex + NUMROWS >= [cdrDetailArray count])
1130 {
1131 lastIndex=[cdrDetailArray count];
1132 [gui setSelectableRange: NSMakeRange(rangeStart, rangeStart + NUMROWS + 2)];
1133 }
1134 else
1135 {
1136 lastIndex=(page * NUMROWS) + NUMROWS;
1137 [gui setColor:[OOColor greenColor] forRow:ENDROW];
1138 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @" --> ", nil]
1139 forRow:ENDROW];
1140 [gui setKey:GUI_KEY_OK forRow:ENDROW];
1141 [gui setSelectableRange: NSMakeRange(rangeStart, MOREROW)];
1142 }
1143
1144 for (i=firstIndex; i < lastIndex; i++)
1145 {
1146 NSDictionary *cdr=[cdrDetailArray objectAtIndex: i];
1147 if ([cdr oo_boolForKey:@"isSavedGame"])
1148 {
1149 NSString *ratingDesc = OODisplayRatingStringFromKillCount([cdr oo_unsignedIntForKey:@"ship_kills"]);
1150 [gui setArray:[NSArray arrayWithObjects:
1151 [NSString stringWithFormat:@" %@ ",[cdr oo_stringForKey:@"player_save_name" defaultValue:[cdr oo_stringForKey:@"player_name"]]],
1152 [NSString stringWithFormat:@" %@ ",ratingDesc],
1153 nil]
1154 forRow:row];
1155 if ([[self lastsaveName] isEqualToString:[cdr oo_stringForKey:@"player_save_name" defaultValue:[cdr oo_stringForKey:@"player_name"]]])
1156 {
1157 highlightRowOnPage = row;
1158 }
1159
1160 [gui setKey:GUI_KEY_OK forRow:row];
1161 row++;
1162 }
1163 if ([cdr oo_boolForKey:@"isParentFolder"])
1164 {
1165 [gui setArray:[NSArray arrayWithObjects:
1166 [NSString stringWithFormat:@" (..) %@ ", [[cdr oo_stringForKey:@"saved_game_path"] lastPathComponent]],
1167 @"",
1168 nil]
1169 forRow:row];
1170 [gui setColor: [OOColor orangeColor] forRow: row];
1171 [gui setKey:GUI_KEY_OK forRow:row];
1172 row++;
1173 }
1174 if ([cdr oo_boolForKey:@"isFolder"])
1175 {
1176 [gui setArray:[NSArray arrayWithObjects:
1177 [NSString stringWithFormat:@" >> %@ ", [[cdr oo_stringForKey:@"saved_game_path"] lastPathComponent]],
1178 @"",
1179 nil]
1180 forRow:row];
1181 [gui setColor: [OOColor orangeColor] forRow: row];
1182 [gui setKey:GUI_KEY_OK forRow:row];
1183 row++;
1184 }
1185 }
1186 [gui setSelectedRow: highlightRowOnPage];
1187 highlightIdx = (highlightRowOnPage - STARTROW) + (currentPage * NUMROWS);
1188 // show the first ship, this will be the selected row
1189 [self showCommanderShip: highlightIdx];
1190}
1191
1192
1193// check for an existing saved game...
1194- (BOOL) existingNativeSave: (NSString *)cdrName
1195{
1196 NSString* dir = [[UNIVERSE gameController] playerFileDirectory];
1197
1198 NSString *savePath=[dir stringByAppendingPathComponent:[cdrName stringByAppendingPathExtension:@"oolite-save"]];
1199 return [[NSFileManager defaultManager] fileExistsAtPath:savePath];
1200}
1201
1202
1203// Get some brief details about the commander file.
1204- (void) showCommanderShip:(int)cdrArrayIndex
1205{
1206 GuiDisplayGen *gui=[UNIVERSE gui];
1207 [UNIVERSE removeDemoShips];
1208 NSDictionary *cdr=[cdrDetailArray objectAtIndex: cdrArrayIndex];
1209
1210 [gui setText:@"" forRow:CDRDESCROW align:GUI_ALIGN_LEFT];
1211 [gui setText:@"" forRow:CDRDESCROW + 1 align:GUI_ALIGN_LEFT];
1212 [gui setText:@"" forRow:CDRDESCROW + 2 align:GUI_ALIGN_LEFT];
1213
1214 if ([cdr oo_boolForKey:@"isFolder"])
1215 {
1216 NSString *folderDesc=[NSString stringWithFormat: DESC(@"loadsavescreen-hold-@-and-press-return-to-open-folder-@"), @COMMAND_MODIFIER_KEY, [[cdr oo_stringForKey:@"saved_game_path"] lastPathComponent]];
1217 [gui setColor: [OOColor orangeColor] forRow: CDRDESCROW];
1218 [gui addLongText: folderDesc startingAtRow: CDRDESCROW align: GUI_ALIGN_LEFT];
1219 return;
1220 }
1221
1222 if ([cdr oo_boolForKey:@"isParentFolder"])
1223 {
1224 NSString *folderDesc=[NSString stringWithFormat: DESC(@"loadsavescreen-hold-@-and-press-return-to-open-parent-folder-@"), @COMMAND_MODIFIER_KEY, [[cdr oo_stringForKey:@"saved_game_path"] lastPathComponent]];
1225 [gui setColor: [OOColor orangeColor] forRow: CDRDESCROW];
1226 [gui addLongText: folderDesc startingAtRow: CDRDESCROW align: GUI_ALIGN_LEFT];
1227 return;
1228 }
1229 [gui setColor:[gui colorFromSetting:nil defaultValue:nil] forRow: CDRDESCROW];
1230
1231 if (![cdr oo_boolForKey:@"isSavedGame"]) return; // don't show things that aren't saved games
1232
1233 if ([self dockedStation] == nil) [self setDockedAtMainStation];
1234
1235 // Display the commander's ship.
1236 NSString *shipDesc = [cdr oo_stringForKey:@"ship_desc"];
1237 NSString *shipName = nil;
1238 NSDictionary *shipDict = nil;
1239 NSString *rating = nil;
1240 uint16_t personality = PersonalityForCommanderDict(cdr);
1241
1242 shipDict = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipDesc];
1243 if(shipDict != nil)
1244 {
1245 NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:[shipDict count] + 1];
1246 [dict setDictionary:shipDict];
1247 id subEntStatus = [cdr objectForKey:@"subentities_status"];
1248 // don't add it to the dictionary if there's no subentities_status key
1249 if (subEntStatus != nil) [dict setObject:subEntStatus forKey:@"subentities_status"];
1250 [self showShipyardModel:shipDesc shipData:dict personality:personality];
1251 [dict release];
1252 shipName = [shipDict oo_stringForKey:@"display_name"];
1253 if (shipName == nil) shipName = [shipDict oo_stringForKey:KEY_NAME];
1254 }
1255 else
1256 {
1257 [self showShipyardModel:@"oolite-unknown-ship" shipData:nil personality:personality];
1258 shipName = [cdr oo_stringForKey:@"ship_name" defaultValue:@"unknown"];
1259 if (![[UNIVERSE useAddOns] isEqualToString:SCENARIO_OXP_DEFINITION_ALL])
1260 {
1261 shipName = [shipName stringByAppendingString:@" - OXPs disabled or not installed"];
1262 }
1263 else
1264 {
1265 shipName = [shipName stringByAppendingString:@" - OXP not installed"];
1266 }
1267 }
1268
1269 // Make a short description of the commander
1270 NSString *legalDesc = OODisplayStringFromLegalStatus([cdr oo_intForKey:@"legal_status"]);
1271
1272 rating = KillCountToRatingAndKillString([cdr oo_unsignedIntForKey:@"ship_kills"]);
1273 OOCreditsQuantity money = OODeciCreditsFromObject([cdr objectForKey:@"credits"]);
1274
1275 // Nikos - Add some more information in the load game screen (current location, galaxy number and timestamp).
1276 //-------------------------------------------------------------------------------------------------------------------------
1277
1278 int galNumber;
1279 NSString *timeStamp = nil;
1280 NSString *locationName = [cdr oo_stringForKey:@"current_system_name"];
1281
1282 // If there is no key containing the name of the current system in
1283 // the savefile, calculating what it should have been is going to
1284 // be tricky now that system generation isn't seed based - but
1285 // this implies a save game well over 5 years old.
1286 if (locationName == nil)
1287 {
1288 // Leaving the location blank in this case is probably okay
1289 locationName = @"";
1290 }
1291
1292 galNumber = [cdr oo_intForKey:@"galaxy_number"] + 1; // Galaxy numbering starts at 0.
1293
1294 NSString *locationGov = @"";
1295 NSString *locationEco = @"";
1296 NSString *locationTL = [cdr objectForKey:@"current_system_techlevel"] ? [NSString stringWithFormat:@"%u", [cdr oo_unsignedIntForKey:@"current_system_techlevel"] + 1] : nil;
1297 if (locationTL)
1298 {
1299 locationGov = [NSString stringWithFormat:@"%c", [cdr oo_unsignedCharForKey:@"current_system_government"]];
1300 locationEco = [NSString stringWithFormat:@" %c", (7 - [cdr oo_unsignedCharForKey:@"current_system_economy"]) + 16];
1301 }
1302 else locationTL = @"";
1303
1304 timeStamp = ClockToString([cdr oo_doubleForKey:@"ship_clock" defaultValue:PLAYER_SHIP_CLOCK_START], NO);
1305
1306 //-------------------------------------------------------------------------------------------------------------------------
1307
1308 NSString *cdrDesc = nil;
1309
1310 cdrDesc = [NSString stringWithFormat:DESC(@"loadsavescreen-commander-@-rated-@-has-@-legal-status-@-ship-@-location-@-g-@-eco-@-gov-@-tl-@-timestamp-@"),
1311 [cdr oo_stringForKey:@"player_name"],
1312 rating,
1313 OOCredits(money),
1314 legalDesc,
1315 shipName,
1316 locationName,
1317 galNumber,
1318 locationEco,
1319 locationGov,
1320 locationTL,
1321 timeStamp];
1322
1323 //-------------------------------------------------------------------------------------------------------------------------
1324
1325 [gui addLongText:cdrDesc startingAtRow:CDRDESCROW align:GUI_ALIGN_LEFT];
1326
1327}
1328
1329
1330- (int) findIndexOfCommander: (NSString *)cdrName
1331{
1332 unsigned i;
1333 for (i=0; i < [cdrDetailArray count]; i++)
1334 {
1335 NSString *currentName = [[cdrDetailArray oo_dictionaryAtIndex: i] oo_stringForKey:@"player_save_name" defaultValue:[[cdrDetailArray oo_dictionaryAtIndex: i] oo_stringForKey:@"player_name"]];
1336 if([cdrName compare: currentName] == NSOrderedSame)
1337 {
1338 return i;
1339 }
1340 }
1341
1342 // not found!
1343 return -1;
1344}
1345
1346#endif
1347
1348@end
1349
1350
1351#if OO_USE_CUSTOM_LOAD_SAVE
1352
1353@implementation MyOpenGLView (OOLoadSaveExtensions)
1354
1356{
1357 return [self isCtrlDown];
1358}
1359
1360@end
1361
1362#endif
1363
1364
1365static uint16_t PersonalityForCommanderDict(NSDictionary *dict)
1366{
1367 uint16_t personality = [dict oo_unsignedShortForKey:@"entity_personality" defaultValue:ENTITY_PERSONALITY_INVALID];
1368
1369 if (personality == ENTITY_PERSONALITY_INVALID)
1370 {
1371 // For pre-1.74 saved games, generate a default personality based on some hashes.
1372 personality = [[dict oo_stringForKey:@"ship_desc"] oo_hash] * [[dict oo_stringForKey:@"player_name"] oo_hash];
1373 }
1374
1375 return personality & ENTITY_PERSONALITY_MAX;
1376}
1377
1378
1380{
1381 /* Clamp value to 0..kOOMaxCredits.
1382 The important bit here is that kOOMaxCredits can't be represented
1383 exactly as a double, and casting it rounds it up; casting this value
1384 back to an OOCreditsQuantity truncates it. Comparing value directly to
1385 kOOMaxCredits promotes kOOMaxCredits to a double, giving us this
1386 problem.
1387 nextafter(kOOMaxCredits, -1) gives us the highest non-truncated
1388 credits value that's representable as a double (namely,
1389 18 446 744 073 709 549 568 decicredits, or 2047 less than kOOMaxCredits).
1390 -- Ahruman 2011-02-27
1391 */
1392 if (doubleDeciCredits > 0)
1393 {
1394 doubleDeciCredits = round(doubleDeciCredits);
1395 double threshold = nextafter(kOOMaxCredits, -1);
1396
1397 if (doubleDeciCredits <= threshold)
1398 {
1399 return doubleDeciCredits;
1400 }
1401 else
1402 {
1403 return kOOMaxCredits;
1404 }
1405 }
1406 else
1407 {
1408 return 0;
1409 }
1410}
1411
1412
1414{
1415 if ([object isKindOfClass:[NSNumber class]] && [object oo_isFloatingPointNumber])
1416 {
1417 return OODeciCreditsFromDouble([object doubleValue]);
1418 }
1419 else
1420 {
1421 return OOUnsignedLongLongFromObject(object, 0);
1422 }
1423}
@ gvMouseDoubleClick
#define GUI_KEY_OK
OOGUITabStop OOGUITabSettings[GUI_MAX_COLUMNS]
NSInteger OOGUIRow
#define DESTROY(x)
Definition OOCocoa.h:85
unsigned long long OOUnsignedLongLongFromObject(id object, unsigned long long defaultValue)
#define OOJSSTR(str)
#define OOLog(class, format,...)
Definition OOLogging.h:88
NSDictionary * OODictionaryFromFile(NSString *path)
unsigned count
return nil
Vector vector_up_from_quaternion(Quaternion quat)
Vector vector_right_from_quaternion(Quaternion quat)
Vector vector_forward_from_quaternion(Quaternion quat)
NSString * ClockToString(double clock, BOOL adjusting)
uint64_t OOCreditsQuantity
Definition OOTypes.h:182
#define kOOMaxCredits
Definition OOTypes.h:183
#define ENDROW
#define SAVE_OVERWRITE_NO_ROW
#define EXITROW
#define MOREROW
#define NUMROWS
#define STARTROW
#define SAVE_OVERWRITE_YES_ROW
#define BACKROW
OOCreditsQuantity OODeciCreditsFromObject(id object)
OOCreditsQuantity OODeciCreditsFromObject(id object)
OOCreditsQuantity OODeciCreditsFromDouble(double doubleDeciCredits)
static uint16_t PersonalityForCommanderDict(NSDictionary *dict)
@ GUI_ROW_SCENARIOS_START
@ GUI_ROW_SCENARIOS_DETAIL
@ GUI_MAX_ROWS_SCENARIOS
NSString * OODisplayRatingStringFromKillCount(unsigned kills)
NSString * OODisplayStringFromLegalStatus(int legalStatus)
NSString * KillCountToRatingAndKillString(unsigned kills)
#define PLAYER_SHIP_CLOCK_START
#define SCENARIO_OXP_DEFINITION_NONE
#define SCENARIO_OXP_DEFINITION_ALL
#define ShipScriptEventNoCx(ship, event,...)
#define ENTITY_PERSONALITY_INVALID
Definition ShipEntity.h:111
#define ENTITY_PERSONALITY_MAX
Definition ShipEntity.h:110
#define UNIVERSE
Definition Universe.h:844
#define DESC(key)
Definition Universe.h:850
NSComparisonResult sortCommanders(id cdr1, id cdr2, void *context)
void setGuiToIntroFirstGo:(BOOL justCobra)
void setOrientation:(Quaternion quat)
Definition Entity.m:725
HPVector position
Definition Entity.m:612
NSString * playerFileToLoad
BOOL setBackgroundTextureKey:(NSString *key)
OOColor * colorFromSetting:defaultValue:(NSString *setting,[defaultValue] OOColor *def)
BOOL setSelectedRow:(OOGUIRow row)
OOGUIRow addLongText:startingAtRow:align:(NSString *str,[startingAtRow] OOGUIRow row,[align] OOGUIAlignment alignment)
BOOL setForegroundTextureKey:(NSString *key)
void setText:forRow:(NSString *str,[forRow] OOGUIRow row)
void setText:forRow:align:(NSString *str,[forRow] OOGUIRow row,[align] OOGUIAlignment alignment)
void clearAndKeepBackground:(BOOL keepBackground)
OOGUIRow selectedRow
void setSelectableRange:(NSRange range)
void setColor:forRow:(OOColor *color,[forRow] OOGUIRow row)
NSString * selectedRowKey()
void setTitle:(NSString *str)
void setTabStops:(OOGUITabSettings stops)
void setShowTextCursor:(BOOL yesno)
void setCurrentRow:(OOGUIRow value)
void setArray:forRow:(NSArray *arr,[forRow] OOGUIRow row)
void setKey:forRow:(NSString *str,[forRow] OOGUIRow row)
void suppressKeysUntilKeyUp()
NSMutableString * typedString
void setStringInput:(enum StringInput value)
void setTypedString:(NSString *value)
GameController * gameController
OOColor * cyanColor()
Definition OOColor.m:286
OOColor * orangeColor()
Definition OOColor.m:304
OOColor * redColor()
Definition OOColor.m:268
OOColor * greenColor()
Definition OOColor.m:274
OOColor * yellowColor()
Definition OOColor.m:292
OOJavaScriptEngine * sharedEngine()
void garbageCollectionOpportunity:(BOOL force)
OOShipRegistry * sharedRegistry()
NSDictionary * shipInfoForKey:(NSString *key)
void showShipyardModel:shipData:personality:(NSString *shipKey,[shipData] NSDictionary *shipDict,[personality] uint16_t personality)
void showShipModelWithKey:shipData:personality:factorX:factorY:factorZ:inContext:(NSString *shipKey,[shipData] NSDictionary *shipData,[personality] uint16_t personality,[factorX] GLfloat factorX,[factorY] GLfloat factorY,[factorZ] GLfloat factorZ,[inContext] NSString *context)
void setGuiToScenarioScreen:(int page)
void writePlayerToPath:(NSString *path)
void setGuiToStatusScreen()
BOOL loadPlayerFromFile:asNew:(NSString *fileToOpen,[asNew] BOOL asNew)
StationEntity * dockedStation()
void setShowDemoShips:(BOOL value)
NSDictionary * commanderDataDictionary()
void nativeSavePlayer:(NSString *cdrName)
void setLastsaveName:(NSString *value)
void completeSetUpAndSetTarget:(BOOL setTarget)
void calculateCurrentCargo()
void lsCommanders:directory:pageNumber:highlightName:(GuiDisplayGen *gui,[directory] NSString *directory,[pageNumber] int page,[highlightName] NSString *highlightName)
void addScenarioModel:(NSString *shipKey)
void setDockedAtMainStation()
void showCommanderShip:(int cdrArrayIndex)
void setDockedStation:(StationEntity *station)
NSString * lastsaveName()
int findIndexOfCommander:(NSString *cdrName)
void setGuiToOverwriteScreen:(NSString *cdrName)
void setGuiToSaveCommanderScreen:(NSString *cdrName)
BOOL doWorldEventUntilMissionScreen:(jsid message)
NSString * pathForFileNamed:inFolder:(NSString *fileName,[inFolder] NSString *folderName)
void setEntityPersonalityInt:(uint16_t value)
OOCommodityMarket * initialiseLocalMarket()
void setLocalMarket:(NSArray *market)
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque