Line data Source code
1 0 : /*
2 :
3 : OOCharacter.m
4 :
5 : Oolite
6 : Copyright (C) 2004-2013 Giles C Williams and contributors
7 :
8 : This program is free software; you can redistribute it and/or
9 : modify it under the terms of the GNU General Public License
10 : as published by the Free Software Foundation; either version 2
11 : of the License, or (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program; if not, write to the Free Software
20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 : MA 02110-1301, USA.
22 :
23 : */
24 :
25 : #import "OOCharacter.h"
26 :
27 : #import "Universe.h"
28 : #import "OOStringExpander.h"
29 : #import "OOStringParsing.h"
30 : #import "OOCollectionExtractors.h"
31 : #import "OOJSScript.h"
32 :
33 :
34 : @interface OOCharacter (Private)
35 :
36 0 : - (id) initWithGenSeed:(Random_Seed)characterSeed andOriginalSystem:(OOSystemID)systemSeed;
37 0 : - (void) setCharacterFromDictionary:(NSDictionary *)dict;
38 :
39 0 : - (void)setOriginSystem:(OOSystemID)value;
40 0 : - (Random_Seed)genSeed;
41 :
42 : @end
43 :
44 :
45 : @implementation OOCharacter
46 :
47 0 : - (NSString *) descriptionComponents
48 : {
49 : return [NSString stringWithFormat:@"%@, %@. bounty: %i insurance: %llu", [self name], [self shortDescription], [self legalStatus], [self insuranceCredits]];
50 : }
51 :
52 :
53 0 : - (NSString *) oo_jsClassName
54 : {
55 : return @"Character";
56 : }
57 :
58 :
59 0 : - (void) dealloc
60 : {
61 : [_name release];
62 : [_shortDescription release];
63 : [_scriptActions release];
64 : DESTROY(_script);
65 :
66 : [super dealloc];
67 : }
68 :
69 :
70 0 : - (id) initWithGenSeed:(Random_Seed)characterSeed andOriginalSystem:(OOSystemID)system
71 : {
72 : if ((self = [super init]))
73 : {
74 : // do character set-up
75 : _genSeed = characterSeed;
76 : _originSystem = system;
77 :
78 : [self basicSetUp];
79 : }
80 : return self;
81 : }
82 :
83 :
84 : - (id) initWithRole:(NSString *)role andOriginalSystem:(OOSystemID)system
85 : {
86 : Random_Seed seed;
87 : make_pseudo_random_seed(&seed);
88 :
89 : if ((self = [self initWithGenSeed:seed andOriginalSystem:system]))
90 : {
91 : [self castInRole:role];
92 : }
93 :
94 : return self;
95 : }
96 :
97 : + (OOCharacter *) characterWithRole:(NSString *)role andOriginalSystem:(OOSystemID)system
98 : {
99 : return [[[self alloc] initWithRole:role andOriginalSystem:system] autorelease];
100 : }
101 :
102 :
103 : + (OOCharacter *) randomCharacterWithRole:(NSString *)role andOriginalSystem:(OOSystemID)system
104 : {
105 : Random_Seed seed;
106 :
107 : seed.a = (Ranrot() & 0xff);
108 : seed.b = (Ranrot() & 0xff);
109 : seed.c = (Ranrot() & 0xff);
110 : seed.d = (Ranrot() & 0xff);
111 : seed.e = (Ranrot() & 0xff);
112 : seed.f = (Ranrot() & 0xff);
113 :
114 : OOCharacter *character = [[[OOCharacter alloc] initWithGenSeed:seed andOriginalSystem:system] autorelease];
115 : [character castInRole:role];
116 :
117 : return character;
118 : }
119 :
120 :
121 : + (OOCharacter *) characterWithDictionary:(NSDictionary *)dict
122 : {
123 : OOCharacter *character = [[[OOCharacter alloc] init] autorelease];
124 : [character setCharacterFromDictionary:dict];
125 :
126 : return character;
127 : }
128 :
129 :
130 : - (NSString *) planetOfOrigin
131 : {
132 : // determine the planet of origin
133 : NSDictionary *originInfo = [UNIVERSE generateSystemData:[self planetIDOfOrigin]];
134 : return [originInfo objectForKey:KEY_NAME];
135 : }
136 :
137 :
138 : - (OOSystemID) planetIDOfOrigin
139 : {
140 : // determine the planet of origin
141 : return _originSystem;
142 : }
143 :
144 :
145 : - (NSString *) species
146 : {
147 : // determine the character's species
148 : int species = [self genSeed].f & 0x03; // 0-1 native to home system, 2 human colonial, 3 other
149 : NSString* speciesString = nil;
150 : if (species == 3) speciesString = [UNIVERSE getSystemInhabitants:[self genSeed].e plural:NO];
151 : else speciesString = [UNIVERSE getSystemInhabitants:[self planetIDOfOrigin] plural:NO];
152 :
153 : if (![[UNIVERSE descriptions] oo_boolForKey:@"lowercase_ignore"])
154 : {
155 : speciesString = [speciesString lowercaseString];
156 : }
157 :
158 : return [speciesString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
159 : }
160 :
161 :
162 : - (void) basicSetUp
163 : {
164 : // save random seeds for restoration later
165 : RNG_Seed savedRNGSeed = currentRandomSeed();
166 : RANROTSeed savedRANROTSeed = RANROTGetFullSeed();
167 : // set RNG to character seed
168 : Random_Seed genSeed = [self genSeed];
169 : seed_for_planet_description(genSeed);
170 :
171 : // determine the planet of origin
172 : NSDictionary *originInfo = [UNIVERSE generateSystemData:[self planetIDOfOrigin]];
173 : NSString *planet = [originInfo oo_stringForKey:KEY_NAME];
174 : OOGovernmentID government = [originInfo oo_intForKey:KEY_GOVERNMENT]; // 0 .. 7 (0 anarchic .. 7 most stable)
175 : int criminalTendency = government ^ 0x07;
176 :
177 : // determine the character's species
178 : NSString *species = [self species];
179 :
180 : // determine the character's name
181 : seed_RNG_only_for_planet_description(genSeed);
182 : NSString *genName = nil;
183 : if ([species hasPrefix:@"human"])
184 : {
185 : genName = [NSString stringWithFormat:@"%@ %@", OOExpandWithSeed(genSeed, @"%R"), OOExpandKeyWithSeed(genSeed, @"nom")];
186 : } else {
187 : /* NOTE: we can't use "%R %R" because that will produce the same string
188 : twice. TODO: is there a reason not to use %N and kOOExpandGoodRNG
189 : here? Is there some context where we rely on being able to get the
190 : same name for a given genSeed?
191 : */
192 : genName = [NSString stringWithFormat:@"%@ %@", OOExpandWithSeed(genSeed, @"%R"), OOExpandWithSeed(genSeed, @"%R")];
193 : }
194 : [self setName:genName];
195 :
196 : [self setShortDescription:OOExpandKeyWithSeed(genSeed, @"character-generic-description", species, planet)];
197 :
198 : // determine _legalStatus for a completely random character
199 : [self setLegalStatus:0]; // clean
200 : int legalIndex = gen_rnd_number() & gen_rnd_number() & 0x03;
201 : while (((gen_rnd_number() & 0xf) < criminalTendency) && (legalIndex < 3))
202 : {
203 : legalIndex++;
204 : }
205 : if (legalIndex == 3)
206 : {
207 : // criminal
208 : [self setLegalStatus:criminalTendency + criminalTendency * (gen_rnd_number() & 0x03) + (gen_rnd_number() & gen_rnd_number() & 0x7f)];
209 : }
210 : legalIndex = 0;
211 : if (_legalStatus > 0) legalIndex = (_legalStatus <= 50) ? 1 : 2;
212 :
213 : // if clean - determine insurance level (if any)
214 : [self setInsuranceCredits:0];
215 : if (legalIndex == 0)
216 : {
217 : int insuranceIndex = gen_rnd_number() & gen_rnd_number() & 0x03;
218 : switch (insuranceIndex)
219 : {
220 : case 1:
221 : [self setInsuranceCredits:125];
222 : break;
223 : case 2:
224 : [self setInsuranceCredits:250];
225 : break;
226 : case 3:
227 : [self setInsuranceCredits:500];
228 : }
229 : }
230 :
231 : // restore random seed
232 : setRandomSeed( savedRNGSeed);
233 : RANROTSetFullSeed(savedRANROTSeed);
234 : }
235 :
236 :
237 : - (BOOL) castInRole:(NSString *)role
238 : {
239 : BOOL specialSetUpDone = NO;
240 :
241 : role = [role lowercaseString];
242 : if ([role hasPrefix:@"pirate"])
243 : {
244 : // determine _legalStatus for a completely random character
245 : Random_Seed genSeed = [self genSeed];
246 : int sins = 0x08 | (genSeed.a & genSeed.b);
247 : [self setLegalStatus:sins & 0x7f];
248 :
249 : specialSetUpDone = YES;
250 : }
251 : else if ([role hasPrefix:@"trader"])
252 : {
253 : [self setLegalStatus:0]; // clean
254 :
255 : int insuranceIndex = gen_rnd_number() & 0x03;
256 : switch (insuranceIndex)
257 : {
258 : case 0:
259 : [self setInsuranceCredits:0];
260 : break;
261 : case 1:
262 : [self setInsuranceCredits:125];
263 : break;
264 : case 2:
265 : [self setInsuranceCredits:250];
266 : break;
267 : case 3:
268 : [self setInsuranceCredits:500];
269 : }
270 : specialSetUpDone = YES;
271 : }
272 : else if ([role hasPrefix:@"hunter"])
273 : {
274 : [self setLegalStatus:0]; // clean
275 : int insuranceIndex = gen_rnd_number() & 0x03;
276 : if (insuranceIndex == 3)
277 : [self setInsuranceCredits:500];
278 : specialSetUpDone = YES;
279 : }
280 : else if ([role hasPrefix:@"police"])
281 : {
282 : [self setLegalStatus:0]; // clean
283 : [self setInsuranceCredits:125];
284 : specialSetUpDone = YES;
285 : }
286 : else if ([role isEqual:@"miner"])
287 : {
288 : [self setLegalStatus:0]; // clean
289 : [self setInsuranceCredits:25];
290 : specialSetUpDone = YES;
291 : }
292 : else if ([role isEqual:@"passenger"])
293 : {
294 : [self setLegalStatus:0]; // clean
295 : int insuranceIndex = gen_rnd_number() & 0x03;
296 : switch (insuranceIndex)
297 : {
298 : case 0:
299 : [self setInsuranceCredits:25];
300 : break;
301 : case 1:
302 : [self setInsuranceCredits:125];
303 : break;
304 : case 2:
305 : [self setInsuranceCredits:250];
306 : break;
307 : case 3:
308 : [self setInsuranceCredits:500];
309 : }
310 : specialSetUpDone = YES;
311 : }
312 : else if ([role isEqual:@"slave"])
313 : {
314 : [self setLegalStatus:0]; // clean
315 : [self setInsuranceCredits:0];
316 : specialSetUpDone = YES;
317 : }
318 : else if ([role isEqual:@"thargoid"])
319 : {
320 : [self setLegalStatus:100];
321 : [self setInsuranceCredits:0];
322 : [self setName:DESC(@"character-thargoid-name")];
323 : [self setShortDescription:DESC(@"character-a-thargoid")];
324 : specialSetUpDone = YES;
325 : }
326 :
327 : // do long description here
328 :
329 : return specialSetUpDone;
330 : }
331 :
332 :
333 : - (NSString *)name
334 : {
335 : return _name;
336 : }
337 :
338 :
339 : - (NSString *)shortDescription
340 : {
341 : return _shortDescription;
342 : }
343 :
344 :
345 0 : - (Random_Seed)genSeed
346 : {
347 : return _genSeed;
348 : }
349 :
350 :
351 : - (int)legalStatus
352 : {
353 : return _legalStatus;
354 : }
355 :
356 :
357 : - (OOCreditsQuantity)insuranceCredits
358 : {
359 : return _insuranceCredits;
360 : }
361 :
362 :
363 : - (NSArray *)legacyScript
364 : {
365 : return _scriptActions;
366 : }
367 :
368 :
369 : - (NSDictionary *) infoForScripting
370 : {
371 : return [NSDictionary dictionaryWithObjectsAndKeys:
372 : [self name], @"name",
373 : [self shortDescription], @"description",
374 : [self species], @"species",
375 : [NSNumber numberWithInt:[self legalStatus]], @"legalStatus",
376 : [NSNumber numberWithUnsignedLongLong:[self insuranceCredits]], @"insuranceCredits",
377 : [NSNumber numberWithInt:[self planetIDOfOrigin]], @"homeSystem",
378 : nil];
379 : }
380 :
381 :
382 : - (void)setName:(NSString *)value
383 : {
384 : [_name autorelease];
385 : _name = [value copy];
386 : }
387 :
388 :
389 : - (void)setShortDescription:(NSString *)value
390 : {
391 : [_shortDescription autorelease];
392 : _shortDescription = [value copy];
393 : }
394 :
395 :
396 0 : - (void)setOriginSystem:(OOSystemID)value
397 : {
398 : _originSystem = value;
399 : }
400 :
401 :
402 0 : - (void)setGenSeed:(Random_Seed)value
403 : {
404 : _genSeed = value;
405 : }
406 :
407 :
408 : - (void)setLegalStatus:(int)value
409 : {
410 : _legalStatus = value;
411 : }
412 :
413 :
414 : - (void)setInsuranceCredits:(OOCreditsQuantity)value
415 : {
416 : _insuranceCredits = value;
417 : }
418 :
419 :
420 : - (void)setLegacyScript:(NSArray *)some_actions
421 : {
422 : [_scriptActions autorelease];
423 : _scriptActions = [some_actions copy];
424 : }
425 :
426 :
427 : - (OOJSScript *)script
428 : {
429 : return _script;
430 : }
431 :
432 :
433 : - (void) setCharacterScript:(NSString *)scriptName
434 : {
435 : [_script autorelease];
436 : _script = [OOScript jsScriptFromFileNamed:scriptName
437 : properties:[NSDictionary dictionaryWithObject:self forKey:@"character"]];
438 : [_script retain];
439 : }
440 :
441 :
442 : - (void) doScriptEvent:(jsid)message
443 : {
444 : JSContext *context = OOJSAcquireContext();
445 : [_script callMethod:message inContext:context withArguments:NULL count:0 result:NULL];
446 : OOJSRelinquishContext(context);
447 : }
448 :
449 :
450 0 : - (void) setCharacterFromDictionary:(NSDictionary *)dict
451 : {
452 : id origin = nil;
453 : Random_Seed seed;
454 :
455 : origin = [dict objectForKey:@"origin"];
456 : if ([origin isKindOfClass:[NSNumber class]] ||
457 : ([origin respondsToSelector:@selector(intValue)] && ([origin intValue] != 0 || [origin isEqual:@"0"])))
458 : {
459 : // Number or numerical string
460 : [self setOriginSystem:[origin intValue]];
461 : }
462 : else if ([origin isKindOfClass:[NSString class]])
463 : {
464 : OOSystemID sys = [UNIVERSE findSystemFromName:origin];
465 : if (sys < 0)
466 : {
467 : OOLogERR(@"character.load.unknownSystem", @"could not find a system named '%@' in this galaxy.", origin);
468 : [self setOriginSystem:(ranrot_rand() & 0xff)];
469 : }
470 : else
471 : {
472 : [self setOriginSystem:sys];
473 : }
474 : }
475 : else
476 : {
477 : // no origin defined, select one at random.
478 : [self setOriginSystem:(ranrot_rand() & 0xff)];
479 : }
480 :
481 : if ([dict objectForKey:@"random_seed"])
482 : {
483 : seed = RandomSeedFromString([dict oo_stringForKey:@"random_seed"]); // returns kNilRandomSeed on failure
484 : }
485 : else
486 : {
487 : seed.a = (ranrot_rand() & 0xff);
488 : seed.b = (ranrot_rand() & 0xff);
489 : seed.c = (ranrot_rand() & 0xff);
490 : seed.d = (ranrot_rand() & 0xff);
491 : seed.e = (ranrot_rand() & 0xff);
492 : seed.f = (ranrot_rand() & 0xff);
493 : }
494 : [self setGenSeed:seed];
495 : [self basicSetUp];
496 :
497 : if ([dict oo_stringForKey:@"role"]) [self castInRole:[dict oo_stringForKey:@"role"]];
498 : if ([dict oo_stringForKey:@"name"]) [self setName:[dict oo_stringForKey:@"name"]];
499 : if ([dict oo_stringForKey:@"short_description"]) [self setShortDescription:[dict oo_stringForKey:@"short_description"]];
500 : if ([dict objectForKey:@"legal_status"]) [self setLegalStatus:[dict oo_intForKey:@"legal_status"]];
501 : if ([dict objectForKey:@"bounty"]) [self setLegalStatus:[dict oo_intForKey:@"bounty"]];
502 : if ([dict objectForKey:@"insurance"]) [self setInsuranceCredits:[dict oo_unsignedLongLongForKey:@"insurance"]];
503 : if ([dict oo_stringForKey:@"script"]) [self setCharacterScript:[dict oo_stringForKey:@"script"]];
504 : if ([dict oo_arrayForKey:@"script_actions"]) [self setLegacyScript:[dict oo_arrayForKey:@"script_actions"]];
505 :
506 : }
507 :
508 : @end
|