Line data Source code
1 0 : /*
2 :
3 : OOPlanetDescriptionManager.h
4 :
5 : Singleton class responsible for planet description data.
6 :
7 : Oolite
8 : Copyright (C) 2004-2013 Giles C Williams and contributors
9 :
10 : This program is free software; you can redistribute it and/or
11 : modify it under the terms of the GNU General Public License
12 : as published by the Free Software Foundation; either version 2
13 : of the License, or (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program; if not, write to the Free Software
22 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23 : MA 02110-1301, USA.
24 :
25 : */
26 :
27 : #import "OOSystemDescriptionManager.h"
28 : #import "OOStringParsing.h"
29 : #import "OOCollectionExtractors.h"
30 : #import "OOTypes.h"
31 : #import "PlayerEntity.h"
32 : #import "Universe.h"
33 : #import "ResourceManager.h"
34 :
35 : // character sequence which can't be in property name, system key or layer
36 : // and is highly unlikely to be in a manifest identifier
37 0 : static NSString * const kOOScriptedChangeJoiner = @"~|~";
38 :
39 : // likely maximum number of planetinfo properties to be applied to a system
40 : // just for efficiency - no harm in exceeding it
41 0 : #define OO_LIKELY_PROPERTIES_PER_SYSTEM 50
42 :
43 : @interface OOSystemDescriptionManager (OOPrivate)
44 0 : - (void) setProperties:(NSDictionary *)properties inDescription:(OOSystemDescriptionEntry *)desc;
45 0 : - (NSDictionary *) calculatePropertiesForSystemKey:(NSString *)key;
46 0 : - (void) updateCacheEntry:(NSUInteger)i;
47 0 : - (void) updateCacheEntry:(NSUInteger)i forProperty:(NSString *)property;
48 0 : - (id) getProperty:(NSString *)property forSystemKey:(NSString *)key withUniversal:(BOOL)universal;
49 : /* some planetinfo properties have two ways to specify
50 : * need to get the one with higher layer (if they're both at the same layer,
51 : * go with property1) */
52 0 : - (id) getProperty:(NSString *)property1 orProperty:(NSString *)property2 forSystemKey:(NSString *)key withUniversal:(BOOL)universal;
53 :
54 0 : - (void) saveScriptedChangeToProperty:(NSString *)property forSystemKey:(NSString *)key andLayer:(OOSystemLayer)layer toValue:(id)value fromManifest:(NSString *)manifest;
55 :
56 : @end
57 :
58 0 : static NSString *kOOSystemLayerProperty = @"layer";
59 :
60 : @implementation OOSystemDescriptionManager
61 :
62 0 : - (id) init
63 : {
64 : self = [super init];
65 : if (self != nil)
66 : {
67 : universalProperties = [[NSMutableDictionary alloc] initWithCapacity:OO_LIKELY_PROPERTIES_PER_SYSTEM];
68 : interstellarSpace = [[OOSystemDescriptionEntry alloc] init];
69 : // assume specific interstellar settings are rare
70 : systemDescriptions = [[NSMutableDictionary alloc] initWithCapacity:OO_SYSTEM_CACHE_LENGTH+20];
71 : for (NSUInteger i=0;i<OO_SYSTEM_CACHE_LENGTH;i++)
72 : {
73 : propertyCache[i] = [[NSMutableDictionary alloc] initWithCapacity:OO_LIKELY_PROPERTIES_PER_SYSTEM];
74 : // hub count of 24 is considerably higher than occurs in
75 : // standard planetinfo
76 : neighbourCache[i] = [[NSMutableArray alloc] initWithCapacity:24];
77 : }
78 : propertiesInUse = [[NSMutableSet alloc] initWithCapacity:OO_LIKELY_PROPERTIES_PER_SYSTEM];
79 : scriptedChanges = [[NSMutableDictionary alloc] initWithCapacity:64];
80 : }
81 : return self;
82 : }
83 :
84 0 : - (void) dealloc
85 : {
86 : DESTROY(universalProperties);
87 : DESTROY(interstellarSpace);
88 : DESTROY(systemDescriptions);
89 : for (NSUInteger i=0;i<OO_SYSTEM_CACHE_LENGTH;i++)
90 : {
91 : DESTROY(propertyCache[i]);
92 : DESTROY(neighbourCache[i]);
93 : }
94 : DESTROY(propertiesInUse);
95 : DESTROY(scriptedChanges);
96 : [super dealloc];
97 : }
98 :
99 :
100 : - (void) buildRouteCache
101 : {
102 : NSUInteger i,j,k,jIndex,kIndex;
103 : // firstly, cache all coordinates
104 : for (i=0;i<OO_SYSTEM_CACHE_LENGTH;i++)
105 : {
106 : coordinatesCache[i] = PointFromString([propertyCache[i] oo_stringForKey:@"coordinates"]);
107 : }
108 : // now for each system find its neighbours
109 : for (i=0;i<OO_GALAXIES_AVAILABLE;i++)
110 : {
111 : // one galaxy at a time
112 : for (j=0;j<OO_SYSTEMS_PER_GALAXY;j++)
113 : {
114 : jIndex = j+(i*OO_SYSTEMS_PER_GALAXY);
115 : for (k=j+1;k<OO_SYSTEMS_PER_GALAXY;k++)
116 : {
117 : kIndex = k+(i*OO_SYSTEMS_PER_GALAXY);
118 : if (distanceBetweenPlanetPositions(coordinatesCache[jIndex].x,coordinatesCache[jIndex].y,coordinatesCache[kIndex].x,coordinatesCache[kIndex].y) <= MAX_JUMP_RANGE)
119 : {
120 : // arrays are of system number only
121 : [neighbourCache[jIndex] addObject:[NSNumber numberWithUnsignedInteger:k]];
122 : [neighbourCache[kIndex] addObject:[NSNumber numberWithUnsignedInteger:j]];
123 : }
124 : }
125 : }
126 : }
127 :
128 : }
129 :
130 :
131 : - (void) setUniversalProperties:(NSDictionary *)properties
132 : {
133 : [universalProperties addEntriesFromDictionary:properties];
134 : [propertiesInUse addObjectsFromArray:[properties allKeys]];
135 : for (NSUInteger i = 0; i<OO_SYSTEM_CACHE_LENGTH; i++)
136 : {
137 : [self updateCacheEntry:i];
138 : }
139 : }
140 :
141 :
142 : - (void) setInterstellarProperties:(NSDictionary *)properties
143 : {
144 : [self setProperties:properties inDescription:interstellarSpace];
145 : }
146 :
147 :
148 : - (void) setProperties:(NSDictionary *)properties forSystemKey:(NSString *)key
149 : {
150 : OOSystemDescriptionEntry *desc = [systemDescriptions objectForKey:key];
151 : if (desc == nil)
152 : {
153 : // create it
154 : desc = [[[OOSystemDescriptionEntry alloc] init] autorelease];
155 : [systemDescriptions setObject:desc forKey:key];
156 : }
157 : [self setProperties:properties inDescription:desc];
158 : [propertiesInUse addObjectsFromArray:[properties allKeys]];
159 :
160 : NSArray *tokens = ScanTokensFromString(key);
161 : if ([tokens count] == 2 && [tokens oo_unsignedIntegerAtIndex:0] < OO_GALAXIES_AVAILABLE && [tokens oo_unsignedIntegerAtIndex:1] < OO_SYSTEMS_PER_GALAXY)
162 : {
163 : OOGalaxyID g = [tokens oo_unsignedIntegerAtIndex:0];
164 : OOSystemID s = [tokens oo_unsignedIntegerAtIndex:1];
165 : NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
166 : if (index >= OO_SYSTEM_CACHE_LENGTH)
167 : {
168 : OOLog(@"system.description.error",@"'%@' is an invalid system key. This is an internal error. Please report it.",key);
169 : }
170 : else
171 : {
172 : [self updateCacheEntry:index];
173 : }
174 : }
175 : }
176 :
177 :
178 : - (void) setProperty:(NSString *)property forSystemKey:(NSString *)key andLayer:(OOSystemLayer)layer toValue:(id)value fromManifest:(NSString *)manifest
179 : {
180 : OOSystemDescriptionEntry *desc = [systemDescriptions objectForKey:key];
181 : if (desc == nil)
182 : {
183 : // create it
184 : desc = [[[OOSystemDescriptionEntry alloc] init] autorelease];
185 : [systemDescriptions setObject:desc forKey:key];
186 : }
187 : [desc setProperty:property forLayer:layer toValue:value];
188 : [propertiesInUse addObject:property];
189 :
190 : NSArray *tokens = ScanTokensFromString(key);
191 : if ([tokens count] == 2 && [tokens oo_unsignedIntegerAtIndex:0] < OO_GALAXIES_AVAILABLE && [tokens oo_unsignedIntegerAtIndex:1] < OO_SYSTEMS_PER_GALAXY)
192 : {
193 : [self saveScriptedChangeToProperty:property forSystemKey:key andLayer:layer toValue:value fromManifest:manifest];
194 :
195 : OOGalaxyID g = [tokens oo_unsignedIntegerAtIndex:0];
196 : OOSystemID s = [tokens oo_unsignedIntegerAtIndex:1];
197 : NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
198 : if (index >= OO_SYSTEM_CACHE_LENGTH)
199 : {
200 : OOLog(@"system.description.error",@"'%@' is an invalid system key. This is an internal error. Please report it.",key);
201 : }
202 : else
203 : {
204 : [self updateCacheEntry:index forProperty:property];
205 : }
206 : }
207 : // for interstellar updates, save but don't update cache
208 : else if ([tokens count] == 4 && [tokens oo_unsignedIntegerAtIndex:1] < OO_GALAXIES_AVAILABLE && [tokens oo_unsignedIntegerAtIndex:2] < OO_SYSTEMS_PER_GALAXY && [tokens oo_unsignedIntegerAtIndex:3] < OO_SYSTEMS_PER_GALAXY)
209 : {
210 : [self saveScriptedChangeToProperty:property forSystemKey:key andLayer:layer toValue:value fromManifest:manifest];
211 : }
212 : }
213 :
214 :
215 0 : - (void) saveScriptedChangeToProperty:(NSString *)property forSystemKey:(NSString *)key andLayer:(OOSystemLayer)layer toValue:(id)value fromManifest:(NSString *)manifest
216 : {
217 : // if OXP doesn't have a manifest, cancel saving the change
218 : if (manifest == nil)
219 : {
220 : return;
221 : }
222 : // OOLog(@"saving change",@"%@ %@ %@ %d",manifest,key,property,layer);
223 : NSArray *overrideKey = [NSArray arrayWithObjects:manifest,key,property,[[NSNumber numberWithInt:layer] stringValue],nil];
224 : // Obj-C copes with NSArray keys to dictionaries fine, but the
225 : // plist format doesn't, so they can't be saved.
226 : NSString *overrideKeyStr = [overrideKey componentsJoinedByString:kOOScriptedChangeJoiner];
227 : if (value != nil)
228 : {
229 : [scriptedChanges setObject:value forKey:overrideKeyStr];
230 : }
231 : else
232 : {
233 : [scriptedChanges removeObjectForKey:overrideKeyStr];
234 : }
235 : }
236 :
237 :
238 : - (void) importScriptedChanges:(NSDictionary *)scripted
239 : {
240 : NSArray *key = nil;
241 : NSString *keyStr = nil;
242 : NSString *manifest = nil;
243 : foreachkey(keyStr, scripted)
244 : {
245 : key = [keyStr componentsSeparatedByString:kOOScriptedChangeJoiner];
246 : if ([key count] == 4)
247 : {
248 : manifest = [key oo_stringAtIndex:0];
249 : if ([ResourceManager manifestForIdentifier:manifest] != nil)
250 : {
251 : // OOLog(@"importing",@"%@ -> %@",keyStr,[scripted objectForKey:keyStr]);
252 : [self setProperty:[key oo_stringAtIndex:2]
253 : forSystemKey:[key oo_stringAtIndex:1]
254 : andLayer:[key oo_intAtIndex:3]
255 : toValue:[scripted objectForKey:keyStr]
256 : fromManifest:manifest];
257 : // and doing this set stores it into the manager's copy
258 : // of scripted changes
259 : // this means in theory we could import more than one
260 : }
261 : // else OXP not installed, do not load
262 : }
263 : else
264 : {
265 : OOLog(@"systemManager.import",@"Key '%@' has unexpected format - skipping",keyStr);
266 : }
267 : }
268 :
269 : }
270 :
271 :
272 : // import of the old local_planetinfo_overrides dictionary
273 : - (void) importLegacyScriptedChanges:(NSDictionary *)scripted
274 : {
275 : NSString *systemKey = nil;
276 : NSString *propertyKey = nil;
277 : NSString *defaultManifest = @"org.oolite.oolite";
278 :
279 : foreachkey(systemKey,scripted)
280 : {
281 : NSDictionary *legacyChanges = [scripted oo_dictionaryForKey:systemKey];
282 : if ([legacyChanges objectForKey:@"sun_gone_nova"] != nil)
283 : {
284 : // then this is a change to import even if we don't know
285 : // if the OXP is still installed
286 : foreachkey (propertyKey, legacyChanges)
287 : {
288 : [self setProperty:propertyKey
289 : forSystemKey:systemKey
290 : andLayer:OO_LAYER_OXP_DYNAMIC
291 : toValue:[legacyChanges objectForKey:propertyKey]
292 : fromManifest:defaultManifest];
293 : }
294 : /* Fix for older savegames not having a larger sun radius
295 : * property set from the Nova mission. */
296 : id sr = [self getProperty:@"sun_radius" forSystemKey:systemKey];
297 : float sr_num = [sr floatValue];
298 : if (sr_num < 600000) {
299 : // fix sun radius values
300 : [self setProperty:@"sun_radius"
301 : forSystemKey:systemKey
302 : andLayer:OO_LAYER_OXP_DYNAMIC
303 : toValue:[NSNumber numberWithFloat:sr_num+600000.0f]
304 : fromManifest:defaultManifest];
305 : }
306 : }
307 : }
308 :
309 : }
310 :
311 :
312 : - (NSDictionary *) exportScriptedChanges
313 : {
314 : return [[scriptedChanges copy] autorelease];
315 : }
316 :
317 :
318 : - (NSDictionary *) getPropertiesForCurrentSystem
319 : {
320 : OOSystemID s = [UNIVERSE currentSystemID];
321 : if (s >= 0)
322 : {
323 : NSUInteger index = ([PLAYER galaxyNumber] * OO_SYSTEMS_PER_GALAXY) + s;
324 : if (index >= OO_SYSTEM_CACHE_LENGTH)
325 : {
326 : OOLog(@"system.description.error",@"'%lu' is an invalid system index for the current system. This is an internal error. Please report it.",index);
327 : return [NSDictionary dictionary];
328 : }
329 : return propertyCache[index];
330 : }
331 : else
332 : {
333 : OOLog(@"system.description.error", @"%@", @"getPropertiesForCurrentSystem called while player in interstellar space. This is an internal error. Please report it.");
334 : // this shouldn't be called for interstellar space
335 : return [NSDictionary dictionary];
336 : }
337 : }
338 :
339 :
340 : - (NSDictionary *) getPropertiesForSystemKey:(NSString *)key
341 : {
342 : NSArray *tokens = ScanTokensFromString(key);
343 : if ([tokens count] == 2 && [tokens oo_unsignedIntegerAtIndex:0] < OO_GALAXIES_AVAILABLE && [tokens oo_unsignedIntegerAtIndex:1] < OO_SYSTEMS_PER_GALAXY)
344 : {
345 : OOGalaxyID g = [tokens oo_unsignedIntegerAtIndex:0];
346 : OOSystemID s = [tokens oo_unsignedIntegerAtIndex:1];
347 : NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
348 : if (index >= OO_SYSTEM_CACHE_LENGTH)
349 : {
350 : OOLog(@"system.description.error",@"'%@' is an invalid system key. This is an internal error. Please report it.",key);
351 : return [NSDictionary dictionary];
352 : }
353 : return propertyCache[index];
354 : }
355 : // interstellar spaces aren't cached
356 : return [self calculatePropertiesForSystemKey:key];
357 : }
358 :
359 :
360 : - (NSDictionary *) getPropertiesForSystem:(OOSystemID)s inGalaxy:(OOGalaxyID)g
361 : {
362 : NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
363 : if (index >= OO_SYSTEM_CACHE_LENGTH)
364 : {
365 : OOLog(@"system.description.error",@"'%u, %u' is an invalid system. This is an internal error. Please report it.",g,s);
366 : return [NSDictionary dictionary];
367 : }
368 : return propertyCache[index];
369 : }
370 :
371 :
372 : - (id) getProperty:(NSString *)property forSystemKey:(NSString *)key
373 : {
374 : return [self getProperty:property forSystemKey:key withUniversal:YES];
375 : }
376 :
377 : - (id) getProperty:(NSString *)property forSystem:(OOSystemID)s inGalaxy:(OOGalaxyID)g
378 : {
379 : if (s < 0)
380 : {
381 : OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
382 : return nil;
383 : }
384 : NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
385 : if (index >= OO_SYSTEM_CACHE_LENGTH)
386 : {
387 : OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
388 : return nil;
389 : }
390 : return [propertyCache[index] objectForKey:property];
391 : }
392 :
393 :
394 0 : - (id) getProperty:(NSString *)property forSystemKey:(NSString *)key withUniversal:(BOOL)universal
395 : {
396 : OOSystemDescriptionEntry *desc = nil;
397 : if (EXPECT_NOT([key isEqualToString:@"interstellar"]))
398 : {
399 : desc = interstellarSpace;
400 : }
401 : else
402 : {
403 : desc = [systemDescriptions objectForKey:key];
404 : }
405 : if (desc == nil)
406 : {
407 : return nil;
408 : }
409 : id result = nil;
410 : result = [desc getProperty:property forLayer:OO_LAYER_OXP_PRIORITY];
411 : if (result == nil)
412 : {
413 : result = [desc getProperty:property forLayer:OO_LAYER_OXP_DYNAMIC];
414 : }
415 : if (result == nil)
416 : {
417 : result = [desc getProperty:property forLayer:OO_LAYER_OXP_STATIC];
418 : }
419 : if (result == nil && universal)
420 : {
421 : result = [universalProperties objectForKey:property];
422 : }
423 : if (result == nil)
424 : {
425 : result = [desc getProperty:property forLayer:OO_LAYER_CORE];
426 : }
427 : return result;
428 : }
429 :
430 :
431 0 : - (id) getProperty:(NSString *)property1 orProperty:(NSString *)property2 forSystemKey:(NSString *)key withUniversal:(BOOL)universal
432 : {
433 : OOSystemDescriptionEntry *desc = [systemDescriptions objectForKey:key];
434 : if (desc == nil)
435 : {
436 : return nil;
437 : }
438 : id result = nil;
439 : result = [desc getProperty:property1 forLayer:OO_LAYER_OXP_PRIORITY];
440 : if (result == nil)
441 : {
442 : result = [desc getProperty:property2 forLayer:OO_LAYER_OXP_PRIORITY];
443 : }
444 : if (result == nil)
445 : {
446 : result = [desc getProperty:property1 forLayer:OO_LAYER_OXP_DYNAMIC];
447 : }
448 : if (result == nil)
449 : {
450 : result = [desc getProperty:property2 forLayer:OO_LAYER_OXP_DYNAMIC];
451 : }
452 : if (result == nil)
453 : {
454 : result = [desc getProperty:property1 forLayer:OO_LAYER_OXP_STATIC];
455 : }
456 : if (result == nil)
457 : {
458 : result = [desc getProperty:property2 forLayer:OO_LAYER_OXP_STATIC];
459 : }
460 : if (universal)
461 : {
462 : if (result == nil)
463 : {
464 : result = [universalProperties objectForKey:property1];
465 : }
466 : if (result == nil)
467 : {
468 : result = [universalProperties objectForKey:property2];
469 : }
470 : }
471 : if (result == nil)
472 : {
473 : result = [desc getProperty:property1 forLayer:OO_LAYER_CORE];
474 : }
475 : if (result == nil)
476 : {
477 : result = [desc getProperty:property2 forLayer:OO_LAYER_CORE];
478 : }
479 : return result;
480 : }
481 :
482 :
483 0 : - (void) setProperties:(NSDictionary *)properties inDescription:(OOSystemDescriptionEntry *)desc
484 : {
485 : OOSystemLayer layer = [properties oo_unsignedIntForKey:kOOSystemLayerProperty defaultValue:OO_LAYER_OXP_STATIC];
486 : if (layer > OO_LAYER_OXP_PRIORITY)
487 : {
488 : OOLog(@"system.description.error",@"Layer %u is not a valid layer number in system information.",layer);
489 : layer = OO_LAYER_OXP_PRIORITY;
490 : }
491 : NSString *key = nil;
492 : foreachkey(key, properties)
493 : {
494 : if (![key isEqualToString:kOOSystemLayerProperty])
495 : {
496 : [propertiesInUse addObject:key];
497 : [desc setProperty:key forLayer:layer toValue:[properties objectForKey:key]];
498 : }
499 : }
500 : }
501 :
502 :
503 0 : - (NSDictionary *) calculatePropertiesForSystemKey:(NSString *)key
504 : {
505 : NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:OO_LIKELY_PROPERTIES_PER_SYSTEM];
506 : NSString *property = nil;
507 : id val = nil;
508 : BOOL interstellar = [key hasPrefix:@"interstellar:"];
509 : foreach (property, propertiesInUse)
510 : {
511 : // don't use universal properties on interstellar specific regions
512 : val = [self getProperty:property forSystemKey:key withUniversal:!interstellar];
513 :
514 : if (val != nil)
515 : {
516 : [dict setObject:val forKey:property];
517 : }
518 : else if (interstellar)
519 : {
520 : // interstellar is always overridden by specific regions
521 : // universal properties for interstellar get picked up here
522 : val = [self getProperty:property forSystemKey:@"interstellar"];
523 : if (val != nil)
524 : {
525 : [dict setObject:val forKey:property];
526 : }
527 : }
528 : }
529 : return dict;
530 : }
531 :
532 :
533 0 : - (void) updateCacheEntry:(NSUInteger)i
534 : {
535 : NSAssert(i < OO_SYSTEM_CACHE_LENGTH,@"Invalid cache entry number");
536 : NSString *key = [NSString stringWithFormat:@"%lu %lu",i/OO_SYSTEMS_PER_GALAXY,i%OO_SYSTEMS_PER_GALAXY];
537 : NSDictionary *current = [self calculatePropertiesForSystemKey:key];
538 :
539 : [propertyCache[i] removeAllObjects];
540 : [propertyCache[i] addEntriesFromDictionary:current];
541 : }
542 :
543 :
544 0 : - (void) updateCacheEntry:(NSUInteger)i forProperty:(NSString *)property
545 : {
546 : NSAssert(i < OO_SYSTEM_CACHE_LENGTH,@"Invalid cache entry number");
547 : NSString *key = [NSString stringWithFormat:@"%lu %lu",i/OO_SYSTEMS_PER_GALAXY,i%OO_SYSTEMS_PER_GALAXY];
548 : id current = [self getProperty:property forSystemKey:key];
549 : if (current == nil)
550 : {
551 : [propertyCache[i] removeObjectForKey:property];
552 : }
553 : else
554 : {
555 : [propertyCache[i] setObject:current forKey:property];
556 : }
557 : }
558 :
559 :
560 : - (NSPoint) getCoordinatesForSystem:(OOSystemID)s inGalaxy:(OOGalaxyID)g
561 : {
562 : if (s < 0)
563 : {
564 : OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
565 : return (NSPoint){0,0};
566 : }
567 : NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
568 : if (index >= OO_SYSTEM_CACHE_LENGTH)
569 : {
570 : OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
571 : return (NSPoint){0,0};
572 : }
573 : return coordinatesCache[index];
574 : }
575 :
576 :
577 : - (NSArray *) getNeighbourIDsForSystem:(OOSystemID)s inGalaxy:(OOGalaxyID)g
578 : {
579 : if (s < 0)
580 : {
581 : OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
582 : return nil;
583 : }
584 : NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
585 : if (index >= OO_SYSTEM_CACHE_LENGTH)
586 : {
587 : OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
588 : return nil;
589 : }
590 :
591 : return neighbourCache[index];
592 : }
593 :
594 :
595 : - (Random_Seed) getRandomSeedForCurrentSystem
596 : {
597 : if ([UNIVERSE currentSystemID] < 0)
598 : {
599 : return kNilRandomSeed;
600 : }
601 : else
602 : {
603 : OOSystemID s = [UNIVERSE currentSystemID];
604 : NSUInteger index = ([PLAYER galaxyNumber] * OO_SYSTEMS_PER_GALAXY) + s;
605 : if (index >= OO_SYSTEM_CACHE_LENGTH)
606 : {
607 : OOLog(@"system.description.error",@"'%lu' is an invalid system index for the current system. This is an internal error. Please report it.",(unsigned long)index);
608 : return kNilRandomSeed;
609 : }
610 : return RandomSeedFromString([propertyCache[index] oo_stringForKey:@"random_seed"]);
611 : }
612 : }
613 :
614 :
615 : - (Random_Seed) getRandomSeedForSystem:(OOSystemID)s inGalaxy:(OOGalaxyID)g
616 : {
617 : if (s < 0)
618 : {
619 : OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
620 : return kNilRandomSeed;
621 : }
622 : NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
623 : if (index >= OO_SYSTEM_CACHE_LENGTH)
624 : {
625 : OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
626 : return kNilRandomSeed;
627 : }
628 : return RandomSeedFromString([propertyCache[index] oo_stringForKey:@"random_seed"]);
629 : }
630 :
631 :
632 : @end
633 :
634 :
635 : @interface OOSystemDescriptionEntry (OOPrivate)
636 0 : - (id) validateProperty:(NSString *)property withValue:(id)value;
637 : @end
638 :
639 : @implementation OOSystemDescriptionEntry
640 :
641 0 : - (id) init
642 : {
643 : self = [super init];
644 : if (self != nil)
645 : {
646 : for (NSUInteger i=0;i<OO_SYSTEM_LAYERS;i++)
647 : {
648 : layers[i] = [[NSMutableDictionary alloc] initWithCapacity:OO_LIKELY_PROPERTIES_PER_SYSTEM];
649 : }
650 : }
651 : return self;
652 : }
653 :
654 :
655 0 : - (void) dealloc
656 : {
657 : for (NSUInteger i=0;i<OO_SYSTEM_LAYERS;i++)
658 : {
659 : DESTROY(layers[i]);
660 : }
661 : [super dealloc];
662 : }
663 :
664 :
665 : - (void) setProperty:(NSString *)property forLayer:(OOSystemLayer)layer toValue:(id)value
666 : {
667 : if (value == nil)
668 : {
669 : [layers[layer] removeObjectForKey:property];
670 : }
671 : else
672 : {
673 : // validate type of object for certain properties
674 : value = [self validateProperty:property withValue:value];
675 : // if it's nil now, validation failed and could not be recovered
676 : // so don't actually set anything
677 : if (value != nil)
678 : {
679 : [layers[layer] setObject:value forKey:property];
680 : }
681 : }
682 : }
683 :
684 :
685 : - (id) getProperty:(NSString *)property forLayer:(OOSystemLayer)layer
686 : {
687 : return [layers[layer] objectForKey:property];
688 : }
689 :
690 :
691 : /* Mostly the rest of the game gets a system dictionary from
692 : * [UNIVERSE currentSystemData] or similar, which means that it uses
693 : * safe methods like oo_stringForKey: - a few things use a direct call
694 : * to getProperty for various reasons, so need some type validation
695 : * here instead. */
696 0 : - (id) validateProperty:(NSString *)property withValue:(id)value
697 : {
698 : if ([property isEqualToString:@"coordinates"])
699 : {
700 : // must be a string with two numbers in it
701 : // TODO: convert two element arrays
702 : if (![value isKindOfClass:[NSString class]])
703 : {
704 : OOLog(@"system.description.error",@"'%@' is not a valid format for coordinates",value);
705 : return nil;
706 : }
707 : NSArray *tokens = ScanTokensFromString((NSString *)value);
708 : if ([tokens count] != 2)
709 : {
710 : OOLog(@"system.description.error",@"'%@' is not a valid format for coordinates (must have exactly two numbers)",value);
711 : return nil;
712 : }
713 : }
714 : else if ([property isEqualToString:@"radius"] || [property isEqualToString:@"government"])
715 : {
716 : // read in a context which expects a string, but it's a string representation of a number
717 : if (![value isKindOfClass:[NSString class]])
718 : {
719 : if ([value isKindOfClass:[NSNumber class]])
720 : {
721 : return [value stringValue];
722 : }
723 : else
724 : {
725 : OOLog(@"system.description.error",@"'%@' is not a valid value for '%@' (string required)",value,property);
726 : return nil;
727 : }
728 : }
729 : }
730 : else if ([property isEqualToString:@"inhabitant"] || [property isEqualToString:@"inhabitants"] || [property isEqualToString:@"name"] )
731 : {
732 : // read in a context which expects a string
733 : if (![value isKindOfClass:[NSString class]])
734 : {
735 : OOLog(@"system.description.error",@"'%@' is not a valid value for '%@' (string required)",value,property);
736 : return nil;
737 : }
738 : }
739 :
740 : return value;
741 : }
742 :
743 : @end
|