- fix: only call frameCallBacks when game is actually running. - fix: js reset problems should not stall/crash the game anymore. Kept test harness (#if 0-ed) for further tests. - yep, some code cleanup. git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@4664 127b21dd-08f5-0310-b4b7-95ae10353056
1130 lines
34 KiB
Objective-C
1130 lines
34 KiB
Objective-C
/*
|
|
|
|
PlayerEntityLoadSave.m
|
|
|
|
Oolite
|
|
Copyright (C) 2004-2011 Giles C Williams and contributors
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
#import "PlayerEntityLoadSave.h"
|
|
#import "PlayerEntityContracts.h"
|
|
#import "PlayerEntityControls.h"
|
|
#import "PlayerEntitySound.h"
|
|
|
|
#import "NSFileManagerOOExtensions.h"
|
|
#import "GameController.h"
|
|
#import "PlayerEntityControls.h"
|
|
#import "OOXMLExtensions.h"
|
|
#import "OOSound.h"
|
|
#import "OOColor.h"
|
|
#import "OOStringParsing.h"
|
|
#import "OOPListParsing.h"
|
|
#import "StationEntity.h"
|
|
#import "OOCollectionExtractors.h"
|
|
#import "OOConstToString.h"
|
|
#import "OOShipRegistry.h"
|
|
#import "OOTexture.h"
|
|
#import "NSStringOOExtensions.h"
|
|
#import "NSNumberOOExtensions.h"
|
|
#import "OOJavaScriptEngine.h"
|
|
|
|
|
|
// Set to 1 to use custom load/save dialogs in windowed mode on Macs in debug builds. No effect on other platforms.
|
|
#define USE_CUSTOM_LOAD_SAVE_ON_MAC_DEBUG 0
|
|
|
|
#if USE_CUSTOM_LOAD_SAVE_ON_MAC_DEBUG && OO_DEBUG && defined(OOLITE_USE_APPKIT_LOAD_SAVE)
|
|
#undef OOLITE_USE_APPKIT_LOAD_SAVE
|
|
#endif
|
|
|
|
|
|
// Name of modifier key used to issue commands. See also -isCommandModifierKeyDown.
|
|
#if OOLITE_MAC_OS_X
|
|
#define COMMAND_MODIFIER_KEY "Ctrl" // was "Command"
|
|
#else
|
|
#define COMMAND_MODIFIER_KEY "Ctrl"
|
|
#endif
|
|
|
|
|
|
static uint16_t PersonalityForCommanderDict(NSDictionary *dict);
|
|
|
|
|
|
@interface MyOpenGLView (OOLoadSaveExtensions)
|
|
|
|
- (BOOL)isCommandModifierKeyDown;
|
|
|
|
@end
|
|
|
|
|
|
@interface PlayerEntity (OOLoadSavePrivate)
|
|
|
|
#if OOLITE_USE_APPKIT_LOAD_SAVE
|
|
|
|
- (BOOL) loadPlayerWithPanel;
|
|
- (void) savePlayerWithPanel;
|
|
|
|
#endif
|
|
|
|
- (void) setGuiToLoadCommanderScreen;
|
|
- (void) setGuiToSaveCommanderScreen: (NSString *)cdrName;
|
|
- (void) setGuiToOverwriteScreen: (NSString *)cdrName;
|
|
- (void) lsCommanders: (GuiDisplayGen *)gui directory: (NSString*)directory pageNumber: (int)page highlightName: (NSString *)highlightName;
|
|
- (void) writePlayerToPath:(NSString *)path;
|
|
- (void) nativeSavePlayer: (NSString *)cdrName;
|
|
- (BOOL) existingNativeSave: (NSString *)cdrName;
|
|
- (void) showCommanderShip: (int)cdrArrayIndex;
|
|
- (int) findIndexOfCommander: (NSString *)cdrName;
|
|
|
|
@end
|
|
|
|
|
|
@implementation PlayerEntity (LoadSave)
|
|
|
|
- (BOOL)loadPlayer
|
|
{
|
|
BOOL OK = YES;
|
|
|
|
#if OOLITE_USE_APPKIT_LOAD_SAVE
|
|
// OS X: use system open/save dialogs in windowed mode, custom interface in full-screen.
|
|
if ([[UNIVERSE gameController] inFullScreenMode])
|
|
{
|
|
[self setGuiToLoadCommanderScreen];
|
|
}
|
|
else
|
|
{
|
|
OK = [self loadPlayerWithPanel];
|
|
}
|
|
#else
|
|
// Other platforms: use custom interface all the time.
|
|
[self setGuiToLoadCommanderScreen];
|
|
#endif
|
|
return OK;
|
|
}
|
|
|
|
|
|
- (void)savePlayer
|
|
{
|
|
#if OOLITE_USE_APPKIT_LOAD_SAVE
|
|
// OS X: use system open/save dialogs in windowed mode, custom interface in full-screen.
|
|
if ([[UNIVERSE gameController] inFullScreenMode])
|
|
{
|
|
[self setGuiToSaveCommanderScreen:player_name];
|
|
}
|
|
else
|
|
{
|
|
[self savePlayerWithPanel];
|
|
}
|
|
#else
|
|
// Other platforms: use custom interface all the time.
|
|
[self setGuiToSaveCommanderScreen:player_name];
|
|
#endif
|
|
}
|
|
|
|
- (void) autosavePlayer
|
|
{
|
|
NSString *tmp_path = nil;
|
|
NSString *tmp_name = nil;
|
|
NSString *dir = [[UNIVERSE gameController] playerFileDirectory];
|
|
|
|
tmp_name = player_name;
|
|
tmp_path = save_path;
|
|
|
|
ShipScriptEventNoCx(self, "playerWillSaveGame", OOJSSTR("AUTO_SAVE"));
|
|
|
|
NSString *saveName = player_name;
|
|
if (![player_name hasSuffix:DESC(@"autosave-commander-suffix")])
|
|
saveName = [player_name stringByAppendingString:DESC(@"autosave-commander-suffix")];
|
|
NSString *savePath = [dir stringByAppendingPathComponent:[saveName stringByAppendingString:@".oolite-save"]];
|
|
|
|
[player_name autorelease];
|
|
player_name = [saveName copy];
|
|
|
|
NS_DURING
|
|
[self writePlayerToPath:savePath];
|
|
NS_HANDLER
|
|
// Suppress exceptions silently. Warning the user about failed autosaves would be pretty unhelpful.
|
|
NS_ENDHANDLER
|
|
|
|
if (tmp_path != nil)
|
|
{
|
|
[save_path autorelease];
|
|
save_path = [tmp_path copy];
|
|
}
|
|
[player_name autorelease];
|
|
player_name = [tmp_name copy];
|
|
}
|
|
|
|
|
|
- (void) quicksavePlayer
|
|
{
|
|
MyOpenGLView *gameView = [UNIVERSE gameView];
|
|
NSString *path = nil;
|
|
|
|
path = save_path;
|
|
if (!path) path = [[gameView gameController] playerFileToLoad];
|
|
if (!path)
|
|
{
|
|
OOLog(@"quickSave.failed.noName", @"ERROR no file name returned by [[gameView gameController] playerFileToLoad]");
|
|
[NSException raise:@"OoliteGameNotSavedException"
|
|
format:@"ERROR no file name returned by [[gameView gameController] playerFileToLoad]"];
|
|
}
|
|
|
|
ShipScriptEventNoCx(self, "playerWillSaveGame", OOJSSTR("QUICK_SAVE"));
|
|
|
|
[self writePlayerToPath:path];
|
|
[[UNIVERSE gameView] supressKeysUntilKeyUp];
|
|
[self setGuiToStatusScreen];
|
|
}
|
|
|
|
|
|
- (NSString *)commanderSelector
|
|
{
|
|
MyOpenGLView *gameView = [UNIVERSE gameView];
|
|
GuiDisplayGen *gui = [UNIVERSE gui];
|
|
NSString *dir = [[UNIVERSE gameController] playerFileDirectory];
|
|
|
|
int idx;
|
|
if([self handleGUIUpDownArrowKeys])
|
|
{
|
|
int guiSelectedRow=[gui selectedRow];
|
|
idx=(guiSelectedRow - STARTROW) + (currentPage * NUMROWS);
|
|
if (guiSelectedRow != MOREROW && guiSelectedRow != BACKROW)
|
|
{
|
|
[self showCommanderShip: idx];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
idx=([gui selectedRow] - STARTROW) + (currentPage * NUMROWS);
|
|
}
|
|
|
|
// handle page <-- and page --> keys
|
|
if ([gameView isDown:gvArrowKeyLeft] && [[gui keyForRow:BACKROW] isEqual: GUI_KEY_OK])
|
|
{
|
|
currentPage--;
|
|
[self playMenuPagePrevious];
|
|
[self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
|
|
[gameView supressKeysUntilKeyUp];
|
|
}
|
|
//
|
|
if ([gameView isDown:gvArrowKeyRight] && [[gui keyForRow:MOREROW] isEqual: GUI_KEY_OK])
|
|
{
|
|
currentPage++;
|
|
[self playMenuPageNext];
|
|
[self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
|
|
[gameView supressKeysUntilKeyUp];
|
|
}
|
|
|
|
// Enter pressed - find the commander name underneath.
|
|
if ([gameView isDown:13]||[gameView isDown:gvMouseDoubleClick])
|
|
{
|
|
NSDictionary *cdr;
|
|
switch ([gui selectedRow])
|
|
{
|
|
case BACKROW:
|
|
currentPage--;
|
|
[self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
|
|
[gameView supressKeysUntilKeyUp];
|
|
break;
|
|
case MOREROW:
|
|
currentPage++;
|
|
[self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
|
|
[gameView supressKeysUntilKeyUp];
|
|
break;
|
|
default:
|
|
cdr=[cdrDetailArray objectAtIndex: idx];
|
|
if ([cdr oo_boolForKey:@"isSavedGame"])
|
|
return [cdr oo_stringForKey:@"saved_game_path"];
|
|
else
|
|
{
|
|
if ([gameView isCommandModifierKeyDown]||[gameView isDown:gvMouseDoubleClick])
|
|
{
|
|
// change directory to the selected path
|
|
NSString* newDir = [cdr oo_stringForKey:@"saved_game_path"];
|
|
[[UNIVERSE gameController] setPlayerFileDirectory: newDir];
|
|
dir = newDir;
|
|
currentPage = 0;
|
|
[self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
|
|
[gameView supressKeysUntilKeyUp];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if([gameView isDown: 27]) // escape key
|
|
{
|
|
[self setGuiToStatusScreen];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
|
|
- (void) saveCommanderInputHandler
|
|
{
|
|
MyOpenGLView *gameView = [UNIVERSE gameView];
|
|
GuiDisplayGen *gui = [UNIVERSE gui];
|
|
NSString *dir = [[UNIVERSE gameController] playerFileDirectory];
|
|
|
|
if ([self handleGUIUpDownArrowKeys])
|
|
{
|
|
int guiSelectedRow=[gui selectedRow];
|
|
int idx = (guiSelectedRow - STARTROW) + (currentPage * NUMROWS);
|
|
if (guiSelectedRow != MOREROW && guiSelectedRow != BACKROW)
|
|
{
|
|
[self showCommanderShip: idx];
|
|
if ([(NSDictionary *)[cdrDetailArray objectAtIndex:idx] oo_boolForKey:@"isSavedGame"]) // don't show things that aren't saved games
|
|
commanderNameString = [[cdrDetailArray oo_dictionaryAtIndex:idx] oo_stringForKey:@"player_name"];
|
|
else
|
|
commanderNameString = [gameView typedString];
|
|
}
|
|
}
|
|
else
|
|
commanderNameString = [gameView typedString];
|
|
[gameView setTypedString: commanderNameString];
|
|
|
|
[gui setText:
|
|
[NSString stringWithFormat:DESC(@"savescreen-commander-name-@"), commanderNameString]
|
|
forRow: INPUTROW];
|
|
[gui setColor:[OOColor cyanColor] forRow:INPUTROW];
|
|
|
|
// handle page <-- and page --> keys, and on-screen buttons
|
|
if ((([gameView isDown:gvMouseDoubleClick] && [gui selectedRow] == BACKROW) || [gameView isDown:gvArrowKeyLeft]) && [[gui keyForRow:BACKROW] isEqual: GUI_KEY_OK])
|
|
{
|
|
currentPage--;
|
|
[self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
|
|
[gameView supressKeysUntilKeyUp];
|
|
}
|
|
//
|
|
if ((([gameView isDown:gvMouseDoubleClick] && [gui selectedRow] == MOREROW) || [gameView isDown:gvArrowKeyRight]) && [[gui keyForRow:MOREROW] isEqual: GUI_KEY_OK])
|
|
{
|
|
currentPage++;
|
|
[self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
|
|
[gameView supressKeysUntilKeyUp];
|
|
}
|
|
|
|
if(([gameView isDown: 13]||[gameView isDown:gvMouseDoubleClick]) && [commanderNameString length])
|
|
{
|
|
if ([gameView isCommandModifierKeyDown]||[gameView isDown:gvMouseDoubleClick])
|
|
{
|
|
int guiSelectedRow=[gui selectedRow];
|
|
int idx = (guiSelectedRow - STARTROW) + (currentPage * NUMROWS);
|
|
NSDictionary* cdr = [cdrDetailArray objectAtIndex:idx];
|
|
if (![cdr oo_boolForKey:@"isSavedGame"]) // don't open saved games
|
|
{
|
|
// change directory to the selected path
|
|
NSString* newDir = [cdr oo_stringForKey:@"saved_game_path"];
|
|
[[UNIVERSE gameController] setPlayerFileDirectory: newDir];
|
|
dir = newDir;
|
|
currentPage = 0;
|
|
[self lsCommanders: gui directory: dir pageNumber: currentPage highlightName: nil];
|
|
[gameView supressKeysUntilKeyUp];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pollControls = YES;
|
|
if ([self existingNativeSave: commanderNameString])
|
|
{
|
|
[gameView supressKeysUntilKeyUp];
|
|
[self setGuiToOverwriteScreen: commanderNameString];
|
|
}
|
|
else
|
|
{
|
|
[self nativeSavePlayer: commanderNameString];
|
|
[[UNIVERSE gameView] supressKeysUntilKeyUp];
|
|
[self setGuiToStatusScreen];
|
|
}
|
|
}
|
|
}
|
|
|
|
if([gameView isDown: 27]) // escape key
|
|
{
|
|
// get out of here
|
|
pollControls = YES;
|
|
[[UNIVERSE gameView] resetTypedString];
|
|
[self setGuiToStatusScreen];
|
|
}
|
|
}
|
|
|
|
|
|
- (void) overwriteCommanderInputHandler
|
|
{
|
|
MyOpenGLView *gameView = [UNIVERSE gameView];
|
|
GuiDisplayGen *gui = [UNIVERSE gui];
|
|
|
|
[self handleGUIUpDownArrowKeys];
|
|
|
|
// Translation issue: we can't confidently use raw Y and N ascii as shortcuts. It's better to use the load-previous-commander keys.
|
|
id valueYes = [[[UNIVERSE descriptions] oo_stringForKey:@"load-previous-commander-yes" defaultValue:@"y"] lowercaseString];
|
|
id valueNo = [[[UNIVERSE descriptions] oo_stringForKey:@"load-previous-commander-no" defaultValue:@"n"] lowercaseString];
|
|
unsigned char cYes, cNo;
|
|
|
|
cYes = [valueYes characterAtIndex: 0] & 0x00ff; // Use lower byte of unichar.
|
|
cNo = [valueNo characterAtIndex: 0] & 0x00ff; // Use lower byte of unichar.
|
|
|
|
if (([gameView isDown:13] && ([gui selectedRow] == SAVE_OVERWRITE_YES_ROW))||[gameView isDown:cYes]||[gameView isDown:cYes - 32])
|
|
{
|
|
pollControls=YES;
|
|
[self nativeSavePlayer: commanderNameString];
|
|
[self playSaveOverwriteYes];
|
|
[[UNIVERSE gameView] supressKeysUntilKeyUp];
|
|
[self setGuiToStatusScreen];
|
|
}
|
|
|
|
if (([gameView isDown:13] && ([gui selectedRow] == SAVE_OVERWRITE_NO_ROW))||[gameView isDown:27]||[gameView isDown:cNo]||[gameView isDown:cNo - 32])
|
|
{
|
|
// esc or NO was pressed - get out of here
|
|
pollControls=YES;
|
|
[self playSaveOverwriteNo];
|
|
[self setGuiToSaveCommanderScreen:@""];
|
|
}
|
|
}
|
|
|
|
|
|
- (BOOL) loadPlayerFromFile:(NSString *)fileToOpen
|
|
{
|
|
/* TODO: it would probably be better to load by creating a new
|
|
PlayerEntity, verifying that's OK, then replacing the global player.
|
|
|
|
Actually, it'd be better to separate PlayerEntity into OOPlayer and
|
|
OOPlayerShipEntity. And then move most of OOPlayerShipEntity into
|
|
ShipEntity, and make NPC ships behave more like player ships.
|
|
-- Ahruman
|
|
*/
|
|
|
|
BOOL loadedOK = YES;
|
|
NSDictionary *fileDic = nil;
|
|
NSString *fail_reason = nil;
|
|
|
|
if (fileToOpen == nil)
|
|
{
|
|
fail_reason = DESC(@"loadfailed-no-file-specified");
|
|
loadedOK = NO;
|
|
}
|
|
|
|
if (loadedOK)
|
|
{
|
|
fileDic = OODictionaryFromFile(fileToOpen);
|
|
if (fileDic == nil)
|
|
{
|
|
fail_reason = DESC(@"loadfailed-could-not-load-file");
|
|
loadedOK = NO;
|
|
}
|
|
}
|
|
|
|
if (loadedOK)
|
|
{
|
|
NSString *shipKey = nil;
|
|
NSDictionary *shipDict = nil;
|
|
|
|
shipKey = [fileDic oo_stringForKey:@"ship_desc"];
|
|
shipDict = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipKey];
|
|
|
|
if (shipDict == nil && [UNIVERSE strict] && shipKey != nil)
|
|
{
|
|
fail_reason = [NSString stringWithFormat:DESC(@"loadfailed-could-not-use-ship-type-@-please-switch-to-unrestricted"), shipKey];
|
|
loadedOK = NO;
|
|
}
|
|
}
|
|
|
|
|
|
if (loadedOK)
|
|
{
|
|
// Check that player ship exists
|
|
NSString *shipKey = nil;
|
|
NSDictionary *shipDict = nil;
|
|
|
|
shipKey = [fileDic oo_stringForKey:@"ship_desc"];
|
|
shipDict = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipKey];
|
|
|
|
if (shipDict == nil)
|
|
{
|
|
loadedOK = NO;
|
|
if (shipKey != nil) fail_reason = [NSString stringWithFormat:DESC(@"loadfailed-could-not-find-ship-type-@-please-reinstall-the-appropriate-OXP"), shipKey];
|
|
else fail_reason = DESC(@"loadfailed-invalid-saved-game-no-ship-specified");
|
|
}
|
|
}
|
|
|
|
if (loadedOK)
|
|
{
|
|
if (![self setUpAndConfirmOK:YES saveGame:YES] || ![self setCommanderDataFromDictionary:fileDic])
|
|
{
|
|
fail_reason = DESC(@"loadfailed-could-not-set-up-player-ship");
|
|
loadedOK = NO;
|
|
}
|
|
}
|
|
|
|
if (loadedOK)
|
|
{
|
|
[save_path autorelease];
|
|
save_path = [fileToOpen retain];
|
|
|
|
[[[UNIVERSE gameView] gameController] setPlayerFileToLoad:fileToOpen];
|
|
[[[UNIVERSE gameView] gameController] setPlayerFileDirectory:fileToOpen];
|
|
}
|
|
else
|
|
{
|
|
OOLog(@"load.failed", @"***** Failed to load saved game \"%@\": %@", [fileToOpen lastPathComponent], fail_reason ? fail_reason : (NSString *)@"unknown error");
|
|
[[UNIVERSE gameController] setPlayerFileToLoad:nil];
|
|
[UNIVERSE handleGameOver];
|
|
[UNIVERSE clearPreviousMessage];
|
|
[UNIVERSE addMessage:DESC(@"loadfailed-saved-game-failed-to-load") forCount: 9.0];
|
|
if (fail_reason != nil) [UNIVERSE addMessage: fail_reason forCount: 9.0];
|
|
return NO;
|
|
}
|
|
|
|
[UNIVERSE setTimeAccelerationFactor:TIME_ACCELERATION_FACTOR_DEFAULT];
|
|
[UNIVERSE setSystemTo:system_seed];
|
|
[UNIVERSE removeAllEntitiesExceptPlayer];
|
|
[UNIVERSE setGalaxySeed: galaxy_seed andReinit:YES]; // set overridden planet names on long range map
|
|
[UNIVERSE setUpSpace];
|
|
[UNIVERSE setAutoSaveNow:NO];
|
|
|
|
[self setStatus:STATUS_DOCKED];
|
|
[UNIVERSE setViewDirection:VIEW_GUI_DISPLAY];
|
|
|
|
dockedStation = [UNIVERSE station];
|
|
if (dockedStation)
|
|
{
|
|
position = [dockedStation position];
|
|
[self setOrientation: kIdentityQuaternion];
|
|
v_forward = vector_forward_from_quaternion(orientation);
|
|
v_right = vector_right_from_quaternion(orientation);
|
|
v_up = vector_up_from_quaternion(orientation);
|
|
}
|
|
|
|
flightRoll = 0.0;
|
|
flightPitch = 0.0;
|
|
flightYaw = 0.0;
|
|
flightSpeed = 0.0;
|
|
|
|
[self setEntityPersonalityInt:PersonalityForCommanderDict(fileDic)];
|
|
|
|
if (![dockedStation localMarket])
|
|
{
|
|
NSArray *market = [fileDic oo_arrayForKey:@"localMarket"];
|
|
if (market != nil) [dockedStation setLocalMarket:market];
|
|
else [dockedStation initialiseLocalMarketWithRandomFactor:market_rnd];
|
|
}
|
|
[self calculateCurrentCargo];
|
|
|
|
// Remember the savegame target, run js startUp.
|
|
[self completeSetUpAndSetTarget:NO];
|
|
|
|
[[UNIVERSE gameView] supressKeysUntilKeyUp];
|
|
[self setGuiToStatusScreen];
|
|
if (loadedOK) [self doWorldEventUntilMissionScreen:OOJSID("missionScreenOpportunity")]; // trigger missionScreenOpportunity immediately after loading
|
|
return loadedOK;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@implementation PlayerEntity (OOLoadSavePrivate)
|
|
|
|
#if OOLITE_USE_APPKIT_LOAD_SAVE
|
|
|
|
- (BOOL)loadPlayerWithPanel
|
|
{
|
|
int result;
|
|
NSArray *fileTypes = nil;
|
|
NSOpenPanel *oPanel = nil;
|
|
|
|
fileTypes = [NSArray arrayWithObject:@"oolite-save"];
|
|
oPanel = [NSOpenPanel openPanel];
|
|
|
|
[oPanel setAllowsMultipleSelection:NO];
|
|
result = [oPanel runModalForDirectory:nil file:nil types:fileTypes];
|
|
if (result == NSOKButton)
|
|
{
|
|
return [self loadPlayerFromFile:[oPanel filename]];
|
|
}
|
|
else
|
|
{
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
|
|
- (void) savePlayerWithPanel
|
|
{
|
|
NSSavePanel *sp;
|
|
int runResult;
|
|
|
|
sp = [NSSavePanel savePanel];
|
|
[sp setRequiredFileType:@"oolite-save"];
|
|
[sp setCanSelectHiddenExtension:YES];
|
|
|
|
// display the NSSavePanel
|
|
runResult = [sp runModalForDirectory:nil file:player_name];
|
|
|
|
// if successful, save file under designated name
|
|
if (runResult == NSOKButton)
|
|
{
|
|
NSArray* path_components = [[sp filename] pathComponents];
|
|
NSString* new_name = [[path_components objectAtIndex:[path_components count]-1] stringByDeletingPathExtension];
|
|
|
|
ShipScriptEventNoCx(self, "playerWillSaveGame", OOJSSTR("STANDARD_SAVE"));
|
|
|
|
[player_name release];
|
|
player_name = [new_name copy];
|
|
|
|
[self writePlayerToPath:[sp filename]];
|
|
}
|
|
[self setGuiToStatusScreen];
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
- (void) writePlayerToPath:(NSString *)path
|
|
{
|
|
NSString *errDesc = nil;
|
|
NSDictionary *dict = nil;
|
|
BOOL didSave = NO;
|
|
[[UNIVERSE gameView] resetTypedString];
|
|
|
|
if (!path)
|
|
{
|
|
OOLog(@"save.failed", @"***** SAVE ERROR: %s called with nil path.", __PRETTY_FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
dict = [self commanderDataDictionary];
|
|
if (dict == nil) errDesc = @"could not construct commander data dictionary.";
|
|
else didSave = [dict writeOOXMLToFile:path atomically:YES errorDescription:&errDesc];
|
|
if (didSave)
|
|
{
|
|
[UNIVERSE clearPreviousMessage]; // allow this to be given time and again
|
|
[UNIVERSE addMessage:DESC(@"game-saved") forCount:2];
|
|
[save_path autorelease];
|
|
save_path = [path copy];
|
|
[[UNIVERSE gameController] setPlayerFileToLoad:save_path];
|
|
[[UNIVERSE gameController] setPlayerFileDirectory:save_path];
|
|
// no duplicated autosave immediately after a save.
|
|
[UNIVERSE setAutoSaveNow:NO];
|
|
}
|
|
else
|
|
{
|
|
OOLog(@"save.failed", @"***** SAVE ERROR: %@", errDesc);
|
|
[NSException raise:@"OoliteException"
|
|
format:@"Attempt to save game to file '%@' failed: %@", path, errDesc];
|
|
}
|
|
[[UNIVERSE gameView] supressKeysUntilKeyUp];
|
|
[self setGuiToStatusScreen];
|
|
}
|
|
|
|
|
|
- (void)nativeSavePlayer:(NSString *)cdrName
|
|
{
|
|
NSString* dir = [[UNIVERSE gameController] playerFileDirectory];
|
|
NSString *savePath = [dir stringByAppendingPathComponent:[cdrName stringByAppendingPathExtension:@"oolite-save"]];
|
|
|
|
ShipScriptEventNoCx(self, "playerWillSaveGame", OOJSSTR("STANDARD_SAVE"));
|
|
|
|
[player_name release];
|
|
player_name = [cdrName copy];
|
|
|
|
[self writePlayerToPath:savePath];
|
|
}
|
|
|
|
|
|
|
|
- (void) setGuiToLoadCommanderScreen
|
|
{
|
|
GuiDisplayGen *gui=[UNIVERSE gui];
|
|
NSString* dir = [[UNIVERSE gameController] playerFileDirectory];
|
|
|
|
gui_screen = GUI_SCREEN_LOAD;
|
|
|
|
[gui clear];
|
|
[gui setTitle:DESC(@"loadscreen-title")];
|
|
|
|
currentPage = 0;
|
|
[self lsCommanders:gui directory:dir pageNumber: currentPage highlightName:nil];
|
|
|
|
[gui setForegroundTextureKey:@"docked_overlay"];
|
|
[gui setBackgroundTextureKey:@"load_save"];
|
|
|
|
[[UNIVERSE gameView] supressKeysUntilKeyUp];
|
|
|
|
[self setShowDemoShips: YES];
|
|
[UNIVERSE setDisplayCursor: YES];
|
|
[UNIVERSE setViewDirection: VIEW_GUI_DISPLAY];
|
|
}
|
|
|
|
|
|
- (void) setGuiToSaveCommanderScreen: (NSString *)cdrName
|
|
{
|
|
GuiDisplayGen *gui=[UNIVERSE gui];
|
|
MyOpenGLView* gameView = [UNIVERSE gameView];
|
|
NSString* dir = [[UNIVERSE gameController] playerFileDirectory];
|
|
|
|
pollControls = NO;
|
|
gui_screen = GUI_SCREEN_SAVE;
|
|
|
|
[gui clear];
|
|
[gui setTitle:[NSString stringWithFormat:DESC(@"savescreen-title")]];
|
|
|
|
currentPage = 0;
|
|
[self lsCommanders:gui directory:dir pageNumber: currentPage highlightName:nil];
|
|
|
|
[gui setText:DESC(@"savescreen-commander-name") forRow: INPUTROW];
|
|
[gui setColor:[OOColor cyanColor] forRow:INPUTROW];
|
|
[gui setShowTextCursor: YES];
|
|
[gui setCurrentRow: INPUTROW];
|
|
|
|
[gui setForegroundTextureKey:@"docked_overlay"];
|
|
[gui setBackgroundTextureKey:@"load_save"];
|
|
|
|
[gameView setTypedString: cdrName];
|
|
[gameView supressKeysUntilKeyUp];
|
|
|
|
[self setShowDemoShips: YES];
|
|
[UNIVERSE setDisplayCursor: YES];
|
|
[UNIVERSE setViewDirection: VIEW_GUI_DISPLAY];
|
|
}
|
|
|
|
|
|
- (void) setGuiToOverwriteScreen: (NSString *)cdrName
|
|
{
|
|
GuiDisplayGen *gui=[UNIVERSE gui];
|
|
MyOpenGLView* gameView = [UNIVERSE gameView];
|
|
|
|
// Don't poll controls
|
|
pollControls=NO;
|
|
|
|
gui_screen = GUI_SCREEN_SAVE_OVERWRITE;
|
|
|
|
[gui clear];
|
|
[gui setTitle:[NSString stringWithFormat:DESC(@"overwrite-save-commander-@"), cdrName]];
|
|
|
|
[gui setText:[NSString stringWithFormat:DESC(@"overwritescreen-commander-@-already-exists-overwrite-query"), cdrName]
|
|
forRow:SAVE_OVERWRITE_WARN_ROW align: GUI_ALIGN_CENTER];
|
|
|
|
[gui setText:DESC(@"overwritescreen-yes") forRow: SAVE_OVERWRITE_YES_ROW align: GUI_ALIGN_CENTER];
|
|
[gui setKey:GUI_KEY_OK forRow: SAVE_OVERWRITE_YES_ROW];
|
|
|
|
[gui setText:DESC(@"overwritescreen-no") forRow: SAVE_OVERWRITE_NO_ROW align: GUI_ALIGN_CENTER];
|
|
[gui setKey:GUI_KEY_OK forRow: SAVE_OVERWRITE_NO_ROW];
|
|
|
|
[gui setSelectableRange: NSMakeRange(SAVE_OVERWRITE_YES_ROW, 2)];
|
|
[gui setSelectedRow: SAVE_OVERWRITE_NO_ROW];
|
|
|
|
// We can only leave this screen by answering yes or no, or esc. Therefore
|
|
// use a specific overlay, to allow visual reminders of the available options.
|
|
[gui setForegroundTextureKey:@"overwrite_overlay"];
|
|
[gui setBackgroundTextureKey:@"load_save"];
|
|
|
|
[self setShowDemoShips: NO];
|
|
[UNIVERSE setDisplayCursor: NO];
|
|
[gameView setStringInput: gvStringInputNo];
|
|
[UNIVERSE setViewDirection: VIEW_GUI_DISPLAY];
|
|
}
|
|
|
|
|
|
- (void) lsCommanders: (GuiDisplayGen *)gui
|
|
directory: (NSString*) directory
|
|
pageNumber: (int)page
|
|
highlightName: (NSString *)highlightName
|
|
{
|
|
NSFileManager *cdrFileManager=[NSFileManager defaultManager];
|
|
int rangeStart=STARTROW;
|
|
unsigned lastIndex;
|
|
unsigned i;
|
|
int row=STARTROW;
|
|
|
|
// cdrArray defined in PlayerEntity.h
|
|
NSArray *cdrArray=[cdrFileManager commanderContentsOfPath: directory];
|
|
|
|
// get commander details so a brief rundown of the commander's details may
|
|
// be displayed.
|
|
if (!cdrDetailArray)
|
|
cdrDetailArray=[[NSMutableArray alloc] init]; // alloc retains this so the retain further on in the code was unnecessary
|
|
else
|
|
[cdrDetailArray removeAllObjects];
|
|
|
|
[cdrDetailArray addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
|
@"YES", @"isParentFolder",
|
|
[directory stringByDeletingLastPathComponent], @"saved_game_path", nil]];
|
|
|
|
for(i = 0; i < [cdrArray count]; i++)
|
|
{
|
|
NSString* path = [cdrArray objectAtIndex:i];
|
|
BOOL exists, isDirectory = NO;
|
|
|
|
exists = [cdrFileManager fileExistsAtPath:path isDirectory:&isDirectory];
|
|
|
|
if (exists)
|
|
{
|
|
if (!isDirectory && [[[path pathExtension] lowercaseString] isEqualToString:@"oolite-save"])
|
|
{
|
|
NSDictionary *cdr = OODictionaryFromFile(path);
|
|
if(cdr)
|
|
{
|
|
// okay use the same dictionary but add a 'saved_game_path' attribute
|
|
NSMutableDictionary* cdr1 = [NSMutableDictionary dictionaryWithDictionary:cdr];
|
|
[cdr1 setObject: @"YES" forKey:@"isSavedGame"];
|
|
[cdr1 setObject: path forKey:@"saved_game_path"];
|
|
[cdrDetailArray addObject: cdr1];
|
|
}
|
|
}
|
|
if (isDirectory && ![[path lastPathComponent] hasPrefix:@"."])
|
|
{
|
|
[cdrDetailArray addObject: [NSDictionary dictionaryWithObjectsAndKeys: @"YES", @"isFolder", path, @"saved_game_path", nil]];
|
|
}
|
|
}
|
|
}
|
|
|
|
if(![cdrDetailArray count])
|
|
{
|
|
// Empty directory; tell the user and exit immediately.
|
|
[gui setText:DESC(@"loadsavescreen-no-commanders-found") forRow:STARTROW align:GUI_ALIGN_CENTER];
|
|
return;
|
|
}
|
|
|
|
// Do we need to highlight a name?
|
|
int highlightRowOnPage=STARTROW;
|
|
int highlightIdx=0;
|
|
if(highlightName)
|
|
{
|
|
highlightIdx=[self findIndexOfCommander: highlightName];
|
|
if(highlightIdx < 0)
|
|
{
|
|
OOLog(@"save.list.commanders.commanderNotFound", @"Commander %@ doesn't exist, very bad", highlightName);
|
|
highlightIdx=0;
|
|
}
|
|
|
|
// figure out what page we need to be on
|
|
page=highlightIdx/NUMROWS;
|
|
highlightRowOnPage=highlightIdx % NUMROWS + STARTROW;
|
|
}
|
|
|
|
// We now know for certain what page we're on -
|
|
// set the first index of the first commander on this page.
|
|
unsigned firstIndex=page * NUMROWS;
|
|
|
|
// Set up the GUI.
|
|
OOGUITabSettings tabStop;
|
|
tabStop[0]=0;
|
|
tabStop[1]=160;
|
|
tabStop[2]=270;
|
|
[gui setTabStops: tabStop];
|
|
[gui setColor: [OOColor greenColor] forRow: LABELROW];
|
|
[gui setArray: [NSArray arrayWithObjects: DESC(@"loadsavescreen-commander-name"), DESC(@"loadsavescreen-rating"), nil]
|
|
forRow:LABELROW];
|
|
|
|
// clear text lines here
|
|
for (i = STARTROW - 1; i < ENDROW + 1; i++)
|
|
{
|
|
[gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
|
|
[gui setColor: [OOColor yellowColor] forRow: i];
|
|
[gui setKey:GUI_KEY_SKIP forRow:i];
|
|
}
|
|
|
|
if (page)
|
|
{
|
|
[gui setColor:[OOColor greenColor] forRow:STARTROW-1];
|
|
[gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @" <-- ", nil]
|
|
forRow:STARTROW-1];
|
|
[gui setKey:GUI_KEY_OK forRow:STARTROW-1];
|
|
rangeStart=STARTROW-1;
|
|
}
|
|
|
|
if (firstIndex + NUMROWS >= [cdrDetailArray count])
|
|
{
|
|
lastIndex=[cdrDetailArray count];
|
|
[gui setSelectableRange: NSMakeRange(rangeStart, rangeStart + NUMROWS)];
|
|
}
|
|
else
|
|
{
|
|
lastIndex=(page * NUMROWS) + NUMROWS;
|
|
[gui setColor:[OOColor greenColor] forRow:ENDROW];
|
|
[gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @" --> ", nil]
|
|
forRow:ENDROW];
|
|
[gui setKey:GUI_KEY_OK forRow:ENDROW];
|
|
[gui setSelectableRange: NSMakeRange(rangeStart, MOREROW)];
|
|
}
|
|
|
|
for (i=firstIndex; i < lastIndex; i++)
|
|
{
|
|
NSDictionary *cdr=[cdrDetailArray objectAtIndex: i];
|
|
if ([cdr oo_boolForKey:@"isSavedGame"])
|
|
{
|
|
NSString *ratingDesc = OODisplayRatingStringFromKillCount([cdr oo_unsignedIntForKey:@"ship_kills"]);
|
|
[gui setArray:[NSArray arrayWithObjects:
|
|
[NSString stringWithFormat:@" %@ ",[cdr oo_stringForKey:@"player_name"]],
|
|
[NSString stringWithFormat:@" %@ ",ratingDesc],
|
|
nil]
|
|
forRow:row];
|
|
if ([player_name isEqualToString:[cdr oo_stringForKey:@"player_name"]])
|
|
highlightRowOnPage = row;
|
|
|
|
[gui setKey:GUI_KEY_OK forRow:row];
|
|
row++;
|
|
}
|
|
if ([cdr oo_boolForKey:@"isParentFolder"])
|
|
{
|
|
[gui setArray:[NSArray arrayWithObjects:
|
|
[NSString stringWithFormat:@" (..) %@ ", [[cdr oo_stringForKey:@"saved_game_path"] lastPathComponent]],
|
|
@"",
|
|
nil]
|
|
forRow:row];
|
|
[gui setColor: [OOColor orangeColor] forRow: row];
|
|
[gui setKey:GUI_KEY_OK forRow:row];
|
|
row++;
|
|
}
|
|
if ([cdr oo_boolForKey:@"isFolder"])
|
|
{
|
|
[gui setArray:[NSArray arrayWithObjects:
|
|
[NSString stringWithFormat:@" >> %@ ", [[cdr oo_stringForKey:@"saved_game_path"] lastPathComponent]],
|
|
@"",
|
|
nil]
|
|
forRow:row];
|
|
[gui setColor: [OOColor orangeColor] forRow: row];
|
|
[gui setKey:GUI_KEY_OK forRow:row];
|
|
row++;
|
|
}
|
|
}
|
|
[gui setSelectedRow: highlightRowOnPage];
|
|
highlightIdx = (highlightRowOnPage - STARTROW) + (currentPage * NUMROWS);
|
|
// show the first ship, this will be the selected row
|
|
[self showCommanderShip: highlightIdx];
|
|
}
|
|
|
|
|
|
// check for an existing saved game...
|
|
- (BOOL) existingNativeSave: (NSString *)cdrName
|
|
{
|
|
NSString* dir = [[UNIVERSE gameController] playerFileDirectory];
|
|
|
|
NSString *savePath=[dir stringByAppendingPathComponent:[cdrName stringByAppendingPathExtension:@"oolite-save"]];
|
|
return [[NSFileManager defaultManager] fileExistsAtPath:savePath];
|
|
}
|
|
|
|
|
|
// Get some brief details about the commander file.
|
|
- (void) showCommanderShip:(int)cdrArrayIndex
|
|
{
|
|
GuiDisplayGen *gui=[UNIVERSE gui];
|
|
[UNIVERSE removeDemoShips];
|
|
NSDictionary *cdr=[cdrDetailArray objectAtIndex: cdrArrayIndex];
|
|
|
|
[gui setText:@"" forRow:CDRDESCROW align:GUI_ALIGN_LEFT];
|
|
[gui setText:@"" forRow:CDRDESCROW + 1 align:GUI_ALIGN_LEFT];
|
|
[gui setText:@"" forRow:CDRDESCROW + 2 align:GUI_ALIGN_LEFT];
|
|
|
|
if ([cdr oo_boolForKey:@"isFolder"])
|
|
{
|
|
NSString *folderDesc=[NSString stringWithFormat: DESC(@"loadsavescreen-hold-@-and-press-return-to-open-folder-@"), @COMMAND_MODIFIER_KEY, [[cdr oo_stringForKey:@"saved_game_path"] lastPathComponent]];
|
|
[gui setColor: [OOColor orangeColor] forRow: CDRDESCROW];
|
|
[gui addLongText: folderDesc startingAtRow: CDRDESCROW align: GUI_ALIGN_LEFT];
|
|
return;
|
|
}
|
|
|
|
if ([cdr oo_boolForKey:@"isParentFolder"])
|
|
{
|
|
NSString *folderDesc=[NSString stringWithFormat: DESC(@"loadsavescreen-hold-@-and-press-return-to-open-parent-folder-@"), @COMMAND_MODIFIER_KEY, [[cdr oo_stringForKey:@"saved_game_path"] lastPathComponent]];
|
|
[gui setColor: [OOColor orangeColor] forRow: CDRDESCROW];
|
|
[gui addLongText: folderDesc startingAtRow: CDRDESCROW align: GUI_ALIGN_LEFT];
|
|
return;
|
|
}
|
|
[gui setColor: [OOColor yellowColor] forRow: CDRDESCROW];
|
|
|
|
if (![cdr oo_boolForKey:@"isSavedGame"]) // don't show things that aren't saved games
|
|
return;
|
|
|
|
if(!dockedStation) dockedStation = [UNIVERSE station];
|
|
|
|
// Display the commander's ship.
|
|
NSString *shipDesc = [cdr oo_stringForKey:@"ship_desc"];
|
|
NSString *shipName = nil;
|
|
NSDictionary *shipDict = nil;
|
|
NSString *rating = nil;
|
|
uint16_t personality = PersonalityForCommanderDict(cdr);
|
|
|
|
shipDict = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipDesc];
|
|
if(shipDict != nil)
|
|
{
|
|
NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:[shipDict count] + 1];
|
|
[dict setDictionary:shipDict];
|
|
id subEntStatus = [cdr objectForKey:@"subentities_status"];
|
|
// don't add it to the dictionary if there's no subentities_status key
|
|
if (subEntStatus != nil) [dict setObject:subEntStatus forKey:@"subentities_status"];
|
|
[self showShipyardModel:shipDesc shipData:dict personality:personality];
|
|
[dict release];
|
|
shipName = [shipDict oo_stringForKey:@"display_name"];
|
|
if (shipName == nil) shipName = [shipDict oo_stringForKey:KEY_NAME];
|
|
}
|
|
else
|
|
{
|
|
[self showShipyardModel:@"oolite-unknown-ship" shipData:nil personality:personality];
|
|
shipName = [cdr oo_stringForKey:@"ship_name" defaultValue:@"unknown"];
|
|
shipName = [shipName stringByAppendingString:@" - OXP not installed"];
|
|
}
|
|
|
|
// Make a short description of the commander
|
|
NSString *legalDesc = OODisplayStringFromLegalStatus([cdr oo_intForKey:@"legal_status"]);
|
|
|
|
rating = KillCountToRatingAndKillString([cdr oo_unsignedIntForKey:@"ship_kills"]);
|
|
OOCreditsQuantity money = OODeciCreditsFromObject([cdr objectForKey:@"credits"]);
|
|
|
|
// Nikos - Add some more information in the load game screen (current location, galaxy number and timestamp).
|
|
//-------------------------------------------------------------------------------------------------------------------------
|
|
|
|
// Store the current galaxy seed because findSystemNumberAtCoords may alter it in a while.
|
|
PlayerEntity *player = PLAYER;
|
|
Random_Seed player_galaxy_seed = [player galaxy_seed];
|
|
|
|
int galNumber;
|
|
NSString *timeStamp = nil;
|
|
NSString *locationName = [cdr oo_stringForKey:@"current_system_name"];
|
|
|
|
// If there is no key containing the name of the current system in the savefile, fall back to
|
|
// extracting the name from the galaxy seed and coordinates information.
|
|
if (locationName == nil)
|
|
{
|
|
Random_Seed gal_seed;
|
|
NSPoint gal_coords;
|
|
int locationNumber;
|
|
|
|
gal_coords = PointFromString([cdr oo_stringForKey:@"galaxy_coordinates"]);
|
|
gal_seed = RandomSeedFromString([cdr oo_stringForKey:@"galaxy_seed"]);
|
|
locationNumber = [UNIVERSE findSystemNumberAtCoords:gal_coords withGalaxySeed:gal_seed];
|
|
locationName = [UNIVERSE systemNameIndex:locationNumber];
|
|
}
|
|
|
|
galNumber = [cdr oo_intForKey:@"galaxy_number"] + 1; // Galaxy numbering starts at 0.
|
|
|
|
timeStamp = ClockToString([cdr oo_doubleForKey:@"ship_clock" defaultValue:PLAYER_SHIP_CLOCK_START], NO);
|
|
|
|
//-------------------------------------------------------------------------------------------------------------------------
|
|
|
|
NSString *cdrDesc = nil;
|
|
|
|
cdrDesc = [NSString stringWithFormat:DESC(@"loadsavescreen-commander-@-rated-@-has-@-legal-status-@-ship-@-location-@-g-@-timestamp-@"),
|
|
[cdr oo_stringForKey:@"player_name"],
|
|
rating,
|
|
OOCredits(money),
|
|
legalDesc,
|
|
shipName,
|
|
locationName,
|
|
galNumber,
|
|
timeStamp];
|
|
|
|
[gui addLongText:cdrDesc startingAtRow:CDRDESCROW align:GUI_ALIGN_LEFT];
|
|
|
|
// Restore the seed of the galaxy the player is currently in.
|
|
[UNIVERSE setGalaxySeed: player_galaxy_seed];
|
|
}
|
|
|
|
|
|
- (int) findIndexOfCommander: (NSString *)cdrName
|
|
{
|
|
unsigned i;
|
|
for (i=0; i < [cdrDetailArray count]; i++)
|
|
{
|
|
NSString *currentName = [[cdrDetailArray oo_dictionaryAtIndex: i] oo_stringForKey:@"player_name"];
|
|
if([cdrName compare: currentName] == NSOrderedSame)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
// not found!
|
|
return -1;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@implementation MyOpenGLView (OOLoadSaveExtensions)
|
|
|
|
- (BOOL)isCommandModifierKeyDown
|
|
{
|
|
#if OOLITE_MAC_OS_X
|
|
//return [self isCommandDown];
|
|
return [self isCtrlDown];
|
|
#else
|
|
return [self isCtrlDown];
|
|
#endif
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
static uint16_t PersonalityForCommanderDict(NSDictionary *dict)
|
|
{
|
|
uint16_t personality = [dict oo_unsignedShortForKey:@"entity_personality" defaultValue:ENTITY_PERSONALITY_INVALID];
|
|
|
|
if (personality == ENTITY_PERSONALITY_INVALID)
|
|
{
|
|
// For pre-1.74 saved games, generate a default personality based on some hashes.
|
|
personality = [[dict oo_stringForKey:@"ship_desc"] oo_hash] * [[dict oo_stringForKey:@"player_name"] oo_hash];
|
|
}
|
|
|
|
return personality & ENTITY_PERSONALITY_MAX;
|
|
}
|
|
|
|
|
|
OOCreditsQuantity OODeciCreditsFromDouble(double doubleDeciCredits)
|
|
{
|
|
/* Clamp value to 0..kOOMaxCredits.
|
|
The important bit here is that kOOMaxCredits can't be represented
|
|
exactly as a double, and casting it rounds it up; casting this value
|
|
back to an OOCreditsQuantity truncates it. Comparing value directly to
|
|
kOOMaxCredits promotes kOOMaxCredits to a double, giving us this
|
|
problem.
|
|
nextafter(kOOMaxCredits, -1) gives us the highest non-truncated
|
|
credits value that's representable as a double (namely,
|
|
18 446 744 073 709 549 568 decicredits, or 2047 less than kOOMaxCredits).
|
|
-- Ahruman 2011-02-27
|
|
*/
|
|
if (doubleDeciCredits > 0)
|
|
{
|
|
doubleDeciCredits = round(doubleDeciCredits);
|
|
double threshold = nextafter(kOOMaxCredits, -1);
|
|
|
|
if (doubleDeciCredits <= threshold)
|
|
{
|
|
return doubleDeciCredits;
|
|
}
|
|
else
|
|
{
|
|
return kOOMaxCredits;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
OOCreditsQuantity OODeciCreditsFromObject(id object)
|
|
{
|
|
if ([object isKindOfClass:[NSNumber class]] && [object oo_isFloatingPointNumber])
|
|
{
|
|
return OODeciCreditsFromDouble([object doubleValue]);
|
|
}
|
|
else
|
|
{
|
|
return OOUnsignedLongLongFromObject(object, 0);
|
|
}
|
|
}
|