Oolite
Loading...
Searching...
No Matches
ShipEntity(AI) Category Reference

#include <ShipEntityAI.h>

Instance Methods

(void) - setAITo:
 
(void) - setAIScript:
 
(void) - switchAITo:
 
(void) - scanForHostiles
 
(BOOL) - performHyperSpaceToSpecificSystem:
 
(void) - scanForNearestIncomingMissile
 
(void) - enterTargetWormhole
 
(void) - enterPlayerWormhole
 
(void) - wormholeEscorts
 
(void) - wormholeEntireGroup
 
(BOOL) - suggestEscortTo:
 
(void) - groupAttackTarget
 
(void) - performAttack
 
(void) - performCollect
 
(void) - performEscort
 
(void) - performFaceDestination
 
(void) - performFlee
 
(void) - performFlyToRangeFromDestination
 
(void) - performHold
 
(void) - performIdle
 
(void) - performIntercept
 
(void) - performLandOnPlanet
 
(void) - performMining
 
(void) - performScriptedAI
 
(void) - performScriptedAttackAI
 
(void) - performStop
 
(void) - performTumble
 
(void) - broadcastDistressMessage
 
(void) - broadcastDistressMessageWithDumping:
 
(void) - requestDockingCoordinates
 
(void) - recallDockingInstructions
 
(void) - performBuoyTumble [implementation]
 

Detailed Description

Definition at line 32 of file ShipEntityAI.h.

Method Documentation

◆ broadcastDistressMessage

- (void) broadcastDistressMessage

Definition at line 1 of file ShipEntityAI.m.

761{
762 /*-- Locates all the stations, bounty hunters and police ships in range and tells them that you are under attack --*/
763 [self broadcastDistressMessageWithDumping:YES];
764}

◆ broadcastDistressMessageWithDumping:

- (void) broadcastDistressMessageWithDumping: (BOOL)  dumpCargo

Definition at line 1 of file ShipEntityAI.m.

766 :(BOOL)dumpCargo
767{
768 [self checkScannerIgnoringUnpowered];
769 DESTROY(_foundTarget);
770
771 ShipEntity *aggressor_ship = (ShipEntity*)[self primaryAggressor];
772 if (aggressor_ship == nil) return;
773
774 // don't send too many distress messages at once, space them out semi-randomly
775 if (messageTime > 2.0 * randf()) return;
776
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]";
781
782 unsigned i;
783 for (i = 0; i < n_scanned_ships; i++)
784 {
785 ShipEntity* ship = scanned_ships[i];
786
787 // dump cargo if energy is low
788 if (dumpCargo && !is_buoy && [self primaryAggressor] == ship && energy < 0.375 * maxEnergy)
789 {
790 [self ejectCargo];
791 [self performFlee];
792 }
793
794 // tell it! (only plist AIs send comms here; JS AIs are
795 // expected to handle their own)
796 if (ship->isPlayer && ![self hasNewAI])
797 {
798 [ship doScriptEvent:OOJSID("distressMessageReceived") withArgument:aggressor_ship andArgument:self];
799
800 if (!is_buoy && [self primaryAggressor] == ship && energy < 0.375 * maxEnergy)
801 {
802 [self sendExpandedMessage:@"[beg-for-mercy]" toShip:ship];
803 }
804 else if ([self bounty] == 0)
805 {
806 // only send distress message to player if plausibly sending
807 // one more generally
808 [self sendExpandedMessage:distress_message toShip:ship];
809 }
810
811 // reset the thanked_ship_id
812 DESTROY(_thankedShip);
813 }
814 else if ([self bounty] == 0 && [ship crew]) // Only clean ships can have their distress calls accepted
815 {
816 [ship doScriptEvent:OOJSID("distressMessageReceived") withArgument:aggressor_ship andArgument:self];
817
818 // we only can send distressMessages to ships that are known to have a "ACCEPT_DISTRESS_CALL" reaction
819 // in their AI, or they might react wrong on the added found_target.
820
821 // ship must have a plist AI for this next bit. JS AIs
822 // should already have done something sensible on
823 // distressMessageReceived
824 if (![self hasNewAI])
825 {
826 // FIXME: this test only works with core AIs
827 if (ship->isStation || [ship hasPrimaryRole:@"police"] || [ship hasPrimaryRole:@"hunter"])
828 {
829 [ship acceptDistressMessageFrom:self];
830 }
831 }
832 }
833 }
834}
#define DESTROY(x)
Definition OOCocoa.h:75
return nil
unsigned isStation
Definition Entity.h:92
unsigned isPlayer
Definition Entity.h:93
void acceptDistressMessageFrom:(ShipEntity *other)
void doScriptEvent:withArgument:andArgument:(jsid message,[withArgument] id argument1,[andArgument] id argument2)
float randf(void)

