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
343 target = [
self primaryTarget];
345 if (target ==
nil)
return;
347 if ([
self group] ==
nil)
349 [
self setFoundTarget:target];
350 [shipAI reactToMessage:@"GROUP_ATTACK_TARGET" context:@"groupAttackTarget"];
351 [
self doScriptEvent:OOJSID("helpRequestReceived") withArgument:self andArgument:target];
355 foreach (ship, [[
self group] mutationSafeEnumerator])
357 [ship setFoundTarget:target];
358 [ship reactToAIMessage:@"GROUP_ATTACK_TARGET" context:@"groupAttackTarget"];
359 [ship doScriptEvent:OOJSID("helpRequestReceived") withArgument:self andArgument:target];
361 if ([ship escortGroup] != [ship group] && [[ship escortGroup]
count] > 1)
364 NSArray *escortMembers = [[ship escortGroup] memberArrayExcludingLeader];
365 foreach (escort, escortMembers)
376- (void) performAttack
378 if (behaviour != BEHAVIOUR_EVASIVE_ACTION)
380 behaviour = BEHAVIOUR_ATTACK_TARGET;
381 desired_range = 1250 *
randf() + 750;
387- (void) performCollect
389 behaviour = BEHAVIOUR_COLLECT_TARGET;
394- (void) performEscort
396 if(behaviour != BEHAVIOUR_FORMATION_FORM_UP)
398 behaviour = BEHAVIOUR_FORMATION_FORM_UP;
404- (void) performFaceDestination
406 behaviour = BEHAVIOUR_FACE_DESTINATION;
413 if (behaviour != BEHAVIOUR_FLEE_EVASIVE_ACTION)
415 behaviour = BEHAVIOUR_FLEE_TARGET;
416 [
self setEvasiveJink:400.0];
421 if ([
self approachAspectToPrimaryTarget] > 0.9995)
423 behaviour =
randf() < 0.15 ? BEHAVIOUR_EVASIVE_ACTION : BEHAVIOUR_FLEE_EVASIVE_ACTION;
430- (void) performFlyToRangeFromDestination
432 behaviour = BEHAVIOUR_FLY_RANGE_FROM_DESTINATION;
440 behaviour = BEHAVIOUR_TRACK_TARGET;
447 behaviour = BEHAVIOUR_IDLE;
452- (void) performIntercept
454 behaviour = BEHAVIOUR_INTERCEPT_TARGET;
459- (void) performLandOnPlanet
462 if (isNearPlanetSurface)
465 behaviour = BEHAVIOUR_LAND_ON_PLANET;
470 behaviour = BEHAVIOUR_IDLE;
471 [shipAI message:@"NO_PLANET_NEARBY"];
478- (void) performMining
480 Entity *target = [
self primaryTarget];
482 if (target && [target scanClass] == CLASS_ROCK)
484 behaviour = BEHAVIOUR_ATTACK_MINING_TARGET;
489 [
self noteLostTargetAndGoIdle];
494- (void) performScriptedAI
496 behaviour = BEHAVIOUR_SCRIPTED_AI;
501- (void) performScriptedAttackAI
503 behaviour = BEHAVIOUR_SCRIPTED_ATTACK_AI;
508- (void) performBuoyTumble
512 behaviour = BEHAVIOUR_TUMBLE;
519 behaviour = BEHAVIOUR_STOP_STILL;
525- (void) performTumble
527 stick_roll = max_flight_roll*2.0*(
randf() - 0.5);
528 stick_pitch = max_flight_pitch*2.0*(
randf() - 0.5);
529 behaviour = BEHAVIOUR_TUMBLE;
534- (BOOL) performHyperSpaceToSpecificSystem:(
OOSystemID)systemID
536 return [
self performHyperSpaceExitReplace:NO toSystem:systemID];
540- (void) requestDockingCoordinates
548 NSString *message =
nil;
549 double distanceToStation2 = 0.0;
551 targStation = [
self targetStation];
552 if ([targStation isStation])
558 station = [UNIVERSE nearestShipMatchingPredicate:IsStationPredicate
560 relativeToEntity:self];
563 distanceToStation2 = HPdistance2([station position], [
self position]);
573 [dockingInstructions release];
575 if (dockingInstructions !=
nil)
577 [
self recallDockingInstructions];
579 message = [dockingInstructions objectForKey:@"ai_message"];
580 if (message !=
nil) [shipAI message:message];
581 message = [dockingInstructions objectForKey:@"comms_message"];
590 if (dockingInstructions ==
nil)
592 [shipAI message:@"NO_STATION_FOUND"];
597- (void) recallDockingInstructions
599 if (dockingInstructions !=
nil)
601 _destination = [dockingInstructions oo_hpvectorForKey:@"destination"];
602 desired_speed = fmin([dockingInstructions oo_floatForKey:
@"speed"], maxFlightSpeed);
603 desired_range = [dockingInstructions oo_floatForKey:@"range"];
604 if ([dockingInstructions objectForKey:
@"station"])
606 StationEntity *targetStation = [[dockingInstructions objectForKey:@"station"] weakRefUnderlyingObject];
607 if (targetStation !=
nil)
609 [
self addTarget:targetStation];
610 [
self setTargetStation:targetStation];
614 [
self removeTarget:[
self primaryTarget]];
617 docking_match_rotation = [dockingInstructions oo_boolForKey:@"match_rotation"];
622- (void) scanForNearestIncomingMissile
629 [
self scanForNearestShipWithPredicate:ANDPredicate parameter:¶m];
632- (void) enterPlayerWormhole
634 [
self enterWormhole:[PLAYER wormhole] replacing:NO];
637- (void) enterTargetWormhole
641 double found_d2 = scannerRange * scannerRange;
643 if (targEnt && (HPdistance2(position, [targEnt position]) < found_d2))
645 if ([targEnt isWormhole])
647 else if ([targEnt isPlayer])
648 whole = [PLAYER wormhole];
654 int ent_count =
UNIVERSE->n_entities;
659 for (i = 0; i < ent_count; i++)
660 if (uni_entities[i]->isWormhole)
661 wormholes[wh_count++] = [(
WormholeEntity *)uni_entities[i] retain];
664 for (i = 0; i < wh_count ; i++)
667 double d2 = HPdistance2(position, wh->
position);
677 [
self enterWormhole:whole replacing:NO];
682- (void) wormholeEscorts
685 NSString *context =
nil;
688 whole = [
self primaryTarget];
689 if (![whole isWormhole])
return;
692 context = [NSString stringWithFormat:@"%@ wormholeEscorts", [
self shortDescription]];
695 foreach (ship, [
self escortEnumerator])
704 [_escortGroup release];
710- (void) wormholeEntireGroup
712 [
self wormholeGroup];
713 [
self wormholeEscorts];
722 if (reportAIMessages)
724 OOLog(
@"ai.suggestEscort",
@"DEBUG: %@ suggests escorting %@",
self, mother);
728 if ([mother acceptAsEscort:
self])
731 if (([mother legalStatus] > 0)&&(bounty <= 0))
735 [
self markAsOffender:extra withReason:kOOLegalStatusReasonAssistingOffender];
739 [
self setOwner:mother];
741 [shipAI message:@"ESCORTING"];
746 if (reportAIMessages)
748 OOLog(
@"ai.suggestEscort.refused",
@"DEBUG: %@ refused by %@",
self, mother);
753 [
self setOwner:self];
754 [shipAI message:@"NOT_ESCORTING"];
755 [
self doScriptEvent:OOJSID("escortRejected") withArgument:mother];
760- (void) broadcastDistressMessage
763 [
self broadcastDistressMessageWithDumping:YES];
766- (void) broadcastDistressMessageWithDumping:(BOOL)dumpCargo
768 [
self checkScannerIgnoringUnpowered];
772 if (aggressor_ship ==
nil)
return;
775 if (messageTime > 2.0 *
randf())
return;
777 NSString *distress_message =
nil;
778 BOOL is_buoy = (scanClass == CLASS_BUOY);
779 if (is_buoy) distress_message =
@"[buoy-distress-call]";
780 else distress_message =
@"[distress-call]";
783 for (i = 0; i < n_scanned_ships; i++)
788 if (dumpCargo && !is_buoy && [
self primaryAggressor] == ship && energy < 0.375 * maxEnergy)
796 if (ship->
isPlayer && ![
self hasNewAI])
800 if (!is_buoy && [
self primaryAggressor] == ship && energy < 0.375 * maxEnergy)
802 [
self sendExpandedMessage:@"[beg-for-mercy]" toShip:ship];
804 else if ([
self bounty] == 0)
808 [
self sendExpandedMessage:distress_message toShip:ship];
814 else if ([
self bounty] == 0 && [ship crew])
824 if (![
self hasNewAI])
827 if (ship->
isStation || [ship hasPrimaryRole:
@"police"] || [ship hasPrimaryRole:
@"hunter"])
840@implementation ShipEntity (PureAI)
842- (void) setStateTo:(NSString *)state
844 [[
self getAI] setState:state];
848- (void) pauseAI:(NSString *)intervalString
850 [shipAI setNextThinkTime:[UNIVERSE getTime] + [intervalString doubleValue]];
854- (void) randomPauseAI:(NSString *)intervalString
859 if ([tokens
count] != 2)
861 OOLog(
@"ai.syntax.randomPauseAI",
@"***** ERROR: cannot read min and max value for randomPauseAI:, needs 2 values: '%@'.", intervalString);
865 start = [tokens oo_doubleAtIndex:0];
866 end = [tokens oo_doubleAtIndex:1];
868 [shipAI setNextThinkTime:[UNIVERSE getTime] + (start + (end - start)*randf())];
872- (void) dropMessages:(NSString *)messageString
874 NSArray *messages =
nil;
875 NSString *message =
nil;
876 NSCharacterSet *whiteSpace = [NSCharacterSet whitespaceCharacterSet];
878 messages = [messageString componentsSeparatedByString:@","];
879 foreach (message, messages)
881 [shipAI dropMessage:[message stringByTrimmingCharactersInSet:whiteSpace]];
886- (void) debugDumpPendingMessages
888 [shipAI debugDumpPendingMessages];
892- (void) setDestinationToCurrentLocation
899- (void) setDestinationToJinkPosition
901 Vector front = vector_multiply_scalar([
self forwardVector], flightSpeed / max_flight_pitch * 2);
902 _destination = HPvector_add(position, vectorToHPVector(vector_add(front,
OOVectorRandomSpatial(100))));
907- (void) setDesiredRangeTo:(NSString *)rangeString
909 desired_range = [rangeString doubleValue];
912- (void) setDesiredRangeForWaypoint
914 desired_range = fmax(maxFlightSpeed / max_flight_pitch / 6, 50.0);
917- (void) setSpeedTo:(NSString *)speedString
919 desired_speed = [speedString doubleValue];
923- (void) setSpeedFactorTo:(NSString *)speedString
925 desired_speed = maxFlightSpeed * [speedString doubleValue];
928- (void) setSpeedToCruiseSpeed
930 desired_speed = cruiseSpeed;
933- (void) setThrustFactorTo:(NSString *)thrustFactorString
935 thrust = OOClamp_0_1_f([thrustFactorString doubleValue]) * max_thrust;
939- (void) setTargetToPrimaryAggressor
941 Entity *primeAggressor = [
self primaryAggressor];
944 if ([
self primaryTarget] == primeAggressor)
950 if ([
self hasHostileTarget] &&
randf() < 0.75)
953 [
self addDefenseTarget:(ShipEntity*)primeAggressor];
957 if ([primeAggressor isShip] && ![(
ShipEntity *)primeAggressor isFriendlyTo:
self])
961 Entity *primeTarget = [
self primaryTarget];
962 if ((primeTarget)&&(primeTarget->
isShip))
964 ShipEntity *currentShip = [
self primaryTarget];
965 [[currentShip
getAI]
message:[NSString stringWithFormat:@"%@ %d %d", AIMS_AGGRESSOR_SWITCHED_TARGET, universalID, [[
self primaryAggressor] universalID]]];
970 [
self addTarget:[
self primaryAggressor]];
975- (void) addPrimaryAggressorAsDefenseTarget
977 Entity *primeAggressor = [
self primaryAggressor];
980 if ([
self isDefenseTarget:primeAggressor])
983 if ([primeAggressor isShip] && ![(
ShipEntity*)primeAggressor isFriendlyTo:
self])
985 [
self addDefenseTarget:primeAggressor];
990- (void) scanForNearestMerchantman
997 [
self checkScannerIgnoringUnpowered];
999 found_d2 = scannerRange * scannerRange;
1002 for (i = 0; i < n_scanned_ships ; i++)
1004 ship = scanned_ships[i];
1005 if ([ship isPirateVictim] && ([ship status] != STATUS_DEAD) && ([ship status] != STATUS_DOCKED) && ![ship isCloaked])
1007 d2 = distance2_scanned_ships[i];
1012 else d2 = distance2_scanned_ships[i];
1016 [
self setFoundTarget:ship];
1020 [
self checkFoundTarget];
1024- (void) scanForRandomMerchantman
1026 unsigned n_found, i;
1029 [
self checkScannerIgnoringUnpowered];
1034 for (i = 0; i < n_scanned_ships ; i++)
1037 if (([ship status] != STATUS_DEAD) && ([ship status] != STATUS_DOCKED) && [ship isPirateVictim] && ![ship isCloaked])
1038 ids_found[n_found++] = ship;
1042 [shipAI message:@"NOTHING_FOUND"];
1047 [
self setFoundTarget:ids_found[i]];
1048 [shipAI message:@"TARGET_FOUND"];
1058 if (![
self hasCargoScoop])
1060 [shipAI message:@"NOTHING_FOUND"];
1063 if ([cargo
count] >= [
self maxAvailableCargoSpace])
1065 if (max_cargo) [shipAI message:@"HOLD_FULL"];
1066 [shipAI message:@"NOTHING_FOUND"];
1072 if (magnitude2([
self velocity]))
1074 [shipAI message:@"NOTHING_FOUND"];
1079 [
self checkScanner];
1081 double found_d2 = scannerRange * scannerRange;
1084 for (i = 0; i < n_scanned_ships; i++)
1087 if ([other scanClass] == CLASS_CARGO && [other cargoType] !=
CARGO_NOT_CARGO && [other status] != STATUS_BEING_SCOOPED)
1089 if ((![
self isPolice]) || ([[other commodityType] isEqualToString:
@"slaves"]))
1091 GLfloat d2 = distance2_scanned_ships[i];
1095 [
self setFoundTarget:other];
1100 [
self checkFoundTarget];
1104- (void) scanForRandomLoot
1107 if (![
self isStation] && ![
self hasCargoScoop])
1109 [shipAI message:@"NOTHING_FOUND"];
1113 [
self checkScanner];
1116 unsigned things_found = 0;
1119 for (i = 0; (i < n_scanned_ships)&&(things_found < 16) ; i++)
1122 if ([other scanClass] == CLASS_CARGO && [other cargoType] !=
CARGO_NOT_CARGO && [other status] != STATUS_BEING_SCOOPED)
1124 thing_uids_found[things_found++] = other;
1128 if (things_found != 0)
1130 [
self setFoundTarget:thing_uids_found[ranrot_rand() % things_found]];
1131 [shipAI message:@"TARGET_FOUND"];
1134 [shipAI message:@"NOTHING_FOUND"];
1138- (void) setTargetToFoundTarget
1140 if ([
self foundTarget] !=
nil)
1142 [
self addTarget:[
self foundTarget]];
1146 [shipAI message:@"TARGET_LOST"];
1151- (void) addFoundTargetAsDefenseTarget
1153 Entity* fTarget = [
self foundTarget];
1156 if ([fTarget isShip] && ![(
ShipEntity *)fTarget isFriendlyTo:
self])
1158 [
self addDefenseTarget:fTarget];
1163- (void) checkForFullHold
1167 [shipAI message:@"NO_CARGO_BAY"];
1169 else if ([cargo
count] >= [
self maxAvailableCargoSpace])
1171 [shipAI message:@"HOLD_FULL"];
1175 [shipAI message:@"HOLD_NOT_FULL"];
1183- (void) getWitchspaceEntryCoordinates
1188 Vector vr = vector_multiply_scalar(v_forward, maxFlightSpeed * 10.0);
1189 coordinates = HPvector_add(position, vectorToHPVector(vr));
1198 station = [UNIVERSE nearestShipMatchingPredicate:IsStationPredicate
1200 relativeToEntity:self];
1204 Vector vr = vector_multiply_scalar([station rightVector], 10000);
1205 coordinates = HPvector_add([station position], vectorToHPVector(vr));
1209 Vector vr = vector_multiply_scalar(v_forward, maxFlightSpeed * 10.0);
1210 coordinates = HPvector_add(position, vectorToHPVector(vr));
1215- (void) setDestinationFromCoordinates
1217 _destination = coordinates;
1221- (void) setCoordinatesFromPosition
1223 coordinates = position;
1227- (void) fightOrFleeMissile
1236 [
self checkScannerIgnoringUnpowered];
1237 for (i = 0; (i < n_scanned_ships)&&(missile ==
nil); i++)
1250 foreach (escort, [
self escortEnumerator])
1252 if (target == escort)
1261 if (missile ==
nil)
return;
1263 [
self addTarget:missile];
1264 [
self addDefenseTarget:missile];
1268 [
self doScriptEvent:OOJSID("shipBeingAttacked") withArgument:hunter];
1271 if ([
self isPolice])
1277 foreach (police, [[
self group] mutationSafeEnumerator])
1285 if ([
self isPolice] && ![hunter isPolice]) [hunter
markAsOffender:64
withReason:kOOLegalStatusReasonAttackedPolice];
1291 [
self setPrimaryAggressor:hunter];
1292 [
self setFoundTarget:hunter];
1299 desired_range = 10000;
1301 [shipAI message:@"FLEEING"];
1305- (void) setCourseToPlanet
1308 OOPlanetEntity *the_planet = [
self findNearestPlanetExcludingMoons];
1311 double variation = (aegis_status ==
AEGIS_NONE ? 0.5 : 0.2);
1312 HPVector p_pos = the_planet->
position;
1314 HPVector p1 = HPvector_between(p_pos, position);
1315 p1 = HPvector_normal(p1);
1316 p1.x += variation * (
randf() - variation);
1317 p1.y += variation * (
randf() - variation);
1318 p1.z += variation * (
randf() - variation);
1319 p1 = HPvector_normal(p1);
1320 _destination = HPvector_add(p_pos, HPvector_multiply_scalar(p1, p_cr));
1321 desired_range = collision_radius + 100.0;
1325 [shipAI message:@"NO_PLANET_FOUND"];
1330- (void) setTakeOffFromPlanet
1336 _destination = HPvector_add([the_planet position], HPvector_multiply_scalar(
1337 HPvector_normal(HPvector_subtract([the_planet position],position)),-10000.0-the_planet->
collision_radius));
1338 desired_range = 50.0;
1342 OOLog(
@"ai.setTakeOffFromPlanet.noPlanet",
@"%@",
@"***** Error. Planet not found during take off!");
1347- (void) landOnPlanet
1350 [
self landOnPlanet:[
self findNearestPlanet]];
1354- (void) checkTargetLegalStatus
1356 ShipEntity *other_ship = [
self primaryTarget];
1359 [shipAI message:@"NO_TARGET"];
1367 [shipAI message:@"TARGET_FUGITIVE"];
1372 [shipAI message:@"TARGET_OFFENDER"];
1377 [shipAI message:@"TARGET_MINOR_OFFENDER"];
1380 [shipAI message:@"TARGET_CLEAN"];
1385- (void) checkOwnLegalStatus
1387 if (scanClass == CLASS_THARGOID)
1389 [shipAI message:@"SELF_THARGOID"];
1392 int ls = [
self legalStatus];
1395 [shipAI message:@"SELF_FUGITIVE"];
1400 [shipAI message:@"SELF_OFFENDER"];
1405 [shipAI message:@"SELF_MINOR_OFFENDER"];
1408 [shipAI message:@"SELF_CLEAN"];
1412- (void) exitAIWithMessage:(NSString *)message
1414 if ([message length] == 0) message =
@"RESTARTED";
1415 [shipAI exitStateMachineWithMessage:message];
1419- (void) setDestinationToTarget
1421 Entity *the_target = [
self primaryTarget];
1423 _destination = the_target->
position;
1427- (void) setDestinationWithinTarget
1429 Entity *the_target = [
self primaryTarget];
1432 HPVector pos = the_target->
position;
1435 GLfloat d = (
randf() -
randf()) * the_target->collision_radius;
1436 _destination = make_HPvector(pos.x + d * v.x, pos.y + d * v.y, pos.z + d * v.z);
1441- (void) checkCourseToDestination
1443 Entity *hazard = [UNIVERSE hazardOnRouteFromEntity: self toDistance: desired_range fromPoint: _destination];
1445 if (hazard ==
nil || ([hazard isShip] && HPdistance(position, [hazard position]) > scannerRange) || ([hazard isPlanet] && aegis_status ==
AEGIS_NONE))
1446 [shipAI message:@"COURSE_OK"];
1449 if ([hazard isShip] && (weapon_damage * 24.0 > [hazard energy]))
1451 [shipAI reactToMessage:@"HAZARD_CAN_BE_DESTROYED" context:@"checkCourseToDestination"];
1454 _destination = [UNIVERSE getSafeVectorFromEntity:self toDistance:desired_range fromPoint:_destination];
1455 [shipAI message:@"WAYPOINT_SET"];
1462 switch (aegis_status)
1465 [shipAI message:@"AEGIS_CLOSE_TO_MAIN_PLANET"];
1475 [shipAI message:@"CLOSE_TO_SUN"];
1479 [shipAI message:@"CLOSE_TO_PLANET"];
1482 [shipAI message:@"CLOSE_TO_MOON"];
1486 [shipAI message:@"CLOSE_TO_SECONDARY_PLANET"];
1492 [shipAI message:@"AEGIS_IN_DOCKING_RANGE"];
1495 [shipAI message:@"AEGIS_NONE"];
1499 NSLog(
@"Aegis status for %@ has taken on invalid value %i. This is an internal error, please report it.",
self, aegis_status);
1501 [shipAI message:@"AEGIS_NONE"];
1507 if (energy == maxEnergy)
1509 [shipAI message:@"ENERGY_FULL"];
1512 if (energy >= maxEnergy * 0.75)
1514 [shipAI message:@"ENERGY_HIGH"];
1517 if (energy <= maxEnergy * 0.25)
1519 [shipAI message:@"ENERGY_LOW"];
1522 [shipAI message:@"ENERGY_MEDIUM"];
1525- (void) checkHeatInsulation
1527 float minInsulation = 1000 / [
self maxFlightSpeed] + 1;
1529 if ([
self heatInsulation] < minInsulation)
1531 [shipAI message:@"INSULATION_POOR"];
1534 [shipAI message:@"INSULATION_OK"];
1538- (void) findNewDefenseTarget
1540 [
self checkScanner];
1542 for (i = 0; i < n_scanned_ships ; i++)
1545 if (![ship isCloaked] && (([ship primaryTarget] ==
self && [ship hasHostileTarget]) || [ship isMine] || ([ship isThargoid] != [
self isThargoid])))
1547 if (![
self isDefenseTarget:ship])
1549 [
self addDefenseTarget:ship];
1557- (void) scanForOffenders
1560 NSDictionary *systeminfo = [UNIVERSE currentSystemData];
1561 float gov_factor = 0.4 * [(NSNumber *)[systeminfo objectForKey:KEY_GOVERNMENT] intValue];
1570 [
self checkScanner];
1572 float worst_legal_factor = 0;
1573 GLfloat found_d2 = scannerRange * scannerRange;
1575 for (i = 0; i < n_scanned_ships ; i++)
1578 if ((ship->
scanClass != CLASS_CARGO)&&([ship status] != STATUS_DEAD)&&([ship status] != STATUS_DOCKED)&& ![ship isCloaked])
1580 GLfloat d2 = distance2_scanned_ships[i];
1581 float legal_factor = [ship legalStatus] * gov_factor;
1582 int random_factor = ranrot_rand() & 255;
1583 if ((d2 < found_d2)&&(random_factor < legal_factor)&&(legal_factor > worst_legal_factor))
1585 if (group == nil || group != [ship group])
1587 [self setFoundTarget:ship];
1588 worst_legal_factor = legal_factor;
1594 [
self checkFoundTarget];
1598- (void) setCourseToWitchpoint
1602 _destination = [UNIVERSE getWitchspaceExitPosition];
1603 desired_range = 10000.0;
1608- (void) setDestinationToWitchpoint
1610 _destination = [UNIVERSE getWitchspaceExitPosition];
1614- (void) setDestinationToStationBeacon
1618 _destination = [[UNIVERSE station] beaconPosition];
1623- (void) performHyperSpaceExit
1625 [
self performHyperSpaceExitReplace:YES];
1629- (void) performHyperSpaceExitWithoutReplacing
1631 [
self performHyperSpaceExitReplace:NO];
1635- (void) disengageAutopilot
1637 OOLogERR(
@"ai.invalid.notPlayer",
@"Error in %@:%@, AI method endAutoPilot is only applicable to the player.", [shipAI name], [shipAI state]);
1641- (void) wormholeGroup
1646 whole = [
self primaryTarget];
1647 if (![whole isWormhole])
return;
1649 foreach (ship, [[
self group] mutationSafeEnumerator])
1658- (void) commsMessage:(NSString *)valueString
1660 [
self commsMessage:valueString withUnpilotedOverride:NO];
1664- (void) commsMessageByUnpiloted:(NSString *)valueString
1666 [
self commsMessage:valueString withUnpilotedOverride:YES];
1673 while (cargo_to_go > 15)
1678 for (i = 1; i < cargo_to_go; i++)
1680 [
self performSelector:@selector(dumpCargo) withObject:nil afterDelay:0.75 * i];
1685- (void) scanForThargoid
1687 return [
self scanForNearestShipWithPrimaryRole:@"thargoid"];
1691- (void) scanForNonThargoid
1696 [
self checkScanner];
1698 GLfloat found_d2 = scannerRange * scannerRange;
1699 for (i = 0; i < n_scanned_ships ; i++)
1702 GLfloat d2 = distance2_scanned_ships[i];
1703 if (([thing scanClass] != CLASS_CARGO) && ([thing status] != STATUS_DOCKED) && ![thing isThargoid] && ![thing isCloaked] && (d2 < found_d2))
1705 [
self setFoundTarget:thing];
1706 if ([thing isPlayer]) d2 = 0.0;
1711 [
self checkFoundTarget];
1715- (void) thargonCheckMother
1718 if (mother ==
nil && [
self group]) mother = [[
self group] leader];
1720 double maxRange2 = scannerRange * scannerRange;
1722 if (mother && mother !=
self && HPdistance2(mother->
position, position) < maxRange2)
1724 [shipAI message:
@"TARGET_FOUND"];
1729 [self scanForNearestShipHavingRole:
@"thargoid-mothership"];
1730 if ([self foundTarget] != nil)
1732 mother = (ShipEntity*)[self foundTarget];
1733 [self setOwner:mother];
1734 if ([mother group] != [mother escortGroup])
1736 [self setGroup:[mother group]];
1743- (void) becomeUncontrolledThargon
1745 int ent_count =
UNIVERSE->n_entities;
1748 for (i = 0; i < ent_count; i++) if (uni_entities[i]->isShip)
1751 if ([other primaryTarget] ==
self)
1755 if ([other isDefenseTarget:
self])
1761 scanClass = CLASS_CARGO;
1762 reportAIMessages = NO;
1763 [
self setAITo:@"dumbAI.plist"];
1765 [
self setSpeed: 0.0];
1766 [
self setGroup:nil];
1770- (void) checkDistanceTravelled
1772 if (distanceTravelled > desired_range)
1773 [shipAI message:@"GONE_BEYOND_RANGE"];
1777- (void) fightOrFleeHostiles
1779 [
self addDefenseTarget:[
self foundTarget]];
1781 if ([
self hasEscorts])
1783 Entity *leTarget = [
self lastEscortTarget];
1784 if (leTarget !=
nil)
1786 [
self setFoundTarget:leTarget];
1787 [shipAI message:@"FLEEING"];
1791 [
self setPrimaryAggressor:[
self foundTarget]];
1792 [
self addTarget:[
self foundTarget]];
1793 [
self deployEscorts];
1794 [shipAI message:@"DEPLOYING_ESCORTS"];
1795 [shipAI message:@"FLEEING"];
1804 [
self setPrimaryAggressor:[
self foundTarget]];
1805 [
self addTarget:[
self foundTarget]];
1807 [shipAI message:@"FLEEING"];
1813 if (energy > maxEnergy * 0.80)
1815 [
self setPrimaryAggressor:[
self foundTarget]];
1817 [shipAI message:@"FIGHTING"];
1821 [shipAI message:@"FLEEING"];
1825- (void) suggestEscort
1828 [
self suggestEscortTo:mother];
1833- (void) escortCheckMother
1837 if ([mother acceptAsEscort:
self])
1839 [
self setOwner:mother];
1841 [shipAI message:@"ESCORTING"];
1845 [
self setOwner:self];
1846 if ([
self group] == [mother escortGroup]) [
self setGroup:nil];
1847 [shipAI message:@"NOT_ESCORTING"];
1852- (void) checkGroupOddsVersusTarget
1854 NSUInteger ownGroupCount = [[
self group] count] + (
ranrot_rand() & 3);
1855 NSUInteger targetGroupCount = [[[
self primaryTarget] group] count] + (
ranrot_rand() & 3);
1857 if (ownGroupCount == targetGroupCount)
1859 [shipAI message:@"ODDS_LEVEL"];
1861 else if (ownGroupCount > targetGroupCount)
1863 [shipAI message:@"ODDS_GOOD"];
1867 [shipAI message:@"ODDS_BAD"];
1874- (void) scanForFormationLeader
1878 [
self checkScannerIgnoringUnpowered];
1880 GLfloat found_d2 = scannerRange * scannerRange;
1881 for (i = 0; i < n_scanned_ships; i++)
1884 if ((ship !=
self) && (!ship->
isPlayer) && (ship->
scanClass == scanClass) && [ship primaryTarget] !=
self && ![ship isCloaked])
1886 GLfloat d2 = distance2_scanned_ships[i];
1887 if ((d2 < found_d2) && [ship canAcceptEscort:self])
1890 [self setFoundTarget:ship];
1895 if ([
self foundTarget] !=
nil) [shipAI message:
@"TARGET_FOUND"];
1898 [shipAI message:@"NOTHING_FOUND"];
1899 if ([
self hasPrimaryRole:
@"wingman"])
1902 [
self setAITo:@"route1patrolAI.plist"];
1903 [
self setPrimaryRole:@"police"];
1910- (void) messageMother:(NSString *)msgString
1913 if (mother !=
nil && mother !=
self)
1915 NSString *context =
nil;
1917 context = [NSString stringWithFormat:@"%@ messageMother", [
self shortDescription]];
1924- (void) messageSelf:(NSString *)msgString
1926 [
self sendAIMessage:msgString];
1930- (void) setPlanetPatrolCoordinates
1933 HPVector r_pos = HPvector_subtract(position, coordinates);
1934 if (HPmagnitude2(r_pos) < 1000000 || patrol_counter == 0)
1936 Entity *the_sun = [UNIVERSE sun];
1937 ShipEntity *the_station = [[
self group] leader];
1938 if(!the_station || ![the_station isStation]) the_station = [UNIVERSE station];
1939 if ((!the_sun)||(!the_station))
1941 HPVector sun_pos = the_sun->
position;
1942 HPVector stn_pos = the_station->
position;
1943 HPVector sun_dir = HPvector_subtract(sun_pos,stn_pos);
1944 Vector vSun = make_vector(0, 0, 1);
1945 if (sun_dir.x||sun_dir.y||sun_dir.z)
1946 vSun = HPVectorToVector(HPvector_normal(sun_dir));
1948 Vector v1 = cross_product(v0, vSun);
1949 Vector v2 = cross_product(v0, v1);
1950 switch (patrol_counter)
1953 coordinates = make_HPvector(stn_pos.x + 5000 * v0.x, stn_pos.y + 5000 * v0.y, stn_pos.z + 5000 * v0.z);
1954 desired_range = 250.0;
1957 coordinates = make_HPvector(stn_pos.x + 25000 * v1.x, stn_pos.y + 25000 * v1.y, stn_pos.z + 25000 * v1.z);
1958 desired_range = 250.0;
1961 coordinates = make_HPvector(stn_pos.x + 25000 * v2.x, stn_pos.y + 25000 * v2.y, stn_pos.z + 25000 * v2.z);
1962 desired_range = 250.0;
1965 coordinates = make_HPvector(stn_pos.x - 25000 * v1.x, stn_pos.y - 25000 * v1.y, stn_pos.z - 25000 * v1.z);
1966 desired_range = 250.0;
1969 coordinates = make_HPvector(stn_pos.x - 25000 * v2.x, stn_pos.y - 25000 * v2.y, stn_pos.z - 25000 * v2.z);
1970 desired_range = 250.0;
1973 coordinates = make_HPvector(stn_pos.x + 5000 * v0.x, stn_pos.y + 5000 * v0.y, stn_pos.z + 5000 * v0.z);
1974 desired_range = 250.0;
1978 if (patrol_counter > 4)
1983 [
self setTargetStation:the_station];
1984 [
self setAITo:@"dockingAI.plist"];
1994 [shipAI message:@"APPROACH_COORDINATES"];
1998- (void) setSunSkimStartCoordinates
2002 [shipAI message:@"NO_SUN_FOUND"];
2006 HPVector v0 = [UNIVERSE getSunSkimStartPositionForShip:self];
2011 [shipAI message:@"APPROACH_COORDINATES"];
2015 [shipAI message:@"WAIT_FOR_SUN"];
2020- (void) setSunSkimEndCoordinates
2024 [shipAI message:@"NO_SUN_FOUND"];
2028 coordinates = [UNIVERSE getSunSkimEndPositionForShip:self];
2029 [shipAI message:@"APPROACH_COORDINATES"];
2033- (void) setSunSkimExitCoordinates
2035 Entity *the_sun = [UNIVERSE sun];
2036 if (the_sun ==
nil)
return;
2037 HPVector v1 = [UNIVERSE getSunSkimEndPositionForShip:self];
2039 HPVector vout = HPvector_subtract(v1,vs);
2040 if (vout.x||vout.y||vout.z)
2041 vout = HPvector_normal(vout);
2044 v1.x += 10000 * vout.
x; v1.y += 10000 * vout.
y; v1.z += 10000 * vout.z;
2046 [shipAI message:@"APPROACH_COORDINATES"];
2050- (void) patrolReportIn
2053 ShipEntity *the_station = [[
self group] leader];
2054 if(!the_station || ![the_station isStation]) the_station = [UNIVERSE station];
2059- (void) checkForMotherStation
2061 ShipEntity *motherStation = [[
self group] leader];
2062 if ((!motherStation) || (!(motherStation->
isStation)))
2064 [shipAI message:@"NOTHING_FOUND"];
2067 double found_d2 = scannerRange * scannerRange;
2068 HPVector v0 = motherStation->
position;
2069 if (HPdistance2(v0,position) > found_d2)
2071 [shipAI message:@"NOTHING_FOUND"];
2074 [shipAI message:@"STATION_FOUND"];
2078- (void) sendTargetCommsMessage:(NSString*) message
2081 if ((ship ==
nil) || ([ship status] == STATUS_DEAD) || ([ship status] == STATUS_DOCKED))
2083 [
self noteLostTarget];
2086 [
self sendExpandedMessage:message toShip:[
self primaryTarget]];
2090- (void) markTargetForFines
2093 if ((ship ==
nil) || ([ship status] == STATUS_DEAD) || ([ship status] == STATUS_DOCKED))
2095 [
self noteLostTarget];
2098 if ([ship markForFines]) [shipAI message:@"TARGET_MARKED"];
2102- (void) markTargetForOffence:(NSString *)valueString
2104 if ((isStation)||(scanClass == CLASS_POLICE))
2107 if ((ship ==
nil) || ([ship status] == STATUS_DEAD) || ([ship status] == STATUS_DOCKED))
2109 [
self noteLostTarget];
2112 NSString *finalValue =
OOExpand(valueString);
2120 Entity *target = [
self primaryTarget];
2124 [
self setRememberedShip:target];
2133- (void) recallStoredTarget
2138 if (oldTarget && ![oldTarget isCloaked])
2140 GLfloat range2 = HPdistance2([oldTarget position], position);
2149 [
self setFoundTarget:oldTarget];
2150 [shipAI message:@"TARGET_FOUND"];
2154 if (oldTarget ==
nil)
DESTROY(_rememberedShip);
2155 [shipAI message:@"NOTHING_FOUND"];
2160- (void) scanForRocks
2167 [
self checkScanner];
2169 GLfloat found_d2 = scannerRange * scannerRange;
2170 for (i = 0; i < n_scanned_ships; i++)
2173 if ([thing isBoulder])
2175 GLfloat d2 = distance2_scanned_ships[i];
2178 [
self setFoundTarget:thing];
2183 if ([
self foundTarget] ==
nil)
2185 for (i = 0; i < n_scanned_ships; i++)
2188 if ([thing hasRole:
@"asteroid"])
2190 GLfloat d2 = distance2_scanned_ships[i];
2193 [
self setFoundTarget:thing];
2200 [
self checkFoundTarget];
2204- (void) setDestinationToDockingAbort
2206 Entity *the_target = [
self targetStation];
2210 the_target = [UNIVERSE station];
2212 double bo_distance = 8000;
2213 HPVector v0 = position;
2215 v0.x += (
randf() - 0.5)*collision_radius; v0.y += (
randf() - 0.5)*collision_radius; v0.z += (
randf() - 0.5)*collision_radius;
2216 v0.x -= d0.
x; v0.y -= d0.
y; v0.z -= d0.z;
2217 v0 = HPvector_normal_or_fallback(v0, make_HPvector(0, 0, -1));
2219 v0.x *= bo_distance; v0.y *= bo_distance; v0.z *= bo_distance;
2220 v0.x += d0.
x; v0.y += d0.
y; v0.z += d0.z;
2226- (void) requestNewTarget
2231 [shipAI message:@"MOTHER_LOST"];
2237 [
self checkScanner];
2239 GLfloat found_d2 = scannerRange * scannerRange;
2241 for (i = 0; i < n_scanned_ships ; i++)
2244 GLfloat d2 = distance2_scanned_ships[i];
2245 GLfloat e1 = [thing
energy];
2246 if ((d2 < found_d2) && ![thing isCloaked] && (([thing isThargoid] && ![mother isThargoid]) || (([thing primaryTarget] == mother) && [thing hasHostileTarget])))
2250 [
self setFoundTarget:thing];
2256 [
self checkFoundTarget];
2260- (void) rollD:(NSString *)die_number
2262 int die_sides = [die_number intValue];
2266 NSString* result = [NSString stringWithFormat:@"ROLL_%d", die_roll];
2267 [shipAI reactToMessage:result context:@"rollD:"];
2271 OOLog(
@"ai.rollD.invalidValue",
@"***** ERROR: invalid value supplied to rollD: '%@'.", die_number);
2276- (void) scanForNearestShipWithPrimaryRole:(NSString *)scanRole
2278 [
self scanForNearestShipWithPredicate:HasPrimaryRolePredicate parameter:scanRole];
2282- (void) scanForNearestShipHavingRole:(NSString *)scanRole
2284 [
self scanForNearestShipWithPredicate:HasRolePredicate parameter:scanRole];
2288- (void) scanForNearestShipWithAnyPrimaryRole:(NSString *)scanRoles
2290 NSSet *
set = [NSSet setWithArray:ScanTokensFromString(scanRoles)];
2291 [
self scanForNearestShipWithPredicate:HasPrimaryRoleInSetPredicate parameter:set];
2295- (void) scanForNearestShipHavingAnyRole:(NSString *)scanRoles
2297 NSSet *
set = [NSSet setWithArray:ScanTokensFromString(scanRoles)];
2298 [
self scanForNearestShipWithPredicate:HasRoleInSetPredicate parameter:set];
2302- (void) scanForNearestShipWithScanClass:(NSString *)scanScanClass
2304 NSNumber *parameter = [NSNumber numberWithInt:OOScanClassFromString(scanScanClass)];
2305 [
self scanForNearestShipWithPredicate:HasScanClassPredicate parameter:parameter];
2309- (void) scanForNearestShipWithoutPrimaryRole:(NSString *)scanRole
2311 [
self scanForNearestShipWithNegatedPredicate:HasPrimaryRolePredicate parameter:scanRole];
2315- (void) scanForNearestShipNotHavingRole:(NSString *)scanRole
2317 [
self scanForNearestShipWithNegatedPredicate:HasRolePredicate parameter:scanRole];
2321- (void) scanForNearestShipWithoutAnyPrimaryRole:(NSString *)scanRoles
2323 NSSet *
set = [NSSet setWithArray:ScanTokensFromString(scanRoles)];
2324 [
self scanForNearestShipWithNegatedPredicate:HasPrimaryRoleInSetPredicate parameter:set];
2328- (void) scanForNearestShipNotHavingAnyRole:(NSString *)scanRoles
2330 NSSet *
set = [NSSet setWithArray:ScanTokensFromString(scanRoles)];
2331 [
self scanForNearestShipWithNegatedPredicate:HasRoleInSetPredicate parameter:set];
2335- (void) scanForNearestShipWithoutScanClass:(NSString *)scanScanClass
2337 NSNumber *parameter = [NSNumber numberWithInt:OOScanClassFromString(scanScanClass)];
2338 [
self scanForNearestShipWithNegatedPredicate:HasScanClassPredicate parameter:parameter];
2342- (void) scanForNearestShipMatchingPredicate:(NSString *)predicateExpression
2359 static NSMutableDictionary *scriptCache =
nil;
2360 NSString *aiName =
nil;
2361 NSString *key =
nil;
2363 JSContext *context = NULL;
2367 if (predicateExpression ==
nil) predicateExpression =
@"false";
2369 aiName = [[
self getAI] name];
2376 key = [NSString stringWithFormat:@"%@\n%@", aiName, predicateExpression];
2378 key = predicateExpression;
2382 function = [scriptCache objectForKey:key];
2383 if (
function ==
nil)
2385 NSString *predicateCode =
nil;
2386 const char *argNames[] = {
"ship" };
2389 predicateCode = [NSString stringWithFormat:@"return %@;", predicateExpression];
2390 function = [[
OOJSFunction alloc] initWithName:@"_oo_AIScanPredicate"
2394 argumentNames:argNames
2398 [function autorelease];
2401 if (
function !=
nil)
2403 if (scriptCache ==
nil) scriptCache = [[NSMutableDictionary alloc] init];
2404 [scriptCache setObject:function forKey:key];
2408 if (
function !=
nil)
2413 .function = [function functionValue],
2416 [
self scanForNearestShipWithPredicate:JSFunctionPredicate parameter:¶m];
2421 static NSMutableSet *errorCache =
nil;
2423 if (![errorCache containsObject:key])
2425 OOLog(
@"ai.scanForNearestShipMatchingPredicate.compile.failed",
@"Could not compile JavaScript predicate \"%@\
" for AI %@.", predicateExpression, [[
self getAI] name]);
2426 if (errorCache ==
nil) errorCache = [[NSMutableSet alloc] init];
2427 [errorCache addObject:key];
2432 [[
self getAI] message:@"NOTHING_FOUND"];
2435 JS_ReportPendingException(context);
2440- (void) setCoordinates:(NSString *)system_x_y_z
2443 NSString* systemString =
nil;
2444 NSString* xString =
nil;
2445 NSString* yString =
nil;
2446 NSString* zString =
nil;
2448 if ([tokens
count] != 4)
2450 OOLog(
@"ai.syntax.setCoordinates",
@"***** ERROR: cannot setCoordinates: '%@'.",system_x_y_z);
2454 systemString = (NSString *)[tokens objectAtIndex:0];
2455 xString = (NSString *)[tokens objectAtIndex:1];
2456 if ([xString hasPrefix:
@"rand:"])
2457 xString = [NSString stringWithFormat:
@"%.3f",
bellf([(NSString*)[[xString componentsSeparatedByString:
@":"] objectAtIndex:1] intValue])];
2458 yString = (NSString *)[tokens objectAtIndex:2];
2459 if ([yString hasPrefix:
@"rand:"])
2460 yString = [NSString stringWithFormat:
@"%.3f",
bellf([(NSString*)[[yString componentsSeparatedByString:
@":"] objectAtIndex:1] intValue])];
2461 zString = (NSString *)[tokens objectAtIndex:3];
2462 if ([zString hasPrefix:
@"rand:"])
2463 zString = [NSString stringWithFormat:
@"%.3f",
bellf([(NSString*)[[zString componentsSeparatedByString:
@":"] objectAtIndex:1] intValue])];
2465 HPVector posn = make_HPvector([xString floatValue], [yString floatValue], [zString floatValue]);
2466 GLfloat scalar = 1.0;
2468 coordinates = [UNIVERSE coordinatesForPosition:posn withCoordinateSystem:systemString returningScalar:&scalar];
2470 [shipAI message:@"APPROACH_COORDINATES"];
2474- (void) checkForNormalSpace
2477 [shipAI message:@"NORMAL_SPACE"];
2479 [shipAI message:@"INTERSTELLAR_SPACE"];
2483- (void) setTargetToRandomStation
2486 int ent_count =
UNIVERSE->n_entities;
2488 Entity *my_entities[ent_count];
2490 double maxRange2 = desired_range * desired_range;
2492 int station_count = 0;
2494 for (i = 0; i < ent_count; i++)
2497 if (uni_entities[i]->isStation)
2500 if ([my_station maxFlightSpeed] == 0 && [my_station hasNPCTraffic] && HPdistance2(position, [my_station position]) < maxRange2)
2502 my_entities[station_count++] = [uni_entities[i] retain];
2507 if (station_count != 0)
2512 if (station == [
UNIVERSE station] && station_count > 1)
2514 while (station == [
UNIVERSE station])
2521 for (i = 0; i < station_count; i++)
2522 [my_entities[i] release];
2526 [
self addTarget:station];
2527 [
self setTargetStation:station];
2528 [shipAI message:@"STATION_FOUND"];
2532 [shipAI message:@"NO_STATION_IN_RANGE"];
2536- (void) setTargetToLastStation
2538 Entity *station = [
self targetStation];
2540 if (station !=
nil && [station isStation])
2542 [
self addTarget:station];
2546 [shipAI message:@"NO_STATION_FOUND"];
2547 [
self setTargetStation:nil];
2553- (void) addFuel:(NSString*) fuel_number
2555 [
self setFuel:[
self fuel] + [fuel_number intValue] * 10];
2560- (void) scriptActionOnTarget:(NSString *)action
2567 static BOOL deprecationWarning = NO;
2569 if (!deprecationWarning)
2571 deprecationWarning = YES;
2572 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]);
2576 OOLog(
@"script.deprecated.scriptActionOnTarget.repeat",
@"----- WARNING in AI %@: the AI method scriptActionOnTarget: is deprecated and should not be used.", [
AI currentlyRunningAIDescription]);
2580 if ([targEnt isShip])
2586 withContextName:[NSString stringWithFormat:@"<AI \"%@\" state %@ - scriptActionOnTarget:>", [[
self getAI] name], [[
self getAI] state]]
2594- (void) safeScriptActionOnTarget:(NSString *)action
2600 if ([targEnt isShip])
2606 withContextName:[NSString stringWithFormat:@"<AI \"%@\" state %@ - safeScriptActionOnTarget:>", [[
self getAI] name], [[
self getAI] state]]
2614- (void) sendScriptMessage:(NSString *)message
2618 if ([components
count] == 1)
2620 [
self doScriptEvent:OOJSIDFromString(message)];
2624 NSString *
function = [components objectAtIndex:0];
2625 components = [components subarrayWithRange:NSMakeRange(1, [components count] - 1)];
2626 [
self doScriptEvent:OOJSIDFromString(function) withArgument:components];
2631- (void) ai_throwSparks
2633 [
self setThrowSparks:YES];
2639 [
self getDestroyedBy:nil damageType:kOODamageTypeEnergy];
2643- (void) ai_debugMessage:(NSString *)message
2645 NSString *desc = [NSString stringWithFormat:@"%@ %d", [
self name], [
self universalID]];
2646 if ([
self isPlayer]) desc =
@"player autopilot";
2647 OOLog(
@"ai.takeAction.debugMessage",
@"DEBUG: AI MESSAGE from %@: %@", desc, message);
2653- (void) targetFirstBeaconWithCode:(NSString*) code
2655 NSArray *all_beacons = [UNIVERSE listBeaconsWithCode: code];
2656 if ([all_beacons
count])
2658 [
self addTarget:(ShipEntity*)[all_beacons objectAtIndex:0]];
2659 [shipAI message:@"TARGET_FOUND"];
2662 [shipAI message:@"NOTHING_FOUND"];
2666- (void) targetNextBeaconWithCode:(NSString*) code
2668 NSArray *all_beacons = [UNIVERSE listBeaconsWithCode: code];
2669 ShipEntity *current_beacon = [
self primaryTarget];
2671 if ((!current_beacon)||(![current_beacon isBeacon]))
2673 [shipAI message:@"NO_CURRENT_BEACON"];
2674 [shipAI message:@"NOTHING_FOUND"];
2679 NSUInteger i = [all_beacons indexOfObject:current_beacon];
2681 if (i == NSNotFound)
2683 [shipAI message:@"NOTHING_FOUND"];
2689 if (i < [all_beacons
count])
2692 [
self addTarget:(ShipEntity*)[all_beacons objectAtIndex:i]];
2693 [shipAI message:@"TARGET_FOUND"];
2697 [shipAI message:@"LAST_BEACON"];
2698 [shipAI message:@"NOTHING_FOUND"];
2703- (void) setRacepointsFromTarget
2709 [shipAI message:@"NOTHING_FOUND"];
2715 navpoints[0] = make_HPvector(o.x - c * k.x, o.y - c * k.y, o.z - c * k.z);
2716 navpoints[1] = make_HPvector(o.x + c * k.x, o.y + c * k.y, o.z + c * k.z);
2717 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);
2718 number_of_navpoints = 2;
2719 next_navpoint_index = 0;
2720 _destination = navpoints[0];
2721 [shipAI message:@"RACEPOINTS_SET"];
2725- (void) performFlyRacepoints
2727 next_navpoint_index = 0;
2728 desired_range = collision_radius;
2729 behaviour = BEHAVIOUR_FLY_THRU_NAVPOINTS;
2735@implementation ShipEntity (OOAIPrivate)
2738- (void) checkFoundTarget
2740 if ([
self foundTarget] !=
nil)
2742 [shipAI message:@"TARGET_FOUND"];
2746 [shipAI message:@"NOTHING_FOUND"];
2751- (BOOL) performHyperSpaceExitReplace:(BOOL)replace
2753 return [
self performHyperSpaceExitReplace:replace toSystem:-1];
2757- (BOOL) performHyperSpaceExitReplace:(BOOL)replace toSystem:(
OOSystemID)systemID
2759 if(![
self hasHyperspaceMotor])
2761 [shipAI reactToMessage:@"WITCHSPACE UNAVAILABLE" context:@"performHyperSpaceExit"];
2764 if([
self status] == STATUS_ENTERING_WITCHSPACE)
2770 NSArray *sDests =
nil;
2775 sDests = [UNIVERSE nearbyDestinationsWithinRange: 0.1f * fuel];
2776 NSUInteger n_dests = [sDests count];
2781 [shipAI reactToMessage:@"WITCHSPACE UNAVAILABLE" context:@"performHyperSpaceExit"];
2789 ShipEntity *blocker = [UNIVERSE entityForUniversalID:[
self checkShipsInVicinityForWitchJumpExit]];
2792 [
self setFoundTarget:blocker];
2793 [shipAI reactToMessage:@"WITCHSPACE BLOCKED" context:@"performHyperSpaceExit"];
2794 [
self doScriptEvent:OOJSID("shipWitchspaceBlocked") withArgument:blocker];
2808 targetSystem = [[sDests oo_dictionaryAtIndex:i] oo_intForKey:@"sysID"];
2812 targetSystem = systemID;
2814 for (i = 0; i < n_dests; i++)
2816 if (systemID == [[sDests oo_dictionaryAtIndex:i] oo_intForKey:
@"sysID"]) break;
2824 float dist = [[sDests oo_dictionaryAtIndex:i] oo_floatForKey:@"distance"];
2825 if (dist > [
self maxHyperspaceDistance] || dist > fuel/10.0f)
2827 OOLogWARN(
@"script.debug",
@"DEBUG: %@ Jumping %f which is further than allowed. I have %d fuel",
self, dist, fuel);
2833 [UNIVERSE addEntity: whole];
2835 [
self enterWormhole:whole replacing:replace];
2842- (void) scanForNearestShipWithPredicate:(
EntityFilterPredicate)predicate parameter:(
void *)parameter
2847 float d2, found_d2 = scannerRange * scannerRange;
2850 [
self checkScanner];
2852 if (predicate == NULL)
return;
2854 for (i = 0; i < n_scanned_ships ; i++)
2856 candidate = scanned_ships[i];
2857 d2 = distance2_scanned_ships[i];
2858 if ((d2 < found_d2) && (candidate->
scanClass != CLASS_CARGO) && ([candidate status] != STATUS_DOCKED)
2859 && predicate(candidate, parameter) && ![candidate isCloaked])
2861 [
self setFoundTarget:candidate];
2866 [
self checkFoundTarget];
2870- (void) scanForNearestShipWithNegatedPredicate:(
EntityFilterPredicate)predicate parameter:(
void *)parameter
2873 [
self scanForNearestShipWithPredicate:NOTPredicate parameter:¶m];
2877- (void) acceptDistressMessageFrom:(
ShipEntity *)other
2880 if ([
self isPolice])
2882 [(
ShipEntity*)[
self foundTarget] markAsOffender:8 withReason:kOOLegalStatusReasonDistressCall];
2885 NSString *context =
nil;
2887 context = [NSString stringWithFormat:@"%@ broadcastDistressMessage", [other shortDescription]];
2889 [shipAI reactToMessage:@"ACCEPT_DISTRESS_CALL" context:context];