Oolite 1.91.0.7604-240417-a536cbe
Loading...
Searching...
No Matches
OOShipRegistry.m
Go to the documentation of this file.
1/*
2
3OOShipRegistry.m
4
5
6Copyright (C) 2008-2013 Jens Ayton and contributors
7
8Permission is hereby granted, free of charge, to any person obtaining a copy
9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
14
15The above copyright notice and this permission notice shall be included in all
16copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24SOFTWARE.
25
26*/
27
28#import "OOShipRegistry.h"
29#import "OOCacheManager.h"
30#import "ResourceManager.h"
33#import "OOProbabilitySet.h"
34#import "OORoleSet.h"
35#import "OOStringParsing.h"
36#import "OOMesh.h"
37#import "GameController.h"
39#import "OODeepCopy.h"
40#import "OOColor.h"
41#import "OOStringExpander.h"
43#import "Universe.h"
44#import "OOJSScript.h"
45
46#import "OODebugStandards.h"
47
48#define PRELOAD 0
49
50
51static void DumpStringAddrs(NSDictionary *dict, NSString *context);
52static NSComparisonResult SortDemoShipsByName (id a, id b, void* context);
53static NSComparisonResult SortDemoCategoriesByName (id a, id b, void* context);
54
55
57
58
59static NSString * const kShipRegistryCacheName = @"ship registry";
60static NSString * const kShipDataCacheKey = @"ship data";
61static NSString * const kPlayerShipsCacheKey = @"player ships";
62static NSString * const kRoleWeightsCacheKey = @"role weights";
63static NSString * const kDefaultDemoShip = @"coriolis-station";
64static NSString * const kVisualEffectRegistryCacheName = @"visual effect registry";
65static NSString * const kVisualEffectDataCacheKey = @"visual effect data";
66
67
68@interface OOShipRegistry (OODataLoader)
69
70- (void) loadShipData;
72- (void) loadDemoShips;
75
76- (BOOL) applyLikeShips:(NSMutableDictionary *)ioData withKey:(NSString *)likeKey;
77- (BOOL) loadAndMergeShipyard:(NSMutableDictionary *)ioData;
78- (BOOL) stripPrivateKeys:(NSMutableDictionary *)ioData;
79- (BOOL) makeShipEntriesMutable:(NSMutableDictionary *)ioData;
80- (BOOL) loadAndApplyShipDataOverrides:(NSMutableDictionary *)ioData;
81- (BOOL) canonicalizeAndTagSubentities:(NSMutableDictionary *)ioData;
82- (BOOL) removeUnusableEntries:(NSMutableDictionary *)ioData shipMode:(BOOL)shipMode;
83- (BOOL) sanitizeConditions:(NSMutableDictionary *)ioData;
84
85#if PRELOAD
86- (BOOL) preloadShipMeshes:(NSMutableDictionary *)ioData;
87#endif
88
89- (NSMutableDictionary *) mergeShip:(NSDictionary *)child withParent:(NSDictionary *)parent;
90- (void) mergeShipRoles:(NSString *)roles forShipKey:(NSString *)shipKey intoProbabilityMap:(NSMutableDictionary *)probabilitySets;
91
92- (NSDictionary *) canonicalizeSubentityDeclaration:(id)declaration
93 forShip:(NSString *)shipKey
94 shipData:(NSDictionary *)shipData
95 fatalError:(BOOL *)outFatalError;
96- (NSDictionary *) translateOldStyleSubentityDeclaration:(NSString *)declaration
97 forShip:(NSString *)shipKey
98 shipData:(NSDictionary *)shipData
99 fatalError:(BOOL *)outFatalError;
100- (NSDictionary *) translateOldStyleFlasherDeclaration:(NSArray *)tokens
101 forShip:(NSString *)shipKey
102 fatalError:(BOOL *)outFatalError;
103- (NSDictionary *) translateOldStandardBasicSubentityDeclaration:(NSArray *)tokens
104 forShip:(NSString *)shipKey
105 shipData:(NSDictionary *)shipData
106 fatalError:(BOOL *)outFatalError;
107- (NSDictionary *) validateNewStyleSubentityDeclaration:(NSDictionary *)declaration
108 forShip:(NSString *)shipKey
109 fatalError:(BOOL *)outFatalError;
110- (NSDictionary *) validateNewStyleFlasherDeclaration:(NSDictionary *)declaration
111 forShip:(NSString *)shipKey
112 fatalError:(BOOL *)outFatalError;
113- (NSDictionary *) validateNewStyleStandardSubentityDeclaration:(NSDictionary *)declaration
114 forShip:(NSString *)shipKey
115 fatalError:(BOOL *)outFatalError;
116
117- (BOOL) shipIsBallTurretForKey:(NSString *)shipKey inShipData:(NSDictionary *)shipData;
118
119@end
120
121
122@implementation OOShipRegistry
123
124+ (OOShipRegistry *) sharedRegistry
125{
126 if (sSingleton == nil)
127 {
128 sSingleton = [[self alloc] init];
129 }
130
131 return sSingleton;
132}
133
134
135+ (void) reload
136{
137 if (sSingleton != nil)
138 {
139 /* CIM: 'release' doesn't work - the class definition
140 * overrides it, so this leaks memory. Needs a proper reset
141 * method for reloading the ship registry data instead */
142 [sSingleton release];
143 sSingleton = nil;
144
145 (void) [self sharedRegistry];
146 }
147}
148
149
150- (id) init
151{
152 if ((self = [super init]))
153 {
154 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
156
157 _shipData = [[cache objectForKey:kShipDataCacheKey inCache:kShipRegistryCacheName] retain];
158 _playerShips = [[cache objectForKey:kPlayerShipsCacheKey inCache:kShipRegistryCacheName] retain];
159 _effectData = [[cache objectForKey:kVisualEffectDataCacheKey inCache:kVisualEffectRegistryCacheName] retain];
160 if ([_shipData count] == 0) // Don't accept nil or empty
161 {
162 [self loadShipData];
163 if ([_shipData count] == 0)
164 {
165 [NSException raise:@"OOShipRegistryLoadFailure" format:@"Could not load any ship data."];
166 }
167 if ([_playerShips count] == 0)
168 {
169 [NSException raise:@"OOShipRegistryLoadFailure" format:@"Could not load any player ships."];
170 }
171 }
172
173 [self loadDemoShipConditions];
174 [self loadDemoShips]; // testing only
175 if ([_demoShips count] == 0)
176 {
177 [NSException raise:@"OOShipRegistryLoadFailure" format:@"Could not load or synthesize any demo ships."];
178 }
179
180 [self loadCachedRoleProbabilitySets];
181 if (_probabilitySets == nil)
182 {
183 [self buildRoleProbabilitySets];
184 if ([_probabilitySets count] == 0)
185 {
186 [NSException raise:@"OOShipRegistryLoadFailure" format:@"Could not load or synthesize role probability sets."];
187 }
188 }
189
190 [pool release];
191 }
192 return self;
193}
194
195
196- (void) dealloc
197{
198 [_shipData release];
199 [_demoShips release];
200 [_playerShips release];
201 [_probabilitySets release];
202
203 [super dealloc];
204}
205
206
207- (NSDictionary *) shipInfoForKey:(NSString *)key
208{
209 return [_shipData objectForKey:key];
210}
211
212
213- (void) setShipInfoForKey:(NSString *)key with:(NSDictionary *)newShipData
214{
215 NSMutableDictionary *mutableDict = [NSMutableDictionary dictionaryWithDictionary:_shipData];
216 [mutableDict setObject:OODeepCopy(newShipData) forKey:key];
217 DESTROY(_shipData);
218 _shipData = [[NSDictionary dictionaryWithDictionary:mutableDict] retain];
219}
220
221
222- (NSDictionary *) effectInfoForKey:(NSString *)key
223{
224 return [_effectData objectForKey:key];
225}
226
227
228- (NSDictionary *) shipyardInfoForKey:(NSString *)key
229{
230 return [[self shipInfoForKey:key] objectForKey:@"_oo_shipyard"];
231}
232
233
234- (OOProbabilitySet *) probabilitySetForRole:(NSString *)role
235{
236 if (role == nil) return nil;
237 return [_probabilitySets objectForKey:role];
238}
239
240
241- (NSArray *) demoShipKeys
242{
243 // with condition scripts in use, can't cache this value
244 [self loadDemoShips];
245
246 return [[_demoShips copy] autorelease];
247}
248
249
250- (NSArray *) playerShipKeys
251{
252 return _playerShips;
253}
254
255@end
256
257
258@implementation OOShipRegistry (OOConveniences)
259
260- (NSArray *) shipKeys
261{
262 return [_shipData allKeys];
263}
264
265- (NSArray *) shipRoles
266{
267 return [_probabilitySets allKeys];
268}
269
270- (NSArray *) shipKeysWithRole:(NSString *)role
271{
272 return [[self probabilitySetForRole:role] allObjects];
273}
274
275
276- (NSString *) randomShipKeyForRole:(NSString *)role
277{
278 return [[self probabilitySetForRole:role] randomObject];
279}
280
281@end
282
283
284@implementation OOShipRegistry (OODataLoader)
285
286/* -loadShipData
287
288 Load the data for all ships. This consists of five stages:
289 * Load merges shipdata.plist dictionary.
290 * Apply all like_ship entries.
291 * Load shipdata-overrides.plist and apply patches.
292 * Load shipyard.plist, add shipyard data into ship dictionaries, and
293 create _playerShips array.
294 * Build role->ship type probability sets.
295*/
296- (void) loadShipData
297{
298 NSMutableDictionary *result = nil;
299
300 [_shipData release];
301 _shipData = nil;
302 [_playerShips release];
303 _playerShips = nil;
304
305 // Load shipdata.plist.
306 result = [[[ResourceManager dictionaryFromFilesNamed:@"shipdata.plist"
307 inFolder:@"Config"
308 mergeMode:MERGE_BASIC
309 cache:NO] mutableCopy] autorelease];
310 if (result == nil) return;
311
312 DumpStringAddrs(result, @"shipdata.plist");
313
314 // Make each entry mutable to simplify later stages. Also removes any entries that aren't dictionaries.
315 if (![self makeShipEntriesMutable:result]) return;
316 OOLog(@"shipData.load.progress", @"%@", @"Finished initial cleanup...");
317
318 // Apply patches.
319 if (![self loadAndApplyShipDataOverrides:result]) return;
320 OOLog(@"shipData.load.progress", @"%@", @"Finished applying patches...");
321
322 // Strip private keys (anything starting with _oo_).
323 if (![self stripPrivateKeys:result]) return;
324 OOLog(@"shipData.load.progress", @"%@", @"Finished stripping private keys...");
325
326 // Resolve like_ship entries.
327 if (![self applyLikeShips:result withKey:@"like_ship"]) return;
328 OOLog(@"shipData.load.progress", @"%@", @"Finished resolving like_ships...");
329
330 // Clean up subentity declarations and tag subentities so they won't be pruned.
331 if (![self canonicalizeAndTagSubentities:result]) return;
332 OOLog(@"shipData.load.progress", @"%@", @"Finished cleaning up subentities...");
333
334 // Clean out templates and invalid entries.
335 if (![self removeUnusableEntries:result shipMode:YES]) return;
336 OOLog(@"shipData.load.progress", @"%@", @"Finished removing invalid entries...");
337
338 // Add shipyard entries into shipdata entries.
339 if (![self loadAndMergeShipyard:result]) return;
340 OOLog(@"shipData.load.progress", @"%@", @"Finished adding shipyard entries...");
341
342 // Sanitize conditions.
343 if (![self sanitizeConditions:result]) return;
344 OOLog(@"shipData.load.progress", @"%@", @"Finished validating data...");
345
346#if PRELOAD
347 // Preload and cache meshes.
348 if (![self preloadShipMeshes:result]) return;
349 OOLog(@"shipData.load.progress", @"%@", @"Finished loading meshes...");
350#endif
351
352 _shipData = OODeepCopy(result);
353 [[OOCacheManager sharedCache] setObject:_shipData forKey:kShipDataCacheKey inCache:kShipRegistryCacheName];
354
355 OOLog(@"shipData.load.done", @"%@", @"Ship data loaded.");
356
357 [_effectData release];
358 _effectData = nil;
359
360 result = [[[ResourceManager dictionaryFromFilesNamed:@"effectdata.plist"
361 inFolder:@"Config"
362 mergeMode:MERGE_BASIC
363 cache:NO] mutableCopy] autorelease];
364 if (result == nil) return;
365
366 // Make each entry mutable to simplify later stages. Also removes any entries that aren't dictionaries.
367 if (![self makeShipEntriesMutable:result]) return;
368 OOLog(@"effectData.load.progress", @"%@", @"Finished initial cleanup...");
369
370 // Strip private keys (anything starting with _oo_).
371 if (![self stripPrivateKeys:result]) return;
372 OOLog(@"effectData.load.progress", @"%@", @"Finished stripping private keys...");
373
374 // Resolve like_effect entries.
375 if (![self applyLikeShips:result withKey:@"like_effect"]) return;
376 OOLog(@"effectData.load.progress", @"%@", @"Finished resolving like_effects...");
377
378 // Clean up subentity declarations and tag subentities so they won't be pruned.
379 if (![self canonicalizeAndTagSubentities:result]) return;
380 OOLog(@"effectData.load.progress", @"%@", @"Finished cleaning up subentities...");
381
382 // Clean out templates and invalid entries.
383 if (![self removeUnusableEntries:result shipMode:NO]) return;
384 OOLog(@"effectData.load.progress", @"%@", @"Finished removing invalid entries...");
385
386 _effectData = OODeepCopy(result);
387 [[OOCacheManager sharedCache] setObject:_effectData forKey:kVisualEffectDataCacheKey inCache:kVisualEffectRegistryCacheName];
388
389 OOLog(@"effectData.load.done", @"%@", @"Effect data loaded.");
390}
391
392
393- (void) loadDemoShipConditions
394{
395 NSMutableArray *conditionScripts = [[NSMutableArray alloc] init];
396 NSEnumerator *enumerator = nil;
397 NSDictionary *key = nil;
398 NSArray *initialDemoShips = nil;
399
400 initialDemoShips = [ResourceManager arrayFromFilesNamed:@"shiplibrary.plist"
401 inFolder:@"Config"
402 andMerge:YES
403 cache:NO];
404
405 for (enumerator = [initialDemoShips objectEnumerator]; (key = [enumerator nextObject]); )
406 {
407 NSString *conditions = [key oo_stringForKey:kOODemoShipConditions defaultValue:nil];
408 if (conditions != nil)
409 {
410 [conditionScripts addObject:conditions];
411 }
412 }
413
414 [[OOCacheManager sharedCache] setObject:conditionScripts forKey:@"demoship conditions" inCache:@"condition scripts"];
415 [conditionScripts release];
416}
417
418
419/* -loadDemoShips
420
421 Load demoships.plist, and filter out non-existent ships. If no existing
422 ships remain, try adding coriolis; if this fails, add any ship in
423 shipdata.
424*/
425- (void) loadDemoShips
426{
427 NSEnumerator *enumerator = nil;
428 NSDictionary *key = nil;
429 NSArray *initialDemoShips = nil;
430 NSMutableArray *demoShips = nil;
431
432 DESTROY(_demoShips);
433
434 initialDemoShips = [ResourceManager arrayFromFilesNamed:@"shiplibrary.plist"
435 inFolder:@"Config"
436 andMerge:YES
437 cache:NO];
438 demoShips = [NSMutableArray arrayWithArray:initialDemoShips];
439
440 // Note: iterate over initialDemoShips to avoid mutating the collection being enu,erated.
441 for (enumerator = [initialDemoShips objectEnumerator]; (key = [enumerator nextObject]); )
442 {
443 NSString *shipKey = [key oo_stringForKey:kOODemoShipKey];
444 if (![key isKindOfClass:[NSDictionary class]] || [self shipInfoForKey:shipKey] == nil)
445 {
446 [demoShips removeObject:key];
447 }
448 else
449 {
450 NSString *conditions = [key oo_stringForKey:kOODemoShipConditions defaultValue:nil];
451 if (conditions != nil)
452 {
453 if ([PLAYER status] == STATUS_START_GAME)
454 {
455 // conditions always false here
456 [demoShips removeObject:key];
457 }
458 else
459 {
460 OOJSScript *condScript = [UNIVERSE getConditionScript:conditions];
461 if (condScript != nil) // should always be non-nil, but just in case
462 {
463 JSContext *context = OOJSAcquireContext();
464 BOOL OK;
465 JSBool allow_use;
466 jsval result;
467 jsval args[] = { OOJSValueFromNativeObject(context, shipKey) };
468
469 OK = [condScript callMethod:OOJSID("allowShowLibraryShip")
470 inContext:context
471 withArguments:args count:sizeof args / sizeof *args
472 result:&result];
473
474 if (OK) OK = JS_ValueToBoolean(context, result, &allow_use);
475
476 OOJSRelinquishContext(context);
477 if (OK && !allow_use)
478 {
479 /* if the script exists, the function exists, the function
480 * returns a bool, and that bool is false, hide the
481 * ship. Otherwise allow it as default */
482 [demoShips removeObject:key];
483 }
484 }
485 }
486 }
487 }
488 }
489
490 if ([demoShips count] == 0)
491 {
492 NSString *shipKey = nil;
493 if ([self shipInfoForKey:kDefaultDemoShip] != nil)
494 {
495 shipKey = kDefaultDemoShip;
496 }
497 else
498 {
499 shipKey = [[_shipData allKeys] objectAtIndex:0];
500 }
501 [demoShips addObject:[NSDictionary dictionaryWithObject:shipKey forKey:kOODemoShipKey]];
502 }
503
504 // now separate out the demoships by class, and add some extra keys
505 NSMutableDictionary *demoList = [NSMutableDictionary dictionaryWithCapacity:8];
506 NSMutableArray *demoClass = nil;
507 foreach(key, demoShips)
508 {
509 NSString *class = [key oo_stringForKey:kOODemoShipClass defaultValue:@"ship"];
510 if ([OOShipLibraryCategoryPlural(class) length] == 0)
511 {
512 OOLog(@"shipdata.load.warning",@"Unexpected class '%@' in shiplibrary.plist for '%@'",class,[key oo_stringForKey:kOODemoShipKey]);
513 class = @"ship";
514 }
515 demoClass = [demoList objectForKey:class];
516 if (demoClass == nil)
517 {
518 [demoList setObject:[NSMutableArray array] forKey:class];
519 demoClass = [demoList objectForKey:class];
520 }
521 NSMutableDictionary *demoEntry = [NSMutableDictionary dictionaryWithDictionary:key];
522 // add "name" object to dictionary from ship definition
523 [demoEntry setObject:[[self shipInfoForKey:[demoEntry oo_stringForKey:@"ship"]] oo_stringForKey:kOODemoShipName] forKey:kOODemoShipName];
524 // set "class" object to standard ship if not otherwise set
525 if (![[demoEntry oo_stringForKey:kOODemoShipClass defaultValue:nil] isEqualToString:class])
526 {
527 [demoEntry setObject:class forKey:kOODemoShipClass];
528 }
529 [demoClass addObject:demoEntry];
530 }
531 // sort each ship list by name
532 NSString *demoClassName = nil;
533 foreach(demoClassName, demoList)
534 {
535 [[demoList objectForKey:demoClassName] sortUsingFunction:SortDemoShipsByName context:NULL];
536 }
537
538 // and then sort the ship list list by class name
539 _demoShips = [[[demoList allValues] sortedArrayUsingFunction:SortDemoCategoriesByName context:NULL] retain];
540}
541
542
543- (void) loadCachedRoleProbabilitySets
544{
545 NSDictionary *cachedSets = nil;
546 NSMutableDictionary *restoredSets = nil;
547 NSEnumerator *roleEnum = nil;
548 NSString *role = nil;
549
550 cachedSets = [[OOCacheManager sharedCache] objectForKey:kRoleWeightsCacheKey inCache:kShipRegistryCacheName];
551 if (cachedSets == nil) return;
552
553 restoredSets = [NSMutableDictionary dictionaryWithCapacity:[cachedSets count]];
554 for (roleEnum = [cachedSets keyEnumerator]; (role = [roleEnum nextObject]); )
555 {
556 [restoredSets setObject:[OOProbabilitySet probabilitySetWithPropertyListRepresentation:[cachedSets objectForKey:role]] forKey:role];
557 }
558
559 _probabilitySets = [restoredSets copy];
560}
561
562
563- (void) buildRoleProbabilitySets
564{
565 NSMutableDictionary *probabilitySets = nil;
566 NSEnumerator *shipEnum = nil;
567 NSString *shipKey = nil;
568 NSDictionary *shipEntry = nil;
569 NSString *roles = nil;
570 NSEnumerator *roleEnum = nil;
571 NSString *role = nil;
572 OOProbabilitySet *pset = nil;
573 NSMutableDictionary *cacheEntry = nil;
574
575 probabilitySets = [NSMutableDictionary dictionary];
576
577 // Build role sets
578 for (shipEnum = [_shipData keyEnumerator]; (shipKey = [shipEnum nextObject]); )
579 {
580 shipEntry = [_shipData objectForKey:shipKey];
581 roles = [shipEntry oo_stringForKey:@"roles"];
582 [self mergeShipRoles:roles forShipKey:shipKey intoProbabilityMap:probabilitySets];
583 }
584
585 // Convert role sets to immutable form, and build cache entry.
586 // Note: we iterate over a copy of the keys to avoid mutating while iterating.
587 cacheEntry = [NSMutableDictionary dictionaryWithCapacity:[probabilitySets count]];
588 for (roleEnum = [[probabilitySets allKeys] objectEnumerator]; (role = [roleEnum nextObject]); )
589 {
590 pset = [probabilitySets objectForKey:role];
591 pset = [[pset copy] autorelease];
592 [probabilitySets setObject:pset forKey:role];
593 [cacheEntry setObject:[pset propertyListRepresentation] forKey:role];
594 }
595
596 _probabilitySets = [probabilitySets copy];
597 [[OOCacheManager sharedCache] setObject:cacheEntry forKey:kRoleWeightsCacheKey inCache:kShipRegistryCacheName];
598}
599
600
601/* -applyLikeShips:
602
603 Implement like_ship by copying inherited ship and overwriting with child
604 ship values. Done iteratively to report recursive references of arbitrary
605 depth. Also removes and reports ships whose like_ship entry does not
606 resolve, and handles reference loops by removing all ships involved.
607
608 We start with a set of keys all ships that have a like_ships entry. In
609 each iteration, every ship whose like_ship entry does not refer to a ship
610 which itself has a like_ship entry is finalized. If the set of pending
611 ships does not shrink in an iteration, the remaining ships cannot be
612 resolved (either their like_ships do not exist, or they form reference
613 cycles) so we stop looping and report it.
614*/
615- (BOOL) applyLikeShips:(NSMutableDictionary *)ioData withKey:(NSString *)likeKey
616{
617 NSMutableSet *remainingLikeShips = nil;
618 NSEnumerator *enumerator = nil;
619 NSString *key = nil;
620 NSString *parentKey = nil;
621 NSDictionary *shipEntry = nil;
622 NSDictionary *parentEntry = nil;
623 NSUInteger count, lastCount;
624 NSMutableArray *reportedBadShips = nil;
625
626 // Build set of ships with like_ship references
627 remainingLikeShips = [NSMutableSet set];
628 for (enumerator = [ioData keyEnumerator]; (key = [enumerator nextObject]); )
629 {
630 shipEntry = [ioData objectForKey:key];
631 if ([shipEntry oo_stringForKey:likeKey] != nil)
632 {
633 [remainingLikeShips addObject:key];
634 }
635 }
636
637 count = lastCount = [remainingLikeShips count];
638 while (count != 0)
639 {
640 for (enumerator = [[[remainingLikeShips copy] autorelease] objectEnumerator]; (key = [enumerator nextObject]); )
641 {
642 // Look up like_ship entry
643 shipEntry = [ioData objectForKey:key];
644 parentKey = [shipEntry objectForKey:likeKey];
645 if (![remainingLikeShips containsObject:parentKey])
646 {
647 // If parent is fully resolved, we can resolve this child.
648 parentEntry = [ioData objectForKey:parentKey];
649 shipEntry = [self mergeShip:shipEntry withParent:parentEntry];
650 if (shipEntry != nil)
651 {
652 [remainingLikeShips removeObject:key];
653 [ioData setObject:shipEntry forKey:key];
654 }
655 }
656 }
657
658 count = [remainingLikeShips count];
659 if (count == lastCount)
660 {
661 /* Fail: we couldn't resolve all like_ship entries.
662 Remove unresolved entries, building a list of the ones that
663 don't have is_external_dependency set.
664 */
665 reportedBadShips = [NSMutableArray array];
666 for (enumerator = [remainingLikeShips objectEnumerator]; (key = [enumerator nextObject]); )
667 {
668 if (![[ioData oo_dictionaryForKey:key] oo_boolForKey:@"is_external_dependency"])
669 {
670 [reportedBadShips addObject:key];
671 }
672 [ioData removeObjectForKey:key];
673 }
674
675 if ([reportedBadShips count] != 0)
676 {
677 [reportedBadShips sortUsingSelector:@selector(caseInsensitiveCompare:)];
678 OOLogERR(@"shipData.merge.failed", @"one or more shipdata.plist entries have %@ references that cannot be resolved: %@", likeKey, [reportedBadShips componentsJoinedByString:@", "]); // FIXME: distinguish shipdata and effectdata
679 OOStandardsError(@"Likely missing a dependency in a manifest.plist");
680 }
681 break;
682 }
683 lastCount = count;
684 }
685
686 return YES;
687}
688
689
690- (NSMutableDictionary *) mergeShip:(NSDictionary *)child withParent:(NSDictionary *)parent
691{
692 NSMutableDictionary *result = [[parent mutableCopy] autorelease];
693 if (result == nil) return nil;
694
695 [result addEntriesFromDictionary:child];
696 [result removeObjectForKey:@"like_ship"];
697
698 // Certain properties cannot be inherited.
699 if ([child oo_stringForKey:@"display_name"] == nil) [result removeObjectForKey:@"display_name"];
700 if ([child oo_stringForKey:@"is_template"] == nil) [result removeObjectForKey:@"is_template"];
701
702 // Since both 'scanClass' and 'scan_class' are accepted as valid keys for the scanClass property,
703 // we may end up with conflicting scanClass and scan_class keys from like_ship relationships getting
704 // merged in the result dictionary. We want to always have the child overriding the parent setting
705 // and we do that by determining which of the two keys belongs to the child dictionary and removing
706 // the other one from the result - Nikos 20100512
707 if ([result oo_stringForKey:@"scan_class"] != nil && [result oo_stringForKey:@"scanClass"] != nil)
708 {
709 if ([child oo_stringForKey:@"scanClass"] != nil)
710 [result removeObjectForKey:@"scan_class"];
711 else
712 [result removeObjectForKey:@"scanClass"];
713 }
714 // TODO: all normalised/non-normalised value name pairs need to be catered for. - Kaks 2010-05-13
715 if ([result oo_stringForKey:@"escort_role"] != nil && [result oo_stringForKey:@"escort-role"] != nil)
716 {
717 if ([child oo_stringForKey:@"escort-role"] != nil)
718 [result removeObjectForKey:@"escort_role"];
719 else
720 [result removeObjectForKey:@"escort-role"];
721 }
722 if ([result oo_stringForKey:@"escort_ship"] != nil && [result oo_stringForKey:@"escort-ship"] != nil)
723 {
724 if ([child oo_stringForKey:@"escort-ship"] != nil)
725 [result removeObjectForKey:@"escort_ship"];
726 else
727 [result removeObjectForKey:@"escort-ship"];
728 }
729 if ([result oo_stringForKey:@"is_carrier"] != nil && [result oo_stringForKey:@"isCarrier"] != nil)
730 {
731 if ([child oo_stringForKey:@"isCarrier"] != nil)
732 [result removeObjectForKey:@"is_carrier"];
733 else
734 [result removeObjectForKey:@"isCarrier"];
735 }
736 if ([result oo_stringForKey:@"has_shipyard"] != nil && [result oo_stringForKey:@"hasShipyard"] != nil)
737 {
738 if ([child oo_stringForKey:@"hasShipyard"] != nil)
739 [result removeObjectForKey:@"has_shipyard"];
740 else
741 [result removeObjectForKey:@"hasShipyard"];
742 }
743 return result;
744}
745
746
747- (BOOL) makeShipEntriesMutable:(NSMutableDictionary *)ioData
748{
749 NSEnumerator *shipKeyEnum = nil;
750 NSString *shipKey = nil;
751 NSDictionary *shipEntry = nil;
752
753 for (shipKeyEnum = [[ioData allKeys] objectEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
754 {
755 shipEntry = [ioData objectForKey:shipKey];
756 if (![shipEntry isKindOfClass:[NSDictionary class]])
757 {
758 OOLogERR(@"shipData.load.badEntry", @"the shipdata.plist entry \"%@\" is not a dictionary.", shipKey);
759 [ioData removeObjectForKey:shipKey];
760 }
761 else
762 {
763 shipEntry = [shipEntry mutableCopy];
764
765 [ioData setObject:shipEntry forKey:shipKey];
766 [shipEntry release];
767 }
768 }
769
770 return YES;
771}
772
773
774- (BOOL) loadAndApplyShipDataOverrides:(NSMutableDictionary *)ioData
775{
776 NSEnumerator *shipKeyEnum = nil;
777 NSString *shipKey = nil;
778 NSMutableDictionary *shipEntry = nil;
779 NSDictionary *overrides = nil;
780 NSDictionary *overridesEntry = nil;
781
782 overrides = [ResourceManager dictionaryFromFilesNamed:@"shipdata-overrides.plist"
783 inFolder:@"Config"
784 mergeMode:MERGE_SMART
785 cache:NO];
786
787 for (shipKeyEnum = [overrides keyEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
788 {
789 shipEntry = [ioData objectForKey:shipKey];
790 if (shipEntry != nil)
791 {
792 overridesEntry = [overrides objectForKey:shipKey];
793 if (![overridesEntry isKindOfClass:[NSDictionary class]])
794 {
795 OOLogERR(@"shipData.load.error", @"the shipdata-overrides.plist entry \"%@\" is not a dictionary.", shipKey);
796 }
797 else
798 {
799 [shipEntry addEntriesFromDictionary:overridesEntry];
800 }
801 }
802 }
803
804 return YES;
805}
806
807
808- (BOOL) stripPrivateKeys:(NSMutableDictionary *)ioData
809{
810 NSEnumerator *shipKeyEnum = nil;
811 NSString *shipKey = nil;
812 NSMutableDictionary *shipEntry = nil;
813 NSEnumerator *attrKeyEnum = nil;
814 NSString *attrKey = nil;
815
816 for (shipKeyEnum = [ioData keyEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
817 {
818 shipEntry = [ioData objectForKey:shipKey];
819
820 for (attrKeyEnum = [shipEntry keyEnumerator]; (attrKey = [attrKeyEnum nextObject]); )
821 {
822 if ([attrKey hasPrefix:@"_oo_"])
823 {
824 [shipEntry removeObjectForKey:attrKey];
825 }
826 }
827 }
828
829 return YES;
830}
831
832
833/* -loadAndMergeShipyard:
834
835 Load shipyard.plist, add its entries to appropriate shipyard entries as
836 a dictionary under the key "shipyard", and build list of player ships.
837 Before that, we strip out any "shipyard" entries already in shipdata, and
838 apply any shipyard-overrides.plist stuff to shipyard.
839*/
840- (BOOL) loadAndMergeShipyard:(NSMutableDictionary *)ioData
841{
842 NSEnumerator *shipKeyEnum = nil;
843 NSString *shipKey = nil;
844 NSMutableDictionary *shipEntry = nil;
845 NSDictionary *shipyard = nil;
846 NSDictionary *shipyardOverrides = nil;
847 NSDictionary *shipyardEntry = nil;
848 NSDictionary *shipyardOverridesEntry = nil;
849 NSMutableArray *playerShips = nil;
850
851 // Strip out any shipyard stuff in shipdata (there shouldn't be any).
852 for (shipKeyEnum = [ioData keyEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
853 {
854 shipEntry = [ioData objectForKey:shipKey];
855 if ([shipEntry objectForKey:@"_oo_shipyard"] != nil)
856 {
857 [shipEntry removeObjectForKey:@"_oo_shipyard"];
858 }
859 }
860
861 shipyard = [ResourceManager dictionaryFromFilesNamed:@"shipyard.plist"
862 inFolder:@"Config"
863 mergeMode:MERGE_BASIC
864 cache:NO];
865 shipyardOverrides = [ResourceManager dictionaryFromFilesNamed:@"shipyard-overrides.plist"
866 inFolder:@"Config"
867 mergeMode:MERGE_SMART
868 cache:NO];
869
870 playerShips = [NSMutableArray arrayWithCapacity:[shipyard count]];
871
872 // Insert merged shipyard and shipyardOverrides entries.
873 for (shipKeyEnum = [shipyard keyEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
874 {
875 shipEntry = [ioData objectForKey:shipKey];
876 if (shipEntry != nil)
877 {
878 shipyardEntry = [shipyard objectForKey:shipKey];
879 shipyardOverridesEntry = [shipyardOverrides objectForKey:shipKey];
880 shipyardEntry = [shipyardEntry dictionaryByAddingEntriesFromDictionary:shipyardOverridesEntry];
881
882 [shipEntry setObject:shipyardEntry forKey:@"_oo_shipyard"];
883
884 [playerShips addObject:shipKey];
885 }
886 else
887 {
888 OOLogWARN(@"shipData.load.shipyard.unknown", @"the shipyard.plist entry \"%@\" does not have a corresponding shipdata.plist entry, ignoring.", shipKey);
889 }
890 }
891
892 _playerShips = [playerShips copy];
893 [[OOCacheManager sharedCache] setObject:_playerShips forKey:kPlayerShipsCacheKey inCache:kShipRegistryCacheName];
894
895 return YES;
896}
897
898
899- (BOOL) canonicalizeAndTagSubentities:(NSMutableDictionary *)ioData
900{
901 NSEnumerator *shipKeyEnum = nil;
902 NSString *shipKey = nil;
903 NSMutableDictionary *shipEntry = nil;
904 NSArray *subentityDeclarations = nil;
905 NSEnumerator *subentityEnum = nil;
906 id subentityDecl = nil;
907 NSDictionary *subentityDict = nil;
908 NSString *subentityKey = nil;
909 NSMutableDictionary *subentityShipEntry = nil;
910 NSMutableSet *badSubentities = nil;
911 NSString *badSubentitiesList = nil;
912 NSMutableArray *okSubentities = nil;
913 BOOL remove, fatal;
914
915 // Convert all subentity declarations to dictionaries and add
916 // _oo_is_subentity=YES to all entries used as subentities.
917
918 // Iterate over all ships. (Iterates over a copy of keys since it mutates the dictionary.)
919 for (shipKeyEnum = [[ioData allKeys] objectEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
920 {
921 shipEntry = [ioData objectForKey:shipKey];
922 remove = NO;
923 badSubentities = nil;
924
925 // Iterate over each subentity declaration of each ship
926 subentityDeclarations = [shipEntry oo_arrayForKey:@"subentities"];
927 if (subentityDeclarations != nil)
928 {
929 okSubentities = [NSMutableArray arrayWithCapacity:[subentityDeclarations count]];
930 for (subentityEnum = [subentityDeclarations objectEnumerator]; (subentityDecl = [subentityEnum nextObject]); )
931 {
932 subentityDict = [self canonicalizeSubentityDeclaration:subentityDecl forShip:shipKey shipData:ioData fatalError:&fatal];
933
934 // If entry is broken, we need to kill this ship.
935 if (fatal)
936 {
937 OOStandardsError(@"Bad subentity definition found");
938 remove = YES;
939 }
940 else if (subentityDict != nil)
941 {
942 [okSubentities addObject:subentityDict];
943
944 // Tag subentities.
945 if (![[subentityDict oo_stringForKey:@"type"] isEqualToString:@"flasher"])
946 {
947 subentityKey = [subentityDict oo_stringForKey:@"subentity_key"];
948 subentityShipEntry = [ioData objectForKey:subentityKey];
949 if (subentityKey == nil || subentityShipEntry == nil)
950 {
951 // Oops, reference to non-existent subent.
952 if (badSubentities == nil) badSubentities = [NSMutableSet set];
953 [badSubentities addObject:subentityKey];
954 }
955 else
956 {
957 // Subent exists, add _oo_is_subentity so roles aren't required.
958 [subentityShipEntry oo_setBool:YES forKey:@"_oo_is_subentity"];
959 }
960 }
961 }
962 }
963
964 // Set updated subentity list.
965 if ([okSubentities count] != 0)
966 {
967 [shipEntry setObject:okSubentities forKey:@"subentities"];
968 }
969 else
970 {
971 [shipEntry removeObjectForKey:@"subentities"];
972 }
973
974 if (badSubentities != nil)
975 {
976 if (![shipEntry oo_boolForKey:@"is_external_dependency"])
977 {
978 badSubentitiesList = [[[badSubentities allObjects] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] componentsJoinedByString:@", "];
979 OOLogERR(@"shipData.load.error", @"the shipdata.plist entry \"%@\" has unresolved subentit%@ %@.", shipKey, ([badSubentities count] == 1) ? @"y" : @"ies", badSubentitiesList);
980 OOStandardsError(@"Bad subentity definition found");
981 }
982 remove = YES;
983 }
984
985 if (remove)
986 {
987 // Removal is deferred to avoid bogus "entry doesn't exist" errors.
988 [shipEntry oo_setBool:YES forKey:@"_oo_deferred_remove"];
989 }
990 }
991 }
992
993 return YES;
994}
995
996
997- (BOOL) removeUnusableEntries:(NSMutableDictionary *)ioData shipMode:(BOOL)shipMode
998{
999 NSEnumerator *shipKeyEnum = nil;
1000 NSString *shipKey = nil;
1001 NSMutableDictionary *shipEntry = nil;
1002 BOOL remove;
1003 NSString *modelName = nil;
1004
1005 // Clean out invalid entries and templates. (Iterates over a copy of keys since it mutates the dictionary.)
1006 for (shipKeyEnum = [[ioData allKeys] objectEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
1007 {
1008 shipEntry = [ioData objectForKey:shipKey];
1009 remove = NO;
1010
1011 if ([shipEntry oo_boolForKey:@"is_template"] || [shipEntry oo_boolForKey:@"_oo_deferred_remove"]) remove = YES;
1012 else if (shipMode && [[shipEntry oo_stringForKey:@"roles"] length] == 0 && ![shipEntry oo_boolForKey:@"_oo_is_subentity"] && ![shipEntry oo_boolForKey:@"_oo_is_effect"])
1013 {
1014 OOLogERR(@"shipData.load.error", @"the shipdata.plist entry \"%@\" specifies no %@.", shipKey, @"roles");
1015 remove = YES;
1016 OOStandardsError(@"Error in shipdata.plist");
1017 }
1018 else
1019 {
1020 modelName = [shipEntry oo_stringForKey:@"model"];
1021 if (shipMode && [modelName length] == 0)
1022 {
1023 OOLogERR(@"shipData.load.error", @"the shipdata.plist entry \"%@\" specifies no %@.", shipKey, @"model");
1024 OOStandardsError(@"Error in shipdata.plist");
1025 remove = YES;
1026 }
1027 else if ([modelName length] != 0 && [ResourceManager pathForFileNamed:modelName inFolder:@"Models"] == nil)
1028 {
1029 OOLogERR(@"shipData.load.error", @"the shipdata.plist entry \"%@\" specifies non-existent model \"%@\".", shipKey, modelName);
1030 OOStandardsError(@"Error in shipdata.plist");
1031 remove = YES;
1032 }
1033 }
1034 if (remove) [ioData removeObjectForKey:shipKey];
1035 }
1036
1037 return YES;
1038}
1039
1040
1041/* Transform conditions, determinant (if conditions array) and
1042 shipyard.conditions from hasShipyard to sanitized form.
1043 Also get list of condition_scripts
1044*/
1045- (BOOL) sanitizeConditions:(NSMutableDictionary *)ioData
1046{
1047 NSEnumerator *shipKeyEnum = nil;
1048 NSString *shipKey = nil;
1049 NSMutableDictionary *shipEntry = nil;
1050 NSMutableDictionary *mutableShipyard = nil;
1051 NSArray *conditions = nil;
1052 NSArray *hasShipyard = nil;
1053 NSArray *shipyardConditions = nil;
1054 NSString *condition_script = nil;
1055 NSString *shipyard_condition_script = nil;
1056
1057 NSMutableArray *conditionScripts = [[NSMutableArray alloc] init];
1058
1059 for (shipKeyEnum = [[ioData allKeys] objectEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
1060 {
1061 shipEntry = [ioData objectForKey:shipKey];
1062 conditions = [shipEntry objectForKey:@"conditions"];
1063 condition_script = [shipEntry oo_stringForKey:@"condition_script"];
1064 if (condition_script != nil)
1065 {
1066 if (![conditionScripts containsObject:condition_script])
1067 {
1068 [conditionScripts addObject:condition_script];
1069 }
1070 }
1071
1072 hasShipyard = [shipEntry objectForKey:@"has_shipyard"];
1073 if (![hasShipyard isKindOfClass:[NSArray class]]) hasShipyard = nil; // May also be fuzzy boolean
1074 if (hasShipyard == nil)
1075 {
1076 hasShipyard = [shipEntry objectForKey:@"hasShipyard"];
1077 if (![hasShipyard isKindOfClass:[NSArray class]]) hasShipyard = nil; // May also be fuzzy boolean
1078 }
1079 shipyardConditions = [[shipEntry oo_dictionaryForKey:@"_oo_shipyard"] objectForKey:@"conditions"];
1080 shipyard_condition_script = [[shipEntry oo_dictionaryForKey:@"_oo_shipyard"] oo_stringForKey:@"condition_script"];
1081 if (shipyard_condition_script != nil)
1082 {
1083 if (![conditionScripts containsObject:shipyard_condition_script])
1084 {
1085 [conditionScripts addObject:shipyard_condition_script];
1086 }
1087 }
1088
1089
1090 if (conditions == nil && hasShipyard && shipyardConditions == nil) continue;
1091
1092 if (conditions != nil)
1093 {
1094 OOStandardsDeprecated([NSString stringWithFormat:@"The 'conditions' key is deprecated in shipdata entry %@",shipKey]);
1095 if (!OOEnforceStandards())
1096 {
1097 if ([conditions isKindOfClass:[NSArray class]])
1098 {
1099 conditions = OOSanitizeLegacyScriptConditions(conditions, [NSString stringWithFormat:@"<shipdata.plist entry \"%@\">", shipKey]);
1100 }
1101 else
1102 {
1103 OOLogWARN(@"shipdata.load.warning", @"conditions for shipdata.plist entry \"%@\" are not an array, ignoring.", shipKey);
1104 conditions = nil;
1105 }
1106
1107 if (conditions != nil)
1108 {
1109 [shipEntry setObject:conditions forKey:@"conditions"];
1110 }
1111 else
1112 {
1113 [shipEntry removeObjectForKey:@"conditions"];
1114 }
1115 }
1116 }
1117
1118 if (hasShipyard != nil)
1119 {
1120 hasShipyard = OOSanitizeLegacyScriptConditions(hasShipyard, [NSString stringWithFormat:@"<shipdata.plist entry \"%@\" hasShipyard conditions>", shipKey]);
1121 OOStandardsDeprecated([NSString stringWithFormat:@"Use of legacy script conditions in the 'has_shipyard' key is deprecated in shipyard entry %@",shipKey]);
1122 if (!OOEnforceStandards())
1123 {
1124 if (hasShipyard != nil)
1125 {
1126 [shipEntry setObject:hasShipyard forKey:@"has_shipyard"];
1127 }
1128 else
1129 {
1130 [shipEntry removeObjectForKey:@"hasShipyard"];
1131 [shipEntry removeObjectForKey:@"has_shipyard"];
1132 }
1133 }
1134 }
1135
1136 if (shipyardConditions != nil)
1137 {
1138 OOStandardsDeprecated([NSString stringWithFormat:@"The 'conditions' key is deprecated in shipyard entry %@",shipKey]);
1139 if (!OOEnforceStandards())
1140 {
1141 mutableShipyard = [[[shipEntry oo_dictionaryForKey:@"_oo_shipyard"] mutableCopy] autorelease];
1142
1143 if ([shipyardConditions isKindOfClass:[NSArray class]])
1144 {
1145 shipyardConditions = OOSanitizeLegacyScriptConditions(shipyardConditions, [NSString stringWithFormat:@"<shipyard.plist entry \"%@\">", shipKey]);
1146 }
1147 else
1148 {
1149 OOLogWARN(@"shipdata.load.warning", @"conditions for shipyard.plist entry \"%@\" are not an array, ignoring.", shipKey);
1150 shipyardConditions = nil;
1151 }
1152
1153 if (shipyardConditions != nil)
1154 {
1155 [mutableShipyard setObject:shipyardConditions forKey:@"conditions"];
1156 }
1157 else
1158 {
1159 [mutableShipyard removeObjectForKey:@"conditions"];
1160 }
1161
1162 [shipEntry setObject:mutableShipyard forKey:@"_oo_shipyard"];
1163 }
1164 }
1165 }
1166
1167 [[OOCacheManager sharedCache] setObject:conditionScripts forKey:@"ship conditions" inCache:@"condition scripts"];
1168 [conditionScripts release];
1169
1170 return YES;
1171}
1172
1173
1174#if PRELOAD
1175- (BOOL) preloadShipMeshes:(NSMutableDictionary *)ioData
1176{
1177 NSEnumerator *shipKeyEnum = nil;
1178 NSString *shipKey = nil;
1179 NSMutableDictionary *shipEntry = nil;
1180 BOOL remove;
1181 NSString *modelName = nil;
1182 OOMesh *mesh = nil;
1183 NSAutoreleasePool *pool = nil;
1184 NSUInteger i = 0, count;
1185
1186 count = [ioData count];
1187
1188 // Preload ship meshes. (Iterates over a copy of keys since it mutates the dictionary.)
1189 for (shipKeyEnum = [[ioData allKeys] objectEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
1190 {
1191 pool = [[NSAutoreleasePool alloc] init];
1192
1193 [[GameController sharedController] setProgressBarValue:(float)i++ / (float)count];
1194
1195 shipEntry = [ioData objectForKey:shipKey];
1196 remove = NO;
1197
1198 modelName = [shipEntry oo_stringForKey:@"model"];
1199 mesh = [OOMesh meshWithName:modelName
1200 materialDictionary:[shipEntry oo_dictionaryForKey:@"materials"]
1201 shadersDictionary:[shipEntry oo_dictionaryForKey:@"shaders"]
1202 smooth:[shipEntry oo_boolForKey:@"smooth"]
1203 shaderMacros:nil
1204 shaderBindingTarget:nil];
1205
1206 [pool release]; // NOTE: mesh is now invalid, but pointer nil check is OK.
1207
1208 if (mesh == nil)
1209 {
1210 // FIXME: what if it's a subentity? Need to rearrange things.
1211 OOLogERR(@"shipData.load.error", @"model \"%@\" could not be loaded for ship \"%@\", removing.", modelName, shipKey);
1212 [ioData removeObjectForKey:shipKey];
1213 }
1214 }
1215
1216 [[GameController sharedController] setProgressBarValue:-1.0f];
1217
1218 return YES;
1219}
1220#endif
1221
1222
1223- (void) mergeShipRoles:(NSString *)roles
1224 forShipKey:(NSString *)shipKey
1225 intoProbabilityMap:(NSMutableDictionary *)probabilitySets
1226{
1227 NSDictionary *rolesAndWeights = nil;
1228 NSEnumerator *roleEnum = nil;
1229 NSString *role = nil;
1230 OOMutableProbabilitySet *probSet = nil;
1231
1232
1233 /* probabilitySets is a dictionary whose keys are roles and whose values
1234 are mutable probability sets, whose values are ship keys.
1235
1236 When creating new ships Oolite looks up this probability map.
1237 To upgrade all soliton 'thargon' roles to 'EQ_THARGON' we need
1238 to swap these roles here.
1239 */
1240
1241 rolesAndWeights = OOParseRolesFromString(roles);
1242 // add default [shipKey] role
1243 NSMutableDictionary *mutable = [NSMutableDictionary dictionaryWithDictionary:rolesAndWeights];
1244 [mutable setObject:[NSNumber numberWithFloat:1.0] forKey:[[[NSString alloc] initWithFormat:@"[%@]",shipKey] autorelease]];
1245 rolesAndWeights = mutable;
1246
1247 id thargonValue = [rolesAndWeights objectForKey:@"thargon"];
1248 if (thargonValue != nil && [rolesAndWeights objectForKey:@"EQ_THARGON"] == nil)
1249 {
1250 NSMutableDictionary *mutable = [NSMutableDictionary dictionaryWithDictionary:rolesAndWeights];
1251 [mutable setObject:thargonValue forKey:@"EQ_THARGON"];
1252 rolesAndWeights = mutable;
1253 }
1254
1255 for (roleEnum = [rolesAndWeights keyEnumerator]; (role = [roleEnum nextObject]); )
1256 {
1257 probSet = [probabilitySets objectForKey:role];
1258 if (probSet == nil)
1259 {
1261 [probabilitySets setObject:probSet forKey:role];
1262 }
1263
1264 [probSet setWeight:[rolesAndWeights oo_floatForKey:role] forObject:shipKey];
1265 }
1266}
1267
1268
1269- (NSDictionary *) canonicalizeSubentityDeclaration:(id)declaration
1270 forShip:(NSString *)shipKey
1271 shipData:(NSDictionary *)shipData
1272 fatalError:(BOOL *)outFatalError
1273{
1274 NSDictionary *result = nil;
1275
1276 assert(outFatalError != NULL);
1277 *outFatalError = NO;
1278
1279 if ([declaration isKindOfClass:[NSString class]])
1280 {
1281 // Update old-style string-based declaration.
1282 OOStandardsDeprecated([NSString stringWithFormat:@"Old style sub-entity declarations are deprecated in %@",shipKey]);
1283 if (!OOEnforceStandards())
1284 {
1285 result = [self translateOldStyleSubentityDeclaration:declaration
1286 forShip:shipKey
1287 shipData:shipData
1288 fatalError:outFatalError];
1289 }
1290 if (result != nil)
1291 {
1292 // Ensure internal translation made sense, and clean up a bit.
1293 result = [self validateNewStyleSubentityDeclaration:result
1294 forShip:shipKey
1295 fatalError:outFatalError];
1296 }
1297 }
1298 else if ([declaration isKindOfClass:[NSDictionary class]])
1299 {
1300 // Validate dictionary-based declaration.
1301 result = [self validateNewStyleSubentityDeclaration:declaration
1302 forShip:shipKey
1303 fatalError:outFatalError];
1304 }
1305 else
1306 {
1307 OOLogERR(@"shipData.load.error.badSubentity", @"subentity declaration for ship %@ should be string or dictionary, found %@.", shipKey, [declaration class]);
1308 *outFatalError = YES;
1309 }
1310
1311 // For frangible ships, bad subentities are non-fatal.
1312 if (*outFatalError && [[shipData oo_dictionaryForKey:shipKey] oo_boolForKey:@"frangible"]) *outFatalError = NO;
1313
1314 return result;
1315}
1316
1317
1318- (NSDictionary *) translateOldStyleSubentityDeclaration:(NSString *)declaration
1319 forShip:(NSString *)shipKey
1320 shipData:(NSDictionary *)shipData
1321 fatalError:(BOOL *)outFatalError
1322{
1323 NSArray *tokens = nil;
1324 NSString *subentityKey = nil;
1325 BOOL isFlasher;
1326
1327 tokens = ScanTokensFromString(declaration);
1328
1329 subentityKey = [tokens objectAtIndex:0];
1330 isFlasher = [subentityKey isEqualToString:@"*FLASHER*"];
1331
1332 // Sanity check: require eight tokens.
1333 if ([tokens count] != 8)
1334 {
1335 if (!isFlasher)
1336 {
1337 OOLogERR(@"shipData.load.error.badSubentity", @"the shipdata.plist entry \"%@\" has a broken subentity definition \"%@\" (should have 8 tokens, has %lu).", shipKey, subentityKey, [tokens count]);
1338 *outFatalError = YES;
1339 }
1340 else
1341 {
1342 OOLogWARN(@"shipData.load.warning.badFlasher", @"the shipdata.plist entry \"%@\" has a broken flasher definition (should have 8 tokens, has %lu). This flasher will be ignored.", shipKey, [tokens count]);
1343 }
1344 return nil;
1345 }
1346
1347 if (isFlasher)
1348 {
1349 return [self translateOldStyleFlasherDeclaration:tokens
1350 forShip:shipKey
1351 fatalError:outFatalError];
1352 }
1353 else
1354 {
1355 return [self translateOldStandardBasicSubentityDeclaration:tokens
1356 forShip:shipKey
1357 shipData:shipData
1358 fatalError:outFatalError];
1359 }
1360}
1361
1362
1363- (NSDictionary *) translateOldStyleFlasherDeclaration:(NSArray *)tokens
1364 forShip:(NSString *)shipKey
1365 fatalError:(BOOL *)outFatalError
1366{
1367 Vector position;
1368 float size, frequency, phase, hue;
1369 NSDictionary *colorDict = nil;
1370 NSDictionary *result = nil;
1371
1372 position.x = [tokens oo_floatAtIndex:1];
1373 position.y = [tokens oo_floatAtIndex:2];
1374 position.z = [tokens oo_floatAtIndex:3];
1375
1376 hue = [tokens oo_floatAtIndex:4];
1377 frequency = [tokens oo_floatAtIndex:5];
1378 phase = [tokens oo_floatAtIndex:6];
1379 size = [tokens oo_floatAtIndex:7];
1380
1381 colorDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:hue] forKey:@"hue"];
1382
1383 result = [NSDictionary dictionaryWithObjectsAndKeys:
1384 @"flasher", @"type",
1385 OOPropertyListFromVector(position), @"position",
1386 [NSArray arrayWithObject:colorDict], @"colors",
1387 [NSNumber numberWithFloat:frequency], @"frequency",
1388 [NSNumber numberWithFloat:phase], @"phase",
1389 [NSNumber numberWithFloat:size], @"size",
1390 nil];
1391
1392 OOLog(@"shipData.translateSubentity.flasher", @"Translated flasher declaration \"%@\" to %@", [tokens componentsJoinedByString:@" "], result);
1393
1394 return result;
1395}
1396
1397
1398- (NSDictionary *) translateOldStandardBasicSubentityDeclaration:(NSArray *)tokens
1399 forShip:(NSString *)shipKey
1400 shipData:(NSDictionary *)shipData
1401 fatalError:(BOOL *)outFatalError
1402{
1403 NSString *subentityKey = nil;
1404 Vector position;
1405 Quaternion orientation;
1406 NSMutableDictionary *result = nil;
1407 BOOL isTurret, isDock = NO;
1408
1409 subentityKey = [tokens oo_stringAtIndex:0];
1410
1411 isTurret = [self shipIsBallTurretForKey:subentityKey inShipData:shipData];
1412
1413 position.x = [tokens oo_floatAtIndex:1];
1414 position.y = [tokens oo_floatAtIndex:2];
1415 position.z = [tokens oo_floatAtIndex:3];
1416
1417 orientation.w = [tokens oo_floatAtIndex:4];
1418 orientation.x = [tokens oo_floatAtIndex:5];
1419 orientation.y = [tokens oo_floatAtIndex:6];
1420 orientation.z = [tokens oo_floatAtIndex:7];
1421
1422 if(orientation.w == 0 && orientation.x == 0 && orientation.y == 0 && orientation.z == 0)
1423 {
1424 orientation.w = 1; // avoid dividing by zero.
1425 OOLogWARN(@"shipData.load.error", @"The ship %@ has an undefined orientation for its %@ subentity. Setting it now at (1,0,0,0)", shipKey, subentityKey);
1426 }
1427
1428 quaternion_normalize(&orientation);
1429
1430 if (!isTurret)
1431 {
1432 isDock = [subentityKey rangeOfString:@"dock"].location != NSNotFound;
1433 }
1434
1435 result = [NSMutableDictionary dictionaryWithCapacity:5];
1436 [result setObject:isTurret ? @"ball_turret" : @"standard" forKey:@"type"];
1437 [result setObject:subentityKey forKey:@"subentity_key"];
1438 [result oo_setVector:position forKey:@"position"];
1439 [result oo_setQuaternion:orientation forKey:@"orientation"];
1440 if (isDock) [result oo_setBool:YES forKey:@"is_dock"];
1441
1442 OOLog(@"shipData.translateSubentity.standard", @"Translated subentity declaration \"%@\" to %@", [tokens componentsJoinedByString:@" "], result);
1443
1444 return [[result copy] autorelease];
1445}
1446
1447
1448- (NSDictionary *) validateNewStyleSubentityDeclaration:(NSDictionary *)declaration
1449 forShip:(NSString *)shipKey
1450 fatalError:(BOOL *)outFatalError
1451{
1452 NSString *type = nil;
1453
1454 type = [declaration oo_stringForKey:@"type"];
1455 if (type == nil) type = @"standard";
1456
1457 if ([type isEqualToString:@"flasher"])
1458 {
1459 return [self validateNewStyleFlasherDeclaration:declaration forShip:shipKey fatalError:outFatalError];
1460 }
1461 else if ([type isEqualToString:@"standard"] || [type isEqualToString:@"ball_turret"])
1462 {
1463 return [self validateNewStyleStandardSubentityDeclaration:declaration forShip:shipKey fatalError:outFatalError];
1464 }
1465 else
1466 {
1467 OOLogERR(@"shipData.load.error.badSubentity", @"subentity declaration for ship %@ does not declare a valid type (must be standard, flasher or ball_turret).", shipKey);
1468 *outFatalError = YES;
1469 return nil;
1470 }
1471}
1472
1473
1474- (NSDictionary *) validateNewStyleFlasherDeclaration:(NSDictionary *)declaration
1475 forShip:(NSString *)shipKey
1476 fatalError:(BOOL *)outFatalError
1477{
1478 NSMutableDictionary *result = nil;
1479 Vector position = kZeroVector;
1480 NSArray *colors = nil;
1481 id colorDesc = nil;
1482 float size, frequency, phase, brightfraction;
1483 BOOL initiallyOn;
1484
1485#define kDefaultFlasherColor @"redColor"
1486
1487 // "Validate" is really "clean up", since all values have defaults.
1488 colors = [declaration oo_arrayForKey:@"colors"];
1489 if ([colors count] == 0)
1490 {
1491 colorDesc = [declaration objectForKey:@"color"];
1492 if (colorDesc == nil) colorDesc = kDefaultFlasherColor;
1493 if ([colorDesc isKindOfClass:[NSArray class]])
1494 {
1495 // an easy made error is adding an array to "color" instead of "colors"
1496 OOLogWARN(@"shipData.load.warning.flasher.badColor", @"changing flasher for ship %@ from a color to a colors definition.", shipKey);
1497 colors = colorDesc;
1498 }
1499 else
1500 {
1501 colors = [NSArray arrayWithObject:colorDesc];
1502 }
1503 }
1504
1505 // Validate colours.
1506 NSMutableArray *validColors = [NSMutableArray arrayWithCapacity:[colors count]];
1507 foreach (colorDesc, colors)
1508 {
1509 OOColor *color = [OOColor colorWithDescription:colorDesc];
1510 if (color != nil)
1511 {
1512 [validColors addObject:[color normalizedArray]];
1513 }
1514 else
1515 {
1516 OOLogWARN(@"shipdata.load.warning.flasher.badColor", @"skipping invalid colour specifier for flasher for ship %@.", shipKey);
1517 }
1518 }
1519 // Ensure there's at least one.
1520 if ([validColors count] == 0)
1521 {
1522 [validColors addObject:kDefaultFlasherColor];
1523 }
1524 colors = validColors;
1525
1526 position = [declaration oo_vectorForKey:@"position"];
1527
1528 size = [declaration oo_floatForKey:@"size" defaultValue:8.0];
1529
1530 if (size <= 0)
1531 {
1532 OOLogWARN(@"shipData.load.warning.flasher.badSize", @"skipping flasher of invalid size %g for ship %@.", size, shipKey);
1533 return nil;
1534 }
1535
1536 brightfraction = [declaration oo_floatForKey:@"bright_fraction" defaultValue:0.5];
1537 if (brightfraction < 0.0 || brightfraction > 1.0)
1538 {
1539 OOLogWARN(@"shipData.load.warning.flasher.badFraction", @"skipping flasher of invalid bright fraction %g for ship %@.", brightfraction, shipKey);
1540 return nil;
1541 }
1542
1543 frequency = [declaration oo_floatForKey:@"frequency" defaultValue:2.0];
1544 phase = [declaration oo_floatForKey:@"phase" defaultValue:0.0];
1545 initiallyOn = [declaration oo_boolForKey:@"initially_on" defaultValue:YES];
1546
1547 result = [NSMutableDictionary dictionaryWithCapacity:8];
1548 [result setObject:@"flasher" forKey:@"type"];
1549 [result setObject:colors forKey:@"colors"];
1550 [result oo_setVector:position forKey:@"position"];
1551 [result setObject:[NSNumber numberWithFloat:size] forKey:@"size"];
1552 [result setObject:[NSNumber numberWithFloat:frequency] forKey:@"frequency"];
1553 if (phase != 0) [result setObject:[NSNumber numberWithFloat:phase] forKey:@"phase"];
1554 [result setObject:[NSNumber numberWithFloat:brightfraction] forKey:@"bright_fraction"];
1555 [result setObject:[NSNumber numberWithBool:initiallyOn] forKey:@"initially_on"];
1556
1557 return [[result copy] autorelease];
1558}
1559
1560
1561- (NSDictionary *) validateNewStyleStandardSubentityDeclaration:(NSDictionary *)declaration
1562 forShip:(NSString *)shipKey
1563 fatalError:(BOOL *)outFatalError
1564{
1565 NSMutableDictionary *result = nil;
1566 NSString *subentityKey = nil;
1567 Vector position = kZeroVector;
1568 Quaternion orientation = kIdentityQuaternion;
1569 BOOL isTurret;
1570 BOOL isDock = NO;
1571 float fireRate = -1.0f; // out of range constants
1572 float weaponRange = -1.0f;
1573 float weaponEnergy = -1.0f;
1574 NSDictionary *scriptInfo = nil;
1575
1576 subentityKey = [declaration objectForKey:@"subentity_key"];
1577 if (subentityKey == nil)
1578 {
1579 OOLogERR(@"shipData.load.error.badSubentity", @"subentity declaration for ship %@ specifies no subentity_key.", shipKey);
1580 *outFatalError = YES;
1581 return nil;
1582 }
1583
1584 isTurret = [[declaration oo_stringForKey:@"type"] isEqualToString:@"ball_turret"];
1585 if (isTurret)
1586 {
1587 fireRate = [declaration oo_floatForKey:@"fire_rate" defaultValue:-1.0f];
1588 if (fireRate < 0.25f && fireRate >= 0.0f)
1589 {
1590 OOLogWARN(@"shipData.load.warning.turret.badFireRate", @"ball turret fire rate of %g for subentity of ship %@ is invalid, using 0.25.", fireRate, shipKey);
1591 fireRate = 0.25f;
1592 }
1593 weaponRange = [declaration oo_floatForKey:@"weapon_range" defaultValue:-1.0f];
1595 {
1596 OOLogWARN(@"shipData.load.warning.turret.badWeaponRange", @"ball turret weapon range of %g for subentity of ship %@ is too high, using %.1f.", weaponRange, shipKey, TURRET_SHOT_RANGE * COMBAT_WEAPON_RANGE_FACTOR);
1597 weaponRange = TURRET_SHOT_RANGE * COMBAT_WEAPON_RANGE_FACTOR; // approx. range of primary plasma canon.
1598 }
1599
1600 weaponEnergy = [declaration oo_floatForKey:@"weapon_energy" defaultValue:-1.0f];
1601 if (weaponEnergy > 100.0f)
1602
1603 {
1604 OOLogWARN(@"shipData.load.warning.turret.badWeaponEnergy", @"ball turret weapon energy of %g for subentity of ship %@ is too high, using 100.", weaponEnergy, shipKey);
1605 weaponEnergy = 100.0f;
1606 }
1607 }
1608 else
1609 {
1610 isDock = [declaration oo_boolForKey:@"is_dock"];
1611 }
1612
1613 position = [declaration oo_vectorForKey:@"position"];
1614 orientation = [declaration oo_quaternionForKey:@"orientation"];
1615 quaternion_normalize(&orientation);
1616
1617 scriptInfo = [declaration oo_dictionaryForKey:@"script_info"];
1618
1619 result = [NSMutableDictionary dictionaryWithCapacity:10];
1620 [result setObject:isTurret ? @"ball_turret" : @"standard" forKey:@"type"];
1621 [result setObject:subentityKey forKey:@"subentity_key"];
1622 [result oo_setVector:position forKey:@"position"];
1623 [result oo_setQuaternion:orientation forKey:@"orientation"];
1624 if (isDock)
1625 {
1626 [result oo_setBool:YES forKey:@"is_dock"];
1627
1628 NSString* docklabel = [declaration oo_stringForKey:@"dock_label" defaultValue:@"the docking bay"];
1629 [result setObject:docklabel forKey:@"dock_label"];
1630
1631 BOOL dockable = [declaration oo_boolForKey:@"allow_docking" defaultValue:YES];
1632 BOOL playerdockable = [declaration oo_boolForKey:@"disallowed_docking_collides" defaultValue:NO];
1633 BOOL undockable = [declaration oo_boolForKey:@"allow_launching" defaultValue:YES];
1634
1635 [result oo_setBool:dockable forKey:@"allow_docking"];
1636 [result oo_setBool:playerdockable forKey:@"disallowed_docking_collides"];
1637 [result oo_setBool:undockable forKey:@"allow_launching"];
1638
1639 }
1640
1641 if (isTurret)
1642 {
1643 // default constants are defined and set in shipEntity
1644 if (fireRate > 0) [result oo_setFloat:fireRate forKey:@"fire_rate"];
1645 if (weaponRange >= 0) [result oo_setFloat:weaponRange forKey:@"weapon_range"];
1646 if (weaponEnergy >= 0) [result oo_setFloat:weaponEnergy forKey:@"weapon_energy"];
1647 }
1648
1649 if (scriptInfo != nil)
1650 {
1651 [result setObject:scriptInfo forKey:@"script_info"];
1652 }
1653
1654 return [[result copy] autorelease];
1655}
1656
1657
1658- (BOOL) shipIsBallTurretForKey:(NSString *)shipKey inShipData:(NSDictionary *)shipData
1659{
1660 // Test for presence of setup_actions containing initialiseTurret.
1661 NSArray *setupActions = nil;
1662 NSEnumerator *actionEnum = nil;
1663 NSString *action = nil;
1664
1665 setupActions = [[shipData oo_dictionaryForKey:shipKey] oo_arrayForKey:@"setup_actions"];
1666
1667 for (actionEnum = [setupActions objectEnumerator]; (action = [actionEnum nextObject]); )
1668 {
1669 if ([[ScanTokensFromString(action) objectAtIndex:0] isEqualToString:@"initialiseTurret"]) return YES;
1670 }
1671
1672 if ([shipKey isEqualToString:@"ballturret"])
1673 {
1674 // compatibility for OXPs using old subentity declarations and the
1675 // core turret entity
1676 return YES;
1677 }
1678
1679 return NO;
1680}
1681
1682@end
1683
1684
1685@implementation OOShipRegistry (Singleton)
1686
1687/* Canonical singleton boilerplate.
1688 See Cocoa Fundamentals Guide: Creating a Singleton Instance.
1689 See also +sharedRegistry above.
1690
1691 NOTE: assumes single-threaded access.
1692*/
1693
1694+ (id) allocWithZone:(NSZone *)inZone
1695{
1696 if (sSingleton == nil)
1697 {
1698 OOLog(@"shipData.load.begin", @"%@", @"Loading ship data.");
1699 sSingleton = [super allocWithZone:inZone];
1700 return sSingleton;
1701 }
1702 return nil;
1703}
1704
1705
1706- (id) copyWithZone:(NSZone *)inZone
1707{
1708 return self;
1709}
1710
1711
1712- (id) retain
1713{
1714 return self;
1715}
1716
1717
1718- (NSUInteger) retainCount
1719{
1720 return UINT_MAX;
1721}
1722
1723
1724- (void) release
1725{}
1726
1727
1728- (id) autorelease
1729{
1730 return self;
1731}
1732
1733@end
1734
1735
1736static void GatherStringAddrsDict(NSDictionary *dict, NSMutableSet *strings, NSString *context);
1737static void GatherStringAddrsArray(NSArray *array, NSMutableSet *strings, NSString *context);
1738static void GatherStringAddrs(id object, NSMutableSet *strings, NSString *context);
1739
1740
1741static void DumpStringAddrs(NSDictionary *dict, NSString *context)
1742{
1743 return;
1744 static FILE *dump = NULL;
1745 if (dump == NULL) dump = fopen("strings.txt", "w");
1746 if (dump == NULL) return;
1747
1748 NSAutoreleasePool *pool = [NSAutoreleasePool new];
1749 NSMutableSet *strings = [NSMutableSet set];
1750 GatherStringAddrs(dict, strings, context);
1751
1752 NSEnumerator *entryEnum = nil;
1753 NSDictionary *entry = nil;
1754 for (entryEnum = [strings objectEnumerator]; (entry = [entryEnum nextObject]); )
1755 {
1756 NSString *string = [entry objectForKey:@"string"];
1757 NSString *context = [entry objectForKey:@"context"];
1758 void *pointer = [[entry objectForKey:@"address"] pointerValue];
1759
1760 string = [NSString stringWithFormat:@"%p\t%@: \"%@\"", pointer, context, string];
1761
1762 fprintf(dump, "%s\n", [string UTF8String]);
1763 }
1764
1765 fprintf(dump, "\n");
1766 fflush(dump);
1767 [pool release];
1768}
1769
1770
1771static void GatherStringAddrsDict(NSDictionary *dict, NSMutableSet *strings, NSString *context)
1772{
1773 NSEnumerator *keyEnum = nil;
1774 id key = nil;
1775 NSString *keyContext = [context stringByAppendingString:@" key"];
1776 for (keyEnum = [dict keyEnumerator]; (key = [keyEnum nextObject]); )
1777 {
1778 GatherStringAddrs(key, strings, keyContext);
1779 GatherStringAddrs([dict objectForKey:key], strings, [context stringByAppendingFormat:@".%@", key]);
1780 }
1781}
1782
1783
1784static void GatherStringAddrsArray(NSArray *array, NSMutableSet *strings, NSString *context)
1785{
1786 NSEnumerator *vEnum = nil;
1787 NSString *v = nil;
1788 unsigned i = 0;
1789 for (vEnum = [array objectEnumerator]; (v = [vEnum nextObject]); )
1790 {
1791 GatherStringAddrs(v, strings, [context stringByAppendingFormat:@"[%u]", i++]);
1792 }
1793}
1794
1795
1796static void GatherStringAddrs(id object, NSMutableSet *strings, NSString *context)
1797{
1798 if ([object isKindOfClass:[NSString class]])
1799 {
1800 NSDictionary *entry = [NSDictionary dictionaryWithObjectsAndKeys:object, @"string", [NSValue valueWithPointer:object], @"address", context, @"context", nil];
1801 [strings addObject:entry];
1802 }
1803 else if ([object isKindOfClass:[NSArray class]])
1804 {
1805 GatherStringAddrsArray(object, strings, context);
1806 }
1807 else if ([object isKindOfClass:[NSDictionary class]])
1808 {
1809 GatherStringAddrsDict(object, strings, context);
1810 }
1811}
1812
1813
1814static NSComparisonResult SortDemoShipsByName (id a, id b, void* context)
1815{
1816 return [[a oo_stringForKey:@"name"] compare:[b oo_stringForKey:@"name"]];
1817}
1818
1819
1820static NSComparisonResult SortDemoCategoriesByName (id a, id b, void* context)
1821{
1822 return [OOShipLibraryCategoryPlural([[a oo_dictionaryAtIndex:0] oo_stringForKey:@"class"]) compare:OOShipLibraryCategoryPlural([[b oo_dictionaryAtIndex:0] oo_stringForKey:@"class"])];
1823}
#define DESTROY(x)
Definition OOCocoa.h:77
static OODebugMonitor * sSingleton
void OOStandardsDeprecated(NSString *message)
BOOL OOEnforceStandards(void)
void OOStandardsError(NSString *message)
id OODeepCopy(id object) OO_RETURNS_RETAINED
Definition OODeepCopy.m:31
OOINLINE jsval OOJSValueFromNativeObject(JSContext *context, id object)
OOINLINE JSContext * OOJSAcquireContext(void)
OOINLINE void OOJSRelinquishContext(JSContext *context)
NSArray * OOSanitizeLegacyScriptConditions(NSArray *conditions, NSString *context)
#define OOLogWARN(class, format,...)
Definition OOLogging.h:113
#define OOLogERR(class, format,...)
Definition OOLogging.h:112
#define OOLog(class, format,...)
Definition OOLogging.h:88
return self
unsigned count
return nil
const Quaternion kIdentityQuaternion
NSDictionary * OOParseRolesFromString(NSString *string)
static NSString *const kOODemoShipClass
static NSString *const kOODemoShipKey
static OOShipRegistry * sSingleton
#define kDefaultFlasherColor
static NSComparisonResult SortDemoCategoriesByName(id a, id b, void *context)
static NSString *const kDefaultDemoShip
static void GatherStringAddrsArray(NSArray *array, NSMutableSet *strings, NSString *context)
static void GatherStringAddrsDict(NSDictionary *dict, NSMutableSet *strings, NSString *context)
static NSString *const kPlayerShipsCacheKey
static NSString *const kShipDataCacheKey
static NSString *const kVisualEffectDataCacheKey
static NSComparisonResult SortDemoShipsByName(id a, id b, void *context)
static void DumpStringAddrs(NSDictionary *dict, NSString *context)
static NSString *const kShipRegistryCacheName
static void GatherStringAddrs(id object, NSMutableSet *strings, NSString *context)
static NSString *const kRoleWeightsCacheKey
static NSString *const kVisualEffectRegistryCacheName
NSMutableArray * ScanTokensFromString(NSString *values)
const Vector kZeroVector
Definition OOVector.m:28
#define PLAYER
#define TURRET_SHOT_RANGE
Definition ShipEntity.h:82
#define COMBAT_WEAPON_RANGE_FACTOR
Definition ShipEntity.h:58
GameController * sharedController()
void setObject:forKey:inCache:(id inElement,[forKey] NSString *inKey,[inCache] NSString *inCacheKey)
id objectForKey:inCache:(NSString *inKey,[inCache] NSString *inCacheKey)
OOCacheManager * sharedCache()
void setObject:forKey:(id value,[forKey] id key)
Definition OOCache.m:248
OOColor * colorWithDescription:(id description)
Definition OOColor.m:127
NSArray * normalizedArray()
Definition OOColor.m:511
BOOL callMethod:inContext:withArguments:count:result:(jsid methodID,[inContext] JSContext *context,[withArguments] jsval *argv,[count] intN argc,[result] jsval *outResult)
Definition OOJSScript.m:395
void setWeight:forObject:(float weight,[forObject] id object)
NSDictionary * propertyListRepresentation()
id probabilitySetWithPropertyListRepresentation:(NSDictionary *plist)
NSArray * arrayFromFilesNamed:inFolder:andMerge:cache:(NSString *fileName,[inFolder] NSString *folderName,[andMerge] BOOL mergeFiles,[cache] BOOL useCache)
NSDictionary * dictionaryFromFilesNamed:inFolder:mergeMode:cache:(NSString *fileName,[inFolder] NSString *folderName,[mergeMode] OOResourceMergeMode mergeMode,[cache] BOOL useCache)
voidpf void uLong size
Definition ioapi.h:134