◆ enterPlayerWormhole

- (void) enterPlayerWormhole

Definition at line 1 of file ShipEntityAI.m.

633{
634 [self enterWormhole:[PLAYER wormhole] replacing:NO];
635}

◆ enterTargetWormhole

- (void) enterTargetWormhole

Definition at line 1 of file ShipEntityAI.m.

638{
639 WormholeEntity *whole = nil;
640 ShipEntity *targEnt = [self primaryTarget];
641 double found_d2 = scannerRange * scannerRange;
642
643 if (targEnt && (HPdistance2(position, [targEnt position]) < found_d2))
644 {
645 if ([targEnt isWormhole])
646 whole = (WormholeEntity *)targEnt;
647 else if ([targEnt isPlayer])
648 whole = [PLAYER wormhole];
649 }
650
651 if (!whole)
652 {
653 // locate nearest wormhole
654 int ent_count = UNIVERSE->n_entities;
655 Entity** uni_entities = UNIVERSE->sortedEntities; // grab the public sorted list
656 WormholeEntity* wormholes[ent_count];
657 int i;
658 int wh_count = 0;
659 for (i = 0; i < ent_count; i++)
660 if (uni_entities[i]->isWormhole)
661 wormholes[wh_count++] = [(WormholeEntity *)uni_entities[i] retain];
662 //
663 //double found_d2 = scannerRange * scannerRange;
664 for (i = 0; i < wh_count ; i++)
665 {
666 WormholeEntity *wh = wormholes[i];
667 double d2 = HPdistance2(position, wh->position);
668 if (d2 < found_d2)
669 {
670 whole = wh;
671 found_d2 = d2;
672 }
673 [wh release];
674 }
675 }
676
677 [self enterWormhole:whole replacing:NO];
678}
#define UNIVERSE
Definition Universe.h:842
HPVector position
Definition Entity.h:112

◆ groupAttackTarget

- (void) groupAttackTarget

Definition at line 1 of file ShipEntityAI.m.

340{
341 ShipEntity *target = nil, *ship = nil;
342
343 target = [self primaryTarget];
344
345 if (target == nil) return;
346
347 if ([self group] == nil) // ship is alone!
348 {
349 [self setFoundTarget:target];
350 [shipAI reactToMessage:@"GROUP_ATTACK_TARGET" context:@"groupAttackTarget"];
351 [self doScriptEvent:OOJSID("helpRequestReceived") withArgument:self andArgument:target];
352 return;
353 }
354
355 foreach (ship, [[self group] mutationSafeEnumerator])
356 {
357 [ship setFoundTarget:target];
358 [ship reactToAIMessage:@"GROUP_ATTACK_TARGET" context:@"groupAttackTarget"];
359 [ship doScriptEvent:OOJSID("helpRequestReceived") withArgument:self andArgument:target];
360
361 if ([ship escortGroup] != [ship group] && [[ship escortGroup] count] > 1) // Ship has a seperate escort group.
362 {
363 ShipEntity *escort = nil;
364 NSArray *escortMembers = [[ship escortGroup] memberArrayExcludingLeader];
365 foreach (escort, escortMembers)
366 {
367 [escort setFoundTarget:target];
368 [escort reactToAIMessage:@"GROUP_ATTACK_TARGET" context:@"groupAttackTarget"];
369 [escort doScriptEvent:OOJSID("helpRequestReceived") withArgument:self andArgument:target];
370 }
371 }
372 }
373}
unsigned count
void reactToAIMessage:context:(NSString *message,[context] NSString *debugContext)
void setFoundTarget:(Entity *targetEntity)

