Line data Source code
1 0 : /*
2 :
3 : PlayerEntityLegacyScriptEngine.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 "PlayerEntityLegacyScriptEngine.h"
26 : #import "PlayerEntityScriptMethods.h"
27 : #import "PlayerEntitySound.h"
28 : #import "PlayerEntityContracts.h"
29 : #import "GuiDisplayGen.h"
30 : #import "Universe.h"
31 : #import "ResourceManager.h"
32 : #import "AI.h"
33 : #import "ShipEntityAI.h"
34 : #import "ShipEntityScriptMethods.h"
35 : #import "OOScript.h"
36 : #import "OOMusicController.h"
37 : #import "OOColor.h"
38 : #import "OOStringParsing.h"
39 : #import "OOStringExpander.h"
40 : #import "OOConstToString.h"
41 : #import "OOTexture.h"
42 : #import "OOCollectionExtractors.h"
43 : #import "OOLoggingExtended.h"
44 : #import "OOSound.h"
45 : #import "OOSunEntity.h"
46 : #import "OOPlanetEntity.h"
47 : #import "OOPlanetEntity.h"
48 : #import "StationEntity.h"
49 : #import "Comparison.h"
50 : #import "OOLegacyScriptWhitelist.h"
51 : #import "OOJavaScriptEngine.h"
52 : #import "OOEquipmentType.h"
53 : #import "HeadUpDisplay.h"
54 : #import "OOSystemDescriptionManager.h"
55 : #import "OOEntityFilterPredicate.h"
56 :
57 :
58 0 : static NSString * const kOOLogScriptAddShipsFailed = @"script.addShips.failed";
59 0 : static NSString * const kOOLogScriptMissionDescNoText = @"script.missionDescription.noMissionText";
60 0 : static NSString * const kOOLogScriptMissionDescNoKey = @"script.missionDescription.noMissionKey";
61 :
62 0 : static NSString * const kOOLogDebugOnMetaClass = @"$scriptDebugOn";
63 0 : static NSString * const kOOLogDebugMessage = @"script.debug.message";
64 0 : static NSString * const kOOLogDebugOnOff = @"script.debug.onOff";
65 0 : static NSString * const kOOLogDebugAddPlanet = @"script.debug.addPlanet";
66 0 : static NSString * const kOOLogDebugReplaceVariablesInString = @"script.debug.replaceVariablesInString";
67 0 : static NSString * const kOOLogDebugProcessSceneStringAddScene = @"script.debug.processSceneString.addScene";
68 0 : static NSString * const kOOLogDebugProcessSceneStringAddModel = @"script.debug.processSceneString.addModel";
69 0 : static NSString * const kOOLogDebugProcessSceneStringAddMiniPlanet = @"script.debug.processSceneString.addMiniPlanet";
70 :
71 0 : static NSString * const kOOLogNoteRemoveAllCargo = @"script.debug.note.removeAllCargo";
72 0 : static NSString * const kOOLogNoteUseSpecialCargo = @"script.debug.note.useSpecialCargo";
73 0 : static NSString * const kOOLogNoteAddShips = @"script.debug.note.addShips";
74 0 : static NSString * const kOOLogNoteSet = @"script.debug.note.set";
75 0 : static NSString * const kOOLogNoteShowShipModel = @"script.debug.note.showShipModel";
76 0 : static NSString * const kOOLogNoteFuelLeak = @"script.debug.note.setFuelLeak";
77 0 : static NSString * const kOOLogNoteAddPlanet = @"script.debug.note.addPlanet";
78 0 : static NSString * const kOOLogNoteProcessSceneString = @"script.debug.note.processSceneString";
79 :
80 0 : static NSString * const kOOLogSyntaxSetPlanetInfo = @"script.debug.syntax.setPlanetInfo";
81 0 : static NSString * const kOOLogSyntaxAwardCargo = @"script.debug.syntax.awardCargo";
82 0 : static NSString * const kOOLogSyntaxAwardEquipment = @"script.debug.syntax.awardEquipment";
83 0 : static NSString * const kOOLogSyntaxRemoveEquipment = @"script.debug.syntax.removeEquipment";
84 0 : static NSString * const kOOLogSyntaxMessageShipAIs = @"script.debug.syntax.messageShipAIs";
85 0 : static NSString * const kOOLogSyntaxAddShips = @"script.debug.syntax.addShips";
86 0 : static NSString * const kOOLogSyntaxSet = @"script.debug.syntax.set";
87 0 : static NSString * const kOOLogSyntaxReset = @"script.debug.syntax.reset";
88 0 : static NSString * const kOOLogSyntaxIncrement = @"script.debug.syntax.increment";
89 0 : static NSString * const kOOLogSyntaxDecrement = @"script.debug.syntax.decrement";
90 0 : static NSString * const kOOLogSyntaxAdd = @"script.debug.syntax.add";
91 0 : static NSString * const kOOLogSyntaxSubtract = @"script.debug.syntax.subtract";
92 :
93 0 : static NSString * const kOOLogRemoveAllCargoNotDocked = @"script.error.removeAllCargo.notDocked";
94 :
95 :
96 0 : #define ACTIONS_TEMP_PREFIX "__oolite_actions_temp"
97 0 : static NSString * const kActionTempPrefix = @ ACTIONS_TEMP_PREFIX;
98 :
99 :
100 0 : static NSString *sMissionStringValue = nil;
101 0 : static NSString *sCurrentMissionKey = nil;
102 0 : static ShipEntity *scriptTarget = nil;
103 :
104 :
105 : @interface PlayerEntity (ScriptingPrivate)
106 :
107 0 : - (BOOL) scriptTestCondition:(NSArray *)scriptCondition;
108 0 : - (NSString *) expandScriptRightHandSide:(NSArray *)rhsComponents;
109 :
110 0 : - (void) scriptActions:(NSArray *)actions forTarget:(ShipEntity *)target missionKey:(NSString *)missionKey;
111 0 : - (NSString *) expandMessage:(NSString *)valueString;
112 :
113 : @end
114 :
115 :
116 : @implementation PlayerEntity (Scripting)
117 :
118 :
119 0 : static NSString *CurrentScriptNameOr(NSString *alternative)
120 : {
121 : if (sCurrentMissionKey != nil && ![sCurrentMissionKey hasPrefix:kActionTempPrefix])
122 : {
123 : return [NSString stringWithFormat:@"\"%@\"", sCurrentMissionKey];
124 : }
125 : return alternative;
126 : }
127 :
128 :
129 0 : OOINLINE NSString *CurrentScriptDesc(void)
130 : {
131 : return CurrentScriptNameOr(@"<anonymous actions>");
132 : }
133 :
134 :
135 0 : static void PerformScriptActions(NSArray *actions, Entity *target);
136 0 : static void PerformConditionalStatment(NSArray *actions, Entity *target);
137 0 : static void PerformActionStatment(NSArray *statement, Entity *target);
138 0 : static BOOL TestScriptConditions(NSArray *conditions);
139 :
140 :
141 : static void PerformScriptActions(NSArray *actions, Entity *target)
142 : {
143 : NSArray *statement = nil;
144 : foreach (statement, actions)
145 : {
146 : if ([[statement objectAtIndex:0] boolValue])
147 : {
148 : PerformConditionalStatment(statement, target);
149 : }
150 : else
151 : {
152 : PerformActionStatment(statement, target);
153 : }
154 : }
155 : }
156 :
157 :
158 : static void PerformConditionalStatment(NSArray *statement, Entity *target)
159 : {
160 : /* A sanitized conditional statement takes the form of an array:
161 : (true, conditions, trueActions, falseActions)
162 : The first element is always true. The second is an array of conditions.
163 : The third and four elements are actions to perform if the conditions
164 : evaluate to true or false, respectively.
165 : */
166 :
167 : NSArray *conditions = nil;
168 : NSArray *actions = nil;
169 :
170 : conditions = [statement objectAtIndex:1];
171 :
172 : if (TestScriptConditions(conditions))
173 : {
174 : actions = [statement objectAtIndex:2];
175 : }
176 : else
177 : {
178 : actions = [statement objectAtIndex:3];
179 : }
180 :
181 : PerformScriptActions(actions, target);
182 : }
183 :
184 :
185 : static void PerformActionStatment(NSArray *statement, Entity *target)
186 : {
187 : /* A sanitized action statement takes the form of an array:
188 : (false, selector [, argument])
189 : The first element is always false. The second is the method selector
190 : (as a string). If the method takes an argument, the third argument is
191 : the argument string.
192 :
193 : The sanitizer is responsible for ensuring that there is an argument,
194 : even if it's the empty string, for any selector with a colon at the
195 : end, and no arguments for selectors without colons. The runner can
196 : therefore use the list's element count as a flag without examining the
197 : selector.
198 : */
199 :
200 : NSString *selectorString = nil;
201 : NSString *argumentString = nil;
202 : NSString *expandedString = nil;
203 : SEL selector = NULL;
204 : NSMutableDictionary *locals = nil;
205 : PlayerEntity *player = PLAYER;
206 :
207 : selectorString = [statement objectAtIndex:1];
208 : if ([statement count] > 2) argumentString = [statement objectAtIndex:2];
209 :
210 : selector = NSSelectorFromString(selectorString);
211 :
212 : if (target == nil || ![target respondsToSelector:selector])
213 : {
214 : target = player;
215 : }
216 :
217 : if (argumentString != nil)
218 : {
219 : // Method with argument; substitute [description] expressions.
220 : locals = [player localVariablesForMission:sCurrentMissionKey];
221 : expandedString = OOExpandDescriptionString(OOStringExpanderDefaultRandomSeed(), argumentString, nil, locals, nil, kOOExpandNoOptions);
222 :
223 : [target performSelector:selector withObject:expandedString];
224 : }
225 : else
226 : {
227 : // Method without argument.
228 : [target performSelector:selector];
229 : }
230 : }
231 :
232 :
233 : static BOOL TestScriptConditions(NSArray *conditions)
234 : {
235 : NSEnumerator *condEnum = nil;
236 : NSArray *condition = nil;
237 : PlayerEntity *player = PLAYER;
238 :
239 : for (condEnum = [conditions objectEnumerator]; (condition = [condEnum nextObject]); )
240 : {
241 : if (![player scriptTestCondition:condition]) return NO;
242 : }
243 :
244 : return YES;
245 : }
246 :
247 :
248 : - (void) setScriptTarget:(ShipEntity *)ship
249 : {
250 : scriptTarget = ship;
251 : }
252 :
253 :
254 : - (ShipEntity*) scriptTarget
255 : {
256 : return scriptTarget;
257 : }
258 :
259 :
260 0 : OOINLINE OOEntityStatus RecursiveRemapStatus(OOEntityStatus status)
261 : {
262 : // Some player stutuses should only be seen once per "event".
263 : // This remaps them to something innocuous in case of recursion.
264 : if (status == STATUS_DOCKING ||
265 : status == STATUS_LAUNCHING ||
266 : status == STATUS_ENTERING_WITCHSPACE ||
267 : status == STATUS_EXITING_WITCHSPACE)
268 : {
269 : return STATUS_IN_FLIGHT;
270 : }
271 : else
272 : {
273 : return status;
274 : }
275 : }
276 :
277 :
278 0 : static BOOL sRunningScript = NO;
279 :
280 :
281 : // Return the world scripts that care about -checkScript.
282 0 : - (NSDictionary *) worldScriptsRequiringTickle
283 : {
284 : if (worldScriptsRequiringTickle != nil) return worldScriptsRequiringTickle;
285 :
286 : NSMutableDictionary *tickleScripts = [NSMutableDictionary dictionaryWithCapacity:[worldScripts count]];
287 : NSString *scriptName;
288 : foreachkey (scriptName, worldScripts)
289 : {
290 : OOScript *candidateScript = [worldScripts objectForKey:scriptName];
291 : if ([candidateScript requiresTickle])
292 : {
293 : [tickleScripts setObject:candidateScript forKey:scriptName];
294 : }
295 : }
296 :
297 : worldScriptsRequiringTickle = [tickleScripts copy];
298 : return worldScriptsRequiringTickle;
299 : }
300 :
301 :
302 : - (void) checkScript
303 : {
304 : BOOL wasRunningScript = sRunningScript;
305 : OOEntityStatus status, restoreStatus;
306 :
307 : NSDictionary *tickleScripts = [self worldScriptsRequiringTickle];
308 : if ([tickleScripts count] == 0)
309 : {
310 : // Quick exit if we only have JS scripts.
311 : return;
312 : }
313 :
314 : [self setScriptTarget:self];
315 :
316 : /* World scripts can potentially be invoked recursively, through
317 : scriptActionOnTarget: and possibly other mechanisms. This is bad, but
318 : that's the way it is. Legacy world scripts rely on only seeing certain
319 : player statuses once per "event". To ensure this, we must lie about
320 : the player's status when invoked recursively.
321 :
322 : Of course, there are also methods in the game that rely on status not
323 : lying. However, I don't believe any that rely on these particular
324 : statuses can be legitimately invoked by scripts. The alternative would
325 : be to track the "status-as-seen-by-scripts" separately from the "real"
326 : status, which'd risk synchronization problems.
327 :
328 : In summary, scriptActionOnTarget: is bad, and calling it from scripts
329 : rather than AIs is very bad.
330 : -- Ahruman, 20080302
331 :
332 : Addendum: scriptActionOnTarget: is currently not in the whitelist for
333 : script methods. Let's hope this doesn't turn out to be a problem.
334 : -- Ahruman, 20090208
335 : */
336 : status = [self status];
337 : restoreStatus = status;
338 : @try
339 : {
340 : if (sRunningScript)
341 : {
342 : status = RecursiveRemapStatus(status);
343 : [self setStatus:status];
344 : }
345 : sRunningScript = YES;
346 :
347 : // After all that, actually running the scripts is trivial.
348 : [[tickleScripts allValues] makeObjectsPerformSelector:@selector(runWithTarget:) withObject:self];
349 : }
350 : @catch (NSException *exception)
351 : {
352 : OOLog(kOOLogException, @"***** Exception running world scripts: %@ : %@", [exception name], [exception reason]);
353 : }
354 :
355 : // Restore anti-recursion measures.
356 : sRunningScript = wasRunningScript;
357 : if (status != restoreStatus) [self setStatus:restoreStatus];
358 : }
359 :
360 :
361 : - (void)runScriptActions:(NSArray *)actions withContextName:(NSString *)contextName forTarget:(ShipEntity *)target
362 : {
363 : NSAutoreleasePool *pool = nil;
364 : NSString *oldMissionKey = nil;
365 : NSString * volatile theMissionKey = contextName; // Work-around for silly exception macros
366 :
367 : pool = [[NSAutoreleasePool alloc] init];
368 :
369 : // FIXME: does this actually make sense in the context of non-missions?
370 : oldMissionKey = sCurrentMissionKey;
371 : sCurrentMissionKey = theMissionKey;
372 :
373 : @try
374 : {
375 : PerformScriptActions(actions, target);
376 : }
377 : @catch (NSException *exception)
378 : {
379 : OOLog(@"script.error.exception",
380 : @"***** EXCEPTION %@: %@ while handling legacy script actions for %@",
381 : [exception name],
382 : [exception reason],
383 : [theMissionKey hasPrefix:kActionTempPrefix] ? [target shortDescription] : theMissionKey);
384 : // Suppress exception
385 : }
386 :
387 : sCurrentMissionKey = oldMissionKey;
388 : [pool release];
389 : }
390 :
391 :
392 : - (void) runUnsanitizedScriptActions:(NSArray *)actions allowingAIMethods:(BOOL)allowAIMethods withContextName:(NSString *)contextName forTarget:(ShipEntity *)target
393 : {
394 : [self runScriptActions:OOSanitizeLegacyScript(actions, contextName, allowAIMethods)
395 : withContextName:contextName
396 : forTarget:target];
397 : }
398 :
399 :
400 : - (BOOL) scriptTestConditions:(NSArray *)array
401 : {
402 : BOOL result = NO;
403 :
404 : @try
405 : {
406 : result = TestScriptConditions(array);
407 : }
408 : @catch (NSException *exception)
409 : {
410 : OOLog(@"script.error.exception",
411 : @"***** EXCEPTION %@: %@ while testing legacy script conditions.",
412 : [exception name],
413 : [exception reason]);
414 : // Suppress exception
415 : }
416 :
417 : return result;
418 : }
419 :
420 :
421 0 : - (BOOL) scriptTestCondition:(NSArray *)scriptCondition
422 : {
423 : /* Test a script condition sanitized by OOLegacyScriptWhitelist.
424 :
425 : A sanitized condition is an array of the form:
426 : (opType, rawString, selector, comparisonType, operandArray).
427 :
428 : opType and comparisonType are NSNumbers containing OOOperationType and
429 : OOComparisonType enumerators, respectively.
430 :
431 : rawString is the original textual representation of the condition for
432 : display purposes.
433 :
434 : selector is a string, either a method selector or a mission/local
435 : variable name.
436 :
437 : operandArray is an array of operands. Each operand is itself an array
438 : of two items: a boolean indicating whether it's a method selector
439 : (true) or a literal string (false), and a string.
440 :
441 : The special opType OP_FALSE doesn't require any other elements in the
442 : array. All other valid opTypes require the array to have five elements.
443 :
444 : For performance reasons, this method assumes the script condition will
445 : have been generated by OOSanitizeLegacyScriptConditions() and doesn't
446 : perform extensive validity checks.
447 : */
448 :
449 : OOOperationType opType;
450 : NSString *selectorString = nil;
451 : SEL selector = NULL;
452 : OOComparisonType comparator;
453 : NSArray *operandArray = nil;
454 : NSString *lhsString = nil;
455 : NSString *expandedRHS = nil;
456 : NSArray *rhsComponents = nil;
457 : NSString *rhsItem = nil;
458 : NSUInteger i, count;
459 : NSCharacterSet *whitespace = nil;
460 : double lhsValue, rhsValue;
461 : BOOL lhsFlag, rhsFlag;
462 :
463 : opType = [scriptCondition oo_unsignedIntAtIndex:0];
464 : if (opType == OP_FALSE) return NO;
465 :
466 : selectorString = [scriptCondition oo_stringAtIndex:2];
467 : comparator = [scriptCondition oo_unsignedIntAtIndex:3];
468 : operandArray = [scriptCondition oo_arrayAtIndex:4];
469 :
470 : // Transform mission/local var ops into string ops.
471 : if (opType == OP_MISSION_VAR)
472 : {
473 : sMissionStringValue = [mission_variables objectForKey:selectorString];
474 : selector = @selector(mission_string);
475 : opType = OP_STRING;
476 : }
477 : else if (opType == OP_LOCAL_VAR)
478 : {
479 : sMissionStringValue = [[self localVariablesForMission:sCurrentMissionKey] objectForKey:selectorString];
480 : selector = @selector(mission_string);
481 : opType = OP_STRING;
482 : }
483 : else
484 : {
485 : selector = NSSelectorFromString(selectorString);
486 : }
487 :
488 : expandedRHS = [self expandScriptRightHandSide:operandArray];
489 :
490 : if (opType == OP_STRING)
491 : {
492 : lhsString = [self performSelector:selector];
493 :
494 0 : #define DOUBLEVAL(x) ((x != nil) ? [x doubleValue] : 0.0)
495 :
496 : switch (comparator)
497 : {
498 : case COMPARISON_UNDEFINED:
499 : return lhsString == nil;
500 :
501 : case COMPARISON_EQUAL:
502 : return [lhsString isEqualToString:expandedRHS];
503 :
504 : case COMPARISON_NOTEQUAL:
505 : return ![lhsString isEqualToString:expandedRHS];
506 :
507 : case COMPARISON_LESSTHAN:
508 : return DOUBLEVAL(lhsString) < DOUBLEVAL(expandedRHS);
509 :
510 : case COMPARISON_GREATERTHAN:
511 : return DOUBLEVAL(lhsString) > DOUBLEVAL(expandedRHS);
512 :
513 : case COMPARISON_ONEOF:
514 : {
515 : rhsComponents = [expandedRHS componentsSeparatedByString:@","];
516 : count = [rhsComponents count];
517 :
518 : whitespace = [NSCharacterSet whitespaceCharacterSet];
519 : lhsString = [lhsString stringByTrimmingCharactersInSet:whitespace];
520 :
521 : for (i = 0; i < count; i++)
522 : {
523 : rhsItem = [[rhsComponents objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
524 : if ([lhsString isEqualToString:rhsItem])
525 : {
526 : return YES;
527 : }
528 : }
529 : }
530 : return NO;
531 : }
532 : }
533 : else if (opType == OP_NUMBER)
534 : {
535 : lhsValue = [[self performSelector:selector] doubleValue];
536 :
537 : if (comparator == COMPARISON_ONEOF)
538 : {
539 : rhsComponents = [expandedRHS componentsSeparatedByString:@","];
540 : count = [rhsComponents count];
541 :
542 : for (i = 0; i < count; i++)
543 : {
544 : rhsItem = [rhsComponents objectAtIndex:i];
545 : rhsValue = [rhsItem doubleValue];
546 :
547 : if (lhsValue == rhsValue)
548 : {
549 : return YES;
550 : }
551 : }
552 :
553 : return NO;
554 : }
555 : else
556 : {
557 : rhsValue = [expandedRHS doubleValue];
558 :
559 : switch (comparator)
560 : {
561 : case COMPARISON_EQUAL:
562 : return lhsValue == rhsValue;
563 :
564 : case COMPARISON_NOTEQUAL:
565 : return lhsValue != rhsValue;
566 :
567 : case COMPARISON_LESSTHAN:
568 : return lhsValue < rhsValue;
569 :
570 : case COMPARISON_GREATERTHAN:
571 : return lhsValue > rhsValue;
572 :
573 : case COMPARISON_UNDEFINED:
574 : case COMPARISON_ONEOF:
575 : // "Can't happen" - undefined should have been caught by the sanitizer, oneof is handled above.
576 : OOLog(@"script.error.unexpectedOperator", @"***** SCRIPT ERROR: in %@, operator %@ is not valid for numbers, evaluating to false.", CurrentScriptDesc(), OOComparisonTypeToString(comparator));
577 : return NO;
578 : }
579 : }
580 : }
581 : else if (opType == OP_BOOL)
582 : {
583 : lhsFlag = [[self performSelector:selector] isEqualToString:@"YES"];
584 : rhsFlag = [expandedRHS isEqualToString:@"YES"];
585 :
586 : switch (comparator)
587 : {
588 : case COMPARISON_EQUAL:
589 : return lhsFlag == rhsFlag;
590 :
591 : case COMPARISON_NOTEQUAL:
592 : return lhsFlag != rhsFlag;
593 :
594 : case COMPARISON_LESSTHAN:
595 : case COMPARISON_GREATERTHAN:
596 : case COMPARISON_UNDEFINED:
597 : case COMPARISON_ONEOF:
598 : // "Can't happen" - should have been caught by the sanitizer.
599 : OOLog(@"script.error.unexpectedOperator", @"***** SCRIPT ERROR: in %@, operator %@ is not valid for booleans, evaluating to false.", CurrentScriptDesc(), OOComparisonTypeToString(comparator));
600 : return NO;
601 : }
602 : }
603 :
604 : // What are we doing here?
605 : OOLog(@"script.error.fallthrough", @"***** SCRIPT ERROR: in %@, unhandled condition '%@' (%@). %@", CurrentScriptDesc(), [scriptCondition objectAtIndex:1], scriptCondition, @"This is an internal error, please report it.");
606 : return NO;
607 : }
608 :
609 :
610 0 : - (NSString *) expandScriptRightHandSide:(NSArray *)rhsComponents
611 : {
612 : NSMutableArray *result = nil;
613 : NSEnumerator *componentEnum = nil;
614 : NSArray *component = nil;
615 : NSString *value = nil;
616 :
617 : result = [NSMutableArray arrayWithCapacity:[rhsComponents count]];
618 :
619 : for (componentEnum = [rhsComponents objectEnumerator]; (component = [componentEnum nextObject]); )
620 : {
621 : /* Each component is a two-element array. The second element is a
622 : string. The first element is a boolean indicating whether the
623 : string is a selector (true) or a literal (false).
624 :
625 : All valid selectors return a string or an NSNumber; in either
626 : case, -description gives us a useful value to substitute into
627 : the expanded string.
628 : */
629 :
630 : value = [component oo_stringAtIndex:1];
631 :
632 : if ([[component objectAtIndex:0] boolValue])
633 : {
634 : value = [[self performSelector:NSSelectorFromString(value)] description];
635 : if (value == nil) value = @"(null)"; // for backwards compatibility
636 : }
637 :
638 : [result addObject:value];
639 : }
640 :
641 : return [result componentsJoinedByString:@" "];
642 : }
643 :
644 :
645 : - (NSDictionary *) missionVariables
646 : {
647 : return mission_variables;
648 : }
649 :
650 :
651 : - (NSString *)missionVariableForKey:(NSString *)key
652 : {
653 : NSString *result = nil;
654 : if (key != nil) result = [mission_variables objectForKey:key];
655 : return result;
656 : }
657 :
658 :
659 : - (void)setMissionVariable:(NSString *)value forKey:(NSString *)key
660 : {
661 : if (key != nil)
662 : {
663 : if (value != nil) [mission_variables setObject:value forKey:key];
664 : else [mission_variables removeObjectForKey:key];
665 : }
666 : }
667 :
668 :
669 : - (NSMutableDictionary *)localVariablesForMission:(NSString *)missionKey
670 : {
671 : NSMutableDictionary *result = nil;
672 :
673 : if (missionKey == nil) return nil;
674 :
675 : result = [localVariables objectForKey:missionKey];
676 : if (result == nil)
677 : {
678 : result = [NSMutableDictionary dictionary];
679 : [localVariables setObject:result forKey:missionKey];
680 : }
681 :
682 : return result;
683 : }
684 :
685 :
686 : - (NSString *)localVariableForKey:(NSString *)variableName andMission:(NSString *)missionKey
687 : {
688 : return [[localVariables oo_dictionaryForKey:missionKey] objectForKey:variableName];
689 : }
690 :
691 :
692 : - (void)setLocalVariable:(NSString *)value forKey:(NSString *)variableName andMission:(NSString *)missionKey
693 : {
694 : NSMutableDictionary *locals = nil;
695 :
696 : if (variableName != nil && missionKey != nil)
697 : {
698 : locals = [self localVariablesForMission:missionKey];
699 : if (value != nil)
700 : {
701 : [locals setObject:value forKey:variableName];
702 : }
703 : else
704 : {
705 : [locals removeObjectForKey:variableName];
706 : }
707 : }
708 : }
709 :
710 :
711 : - (NSArray *) missionsList
712 : {
713 : NSEnumerator *scriptEnum = nil;
714 : NSString *scriptName = nil;
715 : NSString *vars = nil;
716 : NSMutableArray *result1 = nil;
717 : NSMutableArray *result2 = nil;
718 :
719 : result1 = [NSMutableArray array];
720 : result2 = [NSMutableArray array];
721 :
722 : NSArray* passengerManifest = [self passengerList];
723 : NSArray* contractManifest = [self contractList];
724 : NSArray* parcelManifest = [self parcelList];
725 :
726 : if ([passengerManifest count] > 0)
727 : {
728 : [result2 addObject:[[NSArray arrayWithObject:DESC(@"manifest-passengers")] arrayByAddingObjectsFromArray:passengerManifest]];
729 : }
730 :
731 : if ([parcelManifest count] > 0)
732 : {
733 : [result2 addObject:[[NSArray arrayWithObject:DESC(@"manifest-parcels")] arrayByAddingObjectsFromArray:parcelManifest]];
734 : }
735 :
736 : if ([contractManifest count] > 0)
737 : {
738 : [result2 addObject:[[NSArray arrayWithObject:DESC(@"manifest-contracts")] arrayByAddingObjectsFromArray:contractManifest]];
739 : }
740 :
741 : /* For proper display, array entries need to all be after string
742 : * entries, so sort them now */
743 : for (scriptEnum = [worldScripts keyEnumerator]; (scriptName = [scriptEnum nextObject]); )
744 : {
745 : vars = [mission_variables objectForKey:scriptName];
746 :
747 : if (vars != nil)
748 : {
749 : if ([vars isKindOfClass:[NSString class]])
750 : {
751 : [result1 addObject:vars];
752 : }
753 : else if ([vars isKindOfClass:[NSArray class]])
754 : {
755 : BOOL found = NO;
756 : NSArray *element = nil;
757 : foreach (element, result2)
758 : {
759 : if ([[element oo_stringAtIndex:0] isEqualToString:[(NSArray*)vars oo_stringAtIndex:0]])
760 : {
761 :
762 : [result2 removeObject:element];
763 : NSRange notTheHeader;
764 : notTheHeader.location = 1;
765 : notTheHeader.length = [(NSArray*)vars count]-1;
766 : [result2 addObject:[element arrayByAddingObjectsFromArray:[(NSArray*)vars subarrayWithRange:notTheHeader]]];
767 : found = YES;
768 : break;
769 : }
770 : }
771 : if (!found)
772 : {
773 : [result2 addObject:vars];
774 : }
775 : }
776 : }
777 : }
778 : return [result1 arrayByAddingObjectsFromArray:result2];
779 : }
780 :
781 :
782 : - (NSString*) replaceVariablesInString:(NSString*) args
783 : {
784 : NSMutableDictionary *locals = [self localVariablesForMission:sCurrentMissionKey];
785 : NSMutableString *resultString = [NSMutableString stringWithString: args];
786 : NSString *valueString;
787 : unsigned i;
788 : NSMutableArray *tokens = ScanTokensFromString(args);
789 :
790 : for (i = 0; i < [tokens count]; i++)
791 : {
792 : valueString = [tokens objectAtIndex:i];
793 :
794 : if ([valueString hasPrefix:@"mission_"] && [mission_variables objectForKey:valueString])
795 : {
796 : [resultString replaceOccurrencesOfString:valueString withString:[mission_variables objectForKey:valueString] options:NSLiteralSearch range:NSMakeRange(0, [resultString length])];
797 : }
798 : else if ([locals objectForKey:valueString])
799 : {
800 : [resultString replaceOccurrencesOfString:valueString withString:[locals objectForKey:valueString] options:NSLiteralSearch range:NSMakeRange(0, [resultString length])];
801 : }
802 : else if (([valueString hasSuffix:@"_number"])||([valueString hasSuffix:@"_bool"])||([valueString hasSuffix:@"_string"]))
803 : {
804 : SEL valueselector = NSSelectorFromString(valueString);
805 : if ([self respondsToSelector:valueselector])
806 : {
807 : [resultString replaceOccurrencesOfString:valueString withString:[NSString stringWithFormat:@"%@", [self performSelector:valueselector]] options:NSLiteralSearch range:NSMakeRange(0, [resultString length])];
808 : }
809 : }
810 : else if ([valueString hasPrefix:@"["]&&[valueString hasSuffix:@"]"])
811 : {
812 : NSString* replaceString = OOExpand(valueString);
813 : [resultString replaceOccurrencesOfString:valueString withString:replaceString options:NSLiteralSearch range:NSMakeRange(0, [resultString length])];
814 : }
815 : }
816 :
817 : OOLog(kOOLogDebugReplaceVariablesInString, @"EXPANSION: \"%@\" becomes \"%@\"", args, resultString);
818 :
819 : return [NSString stringWithString: resultString];
820 : }
821 :
822 : /*-----------------------------------------------------*/
823 :
824 :
825 : - (void) setMissionDescription:(NSString *)textKey
826 : {
827 : [self setMissionDescription:textKey forMission:sCurrentMissionKey];
828 : }
829 :
830 :
831 : - (void) setMissionDescription:(NSString *)textKey forMission:(NSString *)key
832 : {
833 : NSString *text = [[UNIVERSE missiontext] oo_stringForKey:textKey];
834 :
835 : if (!text)
836 : {
837 : OOLogERR(kOOLogScriptMissionDescNoText, @"in %@, no mission text set for key '%@' [UNIVERSE missiontext] is:\n%@ ", CurrentScriptDesc(), textKey, [UNIVERSE missiontext]);
838 : return;
839 : }
840 :
841 : [self setMissionInstructions:text forMission:key];
842 : }
843 :
844 :
845 : // implementation of mission.setInstructions(), also final part of legacy setMissionDescription
846 : - (void) setMissionInstructions:(NSString *)text forMission:(NSString *)key
847 : {
848 : if (!key)
849 : {
850 : OOLogERR(kOOLogScriptMissionDescNoKey, @"in %@, mission key not set", CurrentScriptDesc());
851 : return;
852 : }
853 :
854 : text = OOExpand(text);
855 : text = [self replaceVariablesInString: text];
856 :
857 : [mission_variables setObject:text forKey:key];
858 : }
859 :
860 :
861 : - (void) setMissionInstructionsList:(NSArray *)list forMission:(NSString *)key
862 : {
863 : if (!key)
864 : {
865 : OOLogERR(kOOLogScriptMissionDescNoKey, @"in %@, mission key not set", CurrentScriptDesc());
866 : return;
867 : }
868 :
869 : NSString *text = nil;
870 : NSUInteger i,ct = [list count];
871 : NSMutableArray *expandedList = [NSMutableArray arrayWithCapacity:ct];
872 : for (i=0 ; i<ct ; i++)
873 : {
874 : text = [list oo_stringAtIndex:i defaultValue:nil];
875 : if (text != nil)
876 : {
877 : text = OOExpand(text);
878 : text = [self replaceVariablesInString: text];
879 : [expandedList addObject:text];
880 : }
881 : }
882 :
883 : [mission_variables setObject:expandedList forKey:key];
884 : }
885 :
886 :
887 : - (void) clearMissionDescription
888 : {
889 : [self clearMissionDescriptionForMission:sCurrentMissionKey];
890 : }
891 :
892 :
893 : - (void) clearMissionDescriptionForMission:(NSString *)key
894 : {
895 : if (!key)
896 : {
897 : OOLogERR(kOOLogScriptMissionDescNoKey, @"in %@, mission key not set", CurrentScriptDesc());
898 : return;
899 : }
900 :
901 : if (![mission_variables objectForKey:key]) return;
902 :
903 : [mission_variables removeObjectForKey:key];
904 : }
905 :
906 :
907 : - (NSString *) mission_string
908 : {
909 : return sMissionStringValue;
910 : }
911 :
912 :
913 : - (NSString *) status_string
914 : {
915 : return OOStringFromEntityStatus([self status]);
916 : }
917 :
918 :
919 : - (NSString *) gui_screen_string
920 : {
921 : return OOStringFromGUIScreenID(gui_screen);
922 : }
923 :
924 :
925 : - (NSNumber *) galaxy_number
926 : {
927 : return [NSNumber numberWithInt:[self currentGalaxyID]];
928 : }
929 :
930 :
931 : - (NSNumber *) planet_number
932 : {
933 : return [NSNumber numberWithInt:[self currentSystemID]];
934 : }
935 :
936 :
937 : - (NSNumber *) score_number
938 : {
939 : return [NSNumber numberWithUnsignedInt:[self score]];
940 : }
941 :
942 :
943 : - (NSNumber *) credits_number
944 : {
945 : return [NSNumber numberWithDouble:[self creditBalance]];
946 : }
947 :
948 :
949 : - (NSNumber *) scriptTimer_number
950 : {
951 : return [NSNumber numberWithDouble:[self scriptTimer]];
952 : }
953 :
954 :
955 0 : static int shipsFound;
956 : - (NSNumber *) shipsFound_number
957 : {
958 : return [NSNumber numberWithInt:shipsFound];
959 : }
960 :
961 :
962 : - (NSNumber *) commanderLegalStatus_number
963 : {
964 : return [NSNumber numberWithInt:[self legalStatus]];
965 : }
966 :
967 :
968 : - (void) setLegalStatus:(NSString *)valueString
969 : {
970 : legalStatus = [valueString intValue];
971 : }
972 :
973 :
974 : - (NSString *) commanderLegalStatus_string
975 : {
976 : return OODisplayStringFromLegalStatus(legalStatus);
977 : }
978 :
979 :
980 : - (NSNumber *) d100_number
981 : {
982 : int d100 = ranrot_rand() % 100;
983 : return [NSNumber numberWithInt:d100];
984 : }
985 :
986 :
987 : - (NSNumber *) pseudoFixedD100_number
988 : {
989 : return [NSNumber numberWithInt:[self systemPseudoRandom100]];
990 : }
991 :
992 :
993 : - (NSNumber *) d256_number
994 : {
995 : int d256 = ranrot_rand() % 256;
996 : return [NSNumber numberWithInt:d256];
997 : }
998 :
999 :
1000 : - (NSNumber *) pseudoFixedD256_number
1001 : {
1002 : return [NSNumber numberWithInt:[self systemPseudoRandom256]];
1003 : }
1004 :
1005 :
1006 : - (NSNumber *) clock_number // returns the game time in seconds
1007 : {
1008 : return [NSNumber numberWithDouble:ship_clock];
1009 : }
1010 :
1011 :
1012 : - (NSNumber *) clock_secs_number // returns the game time in seconds
1013 : {
1014 : return [NSNumber numberWithUnsignedLongLong:ship_clock];
1015 : }
1016 :
1017 :
1018 : - (NSNumber *) clock_mins_number // returns the game time in minutes
1019 : {
1020 : return [NSNumber numberWithUnsignedLongLong:ship_clock / 60.0];
1021 : }
1022 :
1023 :
1024 : - (NSNumber *) clock_hours_number // returns the game time in hours
1025 : {
1026 : return [NSNumber numberWithUnsignedLongLong:ship_clock / 3600.0];
1027 : }
1028 :
1029 :
1030 : - (NSNumber *) clock_days_number // returns the game time in days
1031 : {
1032 : return [NSNumber numberWithUnsignedLongLong:ship_clock / 86400.0];
1033 : }
1034 :
1035 :
1036 : - (NSNumber *) fuelLevel_number // returns the fuel level in LY
1037 : {
1038 : return [NSNumber numberWithFloat:floor(0.1 * fuel)];
1039 : }
1040 :
1041 :
1042 : - (NSString *) dockedAtMainStation_bool
1043 : {
1044 : if ([self dockedAtMainStation]) return @"YES";
1045 : else return @"NO";
1046 : }
1047 :
1048 :
1049 : - (NSString *) foundEquipment_bool
1050 : {
1051 : return (found_equipment)? @"YES" : @"NO";
1052 : }
1053 :
1054 :
1055 : - (NSString *) sunWillGoNova_bool // returns whether the sun is going to go nova
1056 : {
1057 : return ([[UNIVERSE sun] willGoNova])? @"YES" : @"NO";
1058 : }
1059 :
1060 :
1061 : - (NSString *) sunGoneNova_bool // returns whether the sun has gone nova
1062 : {
1063 : return ([[UNIVERSE sun] goneNova])? @"YES" : @"NO";
1064 : }
1065 :
1066 :
1067 : - (NSString *) missionChoice_string // returns nil or the key for the chosen option
1068 : {
1069 : return missionChoice;
1070 : }
1071 :
1072 :
1073 : - (NSString *) missionKeyPress_string
1074 : {
1075 : return missionKeyPress;
1076 : }
1077 :
1078 :
1079 : - (NSNumber *) dockedTechLevel_number
1080 : {
1081 : StationEntity *dockedStation = [self dockedStation];
1082 : if (!dockedStation)
1083 : {
1084 : return [self systemTechLevel_number];
1085 : }
1086 : return [NSNumber numberWithUnsignedInteger:[dockedStation equivalentTechLevel]];
1087 : }
1088 :
1089 : - (NSString *) dockedStationName_string // returns 'NONE' if the player isn't docked, [station name] if it is, 'UNKNOWN' otherwise (?)
1090 : {
1091 : NSString *result = nil;
1092 : if ([self status] != STATUS_DOCKED) return @"NONE";
1093 :
1094 : result = [self dockedStationName];
1095 : if (result == nil) result = @"UNKNOWN";
1096 : return result;
1097 : }
1098 :
1099 :
1100 : - (NSString *) systemGovernment_string
1101 : {
1102 : int government = [[self systemGovernment_number] intValue]; // 0 .. 7 (0 anarchic .. 7 most stable)
1103 : NSString *result = OODisplayStringFromGovernmentID(government);
1104 : if (result == nil) result = @"UNKNOWN";
1105 :
1106 : return result;
1107 : }
1108 :
1109 :
1110 : - (NSNumber *) systemGovernment_number
1111 : {
1112 : NSDictionary *systeminfo = [UNIVERSE currentSystemData];
1113 : return [systeminfo objectForKey:KEY_GOVERNMENT];
1114 : }
1115 :
1116 :
1117 : - (NSString *) systemEconomy_string
1118 : {
1119 : int economy = [[self systemEconomy_number] intValue]; // 0 .. 7 (0 rich industrial .. 7 poor agricultural)
1120 : NSString *result = OODisplayStringFromEconomyID(economy);
1121 : if (result == nil) result = @"UNKNOWN";
1122 :
1123 : return result;
1124 : }
1125 :
1126 :
1127 : - (NSNumber *) systemEconomy_number
1128 : {
1129 : NSDictionary *systeminfo = [UNIVERSE currentSystemData];
1130 : return [systeminfo objectForKey:KEY_ECONOMY];
1131 : }
1132 :
1133 :
1134 : - (NSNumber *) systemTechLevel_number
1135 : {
1136 : NSDictionary *systeminfo = [UNIVERSE currentSystemData];
1137 : return [systeminfo objectForKey:KEY_TECHLEVEL];
1138 : }
1139 :
1140 :
1141 : - (NSNumber *) systemPopulation_number
1142 : {
1143 : NSDictionary *systeminfo = [UNIVERSE currentSystemData];
1144 : return [systeminfo objectForKey:KEY_POPULATION];
1145 : }
1146 :
1147 :
1148 : - (NSNumber *) systemProductivity_number
1149 : {
1150 : NSDictionary *systeminfo = [UNIVERSE currentSystemData];
1151 : return [systeminfo objectForKey:KEY_PRODUCTIVITY];
1152 : }
1153 :
1154 :
1155 : - (NSString *) commanderName_string
1156 : {
1157 : return [self commanderName];
1158 : }
1159 :
1160 :
1161 : - (NSString *) commanderRank_string
1162 : {
1163 : return OODisplayRatingStringFromKillCount([self score]);
1164 : }
1165 :
1166 :
1167 : - (NSString *) commanderShip_string
1168 : {
1169 : return [self name];
1170 : }
1171 :
1172 :
1173 : - (NSString *) commanderShipDisplayName_string
1174 : {
1175 : return [self displayName];
1176 : }
1177 :
1178 : /*-----------------------------------------------------*/
1179 :
1180 :
1181 0 : - (NSString *) expandMessage:(NSString *)valueString
1182 : {
1183 : Random_Seed very_random_seed;
1184 : very_random_seed.a = rand() & 255;
1185 : very_random_seed.b = rand() & 255;
1186 : very_random_seed.c = rand() & 255;
1187 : very_random_seed.d = rand() & 255;
1188 : very_random_seed.e = rand() & 255;
1189 : very_random_seed.f = rand() & 255;
1190 : seed_RNG_only_for_planet_description(very_random_seed);
1191 : NSString* expandedMessage = OOExpand(valueString);
1192 : return [self replaceVariablesInString: expandedMessage];
1193 : }
1194 :
1195 :
1196 : - (void) commsMessage:(NSString *)valueString
1197 : {
1198 : [UNIVERSE addCommsMessage:[self expandMessage:valueString] forCount:4.5];
1199 : }
1200 :
1201 :
1202 : // Enabled on 02-May-2008 - Nikos
1203 : // This method does the same as -commsMessage, (which in fact calls), the difference being that scripts can use this
1204 : // method to have unpiloted ship entities sending comms messages.
1205 : - (void) commsMessageByUnpiloted:(NSString *)valueString
1206 : {
1207 : [self commsMessage:valueString];
1208 : }
1209 :
1210 :
1211 : - (void) consoleMessage3s:(NSString *)valueString
1212 : {
1213 : [UNIVERSE addMessage:[self expandMessage:valueString] forCount: 3];
1214 : }
1215 :
1216 :
1217 : - (void) consoleMessage6s:(NSString *)valueString
1218 : {
1219 : [UNIVERSE addMessage:[self expandMessage:valueString] forCount: 6];
1220 : }
1221 :
1222 :
1223 : - (void) awardCredits:(NSString *)valueString
1224 : {
1225 : if (scriptTarget != self) return;
1226 :
1227 : /* We can't use -longLongValue here for Mac OS X 10.4 compatibility, but
1228 : we don't need to since larger values have never been supported for
1229 : legacy scripts.
1230 : */
1231 : int64_t award = [valueString intValue];
1232 : award *= 10;
1233 : if (award < 0 && credits < (OOCreditsQuantity)-award) credits = 0;
1234 : else credits += award;
1235 : }
1236 :
1237 :
1238 : - (void) awardShipKills:(NSString *)valueString
1239 : {
1240 : if (scriptTarget != self) return;
1241 :
1242 : int value = [valueString intValue];
1243 : if (0 < value) ship_kills += value;
1244 : }
1245 :
1246 :
1247 : - (void) awardEquipment:(NSString *)equipString //eg. EQ_NAVAL_ENERGY_UNIT
1248 : {
1249 : if (scriptTarget != self) return;
1250 :
1251 : if ([equipString isEqualToString:@"EQ_FUEL"])
1252 : {
1253 : [self setFuel:[self fuelCapacity]];
1254 : }
1255 :
1256 : OOEquipmentType *eqType = [OOEquipmentType equipmentTypeWithIdentifier:equipString];
1257 :
1258 : if ([eqType isMissileOrMine])
1259 : {
1260 : [self mountMissileWithRole:equipString];
1261 : }
1262 : else if([equipString hasPrefix:@"EQ_WEAPON"] && ![equipString hasSuffix:@"_DAMAGED"])
1263 : {
1264 : OOLog(kOOLogSyntaxAwardEquipment, @"***** SCRIPT ERROR: in %@, CANNOT award undamaged weapon:'%@'. Damaged weapons can be awarded instead.", CurrentScriptDesc(), equipString);
1265 : }
1266 : else if ([equipString hasSuffix:@"_DAMAGED"] && [self hasEquipmentItem:[equipString substringToIndex:[equipString length] - [@"_DAMAGED" length]]])
1267 : {
1268 : OOLog(kOOLogSyntaxAwardEquipment, @"***** SCRIPT ERROR: in %@, CANNOT award damaged equipment:'%@'. Undamaged version already equipped.", CurrentScriptDesc(), equipString);
1269 : }
1270 : else if ([eqType canCarryMultiple] || ![self hasEquipmentItem:equipString])
1271 : {
1272 : [self addEquipmentItem:equipString withValidation:YES inContext:@"scripted"];
1273 : }
1274 : }
1275 :
1276 :
1277 : - (void) removeEquipment:(NSString *)equipKey //eg. EQ_NAVAL_ENERGY_UNIT
1278 : {
1279 : if (scriptTarget != self) return;
1280 :
1281 : if ([equipKey isEqualToString:@"EQ_FUEL"])
1282 : {
1283 : fuel = 0;
1284 : return;
1285 : }
1286 :
1287 : if ([equipKey isEqualToString:@"EQ_CARGO_BAY"] && [self hasEquipmentItem:equipKey]
1288 : && ([self extraCargo] > [self availableCargoSpace]))
1289 : {
1290 : OOLog(kOOLogSyntaxRemoveEquipment, @"***** SCRIPT ERROR: in %@, CANNOT remove cargo bay. Too much cargo.", CurrentScriptDesc());
1291 : return;
1292 : }
1293 : if ([self hasEquipmentItem:equipKey] || [self hasEquipmentItem:[equipKey stringByAppendingString:@"_DAMAGED"]])
1294 : {
1295 : [self removeEquipmentItem:equipKey];
1296 : }
1297 :
1298 : }
1299 :
1300 :
1301 : - (void) setPlanetinfo:(NSString *)key_valueString // uses key=value format
1302 : {
1303 : NSArray * tokens = [key_valueString componentsSeparatedByString:@"="];
1304 : NSString* keyString = nil;
1305 : NSString* valueString = nil;
1306 :
1307 : if ([tokens count] != 2)
1308 : {
1309 : OOLog(kOOLogSyntaxSetPlanetInfo, @"***** SCRIPT ERROR: in %@, CANNOT setPlanetinfo: '%@' (bad parameter count)", CurrentScriptDesc(), key_valueString);
1310 : return;
1311 : }
1312 :
1313 : keyString = [[tokens objectAtIndex:0] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
1314 : valueString = [[tokens objectAtIndex:1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
1315 :
1316 : /* Legacy script planetinfo settings are now non-persistent over save/load
1317 : * Virtually nothing uses them any more, and expecting them to have a
1318 : * manifest and identifying what it is if so seems unnecessary */
1319 : [UNIVERSE setSystemDataKey:keyString value:valueString fromManifest:@""];
1320 :
1321 : }
1322 :
1323 :
1324 : - (void) setSpecificPlanetInfo:(NSString *)key_valueString // uses galaxy#=planet#=key=value
1325 : {
1326 : NSArray * tokens = [key_valueString componentsSeparatedByString:@"="];
1327 : NSString* keyString = nil;
1328 : NSString* valueString = nil;
1329 : int gnum, pnum;
1330 :
1331 : if ([tokens count] != 4)
1332 : {
1333 : OOLog(kOOLogSyntaxSetPlanetInfo, @"***** SCRIPT ERROR: in %@, CANNOT setSpecificPlanetInfo: '%@' (bad parameter count)", CurrentScriptDesc(), key_valueString);
1334 : return;
1335 : }
1336 :
1337 : gnum = [tokens oo_intAtIndex:0];
1338 : pnum = [tokens oo_intAtIndex:1];
1339 : keyString = [[tokens objectAtIndex:2] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
1340 : valueString = [[tokens objectAtIndex:3] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
1341 :
1342 : [UNIVERSE setSystemDataForGalaxy:gnum planet:pnum key:keyString value:valueString fromManifest:@"" forLayer:OO_LAYER_OXP_DYNAMIC];
1343 : }
1344 :
1345 :
1346 : - (void) awardCargo:(NSString *)amount_typeString
1347 : {
1348 : if (scriptTarget != self) return;
1349 :
1350 : NSArray *tokens = ScanTokensFromString(amount_typeString);
1351 : OOCargoQuantityDelta amount;
1352 : OOCommodityType type;
1353 : OOMassUnit unit;
1354 :
1355 : if ([tokens count] != 2)
1356 : {
1357 : OOLog(kOOLogSyntaxAwardCargo, @"***** SCRIPT ERROR: in %@, CANNOT awardCargo: '%@' (%@)", CurrentScriptDesc(), amount_typeString, @"bad parameter count");
1358 : return;
1359 : }
1360 :
1361 :
1362 : type = [tokens oo_stringAtIndex:1];
1363 : if (![[UNIVERSE commodities] goodDefined:type])
1364 : {
1365 : OOLog(kOOLogSyntaxAwardCargo, @"***** SCRIPT ERROR: in %@, CANNOT awardCargo: '%@' (%@)", CurrentScriptDesc(), amount_typeString, @"unknown type");
1366 : return;
1367 : }
1368 :
1369 : amount = [tokens oo_intAtIndex:0];
1370 : if (amount < 0)
1371 : {
1372 : OOLog(kOOLogSyntaxAwardCargo, @"***** SCRIPT ERROR: in %@, CANNOT awardCargo: '%@' (%@)", CurrentScriptDesc(), amount_typeString, @"negative quantity");
1373 : return;
1374 : }
1375 :
1376 : unit = [shipCommodityData massUnitForGood:type];
1377 : if (specialCargo && unit == UNITS_TONS)
1378 : {
1379 : OOLog(kOOLogSyntaxAwardCargo, @"***** SCRIPT ERROR: in %@, CANNOT awardCargo: '%@' (%@)", CurrentScriptDesc(), amount_typeString, @"cargo hold full with special cargo");
1380 : return;
1381 : }
1382 :
1383 : [self awardCommodityType:type amount:amount];
1384 : }
1385 :
1386 :
1387 : - (void) removeAllCargo
1388 : {
1389 : [self removeAllCargo:NO];
1390 : }
1391 :
1392 : - (void) removeAllCargo:(BOOL)forceRemoval
1393 : {
1394 : // Misnamed method. It only removes cargo measured in TONS, g & Kg items are not removed. --Kaks 20091004
1395 : OOCommodityType type;
1396 :
1397 : if (scriptTarget != self) return;
1398 :
1399 : if ([self status] != STATUS_DOCKED && !forceRemoval)
1400 : {
1401 : OOLogWARN(kOOLogRemoveAllCargoNotDocked, @"%@removeAllCargo only works when docked.", [NSString stringWithFormat:@" in %@, ", CurrentScriptDesc()]);
1402 : return;
1403 : }
1404 :
1405 : OOLog(kOOLogNoteRemoveAllCargo, @"%@ removeAllCargo", forceRemoval ? @"Forcing" : @"Going to");
1406 :
1407 : foreach(type, [shipCommodityData goods])
1408 : {
1409 : if ([shipCommodityData massUnitForGood:type] == UNITS_TONS)
1410 : {
1411 : [shipCommodityData setQuantity:0 forGood:type];
1412 : }
1413 : }
1414 :
1415 :
1416 : if (forceRemoval && [self status] != STATUS_DOCKED)
1417 : {
1418 : NSInteger i;
1419 : for (i = [cargo count] - 1; i >= 0; i--)
1420 : {
1421 : ShipEntity* canister = [cargo objectAtIndex:i];
1422 : if (!canister) break;
1423 : // Since we are forcing cargo removal, we don't really care about the unit of measurement. Any
1424 : // commodity at more than 1000kg or 1000000gr will be inside cargopods, so remove those too.
1425 : [cargo removeObjectAtIndex:i];
1426 : }
1427 : }
1428 :
1429 : DESTROY(specialCargo);
1430 :
1431 : [self calculateCurrentCargo];
1432 : }
1433 :
1434 :
1435 : - (void) useSpecialCargo:(NSString *)descriptionString
1436 : {
1437 : if (scriptTarget != self) return;
1438 :
1439 : [self removeAllCargo:YES];
1440 : OOLog(kOOLogNoteUseSpecialCargo, @"Going to useSpecialCargo:'%@'", descriptionString);
1441 : specialCargo = [OOExpand(descriptionString) retain];
1442 : }
1443 :
1444 :
1445 : - (void) testForEquipment:(NSString *)equipString //eg. EQ_NAVAL_ENERGY_UNIT
1446 : {
1447 : found_equipment = [self hasEquipmentItem:equipString];
1448 : }
1449 :
1450 :
1451 : - (void) awardFuel:(NSString *)valueString // add to fuel up to 7.0 LY
1452 : {
1453 : int delta = 10 * [valueString floatValue];
1454 : OOFuelQuantity scriptTargetFuelBeforeAward = [scriptTarget fuel];
1455 :
1456 : if (delta < 0 && scriptTargetFuelBeforeAward < (unsigned)-delta) [scriptTarget setFuel:0];
1457 : else
1458 : {
1459 : [scriptTarget setFuel:(scriptTargetFuelBeforeAward + delta)];
1460 : }
1461 : }
1462 :
1463 :
1464 : - (void) messageShipAIs:(NSString *)roles_message
1465 : {
1466 : NSMutableArray* tokens = ScanTokensFromString(roles_message);
1467 : NSString* roleString = nil;
1468 : NSString* messageString = nil;
1469 :
1470 : if ([tokens count] < 2)
1471 : {
1472 : OOLog(kOOLogSyntaxMessageShipAIs, @"***** SCRIPT ERROR: in %@, CANNOT messageShipAIs: '%@' (bad parameter count)", CurrentScriptDesc(), roles_message);
1473 : return;
1474 : }
1475 :
1476 : roleString = [tokens objectAtIndex:0];
1477 : [tokens removeObjectAtIndex:0];
1478 : messageString = [tokens componentsJoinedByString:@" "];
1479 :
1480 : NSArray *targets = [UNIVERSE findShipsMatchingPredicate:HasPrimaryRolePredicate
1481 : parameter:roleString
1482 : inRange:-1
1483 : ofEntity:nil];
1484 :
1485 : ShipEntity *target;
1486 : foreach(target, targets) {
1487 : [[target getAI] reactToMessage:messageString context:@"messageShipAIs:"];
1488 : }
1489 : }
1490 :
1491 :
1492 : - (void) ejectItem:(NSString *)itemKey
1493 : {
1494 : if (scriptTarget == nil) scriptTarget = self;
1495 : [scriptTarget ejectShipOfType:itemKey];
1496 : }
1497 :
1498 :
1499 : - (void) addShips:(NSString *)roles_number
1500 : {
1501 : NSMutableArray* tokens = ScanTokensFromString(roles_number);
1502 : NSString* roleString = nil;
1503 : NSString* numberString = nil;
1504 :
1505 : if ([tokens count] != 2)
1506 : {
1507 : OOLog(kOOLogSyntaxAddShips, @"***** SCRIPT ERROR: in %@, CANNOT addShips: '%@' (expected <role> <count>)", CurrentScriptDesc(), roles_number);
1508 : return;
1509 : }
1510 :
1511 : roleString = [tokens objectAtIndex:0];
1512 : numberString = [tokens objectAtIndex:1];
1513 :
1514 : int number = [numberString intValue];
1515 : if (number < 0)
1516 : {
1517 : OOLog(kOOLogSyntaxAddShips, @"***** SCRIPT ERROR: in %@, can't add %i ships -- that's less than zero, y'know..", CurrentScriptDesc(), number);
1518 : return;
1519 : }
1520 :
1521 : OOLog(kOOLogNoteAddShips, @"DEBUG: Going to add %d ships with role '%@'", number, roleString);
1522 :
1523 : while (number--)
1524 : [UNIVERSE witchspaceShipWithPrimaryRole:roleString];
1525 : }
1526 :
1527 :
1528 : - (void) addSystemShips:(NSString *)roles_number_position
1529 : {
1530 : NSMutableArray* tokens = ScanTokensFromString(roles_number_position);
1531 : NSString* roleString = nil;
1532 : NSString* numberString = nil;
1533 : NSString* positionString = nil;
1534 :
1535 : if ([tokens count] != 3)
1536 : {
1537 : OOLog(kOOLogSyntaxAddShips, @"***** SCRIPT ERROR: in %@, CANNOT addSystemShips: '%@' (expected <role> <count> <position>)", CurrentScriptDesc(), roles_number_position);
1538 : return;
1539 : }
1540 :
1541 : roleString = [tokens objectAtIndex:0];
1542 : numberString = [tokens objectAtIndex:1];
1543 : positionString = [tokens objectAtIndex:2];
1544 :
1545 : int number = [numberString intValue];
1546 : double posn = [positionString doubleValue];
1547 : if (number < 0)
1548 : {
1549 : OOLog(kOOLogSyntaxAddShips, @"***** SCRIPT ERROR: in %@, can't add %i ships -- that's less than zero, y'know..", CurrentScriptDesc(), number);
1550 : return;
1551 : }
1552 :
1553 : OOLog(kOOLogNoteAddShips, @"DEBUG: Going to add %d ships with role '%@' at a point %.3f along route1", number, roleString, posn);
1554 :
1555 : while (number--)
1556 : [UNIVERSE addShipWithRole:roleString nearRouteOneAt:posn];
1557 : }
1558 :
1559 :
1560 : - (void) addShipsAt:(NSString *)roles_number_system_x_y_z
1561 : {
1562 : NSMutableArray* tokens = ScanTokensFromString(roles_number_system_x_y_z);
1563 :
1564 : NSString* roleString = nil;
1565 : NSString* numberString = nil;
1566 : NSString* systemString = nil;
1567 : NSString* xString = nil;
1568 : NSString* yString = nil;
1569 : NSString* zString = nil;
1570 :
1571 : if ([tokens count] != 6)
1572 : {
1573 : OOLog(kOOLogSyntaxAddShips, @"***** SCRIPT ERROR: in %@, CANNOT addShipsAt: '%@' (expected <role> <count> <coordinate-system> <x> <y> <z>)", CurrentScriptDesc(), roles_number_system_x_y_z);
1574 : return;
1575 : }
1576 :
1577 : roleString = [tokens objectAtIndex:0];
1578 : numberString = [tokens objectAtIndex:1];
1579 : systemString = [tokens objectAtIndex:2];
1580 : xString = [tokens objectAtIndex:3];
1581 : yString = [tokens objectAtIndex:4];
1582 : zString = [tokens objectAtIndex:5];
1583 :
1584 : HPVector posn = make_HPvector([xString doubleValue], [yString doubleValue], [zString doubleValue]);
1585 :
1586 : int number = [numberString intValue];
1587 : if (number < 1)
1588 : {
1589 : OOLog(kOOLogSyntaxAddShips, @"----- WARNING in %@ Tried to add %i ships -- no ship added.", CurrentScriptDesc(), number);
1590 : return;
1591 : }
1592 :
1593 : OOLog(kOOLogNoteAddShips, @"DEBUG: Going to add %d ship(s) with role '%@' at point (%.3f, %.3f, %.3f) using system %@", number, roleString, posn.x, posn.y, posn.z, systemString);
1594 :
1595 : if (![UNIVERSE addShips: number withRole:roleString nearPosition: posn withCoordinateSystem: systemString])
1596 : {
1597 : OOLog(kOOLogScriptAddShipsFailed, @"***** SCRIPT ERROR: in %@, %@ could not add %u ships with role \"%@\"", CurrentScriptDesc(), @"addShipsAt:", number, roleString);
1598 : }
1599 : }
1600 :
1601 :
1602 : - (void) addShipsAtPrecisely:(NSString *)roles_number_system_x_y_z
1603 : {
1604 : NSMutableArray* tokens = ScanTokensFromString(roles_number_system_x_y_z);
1605 :
1606 : NSString* roleString = nil;
1607 : NSString* numberString = nil;
1608 : NSString* systemString = nil;
1609 : NSString* xString = nil;
1610 : NSString* yString = nil;
1611 : NSString* zString = nil;
1612 :
1613 : if ([tokens count] != 6)
1614 : {
1615 : OOLog(kOOLogSyntaxAddShips, @"***** SCRIPT ERROR: in %@,* CANNOT addShipsAtPrecisely: '%@' (expected <role> <count> <coordinate-system> <x> <y> <z>)", CurrentScriptDesc(), roles_number_system_x_y_z);
1616 : return;
1617 : }
1618 :
1619 : roleString = [tokens objectAtIndex:0];
1620 : numberString = [tokens objectAtIndex:1];
1621 : systemString = [tokens objectAtIndex:2];
1622 : xString = [tokens objectAtIndex:3];
1623 : yString = [tokens objectAtIndex:4];
1624 : zString = [tokens objectAtIndex:5];
1625 :
1626 : HPVector posn = make_HPvector([xString doubleValue], [yString doubleValue], [zString doubleValue]);
1627 :
1628 : int number = [numberString intValue];
1629 : if (number < 1)
1630 : {
1631 : OOLog(kOOLogSyntaxAddShips, @"----- WARNING: in %@, Can't add %i ships -- no ship added.", CurrentScriptDesc(), number);
1632 : return;
1633 : }
1634 :
1635 : OOLog(kOOLogNoteAddShips, @"DEBUG: Going to add %d ship(s) with role '%@' precisely at point (%.3f, %.3f, %.3f) using system %@", number, roleString, posn.x, posn.y, posn.z, systemString);
1636 :
1637 : if (![UNIVERSE addShips: number withRole:roleString atPosition: posn withCoordinateSystem: systemString])
1638 : {
1639 : OOLog(kOOLogScriptAddShipsFailed, @"***** SCRIPT ERROR: in %@, %@ could not add %u ships with role '%@'", CurrentScriptDesc(), @"addShipsAtPrecisely:", number, roleString);
1640 : }
1641 : }
1642 :
1643 :
1644 : - (void) addShipsWithinRadius:(NSString *)roles_number_system_x_y_z_r
1645 : {
1646 : NSMutableArray* tokens = ScanTokensFromString(roles_number_system_x_y_z_r);
1647 :
1648 : if ([tokens count] != 7)
1649 : {
1650 : OOLog(kOOLogSyntaxAddShips, @"***** SCRIPT ERROR: in %@, CANNOT 'addShipsWithinRadius: %@' (expected <role> <count> <coordinate-system> <x> <y> <z> <radius>))", CurrentScriptDesc(), roles_number_system_x_y_z_r);
1651 : return;
1652 : }
1653 :
1654 : NSString* roleString = [tokens objectAtIndex:0];
1655 : int number = [[tokens objectAtIndex:1] intValue];
1656 : NSString* systemString = [tokens objectAtIndex:2];
1657 : double x = [[tokens objectAtIndex:3] doubleValue];
1658 : double y = [[tokens objectAtIndex:4] doubleValue];
1659 : double z = [[tokens objectAtIndex:5] doubleValue];
1660 : GLfloat r = [[tokens objectAtIndex:6] floatValue];
1661 : HPVector posn = make_HPvector(x, y, z);
1662 :
1663 : if (number < 1)
1664 : {
1665 : OOLog(kOOLogSyntaxAddShips, @"----- WARNING: in %@, can't add %i ships -- no ship added.", CurrentScriptDesc(), number);
1666 : return;
1667 : }
1668 :
1669 : OOLog(kOOLogNoteAddShips, @"DEBUG: Going to add %d ship(s) with role '%@' within %.2f radius about point (%.3f, %.3f, %.3f) using system %@", number, roleString, r, x, y, z, systemString);
1670 :
1671 : if (![UNIVERSE addShips:number withRole: roleString nearPosition: posn withCoordinateSystem: systemString withinRadius: r])
1672 : {
1673 : OOLog(kOOLogScriptAddShipsFailed, @"***** SCRIPT ERROR :in %@, %@ could not add %u ships with role \"%@\"", CurrentScriptDesc(), @"addShipsWithinRadius:", number, roleString);
1674 : }
1675 : }
1676 :
1677 :
1678 : - (void) spawnShip:(NSString *)ship_key
1679 : {
1680 : if ([UNIVERSE spawnShip:ship_key])
1681 : {
1682 : OOLog(kOOLogNoteAddShips, @"DEBUG: Spawned ship with shipdata key '%@'.", ship_key);
1683 : }
1684 : else
1685 : {
1686 : OOLog(kOOLogScriptAddShipsFailed, @"***** SCRIPT ERROR: in %@, could not spawn ship with shipdata key '%@'.", CurrentScriptDesc(), ship_key);
1687 : }
1688 : }
1689 :
1690 :
1691 : - (void) set:(NSString *)missionvariable_value
1692 : {
1693 : NSMutableArray *tokens = ScanTokensFromString(missionvariable_value);
1694 : NSString *missionVariableString = nil;
1695 : NSString *valueString = nil;
1696 : BOOL hasMissionPrefix, hasLocalPrefix;
1697 :
1698 : if ([tokens count] < 2)
1699 : {
1700 : OOLog(kOOLogSyntaxSet, @"***** SCRIPT ERROR: in %@, CANNOT SET '%@' (expected mission_variable or local_variable followed by value expression)", CurrentScriptDesc(), missionvariable_value);
1701 : return;
1702 : }
1703 :
1704 : missionVariableString = [tokens objectAtIndex:0];
1705 : [tokens removeObjectAtIndex:0];
1706 : valueString = [tokens componentsJoinedByString:@" "];
1707 :
1708 : hasMissionPrefix = [missionVariableString hasPrefix:@"mission_"];
1709 : hasLocalPrefix = [missionVariableString hasPrefix:@"local_"];
1710 :
1711 : if (!hasMissionPrefix && !hasLocalPrefix)
1712 : {
1713 : OOLog(kOOLogSyntaxSet, @"***** SCRIPT ERROR: in %@, IDENTIFIER '%@' DOES NOT BEGIN WITH 'mission_' or 'local_'", CurrentScriptDesc(), missionVariableString);
1714 : return;
1715 : }
1716 :
1717 : OOLog(kOOLogNoteSet, @"DEBUG: script %@ is set to %@", missionVariableString, valueString);
1718 :
1719 : if (hasMissionPrefix)
1720 : {
1721 : [self setMissionVariable:valueString forKey:missionVariableString];
1722 : }
1723 : else
1724 : {
1725 : [self setLocalVariable:valueString forKey:missionVariableString andMission:sCurrentMissionKey];
1726 : }
1727 : }
1728 :
1729 :
1730 : - (void) reset:(NSString *)missionvariable
1731 : {
1732 : NSString* missionVariableString = [missionvariable stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
1733 : BOOL hasMissionPrefix, hasLocalPrefix;
1734 :
1735 : hasMissionPrefix = [missionVariableString hasPrefix:@"mission_"];
1736 : hasLocalPrefix = [missionVariableString hasPrefix:@"local_"];
1737 :
1738 : if (hasMissionPrefix)
1739 : {
1740 : [self setMissionVariable:nil forKey:missionVariableString];
1741 : }
1742 : else if (hasLocalPrefix)
1743 : {
1744 : [self setLocalVariable:nil forKey:missionVariableString andMission:sCurrentMissionKey];
1745 : }
1746 : else
1747 : {
1748 : OOLog(kOOLogSyntaxReset, @"***** SCRIPT ERROR: in %@, IDENTIFIER '%@' DOES NOT BEGIN WITH 'mission_' or 'local_'", CurrentScriptDesc(), missionVariableString);
1749 : }
1750 : }
1751 :
1752 :
1753 : - (void) increment:(NSString *)missionVariableString
1754 : {
1755 : BOOL hasMissionPrefix, hasLocalPrefix;
1756 : int value = 0;
1757 :
1758 : hasMissionPrefix = [missionVariableString hasPrefix:@"mission_"];
1759 : hasLocalPrefix = [missionVariableString hasPrefix:@"local_"];
1760 :
1761 : if (hasMissionPrefix)
1762 : {
1763 : value = [[self missionVariableForKey:missionVariableString] intValue];
1764 : value++;
1765 : [self setMissionVariable:[NSString stringWithFormat:@"%d", value] forKey:missionVariableString];
1766 : }
1767 : else if (hasLocalPrefix)
1768 : {
1769 : value = [[self localVariableForKey:missionVariableString andMission:sCurrentMissionKey] intValue];
1770 : value++;
1771 : [self setLocalVariable:[NSString stringWithFormat:@"%d", value] forKey:missionVariableString andMission:sCurrentMissionKey];
1772 : }
1773 : else
1774 : {
1775 : OOLog(kOOLogSyntaxIncrement, @"***** SCRIPT ERROR: in %@, IDENTIFIER '%@' DOES NOT BEGIN WITH 'mission_' or 'local_'", CurrentScriptDesc(), missionVariableString);
1776 : }
1777 : }
1778 :
1779 :
1780 : - (void) decrement:(NSString *)missionVariableString
1781 : {
1782 : BOOL hasMissionPrefix, hasLocalPrefix;
1783 : int value = 0;
1784 :
1785 : hasMissionPrefix = [missionVariableString hasPrefix:@"mission_"];
1786 : hasLocalPrefix = [missionVariableString hasPrefix:@"local_"];
1787 :
1788 : if (hasMissionPrefix)
1789 : {
1790 : value = [[self missionVariableForKey:missionVariableString] intValue];
1791 : value--;
1792 : [self setMissionVariable:[NSString stringWithFormat:@"%d", value] forKey:missionVariableString];
1793 : }
1794 : else if (hasLocalPrefix)
1795 : {
1796 : value = [[self localVariableForKey:missionVariableString andMission:sCurrentMissionKey] intValue];
1797 : value--;
1798 : [self setLocalVariable:[NSString stringWithFormat:@"%d", value] forKey:missionVariableString andMission:sCurrentMissionKey];
1799 : }
1800 : else
1801 : {
1802 : OOLog(kOOLogSyntaxDecrement, @"***** SCRIPT ERROR: in %@, IDENTIFIER '%@' DOES NOT BEGIN WITH 'mission_' or 'local_'", CurrentScriptDesc(), missionVariableString);
1803 : }
1804 : }
1805 :
1806 :
1807 : - (void) add:(NSString *)missionVariableString_value
1808 : {
1809 : NSString* missionVariableString = nil;
1810 : NSString* valueString;
1811 : double value;
1812 : NSMutableArray* tokens = ScanTokensFromString(missionVariableString_value);
1813 : BOOL hasMissionPrefix, hasLocalPrefix;
1814 :
1815 : if ([tokens count] < 2)
1816 : {
1817 : OOLog(kOOLogSyntaxAdd, @"***** SCRIPT ERROR: in %@, CANNOT ADD: '%@'", CurrentScriptDesc(), missionVariableString_value);
1818 : return;
1819 : }
1820 :
1821 : missionVariableString = [tokens objectAtIndex:0];
1822 : [tokens removeObjectAtIndex:0];
1823 : valueString = [tokens componentsJoinedByString:@" "];
1824 :
1825 : hasMissionPrefix = [missionVariableString hasPrefix:@"mission_"];
1826 : hasLocalPrefix = [missionVariableString hasPrefix:@"local_"];
1827 :
1828 : if (hasMissionPrefix)
1829 : {
1830 : value = [[self missionVariableForKey:missionVariableString] doubleValue];
1831 : value += [valueString doubleValue];
1832 : [self setMissionVariable:[NSString stringWithFormat:@"%f", value] forKey:missionVariableString];
1833 : }
1834 : else if (hasLocalPrefix)
1835 : {
1836 : value = [[self localVariableForKey:missionVariableString andMission:sCurrentMissionKey] doubleValue];
1837 : value += [valueString doubleValue];
1838 : [self setLocalVariable:[NSString stringWithFormat:@"%f", value] forKey:missionVariableString andMission:sCurrentMissionKey];
1839 : }
1840 : else
1841 : {
1842 : OOLog(kOOLogSyntaxAdd, @"***** SCRIPT ERROR: in %@, CANNOT ADD: '%@' -- IDENTIFIER '%@' DOES NOT BEGIN WITH 'mission_' or 'local_'", CurrentScriptDesc(), missionVariableString_value, missionVariableString_value);
1843 : }
1844 : }
1845 :
1846 :
1847 : - (void) subtract:(NSString *)missionVariableString_value
1848 : {
1849 : NSString* missionVariableString = nil;
1850 : NSString* valueString;
1851 : double value;
1852 : NSMutableArray* tokens = ScanTokensFromString(missionVariableString_value);
1853 : BOOL hasMissionPrefix, hasLocalPrefix;
1854 :
1855 : if ([tokens count] < 2)
1856 : {
1857 : OOLog(kOOLogSyntaxSubtract, @"***** SCRIPT ERROR: in %@, CANNOT SUBTRACT: '%@'", CurrentScriptDesc(), missionVariableString_value);
1858 : return;
1859 : }
1860 :
1861 : missionVariableString = [tokens objectAtIndex:0];
1862 : [tokens removeObjectAtIndex:0];
1863 : valueString = [tokens componentsJoinedByString:@" "];
1864 :
1865 : hasMissionPrefix = [missionVariableString hasPrefix:@"mission_"];
1866 : hasLocalPrefix = [missionVariableString hasPrefix:@"local_"];
1867 :
1868 : if (hasMissionPrefix)
1869 : {
1870 : value = [[self missionVariableForKey:missionVariableString] doubleValue];
1871 : value -= [valueString doubleValue];
1872 : [self setMissionVariable:[NSString stringWithFormat:@"%f", value] forKey:missionVariableString];
1873 : }
1874 : else if (hasLocalPrefix)
1875 : {
1876 : value = [[self localVariableForKey:missionVariableString andMission:sCurrentMissionKey] doubleValue];
1877 : value -= [valueString doubleValue];
1878 : [self setLocalVariable:[NSString stringWithFormat:@"%f", value] forKey:missionVariableString andMission:sCurrentMissionKey];
1879 : }
1880 : else
1881 : {
1882 : OOLog(kOOLogSyntaxSubtract, @"***** SCRIPT ERROR: in %@, CANNOT SUBTRACT: '%@' -- IDENTIFIER '%@' DOES NOT BEGIN WITH 'mission_' or 'local_'", CurrentScriptDesc(), missionVariableString_value, missionVariableString_value);
1883 : }
1884 : }
1885 :
1886 :
1887 : - (void) checkForShips:(NSString *)roleString
1888 : {
1889 : shipsFound = [UNIVERSE countShipsWithPrimaryRole:roleString];
1890 : }
1891 :
1892 :
1893 : - (void) resetScriptTimer
1894 : {
1895 : script_time = 0.0;
1896 : script_time_check = SCRIPT_TIMER_INTERVAL;
1897 : script_time_interval = SCRIPT_TIMER_INTERVAL;
1898 : }
1899 :
1900 :
1901 : - (void) addMissionText: (NSString *)textKey
1902 : {
1903 : NSString *text = nil;
1904 :
1905 : if ([textKey isEqualToString:lastTextKey]) return; // don't repeatedly add the same text
1906 : [lastTextKey release];
1907 : lastTextKey = [textKey copy];
1908 :
1909 : // Replace literal \n in strings with line breaks and perform expansions.
1910 : text = [[UNIVERSE missiontext] oo_stringForKey:textKey];
1911 : if (text == nil) return;
1912 : text = OOExpandWithOptions(OOStringExpanderDefaultRandomSeed(), kOOExpandBackslashN, text);
1913 : text = [self replaceVariablesInString:text];
1914 :
1915 : [self addLiteralMissionText:text];
1916 : }
1917 :
1918 :
1919 : - (void) addLiteralMissionText:(NSString *)text
1920 : {
1921 : if (text != nil)
1922 : {
1923 : GuiDisplayGen *gui = [UNIVERSE gui];
1924 :
1925 : NSString *para = nil;
1926 : foreach (para, [text componentsSeparatedByString:@"\n"])
1927 : {
1928 : missionTextRow = [gui addLongText:para startingAtRow:missionTextRow align:GUI_ALIGN_LEFT];
1929 : }
1930 : }
1931 : }
1932 :
1933 :
1934 : - (void) setMissionChoiceByTextEntry:(BOOL)enable
1935 : {
1936 : MyOpenGLView *gameView = [UNIVERSE gameView];
1937 : _missionTextEntry = enable;
1938 : [gameView resetTypedString];
1939 : }
1940 :
1941 :
1942 : - (void) setMissionChoices:(NSString *)choicesKey // choicesKey is a key for a dictionary of
1943 : { // choices/choice phrases in missiontext.plist and also..
1944 : NSDictionary *choicesDict = [[UNIVERSE missiontext] oo_dictionaryForKey:choicesKey];
1945 : if ([choicesDict count] == 0)
1946 : {
1947 : return;
1948 : }
1949 : [self setMissionChoicesDictionary:choicesDict];
1950 : }
1951 :
1952 :
1953 : - (void) setMissionChoicesDictionary:(NSDictionary *)choicesDict
1954 : {
1955 : unsigned i;
1956 : bool keysOK = true;
1957 : GuiDisplayGen* gui = [UNIVERSE gui];
1958 : // TODO: MORE STUFF HERE
1959 : //
1960 : // What it does now:
1961 : // find list of choices in missiontext.plist
1962 : // add them to gui setting the key for each line to the key in the dict of choices
1963 : // and the text of the line to the value in the dict of choices
1964 : // and also set the selectable range
1965 : // ++ change the mission screen's response to wait for a choice
1966 : // and only if the selectable range is not present ask:
1967 : // Press Space Commander...
1968 : //
1969 :
1970 : NSUInteger end_row = 21;
1971 : if ([[self hud] allowBigGui])
1972 : {
1973 : end_row = 27;
1974 : }
1975 :
1976 : NSArray *choiceKeys = [choicesDict allKeys];
1977 : /* Guard against potential for numeric keys in dictionary, which
1978 : * would cause an unhandled exception in the sorter. See
1979 : * OOJavaScriptEngine::OOJSDictionaryFromJSObject for further
1980 : * thoughts. - CIM 15/2/13 */
1981 : for (i=0; i < [choiceKeys count]; i++)
1982 : {
1983 : if (![[choiceKeys objectAtIndex:i] isKindOfClass:[NSString class]])
1984 : {
1985 : OOLog(@"test.script.error",@"Choices list in mission screen has non-string value %@",[choiceKeys objectAtIndex:i]);
1986 : keysOK = false;
1987 : }
1988 : }
1989 : if (keysOK)
1990 : {
1991 : // only try this if they're all strings
1992 : choiceKeys = [choiceKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
1993 : }
1994 :
1995 : NSInteger keysCount = [choiceKeys count];
1996 : if ((end_row + 1) < [choiceKeys count]) {
1997 : OOLogERR(kOOLogException, @"in mission.runScreen choices: number of choices defined (%i) is greater than available lines (%i). Check HUD settings for allowBigGui.", [choiceKeys count], (end_row + 1));
1998 : keysCount = end_row + 1;
1999 : }
2000 :
2001 : [gui setText:@"" forRow:end_row]; // clears out the 'Press spacebar' message
2002 : [gui setKey:@"" forRow:end_row]; // clears the key to enable pollDemoControls to check for a selection
2003 : [gui setSelectableRange:NSMakeRange(0,0)]; // clears the selectable range
2004 : [UNIVERSE enterGUIViewModeWithMouseInteraction:YES]; // enables mouse selection of the choices list items
2005 :
2006 : OOGUIRow choicesRow = (end_row+1) - keysCount;
2007 : NSEnumerator *choiceEnum = nil;
2008 : NSString *choiceKey = nil;
2009 : id choiceValue = nil;
2010 : NSString *choiceText = nil;
2011 :
2012 : BOOL selectableRowExists = NO;
2013 : NSUInteger firstSelectableRow = end_row;
2014 :
2015 : for (choiceEnum = [choiceKeys objectEnumerator]; (choiceKey = [choiceEnum nextObject]); )
2016 : {
2017 : choiceValue = [choicesDict objectForKey:choiceKey];
2018 : OOGUIAlignment alignment = GUI_ALIGN_CENTER;
2019 : OOColor *rowColor = [OOColor yellowColor];
2020 : BOOL selectable = YES;
2021 : if ([choiceValue isKindOfClass:[NSString class]])
2022 : {
2023 : choiceText = [NSString stringWithFormat:@" %@ ",(NSString*)choiceValue];
2024 : }
2025 : else if ([choiceValue isKindOfClass:[NSDictionary class]])
2026 : {
2027 : NSDictionary *choiceOpts = (NSDictionary*)choiceValue;
2028 : choiceText = [NSString stringWithFormat:@" %@ ",[choiceOpts oo_stringForKey:@"text"]];
2029 : NSString *alignmentChoice = [choiceOpts oo_stringForKey:@"alignment" defaultValue:@"CENTER"];
2030 : if ([alignmentChoice isEqualToString:@"LEFT"])
2031 : {
2032 : alignment = GUI_ALIGN_LEFT;
2033 : }
2034 : else if ([alignmentChoice isEqualToString:@"RIGHT"])
2035 : {
2036 : alignment = GUI_ALIGN_RIGHT;
2037 : }
2038 : id colorDesc = [choiceOpts objectForKey:@"color"];
2039 : if ([choiceOpts oo_boolForKey:@"unselectable"])
2040 : {
2041 : selectable = NO;
2042 : }
2043 : if (colorDesc != nil)
2044 : {
2045 : rowColor = [OOColor colorWithDescription:colorDesc];
2046 : }
2047 : else if (!selectable) // different default
2048 : {
2049 : rowColor = [OOColor darkGrayColor];
2050 : }
2051 : }
2052 : else
2053 : {
2054 : continue; // invalid type
2055 : }
2056 : choiceText = OOExpand(choiceText);
2057 : choiceText = [self replaceVariablesInString:choiceText];
2058 : // allow blank rows
2059 : if (![choiceText isEqualToString:@" "])
2060 : {
2061 : [gui setText:choiceText forRow:choicesRow align: alignment];
2062 : if (selectable)
2063 : {
2064 : [gui setKey:choiceKey forRow:choicesRow];
2065 : }
2066 : else
2067 : {
2068 : [gui setKey:GUI_KEY_SKIP forRow:choicesRow];
2069 : }
2070 : [gui setColor:rowColor forRow:choicesRow];
2071 : if (selectable && !selectableRowExists)
2072 : {
2073 : selectableRowExists = YES;
2074 : firstSelectableRow = choicesRow;
2075 : }
2076 : }
2077 : else
2078 : {
2079 : [gui setKey:GUI_KEY_SKIP forRow:choicesRow];
2080 : }
2081 : choicesRow++;
2082 : if (choicesRow > (end_row + 1)) break;
2083 : }
2084 :
2085 : if (!selectableRowExists)
2086 : {
2087 : // just in case choices are set but they're all blank.
2088 : [gui setText:@" " forRow:end_row align: GUI_ALIGN_CENTER];
2089 : [gui setKey:@"" forRow:end_row];
2090 : [gui setColor:[OOColor yellowColor] forRow:end_row];
2091 : }
2092 :
2093 : [gui setSelectableRange:NSMakeRange((end_row+1) - keysCount, keysCount)];
2094 : [gui setSelectedRow: firstSelectableRow];
2095 :
2096 : [self resetMissionChoice];
2097 : }
2098 :
2099 :
2100 : - (void) resetMissionChoice
2101 : {
2102 : [self setMissionChoice:nil];
2103 : }
2104 :
2105 :
2106 : - (void) clearMissionScreen
2107 : {
2108 : [self setMissionOverlayDescriptor:nil];
2109 : [self setMissionBackgroundDescriptor:nil];
2110 : [self setMissionBackgroundSpecial:nil];
2111 : [self setMissionTitle:nil];
2112 : [self setMissionMusic:nil];
2113 : [self showShipModel:nil];
2114 : }
2115 :
2116 :
2117 : - (void) addMissionDestination:(NSString *)destinations
2118 : {
2119 : unsigned j;
2120 : int dest;
2121 : NSMutableArray *tokens = ScanTokensFromString(destinations);
2122 :
2123 : for (j = 0; j < [tokens count]; j++)
2124 : {
2125 : dest = [tokens oo_intAtIndex:j];
2126 : if (dest < 0 || dest > 255)
2127 : continue;
2128 :
2129 : [self addMissionDestinationMarker:[self defaultMarker:dest]];
2130 : }
2131 : }
2132 :
2133 :
2134 : - (void) removeMissionDestination:(NSString *)destinations
2135 : {
2136 : unsigned j;
2137 : int dest;
2138 : NSMutableArray *tokens = ScanTokensFromString(destinations);
2139 :
2140 : for (j = 0; j < [tokens count]; j++)
2141 : {
2142 : dest = [[tokens objectAtIndex:j] intValue];
2143 : if (dest < 0 || dest > 255) continue;
2144 :
2145 : [self removeMissionDestinationMarker:[self defaultMarker:dest]];
2146 : }
2147 : }
2148 :
2149 :
2150 : - (void) showShipModel:(NSString *)role
2151 : {
2152 : if ([role isEqualToString:@"none"] || [role length] == 0)
2153 : {
2154 : [UNIVERSE removeDemoShips];
2155 : return;
2156 : }
2157 :
2158 : ShipEntity *ship = [UNIVERSE makeDemoShipWithRole:role spinning:YES];
2159 : OOLog(kOOLogNoteShowShipModel, @"::::: showShipModel:'%@' (%@) (%@)", role, ship, [ship name]);
2160 : }
2161 :
2162 :
2163 : - (void) setMissionMusic:(NSString *)value
2164 : {
2165 : if ([value length] == 0 || [[value lowercaseString] isEqualToString:@"none"])
2166 : {
2167 : value = nil;
2168 : }
2169 : [[OOMusicController sharedController] setMissionMusic:value];
2170 : }
2171 :
2172 :
2173 : - (NSString *) missionTitle
2174 : {
2175 : return _missionTitle;
2176 : }
2177 :
2178 :
2179 : - (void) setMissionTitle:(NSString *)value
2180 : {
2181 : if (_missionTitle != value)
2182 : {
2183 : [_missionTitle release];
2184 : _missionTitle = [value copy];
2185 : }
2186 : }
2187 :
2188 :
2189 0 : - (void) setMissionImage:(NSString *)value
2190 : {
2191 : if ([value length] != 0 && ![[value lowercaseString] isEqualToString:@"none"])
2192 : {
2193 : [self setMissionOverlayDescriptor:[NSDictionary dictionaryWithObject:value forKey:@"name"]];
2194 : }
2195 : else
2196 : {
2197 : [self setMissionOverlayDescriptor:nil];
2198 : }
2199 :
2200 : }
2201 :
2202 :
2203 0 : - (void) setMissionBackground:(NSString *)value
2204 : {
2205 : if ([value length] != 0 && ![[value lowercaseString] isEqualToString:@"none"])
2206 : {
2207 : [self setMissionBackgroundDescriptor:[NSDictionary dictionaryWithObject:value forKey:@"name"]];
2208 : }
2209 : else
2210 : {
2211 : [self setMissionBackgroundDescriptor:nil];
2212 : }
2213 : }
2214 :
2215 :
2216 : - (void) setFuelLeak:(NSString *)value
2217 : {
2218 : if (scriptTarget != self)
2219 : {
2220 : [scriptTarget setFuel:0];
2221 : return;
2222 : }
2223 :
2224 : fuel_leak_rate = [value doubleValue];
2225 : if (fuel_leak_rate > 0)
2226 : {
2227 : [self playFuelLeak];
2228 : [UNIVERSE addMessage:DESC(@"danger-fuel-leak") forCount:6];
2229 : OOLog(kOOLogNoteFuelLeak, @"%@", @"FUEL LEAK activated!");
2230 : }
2231 : }
2232 :
2233 :
2234 : - (NSNumber *) fuelLeakRate_number
2235 : {
2236 : return [NSNumber numberWithFloat:[self fuelLeakRate]];
2237 : }
2238 :
2239 :
2240 : - (void) setSunNovaIn:(NSString *)time_value
2241 : {
2242 : double time_until_nova = [time_value doubleValue];
2243 : [[UNIVERSE sun] setGoingNova:YES inTime: time_until_nova];
2244 : }
2245 :
2246 :
2247 : - (void) launchFromStation
2248 : {
2249 : // ensure autosave is ready for the next unscripted launch
2250 : if ([UNIVERSE autoSave]) [UNIVERSE setAutoSaveNow:YES];
2251 : if ([self status] == STATUS_DOCKING) [self setStatus:STATUS_DOCKED]; // needed here to prevent the normal update from continuing with docking.
2252 : [self leaveDock:[self dockedStation]];
2253 : }
2254 :
2255 :
2256 : - (void) blowUpStation
2257 : {
2258 : StationEntity *mainStation = nil;
2259 :
2260 : mainStation = [UNIVERSE station];
2261 : if (mainStation != nil)
2262 : {
2263 : [UNIVERSE unMagicMainStation];
2264 : [mainStation takeEnergyDamage:500000000.0 from:nil becauseOf:nil weaponIdentifier:@""]; // 500 million should do it!
2265 : }
2266 : }
2267 :
2268 :
2269 : - (void) sendAllShipsAway
2270 : {
2271 : if (!UNIVERSE)
2272 : return;
2273 : int ent_count = UNIVERSE->n_entities;
2274 : Entity** uni_entities = UNIVERSE->sortedEntities; // grab the public sorted list
2275 : Entity* my_entities[ent_count];
2276 : int i;
2277 : for (i = 0; i < ent_count; i++)
2278 : my_entities[i] = [uni_entities[i] retain]; // retained
2279 :
2280 : for (i = 1; i < ent_count; i++)
2281 : {
2282 : Entity* e1 = my_entities[i];
2283 : if ([e1 isShip])
2284 : {
2285 : ShipEntity* se1 = (ShipEntity*)e1;
2286 : int e_class = [e1 scanClass];
2287 : if (((e_class == CLASS_NEUTRAL)||(e_class == CLASS_POLICE)||(e_class == CLASS_MILITARY)||(e_class == CLASS_THARGOID)) &&
2288 : ! ([se1 isStation] && [se1 maxFlightSpeed] == 0) && // exclude only stations, not carriers.
2289 : [se1 hasHyperspaceMotor]) // exclude non jumping ships. Escorts will still be able to follow a mother.
2290 : {
2291 : AI* se1AI = [se1 getAI];
2292 : [se1 setFuel:MAX(PLAYER_MAX_FUEL, [se1 fuelCapacity])];
2293 : [se1 setAITo:@"exitingTraderAI.plist"]; // lets them return to their previous state after the jump
2294 : [se1AI setState:@"EXIT_SYSTEM"];
2295 : // The following should prevent all ships leaving at once (freezes oolite on slower machines)
2296 : [se1AI setNextThinkTime:[UNIVERSE getTime] + 3 + (ranrot_rand() & 15)];
2297 : [se1 setPrimaryRole:@"oolite-none"]; // prevents new ship from appearing at witchpoint when this one leaves!
2298 : }
2299 : }
2300 : }
2301 :
2302 : for (i = 0; i < ent_count; i++)
2303 : {
2304 : [my_entities[i] release]; // released
2305 : }
2306 : }
2307 :
2308 :
2309 : - (OOPlanetEntity *) addPlanet: (NSString *)planetKey
2310 : {
2311 : OOLog(kOOLogNoteAddPlanet, @"addPlanet: %@", planetKey);
2312 :
2313 : if (!UNIVERSE)
2314 : return nil;
2315 : NSDictionary* dict = [[UNIVERSE systemManager] getPropertiesForSystemKey:planetKey];
2316 : if (!dict)
2317 : {
2318 : OOLog(@"script.error.addPlanet.keyNotFound", @"***** ERROR: could not find an entry in planetinfo.plist for '%@'", planetKey);
2319 : return nil;
2320 : }
2321 :
2322 : /*- add planet -*/
2323 : OOLog(kOOLogDebugAddPlanet, @"DEBUG: initPlanetFromDictionary: %@", dict);
2324 : OOPlanetEntity *planet = [[[OOPlanetEntity alloc] initFromDictionary:dict withAtmosphere:YES andSeed:[[UNIVERSE systemManager] getRandomSeedForCurrentSystem] forSystem:system_id] autorelease];
2325 :
2326 : Quaternion planetOrientation;
2327 : if (ScanQuaternionFromString([dict objectForKey:@"orientation"], &planetOrientation))
2328 : {
2329 : [planet setOrientation:planetOrientation];
2330 : }
2331 :
2332 : if (![dict objectForKey:@"position"])
2333 : {
2334 : OOLog(@"script.error.addPlanet.noPosition", @"***** ERROR: you must specify a position for scripted planet '%@' before it can be created", planetKey);
2335 : return nil;
2336 : }
2337 :
2338 : NSString *positionString = [dict objectForKey:@"position"];
2339 : if([positionString hasPrefix:@"abs "] && ([UNIVERSE planet] != nil || [UNIVERSE sun] !=nil))
2340 : {
2341 : OOLogWARN(@"script.deprecated", @"setting %@ for %@ '%@' in 'abs' inside .plists can cause compatibility issues across Oolite versions. Use coordinates relative to main system objects instead.",@"position",@"planet",planetKey);
2342 : }
2343 :
2344 : HPVector posn = [UNIVERSE coordinatesFromCoordinateSystemString:positionString];
2345 : if (posn.x || posn.y || posn.z)
2346 : {
2347 : OOLog(kOOLogDebugAddPlanet, @"planet position (%.2f %.2f %.2f) derived from %@", posn.x, posn.y, posn.z, positionString);
2348 : }
2349 : else
2350 : {
2351 : ScanHPVectorFromString(positionString, &posn);
2352 : OOLog(kOOLogDebugAddPlanet, @"planet position (%.2f %.2f %.2f) derived from %@", posn.x, posn.y, posn.z, positionString);
2353 : }
2354 : [planet setPosition: posn];
2355 :
2356 : [UNIVERSE addEntity:planet];
2357 : return planet;
2358 : }
2359 :
2360 :
2361 : - (OOPlanetEntity *) addMoon: (NSString *)moonKey
2362 : {
2363 : OOLog(kOOLogNoteAddPlanet, @"DEBUG: addMoon '%@'", moonKey);
2364 :
2365 : if (!UNIVERSE)
2366 : return nil;
2367 : NSDictionary* dict = [[UNIVERSE systemManager] getPropertiesForSystemKey:moonKey];
2368 : if (!dict)
2369 : {
2370 : OOLog(@"script.error.addPlanet.keyNotFound", @"***** ERROR: could not find an entry in planetinfo.plist for '%@'", moonKey);
2371 : return nil;
2372 : }
2373 :
2374 : OOLog(kOOLogDebugAddPlanet, @"DEBUG: initMoonFromDictionary: %@", dict);
2375 : OOPlanetEntity *planet = [[[OOPlanetEntity alloc] initFromDictionary:dict withAtmosphere:NO andSeed:[[UNIVERSE systemManager] getRandomSeedForCurrentSystem] forSystem:system_id] autorelease];
2376 :
2377 : Quaternion planetOrientation;
2378 : if (ScanQuaternionFromString([dict objectForKey:@"orientation"], &planetOrientation))
2379 : {
2380 : [planet setOrientation:planetOrientation];
2381 : }
2382 :
2383 : if (![dict objectForKey:@"position"])
2384 : {
2385 : OOLog(@"script.error.addPlanet.noPosition", @"***** ERROR: you must specify a position for scripted moon '%@' before it can be created", moonKey);
2386 : return nil;
2387 : }
2388 :
2389 : NSString *positionString = [dict objectForKey:@"position"];
2390 : if([positionString hasPrefix:@"abs "] && ([UNIVERSE planet] != nil || [UNIVERSE sun] !=nil))
2391 : {
2392 : OOLogWARN(@"script.deprecated", @"setting %@ for %@ '%@' in 'abs' inside .plists can cause compatibility issues across Oolite versions. Use coordinates relative to main system objects instead.",@"position",@"moon",moonKey);
2393 : }
2394 : HPVector posn = [UNIVERSE coordinatesFromCoordinateSystemString:positionString];
2395 : if (posn.x || posn.y || posn.z)
2396 : {
2397 : OOLog(kOOLogDebugAddPlanet, @"moon position (%.2f %.2f %.2f) derived from %@", posn.x, posn.y, posn.z, positionString);
2398 : }
2399 : else
2400 : {
2401 : ScanHPVectorFromString(positionString, &posn);
2402 : OOLog(kOOLogDebugAddPlanet, @"moon position (%.2f %.2f %.2f) derived from %@", posn.x, posn.y, posn.z, positionString);
2403 : }
2404 : [planet setPosition: posn];
2405 :
2406 : [UNIVERSE addEntity:planet];
2407 : return planet;
2408 : }
2409 :
2410 :
2411 : - (void) debugOn
2412 : {
2413 : OOLogSetDisplayMessagesInClass(kOOLogDebugOnMetaClass, YES);
2414 : OOLog(kOOLogDebugOnOff, @"%@", @"SCRIPT debug messages ON");
2415 : }
2416 :
2417 :
2418 : - (void) debugOff
2419 : {
2420 : OOLog(kOOLogDebugOnOff, @"%@", @"SCRIPT debug messages OFF");
2421 : OOLogSetDisplayMessagesInClass(kOOLogDebugOnMetaClass, NO);
2422 : }
2423 :
2424 :
2425 : - (void) debugMessage:(NSString *)args
2426 : {
2427 : OOLog(kOOLogDebugMessage, @"SCRIPT debugMessage: %@", args);
2428 : }
2429 :
2430 :
2431 : - (void) playSound:(NSString *) soundName
2432 : {
2433 : [self playLegacyScriptSound:soundName];
2434 : }
2435 :
2436 : /*-----------------------------------------------------*/
2437 :
2438 :
2439 : - (void) doMissionCallback
2440 : {
2441 : // make sure we don't call the same callback twice
2442 : _missionWithCallback = NO;
2443 : [[OOJavaScriptEngine sharedEngine] runMissionCallback];
2444 : }
2445 :
2446 :
2447 : - (void) clearMissionScreenID
2448 : {
2449 : [_missionScreenID release];
2450 : _missionScreenID = nil;
2451 : }
2452 :
2453 :
2454 : - (void) setMissionScreenID:(NSString *)msid
2455 : {
2456 : _missionScreenID = [msid retain];
2457 : }
2458 :
2459 :
2460 : - (NSString *) missionScreenID
2461 : {
2462 : return _missionScreenID;
2463 : }
2464 :
2465 :
2466 : - (void) endMissionScreenAndNoteOpportunity
2467 : {
2468 : _missionAllowInterrupt = NO;
2469 : [self clearMissionScreenID];
2470 : // Older scripts might intercept missionScreenEnded first, and call secondary mission screens.
2471 : if(![self doWorldEventUntilMissionScreen:OOJSID("missionScreenEnded")])
2472 : {
2473 : // if we're here, no mission screen is running. Opportunity! :)
2474 : [self doWorldEventUntilMissionScreen:OOJSID("missionScreenOpportunity")];
2475 : }
2476 : }
2477 :
2478 :
2479 : - (void) setGuiToMissionScreen
2480 : {
2481 : // reset special background as legacy scripts can't use it, and this
2482 : // is only called by legacy scripts
2483 : [self setMissionBackgroundSpecial:nil];
2484 : // likewise exit screen target
2485 : [self setMissionExitScreen:GUI_SCREEN_STATUS];
2486 :
2487 : [self setGuiToMissionScreenWithCallback:NO];
2488 : }
2489 :
2490 :
2491 : - (void) refreshMissionScreenTextEntry
2492 : {
2493 : MyOpenGLView *gameView = [UNIVERSE gameView];
2494 : GuiDisplayGen *gui = [UNIVERSE gui];
2495 : NSUInteger end_row = 21;
2496 : if ([[self hud] allowBigGui])
2497 : {
2498 : end_row = 27;
2499 : }
2500 :
2501 : [gui setText:[NSString stringWithFormat:DESC(@"mission-screen-text-prompt-@"), [gameView typedString]] forRow:end_row align:GUI_ALIGN_LEFT];
2502 : [gui setColor:[OOColor cyanColor] forRow:end_row];
2503 :
2504 : [gui setShowTextCursor:YES];
2505 : [gui setCurrentRow:end_row];
2506 :
2507 : }
2508 :
2509 :
2510 : - (void) setGuiToMissionScreenWithCallback:(BOOL) callback
2511 : {
2512 : GuiDisplayGen *gui = [UNIVERSE gui];
2513 : OOGUIScreenID oldScreen = gui_screen;
2514 : NSUInteger end_row = 21;
2515 : if ([[self hud] allowBigGui])
2516 : {
2517 : end_row = 27;
2518 : }
2519 :
2520 : // GUI stuff
2521 : {
2522 : [gui clear];
2523 : [gui setTitle:[self missionTitle] ?: DESC(@"mission-information")];
2524 :
2525 : if (!_missionTextEntry)
2526 : {
2527 : [gui setText:DESC(@"press-space-commander") forRow:end_row align:GUI_ALIGN_CENTER];
2528 : [gui setColor:[OOColor yellowColor] forRow:end_row];
2529 : [gui setKey:@"spacebar" forRow:end_row];
2530 : [gui setShowTextCursor:NO];
2531 : }
2532 : else
2533 : {
2534 : [self refreshMissionScreenTextEntry];
2535 : }
2536 : [gui setSelectableRange:NSMakeRange(0,0)];
2537 :
2538 : [gui setForegroundTextureDescriptor:[self missionOverlayDescriptorOrDefault]];
2539 : NSDictionary *background_desc = [self missionBackgroundDescriptorOrDefault];
2540 : [gui setBackgroundTextureDescriptor:background_desc];
2541 : // must set special second as setting the descriptor resets it
2542 : BOOL overridden = ([self missionBackgroundDescriptor] != nil);
2543 : [gui setBackgroundTextureSpecial:[self missionBackgroundSpecial] withBackground:!overridden];
2544 :
2545 :
2546 : }
2547 : /* ends */
2548 :
2549 : missionTextRow = 1;
2550 :
2551 :
2552 : if (gui)
2553 : gui_screen = GUI_SCREEN_MISSION;
2554 :
2555 : if (lastTextKey)
2556 : {
2557 : [lastTextKey release];
2558 : lastTextKey = nil;
2559 : }
2560 :
2561 : [[OOMusicController sharedController] playMissionMusic];
2562 :
2563 : // the following are necessary...
2564 : [UNIVERSE enterGUIViewModeWithMouseInteraction:NO];
2565 : _missionWithCallback = callback;
2566 : _missionAllowInterrupt = NO;
2567 : [self noteGUIDidChangeFrom:oldScreen to:gui_screen];
2568 :
2569 : }
2570 :
2571 :
2572 : - (void) setBackgroundFromDescriptionsKey:(NSString*) d_key
2573 : {
2574 : NSArray * items = (NSArray *)[[UNIVERSE descriptions] objectForKey:d_key];
2575 : //
2576 : if (!items)
2577 : return;
2578 : //
2579 : [self addScene: items atOffset: kZeroVector];
2580 : //
2581 : [self setShowDemoShips: YES];
2582 : }
2583 :
2584 :
2585 : - (void) addScene:(NSArray *)items atOffset:(Vector)off
2586 : {
2587 : unsigned i;
2588 :
2589 : if (items == nil) return;
2590 :
2591 : for (i = 0; i < [items count]; i++)
2592 : {
2593 : id item = [items objectAtIndex:i];
2594 : if ([item isKindOfClass:[NSString class]])
2595 : {
2596 : [self processSceneString:item atOffset: off];
2597 : }
2598 : else if ([item isKindOfClass:[NSArray class]])
2599 : {
2600 : [self addScene:item atOffset: off];
2601 : }
2602 : else if ([item isKindOfClass:[NSDictionary class]])
2603 : {
2604 : [self processSceneDictionary:item atOffset: off];
2605 : }
2606 : }
2607 : }
2608 :
2609 :
2610 : - (BOOL) processSceneDictionary:(NSDictionary *) couplet atOffset:(Vector) off
2611 : {
2612 : NSArray *conditions = [couplet objectForKey:@"conditions"];
2613 : NSArray *actions = nil;
2614 : if ([couplet objectForKey:@"do"])
2615 : actions = [NSArray arrayWithObject: [couplet objectForKey:@"do"]];
2616 : NSArray *else_actions = nil;
2617 : if ([couplet objectForKey:@"else"])
2618 : else_actions = [NSArray arrayWithObject: [couplet objectForKey:@"else"]];
2619 : BOOL success = YES;
2620 : if (conditions == nil)
2621 : {
2622 : OOLog(@"script.scene.couplet.badConditions", @"***** SCENE ERROR: %@ - conditions not %@, returning %@.", [couplet description], @" found",@"YES and performing 'do' actions");
2623 : }
2624 : else
2625 : {
2626 : if (![conditions isKindOfClass:[NSArray class]])
2627 : {
2628 : OOLog(@"script.scene.couplet.badConditions", @"***** SCENE ERROR: %@ - conditions not %@, returning %@.", [conditions description], @"an array",@"NO");
2629 : return NO;
2630 : }
2631 : }
2632 :
2633 : // check conditions..
2634 : success = TestScriptConditions(OOSanitizeLegacyScriptConditions(conditions, @"<scene dictionary conditions>"));
2635 :
2636 : // perform successful actions...
2637 : if ((success) && (actions) && [actions count])
2638 : [self addScene: actions atOffset: off];
2639 :
2640 : // perform unsuccessful actions
2641 : if ((!success) && (else_actions) && [else_actions count])
2642 : [self addScene: else_actions atOffset: off];
2643 :
2644 : return success;
2645 : }
2646 :
2647 :
2648 : - (BOOL) processSceneString:(NSString*) item atOffset:(Vector) off
2649 : {
2650 : Vector model_p0;
2651 : Quaternion model_q;
2652 :
2653 : if (!item)
2654 : return NO;
2655 : NSArray * i_info = ScanTokensFromString(item);
2656 : if (!i_info)
2657 : return NO;
2658 : NSString* i_key = [(NSString*)[i_info objectAtIndex:0] lowercaseString];
2659 :
2660 : OOLog(kOOLogNoteProcessSceneString, @"..... processing %@ (%@)", i_info, i_key);
2661 :
2662 : //
2663 : // recursively add further scenes:
2664 : //
2665 : if ([i_key isEqualToString:@"scene"])
2666 : {
2667 : if ([i_info count] != 5) // must be scene_key_x_y_z
2668 : return NO; // 0.... 1.. 2 3 4
2669 : NSString* scene_key = (NSString*)[i_info objectAtIndex: 1];
2670 : Vector scene_offset = {0};
2671 : ScanVectorFromString([[i_info subarrayWithRange:NSMakeRange(2, 3)] componentsJoinedByString:@" "], &scene_offset);
2672 : scene_offset.x += off.x; scene_offset.y += off.y; scene_offset.z += off.z;
2673 : NSArray * scene_items = (NSArray *)[[UNIVERSE descriptions] objectForKey:scene_key];
2674 : OOLog(kOOLogDebugProcessSceneStringAddScene, @"::::: adding scene: '%@'", scene_key);
2675 : //
2676 : if (scene_items)
2677 : {
2678 : [self addScene: scene_items atOffset: scene_offset];
2679 : return YES;
2680 : }
2681 : else
2682 : return NO;
2683 : }
2684 : //
2685 : // Add ship models:
2686 : //
2687 : if ([i_key isEqualToString:@"ship"]||[i_key isEqualToString:@"model"]||[i_key isEqualToString:@"role"])
2688 : {
2689 : if ([i_info count] != 10) // must be item_name_x_y_z_W_X_Y_Z_align
2690 : {
2691 : return NO; // 0... 1... 2 3 4 5 6 7 8 9....
2692 : }
2693 :
2694 : ShipEntity* ship = nil;
2695 :
2696 : if ([i_key isEqualToString:@"ship"]||[i_key isEqualToString:@"model"])
2697 : {
2698 : ship = [UNIVERSE newShipWithName:[i_info oo_stringAtIndex: 1]];
2699 : }
2700 : else if ([i_key isEqualToString:@"role"])
2701 : {
2702 : ship = [UNIVERSE newShipWithRole:[i_info oo_stringAtIndex: 1]];
2703 : }
2704 : if (!ship)
2705 : return NO;
2706 :
2707 : ScanVectorAndQuaternionFromString([[i_info subarrayWithRange:NSMakeRange(2, 7)] componentsJoinedByString:@" "], &model_p0, &model_q);
2708 :
2709 : Vector model_offset = positionOffsetForShipInRotationToAlignment(ship, model_q, [i_info oo_stringAtIndex:9]);
2710 : model_p0 = vector_add(model_p0, vector_subtract(off, model_offset));
2711 :
2712 : OOLog(kOOLogDebugProcessSceneStringAddModel, @"::::: adding model to scene:'%@'", ship);
2713 : [ship setOrientation: model_q];
2714 : [ship setPosition: vectorToHPVector(model_p0)];
2715 : [UNIVERSE setMainLightPosition:(Vector){ DEMO_LIGHT_POSITION }]; // set light origin
2716 : [ship setScanClass: CLASS_NO_DRAW];
2717 : [ship switchAITo: @"nullAI.plist"];
2718 : [UNIVERSE addEntity: ship]; // STATUS_IN_FLIGHT, AI state GLOBAL
2719 : [ship setStatus: STATUS_COCKPIT_DISPLAY];
2720 : [ship setRoll: 0.0];
2721 : [ship setPitch: 0.0];
2722 : [ship setVelocity: kZeroVector];
2723 : [ship setBehaviour: BEHAVIOUR_STOP_STILL];
2724 :
2725 : [ship release];
2726 : return YES;
2727 : }
2728 : //
2729 : // Add player ship model:
2730 : //
2731 : if ([i_key isEqualToString:@"player"])
2732 : {
2733 : if ([i_info count] != 9) // must be player_x_y_z_W_X_Y_Z_align
2734 : return NO; // 0..... 1 2 3 4 5 6 7 8....
2735 :
2736 : ShipEntity* doppelganger = [UNIVERSE newShipWithName:[self shipDataKey]]; // retain count = 1
2737 : if (!doppelganger)
2738 : return NO;
2739 :
2740 : ScanVectorAndQuaternionFromString([[i_info subarrayWithRange:NSMakeRange( 1, 7)] componentsJoinedByString:@" "], &model_p0, &model_q);
2741 :
2742 : Vector model_offset = positionOffsetForShipInRotationToAlignment( doppelganger, model_q, (NSString*)[i_info objectAtIndex:8]);
2743 : model_p0.x += off.x - model_offset.x;
2744 : model_p0.y += off.y - model_offset.y;
2745 : model_p0.z += off.z - model_offset.z;
2746 :
2747 : OOLog(kOOLogDebugProcessSceneStringAddModel, @"::::: adding model to scene:'%@'", doppelganger);
2748 : [doppelganger setOrientation: model_q];
2749 : [doppelganger setPosition: vectorToHPVector(model_p0)];
2750 : [UNIVERSE setMainLightPosition:(Vector){ DEMO_LIGHT_POSITION }]; // set light origin
2751 : [doppelganger setScanClass: CLASS_NO_DRAW];
2752 : [doppelganger switchAITo: @"nullAI.plist"];
2753 : [UNIVERSE addEntity: doppelganger];
2754 : [doppelganger setStatus: STATUS_COCKPIT_DISPLAY];
2755 : [doppelganger setRoll: 0.0];
2756 : [doppelganger setPitch: 0.0];
2757 : [doppelganger setVelocity: kZeroVector];
2758 : [doppelganger setBehaviour: BEHAVIOUR_STOP_STILL];
2759 :
2760 : [doppelganger release];
2761 : return YES;
2762 : }
2763 : //
2764 : // Add planet model: selected via gui-scene-show-planet/-local-planet
2765 : //
2766 : if ([i_key isEqualToString:@"local-planet"] || [i_key isEqualToString:@"target-planet"])
2767 : {
2768 : if ([i_info count] != 4) // must be xxxxx-planet_x_y_z
2769 : return NO; // 0........... 1 2 3
2770 :
2771 : // sunlight position for F7 screen is chosen pseudo randomly from 4 different positions.
2772 : if (info_system_id & 8)
2773 : {
2774 : _sysInfoLight = (info_system_id & 2) ? (Vector){ -10000.0, 4000.0, -10000.0 } : (Vector){ -12000.0, -5000.0, -10000.0 };
2775 : }
2776 : else
2777 : {
2778 : _sysInfoLight = (info_system_id & 2) ? (Vector){ 6000.0, -5000.0, -10000.0 } : (Vector){ 6000.0, 4000.0, -10000.0 };
2779 : }
2780 :
2781 : [UNIVERSE setMainLightPosition:_sysInfoLight]; // set light origin
2782 :
2783 : #if NEW_PLANETS
2784 : OOPlanetEntity *originalPlanet = nil;
2785 : if ([i_key isEqualToString:@"local-planet"] && [UNIVERSE sun])
2786 : {
2787 : originalPlanet = [UNIVERSE planet];
2788 : }
2789 : else
2790 : {
2791 : originalPlanet = [[[OOPlanetEntity alloc] initAsMainPlanetForSystem:info_system_id] autorelease];
2792 : }
2793 : OOPlanetEntity *doppelganger = [originalPlanet miniatureVersion];
2794 : if (doppelganger == nil) return NO;
2795 :
2796 : #else
2797 : OOPlanetEntity* doppelganger = nil;
2798 : NSMutableDictionary *planetInfo = [NSMutableDictionary dictionaryWithDictionary:[UNIVERSE generateSystemData:target_system_seed]];
2799 :
2800 : if ([i_key isEqualToString:@"local-planet"] && [UNIVERSE sun])
2801 : {
2802 : OOPlanetEntity *mainPlanet = [UNIVERSE planet];
2803 : OOTexture *texture = [mainPlanet texture];
2804 : if (texture != nil)
2805 : {
2806 : [planetInfo setObject:texture forKey:@"_oo_textureObject"];
2807 : [planetInfo oo_setBool:[mainPlanet isExplicitlyTextured] forKey:@"_oo_isExplicitlyTextured"];
2808 : [planetInfo oo_setBool:YES forKey:@"mainForLocalSystem"];
2809 : //[planetInfo oo_setQuaternion:[mainPlanet orientation] forKey:@"orientation"]; // the orientation is overwritten later on, without regard for the real planet's orientation.
2810 : }
2811 : }
2812 :
2813 : doppelganger = [[OOPlanetEntity alloc] initFromDictionary:planetInfo withAtmosphere:YES andSeed:target_system_seed];
2814 : [doppelganger miniaturize];
2815 : [doppelganger autorelease];
2816 :
2817 : if (doppelganger == nil) return NO;
2818 : #endif
2819 :
2820 : ScanVectorFromString([[i_info subarrayWithRange:NSMakeRange(1, 3)] componentsJoinedByString:@" "], &model_p0);
2821 :
2822 : // miniature radii are roughly between 60 and 120. Place miniatures with a radius bigger than 60 a bit futher away.
2823 : model_p0 = vector_multiply_scalar(model_p0, 1 - 0.5 * ((60 - [doppelganger radius]) / 60));
2824 :
2825 : model_p0 = vector_add(model_p0, off);
2826 :
2827 : // TODO: find better quaternion values.
2828 : #if NEW_PLANETS
2829 : //Quaternion model_q = { 0.83, 0.365148, 0.182574, 0.0 }; // shows new planets' north pole.
2830 : //Quaternion model_q = { 0.83, -0.365148, 0.182574, 0.0 }; // shows new planets' south pole.
2831 : Quaternion model_q = { 0.83, 0.12, 0.44, 0.0 }; // new planets - default orientation.
2832 : #else
2833 : //model_q = make_quaternion( M_SQRT1_2, 0.314, M_SQRT1_2, 0.0 );
2834 : Quaternion model_q = { 0.833492, 0.333396, 0.440611, 0.0 };
2835 : #endif
2836 : OOLog(kOOLogDebugProcessSceneStringAddMiniPlanet, @"::::: adding %@ to scene:'%@'", i_key, doppelganger);
2837 : [doppelganger setOrientation: model_q];
2838 : // HPVect: mission screen coordinates are small enough that we don't need high-precision for calculations
2839 : [doppelganger setPosition: vectorToHPVector(model_p0)];
2840 : /* MKW - add rotation based on current time
2841 : * - necessary to duplicate the rotation already performed in PlanetEntity.m since we reset the orientation above. */
2842 : int deltaT = floor(fmod([self clockTimeAdjusted], 86400));
2843 : [doppelganger update: deltaT];
2844 : [UNIVERSE addEntity:doppelganger];
2845 :
2846 : return YES;
2847 : }
2848 :
2849 : return NO;
2850 : }
2851 :
2852 :
2853 : - (BOOL) addEqScriptForKey:(NSString *)eq_key
2854 : {
2855 : if (eq_key == nil) return NO;
2856 :
2857 : NSString *scriptName = [[OOEquipmentType equipmentTypeWithIdentifier:eq_key] scriptName];
2858 :
2859 : OOLog(@"player.equipmentScript", @"Added equipment %@, with the following script property: '%@'.", eq_key, scriptName);
2860 :
2861 : if (scriptName == nil) return NO;
2862 :
2863 : NSMutableDictionary *properties = [NSMutableDictionary dictionary];
2864 :
2865 : // no duplicates!
2866 : NSArray *eqScript = nil;
2867 : foreach (eqScript, eqScripts)
2868 : {
2869 : NSString *key = [eqScript oo_stringAtIndex:0];
2870 : if ([key isEqualToString: eq_key]) return NO;
2871 : }
2872 :
2873 : [properties setObject:self forKey:@"ship"];
2874 : [properties setObject:eq_key forKey:@"equipmentKey"];
2875 : OOScript *s = [OOScript jsScriptFromFileNamed:scriptName properties:properties];
2876 : if (s == nil) return NO;
2877 :
2878 : OOLog(@"player.equipmentScript", @"Script '%@': installation %@successful.", scriptName,(s == nil ? @"un" : @""));
2879 :
2880 : [eqScripts addObject:[NSArray arrayWithObjects:eq_key,s,nil]];
2881 : if (primedEquipment == [eqScripts count] - 1) primedEquipment++; // if primed-none, keep it as primed-none.
2882 : OOLog(@"player.equipmentScript", @"Scriptable equipment available: %lu.", [eqScripts count]);
2883 : return YES;
2884 : }
2885 :
2886 :
2887 : - (void) removeEqScriptForKey:(NSString *)eq_key
2888 : {
2889 : if (eq_key == nil) return;
2890 :
2891 : NSString *key = nil;
2892 : NSUInteger i, count = [eqScripts count];
2893 :
2894 : for (i = 0; i < count; i++)
2895 : {
2896 : key = [[eqScripts oo_arrayAtIndex:i] oo_stringAtIndex:0];
2897 : if ([key isEqualToString: eq_key])
2898 : {
2899 : [eqScripts removeObjectAtIndex:i];
2900 :
2901 : if (i == primedEquipment) primedEquipment = count; // primed-none
2902 : else if (i < primedEquipment) primedEquipment--; // track the primed equipment
2903 : if (count == primedEquipment) primedEquipment--; // the array has shrunk by one!
2904 :
2905 : OOLog(@"player.equipmentScript", @"Removed equipment %@, with the following script property: '%@'.", eq_key, [[OOEquipmentType equipmentTypeWithIdentifier:eq_key] scriptName]);
2906 : }
2907 : }
2908 : }
2909 :
2910 :
2911 : - (NSUInteger) eqScriptIndexForKey:(NSString *)eq_key
2912 : {
2913 : NSUInteger i, count = [eqScripts count];
2914 :
2915 : if (eq_key != nil)
2916 : {
2917 : for (i = 0; i < count; i++)
2918 : {
2919 : NSString *key = [[eqScripts oo_arrayAtIndex:i] oo_stringAtIndex:0];
2920 : if ([key isEqualToString: eq_key]) return i;
2921 : }
2922 : }
2923 :
2924 : return count;
2925 : }
2926 :
2927 :
2928 : - (void) targetNearestHostile
2929 : {
2930 : [self scanForHostiles];
2931 : Entity *ent = [self foundTarget];
2932 : if (ent != nil)
2933 : {
2934 : ident_engaged = YES;
2935 : missile_status = MISSILE_STATUS_TARGET_LOCKED;
2936 : [self addTarget:ent];
2937 : }
2938 : }
2939 :
2940 :
2941 : - (void) targetNearestIncomingMissile
2942 : {
2943 : [self scanForNearestIncomingMissile];
2944 : Entity *ent = [self foundTarget];
2945 : if (ent != nil)
2946 : {
2947 : ident_engaged = YES;
2948 : missile_status = MISSILE_STATUS_TARGET_LOCKED;
2949 : [self addTarget:ent];
2950 : }
2951 : }
2952 :
2953 :
2954 : - (void) setGalacticHyperspaceBehaviourTo:(NSString *)galacticHyperspaceBehaviourString
2955 : {
2956 : OOGalacticHyperspaceBehaviour ghBehaviour = OOGalacticHyperspaceBehaviourFromString(galacticHyperspaceBehaviourString);
2957 : if (ghBehaviour == GALACTIC_HYPERSPACE_BEHAVIOUR_UNKNOWN)
2958 : {
2959 : OOLog(@"player.setGalacticHyperspaceBehaviour.invalidInput",
2960 : @"setGalacticHyperspaceBehaviourTo: called with unknown behaviour %@.", galacticHyperspaceBehaviourString);
2961 : }
2962 : [self setGalacticHyperspaceBehaviour:ghBehaviour];
2963 : }
2964 :
2965 :
2966 : - (void) setGalacticHyperspaceFixedCoordsTo:(NSString *)galacticHyperspaceFixedCoordsString
2967 : {
2968 : NSArray *coord_vals = ScanTokensFromString(galacticHyperspaceFixedCoordsString);
2969 : if ([coord_vals count] < 2) // Will be 0 if string is nil
2970 : {
2971 : OOLog(@"player.setGalacticHyperspaceFixedCoords.invalidInput", @"%@",
2972 : @"setGalacticHyperspaceFixedCoords: called with bad specifier. Defaulting to Oolite standard.");
2973 : galacticHyperspaceFixedCoords.x = galacticHyperspaceFixedCoords.y = 0x60;
2974 : }
2975 :
2976 : [self setGalacticHyperspaceFixedCoordsX:[coord_vals oo_unsignedCharAtIndex:0]
2977 : y:[coord_vals oo_unsignedCharAtIndex:1]];
2978 : }
2979 :
2980 : @end
2981 :
2982 :
2983 0 : NSString *OOComparisonTypeToString(OOComparisonType type)
2984 : {
2985 : switch (type)
2986 : {
2987 : case COMPARISON_EQUAL: return @"equal";
2988 : case COMPARISON_NOTEQUAL: return @"notequal";
2989 : case COMPARISON_LESSTHAN: return @"lessthan";
2990 : case COMPARISON_GREATERTHAN: return @"greaterthan";
2991 : case COMPARISON_ONEOF: return @"oneof";
2992 : case COMPARISON_UNDEFINED: return @"undefined";
2993 : }
2994 : return @"<error: invalid comparison type>";
2995 : }
|