LCOV - code coverage report
Current view: top level - Core/Entities - ShipEntityLoadRestore.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 26 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : ShipEntityLoadRestore.m
       4             : 
       5             : 
       6             : Oolite
       7             : Copyright (C) 2004-2013 Giles C Williams and contributors
       8             : 
       9             : This program is free software; you can redistribute it and/or
      10             : modify it under the terms of the GNU General Public License
      11             : as published by the Free Software Foundation; either version 2
      12             : of the License, or (at your option) any later version.
      13             : 
      14             : This program is distributed in the hope that it will be useful,
      15             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             : GNU General Public License for more details.
      18             : 
      19             : You should have received a copy of the GNU General Public License
      20             : along with this program; if not, write to the Free Software
      21             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      22             : MA 02110-1301, USA.
      23             : 
      24             : */
      25             : 
      26             : #import "ShipEntityLoadRestore.h"
      27             : #import "Universe.h"
      28             : 
      29             : #import "OOShipRegistry.h"
      30             : #import "OORoleSet.h"
      31             : #import "OOCollectionExtractors.h"
      32             : #import "OOConstToString.h"
      33             : #import "OOShipGroup.h"
      34             : #import "OOEquipmentType.h"
      35             : #import "AI.h"
      36             : #import "ShipEntityAI.h"
      37             : 
      38             : 
      39           0 : #define KEY_SHIP_KEY                            @"ship_key"
      40           0 : #define KEY_SHIPDATA_OVERRIDES          @"shipdata_overrides"
      41           0 : #define KEY_SHIPDATA_DELETES            @"shipdata_deletes"
      42           0 : #define KEY_PRIMARY_ROLE                        @"primary_role"
      43           0 : #define KEY_POSITION                            @"position"
      44           0 : #define KEY_ORIENTATION                         @"orientation"
      45           0 : #define KEY_ROLES                                       @"roles"
      46           0 : #define KEY_FUEL                                        @"fuel"
      47           0 : #define KEY_BOUNTY                                      @"bounty"
      48           0 : #define KEY_ENERGY_LEVEL                        @"energy_level"
      49           0 : #define KEY_EQUIPMENT                           @"equipment"
      50           0 : #define KEY_MISSILES                            @"missiles"
      51           0 : #define KEY_FORWARD_WEAPON                      @"forward_weapon_type"
      52           0 : #define KEY_AFT_WEAPON                          @"aft_weapon_type"
      53           0 : #define KEY_SCAN_CLASS                          @"scan_class"
      54             : 
      55             : // AI is a complete pickled AI state.
      56           0 : #define KEY_AI                                          @"AI"
      57             : 
      58             : // Group IDs are numbers synchronised through the context object.
      59           0 : #define KEY_GROUP_ID                            @"group"
      60           0 : #define KEY_GROUP_NAME                          @"group_name"
      61           0 : #define KEY_IS_GROUP_LEADER                     @"is_group_leader"
      62           0 : #define KEY_ESCORT_GROUP_ID                     @"escort_group"
      63             : 
      64             : 
      65             : static void StripIgnoredKeys(NSMutableDictionary *dict);
      66             : static NSUInteger GroupIDForGroup(OOShipGroup *group, NSMutableDictionary *context);
      67             : static OOShipGroup *GroupForGroupID(NSUInteger groupID, NSMutableDictionary *context);
      68             : 
      69             : 
      70             : @interface ShipEntity (LoadRestoreInternal)
      71             : 
      72           0 : - (void) simplifyShipdata:(NSMutableDictionary *)data andGetDeletes:(NSArray **)deletes;
      73             : 
      74             : @end
      75             : 
      76             : 
      77             : @implementation ShipEntity (LoadRestore)
      78             : 
      79             : - (NSDictionary *) savedShipDictionaryWithContext:(NSMutableDictionary *)context
      80             : {
      81             :         NSMutableDictionary *result = [NSMutableDictionary dictionary];
      82             :         if (context == nil)  context = [NSMutableDictionary dictionary];
      83             :         
      84             :         [result setObject:_shipKey forKey:KEY_SHIP_KEY];
      85             :         
      86             :         NSMutableDictionary *updatedShipInfo = [NSMutableDictionary dictionaryWithDictionary:shipinfoDictionary];
      87             :         
      88             :         [updatedShipInfo setObject:[[self roleSet] roleString] forKey:KEY_ROLES];
      89             :         [updatedShipInfo oo_setUnsignedInteger:fuel forKey:KEY_FUEL];
      90             :         [updatedShipInfo oo_setUnsignedLongLong:bounty forKey:KEY_BOUNTY];
      91             :         [updatedShipInfo setObject:OOStringFromWeaponType(forward_weapon_type) forKey:KEY_FORWARD_WEAPON];
      92             :         [updatedShipInfo setObject:OOStringFromWeaponType(aft_weapon_type) forKey:KEY_AFT_WEAPON];
      93             :         [updatedShipInfo setObject:OOStringFromScanClass(scanClass) forKey:KEY_SCAN_CLASS];
      94             :         
      95             :         NSArray *deletes = nil;
      96             :         [self simplifyShipdata:updatedShipInfo andGetDeletes:&deletes];
      97             :         
      98             :         [result setObject:updatedShipInfo forKey:KEY_SHIPDATA_OVERRIDES];
      99             :         if (deletes != nil)  [result setObject:deletes forKey:KEY_SHIPDATA_DELETES];
     100             :         
     101             :         if (!HPvector_equal([self position], kZeroHPVector))
     102             :         {
     103             :                 [result oo_setHPVector:[self position] forKey:KEY_POSITION];
     104             :         }
     105             :         if (!quaternion_equal([self normalOrientation], kIdentityQuaternion))
     106             :         {
     107             :                 [result oo_setQuaternion:[self normalOrientation] forKey:KEY_ORIENTATION];
     108             :         }
     109             :         
     110             :         if (energy != maxEnergy)  [result oo_setFloat:energy / maxEnergy forKey:KEY_ENERGY_LEVEL];
     111             :         
     112             :         [result setObject:[self primaryRole] forKey:KEY_PRIMARY_ROLE];
     113             :         
     114             :         // Add equipment.
     115             :         NSArray *equipment = [[self equipmentEnumerator] allObjects];
     116             :         if ([equipment count] != 0)  [result setObject:equipment forKey:KEY_EQUIPMENT];
     117             :         
     118             :         // Add missiles.
     119             :         if (missiles > 0)
     120             :         {
     121             :                 NSMutableArray *missileArray = [NSMutableArray array];
     122             :                 unsigned i;
     123             :                 for (i = 0; i < missiles; i++)
     124             :                 {
     125             :                         NSString *missileType = [missile_list[i] identifier];
     126             :                         if (missileType != nil)  [missileArray addObject:missileType];
     127             :                 }
     128             :                 [result setObject:missileArray forKey:KEY_MISSILES];
     129             :         }
     130             :         
     131             :         // Add groups.
     132             :         if (_group != nil)
     133             :         {
     134             :                 [result oo_setUnsignedInteger:GroupIDForGroup(_group, context) forKey:KEY_GROUP_ID];
     135             :                 if ([_group leader] == self)  [result oo_setBool:YES forKey:KEY_IS_GROUP_LEADER];
     136             :                 NSString *groupName = [_group name];
     137             :                 if (groupName != nil)
     138             :                 {
     139             :                         [result setObject:groupName forKey:KEY_GROUP_NAME];
     140             :                 }
     141             :         }
     142             :         if (_escortGroup != nil)
     143             :         {
     144             :                 [result oo_setUnsignedInteger:GroupIDForGroup(_escortGroup, context) forKey:KEY_ESCORT_GROUP_ID];
     145             :         }
     146             :         /*      Eric: 
     147             :                 The escortGroup property is removed from the lead ship, on entering witchspace.
     148             :                 But it is needed in the save file to correctly restore an escorted group.
     149             :         */
     150             :         else if (_group != nil && [_group leader] == self)
     151             :         {
     152             :                 [result oo_setUnsignedInteger:GroupIDForGroup(_group, context) forKey:KEY_ESCORT_GROUP_ID];
     153             :         }
     154             :         
     155             :         // FIXME: AI.
     156             :         // Eric: I think storing the AI name should be enough. On entering a wormhole, the stack is cleared so there are no preserved AI states.
     157             :         // Also the AI restarts itself with the GLOBAL state, so no need to store any old state. 
     158             :         if ([[[self getAI] name] isEqualToString:@"nullAI.plist"])
     159             :         {
     160             :                 // might be a JS version
     161             :                 [result setObject:[[self getAI] associatedJS] forKey:KEY_AI];
     162             :                 // if there isn't, loading nullAI.js will load nullAI.plist anyway
     163             :         }
     164             :         else
     165             :         {
     166             :                 [result setObject:[[self getAI] name] forKey:KEY_AI];
     167             :         }
     168             :         
     169             :         return result;
     170             : }
     171             : 
     172             : 
     173             : + (id) shipRestoredFromDictionary:(NSDictionary *)dict
     174             :                                           useFallback:(BOOL)fallback
     175             :                                                   context:(NSMutableDictionary *)context
     176             : {
     177             :         if (dict == nil)  return nil;
     178             :         if (context == nil)  context = [NSMutableDictionary dictionary];
     179             :         
     180             :         ShipEntity *ship = nil;
     181             :         
     182             :         NSString *shipKey = [dict oo_stringForKey:KEY_SHIP_KEY];
     183             :         NSDictionary *shipData = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipKey];
     184             :         
     185             :         if (shipData != nil)
     186             :         {
     187             :                 NSMutableDictionary *mergedData = [NSMutableDictionary dictionaryWithDictionary:shipData];
     188             :                 
     189             :                 StripIgnoredKeys(mergedData);
     190             :                 NSArray *deletes = [dict oo_arrayForKey:KEY_SHIPDATA_DELETES];
     191             :                 if (deletes != nil)  [mergedData removeObjectsForKeys:deletes];
     192             :                 [mergedData addEntriesFromDictionary:[dict oo_dictionaryForKey:KEY_SHIPDATA_OVERRIDES]];
     193             :                 [mergedData oo_setBool:NO forKey:@"auto_ai"];
     194             :                 [mergedData oo_setUnsignedInteger:0 forKey:@"escorts"];
     195             :                 
     196             :                 Class shipClass = [UNIVERSE shipClassForShipDictionary:mergedData];
     197             :                 ship = [[[shipClass alloc] initWithKey:shipKey definition:mergedData] autorelease];
     198             :                 
     199             :                 // FIXME: restore AI.
     200             :                 [ship setAITo:[dict oo_stringForKey:KEY_AI defaultValue:@"nullAI.plist"]];
     201             :                 
     202             :                 [ship setPrimaryRole:[dict oo_stringForKey:KEY_PRIMARY_ROLE]];
     203             :         
     204             :         }
     205             :         else
     206             :         {
     207             :                 // Unknown ship; fall back on role if desired and possible.
     208             :                 NSString *shipPrimaryRole = [dict oo_stringForKey:KEY_PRIMARY_ROLE];
     209             :                 if (!fallback || shipPrimaryRole == nil)  return nil;
     210             :                 
     211             :                 ship = [[UNIVERSE newShipWithRole:shipPrimaryRole] autorelease];
     212             :                 if (ship == nil)  return nil;
     213             :         }
     214             :         
     215             :         // The following stuff is deliberately set up the same way even if using role fallback.
     216             :         [ship setPosition:[dict oo_hpvectorForKey:KEY_POSITION]];
     217             :         [ship setNormalOrientation:[dict oo_quaternionForKey:KEY_ORIENTATION]];
     218             :         
     219             :         float energyLevel = [dict oo_floatForKey:KEY_ENERGY_LEVEL defaultValue:1.0f];
     220             :         [ship setEnergy:energyLevel * [ship maxEnergy]];
     221             :         
     222             :         [ship removeAllEquipment];
     223             :         NSEnumerator *eqEnum = nil;
     224             :         NSString *eqKey = nil;
     225             :         for (eqEnum = [[dict oo_arrayForKey:KEY_EQUIPMENT] objectEnumerator]; (eqKey = [eqEnum nextObject]); )
     226             :         {
     227             :                 [ship addEquipmentItem:eqKey withValidation:NO inContext:@"loading"];
     228             :         }
     229             :         
     230             :         [ship removeMissiles];
     231             :         for (eqEnum = [[dict oo_arrayForKey:KEY_MISSILES] objectEnumerator]; (eqKey = [eqEnum nextObject]); )
     232             :         {
     233             :                 [ship addEquipmentItem:eqKey withValidation:NO inContext:@"loading"];
     234             :         }
     235             :         
     236             :         // Groups.
     237             :         NSUInteger groupID = [dict oo_integerForKey:KEY_GROUP_ID defaultValue:NSNotFound];
     238             :         if (groupID != NSNotFound)
     239             :         {
     240             :                 OOShipGroup *group = GroupForGroupID(groupID, context);
     241             :                 [ship setGroup:group];  // Handles adding to group
     242             :                 if ([dict oo_boolForKey:KEY_IS_GROUP_LEADER])  [group setLeader:ship];
     243             :                 NSString *groupName = [dict oo_stringForKey:KEY_GROUP_NAME];
     244             :                 if (groupName != nil)  [group setName:groupName];
     245             :                 if ([ship hasPrimaryRole:@"escort"] && ship != [group leader])
     246             :                 {
     247             :                         [ship setOwner:[group leader]];
     248             :                 }
     249             :         }
     250             :         
     251             :         groupID = [dict oo_integerForKey:KEY_ESCORT_GROUP_ID defaultValue:NSNotFound];
     252             :         if (groupID != NSNotFound)
     253             :         {
     254             :                 OOShipGroup *group = GroupForGroupID(groupID, context);
     255             :                 [group setLeader:ship];
     256             :                 [group setName:@"escort group"];
     257             :                 [ship setEscortGroup:group];
     258             :         }
     259             :         
     260             :         return ship;
     261             : }
     262             : 
     263             : 
     264           0 : - (void) simplifyShipdata:(NSMutableDictionary *)data andGetDeletes:(NSArray **)deletes
     265             : {
     266             :         NSParameterAssert(data != nil && deletes != NULL);
     267             :         *deletes = nil;
     268             :         
     269             :         // Get original ship data.
     270             :         NSMutableDictionary *referenceData = [NSMutableDictionary dictionaryWithDictionary:[[OOShipRegistry sharedRegistry] shipInfoForKey:[self shipDataKey]]];
     271             :         
     272             :         // Discard stuff that we handle separately.
     273             :         StripIgnoredKeys(referenceData);
     274             :         StripIgnoredKeys(data);
     275             :         
     276             :         // Note items that are in referenceData, but not data.
     277             :         NSMutableArray *foundDeletes = [NSMutableArray array];
     278             :         NSEnumerator *enumerator = nil;
     279             :         NSString *key = nil;
     280             :         for (enumerator = [referenceData keyEnumerator]; (key = [enumerator nextObject]); )
     281             :         {
     282             :                 if ([data objectForKey:key] == nil)
     283             :                 {
     284             :                         [foundDeletes addObject:key];
     285             :                 }
     286             :         }
     287             :         if ([foundDeletes count] != 0)  *deletes = foundDeletes;
     288             :         
     289             :         // after rev3010 this loop was using cycles without doing anything - commenting this whole loop out for now. -- kaks 20100207
     290             : /*
     291             :         // Discard anything that hasn't changed.
     292             :         for (enumerator = [data keyEnumerator]; (key = [enumerator nextObject]); )
     293             :         {
     294             :                 id referenceVal = [referenceData objectForKey:key];
     295             :                 id myVal = [data objectForKey:key];
     296             :                 if ([referenceVal isEqual:myVal])
     297             :                 {
     298             :                 //      [data removeObjectForKey:key];
     299             :                 }
     300             :         }
     301             : */
     302             : }
     303             : 
     304             : @end
     305             : 
     306             : 
     307           0 : static void StripIgnoredKeys(NSMutableDictionary *dict)
     308             : {
     309             :         static NSArray *ignoredKeys = nil;
     310             :         if (ignoredKeys == nil)  ignoredKeys = [[NSArray alloc] initWithObjects:@"ai_type", @"has_ecm", @"has_scoop", @"has_escape_pod", @"has_energy_bomb", @"has_fuel_injection", @"has_cloaking_device", @"has_military_jammer", @"has_military_scanner_filter", @"has_shield_booster", @"has_shield_enhancer", @"escorts", @"escort_role", @"escort-ship", @"conditions", @"missiles", @"auto_ai", nil];
     311             :         
     312             :         NSEnumerator *keyEnum = nil;
     313             :         NSString *key = nil;
     314             :         for (keyEnum = [ignoredKeys objectEnumerator]; (key = [keyEnum nextObject]); )
     315             :         {
     316             :                 [dict removeObjectForKey:key];
     317             :         }
     318             : }
     319             : 
     320             : 
     321           0 : static NSUInteger GroupIDForGroup(OOShipGroup *group, NSMutableDictionary *context)
     322             : {
     323             :         NSMutableDictionary *groupIDs = [context objectForKey:@"groupIDs"];
     324             :         if (groupIDs == nil)
     325             :         {
     326             :                 groupIDs = [NSMutableDictionary dictionary];
     327             :                 [context setObject:groupIDs forKey:@"groupIDs"];
     328             :         }
     329             :         
     330             :         NSValue *key = [NSValue valueWithNonretainedObject:group];
     331             :         NSNumber *groupIDObj = [groupIDs objectForKey:key];
     332             :         unsigned groupID;
     333             :         if (groupIDObj == nil)
     334             :         {
     335             :                 // Assign a new group ID.
     336             :                 groupID = [context oo_unsignedIntForKey:@"nextGroupID"];
     337             :                 groupIDObj = [NSNumber numberWithUnsignedInt:groupID];
     338             :                 [context oo_setUnsignedInteger:groupID + 1 forKey:@"nextGroupID"];
     339             :                 [groupIDs setObject:groupIDObj forKey:key];
     340             :                 
     341             :                 /*      Also keep references to the groups. This isn't necessary at the
     342             :                         time of writing, but would be if we e.g. switched to pickling
     343             :                         ships in wormholes all the time (each wormhole would then need a
     344             :                         persistent context). We can't simply use the groups instead of
     345             :                         NSValues as keys, becuase dictionary keys must be copyable.
     346             :                 */
     347             :                 NSMutableSet *groups = [context objectForKey:@"groups"];
     348             :                 if (groups == nil)
     349             :                 {
     350             :                         groups = [NSMutableSet set];
     351             :                         [context setObject:groups forKey:@"groups"];
     352             :                 }
     353             :                 [groups addObject:group];
     354             :         }
     355             :         else
     356             :         {
     357             :                 groupID = [groupIDObj unsignedIntValue];
     358             :         }
     359             : 
     360             :         
     361             :         return groupID;
     362             : }
     363             : 
     364             : 
     365           0 : static OOShipGroup *GroupForGroupID(NSUInteger groupID, NSMutableDictionary *context)
     366             : {
     367             :         NSNumber *key = [NSNumber numberWithUnsignedInteger:groupID];
     368             :         
     369             :         NSMutableDictionary *groups = [context objectForKey:@"groupsByID"];
     370             :         if (groups == nil)
     371             :         {
     372             :                 groups = [NSMutableDictionary dictionary];
     373             :                 [context setObject:groups forKey:@"groupsByID"];
     374             :         }
     375             :         
     376             :         OOShipGroup *group = [groups objectForKey:key];
     377             :         if (group == nil)
     378             :         {
     379             :                 group = [[[OOShipGroup alloc] init] autorelease];
     380             :                 [groups setObject:group forKey:key];
     381             :         }
     382             :         
     383             :         return group;
     384             : }

Generated by: LCOV version 1.14