◆ performAttack

- (void) performAttack

Definition at line 1 of file ShipEntityAI.m.

377{
378 if (behaviour != BEHAVIOUR_EVASIVE_ACTION)
379 {
380 behaviour = BEHAVIOUR_ATTACK_TARGET;
381 desired_range = 1250 * randf() + 750; // 750 til 2000
382 frustration = 0.0;
383 }
384}

◆ performBuoyTumble

- (void) performBuoyTumble
implementation

Definition at line 1 of file ShipEntityAI.m.

509{
510 stick_roll = 0.10;
511 stick_pitch = 0.15;
512 behaviour = BEHAVIOUR_TUMBLE;
513 frustration = 0.0;
514}

◆ performCollect

- (void) performCollect

Definition at line 1 of file ShipEntityAI.m.

388{
389 behaviour = BEHAVIOUR_COLLECT_TARGET;
390 frustration = 0.0;
391}

◆ performEscort

- (void) performEscort

Definition at line 1 of file ShipEntityAI.m.

395{
396 if(behaviour != BEHAVIOUR_FORMATION_FORM_UP)
397 {
398 behaviour = BEHAVIOUR_FORMATION_FORM_UP;
399 frustration = 0.0; // behavior changed, reset frustration.
400 }
401}

◆ performFaceDestination

- (void) performFaceDestination

Definition at line 1 of file ShipEntityAI.m.

405{
406 behaviour = BEHAVIOUR_FACE_DESTINATION;
407 frustration = 0.0;
408}

◆ performFlee

- (void) performFlee

Definition at line 1 of file ShipEntityAI.m.

412{
413 if (behaviour != BEHAVIOUR_FLEE_EVASIVE_ACTION)
414 {
415 behaviour = BEHAVIOUR_FLEE_TARGET;
416 [self setEvasiveJink:400.0];
417 frustration = 0.0;
418 if (accuracy > COMBAT_AI_ISNT_AWFUL)
419 {
420 // alert! they've got us in their sights! react!!
421 if ([self approachAspectToPrimaryTarget] > 0.9995)
422 {
423 behaviour = randf() < 0.15 ? BEHAVIOUR_EVASIVE_ACTION : BEHAVIOUR_FLEE_EVASIVE_ACTION;
424 }
425 }
426 }
427}
#define COMBAT_AI_ISNT_AWFUL
Definition ShipEntity.h:123

◆ performFlyToRangeFromDestination

- (void) performFlyToRangeFromDestination

Definition at line 1 of file ShipEntityAI.m.

431{
432 behaviour = BEHAVIOUR_FLY_RANGE_FROM_DESTINATION;
433 frustration = 0.0;
434}

◆ performHold

- (void) performHold

Definition at line 1 of file ShipEntityAI.m.

438{
439 desired_speed = 0.0;
440 behaviour = BEHAVIOUR_TRACK_TARGET;
441 frustration = 0.0;
442}

◆ performHyperSpaceToSpecificSystem:

