50@interface ShipEntity (OOAIPrivate)
54- (BOOL)performHyperSpaceExitReplace:(BOOL)replace;
55- (BOOL)performHyperSpaceExitReplace:(BOOL)replace toSystem:(
OOSystemID)systemID;
58- (void)scanForNearestShipWithNegatedPredicate:(
EntityFilterPredicate)predicate parameter:(
void *)parameter;
60- (void) acceptDistressMessageFrom:(
ShipEntity *)other;
65@interface StationEntity (OOAIPrivate)
67- (void) acceptDistressMessageFrom:(
ShipEntity *)other;
72@interface ShipEntity (PureAI)
76- (void) setStateTo:(NSString *)state;
78- (void) pauseAI:(NSString *)intervalString;
80- (void) randomPauseAI:(NSString *)intervalString;
82- (void) dropMessages:(NSString *)messageString;
84- (void) debugDumpPendingMessages;
86- (void) setDestinationToCurrentLocation;
88- (void) setDesiredRangeTo:(NSString *)rangeString;
90- (void) setDesiredRangeForWaypoint;
92- (void) setSpeedTo:(NSString *)speedString;
94- (void) setSpeedFactorTo:(NSString *)speedString;
96- (void) setSpeedToCruiseSpeed;
98- (void) setThrustFactorTo:(NSString *)thrustFactorString;
101- (void) setTargetToPrimaryAggressor;
103- (void) scanForNearestMerchantman;
104- (void) scanForRandomMerchantman;
108- (void) scanForRandomLoot;
110- (void) setTargetToFoundTarget;
112- (void) checkForFullHold;
114- (void) getWitchspaceEntryCoordinates;
116- (void) setDestinationFromCoordinates;
117- (void) setCoordinatesFromPosition;
119- (void) fightOrFleeMissile;
121- (void) setCourseToPlanet;
122- (void) setTakeOffFromPlanet;
123- (void) landOnPlanet;
125- (void) checkTargetLegalStatus;
126- (void) checkOwnLegalStatus;
128- (void) exitAIWithMessage:(NSString *)message;
130- (void) setDestinationToTarget;
131- (void) setDestinationWithinTarget;
133- (void) checkCourseToDestination;
138- (void) checkHeatInsulation;
140- (void) scanForOffenders;
142- (void) setCourseToWitchpoint;
144- (void) setDestinationToWitchpoint;
145- (void) setDestinationToStationBeacon;
147- (void) performHyperSpaceExit;
148- (void) performHyperSpaceExitWithoutReplacing;
149- (void) wormholeGroup;
151- (void) commsMessage:(NSString *)valueString;
152- (void) commsMessageByUnpiloted:(NSString *)valueString;
156- (void) scanForThargoid;
157- (void) scanForNonThargoid;
158- (void) thargonCheckMother;
159- (void) becomeUncontrolledThargon;
161- (void) checkDistanceTravelled;
163- (void) fightOrFleeHostiles;
165- (void) suggestEscort;
167- (void) escortCheckMother;
169- (void) checkGroupOddsVersusTarget;
171- (void) scanForFormationLeader;
173- (void) messageMother:(NSString *)msgString;
175- (void) setPlanetPatrolCoordinates;
177- (void) setSunSkimStartCoordinates;
179- (void) setSunSkimEndCoordinates;
181- (void) setSunSkimExitCoordinates;
183- (void) patrolReportIn;
185- (void) checkForMotherStation;
187- (void) sendTargetCommsMessage:(NSString *)message;
189- (void) markTargetForFines;
191- (void) markTargetForOffence:(NSString *)valueString;
194- (void) recallStoredTarget;
196- (void) scanForRocks;
198- (void) setDestinationToDockingAbort;
200- (void) requestNewTarget;
202- (void) rollD:(NSString *)die_number;
204- (void) scanForNearestShipWithPrimaryRole:(NSString *)scanRole;
205- (void) scanForNearestShipHavingRole:(NSString *)scanRole;
206- (void) scanForNearestShipWithAnyPrimaryRole:(NSString *)scanRoles;
207- (void) scanForNearestShipHavingAnyRole:(NSString *)scanRoles;
208- (void) scanForNearestShipWithScanClass:(NSString *)scanScanClass;
210- (void) scanForNearestShipWithoutPrimaryRole:(NSString *)scanRole;
211- (void) scanForNearestShipNotHavingRole:(NSString *)scanRole;
212- (void) scanForNearestShipWithoutAnyPrimaryRole:(NSString *)scanRoles;
213- (void) scanForNearestShipNotHavingAnyRole:(NSString *)scanRoles;
214- (void) scanForNearestShipWithoutScanClass:(NSString *)scanScanClass;
216- (void) setCoordinates:(NSString *)system_x_y_z;
218- (void) checkForNormalSpace;
220- (void) setTargetToRandomStation;
221- (void) setTargetToLastStation;
223- (void) addFuel:(NSString *) fuel_number;
225- (void) scriptActionOnTarget:(NSString *) action;
227- (void) sendScriptMessage:(NSString *)message;
229- (void) ai_throwSparks;
233- (void) ai_debugMessage:(NSString *)message;
236- (void) targetFirstBeaconWithCode:(NSString *) code;
237- (void) targetNextBeaconWithCode:(NSString *) code;
238- (void) setRacepointsFromTarget;
239- (void) performFlyRacepoints;
242- (void) addPrimaryAggressorAsDefenseTarget;
243- (void) addFoundTargetAsDefenseTarget;
244- (void) findNewDefenseTarget;
249@implementation ShipEntity (AI)
252- (void) setAITo:(NSString *)aiString
255 if (![
PLAYER scriptsLoaded])
257 aiString =
@"oolite-nullAI.js";
259 if ([aiString hasSuffix:
@".plist"])
264 else if ([aiString hasSuffix:
@".js"])
274 [
self setAITo:[aiString stringByAppendingString:@".plist"]];
278 [
self setAITo:[aiString stringByAppendingString:@".js"]];
284- (void) setAIScript:(NSString *)aiString
286 NSMutableDictionary *properties =
nil;
288 properties = [NSMutableDictionary dictionary];
289 [properties setObject:self forKey:@"ship"];
291 [aiScript autorelease];
295 OOLog(
@"ai.load.failed.unknownAI",
@"Unable to load JS AI %@ for ship %@ (%@ for role %@)",aiString,
self,[
self shipDataKey],[
self primaryRole]);
300 aiScriptWakeTime = 0;
301 haveStartedJSAI = NO;
307- (void) switchAITo:(NSString *)aiString
309 [
self setAITo:aiString];
310 [[
self getAI] clearStack];
314- (void) scanForHostiles
321 GLfloat found_d2 = scannerRange * scannerRange;
322 for (i = 0; i < n_scanned_ships ; i++)
325 GLfloat d2 = distance2_scanned_ships[i];
327 && ([thing isThargoid] || (([thing primaryTarget] ==
self) && [thing hasHostileTarget]) || [thing isDefenseTarget:
self])
328 && ![thing isCloaked])
330 [
self setFoundTarget:thing];
335 [
self checkFoundTarget];
339- (void) groupAttackTarget
341 NSEnumerator *shipEnum =
nil;
344 target = [
self primaryTarget];
346 if (target ==
nil)
return;
348 if ([
self group] ==
nil)
350 [
self setFoundTarget:target];
351 [shipAI reactToMessage:@"GROUP_ATTACK_TARGET" context:@"groupAttackTarget"];
352 [
self doScriptEvent:OOJSID("helpRequestReceived") withArgument:self andArgument:target];
356 for (shipEnum = [[
self group] mutationSafeEnumerator]; (ship = [shipEnum nextObject]); )
358 [ship setFoundTarget:target];
359 [ship reactToAIMessage:@"GROUP_ATTACK_TARGET" context:@"groupAttackTarget"];
360 [ship doScriptEvent:OOJSID("helpRequestReceived") withArgument:self andArgument:target];
362 if ([ship escortGroup] != [ship group] && [[ship escortGroup]
count] > 1)
365 NSEnumerator *shipEnum =
nil;
366 NSArray *escortMembers = [[ship escortGroup] memberArrayExcludingLeader];
367 for (shipEnum = [escortMembers objectEnumerator]; (escort = [shipEnum nextObject]); )
378- (void) performAttack
380 if (behaviour != BEHAVIOUR_EVASIVE_ACTION)
382 behaviour = BEHAVIOUR_ATTACK_TARGET;
383 desired_range = 1250 *
randf() + 750;
389- (void) performCollect
391 behaviour = BEHAVIOUR_COLLECT_TARGET;
396- (void) performEscort
398 if(behaviour != BEHAVIOUR_FORMATION_FORM_UP)
400 behaviour = BEHAVIOUR_FORMATION_FORM_UP;
406- (void) performFaceDestination
408 behaviour = BEHAVIOUR_FACE_DESTINATION;
415 if (behaviour != BEHAVIOUR_FLEE_EVASIVE_ACTION)
417 behaviour = BEHAVIOUR_FLEE_TARGET;
418 [
self setEvasiveJink:400.0];
423 if ([
self approachAspectToPrimaryTarget] > 0.9995)
425 behaviour =
randf() < 0.15 ? BEHAVIOUR_EVASIVE_ACTION : BEHAVIOUR_FLEE_EVASIVE_ACTION;
432- (void) performFlyToRangeFromDestination
434 behaviour = BEHAVIOUR_FLY_RANGE_FROM_DESTINATION;
442 behaviour = BEHAVIOUR_TRACK_TARGET;
449 behaviour = BEHAVIOUR_IDLE;
454- (void) performIntercept
456 behaviour = BEHAVIOUR_INTERCEPT_TARGET;
461- (void) performLandOnPlanet
463 OOPlanetEntity *nearest = [
self findNearestPlanet];
464 if (isNearPlanetSurface)
466 _destination = [nearest position];
467 behaviour = BEHAVIOUR_LAND_ON_PLANET;
468 planetForLanding = [nearest universalID];
472 behaviour = BEHAVIOUR_IDLE;
473 [shipAI message:@"NO_PLANET_NEARBY"];
480- (void) performMining
482 Entity *target = [
self primaryTarget];
484 if (target && [target scanClass] == CLASS_ROCK)
486 behaviour = BEHAVIOUR_ATTACK_MINING_TARGET;
491 [
self noteLostTargetAndGoIdle];
496- (void) performScriptedAI
498 behaviour = BEHAVIOUR_SCRIPTED_AI;
503- (void) performScriptedAttackAI
505 behaviour = BEHAVIOUR_SCRIPTED_ATTACK_AI;
510- (void) performBuoyTumble
514 behaviour = BEHAVIOUR_TUMBLE;
521 behaviour = BEHAVIOUR_STOP_STILL;
527- (void) performTumble
529 stick_roll = max_flight_roll*2.0*(
randf() - 0.5);
530 stick_pitch = max_flight_pitch*2.0*(
randf() - 0.5);
531 behaviour = BEHAVIOUR_TUMBLE;
536- (BOOL) performHyperSpaceToSpecificSystem:(
OOSystemID)systemID
538 return [
self performHyperSpaceExitReplace:NO toSystem:systemID];
542- (void) requestDockingCoordinates
550 NSString *message =
nil;
551 double distanceToStation2 = 0.0;
553 targStation = [
self targetStation];
554 if ([targStation isStation])
560 station = [UNIVERSE nearestShipMatchingPredicate:IsStationPredicate
562 relativeToEntity:self];
565 distanceToStation2 = HPdistance2([station position], [
self position]);
575 [dockingInstructions release];
577 if (dockingInstructions !=
nil)
579 [
self recallDockingInstructions];
581 message = [dockingInstructions objectForKey:@"ai_message"];
582 if (message !=
nil) [shipAI message:message];
583 message = [dockingInstructions objectForKey:@"comms_message"];
592 if (dockingInstructions ==
nil)
594 [shipAI message:@"NO_STATION_FOUND"];
599- (void) recallDockingInstructions
601 if (dockingInstructions !=
nil)
603 _destination = [dockingInstructions oo_hpvectorForKey:@"destination"];
604 desired_speed = fmin([dockingInstructions oo_floatForKey:
@"speed"], maxFlightSpeed);
605 desired_range = [dockingInstructions oo_floatForKey:@"range"];
606 if ([dockingInstructions objectForKey:
@"station"])
608 StationEntity *targetStation = [[dockingInstructions objectForKey:@"station"] weakRefUnderlyingObject];
609 if (targetStation !=
nil)
611 [
self addTarget:targetStation];
612 [
self setTargetStation:targetStation];
616 [
self removeTarget:[
self primaryTarget]];
619 docking_match_rotation = [dockingInstructions oo_boolForKey:@"match_rotation"];
624- (void) scanForNearestIncomingMissile
631 [
self scanForNearestShipWithPredicate:ANDPredicate parameter:¶m];
634- (void) enterPlayerWormhole
636 [
self enterWormhole:[PLAYER wormhole] replacing:NO];
639- (void) enterTargetWormhole
643 double found_d2 = scannerRange * scannerRange;
645 if (targEnt && (HPdistance2(position, [targEnt position]) < found_d2))
647 if ([targEnt isWormhole])
649 else if ([targEnt isPlayer])
650 whole = [PLAYER wormhole];
656 int ent_count =
UNIVERSE->n_entities;
661 for (i = 0; i < ent_count; i++)
662 if (uni_entities[i]->isWormhole)
663 wormholes[wh_count++] = [(
WormholeEntity *)uni_entities[i] retain];
666 for (i = 0; i < wh_count ; i++)
669 double d2 = HPdistance2(position, wh->
position);
679 [
self enterWormhole:whole replacing:NO];
684- (void) wormholeEscorts
686 NSEnumerator *shipEnum =
nil;
688 NSString *context =
nil;
691 whole = [
self primaryTarget];
692 if (![whole isWormhole])
return;
695 context = [NSString stringWithFormat:@"%@ wormholeEscorts", [
self shortDescription]];
698 for (shipEnum = [
self escortEnumerator]; (ship = [shipEnum nextObject]); )
707 [_escortGroup release];
713- (void) wormholeEntireGroup
715 [
self wormholeGroup];
716 [
self wormholeEscorts];
725 if (reportAIMessages)
727 OOLog(
@"ai.suggestEscort",
@"DEBUG: %@ suggests escorting %@",
self, mother);
731 if ([mother acceptAsEscort:
self])
734 if (([mother legalStatus] > 0)&&(bounty <= 0))
738 [
self markAsOffender:extra withReason:kOOLegalStatusReasonAssistingOffender];
742 [
self setOwner:mother];
744 [shipAI message:@"ESCORTING"];
749 if (reportAIMessages)
751 OOLog(
@"ai.suggestEscort.refused",
@"DEBUG: %@ refused by %@",
self, mother);
756 [
self setOwner:self];
757 [shipAI message:@"NOT_ESCORTING"];
758 [
self doScriptEvent:OOJSID("escortRejected") withArgument:mother];
763- (void) broadcastDistressMessage
766 [
self broadcastDistressMessageWithDumping:YES];
769- (void) broadcastDistressMessageWithDumping:(BOOL)dumpCargo
771 [
self checkScannerIgnoringUnpowered];
775 if (aggressor_ship ==
nil)
return;
778 if (messageTime > 2.0 *
randf())
return;
780 NSString *distress_message =
nil;
781 BOOL is_buoy = (scanClass == CLASS_BUOY);
782 if (is_buoy) distress_message =
@"[buoy-distress-call]";
783 else distress_message =
@"[distress-call]";
786 for (i = 0; i < n_scanned_ships; i++)
791 if (dumpCargo && !is_buoy && [
self primaryAggressor] == ship && energy < 0.375 * maxEnergy)
799 if (ship->
isPlayer && ![
self hasNewAI])
801 [ship doScriptEvent:OOJSID(
"distressMessageReceived") withArgument:aggressor_ship andArgument:self];
803 if (!is_buoy && [self primaryAggressor] == ship && energy < 0.375 * maxEnergy)
805 [self sendExpandedMessage:
@"[beg-for-mercy]" toShip:ship];
807 else if ([
self bounty] == 0)
811 [
self sendExpandedMessage:distress_message toShip:ship];
817 else if ([
self bounty] == 0 && [ship crew])
827 if (![
self hasNewAI])
830 if (ship->
isStation || [ship hasPrimaryRole:
@"police"] || [ship hasPrimaryRole:
@"hunter"])
843@implementation ShipEntity (PureAI)
845- (void) setStateTo:(NSString *)state
847 [[
self getAI] setState:state];
851- (void) pauseAI:(NSString *)intervalString
853 [shipAI setNextThinkTime:[UNIVERSE getTime] + [intervalString doubleValue]];
857- (void) randomPauseAI:(NSString *)intervalString
862 if ([tokens
count] != 2)
864 OOLog(
@"ai.syntax.randomPauseAI",
@"***** ERROR: cannot read min and max value for randomPauseAI:, needs 2 values: '%@'.", intervalString);
868 start = [tokens oo_doubleAtIndex:0];
869 end = [tokens oo_doubleAtIndex:1];
871 [shipAI setNextThinkTime:[UNIVERSE getTime] + (start + (end - start)*randf())];
875- (void) dropMessages:(NSString *)messageString
877 NSArray *messages =
nil;
878 NSEnumerator *messageEnum =
nil;
879 NSString *message =
nil;
880 NSCharacterSet *whiteSpace = [NSCharacterSet whitespaceCharacterSet];
882 messages = [messageString componentsSeparatedByString:@","];
883 for (messageEnum = [messages objectEnumerator]; (message = [messageEnum nextObject]); )
885 [shipAI dropMessage:[message stringByTrimmingCharactersInSet:whiteSpace]];
890- (void) debugDumpPendingMessages
892 [shipAI debugDumpPendingMessages];
896- (void) setDestinationToCurrentLocation
903- (void) setDestinationToJinkPosition
905 Vector front = vector_multiply_scalar([
self forwardVector], flightSpeed / max_flight_pitch * 2);
906 _destination = HPvector_add(position, vectorToHPVector(vector_add(front,
OOVectorRandomSpatial(100))));
911- (void) setDesiredRangeTo:(NSString *)rangeString
913 desired_range = [rangeString doubleValue];
916- (void) setDesiredRangeForWaypoint
918 desired_range = fmax(maxFlightSpeed / max_flight_pitch / 6, 50.0);
921- (void) setSpeedTo:(NSString *)speedString
923 desired_speed = [speedString doubleValue];
927- (void) setSpeedFactorTo:(NSString *)speedString
929 desired_speed = maxFlightSpeed * [speedString doubleValue];
932- (void) setSpeedToCruiseSpeed
934 desired_speed = cruiseSpeed;
937- (void) setThrustFactorTo:(NSString *)thrustFactorString
939 thrust = OOClamp_0_1_f([thrustFactorString doubleValue]) * max_thrust;
943- (void) setTargetToPrimaryAggressor
945 Entity *primeAggressor = [
self primaryAggressor];
948 if ([
self primaryTarget] == primeAggressor)
954 if ([
self hasHostileTarget] &&
randf() < 0.75)
957 [
self addDefenseTarget:(ShipEntity*)primeAggressor];
961 if ([primeAggressor isShip] && ![(
ShipEntity *)primeAggressor isFriendlyTo:
self])
965 Entity *primeTarget = [
self primaryTarget];
966 if ((primeTarget)&&(primeTarget->
isShip))
968 ShipEntity *currentShip = [
self primaryTarget];
969 [[currentShip
getAI]
message:[NSString stringWithFormat:@"%@ %d %d", AIMS_AGGRESSOR_SWITCHED_TARGET, universalID, [[
self primaryAggressor] universalID]]];
974 [
self addTarget:[
self primaryAggressor]];
979- (void) addPrimaryAggressorAsDefenseTarget
981 Entity *primeAggressor = [
self primaryAggressor];
984 if ([
self isDefenseTarget:primeAggressor])
987 if ([primeAggressor isShip] && ![(
ShipEntity*)primeAggressor isFriendlyTo:
self])
989 [
self addDefenseTarget:primeAggressor];
994- (void) scanForNearestMerchantman
1001 [
self checkScannerIgnoringUnpowered];
1003 found_d2 = scannerRange * scannerRange;
1006 for (i = 0; i < n_scanned_ships ; i++)
1008 ship = scanned_ships[i];
1009 if ([ship isPirateVictim] && ([ship status] != STATUS_DEAD) && ([ship status] != STATUS_DOCKED) && ![ship isCloaked])
1011 d2 = distance2_scanned_ships[i];
1016 else d2 = distance2_scanned_ships[i];
1020 [
self setFoundTarget:ship];
1024 [
self checkFoundTarget];
1028- (void) scanForRandomMerchantman
1030 unsigned n_found, i;
1033 [
self checkScannerIgnoringUnpowered];
1038 for (i = 0; i < n_scanned_ships ; i++)
1041 if (([ship status] != STATUS_DEAD) && ([ship status] != STATUS_DOCKED) && [ship isPirateVictim] && ![ship isCloaked])
1042 ids_found[n_found++] = ship;
1046 [shipAI message:@"NOTHING_FOUND"];
1051 [
self setFoundTarget:ids_found[i]];
1052 [shipAI message:@"TARGET_FOUND"];
1062 if (![
self hasCargoScoop])
1064 [shipAI message:@"NOTHING_FOUND"];
1067 if ([cargo
count] >= [
self maxAvailableCargoSpace])
1069 if (max_cargo) [shipAI message:@"HOLD_FULL"];
1070 [shipAI message:@"NOTHING_FOUND"];
1076 if (magnitude2([
self velocity]))
1078 [shipAI message:@"NOTHING_FOUND"];
1083 [
self checkScanner];
1085 double found_d2 = scannerRange * scannerRange;
1088 for (i = 0; i < n_scanned_ships; i++)
1091 if ([other scanClass] == CLASS_CARGO && [other cargoType] !=
CARGO_NOT_CARGO && [other status] != STATUS_BEING_SCOOPED)
1093 if ((![
self isPolice]) || ([[other commodityType] isEqualToString:
@"slaves"]))
1095 GLfloat d2 = distance2_scanned_ships[i];
1099 [
self setFoundTarget:other];
1104 [
self checkFoundTarget];
1108- (void) scanForRandomLoot
1111 if (![
self isStation] && ![
self hasCargoScoop])
1113 [shipAI message:@"NOTHING_FOUND"];
1117 [
self checkScanner];
1120 unsigned things_found = 0;
1123 for (i = 0; (i < n_scanned_ships)&&(things_found < 16) ; i++)
1126 if ([other scanClass] == CLASS_CARGO && [other cargoType] !=
CARGO_NOT_CARGO && [other status] != STATUS_BEING_SCOOPED)
1128 thing_uids_found[things_found++] = other;
1132 if (things_found != 0)
1134 [
self setFoundTarget:thing_uids_found[ranrot_rand() % things_found]];
1135 [shipAI message:@"TARGET_FOUND"];
1138 [shipAI message:@"NOTHING_FOUND"];
1142- (void) setTargetToFoundTarget
1144 if ([
self foundTarget] !=
nil)
1146 [
self addTarget:[
self foundTarget]];
1150 [shipAI message:@"TARGET_LOST"];
1155- (void) addFoundTargetAsDefenseTarget
1157 Entity* fTarget = [
self foundTarget];
1160 if ([fTarget isShip] && ![(
ShipEntity *)fTarget isFriendlyTo:
self])
1162 [
self addDefenseTarget:fTarget];
1167- (void) checkForFullHold
1171 [shipAI message:@"NO_CARGO_BAY"];
1173 else if ([cargo
count] >= [
self maxAvailableCargoSpace])
1175 [shipAI message:@"HOLD_FULL"];
1179 [shipAI message:@"HOLD_NOT_FULL"];
1187- (void) getWitchspaceEntryCoordinates
1192 Vector vr = vector_multiply_scalar(v_forward, maxFlightSpeed * 10.0);
1193 coordinates = HPvector_add(position, vectorToHPVector(vr));
1202 station = [UNIVERSE nearestShipMatchingPredicate:IsStationPredicate
1204 relativeToEntity:self];
1208 Vector vr = vector_multiply_scalar([station rightVector], 10000);
1209 coordinates = HPvector_add([station position], vectorToHPVector(vr));
1213 Vector vr = vector_multiply_scalar(v_forward, maxFlightSpeed * 10.0);
1214 coordinates = HPvector_add(position, vectorToHPVector(vr));
1219- (void) setDestinationFromCoordinates
1221 _destination = coordinates;
1225- (void) setCoordinatesFromPosition
1227 coordinates = position;
1231- (void) fightOrFleeMissile
1237 NSEnumerator *escortEnum =
nil;
1241 [
self checkScannerIgnoringUnpowered];
1242 for (i = 0; (i < n_scanned_ships)&&(missile ==
nil); i++)
1247 target = [thing primaryTarget];
1255 for (escortEnum = [self escortEnumerator]; (escort = [escortEnum nextObject]); )
1257 if (target == escort)
1266 if (missile ==
nil)
return;
1268 [
self addTarget:missile];
1269 [
self addDefenseTarget:missile];
1273 [
self doScriptEvent:OOJSID("shipBeingAttacked") withArgument:hunter];
1276 if ([
self isPolice])
1280 NSEnumerator *policeEnum =
nil;
1283 for (policeEnum = [[
self group] mutationSafeEnumerator]; (police = [policeEnum nextObject]); )
1291 if ([
self isPolice] && ![hunter isPolice]) [hunter markAsOffender:64 withReason:kOOLegalStatusReasonAttackedPolice];
1297 [
self setPrimaryAggressor:hunter];
1298 [
self setFoundTarget:hunter];
1305 desired_range = 10000;
1307 [shipAI message:@"FLEEING"];
1311- (void) setCourseToPlanet
1314 OOPlanetEntity *the_planet = [
self findNearestPlanetExcludingMoons];
1317 double variation = (aegis_status ==
AEGIS_NONE ? 0.5 : 0.2);
1318 HPVector p_pos = the_planet->position;
1319 double p_cr = the_planet->collision_radius;
1320 HPVector p1 = HPvector_between(p_pos, position);
1321 p1 = HPvector_normal(p1);
1322 p1.x += variation * (
randf() - variation);
1323 p1.y += variation * (
randf() - variation);
1324 p1.z += variation * (
randf() - variation);
1325 p1 = HPvector_normal(p1);
1326 _destination = HPvector_add(p_pos, HPvector_multiply_scalar(p1, p_cr));
1327 desired_range = collision_radius + 100.0;
1331 [shipAI message:@"NO_PLANET_FOUND"];
1336- (void) setTakeOffFromPlanet
1339 OOPlanetEntity *the_planet = [
self findNearestPlanet];
1342 _destination = HPvector_add([the_planet position], HPvector_multiply_scalar(
1343 HPvector_normal(HPvector_subtract([the_planet position],position)),-10000.0-the_planet->collision_radius));
1344 desired_range = 50.0;
1348 OOLog(
@"ai.setTakeOffFromPlanet.noPlanet",
@"%@",
@"***** Error. Planet not found during take off!");
1353- (void) landOnPlanet
1356 [
self landOnPlanet:[
self findNearestPlanet]];
1360- (void) checkTargetLegalStatus
1362 ShipEntity *other_ship = [
self primaryTarget];
1365 [shipAI message:@"NO_TARGET"];
1373 [shipAI message:@"TARGET_FUGITIVE"];
1378 [shipAI message:@"TARGET_OFFENDER"];
1383 [shipAI message:@"TARGET_MINOR_OFFENDER"];
1386 [shipAI message:@"TARGET_CLEAN"];
1391- (void) checkOwnLegalStatus
1393 if (scanClass == CLASS_THARGOID)
1395 [shipAI message:@"SELF_THARGOID"];
1398 int ls = [
self legalStatus];
1401 [shipAI message:@"SELF_FUGITIVE"];
1406 [shipAI message:@"SELF_OFFENDER"];
1411 [shipAI message:@"SELF_MINOR_OFFENDER"];
1414 [shipAI message:@"SELF_CLEAN"];
1418- (void) exitAIWithMessage:(NSString *)message
1420 if ([message length] == 0) message =
@"RESTARTED";
1421 [shipAI exitStateMachineWithMessage:message];
1425- (void) setDestinationToTarget
1427 Entity *the_target = [
self primaryTarget];
1429 _destination = the_target->
position;
1433- (void) setDestinationWithinTarget
1435 Entity *the_target = [
self primaryTarget];
1438 HPVector pos = the_target->
position;
1441 GLfloat d = (
randf() -
randf()) * the_target->collision_radius;
1442 _destination = make_HPvector(pos.x + d * v.x, pos.y + d * v.y, pos.z + d * v.z);
1447- (void) checkCourseToDestination
1449 Entity *hazard = [UNIVERSE hazardOnRouteFromEntity: self toDistance: desired_range fromPoint: _destination];
1451 if (hazard ==
nil || ([hazard isShip] && HPdistance(position, [hazard position]) > scannerRange) || ([hazard isPlanet] && aegis_status ==
AEGIS_NONE))
1452 [shipAI message:
@"COURSE_OK"];
1455 if ([hazard isShip] && (weapon_damage * 24.0 > [hazard energy]))
1457 [shipAI reactToMessage:@"HAZARD_CAN_BE_DESTROYED" context:@"checkCourseToDestination"];
1460 _destination = [UNIVERSE getSafeVectorFromEntity:self toDistance:desired_range fromPoint:_destination];
1461 [shipAI message:@"WAYPOINT_SET"];
1468 switch (aegis_status)
1471 [shipAI message:@"AEGIS_CLOSE_TO_MAIN_PLANET"];
1481 [shipAI message:@"CLOSE_TO_SUN"];
1485 [shipAI message:@"CLOSE_TO_PLANET"];
1488 [shipAI message:@"CLOSE_TO_MOON"];
1492 [shipAI message:@"CLOSE_TO_SECONDARY_PLANET"];
1498 [shipAI message:@"AEGIS_IN_DOCKING_RANGE"];
1501 [shipAI message:@"AEGIS_NONE"];
1505 NSLog(
@"Aegis status for %@ has taken on invalid value %i. This is an internal error, please report it.",
self, aegis_status);
1507 [shipAI message:@"AEGIS_NONE"];
1513 if (energy == maxEnergy)
1515 [shipAI message:@"ENERGY_FULL"];
1518 if (energy >= maxEnergy * 0.75)
1520 [shipAI message:@"ENERGY_HIGH"];
1523 if (energy <= maxEnergy * 0.25)
1525 [shipAI message:@"ENERGY_LOW"];
1528 [shipAI message:@"ENERGY_MEDIUM"];
1531- (void) checkHeatInsulation
1533 float minInsulation = 1000 / [
self maxFlightSpeed] + 1;
1535 if ([
self heatInsulation] < minInsulation)
1537 [shipAI message:@"INSULATION_POOR"];
1540 [shipAI message:@"INSULATION_OK"];
1544- (void) findNewDefenseTarget
1546 [
self checkScanner];
1548 for (i = 0; i < n_scanned_ships ; i++)
1551 if (![ship isCloaked] && (([ship primaryTarget] ==
self && [ship hasHostileTarget]) || [ship isMine] || ([ship isThargoid] != [
self isThargoid])))
1553 if (![
self isDefenseTarget:ship])
1555 [
self addDefenseTarget:ship];
1563- (void) scanForOffenders
1566 NSDictionary *systeminfo = [UNIVERSE currentSystemData];
1567 float gov_factor = 0.4 * [(NSNumber *)[systeminfo objectForKey:KEY_GOVERNMENT] intValue];
1576 [
self checkScanner];
1578 float worst_legal_factor = 0;
1579 GLfloat found_d2 = scannerRange * scannerRange;
1581 for (i = 0; i < n_scanned_ships ; i++)
1584 if ((ship->
scanClass != CLASS_CARGO)&&([ship status] != STATUS_DEAD)&&([ship status] != STATUS_DOCKED)&& ![ship isCloaked])
1586 GLfloat d2 = distance2_scanned_ships[i];
1587 float legal_factor = [ship legalStatus] * gov_factor;
1588 int random_factor = ranrot_rand() & 255;
1589 if ((d2 < found_d2)&&(random_factor < legal_factor)&&(legal_factor > worst_legal_factor))
1591 if (group == nil || group != [ship group])
1593 [self setFoundTarget:ship];
1594 worst_legal_factor = legal_factor;
1600 [
self checkFoundTarget];
1604- (void) setCourseToWitchpoint
1608 _destination = [UNIVERSE getWitchspaceExitPosition];
1609 desired_range = 10000.0;
1614- (void) setDestinationToWitchpoint
1616 _destination = [UNIVERSE getWitchspaceExitPosition];
1620- (void) setDestinationToStationBeacon
1624 _destination = [[UNIVERSE station] beaconPosition];
1629- (void) performHyperSpaceExit
1631 [
self performHyperSpaceExitReplace:YES];
1635- (void) performHyperSpaceExitWithoutReplacing
1637 [
self performHyperSpaceExitReplace:NO];
1641- (void) disengageAutopilot
1643 OOLogERR(
@"ai.invalid.notPlayer",
@"Error in %@:%@, AI method endAutoPilot is only applicable to the player.", [shipAI name], [shipAI state]);
1647- (void) wormholeGroup
1649 NSEnumerator *shipEnum =
nil;
1653 whole = [
self primaryTarget];
1654 if (![whole isWormhole])
return;
1656 for (shipEnum = [[
self group] mutationSafeEnumerator]; (ship = [shipEnum nextObject]); )
1665- (void) commsMessage:(NSString *)valueString
1667 [
self commsMessage:valueString withUnpilotedOverride:NO];
1671- (void) commsMessageByUnpiloted:(NSString *)valueString
1673 [
self commsMessage:valueString withUnpilotedOverride:YES];
1680 while (cargo_to_go > 15)
1685 for (i = 1; i < cargo_to_go; i++)
1687 [
self performSelector:@selector(dumpCargo) withObject:nil afterDelay:0.75 * i];
1692- (void) scanForThargoid
1694 return [
self scanForNearestShipWithPrimaryRole:@"thargoid"];
1698- (void) scanForNonThargoid
1703 [
self checkScanner];
1705 GLfloat found_d2 = scannerRange * scannerRange;
1706 for (i = 0; i < n_scanned_ships ; i++)
1709 GLfloat d2 = distance2_scanned_ships[i];
1710 if (([thing scanClass] != CLASS_CARGO) && ([thing status] != STATUS_DOCKED) && ![thing isThargoid] && ![thing isCloaked] && (d2 < found_d2))
1712 [
self setFoundTarget:thing];
1713 if ([thing isPlayer]) d2 = 0.0;
1718 [
self checkFoundTarget];
1722- (void) thargonCheckMother
1725 if (mother ==
nil && [
self group]) mother = [[
self group] leader];
1727 double maxRange2 = scannerRange * scannerRange;
1729 if (mother && mother !=
self && HPdistance2(mother->
position, position) < maxRange2)
1731 [shipAI message:
@"TARGET_FOUND"];
1736 [self scanForNearestShipHavingRole:
@"thargoid-mothership"];
1737 if ([self foundTarget] != nil)
1739 mother = (ShipEntity*)[self foundTarget];
1740 [self setOwner:mother];
1741 if ([mother group] != [mother escortGroup])
1743 [self setGroup:[mother group]];
1752 int ent_count =
UNIVERSE->n_entities;
1755 for (i = 0; i < ent_count; i++) if (uni_entities[i]->isShip)
1758 if ([other primaryTarget] ==
self)
1762 if ([other isDefenseTarget:
self])
1768 scanClass = CLASS_CARGO;
1769 reportAIMessages = NO;
1770 [
self setAITo:@"dumbAI.plist"];
1772 [
self setSpeed: 0.0];
1773 [
self setGroup:nil];
1779 if (distanceTravelled > desired_range)
1780 [shipAI message:@"GONE_BEYOND_RANGE"];
1786 [
self addDefenseTarget:[
self foundTarget]];
1788 if ([
self hasEscorts])
1790 Entity *leTarget = [
self lastEscortTarget];
1791 if (leTarget !=
nil)
1793 [
self setFoundTarget:leTarget];
1794 [shipAI message:@"FLEEING"];
1798 [
self setPrimaryAggressor:[
self foundTarget]];
1799 [
self addTarget:[
self foundTarget]];
1800 [
self deployEscorts];
1801 [shipAI message:@"DEPLOYING_ESCORTS"];
1802 [shipAI message:@"FLEEING"];
1811 [
self setPrimaryAggressor:[
self foundTarget]];
1812 [
self addTarget:[
self foundTarget]];
1814 [shipAI message:@"FLEEING"];
1820 if (energy > maxEnergy * 0.80)
1822 [
self setPrimaryAggressor:[
self foundTarget]];
1824 [shipAI message:@"FIGHTING"];
1828 [shipAI message:@"FLEEING"];
1835 [
self suggestEscortTo:mother];
1844 if ([mother acceptAsEscort:
self])
1846 [
self setOwner:mother];
1848 [shipAI message:@"ESCORTING"];
1852 [
self setOwner:self];
1853 if ([
self group] == [mother escortGroup]) [
self setGroup:nil];
1854 [shipAI message:@"NOT_ESCORTING"];
1861 NSUInteger ownGroupCount = [[
self group] count] + (
ranrot_rand() & 3);
1862 NSUInteger targetGroupCount = [[[
self primaryTarget] group] count] + (
ranrot_rand() & 3);
1864 if (ownGroupCount == targetGroupCount)
1866 [shipAI message:@"ODDS_LEVEL"];
1868 else if (ownGroupCount > targetGroupCount)
1870 [shipAI message:@"ODDS_GOOD"];
1874 [shipAI message:@"ODDS_BAD"];
1885 [
self checkScannerIgnoringUnpowered];
1887 GLfloat found_d2 = scannerRange * scannerRange;
1888 for (i = 0; i < n_scanned_ships; i++)
1891 if ((ship !=
self) && (!ship->
isPlayer) && (ship->
scanClass == scanClass) && [ship primaryTarget] !=
self && ![ship isCloaked])
1893 GLfloat d2 = distance2_scanned_ships[i];
1894 if ((d2 < found_d2) && [ship canAcceptEscort:self])
1897 [self setFoundTarget:ship];
1902 if ([
self foundTarget] !=
nil) [shipAI message:
@"TARGET_FOUND"];
1905 [shipAI message:@"NOTHING_FOUND"];
1906 if ([
self hasPrimaryRole:
@"wingman"])
1909 [
self setAITo:@"route1patrolAI.plist"];
1910 [
self setPrimaryRole:@"police"];
1917- (void) messageMother:(NSString *)msgString
1920 if (mother !=
nil && mother !=
self)
1922 NSString *context =
nil;
1924 context = [NSString stringWithFormat:@"%@ messageMother", [
self shortDescription]];
1931- (void) messageSelf:(NSString *)msgString
1933 [
self sendAIMessage:msgString];
1940 HPVector r_pos = HPvector_subtract(position, coordinates);
1941 if (HPmagnitude2(r_pos) < 1000000 || patrol_counter == 0)
1943 Entity *the_sun = [UNIVERSE sun];
1944 ShipEntity *the_station = [[
self group] leader];
1945 if(!the_station || ![the_station isStation]) the_station = [UNIVERSE station];
1946 if ((!the_sun)||(!the_station))
1948 HPVector sun_pos = the_sun->
position;
1949 HPVector stn_pos = the_station->
position;
1950 HPVector sun_dir = HPvector_subtract(sun_pos,stn_pos);
1951 Vector vSun = make_vector(0, 0, 1);
1952 if (sun_dir.x||sun_dir.y||sun_dir.z)
1953 vSun = HPVectorToVector(HPvector_normal(sun_dir));
1955 Vector v1 = cross_product(v0, vSun);
1956 Vector v2 = cross_product(v0, v1);
1957 switch (patrol_counter)
1960 coordinates = make_HPvector(stn_pos.x + 5000 * v0.x, stn_pos.y + 5000 * v0.y, stn_pos.z + 5000 * v0.z);
1961 desired_range = 250.0;
1964 coordinates = make_HPvector(stn_pos.x + 25000 * v1.x, stn_pos.y + 25000 * v1.y, stn_pos.z + 25000 * v1.z);
1965 desired_range = 250.0;
1968 coordinates = make_HPvector(stn_pos.x + 25000 * v2.x, stn_pos.y + 25000 * v2.y, stn_pos.z + 25000 * v2.z);
1969 desired_range = 250.0;
1972 coordinates = make_HPvector(stn_pos.x - 25000 * v1.x, stn_pos.y - 25000 * v1.y, stn_pos.z - 25000 * v1.z);
1973 desired_range = 250.0;
1976 coordinates = make_HPvector(stn_pos.x - 25000 * v2.x, stn_pos.y - 25000 * v2.y, stn_pos.z - 25000 * v2.z);
1977 desired_range = 250.0;
1980 coordinates = make_HPvector(stn_pos.x + 5000 * v0.x, stn_pos.y + 5000 * v0.y, stn_pos.z + 5000 * v0.z);
1981 desired_range = 250.0;
1985 if (patrol_counter > 4)
1990 [
self setTargetStation:the_station];
1991 [
self setAITo:@"dockingAI.plist"];
2001 [shipAI message:@"APPROACH_COORDINATES"];
2009 [shipAI message:@"NO_SUN_FOUND"];
2013 HPVector v0 = [UNIVERSE getSunSkimStartPositionForShip:self];
2018 [shipAI message:@"APPROACH_COORDINATES"];
2022 [shipAI message:@"WAIT_FOR_SUN"];
2031 [shipAI message:@"NO_SUN_FOUND"];
2035 coordinates = [UNIVERSE getSunSkimEndPositionForShip:self];
2036 [shipAI message:@"APPROACH_COORDINATES"];
2042 Entity *the_sun = [UNIVERSE sun];
2043 if (the_sun ==
nil)
return;
2044 HPVector v1 = [UNIVERSE getSunSkimEndPositionForShip:self];
2046 HPVector vout = HPvector_subtract(v1,vs);
2047 if (vout.x||vout.y||vout.z)
2048 vout = HPvector_normal(vout);
2051 v1.x += 10000 * vout.
x; v1.y += 10000 * vout.
y; v1.z += 10000 * vout.z;
2053 [shipAI message:@"APPROACH_COORDINATES"];
2060 ShipEntity *the_station = [[
self group] leader];
2061 if(!the_station || ![the_station isStation]) the_station = [UNIVERSE station];
2068 ShipEntity *motherStation = [[
self group] leader];
2069 if ((!motherStation) || (!(motherStation->
isStation)))
2071 [shipAI message:@"NOTHING_FOUND"];
2074 double found_d2 = scannerRange * scannerRange;
2075 HPVector v0 = motherStation->
position;
2076 if (HPdistance2(v0,position) > found_d2)
2078 [shipAI message:@"NOTHING_FOUND"];
2081 [shipAI message:@"STATION_FOUND"];
2085- (void) sendTargetCommsMessage:(NSString*) message
2088 if ((ship ==
nil) || ([ship status] == STATUS_DEAD) || ([ship status] == STATUS_DOCKED))
2090 [
self noteLostTarget];
2093 [
self sendExpandedMessage:message toShip:[
self primaryTarget]];
2100 if ((ship ==
nil) || ([ship status] == STATUS_DEAD) || ([ship status] == STATUS_DOCKED))
2102 [
self noteLostTarget];
2105 if ([ship markForFines]) [shipAI message:@"TARGET_MARKED"];
2109- (void) markTargetForOffence:(NSString *)valueString
2111 if ((isStation)||(scanClass == CLASS_POLICE))
2114 if ((ship ==
nil) || ([ship status] == STATUS_DEAD) || ([ship status] == STATUS_DOCKED))
2116 [
self noteLostTarget];
2119 NSString *finalValue =
OOExpand(valueString);
2127 Entity *target = [
self primaryTarget];
2131 [
self setRememberedShip:target];
2145 if (oldTarget && ![oldTarget isCloaked])
2147 GLfloat range2 = HPdistance2([oldTarget position], position);
2156 [
self setFoundTarget:oldTarget];
2157 [shipAI message:@"TARGET_FOUND"];
2161 if (oldTarget ==
nil)
DESTROY(_rememberedShip);
2162 [shipAI message:@"NOTHING_FOUND"];
2174 [
self checkScanner];
2176 GLfloat found_d2 = scannerRange * scannerRange;
2177 for (i = 0; i < n_scanned_ships; i++)
2180 if ([thing isBoulder])
2182 GLfloat d2 = distance2_scanned_ships[i];
2185 [
self setFoundTarget:thing];
2190 if ([
self foundTarget] ==
nil)
2192 for (i = 0; i < n_scanned_ships; i++)
2195 if ([thing hasRole:
@"asteroid"])
2197 GLfloat d2 = distance2_scanned_ships[i];
2200 [
self setFoundTarget:thing];
2207 [
self checkFoundTarget];
2213 Entity *the_target = [
self targetStation];
2217 the_target = [UNIVERSE station];
2219 double bo_distance = 8000;
2220 HPVector v0 = position;
2222 v0.x += (
randf() - 0.5)*collision_radius; v0.y += (
randf() - 0.5)*collision_radius; v0.z += (
randf() - 0.5)*collision_radius;
2223 v0.x -= d0.
x; v0.y -= d0.
y; v0.z -= d0.z;
2224 v0 = HPvector_normal_or_fallback(v0, make_HPvector(0, 0, -1));
2226 v0.x *= bo_distance; v0.y *= bo_distance; v0.z *= bo_distance;
2227 v0.x += d0.
x; v0.y += d0.
y; v0.z += d0.z;
2238 [shipAI message:@"MOTHER_LOST"];
2244 [
self checkScanner];
2246 GLfloat found_d2 = scannerRange * scannerRange;
2248 for (i = 0; i < n_scanned_ships ; i++)
2251 GLfloat d2 = distance2_scanned_ships[i];
2252 GLfloat e1 = [thing
energy];
2253 if ((d2 < found_d2) && ![thing isCloaked] && (([thing isThargoid] && ![mother isThargoid]) || (([thing primaryTarget] == mother) && [thing hasHostileTarget])))
2257 [
self setFoundTarget:thing];
2263 [
self checkFoundTarget];
2267- (void) rollD:(NSString *)die_number
2269 int die_sides = [die_number intValue];
2273 NSString* result = [NSString stringWithFormat:@"ROLL_%d", die_roll];
2274 [shipAI reactToMessage:result context:@"rollD:"];
2278 OOLog(
@"ai.rollD.invalidValue",
@"***** ERROR: invalid value supplied to rollD: '%@'.", die_number);
2283- (void) scanForNearestShipWithPrimaryRole:(NSString *)scanRole
2285 [
self scanForNearestShipWithPredicate:HasPrimaryRolePredicate parameter:scanRole];
2289- (void) scanForNearestShipHavingRole:(NSString *)scanRole
2291 [
self scanForNearestShipWithPredicate:HasRolePredicate parameter:scanRole];
2295- (void) scanForNearestShipWithAnyPrimaryRole:(NSString *)scanRoles
2297 NSSet *
set = [NSSet setWithArray:ScanTokensFromString(scanRoles)];
2298 [
self scanForNearestShipWithPredicate:HasPrimaryRoleInSetPredicate parameter:set];
2302- (void) scanForNearestShipHavingAnyRole:(NSString *)scanRoles
2304 NSSet *
set = [NSSet setWithArray:ScanTokensFromString(scanRoles)];
2305 [
self scanForNearestShipWithPredicate:HasRoleInSetPredicate parameter:set];
2309- (void) scanForNearestShipWithScanClass:(NSString *)scanScanClass
2311 NSNumber *parameter = [NSNumber numberWithInt:OOScanClassFromString(scanScanClass)];
2312 [
self scanForNearestShipWithPredicate:HasScanClassPredicate parameter:parameter];
2316- (void) scanForNearestShipWithoutPrimaryRole:(NSString *)scanRole
2318 [
self scanForNearestShipWithNegatedPredicate:HasPrimaryRolePredicate parameter:scanRole];
2322- (void) scanForNearestShipNotHavingRole:(NSString *)scanRole
2324 [
self scanForNearestShipWithNegatedPredicate:HasRolePredicate parameter:scanRole];
2328- (void) scanForNearestShipWithoutAnyPrimaryRole:(NSString *)scanRoles
2330 NSSet *
set = [NSSet setWithArray:ScanTokensFromString(scanRoles)];
2331 [
self scanForNearestShipWithNegatedPredicate:HasPrimaryRoleInSetPredicate parameter:set];
2335- (void) scanForNearestShipNotHavingAnyRole:(NSString *)scanRoles
2337 NSSet *
set = [NSSet setWithArray:ScanTokensFromString(scanRoles)];
2338 [
self scanForNearestShipWithNegatedPredicate:HasRoleInSetPredicate parameter:set];
2342- (void) scanForNearestShipWithoutScanClass:(NSString *)scanScanClass
2344 NSNumber *parameter = [NSNumber numberWithInt:OOScanClassFromString(scanScanClass)];
2345 [
self scanForNearestShipWithNegatedPredicate:HasScanClassPredicate parameter:parameter];
2349- (void) scanForNearestShipMatchingPredicate:(NSString *)predicateExpression
2366 static NSMutableDictionary *scriptCache =
nil;
2367 NSString *aiName =
nil;
2368 NSString *key =
nil;
2370 JSContext *context = NULL;
2374 if (predicateExpression ==
nil) predicateExpression =
@"false";
2376 aiName = [[
self getAI] name];
2383 key = [NSString stringWithFormat:@"%@\n%@", aiName, predicateExpression];
2385 key = predicateExpression;
2389 function = [scriptCache objectForKey:key];
2390 if (
function ==
nil)
2392 NSString *predicateCode =
nil;
2393 const char *argNames[] = {
"ship" };
2396 predicateCode = [NSString stringWithFormat:@"return %@;", predicateExpression];
2397 function = [[
OOJSFunction alloc] initWithName:@"_oo_AIScanPredicate"
2401 argumentNames:argNames
2405 [function autorelease];
2408 if (
function !=
nil)
2410 if (scriptCache ==
nil) scriptCache = [[NSMutableDictionary alloc] init];
2411 [scriptCache setObject:function forKey:key];
2415 if (
function !=
nil)
2420 .function = [function functionValue],
2423 [
self scanForNearestShipWithPredicate:JSFunctionPredicate parameter:¶m];
2428 static NSMutableSet *errorCache =
nil;
2430 if (![errorCache containsObject:key])
2432 OOLog(
@"ai.scanForNearestShipMatchingPredicate.compile.failed",
@"Could not compile JavaScript predicate \"%@\
" for AI %@.", predicateExpression, [[
self getAI] name]);
2433 if (errorCache ==
nil) errorCache = [[NSMutableSet alloc] init];
2434 [errorCache addObject:key];
2439 [[
self getAI] message:@"NOTHING_FOUND"];
2442 JS_ReportPendingException(context);
2447- (void) setCoordinates:(NSString *)system_x_y_z
2450 NSString* systemString =
nil;
2451 NSString* xString =
nil;
2452 NSString* yString =
nil;
2453 NSString* zString =
nil;
2455 if ([tokens
count] != 4)
2457 OOLog(
@"ai.syntax.setCoordinates",
@"***** ERROR: cannot setCoordinates: '%@'.",system_x_y_z);
2461 systemString = (NSString *)[tokens objectAtIndex:0];
2462 xString = (NSString *)[tokens objectAtIndex:1];
2463 if ([xString hasPrefix:
@"rand:"])
2464 xString = [NSString stringWithFormat:@"%.3f", bellf([(NSString*)[[xString componentsSeparatedByString:@":"] objectAtIndex:1] intValue])];
2465 yString = (NSString *)[tokens objectAtIndex:2];
2466 if ([yString hasPrefix:
@"rand:"])
2467 yString = [NSString stringWithFormat:@"%.3f", bellf([(NSString*)[[yString componentsSeparatedByString:@":"] objectAtIndex:1] intValue])];
2468 zString = (NSString *)[tokens objectAtIndex:3];
2469 if ([zString hasPrefix:
@"rand:"])
2470 zString = [NSString stringWithFormat:@"%.3f", bellf([(NSString*)[[zString componentsSeparatedByString:@":"] objectAtIndex:1] intValue])];
2472 HPVector posn = make_HPvector([xString floatValue], [yString floatValue], [zString floatValue]);
2473 GLfloat scalar = 1.0;
2475 coordinates = [UNIVERSE coordinatesForPosition:posn withCoordinateSystem:systemString returningScalar:&scalar];
2477 [shipAI message:@"APPROACH_COORDINATES"];
2484 [shipAI message:@"NORMAL_SPACE"];
2486 [shipAI message:@"INTERSTELLAR_SPACE"];
2493 int ent_count =
UNIVERSE->n_entities;
2495 Entity *my_entities[ent_count];
2497 double maxRange2 = desired_range * desired_range;
2499 int station_count = 0;
2501 for (i = 0; i < ent_count; i++)
2504 if (uni_entities[i]->isStation)
2507 if ([my_station maxFlightSpeed] == 0 && [my_station hasNPCTraffic] && HPdistance2(position, [my_station position]) < maxRange2)
2509 my_entities[station_count++] = [uni_entities[i] retain];
2514 if (station_count != 0)
2519 if (station == [
UNIVERSE station] && station_count > 1)
2521 while (station == [
UNIVERSE station])
2528 for (i = 0; i < station_count; i++)
2529 [my_entities[i] release];
2533 [
self addTarget:station];
2534 [
self setTargetStation:station];
2535 [shipAI message:@"STATION_FOUND"];
2539 [shipAI message:@"NO_STATION_IN_RANGE"];
2545 Entity *station = [
self targetStation];
2547 if (station !=
nil && [station isStation])
2549 [
self addTarget:station];
2553 [shipAI message:@"NO_STATION_FOUND"];
2554 [
self setTargetStation:nil];
2560- (void) addFuel:(NSString*) fuel_number
2562 [
self setFuel:[
self fuel] + [fuel_number intValue] * 10];
2567- (void) scriptActionOnTarget:(NSString *)action
2574 static BOOL deprecationWarning = NO;
2576 if (!deprecationWarning)
2578 deprecationWarning = YES;
2579 OOLog(
@"script.deprecated.scriptActionOnTarget",
@"----- WARNING in AI %@: the AI method scriptActionOnTarget: is deprecated and should not be used. It is slow and has unpredictable side effects. The recommended alternative is to use sendScriptMessage: to call a function in a ship's JavaScript ship script instead. scriptActionOnTarget: should not be used at all from scripts. An alternative is safeScriptActionOnTarget:, which is similar to scriptActionOnTarget: but has less side effects.", [
AI currentlyRunningAIDescription]);
2583 OOLog(
@"script.deprecated.scriptActionOnTarget.repeat",
@"----- WARNING in AI %@: the AI method scriptActionOnTarget: is deprecated and should not be used.", [
AI currentlyRunningAIDescription]);
2587 if ([targEnt isShip])
2593 withContextName:[NSString stringWithFormat:@"<AI \"%@\" state %@ - scriptActionOnTarget:>", [[
self getAI] name], [[
self getAI] state]]
2601- (void) safeScriptActionOnTarget:(NSString *)action
2607 if ([targEnt isShip])
2613 withContextName:[NSString stringWithFormat:@"<AI \"%@\" state %@ - safeScriptActionOnTarget:>", [[
self getAI] name], [[
self getAI] state]]
2621- (void) sendScriptMessage:(NSString *)message
2625 if ([components
count] == 1)
2627 [
self doScriptEvent:OOJSIDFromString(message)];
2631 NSString *
function = [components objectAtIndex:0];
2632 components = [components subarrayWithRange:NSMakeRange(1, [components count] - 1)];
2633 [
self doScriptEvent:OOJSIDFromString(function) withArgument:components];
2640 [
self setThrowSparks:YES];
2646 [
self getDestroyedBy:nil damageType:kOODamageTypeEnergy];
2650- (void) ai_debugMessage:(NSString *)message
2652 NSString *desc = [NSString stringWithFormat:@"%@ %d", [
self name], [
self universalID]];
2653 if ([
self isPlayer]) desc =
@"player autopilot";
2654 OOLog(
@"ai.takeAction.debugMessage",
@"DEBUG: AI MESSAGE from %@: %@", desc, message);
2660- (void) targetFirstBeaconWithCode:(NSString*) code
2662 NSArray *all_beacons = [UNIVERSE listBeaconsWithCode: code];
2663 if ([all_beacons
count])
2665 [
self addTarget:(ShipEntity*)[all_beacons objectAtIndex:0]];
2666 [shipAI message:@"TARGET_FOUND"];
2669 [shipAI message:@"NOTHING_FOUND"];
2673- (void) targetNextBeaconWithCode:(NSString*) code
2675 NSArray *all_beacons = [UNIVERSE listBeaconsWithCode: code];
2676 ShipEntity *current_beacon = [
self primaryTarget];
2678 if ((!current_beacon)||(![current_beacon isBeacon]))
2680 [shipAI message:@"NO_CURRENT_BEACON"];
2681 [shipAI message:@"NOTHING_FOUND"];
2686 NSUInteger i = [all_beacons indexOfObject:current_beacon];
2688 if (i == NSNotFound)
2690 [shipAI message:@"NOTHING_FOUND"];
2696 if (i < [all_beacons
count])
2699 [
self addTarget:(ShipEntity*)[all_beacons objectAtIndex:i]];
2700 [shipAI message:@"TARGET_FOUND"];
2704 [shipAI message:@"LAST_BEACON"];
2705 [shipAI message:@"NOTHING_FOUND"];
2716 [shipAI message:@"NOTHING_FOUND"];
2722 navpoints[0] = make_HPvector(o.x - c * k.x, o.y - c * k.y, o.z - c * k.z);
2723 navpoints[1] = make_HPvector(o.x + c * k.x, o.y + c * k.y, o.z + c * k.z);
2724 navpoints[2] = make_HPvector(o.x + 2.0 * c * k.x, o.y + 2.0 * c * k.y, o.z + 2.0 * c * k.z);
2725 number_of_navpoints = 2;
2726 next_navpoint_index = 0;
2727 _destination = navpoints[0];
2728 [shipAI message:@"RACEPOINTS_SET"];
2734 next_navpoint_index = 0;
2735 desired_range = collision_radius;
2736 behaviour = BEHAVIOUR_FLY_THRU_NAVPOINTS;
2742@implementation ShipEntity (OOAIPrivate)
2747 if ([
self foundTarget] !=
nil)
2749 [shipAI message:@"TARGET_FOUND"];
2753 [shipAI message:@"NOTHING_FOUND"];
2758- (BOOL) performHyperSpaceExitReplace:(BOOL)replace
2760 return [
self performHyperSpaceExitReplace:replace toSystem:-1];
2764- (BOOL) performHyperSpaceExitReplace:(BOOL)replace toSystem:(
OOSystemID)systemID
2766 if(![
self hasHyperspaceMotor])
2768 [shipAI reactToMessage:@"WITCHSPACE UNAVAILABLE" context:@"performHyperSpaceExit"];
2771 if([
self status] == STATUS_ENTERING_WITCHSPACE)
2777 NSArray *sDests =
nil;
2782 sDests = [UNIVERSE nearbyDestinationsWithinRange: 0.1f * fuel];
2783 NSUInteger n_dests = [sDests count];
2788 [shipAI reactToMessage:@"WITCHSPACE UNAVAILABLE" context:@"performHyperSpaceExit"];
2796 ShipEntity *blocker = [UNIVERSE entityForUniversalID:[
self checkShipsInVicinityForWitchJumpExit]];
2799 [
self setFoundTarget:blocker];
2800 [shipAI reactToMessage:@"WITCHSPACE BLOCKED" context:@"performHyperSpaceExit"];
2801 [
self doScriptEvent:OOJSID("shipWitchspaceBlocked") withArgument:blocker];
2815 targetSystem = [[sDests oo_dictionaryAtIndex:i] oo_intForKey:@"sysID"];
2819 targetSystem = systemID;
2821 for (i = 0; i < n_dests; i++)
2823 if (systemID == [[sDests oo_dictionaryAtIndex:i] oo_intForKey:
@"sysID"])
break;
2831 float dist = [[sDests oo_dictionaryAtIndex:i] oo_floatForKey:@"distance"];
2832 if (dist > [
self maxHyperspaceDistance] || dist > fuel/10.0f)
2834 OOLogWARN(
@"script.debug",
@"DEBUG: %@ Jumping %f which is further than allowed. I have %d fuel",
self, dist, fuel);
2840 [UNIVERSE addEntity: whole];
2842 [
self enterWormhole:whole replacing:replace];
2849- (void) scanForNearestShipWithPredicate:(
EntityFilterPredicate)predicate parameter:(
void *)parameter
2854 float d2, found_d2 = scannerRange * scannerRange;
2857 [
self checkScanner];
2859 if (predicate == NULL)
return;
2861 for (i = 0; i < n_scanned_ships ; i++)
2863 candidate = scanned_ships[i];
2864 d2 = distance2_scanned_ships[i];
2865 if ((d2 < found_d2) && (candidate->
scanClass != CLASS_CARGO) && ([candidate status] != STATUS_DOCKED)
2866 && predicate(candidate, parameter) && ![candidate isCloaked])
2868 [
self setFoundTarget:candidate];
2873 [
self checkFoundTarget];
2877- (void) scanForNearestShipWithNegatedPredicate:(
EntityFilterPredicate)predicate parameter:(
void *)parameter
2880 [
self scanForNearestShipWithPredicate:NOTPredicate parameter:¶m];
2884- (void) acceptDistressMessageFrom:(
ShipEntity *)other
2887 if ([
self isPolice])
2889 [(
ShipEntity*)[
self foundTarget] markAsOffender:8 withReason:kOOLegalStatusReasonDistressCall];
2892 NSString *context =
nil;
2894 context = [NSString stringWithFormat:@"%@ broadcastDistressMessage", [other shortDescription]];
2896 [shipAI reactToMessage:@"ACCEPT_DISTRESS_CALL" context:context];