- (BOOL) performHyperSpaceToSpecificSystem: (OOSystemID systemID

Definition at line 1 of file ShipEntityAI.m.

534 :(OOSystemID)systemID
535{
536 return [self performHyperSpaceExitReplace:NO toSystem:systemID];
537}
int16_t OOSystemID
Definition OOTypes.h:211

◆ performIdle

- (void) performIdle

Definition at line 1 of file ShipEntityAI.m.

446{
447 behaviour = BEHAVIOUR_IDLE;
448 frustration = 0.0;
449}

◆ performIntercept

- (void) performIntercept

Definition at line 1 of file ShipEntityAI.m.

453{
454 behaviour = BEHAVIOUR_INTERCEPT_TARGET;
455 frustration = 0.0;
456}

◆ performLandOnPlanet

- (void) performLandOnPlanet

Definition at line 1 of file ShipEntityAI.m.

460{
461 OOPlanetEntity *nearest = [self findNearestPlanet];
462 if (isNearPlanetSurface)
463 {
464 _destination = [nearest position];
465 behaviour = BEHAVIOUR_LAND_ON_PLANET;
466 planetForLanding = [nearest universalID];
467 }
468 else
469 {
470 behaviour = BEHAVIOUR_IDLE;
471 [shipAI message:@"NO_PLANET_NEARBY"];
472 }
473
474 frustration = 0.0;
475}
OOUniversalID universalID
Definition Entity.h:89

◆ performMining

- (void) performMining

Definition at line 1 of file ShipEntityAI.m.

479{
480 Entity *target = [self primaryTarget];
481 // mining is not seen as hostile behaviour, so ensure it is only used against rocks.
482 if (target && [target scanClass] == CLASS_ROCK)
483 {
484 behaviour = BEHAVIOUR_ATTACK_MINING_TARGET;
485 frustration = 0.0;
486 }
487 else
488 {
489 [self noteLostTargetAndGoIdle];
490 }
491}

◆ performScriptedAI

- (void) performScriptedAI

Definition at line 1 of file ShipEntityAI.m.

495{
496 behaviour = BEHAVIOUR_SCRIPTED_AI;
497 frustration = 0.0;
498}

◆ performScriptedAttackAI

- (void) performScriptedAttackAI

Definition at line 1 of file ShipEntityAI.m.

502{
503 behaviour = BEHAVIOUR_SCRIPTED_ATTACK_AI;
504 frustration = 0.0;
505}

◆ performStop

- (void) performStop

Definition at line 1 of file ShipEntityAI.m.

518{
519 behaviour = BEHAVIOUR_STOP_STILL;
520 desired_speed = 0.0;
521 frustration = 0.0;
522}

◆ performTumble

- (void) performTumble

Definition at line 1 of file ShipEntityAI.m.

526{
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;
530 frustration = 0.0;
531}

◆ recallDockingInstructions

- (void) recallDockingInstructions

Definition at line 1 of file ShipEntityAI.m.

598{
599 if (dockingInstructions != nil)
600 {
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"])
605 {
606 StationEntity *targetStation = [[dockingInstructions objectForKey:@"station"] weakRefUnderlyingObject];
607 if (targetStation != nil)
608 {
609 [self addTarget:targetStation];
610 [self setTargetStation:targetStation];
611 }
612 else
613 {
614 [self removeTarget:[self primaryTarget]];
615 }
616 }
617 docking_match_rotation = [dockingInstructions oo_boolForKey:@"match_rotation"];
618 }
619}

◆ requestDockingCoordinates

- (void) requestDockingCoordinates

Definition at line 1 of file ShipEntityAI.m.

541{
542 /*- requests coordinates from the target station
543 if the target station can't be found
544 then use the nearest it can find (which may be a rock hermit) -*/
545
546 StationEntity *station = nil;
547 Entity *targStation = nil;
548 NSString *message = nil;
549 double distanceToStation2 = 0.0;
550
551 targStation = [self targetStation];
552 if ([targStation isStation])
553 {
554 station = (StationEntity*)targStation;
555 }
556 else
557 {
558 station = [UNIVERSE nearestShipMatchingPredicate:IsStationPredicate
559 parameter:nil
560 relativeToEntity:self];
561 }
562
563 distanceToStation2 = HPdistance2([station position], [self position]);
564
565 // Player check for being inside the aegis already exists in PlayerEntityControls. We just
566 // check here that distance to station is less than 2.5 times scanner range to avoid problems with
567 // NPC ships getting stuck with a dockingAI while just outside the aegis - Nikos 20090630, as proposed by Eric
568 // On very busy systems (> 50 docking ships) docking ships can be sent to a hold position outside the range,
569 // so also test for presence of dockingInstructions. - Eric 20091130
570 if (station != nil && (distanceToStation2 < SCANNER_MAX_RANGE2 * 6.25 || dockingInstructions != nil))
571 {
572 // remember the instructions
573 [dockingInstructions release];
574 dockingInstructions = [[station dockingInstructionsForShip:self] retain];
575 if (dockingInstructions != nil)
576 {
577 [self recallDockingInstructions];
578
579 message = [dockingInstructions objectForKey:@"ai_message"];
580 if (message != nil) [shipAI message:message];
581 message = [dockingInstructions objectForKey:@"comms_message"];
582 if (message != nil) [station sendExpandedMessage:message toShip:self];
583 }
584 }
585 else
586 {
587 DESTROY(dockingInstructions);
588 }
589
590 if (dockingInstructions == nil)
591 {
592 [shipAI message:@"NO_STATION_FOUND"];
593 }
594}
#define SCANNER_MAX_RANGE2
Definition Entity.h:52

◆ scanForHostiles

- (void) scanForHostiles

Definition at line 1 of file ShipEntityAI.m.

315{
316 /*-- Locates all the ships in range targeting the receiver and chooses the nearest --*/
317 DESTROY(_foundTarget);
318
319 [self checkScanner];
320 unsigned i;
321 GLfloat found_d2 = scannerRange * scannerRange;
322 for (i = 0; i < n_scanned_ships ; i++)
323 {
324 ShipEntity *thing = scanned_ships[i];
325 GLfloat d2 = distance2_scanned_ships[i];
326 if ((d2 < found_d2)
327 && ([thing isThargoid] || (([thing primaryTarget] == self) && [thing hasHostileTarget]) || [thing isDefenseTarget:self])
328 && ![thing isCloaked])
329 {
330 [self setFoundTarget:thing];
331 found_d2 = d2;
332 }
333 }
334
335 [self checkFoundTarget];
336}
return self

◆ scanForNearestIncomingMissile

- (void) scanForNearestIncomingMissile

Definition at line 1 of file ShipEntityAI.m.

623{
625 {
626 HasScanClassPredicate, [NSNumber numberWithInt:CLASS_MISSILE],
628 };
629 [self scanForNearestShipWithPredicate:ANDPredicate parameter:&param];
630}
BOOL HasScanClassPredicate(Entity *entity, void *parameter)
BOOL IsHostileAgainstTargetPredicate(Entity *ship, void *parameter)

◆ setAIScript:

- (void) setAIScript: (NSString *)  aiString

Definition at line 1 of file ShipEntityAI.m.

284 :(NSString *)aiString
285{
286 NSMutableDictionary *properties = nil;
287
288 properties = [NSMutableDictionary dictionary];
289 [properties setObject:self forKey:@"ship"];
290
291 [aiScript autorelease];
292 aiScript = [OOScript jsAIScriptFromFileNamed:aiString properties:properties];
293 if (aiScript == nil)
294 {
295 OOLog(@"ai.load.failed.unknownAI",@"Unable to load JS AI %@ for ship %@ (%@ for role %@)",aiString,self,[self shipDataKey],[self primaryRole]);
296 aiScript = [OOScript jsAIScriptFromFileNamed:@"oolite-nullAI.js" properties:properties];
297 }
298 else
299 {
300 aiScriptWakeTime = 0;
301 haveStartedJSAI = NO;
302 }
303 [aiScript retain];
304}
#define OOLog(class, format,...)
Definition OOLogging.h:88
id jsAIScriptFromFileNamed:properties:(NSString *fileName,[properties] NSDictionary *properties)
Definition OOScript.m:220

◆ setAITo:

- (void) setAITo: (NSString *)  aiString

Definition at line 1 of file ShipEntityAI.m.

252 :(NSString *)aiString
253{
254 // don't try to load real AIs if the game hasn't started yet
255 if (![PLAYER scriptsLoaded])
256 {
257 aiString = @"oolite-nullAI.js";
258 }
259 if ([aiString hasSuffix:@".plist"])
260 {
261 [[self getAI] setStateMachine:aiString withJSScript:@"oolite-nullAI.js"];
262 [self setAIScript:@"oolite-nullAI.js"];
263 }
264 else if ([aiString hasSuffix:@".js"])
265 {
266 [[self getAI] setStateMachine:@"nullAI.plist" withJSScript:aiString];
267 [self setAIScript:aiString];
268 }
269 else
270 {
271 NSString *path = [ResourceManager pathForFileNamed:[aiString stringByAppendingString:@".js"] inFolder:@"AIs"];
272 if (path == nil) // no js, use plist
273 {
274 [self setAITo:[aiString stringByAppendingString:@".plist"]];
275 }
276 else
277 {
278 [self setAITo:[aiString stringByAppendingString:@".js"]];
279 }
280 }
281}
#define PLAYER
NSString * pathForFileNamed:inFolder:(NSString *fileName,[inFolder] NSString *folderName)

◆ suggestEscortTo:

- (BOOL) suggestEscortTo: (ShipEntity *)  mother

Definition at line 1 of file ShipEntityAI.m.

717 :(ShipEntity *)mother
718{
719 if (mother)
720 {
721#ifndef NDEBUG
722 if (reportAIMessages)
723 {
724 OOLog(@"ai.suggestEscort", @"DEBUG: %@ suggests escorting %@", self, mother);
725 }
726#endif
727
728 if ([mother acceptAsEscort:self])
729 {
730 // copy legal status across
731 if (([mother legalStatus] > 0)&&(bounty <= 0))
732 {
733 int extra = 1 | (ranrot_rand() & 15);
734// [mother setBounty: [mother legalStatus] + extra withReason:kOOLegalStatusReasonAssistingOffender];
735 [self markAsOffender:extra withReason:kOOLegalStatusReasonAssistingOffender];
736 // bounty += extra; // obviously we're dodgier than we thought!
737 }
738
739 [self setOwner:mother];
740 [self setGroup:[mother escortGroup]];
741 [shipAI message:@"ESCORTING"];
742 return YES;
743 }
744
745#ifndef NDEBUG
746 if (reportAIMessages)
747 {
748 OOLog(@"ai.suggestEscort.refused", @"DEBUG: %@ refused by %@", self, mother);
749 }
750#endif
751
752 }
753 [self setOwner:self];
754 [shipAI message:@"NOT_ESCORTING"];
755 [self doScriptEvent:OOJSID("escortRejected") withArgument:mother];
756 return NO;
757}
OOShipGroup * escortGroup()
#define ranrot_rand()

◆ switchAITo:

- (void) switchAITo: (NSString *)  aiString

Definition at line 1 of file ShipEntityAI.m.

307 :(NSString *)aiString
308{
309 [self setAITo:aiString];
310 [[self getAI] clearStack];
311}

◆ wormholeEntireGroup

- (void) wormholeEntireGroup

Definition at line 1 of file ShipEntityAI.m.

711{
712 [self wormholeGroup];
713 [self wormholeEscorts];
714}

◆ wormholeEscorts

- (void) wormholeEscorts

Definition at line 1 of file ShipEntityAI.m.

683{
684 ShipEntity *ship = nil;
685 NSString *context = nil;
686 WormholeEntity *whole = nil;
687
688 whole = [self primaryTarget];
689 if (![whole isWormhole]) return;
690
691#ifndef NDEBUG
692 context = [NSString stringWithFormat:@"%@ wormholeEscorts", [self shortDescription]];
693#endif
694
695 foreach (ship, [self escortEnumerator])
696 {
697 [ship addTarget:whole];
698 [ship reactToAIMessage:@"ENTER WORMHOLE" context:context];
699 [ship doScriptEvent:OOJSID("wormholeSuggested") withArgument:whole];
700 }
701
702 // We now have no escorts..
703
704 [_escortGroup release];
705 _escortGroup = nil;
706
707}
void addTarget:(Entity *targetEntity)
void doScriptEvent:withArgument:(jsid message,[withArgument] id argument)

The documentation for this category was generated from the following files: