48#import "MyOpenGLView.h"
104#if MASS_DEPENDENT_FUEL_PRICES
105static GLfloat calcFuelChargeRate (GLfloat myMass)
107#define kMassCharge 0.65
108#define kBaseCharge (1.0 - kMassCharge)
110 GLfloat baseMass = [PLAYER baseMass];
112 if (myMass <= 0.0 || baseMass <=0.0)
return 1.0;
114 GLfloat result = (kMassCharge * myMass / baseMass) + kBaseCharge;
117 result = roundf(result * 100.0f) / 100.0f;
120 if (result > 3.0f) result = 3.0f;
121 else if (result < 0.33f) result = 0.33f;
131@interface ShipEntity (Private)
140- (void) rescaleBy:(GLfloat)factor;
141- (void) rescaleBy:(GLfloat)factor writeToCache:(BOOL)writeToCache;
143- (BOOL) setUpOneSubentity:(NSDictionary *) subentDict;
144- (BOOL) setUpOneFlasher:(NSDictionary *) subentDict;
148- (void) addSubEntity:(
Entity<OOSubEntity> *) subent;
151- (HPVector) coordinatesForEscortPosition:(
unsigned)idx;
153- (void) setUpOneEscort:(
ShipEntity *)escorter inGroup:(
OOShipGroup *)escortGroup withRole:(NSString *)escortRole atPosition:(HPVector)ex_pos andCount:(uint8_t)currentEscortCount;
155- (void) addSubentityToCollisionRadius:(
Entity<OOSubEntity> *) subent;
156- (
ShipEntity *) launchPodWithCrew:(NSArray *)podCrew;
159- (
OOEquipmentType *) generateMissileEquipmentTypeFrom:(NSString *)role;
163- (void) noteFrustration:(NSString *)context;
182 return [
self initWithKey:@"" definition:nil];
186- (id) initBypassForPlayer
193- (id)initWithKey:(NSString *)key definition:(NSDictionary *)dict
197 NSParameterAssert(dict !=
nil);
200 if (
self ==
nil)
return nil;
202 _shipKey = [key retain];
206 [
self setStatus:STATUS_IN_FLIGHT];
209 weapon_recharge_rate = 6.0;
214 forward_weapon_temp = 0.0f;
215 aft_weapon_temp = 0.0f;
216 port_weapon_temp = 0.0f;
217 starboard_weapon_temp = 0.0f;
219 _nextAegisCheck = -0.1f;
220 aiScriptWakeTime = 0;
222 if (![
self setUpShipFromDictionary:dict])
229 if (
self !=
nil && !isfinite(maxFlightSpeed))
231 OOLog(
@"ship.sanityCheck.failed",
@"Ship %@ %@ infinite top speed, clamped to 300.",
self,
@"generated with");
232 maxFlightSpeed = 300;
240- (BOOL) setUpFromDictionary:(NSDictionary *) shipDict
248 shipinfoDictionary = [shipDict copy];
249 if (shipinfoDictionary ==
nil) shipinfoDictionary = [[NSDictionary alloc] init];
250 shipDict = shipinfoDictionary;
253 haveExecutedSpawnAction = NO;
254 haveStartedJSAI = NO;
255 scripted_misjump = NO;
256 _scriptedMisjumpRange = 0.5;
258 isNearPlanetSurface = NO;
259 suppressAegisMessages = NO;
261 suppressExplosion = NO;
266 _scaleFactor = [shipDict oo_floatForKey:@"model_scale_factor" defaultValue:1.0f];
269 float defaultSpeed = isStation ? 0.0f : 160.0f;
270 maxFlightSpeed = [shipDict oo_floatForKey:@"max_flight_speed" defaultValue:defaultSpeed];
271 max_flight_roll = [shipDict oo_floatForKey:@"max_flight_roll" defaultValue:2.0f];
272 max_flight_pitch = [shipDict oo_floatForKey:@"max_flight_pitch" defaultValue:1.0f];
273 max_flight_yaw = [shipDict oo_floatForKey:@"max_flight_yaw" defaultValue:max_flight_pitch];
274 cruiseSpeed = maxFlightSpeed*0.8f;
276 max_thrust = [shipDict oo_floatForKey:@"thrust" defaultValue:15.0f];
279 afterburner_rate = [shipDict oo_floatForKey:@"injector_burn_rate" defaultValue:AFTERBURNER_BURNRATE];
280 afterburner_speed_factor = [shipDict oo_floatForKey:@"injector_speed_factor" defaultValue:7.0f];
281 if (afterburner_speed_factor < 1.0)
283 OOLog(
@"ship.setup.injectorSpeed",
@"injector_speed_factor cannot be lower than 1.0 for %@",
self);
284 afterburner_speed_factor = 1.0;
286#if OO_VARIABLE_TORUS_SPEED
289 OOLog(
@"ship.setup.injectorSpeed",
@"injector_speed_factor cannot be higher than minimum torus speed factor (%f) for %@.",
MIN_HYPERSPEED_FACTOR,
self);
295 OOLog(
@"ship.setup.injectorSpeed",
@"injector_speed_factor cannot be higher than torus speed factor (%f) for %@.",
HYPERSPEED_FACTOR,
self);
300 maxEnergy = [shipDict oo_floatForKey:@"max_energy" defaultValue:200.0f];
301 energy_recharge_rate = [shipDict oo_floatForKey:@"energy_recharge_rate" defaultValue:1.0f];
303 _showDamage = [shipDict oo_boolForKey:@"show_damage" defaultValue:(energy_recharge_rate > 0)];
305 [
self setThrowSparks:[shipDict oo_boolForKey:@"throw_sparks" defaultValue:NO]];
307 weapon_facings = [shipDict oo_intForKey:@"weapon_facings" defaultValue:VALID_WEAPON_FACINGS] &
VALID_WEAPON_FACINGS;
309 forward_weapon_type =
OOWeaponTypeFromString([shipDict oo_stringForKey:
@"forward_weapon_type" defaultValue:
@"EQ_WEAPON_NONE"]);
311 aft_weapon_type =
OOWeaponTypeFromString([shipDict oo_stringForKey:
@"aft_weapon_type" defaultValue:
@"EQ_WEAPON_NONE"]);
313 port_weapon_type =
OOWeaponTypeFromString([shipDict oo_stringForKey:
@"port_weapon_type" defaultValue:
@"EQ_WEAPON_NONE"]);
315 starboard_weapon_type =
OOWeaponTypeFromString([shipDict oo_stringForKey:
@"starboard_weapon_type" defaultValue:
@"EQ_WEAPON_NONE"]);
317 cloaking_device_active = NO;
318 military_jammer_active = NO;
319 cloakPassive = [shipDict oo_boolForKey:@"cloak_passive" defaultValue:YES];
320 cloakAutomatic = [shipDict oo_boolForKey:@"cloak_automatic" defaultValue:YES];
322 missiles = [shipDict oo_intForKey:@"missiles" defaultValue:0];
332 for (i = 0; i < missiles; i++)
334 missile_list[i] =
nil;
336 max_missiles = [shipDict oo_intForKey:@"max_missiles" defaultValue:missiles];
338 if (missiles > max_missiles) missiles = max_missiles;
339 missile_load_time = fmax(0.0, [shipDict oo_doubleForKey:
@"missile_load_time" defaultValue:0.0]);
340 missile_launch_time = [UNIVERSE getTime] + missile_load_time;
343 equipment_weight = 0;
344 if ([shipDict oo_fuzzyBooleanForKey:
@"has_ecm"]) [
self addEquipmentItem:@"EQ_ECM" inContext:@"npc"];
345 if ([shipDict oo_fuzzyBooleanForKey:
@"has_scoop"]) [
self addEquipmentItem:@"EQ_FUEL_SCOOPS" inContext:@"npc"];
346 if ([shipDict oo_fuzzyBooleanForKey:
@"has_escape_pod"]) [
self addEquipmentItem:@"EQ_ESCAPE_POD" inContext:@"npc"];
347 if ([shipDict oo_fuzzyBooleanForKey:
@"has_cloaking_device"]) [
self addEquipmentItem:@"EQ_CLOAKING_DEVICE" inContext:@"npc"];
348 if ([shipDict oo_floatForKey:
@"has_energy_bomb"] > 0)
358 if ([shipDict oo_fuzzyBooleanForKey:
@"has_energy_bomb"])
364 [
self addEquipmentItem:@"EQ_QC_MINE" inContext:@"npc"];
368 if ([shipDict oo_fuzzyBooleanForKey:
@"has_fuel_injection"]) [
self addEquipmentItem:@"EQ_FUEL_INJECTION" inContext:@"npc"];
371 if ([shipDict oo_fuzzyBooleanForKey:
@"has_military_jammer"]) [
self addEquipmentItem:@"EQ_MILITARY_JAMMER" inContext:@"npc"];
372 if ([shipDict oo_fuzzyBooleanForKey:
@"has_military_scanner_filter"]) [
self addEquipmentItem:@"EQ_MILITARY_SCANNER_FILTER" inContext:@"npc"];
377 canFragment = [shipDict oo_fuzzyBooleanForKey:@"fragment_chance" defaultValue:0.9];
381 isFrangible = [shipDict oo_boolForKey:@"frangible" defaultValue:YES];
383 max_cargo = [shipDict oo_unsignedIntForKey:@"max_cargo"];
384 extra_cargo = [shipDict oo_unsignedIntForKey:@"extra_cargo" defaultValue:15];
386 hyperspaceMotorSpinTime = [shipDict oo_floatForKey:@"hyperspace_motor_spin_time" defaultValue:DEFAULT_HYPERSPACE_SPIN_TIME];
387 if(![shipDict oo_boolForKey:
@"hyperspace_motor" defaultValue:YES]) hyperspaceMotorSpinTime = -1;
390 name = [[shipDict oo_stringForKey:@"name" defaultValue:@"?"] copy];
392 [shipUniqueName autorelease];
393 shipUniqueName = [[shipDict oo_stringForKey:@"ship_name" defaultValue:@""] copy];
395 [shipClassName autorelease];
396 shipClassName = [[shipDict oo_stringForKey:@"ship_class_name" defaultValue:name] copy];
398 [displayName autorelease];
399 displayName = [[shipDict oo_stringForKey:@"display_name" defaultValue:nil] copy];
402 NSString *modelName = [shipDict oo_stringForKey:@"model"];
403 if (modelName !=
nil)
408 cacheKey:[NSString stringWithFormat:@"%@-%.3f",_shipKey,_scaleFactor]
411 smooth:[shipDict oo_boolForKey:@"smooth" defaultValue:NO]
417 if (mesh ==
nil)
return NO;
421 float density = [shipDict oo_floatForKey:@"density" defaultValue:1.0f];
422 if (octree) mass = (GLfloat)(density * 20.0f * [octree volume]);
427 if (default_laser_color ==
nil)
433 [
self setLaserColor:default_laser_color];
437 defaultExhaustEmissiveColorComponents.
r = 0.7f;
438 defaultExhaustEmissiveColorComponents.
g = 0.9f;
439 defaultExhaustEmissiveColorComponents.
b = 1.0f;
440 defaultExhaustEmissiveColorComponents.
a = 0.9f;
443 [
self setExhaustEmissiveColor:color];
445 [
self clearSubEntities];
446 [
self setUpSubEntities];
452 BOOL hasTurrets = NO;
453 NSEnumerator *subEnum = [
self shipSubEntityEnumerator];
455 while (
isWeaponNone(weapon_type) && (se = [subEnum nextObject]))
458 if (se->
behaviour == BEHAVIOUR_TRACK_AS_TURRET)
474 [
self setWeaponDataFromType:weapon_type];
479 [
self setWeaponDataFromType:forward_weapon_type];
484 if ([shipDict objectForKey:
@"rotational_velocity"])
486 subentityRotationalVelocity = [shipDict oo_quaternionForKey:@"rotational_velocity"];
490 NSString *weaponMountMode = [shipDict oo_stringForKey:@"weapon_mount_mode" defaultValue:@"single"];
491 _multiplyWeapons = [weaponMountMode isEqualToString:@"multiply"];
492 forwardWeaponOffset = [[
self getWeaponOffsetFrom:shipDict withKey:@"weapon_position_forward" inMode:weaponMountMode] retain];
493 aftWeaponOffset = [[
self getWeaponOffsetFrom:shipDict withKey:@"weapon_position_aft" inMode:weaponMountMode] retain];
494 portWeaponOffset = [[
self getWeaponOffsetFrom:shipDict withKey:@"weapon_position_port" inMode:weaponMountMode] retain];
495 starboardWeaponOffset = [[
self getWeaponOffsetFrom:shipDict withKey:@"weapon_position_starboard" inMode:weaponMountMode] retain];
498 tractor_position = vector_multiply_scalar([shipDict oo_vectorForKey:
@"scoop_position"],_scaleFactor);
503 [
self setSunGlareFilter:[shipDict oo_floatForKey:@"sun_glare_filter" defaultValue:0.97f]];
506 scriptInfo = [[shipDict oo_dictionaryForKey:@"script_info" defaultValue:nil] retain];
508 explosionType = [[shipDict oo_arrayForKey:@"explosion_type" defaultValue:nil] retain];
519- (BOOL) setUpShipFromDictionary:(NSDictionary *) shipDict
523 if (![
self setUpFromDictionary:shipDict])
return NO;
532 reference = v_forward;
538 scanClass =
OOScanClassFromString([shipDict oo_stringForKey:
@"scan_class" defaultValue:
@"CLASS_NOT_SET"]);
541 if (scanClass == CLASS_NOT_SET)
543 scanClass =
OOScanClassFromString([shipDict oo_stringForKey:
@"scanClass" defaultValue:
@"CLASS_NOT_SET"]);
546 [scan_description autorelease];
547 scan_description = [[shipDict oo_stringForKey:@"scan_description" defaultValue:nil] copy];
551 if ([shipDict oo_fuzzyBooleanForKey:
@"has_shield_booster"]) [
self addEquipmentItem:@"EQ_SHIELD_BOOSTER" inContext:@"npc"];
552 if ([shipDict oo_fuzzyBooleanForKey:
@"has_shield_enhancer"]) [
self addEquipmentItem:@"EQ_SHIELD_ENHANCER" inContext:@"npc"];
557 forward_weapon_temp = 0.0f;
558 aft_weapon_temp = 0.0f;
559 port_weapon_temp = 0.0f;
560 starboard_weapon_temp = 0.0f;
564 if (weapon_damage == 0.0)
566 weapon_damage_override = weapon_damage = [shipDict oo_floatForKey:@"weapon_energy" defaultValue:0];
570 weapon_damage_override = 0;
573 scannerRange = [shipDict oo_floatForKey:@"scanner_range" defaultValue:(float)SCANNER_MAX_RANGE];
575 fuel = [shipDict oo_unsignedShortForKey:@"fuel"];
577 fuel_accumulator = 1.0;
579 [
self setBounty:[shipDict oo_unsignedIntForKey:@"bounty" defaultValue:0] withReason:kOOLegalStatusReasonSetup];
581 [shipAI autorelease];
582 shipAI = [[
AI alloc] init];
583 [shipAI setOwner:self];
584 [
self setAITo:[shipDict oo_stringForKey:@"ai_type" defaultValue:@"nullAI.plist"]];
586 likely_cargo = [shipDict oo_unsignedIntForKey:@"likely_cargo"];
587 noRocks = [shipDict oo_fuzzyBooleanForKey:@"no_boulders"];
589 commodity_amount = 0;
590 commodity_type =
nil;
591 NSString *cargoString = [shipDict oo_stringForKey:@"cargo_carried"];
592 if (cargoString !=
nil)
594 if ([cargoString isEqualToString:
@"SCARCE_GOODS"])
598 else if ([cargoString isEqualToString:
@"PLENTIFUL_GOODS"])
608 NSScanner *scanner = [NSScanner scannerWithString:cargoString];
609 if ([scanner scanInt:&c_amount])
611 [scanner ooliteScanCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:NULL];
612 c_commodity = [[scanner string] substringFromIndex:[scanner scanLocation]];
613 if ([[
UNIVERSE commodities] goodDefined:c_commodity])
615 [
self setCommodityForPod:c_commodity andAmount:c_amount];
619 c_commodity = [[UNIVERSE commodities] goodNamed:c_commodity];
620 if ([[
UNIVERSE commodities] goodDefined:c_commodity])
622 [
self setCommodityForPod:c_commodity andAmount:c_amount];
629 c_commodity = [shipDict oo_stringForKey:@"cargo_carried"];
630 if ([[
UNIVERSE commodities] goodDefined:c_commodity])
632 [
self setCommodityForPod:c_commodity andAmount:c_amount];
636 c_commodity = [[UNIVERSE commodities] goodNamed:c_commodity];
637 if ([[
UNIVERSE commodities] goodDefined:c_commodity])
639 [
self setCommodityForPod:c_commodity andAmount:c_amount];
646 cargoString = [shipDict oo_stringForKey:@"cargo_type"];
649 if (cargo !=
nil) [cargo autorelease];
650 cargo = [[NSMutableArray alloc] initWithCapacity:max_cargo];
652 [
self setUpCargoType:cargoString];
654 else if (scanClass != CLASS_CARGO)
656 if (cargo !=
nil) [cargo autorelease];
657 cargo = [[NSMutableArray alloc] initWithCapacity:max_cargo];
662 hasScoopMessage = [shipDict oo_boolForKey:@"has_scoop_message" defaultValue:YES];
667 [primaryRole release];
670 [
self setOwner:self];
671 [
self setHulk:[shipDict oo_boolForKey:@"is_hulk"]];
674 [
self setScannerDisplayColor1:nil];
675 [
self setScannerDisplayColor2:nil];
677 [
self setScannerDisplayColorHostile1:nil];
678 [
self setScannerDisplayColorHostile2:nil];
682 _missileRole = [shipDict oo_stringForKey:@"missile_role"];
684 for (i = 0, j = 0; i < missiles; i++)
686 missile_list[i] = [
self selectMissile];
688 if (missile_list[i] ==
nil && j < 3)
696 if (missile_list[i] ==
nil)
710 accuracy = [shipDict oo_floatForKey:@"accuracy" defaultValue:-100.0f];
711 if (accuracy < -5.0f || accuracy > 10.0f)
713 accuracy = (
randf() * 10.0)-5.0;
715 if (accuracy < 0.0f && (scanClass == CLASS_MILITARY || scanClass == CLASS_POLICE))
717 accuracy = -accuracy;
720 if (scanClass == CLASS_MISSILE)
722 accuracy = OOClamp_0_max_f(accuracy, 10.0f);
724 [
self setAccuracy:accuracy];
728 _maxEscortCount =
MIN([shipDict oo_unsignedCharForKey:
@"escorts" defaultValue:0], (uint8_t)
MAX_ESCORTS);
729 _pendingEscortCount = _maxEscortCount;
730 if (_pendingEscortCount == 0 && [shipDict oo_arrayForKey:
@"escort_roles" defaultValue:
nil] !=
nil)
740 [
self setBeaconCode:[shipDict oo_stringForKey:@"beacon"]];
741 [
self setBeaconLabel:[shipDict oo_stringForKey:@"beacon_label" defaultValue:[shipDict oo_stringForKey:@"beacon"]]];
745 [
self setTrackCloseContacts:[shipDict oo_boolForKey:@"track_contacts" defaultValue:NO]];
748 [
self setHeatInsulation:[shipDict oo_floatForKey:@"heat_insulation" defaultValue:[
self hasHeatShield] ? 2.0 : 1.0]];
751 _explicitlyUnpiloted = [shipDict oo_fuzzyBooleanForKey:@"unpiloted"];
752 if (_explicitlyUnpiloted)
759 NSDictionary *cdict = [[UNIVERSE characters] objectForKey:[shipDict oo_stringForKey:@"pilot"]];
763 [
self setCrew:[NSArray arrayWithObject:pilot]];
767 [
self setShipScript:[shipDict oo_stringForKey:@"script"]];
769 home_system = [UNIVERSE currentSystemID];
770 destination_system = [UNIVERSE currentSystemID];
772 reactionTime = [shipDict oo_floatForKey: @"reaction_time" defaultValue: COMBAT_AI_STANDARD_REACTION_TIME];
780- (void) setSubIdx:(NSUInteger)value
792- (NSUInteger) maxShipSubEntities
794 return _maxShipSubIdx;
798- (NSString *) repeatString:(NSString *)str times:(NSUInteger)times
800 if (times == 0)
return @"";
802 NSMutableString *result = [NSMutableString stringWithCapacity:[str length] * times];
804 for (NSUInteger i = 0; i < times; i++)
806 [result appendString:str];
813- (NSString *) serializeShipSubEntities
815 NSMutableString *result = [NSMutableString stringWithCapacity:4];
816 NSEnumerator *subEnum =
nil;
818 NSUInteger diff, i = 0;
820 for (subEnum = [
self shipSubEntityEnumerator]; (se = [subEnum nextObject]); )
824 [result appendString:[
self repeatString:@"0" times:diff]];
825 [result appendString:@"1"];
828 [result appendString:[
self repeatString:@"0" times:[
self maxShipSubEntities] - i]];
833- (void) deserializeShipSubEntitiesFrom:(NSString *)string
835 NSArray *subEnts = [[
self shipSubEntityEnumerator] allObjects];
836 NSInteger i,idx, start = [subEnts count] - 1;
837 NSInteger strMaxIdx = [string length] - 1;
841 for (i = start; i >= 0; i--)
845 if (idx <= strMaxIdx && [[
string substringWithRange:NSMakeRange(idx, 1)] isEqualToString:
@"0"])
855- (BOOL) setUpSubEntities
860 NSDictionary *shipDict = [
self shipInfoDictionary];
861 NSArray *plumes = [shipDict oo_arrayForKey:@"exhaust"];
863 _profileRadius = collision_radius;
866 for (i = 0; i < [plumes count]; i++)
870 [
self addSubEntity:exhaust];
873 NSArray *subs = [shipDict oo_arrayForKey:@"subentities"];
875 totalBoundingBox = boundingBox;
877 for (i = 0; i < [subs count]; i++)
879 [
self setUpOneSubentity:[subs oo_dictionaryAtIndex:i]];
890- (GLfloat) frustumRadius
893 NSEnumerator *exEnum =
nil;
895 for (exEnum = [
self exhaustEnumerator]; (exEnt = [exEnum nextObject]); )
897 if ([exEnt findCollisionRadius] > exhaust_length)
902 return _profileRadius + exhaust_length;
906- (BOOL) setUpOneSubentity:(NSDictionary *) subentDict
910 NSString *type =
nil;
912 type = [subentDict oo_stringForKey:@"type"];
913 if ([type isEqualToString:
@"flasher"])
915 return [
self setUpOneFlasher:subentDict];
919 return [
self setUpOneStandardSubentity:subentDict asTurret:[type isEqualToString:@"ball_turret"]];
926- (BOOL) setUpOneFlasher:(NSDictionary *) subentDict
929 [flasher
setPosition:HPvector_multiply_scalar([subentDict oo_hpvectorForKey:@"position"],_scaleFactor)];
931 [
self addSubEntity:flasher];
936- (BOOL) setUpOneStandardSubentity:(NSDictionary *)subentDict asTurret:(BOOL)asTurret
939 NSString *subentKey =
nil;
940 HPVector subPosition;
941 Quaternion subOrientation;
943 subentKey = [subentDict oo_stringForKey:@"subentity_key"];
944 if (subentKey ==
nil) {
945 OOLog(
@"setup.ship.badEntry.subentities",
@"Failed to set up entity - no subentKey in %@",subentDict);
949 if (!asTurret && [
self isStation] && [subentDict oo_boolForKey:
@"is_dock"])
951 subentity = [UNIVERSE newDockWithName:subentKey andScaleFactor:_scaleFactor];
955 subentity = [UNIVERSE newSubentityWithName:subentKey andScaleFactor:_scaleFactor];
957 if (subentity ==
nil) {
958 OOLog(
@"setup.ship.badEntry.subentities",
@"Failed to set up entity %@",subentKey);
962 subPosition = HPvector_multiply_scalar([subentDict oo_hpvectorForKey:
@"position"],_scaleFactor);
963 subOrientation = [subentDict oo_quaternionForKey:@"orientation"];
967 [subentity
setReference:vector_forward_from_quaternion(subOrientation)];
974 [subentity
setWeaponRechargeRate:[subentDict oo_floatForKey:@"fire_rate" defaultValue:TURRET_SHOT_FREQUENCY]];
975 [subentity
setWeaponEnergy:[subentDict oo_floatForKey:@"weapon_energy" defaultValue:TURRET_TYPICAL_ENERGY]];
976 [subentity
setWeaponRange:[subentDict oo_floatForKey:@"weapon_range" defaultValue:TURRET_SHOT_RANGE]];
986 [
self addSubEntity:subentity];
992 bounding_box_add_vector(&totalBoundingBox, sebb.max);
993 bounding_box_add_vector(&totalBoundingBox, sebb.min);
995 if (!asTurret && [
self isStation] && [subentDict oo_boolForKey:
@"is_dock"])
997 BOOL allow_docking = [subentDict oo_boolForKey:@"allow_docking" defaultValue:YES];
998 BOOL ddc = [subentDict oo_boolForKey:@"disallowed_docking_collides" defaultValue:NO];
999 BOOL allow_launching = [subentDict oo_boolForKey:@"allow_launching" defaultValue:YES];
1001 BOOL virtual_dock = [subentDict oo_boolForKey:@"_is_virtual_dock" defaultValue:NO];
1008 [subentity
setDisplayName:[subentDict oo_stringForKey:@"dock_label" defaultValue:@"the docking bay"]];
1011 [subentity release];
1017- (BOOL) isTemplateCargoPod
1019 return [[
self primaryRole] isEqualToString:@"oolite-template-cargopod"];
1023- (void) setUpCargoType:(NSString *) cargoString
1030 commodity_amount = 1;
1032 commodity_type =
@"slaves";
1037 commodity_amount = 1;
1039 commodity_type =
@"alloys";
1044 commodity_amount = 1;
1046 commodity_type =
@"minerals";
1051 commodity_amount = 1;
1053 commodity_type =
@"alien_items";
1058 commodity_amount = 1;
1064 if (commodity_amount == 0) commodity_amount = 1;
1085 [weakSelf weakRefDrop];
1089 [
self setTrackCloseContacts:NO];
1090 [[
self parentEntity] subEntityReallyDied:self];
1091 [
self clearSubEntities];
1106 DESTROY(exhaust_emissive_color);
1107 DESTROY(scanner_display_color1);
1108 DESTROY(scanner_display_color2);
1109 DESTROY(scanner_display_color_hostile1);
1110 DESTROY(scanner_display_color_hostile2);
1119 DESTROY(_collisionExceptions);
1124 DESTROY(starboardWeaponOffset);
1128 [
self setSubEntityTakingDamage:nil];
1129 [
self removeAllEquipment];
1131 [_group removeShip:self];
1133 [_escortGroup removeShip:self];
1148- (void) removeScript
1150 [script autorelease];
1155- (void) clearSubEntities
1157 [subEntities makeObjectsPerformSelector:@selector(setOwner:) withObject:nil];
1158 [subEntities release];
1162 collision_radius = [
self findCollisionRadius];
1163 _profileRadius = collision_radius;
1164 float density = [[
self shipInfoDictionary] oo_floatForKey:@"density" defaultValue:1.0f];
1165 if (octree) mass = (GLfloat)(density * 20.0f * [octree volume]);
1169- (Quaternion) subEntityRotationalVelocity
1171 return subentityRotationalVelocity;
1175- (void) setSubEntityRotationalVelocity:(Quaternion)rv
1177 subentityRotationalVelocity = rv;
1181- (NSString *)descriptionComponents
1183 if (![
self isSubEntity])
1185 return [NSString stringWithFormat:@"\"%@\" %@", [
self name], [
super descriptionComponents]];
1190 NSString *subtype =
nil;
1191 if ([
self behaviour] == BEHAVIOUR_TRACK_AS_TURRET) subtype =
@"(turret)";
1192 else subtype =
@"(subentity)";
1194 return [NSString stringWithFormat:@"\"%@\" position: %@ %@", [
self name], HPVectorDescription([
self position]), subtype];
1199- (NSString *) shortDescriptionComponents
1201 return [NSString stringWithFormat:@"\"%@\"", [
self name]];
1205- (GLfloat) sunGlareFilter
1207 return sunGlareFilter;
1211- (void) setSunGlareFilter:(GLfloat)newValue
1213 sunGlareFilter = OOClamp_0_1_f(newValue);
1223- (void) setAccuracy:(GLfloat) new_accuracy
1225 if (new_accuracy < 0.0f && scanClass == CLASS_MISSILE)
1229 else if (new_accuracy < -5.0f)
1231 new_accuracy = -5.0;
1233 else if (new_accuracy > 10.0f)
1235 new_accuracy = 10.0;
1237 accuracy = new_accuracy;
1238 pitch_tolerance = 0.01 * (85.0f + accuracy);
1240 aim_tolerance = 240.0 - (18.0f * accuracy);
1244 missile_load_time = 2.0;
1250 return (
OOMesh *)[
self drawable];
1254- (void)setMesh:(
OOMesh *)mesh
1256 if (mesh != [
self mesh])
1258 [
self setDrawable:mesh];
1259 [octree autorelease];
1260 octree = [[mesh
octree] retain];
1265- (BoundingBox) totalBoundingBox
1267 return totalBoundingBox;
1271- (Vector) forwardVector
1283- (Vector) rightVector
1289- (BOOL) scriptedMisjump
1291 return scripted_misjump;
1295- (void) setScriptedMisjump:(BOOL)newValue
1297 scripted_misjump = !!newValue;
1301- (GLfloat) scriptedMisjumpRange
1303 return _scriptedMisjumpRange;
1307- (void) setScriptedMisjumpRange:(GLfloat)newValue
1309 _scriptedMisjumpRange = newValue;
1313- (NSArray *) subEntities
1315 return [[subEntities copy] autorelease];
1319- (NSUInteger) subEntityCount
1321 return [subEntities count];
1325- (BOOL) hasSubEntity:(
Entity<OOSubEntity> *)sub
1327 return [subEntities containsObject:sub];
1331- (NSEnumerator *)subEntityEnumerator
1333 return [[
self subEntities] objectEnumerator];
1337- (NSEnumerator *)shipSubEntityEnumerator
1339 return [[
self subEntities] objectEnumeratorFilteredWithSelector:@selector(isShip)];
1343- (NSEnumerator *)flasherEnumerator
1345 return [[
self subEntities] objectEnumeratorFilteredWithSelector:@selector(isFlasher)];
1349- (NSEnumerator *)exhaustEnumerator
1351 return [[
self subEntities] objectEnumeratorFilteredWithSelector:@selector(isExhaust)];
1357 ShipEntity *result = [_subEntityTakingDamage weakRefUnderlyingObject];
1362 if ([result parentEntity] !=
self) result =
nil;
1366 if (result ==
nil) [
self setSubEntityTakingDamage:
nil];
1372- (void) setSubEntityTakingDamage:(
ShipEntity *)sub
1378 if (![
self hasSubEntity:sub])
1380 OOLog(
@"ship.subentity.sanityCheck.failed.details",
@"Attempt to set subentity taking damage of %@ to %@, which is not a subentity.", [
self shortDescription], sub);
1383 else if (![sub isShip])
1385 OOLog(
@"ship.subentity.sanityCheck.failed",
@"Attempt to set subentity taking damage of %@ to %@, which is not a ship.", [
self shortDescription], sub);
1391 [_subEntityTakingDamage release];
1392 _subEntityTakingDamage = [sub weakRetain];
1410 return aiScriptWakeTime;
1416 aiScriptWakeTime = t;
1420- (BoundingBox)findBoundingBoxRelativeToPosition:(HPVector)opv InVectors:(Vector) _i :(Vector) _j :(Vector) _k
1423 return [[
self mesh] findBoundingBoxRelativeToPosition:HPVectorToVector(opv)
1425 selfPosition:HPVectorToVector(position)
1426 selfBasis:v_right :v_up :v_forward];
1438 return [octree volume];
1442- (GLfloat) doesHitLine:(HPVector)v0 :(HPVector)v1
1444 Vector u0 = HPVectorToVector(HPvector_between(position, v0));
1445 Vector u1 = HPVectorToVector(HPvector_between(position, v1));
1446 Vector w0 = make_vector(dot_product(u0, v_right), dot_product(u0, v_up), dot_product(u0, v_forward));
1447 Vector w1 = make_vector(dot_product(u1, v_right), dot_product(u1, v_up), dot_product(u1, v_forward));
1448 return [octree isHitByLine:w0 :w1];
1452- (GLfloat) doesHitLine:(HPVector)v0 :(HPVector)v1 :(
ShipEntity **)hitEntity
1456 Vector u0 = HPVectorToVector(HPvector_between(position, v0));
1457 Vector u1 = HPVectorToVector(HPvector_between(position, v1));
1458 Vector w0 = make_vector(dot_product(u0, v_right), dot_product(u0, v_up), dot_product(u0, v_forward));
1459 Vector w1 = make_vector(dot_product(u1, v_right), dot_product(u1, v_up), dot_product(u1, v_forward));
1460 GLfloat hit_distance = [octree isHitByLine:w0 :w1];
1464 hitEntity[0] =
self;
1467 NSEnumerator *subEnum =
nil;
1469 for (subEnum = [
self shipSubEntityEnumerator]; (se = [subEnum nextObject]); )
1473 u0 = HPVectorToVector(HPvector_between(p0, v0));
1474 u1 = HPVectorToVector(HPvector_between(p0, v1));
1475 w0 = resolveVectorInIJK(u0, ijk);
1476 w1 = resolveVectorInIJK(u1, ijk);
1478 GLfloat hitSub = [se->octree isHitByLine:w0 :w1];
1479 if (hitSub && (hit_distance == 0 || hit_distance > hitSub))
1481 hit_distance = hitSub;
1489 return hit_distance;
1493- (GLfloat)doesHitLine:(HPVector)v0 :(HPVector)v1 withPosition:(HPVector)o andIJK:(Vector)i :(Vector)j :(Vector)k
1495 Vector u0 = HPVectorToVector(HPvector_between(o, v0));
1496 Vector u1 = HPVectorToVector(HPvector_between(o, v1));
1497 Vector w0 = make_vector(dot_product(u0, i), dot_product(u0, j), dot_product(u0, k));
1498 Vector w1 = make_vector(dot_product(u1, j), dot_product(u1, j), dot_product(u1, k));
1499 return [octree isHitByLine:w0 :w1];
1503- (void) wasAddedToUniverse
1505 [
super wasAddedToUniverse];
1512 if (([
self status] == STATUS_IN_FLIGHT || [
self status] == STATUS_LAUNCHING) && _pendingEscortCount != 0)
1514 [
self setUpEscorts];
1523 _pendingEscortCount = 0;
1528 [subEntities makeObjectsPerformSelector:@selector(wasAddedToUniverse)];
1530 [
self resetExhaustPlumes];
1534- (void)wasRemovedFromUniverse
1536 [subEntities makeObjectsPerformSelector:@selector(wasRemovedFromUniverse)];
1540- (HPVector)absoluteTractorPosition
1542 return HPvector_add(position, vectorToHPVector(
quaternion_rotate_vector([
self normalOrientation], tractor_position)));
1546- (NSString *) beaconCode
1552- (void) setBeaconCode:(NSString *)bcode
1554 if ([bcode length] == 0) bcode =
nil;
1556 if (_beaconCode != bcode)
1558 [_beaconCode release];
1559 _beaconCode = [bcode copy];
1564 if (bcode !=
nil && (_beaconLabel ==
nil || [_beaconLabel length] == 0))
1566 [
self setBeaconLabel:bcode];
1571- (NSString *) beaconLabel
1573 return _beaconLabel;
1577- (void) setBeaconLabel:(NSString *)blabel
1579 if ([blabel length] == 0) blabel =
nil;
1581 if (_beaconLabel != blabel)
1583 [_beaconLabel release];
1584 _beaconLabel = [OOExpand(blabel) retain];
1591 return cam_zero_distance <= no_draw_distance;
1597 return [
self beaconCode] !=
nil;
1601- (
id <OOHUDBeaconIcon>) beaconDrawable
1603 if (_beaconDrawable ==
nil)
1605 NSString *beaconCode = [
self beaconCode];
1606 NSUInteger length = [beaconCode length];
1610 NSArray *iconData = [[UNIVERSE descriptions] oo_arrayForKey:beaconCode];
1611 if (iconData !=
nil) _beaconDrawable = [[
OOPolygonSprite alloc] initWithDataArray:iconData outlineWidth:0.5 name:beaconCode];
1614 if (_beaconDrawable ==
nil)
1616 if (length > 0) _beaconDrawable = [[beaconCode substringToIndex:1] retain];
1617 else _beaconDrawable =
@"";
1621 return _beaconDrawable;
1625- (
Entity <OOBeaconEntity> *) prevBeacon
1627 return [_prevBeacon weakRefUnderlyingObject];
1631- (
Entity <OOBeaconEntity> *) nextBeacon
1633 return [_nextBeacon weakRefUnderlyingObject];
1637- (void) setPrevBeacon:(
Entity <OOBeaconEntity> *)beaconShip
1639 if (beaconShip != [
self prevBeacon])
1641 [_prevBeacon release];
1642 _prevBeacon = [beaconShip weakRetain];
1647- (void) setNextBeacon:(
Entity <OOBeaconEntity> *)beaconShip
1649 if (beaconShip != [
self nextBeacon])
1651 [_nextBeacon release];
1652 _nextBeacon = [beaconShip weakRetain];
1657#define kBoulderRole (@"boulder")
1659- (void) setIsBoulder:(BOOL)flag
1661 if (flag) [
self addRole:kBoulderRole];
1662 else [
self removeRole:kBoulderRole];
1668 return [roleSet hasRole:kBoulderRole];
1674 if ([
self hasRole:
@"asteroid"] || [
self isBoulder])
1685- (BOOL) countsAsKill
1687 return [[
self shipInfoDictionary] oo_boolForKey:@"counts_as_kill" defaultValue:YES];
1691- (void) setUpEscorts
1696 if ([
self isEscort])
1698 OOLogWARN(
@"ship.setUp.escortShipCircularReference",
1699 @"Ship %@ requested escorts, when it is an escort ship itself. Avoiding possible circular reference overflow by ignoring escort setup.",
self);
1703 if ([shipinfoDictionary objectForKey:
@"escort_roles"] !=
nil)
1705 [
self setUpMixedEscorts];
1709 NSString *defaultRole =
@"escort";
1710 NSString *escortRole =
nil;
1711 NSString *escortShipKey =
nil;
1713 if (_pendingEscortCount == 0)
return;
1715 if (_maxEscortCount < _pendingEscortCount)
1717 if ([
self hasPrimaryRole:
@"police"] || [
self hasPrimaryRole:
@"hunter"])
1720 [
self updateEscortFormation];
1724 _pendingEscortCount = _maxEscortCount;
1728 if ([
self isPolice]) defaultRole =
@"wingman";
1730 escortRole = [shipinfoDictionary oo_stringForKey:@"escort_role" defaultValue:nil];
1731 if (escortRole ==
nil)
1732 escortRole = [shipinfoDictionary oo_stringForKey:@"escort-role" defaultValue:defaultRole];
1733 if (![escortRole isEqualToString: defaultRole])
1735 if (![[
UNIVERSE newShipWithRole:escortRole] autorelease])
1737 escortRole = defaultRole;
1741 escortShipKey = [shipinfoDictionary oo_stringForKey:@"escort_ship" defaultValue:nil];
1742 if (escortShipKey ==
nil)
1743 escortShipKey = [shipinfoDictionary oo_stringForKey:@"escort-ship"];
1745 if (escortShipKey !=
nil)
1747 if (![[
UNIVERSE newShipWithName:escortShipKey] autorelease])
1749 escortShipKey =
nil;
1753 escortRole = [NSString stringWithFormat:@"[%@]",escortShipKey];
1758 if ([
self group] ==
nil)
1760 [
self setGroup:escortGroup];
1764 [
self refreshEscortPositions];
1766 uint8_t currentEscortCount = [escortGroup
count] - 1;
1768 while (_pendingEscortCount > 0 && ([
self isThargoid] || currentEscortCount < _maxEscortCount))
1771 HPVector ex_pos = [
self coordinatesForEscortPosition:currentEscortCount];
1775 escorter = [UNIVERSE newShipWithRole:escortRole];
1777 if (escorter ==
nil)
break;
1778 [
self setUpOneEscort:escorter inGroup:escortGroup withRole:escortRole atPosition:ex_pos andCount:currentEscortCount];
1782 _pendingEscortCount--;
1783 currentEscortCount = [escortGroup
count] - 1;
1786 _pendingEscortCount = 0;
1792 NSArray *escortRoles = [shipinfoDictionary oo_arrayForKey:@"escort_roles" defaultValue:nil];
1793 if (escortRoles ==
nil)
1795 OOLogWARN(
@"eship.setUp.escortShipRoles",
1796 @"Ship %@ has bad escort_roles definition.",
self);
1799 NSEnumerator *edefEnumerator =
nil;
1800 NSDictionary *escortDefinition =
nil;
1801 NSDictionary *systeminfo =
nil;
1804 systeminfo = [UNIVERSE currentSystemData];
1805 government = [systeminfo oo_unsignedCharForKey:KEY_GOVERNMENT];
1808 if ([
self group] ==
nil)
1810 [
self setGroup:escortGroup];
1814 [
self refreshEscortPositions];
1816 uint8_t currentEscortCount = [escortGroup
count] - 1;
1818 _maxEscortCount = 0;
1820 for (edefEnumerator = [escortRoles objectEnumerator]; (escortDefinition = [edefEnumerator nextObject]); )
1828 int8_t min = [escortDefinition oo_intForKey:@"min" defaultValue:0];
1829 int8_t max = [escortDefinition oo_intForKey:@"max" defaultValue:2];
1830 NSString *escortRole = [escortDefinition oo_stringForKey:@"role" defaultValue:@"escort"];
1831 int8_t desired = max;
1834 for (i = min ; i < max ; i++)
1836 if (
Ranrot()%11 < government+2)
1842 for (i = 0; i < desired; i++)
1848 if (![escortRole isEqualToString:
@""])
1850 HPVector ex_pos = [
self coordinatesForEscortPosition:currentEscortCount];
1851 ShipEntity *escorter = [UNIVERSE newShipWithRole:escortRole];
1852 if (escorter ==
nil)
1856 [
self setUpOneEscort:escorter inGroup:escortGroup withRole:escortRole atPosition:ex_pos andCount:currentEscortCount];
1859 currentEscortCount++;
1864 _pendingEscortCount = 0;
1868- (void) setUpOneEscort:(
ShipEntity *)escorter inGroup:(
OOShipGroup *)escortGroup withRole:(NSString *)escortRole atPosition:(HPVector)ex_pos andCount:(uint8_t)currentEscortCount
1870 NSString *autoAI =
nil;
1871 NSString *pilotRole =
nil;
1872 NSDictionary *autoAIMap =
nil;
1873 NSDictionary *escortShipDict =
nil;
1875 NSString *defaultRole =
@"escort";
1877 if ([
self isPolice])
1879 defaultRole =
@"wingman";
1880 pilotRole =
@"police";
1884 pilotRole = bounty ?
@"pirate" :
@"hunter";
1892 ex_pos.x += dd * 6.0 * (
randf() - 0.5);
1893 ex_pos.y += dd * 6.0 * (
randf() - 0.5);
1894 ex_pos.z += dd * 6.0 * (
randf() - 0.5);
1899 ex_pos.x += dd * 12.0 * (
randf() - 0.5);
1900 ex_pos.y += dd * 12.0 * (
randf() - 0.5);
1901 ex_pos.z += dd * 12.0 * (
randf() - 0.5);
1906 if ([escorter crew] ==
nil)
1914 if (scanClass == CLASS_NOT_SET)
1916 scanClass = CLASS_NEUTRAL;
1925 autoAI = [autoAIMap oo_stringForKey:defaultRole];
1928 autoAI = [autoAIMap oo_stringForKey:@"escort" defaultValue:@"nullAI.plist"];
1931 escortAI = [escorter
getAI];
1934 if ( (escortRole && [escortShipDict oo_fuzzyBooleanForKey:
@"auto_ai" defaultValue:YES])
1935 || ([[escortAI name] isEqualToString:
@"nullAI.plist"] && ![autoAI isEqualToString:
@"nullAI.plist"]) )
1944 if ([
self status] == STATUS_DOCKED)
1946 [[
self owner] addShipToLaunchQueue:escorter withPriority:NO];
1950 [UNIVERSE addEntity:escorter];
1951 [escortAI
setState:@"FLYING_ESCORT"];
1955 if([escorter heatInsulation] < [
self heatInsulation]) [escorter
setHeatInsulation:[
self heatInsulation]];
1956 if(([escorter maxFlightSpeed] < cruiseSpeed) && ([escorter maxFlightSpeed] > cruiseSpeed * 0.3))
1974- (NSString *)shipDataKey
1980- (NSString *)shipDataKeyAutoRole
1982 return [[[NSString alloc] initWithFormat:@"[%@]",[
self shipDataKey]] autorelease];
1986- (void)setShipDataKey:(NSString *)key
1989 _shipKey = [key copy];
1993- (NSDictionary *)shipInfoDictionary
1995 return shipinfoDictionary;
1999#define MAKE_VECTOR_ARRAY(v) [[[OONativeVector alloc] initWithVector:v] autorelease]
2001- (NSArray *) getWeaponOffsetFrom:(NSDictionary *)dict withKey:(NSString *)key inMode:(NSString *)mode
2004 if ([
mode isEqualToString:
@"single"])
2006 offset = vector_multiply_scalar([dict oo_vectorForKey:key defaultValue:
kZeroVector],_scaleFactor);
2007 return [NSArray arrayWithObject:MAKE_VECTOR_ARRAY(offset)];
2011 NSArray *offsets = [dict oo_arrayForKey:key defaultValue:nil];
2012 if (offsets ==
nil) {
2014 return [NSArray arrayWithObject:MAKE_VECTOR_ARRAY(offset)];
2016 NSMutableArray *output = [NSMutableArray arrayWithCapacity:[offsets count]];
2018 for (i=0;i<[offsets count];i++) {
2019 offset = vector_multiply_scalar([offsets oo_vectorAtIndex:i defaultValue:
kZeroVector],_scaleFactor);
2020 [output addObject:MAKE_VECTOR_ARRAY(offset)];
2022 return [NSArray arrayWithArray:output];
2027- (NSArray *) aftWeaponOffset
2029 return aftWeaponOffset;
2033- (NSArray *) forwardWeaponOffset
2035 return forwardWeaponOffset;
2039- (NSArray *) portWeaponOffset
2041 return portWeaponOffset;
2045- (NSArray *) starboardWeaponOffset
2047 return starboardWeaponOffset;
2057- (BOOL) suppressFlightNotifications
2059 return suppressAegisMessages;
2065 if (cloaking_device_active)
return CLASS_NO_DRAW;
2073 int status = [
self status];
2074 if (status == STATUS_COCKPIT_DISPLAY || status == STATUS_DEAD || status == STATUS_BEING_SCOOPED)
2085 if (isMissile && [
self shotTime] < 0.25)
2104 Vector relative_position_of_other = resolveVectorInIJK(HPVectorToVector(HPvector_between(prime_position, other_position)), prime_ijk);
2105 Triangle relative_ijk_of_other;
2106 relative_ijk_of_other.v[0] = resolveVectorInIJK(other_ijk.v[0], prime_ijk);
2107 relative_ijk_of_other.v[1] = resolveVectorInIJK(other_ijk.v[1], prime_ijk);
2108 relative_ijk_of_other.v[2] = resolveVectorInIJK(other_ijk.v[2], prime_ijk);
2112 withOrigin:relative_position_of_other
2113 andIJK:relative_ijk_of_other])
2122 NSUInteger i, n_subs = [prime_subs count];
2123 for (i = 0; i < n_subs; i++)
2125 Entity* se = [prime_subs objectAtIndex:i];
2135 NSUInteger i, n_subs = [other_subs count];
2136 for (i = 0; i < n_subs; i++)
2138 Entity* se = [other_subs objectAtIndex:i];
2145 if ((prime_subs)&&(other_subs))
2147 NSUInteger i, n_osubs = [other_subs count];
2148 for (i = 0; i < n_osubs; i++)
2150 Entity* oe = [other_subs objectAtIndex:i];
2151 if ([oe isShip] && [oe canCollide])
2153 NSUInteger j, n_psubs = [prime_subs count];
2154 for (j = 0; j < n_psubs; j++)
2156 Entity* pe = [prime_subs objectAtIndex:j];
2169- (BOOL) checkCloseCollisionWith:(
Entity *)other
2171 if (other ==
nil)
return NO;
2172 if ([collidingEntities containsObject:other])
return NO;
2175 if ([other isShip]) otherShip = (
ShipEntity *)other;
2177 if ([
self canScoop:otherShip])
return YES;
2179 if (otherShip !=
nil && trackCloseContacts)
2183 HPVector otherPos = [otherShip position];
2185 NSString *other_key = [NSString stringWithFormat:@"%d", otherID];
2187 if (![closeContactsInfo objectForKey:other_key] &&
2188 HPdistance2(position, otherPos) < collision_radius * collision_radius)
2191 Vector dpos = HPVectorToVector(HPvector_between(position, otherPos));
2192 Vector rpos = make_vector(dot_product(dpos, v_right), dot_product(dpos, v_up), dot_product(dpos, v_forward));
2193 [closeContactsInfo setObject:[NSString stringWithFormat:@"%f %f %f", rpos.x, rpos.y, rpos.z] forKey: other_key];
2197 _primaryTarget = [otherShip weakRetain];
2198 [
self doScriptEvent:OOJSID("shipCloseContact") withArgument:otherShip andReactToAIMessage:@"CLOSE CONTACT"];
2199 _primaryTarget = temp;
2215 if (otherShip !=
nil)
2219 return (collider !=
nil);
2228- (BoundingBox)findSubentityBoundingBox
2230 return [[
self mesh] findSubentityBoundingBoxWithPosition:HPVectorToVector(position) rotMatrix:rotMatrix];
2234- (Triangle) absoluteIJKForSubentity
2241 while ((father)&&(father != last) && (father !=
NO_TARGET))
2249 if (![last isSubEntity])
break;
2250 father = [father
owner];
2256- (void) addSubentityToCollisionRadius:(
Entity<OOSubEntity> *)subent
2258 if (!subent)
return;
2260 double distance = HPmagnitude([subent position]) + [subent findCollisionRadius];
2261 if ([subent isKindOfClass:[
ShipEntity class]])
2263 if (distance > collision_radius)
2265 collision_radius = distance;
2268 mass += [subent mass];
2270 if (distance > _profileRadius)
2272 _profileRadius = distance;
2277- (
ShipEntity *) launchPodWithCrew:(NSArray *)podCrew
2281 pod = [UNIVERSE newShipWithRole:[shipinfoDictionary oo_stringForKey:@"escape_pod_role"]];
2285 pod = [UNIVERSE newShipWithRole:[shipinfoDictionary oo_stringForKey:@"escape_pod_model" defaultValue:@"escape-capsule"]];
2288 pod = [UNIVERSE newShipWithRole:@"escape-capsule"];
2289 OOLog(
@"shipEntity.noEscapePod",
@"Ship %@ has no correct escape_pod_role defined. Now using default capsule.",
self);
2296 [pod
setTemperature:[
self randomEjectaTemperatureWithMaxFactor:0.9]];
2300 [
self dumpItem:pod];
2308- (BOOL) validForAddToUniverse
2310 if (shipinfoDictionary ==
nil)
2312 OOLog(
@"shipEntity.notDict",
@"Ship %@ was not set up from dictionary.",
self);
2315 return [
super validForAddToUniverse];
2321 if (shipinfoDictionary ==
nil)
2323 OOLog(
@"shipEntity.notDict",
@"Ship %@ was not set up from dictionary.",
self);
2324 [UNIVERSE removeEntity:self];
2328 if (!isfinite(maxFlightSpeed))
2330 OOLog(
@"ship.sanityCheck.failed",
@"Ship %@ %@ infinite top speed, clamped to 300.",
self,
@"had");
2331 maxFlightSpeed = 300;
2334 bool isSubEnt = [
self isSubEntity];
2344 Quaternion q1 = make_quaternion(cos1, sin1*sqrt(3)/2, sin1/2, 0);
2345 Quaternion q2 = make_quaternion(cos2, -sin2*sqrt(4)/sqrt(5), 0, sin2*sqrt(1)/sqrt(5));
2346 [
self setOrientation: quaternion_multiply(q2, quaternion_multiply(q1, demoStartOrientation))];
2349 [
super update:delta_t];
2350 if ([
self subEntityCount] > 0)
2354 foreach (se, [
self subEntities])
2360 bounding_box_add_vector(&totalBoundingBox, sebb.max);
2361 bounding_box_add_vector(&totalBoundingBox, sebb.min);
2371 if (scanClass == CLASS_NOT_SET)
2373 scanClass = CLASS_NEUTRAL;
2374 OOLog(
@"ship.sanityCheck.failed",
@"Ship %@ %@ with scanClass CLASS_NOT_SET; forced to CLASS_NEUTRAL.",
self, [
self primaryRole]);
2377 [
self updateTrackingCurve];
2382 [
self manageCollisions];
2389 if (scanClass == CLASS_POLICE)
2393 [
self setBounty:0 withReason:kOOLegalStatusReasonPoliceAreClean];
2396 if ((target)&&([target scanClass] == CLASS_POLICE))
2398 [
self noteLostTarget];
2402 if (trackCloseContacts)
2406 NSString *other_key =
nil;
2410 NSDictionary *closeContactsTemp = [[NSDictionary alloc] initWithDictionary:closeContactsInfo];
2413 ShipEntity* other = [UNIVERSE entityForUniversalID:[other_key intValue]];
2416 if (HPdistance2(position, other->
position) > collision_radius * collision_radius)
2419 Vector dpos = HPVectorToVector(HPvector_between(position, other->
position));
2420 Vector pos1 = make_vector(dot_product(dpos, v_right), dot_product(dpos, v_up), dot_product(dpos, v_forward));
2421 Vector pos0 = {0, 0, 0};
2426 if ((pos0.x < 0.0)&&(pos1.x > 0.0))
2428 [
self doScriptEvent:OOJSID("shipTraversePositiveX") withArgument:other andReactToAIMessage:@"POSITIVE X TRAVERSE"];
2430 if ((pos0.x > 0.0)&&(pos1.x < 0.0))
2432 [
self doScriptEvent:OOJSID("shipTraverseNegativeX") withArgument:other andReactToAIMessage:@"NEGATIVE X TRAVERSE"];
2434 if ((pos0.y < 0.0)&&(pos1.y > 0.0))
2436 [
self doScriptEvent:OOJSID("shipTraversePositiveY") withArgument:other andReactToAIMessage:@"POSITIVE Y TRAVERSE"];
2438 if ((pos0.y > 0.0)&&(pos1.y < 0.0))
2440 [
self doScriptEvent:OOJSID("shipTraverseNegativeY") withArgument:other andReactToAIMessage:@"NEGATIVE Y TRAVERSE"];
2442 if ((pos0.z < 0.0)&&(pos1.z > 0.0))
2444 [
self doScriptEvent:OOJSID("shipTraversePositiveZ") withArgument:other andReactToAIMessage:@"POSITIVE Z TRAVERSE"];
2446 if ((pos0.z > 0.0)&&(pos1.z < 0.0))
2448 [
self doScriptEvent:OOJSID("shipTraverseNegativeZ") withArgument:other andReactToAIMessage:@"NEGATIVE Z TRAVERSE"];
2450 _primaryTarget = temp;
2451 [closeContactsInfo removeObjectForKey: other_key];
2456 [closeContactsInfo removeObjectForKey: other_key];
2459 [closeContactsTemp release];
2467 if (reportAIMessages && (debugLastBehaviour != behaviour))
2470 debugLastBehaviour = behaviour;
2479 starboard_weapon_temp = fmaxf(starboard_weapon_temp - (
float)(
WEAPON_COOLING_FACTOR * delta_t), 0.0f);
2482 shot_time += delta_t;
2485 if (messageTime > 0.0)
2487 messageTime -= delta_t;
2488 if (messageTime < 0.0) messageTime = 0.0;
2494 double external_temp = 0.0;
2499 double sun_zd = HPdistance2(position, [sun position]);
2501 double alt1 = sun_cr * sun_cr / sun_zd;
2503 if ([sun goneNova]) external_temp *= 100;
2505 if ([
self hasFuelScoop] && alt1 > 0.75 && [
self fuel] < [
self fuelCapacity])
2507 fuel_accumulator += (float)(delta_t * flightSpeed * 0.010 / [
self fuelChargeRate]);
2509 while (fuel_accumulator > 1.0f)
2511 [
self setFuel:[
self fuel] + 1];
2512 fuel_accumulator -= 1.0f;
2513 [
self doScriptEvent:OOJSID("shipScoopedFuel")];
2520 float heatThreshold = [
self heatInsulation] * 100.0f;
2521 if (external_temp > heatThreshold && external_temp > ship_temperature)
2527 ship_temperature += (external_temp - heatThreshold - ship_temperature) * delta_t *
SHIP_COOLING_FACTOR / [
self heatInsulation];
2534 ship_temperature = [[
self owner] temperature];
2538 [
self takeHeatDamage: delta_t * ship_temperature];
2541 if ((energy < maxEnergy * 0.20)&&_showDamage)
2547 next_spark_time -= delta_t;
2548 if (next_spark_time < 0.0)
2559 if ([
self hasCloakingDevice])
2561 if (cloaking_device_active)
2566 [
self deactivateCloakingDevice];
2567 if (energy < 0) energy = 0;
2573 if ([
self hasMilitaryJammer])
2575 if (military_jammer_active)
2580 military_jammer_active = NO;
2581 if (energy < 0) energy = 0;
2587 military_jammer_active = YES;
2598 if (_nextAegisCheck < distanceTravelled || !vector_equal([super velocity],
kZeroVector))
2600 aegis_status = [
self checkForAegis];
2604 _nextAegisCheck = distanceTravelled + 1000.0;
2609 _nextAegisCheck = distanceTravelled + 100.0;
2615 if (!haveExecutedSpawnAction)
2619 if (script !=
nil && (status == STATUS_IN_FLIGHT ||
2620 status == STATUS_LAUNCHING ||
2621 status == STATUS_BEING_SCOOPED ||
2622 (status == STATUS_ACTIVE &&
self == [
UNIVERSE station])
2625 [PLAYER setScriptTarget:self];
2626 [
self doScriptEvent:OOJSID("shipSpawned")];
2627 if ([
self status] != STATUS_DEAD) [PLAYER doScriptEvent:OOJSID("shipSpawned") withArgument:self];
2629 haveExecutedSpawnAction = YES;
2632 if (!haveStartedJSAI && [
self status] != STATUS_LAUNCHING)
2634 haveStartedJSAI = YES;
2635 [
self doScriptEvent:OOJSID("aiStarted")];
2640 if ([
self status] == STATUS_LAUNCHING)
2642 if ([
UNIVERSE getTime] > launch_time + launch_delay)
2644 StationEntity *stationLaunchedFrom = [UNIVERSE nearestEntityMatchingPredicate:IsStationPredicate parameter:NULL relativeToEntity:self];
2645 [
self setStatus:STATUS_IN_FLIGHT];
2647 haveStartedJSAI = YES;
2648 [
self doScriptEvent:OOJSID("aiStarted")];
2649 [
self doScriptEvent:OOJSID("shipLaunchedFromStation") withArgument:stationLaunchedFrom];
2650 [shipAI reactToMessage:@"LAUNCHED OKAY" context:@"launched"];
2656 [
self applyAttitudeChanges:delta_t];
2657 [
self applyThrust:delta_t];
2658 if (energy < maxEnergy)
2660 energy += energy_recharge_rate * delta_t;
2661 if (energy > maxEnergy)
2664 [
self doScriptEvent:OOJSID("shipEnergyBecameFull")];
2665 [shipAI message:@"ENERGY_FULL"];
2669 if ([
self subEntityCount] > 0)
2673 foreach (se, [
self subEntities])
2679 [
super update:delta_t];
2687 if ([
self status] == STATUS_BEING_SCOOPED)
2690 if (behaviour != BEHAVIOUR_TRACTORED || [
self owner] ==
nil || [
self owner] ==
self || [
self owner] ==
NO_TARGET)
2693 [
self setStatus:STATUS_IN_FLIGHT];
2694 behaviour = BEHAVIOUR_IDLE;
2696 [
self setOwner:self];
2697 [shipAI exitStateMachineWithMessage:nil];
2701 if ([
self status] == STATUS_COCKPIT_DISPLAY)
2704 [
self applyAttitudeChanges:delta_t];
2705 GLfloat range2 = 0.1 * HPdistance2(position, _destination) / (collision_radius * collision_radius);
2706 if ((range2 > 1.0)||(velocity.z > 0.0)) range2 = 1.0;
2707 position = HPvector_add(position, vectorToHPVector(vector_multiply_scalar(velocity, range2 * delta_t)));
2711 [
self processBehaviour:delta_t];
2714 if (energy < maxEnergy)
2716 energy += energy_recharge_rate * delta_t;
2717 if (energy > maxEnergy)
2720 [
self doScriptEvent:OOJSID("shipEnergyBecameFull")];
2721 [shipAI message:@"ENERGY_FULL"];
2728 [
self refreshEscortPositions];
2729 if ([
self hasEscorts])
2734 foreach(escort, [
self escortArray])
2739 ShipEntity *leader = [[
self escortGroup] leader];
2740 if (leader !=
nil && ([leader scanClass] != [
self scanClass])) {
2741 OOLog(
@"ship.sanityCheck.failed",
@"Ship %@ escorting %@ with wrong scanclass!",
self, leader);
2742 [[
self escortGroup] removeShip:self];
2743 [
self setEscortGroup:nil];
2753 Quaternion qf = subentityRotationalVelocity;
2754 qf.w *= (1.0 - delta_t);
2758 [
self setOrientation:quaternion_multiply(qf, orientation)];
2762 totalBoundingBox = boundingBox;
2765 [
super update:delta_t];
2769 if ([
self subEntityCount] > 0)
2773 foreach (se, [
self subEntities])
2779 bounding_box_add_vector(&totalBoundingBox, sebb.max);
2780 bounding_box_add_vector(&totalBoundingBox, sebb.min);
2785 if (aiScriptWakeTime > 0 && [
PLAYER clockTimeAdjusted] > aiScriptWakeTime)
2787 aiScriptWakeTime = 0;
2788 [
self doScriptEvent:OOJSID("aiAwoken")];
2795 BOOL applyThrust = YES;
2798 case BEHAVIOUR_TUMBLE :
2799 [
self behaviour_tumble: delta_t];
2802 case BEHAVIOUR_STOP_STILL :
2803 case BEHAVIOUR_STATION_KEEPING :
2804 [
self behaviour_stop_still: delta_t];
2807 case BEHAVIOUR_IDLE :
2808 if ([
self isSubEntity])
2812 [
self behaviour_idle: delta_t];
2815 case BEHAVIOUR_TRACTORED :
2816 [
self behaviour_tractored: delta_t];
2819 case BEHAVIOUR_TRACK_TARGET :
2820 [
self behaviour_track_target: delta_t];
2823 case BEHAVIOUR_INTERCEPT_TARGET :
2824 case BEHAVIOUR_COLLECT_TARGET :
2825 [
self behaviour_intercept_target: delta_t];
2828 case BEHAVIOUR_ATTACK_TARGET :
2829 [
self behaviour_attack_target: delta_t];
2832 case BEHAVIOUR_ATTACK_FLY_TO_TARGET_SIX :
2833 case BEHAVIOUR_ATTACK_FLY_TO_TARGET_TWELVE :
2834 [
self behaviour_fly_to_target_six: delta_t];
2837 case BEHAVIOUR_ATTACK_MINING_TARGET :
2838 [
self behaviour_attack_mining_target: delta_t];
2841 case BEHAVIOUR_ATTACK_FLY_TO_TARGET :
2842 [
self behaviour_attack_fly_to_target: delta_t];
2845 case BEHAVIOUR_ATTACK_FLY_FROM_TARGET :
2846 [
self behaviour_attack_fly_from_target: delta_t];
2849 case BEHAVIOUR_ATTACK_BREAK_OFF_TARGET :
2850 [
self behaviour_attack_break_off_target: delta_t];
2853 case BEHAVIOUR_ATTACK_SLOW_DOGFIGHT :
2854 [
self behaviour_attack_slow_dogfight: delta_t];
2857 case BEHAVIOUR_RUNNING_DEFENSE :
2858 [
self behaviour_running_defense: delta_t];
2861 case BEHAVIOUR_ATTACK_BROADSIDE :
2862 [
self behaviour_attack_broadside: delta_t];
2865 case BEHAVIOUR_ATTACK_BROADSIDE_LEFT :
2866 [
self behaviour_attack_broadside_left: delta_t];
2869 case BEHAVIOUR_ATTACK_BROADSIDE_RIGHT :
2870 [
self behaviour_attack_broadside_right: delta_t];
2873 case BEHAVIOUR_CLOSE_TO_BROADSIDE_RANGE :
2874 [
self behaviour_close_to_broadside_range: delta_t];
2877 case BEHAVIOUR_CLOSE_WITH_TARGET :
2878 [
self behaviour_close_with_target: delta_t];
2881 case BEHAVIOUR_ATTACK_SNIPER :
2882 [
self behaviour_attack_sniper: delta_t];
2885 case BEHAVIOUR_EVASIVE_ACTION :
2886 case BEHAVIOUR_FLEE_EVASIVE_ACTION :
2887 [
self behaviour_evasive_action: delta_t];
2890 case BEHAVIOUR_FLEE_TARGET :
2891 [
self behaviour_flee_target: delta_t];
2894 case BEHAVIOUR_FLY_RANGE_FROM_DESTINATION :
2895 [
self behaviour_fly_range_from_destination: delta_t];
2898 case BEHAVIOUR_FACE_DESTINATION :
2899 [
self behaviour_face_destination: delta_t];
2902 case BEHAVIOUR_LAND_ON_PLANET :
2903 [
self behaviour_land_on_planet: delta_t];
2906 case BEHAVIOUR_FORMATION_FORM_UP :
2907 [
self behaviour_formation_form_up: delta_t];
2910 case BEHAVIOUR_FLY_TO_DESTINATION :
2911 [
self behaviour_fly_to_destination: delta_t];
2914 case BEHAVIOUR_FLY_FROM_DESTINATION :
2915 case BEHAVIOUR_FORMATION_BREAK :
2916 [
self behaviour_fly_from_destination: delta_t];
2919 case BEHAVIOUR_AVOID_COLLISION :
2920 [
self behaviour_avoid_collision: delta_t];
2923 case BEHAVIOUR_TRACK_AS_TURRET :
2925 [
self behaviour_track_as_turret: delta_t];
2928 case BEHAVIOUR_FLY_THRU_NAVPOINTS :
2929 [
self behaviour_fly_thru_navpoints: delta_t];
2932 case BEHAVIOUR_SCRIPTED_AI:
2933 case BEHAVIOUR_SCRIPTED_ATTACK_AI:
2934 [
self behaviour_scripted_ai: delta_t];
2937 case BEHAVIOUR_ENERGY_BOMB_COUNTDOWN:
2946 [
self applyAttitudeChanges:delta_t];
2947 [
self applyThrust:delta_t];
2953- (void)noteFrustration:(NSString *)context
2955 [shipAI reactToMessage:@"FRUSTRATED" context:context];
2956 [
self doScriptEvent:OOJSID("shipAIFrustrated") withArgument:context];
2960- (void)respondToAttackFrom:(
Entity *)from becauseOf:(
Entity *)other
2964 if ([other isKindOfClass:[
ShipEntity class]])
2969 if (![
self hasNewAI])
2974 if ([
self isPolice] && [hunter isPolice])
2982 if (group !=
nil && group == [hunter group])
2986 if (
randf() < (0.8 - (bounty/100)))
2993 if (hunter == groupLeader)
2996 [group removeShip:self];
3001 [group removeShip:hunter];
3015 [
self doScriptEvent:OOJSID("shipBeingAttacked") withArgument:source andReactToAIMessage:@"ATTACKED"];
3016 if ([source isShip]) [(
ShipEntity *)
source doScriptEvent:OOJSID("shipAttackedOther") withArgument:self];
3022- (BOOL) hasOneEquipmentItem:(NSString *)itemKey includeWeapons:(BOOL)includeWeapons whileLoading:(BOOL)loading
3024 if ([
self hasOneEquipmentItem:itemKey includeMissiles:includeWeapons whileLoading:loading])
return YES;
3028 NSString *damaged = [itemKey stringByAppendingString:@"_DAMAGED"];
3029 if ([_equipment containsObject:damaged])
return YES;
3038 if ([
self hasPrimaryWeapon:weaponType])
return YES;
3046- (BOOL) hasOneEquipmentItem:(NSString *)itemKey includeMissiles:(BOOL)includeMissiles whileLoading:(BOOL)loading
3048 if ([_equipment containsObject:itemKey])
return YES;
3052 NSString *damaged = [itemKey stringByAppendingString:@"_DAMAGED"];
3053 if ([_equipment containsObject:damaged])
return YES;
3056 if (includeMissiles && missiles > 0)
3059 if ([itemKey isEqualToString:
@"thargon"]) itemKey =
@"EQ_THARGON";
3060 for (i = 0; i < missiles; i++)
3062 if (missile_list[i] !=
nil && [[missile_list[i] identifier] isEqualTo:itemKey])
return YES;
3072 NSEnumerator *subEntEnum =
nil;
3075 if ([[forward_weapon_type identifier] isEqualToString:[weaponType identifier]] ||
3076 [[aft_weapon_type identifier] isEqualToString:[weaponType identifier]] ||
3077 [[port_weapon_type identifier] isEqualToString:[weaponType identifier]] ||
3078 [[starboard_weapon_type identifier] isEqualToString:[weaponType identifier]])
3083 for (subEntEnum = [
self shipSubEntityEnumerator]; (subEntity = [subEntEnum nextObject]); )
3085 if ([subEntity hasPrimaryWeapon:weaponType])
return YES;
3092- (NSUInteger) countEquipmentItem:(NSString *)eqkey
3095 NSUInteger
count = 0;
3096 foreach (eq, _equipment)
3098 if ([eqkey isEqualToString:eq])
3107- (BOOL) hasEquipmentItem:(
id)equipmentKeys includeWeapons:(BOOL)includeWeapons whileLoading:(BOOL)loading
3110 if ([equipmentKeys isKindOfClass:[NSString
class]])
3112 return [
self hasOneEquipmentItem:equipmentKeys includeWeapons:includeWeapons whileLoading:loading];
3116 NSParameterAssert([equipmentKeys isKindOfClass:[NSArray
class]] || [equipmentKeys isKindOfClass:[NSSet
class]]);
3119 foreach (key, equipmentKeys)
3121 if ([
self hasOneEquipmentItem:key includeWeapons:includeWeapons whileLoading:loading])
return YES;
3129- (BOOL) hasEquipmentItem:(
id)equipmentKeys
3131 return [
self hasEquipmentItem:equipmentKeys includeWeapons:NO whileLoading:NO];
3137- (BOOL) hasEquipmentItemProviding:(NSString *)equipmentType
3139 NSString *key =
nil;
3140 foreach (key, _equipment) {
3141 if ([key isEqualToString:equipmentType])
3149 if (et !=
nil && [et provides:equipmentType])
3159- (NSString *) equipmentItemProviding:(NSString *)equipmentType
3161 NSString *key =
nil;
3162 foreach (key, _equipment) {
3163 if ([key isEqualToString:equipmentType])
3166 return [[key copy] autorelease];
3171 if (et !=
nil && [et provides:equipmentType])
3173 return [[key copy] autorelease];
3181- (BOOL) hasAllEquipment:(
id)equipmentKeys includeWeapons:(BOOL)includeWeapons whileLoading:(BOOL)loading
3183 NSEnumerator *keyEnum =
nil;
3186 if (_equipment ==
nil)
return NO;
3189 if ([equipmentKeys isKindOfClass:[NSString
class]]) equipmentKeys = [NSArray arrayWithObject:equipmentKeys];
3190 else if (![equipmentKeys isKindOfClass:[NSArray
class]] && ![equipmentKeys isKindOfClass:[NSSet
class]])
return NO;
3192 for (keyEnum = [equipmentKeys objectEnumerator]; (key = [keyEnum nextObject]); )
3194 if (![
self hasOneEquipmentItem:key includeWeapons:includeWeapons whileLoading:loading])
return NO;
3201- (BOOL) hasAllEquipment:(
id)equipmentKeys
3203 return [
self hasAllEquipment:equipmentKeys includeWeapons:NO whileLoading:NO];
3207- (BOOL) hasHyperspaceMotor
3209 return hyperspaceMotorSpinTime >= 0;
3213- (float) hyperspaceSpinTime
3215 return hyperspaceMotorSpinTime;
3219- (void) setHyperspaceSpinTime:(
float)new
3221 hyperspaceMotorSpinTime =
new;
3225- (BOOL) canAddEquipment:(NSString *)equipmentKey inContext:(NSString *)context
3227 if ([equipmentKey hasSuffix:
@"_DAMAGED"])
3229 equipmentKey = [equipmentKey substringToIndex:[equipmentKey length] - [@"_DAMAGED" length]];
3232 NSString * lcEquipmentKey = [equipmentKey lowercaseString];
3233 if ([equipmentKey hasSuffix:
@"MISSILE"]||[equipmentKey hasSuffix:
@"MINE"]||([
self isThargoid] && ([lcEquipmentKey hasPrefix:
@"thargon"] || [lcEquipmentKey hasSuffix:
@"thargon"])))
3235 if (missiles >= max_missiles)
return NO;
3240 if (![eqType canCarryMultiple] && [
self hasEquipmentItem:equipmentKey])
return NO;
3241 if (![
self equipmentValidToAdd:equipmentKey inContext:context])
return NO;
3249 return weapon_facings;
3257 if (facing & weapon_facings)
3262 weaponType = forward_weapon_type;
3266 NSEnumerator *subEntEnum = [
self shipSubEntityEnumerator];
3268 while (
isWeaponNone(weaponType) && (subEntity = [subEntEnum nextObject]))
3276 weaponType = aft_weapon_type;
3280 weaponType = port_weapon_type;
3284 weaponType = starboard_weapon_type;
3298 return [
self weaponTypeIDForFacing:facing strict:strict];
3302- (NSArray *) missilesList
3305 return missile_list[0] !=
nil ? [NSArray arrayWithObjects:missile_list count:missiles] :
3310- (NSArray *) passengerListForScripting
3312 return [NSArray array];
3316- (NSArray *) parcelListForScripting
3318 return [NSArray array];
3322- (NSArray *) contractListForScripting
3324 return [NSArray array];
3328- (
OOEquipmentType *) generateMissileEquipmentTypeFrom:(NSString *)role
3343 NSArray *itemInfo = [NSArray arrayWithObjects:@"100", @"100000", @"Missile", role, @"Unidentified missile type.",
3344 [NSDictionary dictionaryWithObjectsAndKeys: @"true", @"is_external_store", nil], nil];
3351- (NSArray *) equipmentListForScripting
3354 NSMutableArray *quip = [NSMutableArray arrayWithCapacity:[eqTypes count]];
3355 NSEnumerator *eqTypeEnum =
nil;
3359 for (eqTypeEnum = [eqTypes objectEnumerator]; (eqType = [eqTypeEnum nextObject]); )
3362 if ([eqType canCarryMultiple])
3364 NSString *damagedIdentifier = [[eqType
identifier] stringByAppendingString:@"_DAMAGED"];
3365 NSUInteger i,
count = 0;
3366 count += [
self countEquipmentItem:[eqType identifier]];
3367 count += [
self countEquipmentItem:damagedIdentifier];
3368 for (i=0;i<
count;i++)
3370 [quip addObject:eqType];
3375 isDamaged = [
self hasEquipmentItem:[[eqType
identifier] stringByAppendingString:@"_DAMAGED"]];
3376 if ([
self hasEquipmentItem:[eqType identifier]] || isDamaged)
3378 [quip addObject:eqType];
3384 if ([
self passengerCapacity] > 0)
3388 [quip addObject:eqType];
3391 return [[quip copy] autorelease];
3395- (BOOL) equipmentValidToAdd:(NSString *)equipmentKey inContext:(NSString *)context
3397 return [
self equipmentValidToAdd:equipmentKey whileLoading:NO inContext:context];
3401- (BOOL) equipmentValidToAdd:(NSString *)equipmentKey whileLoading:(BOOL)loading inContext:(NSString *)context
3404 BOOL validationForDamagedEquipment = NO;
3406 if ([equipmentKey hasSuffix:
@"_DAMAGED"])
3408 equipmentKey = [equipmentKey substringToIndex:[equipmentKey length] - [@"_DAMAGED" length]];
3412 if (eqType ==
nil)
return NO;
3419 if ([
self hasEquipmentItem:[eqType damagedIdentifier]])
3421 validationForDamagedEquipment = YES;
3426 if ([eqType requiresEmptyPylon] && [
self missileCount] >= [
self missileCapacity] && !loading)
return NO;
3427 if ([eqType requiresMountedPylon] && [
self missileCount] == 0 && !loading)
return NO;
3428 if ([
self availableCargoSpace] < [eqType requiredCargoSpace] && !validationForDamagedEquipment && !loading)
return NO;
3429 if ([eqType requiresEquipment] !=
nil && ![
self hasAllEquipment:[eqType requiresEquipment] includeWeapons:YES whileLoading:loading])
return NO;
3430 if ([eqType requiresAnyEquipment] !=
nil && ![
self hasEquipmentItem:[eqType requiresAnyEquipment] includeWeapons:YES whileLoading:loading])
return NO;
3431 if ([eqType incompatibleEquipment] !=
nil && [
self hasEquipmentItem:[eqType incompatibleEquipment] includeWeapons:YES whileLoading:loading])
return NO;
3432 if ([eqType requiresCleanLegalRecord] && [
self legalStatus] != 0 && !loading)
return NO;
3433 if ([eqType requiresNonCleanLegalRecord] && [
self legalStatus] == 0 && !loading)
return NO;
3434 if ([eqType requiresFreePassengerBerth] && [
self passengerCount] >= [
self passengerCapacity])
return NO;
3435 if ([eqType requiresFullFuel] && [
self fuel] < [
self fuelCapacity] && !loading)
return NO;
3436 if ([eqType requiresNonFullFuel] && [
self fuel] >= [
self fuelCapacity] && !loading)
return NO;
3441 if (condition_script !=
nil)
3443 OOJSScript *condScript = [UNIVERSE getConditionScript:condition_script];
3444 if (condScript !=
nil)
3448 JSBool allow_addition =
false;
3452 OK = [condScript
callMethod:OOJSID("allowAwardEquipment")
3457 if (OK) OK = JS_ValueToBoolean(JScontext, result, &allow_addition);
3461 if (OK && !allow_addition)
3472 if ([
self isPlayer])
3474 if (![eqType isAvailableToPlayer])
return NO;
3475 if (![eqType isAvailableToAll])
3480 NSMutableSet *options = [NSMutableSet setWithArray:[shipyardInfo oo_arrayForKey:KEY_OPTIONAL_EQUIPMENT]];
3481 [options addObjectsFromArray:[[shipyardInfo oo_dictionaryForKey:KEY_STANDARD_EQUIPMENT] oo_arrayForKey:KEY_EQUIPMENT_EXTRAS]];
3482 if (![options containsObject:equipmentKey])
return NO;
3487 if (![eqType isAvailableToNPCs])
return NO;
3494- (BOOL) setWeaponMount:(
OOWeaponFacing)facing toWeapon:(NSString *)eqKey
3497 if (weapon_facings & facing)
3503 forward_weapon_type = chosen_weapon;
3507 aft_weapon_type = chosen_weapon;
3511 port_weapon_type = chosen_weapon;
3515 starboard_weapon_type = chosen_weapon;
3531- (BOOL) addEquipmentItem:(NSString *)equipmentKey inContext:(NSString *)context
3533 return [
self addEquipmentItem:equipmentKey withValidation:YES inContext:context];
3537- (BOOL) addEquipmentItem:(NSString *)equipmentKey withValidation:(BOOL)validateAddition inContext:(NSString *)context
3540 NSString *lcEquipmentKey = [equipmentKey lowercaseString];
3541 NSString *damagedKey;
3542 BOOL isEqThargon = [lcEquipmentKey hasSuffix:@"thargon"] || [lcEquipmentKey hasPrefix:@"thargon"];
3543 BOOL isRepairedEquipment = NO;
3545 if([lcEquipmentKey isEqualToString:
@"thargon"]) equipmentKey =
@"EQ_THARGON";
3548 if (validateAddition == YES && ![
self canAddEquipment:equipmentKey inContext:context])
return NO;
3550 if ([equipmentKey hasSuffix:
@"_DAMAGED"])
3558 if (![eqType canCarryMultiple])
3560 damagedKey = [equipmentKey stringByAppendingString:@"_DAMAGED"];
3561 if ([_equipment containsObject:damagedKey])
3563 [_equipment removeObject:damagedKey];
3564 isRepairedEquipment = YES;
3570 if (eqType ==
nil)
return NO;
3573 if ([eqType isMissileOrMine] || ([
self isThargoid] && isEqThargon))
3575 if (missiles >= max_missiles)
return NO;
3577 missile_list[missiles] = eqType;
3583 if(isEqThargon)
return NO;
3586 if([equipmentKey hasPrefix:
@"EQ_WEAPON"] && ![equipmentKey hasSuffix:
@"_DAMAGED"])
3592 if (_equipment ==
nil) _equipment = [[NSMutableArray alloc] init];
3594 if (![equipmentKey isEqualToString:
@"EQ_PASSENGER_BERTH"] && !isRepairedEquipment)
3597 equipment_weight += [eqType requiredCargoSpace];
3598 if (equipment_weight > max_cargo)
3601 equipment_weight -= [eqType requiredCargoSpace];
3609 if ([equipmentKey isEqual:
@"EQ_CARGO_BAY"])
3611 max_cargo += extra_cargo;
3613 else if([equipmentKey isEqualToString:
@"EQ_SHIELD_BOOSTER"])
3615 maxEnergy += 256.0f;
3617 if([equipmentKey isEqualToString:
@"EQ_SHIELD_ENHANCER"])
3619 maxEnergy += 256.0f;
3620 energy_recharge_rate *= 1.5;
3624 [_equipment addObject:equipmentKey];
3625 [
self doScriptEvent:OOJSID("equipmentAdded") withArgument:equipmentKey];
3630- (NSEnumerator *) equipmentEnumerator
3632 return [_equipment objectEnumerator];
3636- (NSUInteger) equipmentCount
3638 return [_equipment count];
3642- (void) removeEquipmentItem:(NSString *)equipmentKey
3644 NSString *equipmentTypeCheckKey = equipmentKey;
3645 NSString *lcEquipmentKey = [equipmentKey lowercaseString];
3646 NSUInteger equipmentIndex = NSNotFound;
3648 if ([equipmentKey hasSuffix:
@"_DAMAGED"])
3650 equipmentTypeCheckKey = [equipmentKey substringToIndex:[equipmentKey length] - [@"_DAMAGED" length]];
3653 if (eqType ==
nil)
return;
3655 if ([eqType isMissileOrMine] || ([
self isThargoid] && ([lcEquipmentKey hasSuffix:
@"thargon"] || [lcEquipmentKey hasPrefix:
@"thargon"])))
3657 [
self removeExternalStore:eqType];
3661 if ([_equipment containsObject:equipmentKey])
3663 if (![equipmentKey isEqualToString:
@"EQ_PASSENGER_BERTH"])
3665 equipment_weight -= [eqType requiredCargoSpace];
3668 if ([equipmentKey isEqualToString:
@"EQ_CLOAKING_DEVICE"])
3670 if ([
self isCloaked]) [
self setCloaked:NO];
3675 if([equipmentKey isEqualToString:
@"EQ_SHIELD_BOOSTER"])
3677 maxEnergy -= 256.0f;
3678 if (maxEnergy < energy) energy = maxEnergy;
3680 else if([equipmentKey isEqualToString:
@"EQ_SHIELD_ENHANCER"])
3682 maxEnergy -= 256.0f;
3683 energy_recharge_rate /= 1.5;
3684 if (maxEnergy < energy) energy = maxEnergy;
3686 else if ([equipmentKey isEqual:
@"EQ_CARGO_BAY"])
3688 max_cargo -= extra_cargo;
3693 if (![equipmentKey hasSuffix:
@"_DAMAGED"] && ![eqType canCarryMultiple])
3695 NSString *damagedKey = [equipmentKey stringByAppendingString:@"_DAMAGED"];
3696 if ([_equipment containsObject:damagedKey])
3698 equipmentIndex = [_equipment indexOfObject:damagedKey];
3699 if (equipmentIndex != NSNotFound)
3702 [_equipment removeObjectAtIndex:equipmentIndex];
3704 equipment_weight -= [eqType requiredCargoSpace];
3707 equipmentIndex = [_equipment indexOfObject:equipmentKey];
3708 if (equipmentIndex != NSNotFound)
3710 [_equipment removeObjectAtIndex:equipmentIndex];
3713 [
self doScriptEvent:OOJSID("equipmentRemoved") withArgument:equipmentKey];
3716 if ([
self isPlayer] && [
self status] == STATUS_AUTOPILOT_ENGAGED && ![
self hasDockingComputer])
3722 if ([_equipment
count] == 0) [
self removeAllEquipment];
3732 for (i = 0; i < missiles; i++)
3734 if ([[missile_list[i] identifier] isEqualTo:identifier])
3737 while ( ++i < missiles ) missile_list[i - 1] = missile_list[i];
3749 NSString *eqRole =
nil;
3750 NSString *shipKey =
nil;
3753 BOOL isRandomMissile = [role isEqualToString:@"missile"];
3755 if (isRandomMissile)
3759 shipKey = [UNIVERSE randomShipKeyForRoleRespectingConditions:role];
3762 OOLogWARN(
@"ship.setUp.missiles",
@"%@ \"%@\
" used in ship \"%@\" needs a valid %@.plist entry.%@",
@"random missile", shipKey, [
self name],
@"shipdata",
@"Trying another missile.");
3768 shipKey = [UNIVERSE randomShipKeyForRoleRespectingConditions:role];
3771 OOLogWARN(
@"ship.setUp.missiles",
@"%@ \"%@\
" used in ship \"%@\" needs a valid %@.plist entry.%@",
@"missile_role", role, [
self name],
@"shipdata",
@" Using defaults instead.");
3780 missile = [UNIVERSE newShipWithName:shipKey];
3783 if (isRandomMissile)
3784 OOLogWARN(
@"ship.setUp.missiles",
@"%@ \"%@\
" used in ship \"%@\" needs a valid %@.plist entry.%@",
@"random missile", shipKey, [
self name],
@"shipdata",
@"Trying another missile.");
3786 OOLogWARN(
@"ship.setUp.missiles",
@"%@ \"%@\
" used in ship \"%@\" needs a valid %@.plist entry.%@",
@"missile_role", role, [
self name],
@"shipdata",
@" Using defaults instead.");
3789 if (isRandomMissile)
return [
self verifiedMissileTypeFromRole:role];
3796 NSEnumerator *enumerator = [[[missile
roleSet]
roles] objectEnumerator];
3798 while ((value = [enumerator nextObject]))
3800 role = (NSString *)value;
3803 if ([missileType isMissileOrMine])
break;
3806 if (![missileType isMissileOrMine])
3816 OOLogWARN(
@"ship.setUp.missiles",
@"%@ \"%@\
" used in ship \"%@\" needs a valid %@.plist entry.%@", (isRandomMissile ?
@"random missile" :
@"missile_role"), role, [
self name],
@"equipment",
@" Enabling compatibility mode.");
3817 missileType = [
self generateMissileEquipmentTypeFrom:role];
3825 if ([eqRole isEqualToString:
@""])
3828 if (isRandomMissile)
return [
self verifiedMissileTypeFromRole:role];
3841 NSString *role =
nil;
3842 double chance =
randf();
3843 BOOL thargoidMissile = NO;
3845 if ([
self isThargoid])
3847 if (_missileRole !=
nil) missileType = [
self verifiedMissileTypeFromRole:_missileRole];
3848 if (missileType ==
nil) {
3849 _missileRole =
@"EQ_THARGON";
3850 missileType = [
self verifiedMissileTypeFromRole:_missileRole];
3857 float randomSelectionChance = chance;
3858 if(![
self hasAutoWeapons]) randomSelectionChance = 0.0f;
3859 if (randomSelectionChance < 0.9f && _missileRole !=
nil)
3861 missileType = [
self verifiedMissileTypeFromRole:_missileRole];
3864 if (missileType ==
nil)
3866 if (chance < 0.9f && _missileRole !=
nil)
3872 if (chance > 0.8f) role =
@"missile";
3874 else role =
@"EQ_MISSILE";
3876 missileType = [
self verifiedMissileTypeFromRole:role];
3880 if (missileType ==
nil)
OOLogERR(
@"ship.setUp.missiles",
@"could not resolve missile / mine type for ship \"%@\
". Original missile role:\"%@\".", [
self name],_missileRole);
3882 role = [[missileType
identifier] lowercaseString];
3883 thargoidMissile = [
self isThargoid] && ([role hasSuffix:@"thargon"] || [role hasPrefix:@"thargon"]);
3885 if (thargoidMissile || (!thargoidMissile && [missileType isMissileOrMine]))
3891 OOLogWARN(
@"ship.setUp.missiles",
@"missile_role \"%@\
" is not a valid missile / mine type for ship \"%@\".%@", [missileType identifier] , [
self name],
@" No missile selected.");
3897- (void) removeAllEquipment
3899 [_equipment release];
3911- (NSUInteger) parcelCount
3917- (NSUInteger) passengerCount
3923- (NSUInteger) passengerCapacity
3929- (NSUInteger) missileCount
3935- (NSUInteger) missileCapacity
3937 return max_missiles;
3941- (NSUInteger) extraCargo
3950 return [
self hasEquipmentItemProviding:@"EQ_FUEL_SCOOPS"] || [
self hasEquipmentItemProviding:@"EQ_CARGO_SCOOPS"];
3954- (BOOL) hasFuelScoop
3956 return [
self hasEquipmentItemProviding:@"EQ_FUEL_SCOOPS"];
3961- (BOOL) hasCargoScoop
3963 return [
self hasEquipmentItemProviding:@"EQ_CARGO_SCOOPS"];
3969 return [
self hasEquipmentItemProviding:@"EQ_ECM"];
3973- (BOOL) hasCloakingDevice
3976 return [
self hasEquipmentItem:@"EQ_CLOAKING_DEVICE"];
3980- (BOOL) hasMilitaryScannerFilter
3983 return [
self hasEquipmentItemProviding:@"EQ_MILITARY_SCANNER_FILTER"];
3990- (BOOL) hasMilitaryJammer
3993 return [
self hasEquipmentItemProviding:@"EQ_MILITARY_JAMMER"];
4000- (BOOL) hasExpandedCargoBay
4003 return [
self hasEquipmentItem:@"EQ_CARGO_BAY"];
4007- (BOOL) hasShieldBooster
4010 return [
self hasEquipmentItem:@"EQ_SHIELD_BOOSTER"];
4014- (BOOL) hasMilitaryShieldEnhancer
4017 return [
self hasEquipmentItem:@"EQ_NAVAL_SHIELD_BOOSTER"];
4021- (BOOL) hasHeatShield
4023 return [
self hasEquipmentItemProviding:@"EQ_HEAT_SHIELD"];
4027- (BOOL) hasFuelInjection
4029 return [
self hasEquipmentItemProviding:@"EQ_FUEL_INJECTION"];
4033- (BOOL) hasCascadeMine
4038 return [
self hasEquipmentItem:@"EQ_QC_MINE" includeWeapons:YES whileLoading:NO];
4042- (BOOL) hasEscapePod
4044 return [
self hasEquipmentItemProviding:@"EQ_ESCAPE_POD"];
4048- (BOOL) hasDockingComputer
4050 return [
self hasEquipmentItemProviding:@"EQ_DOCK_COMP"];
4054- (BOOL) hasGalacticHyperdrive
4056 return [
self hasEquipmentItemProviding:@"EQ_GAL_DRIVE"];
4060- (float) shieldBoostFactor
4062 float boostFactor = 1.0f;
4063 if ([
self hasShieldBooster]) boostFactor += 1.0f;
4064 if ([
self hasMilitaryShieldEnhancer]) boostFactor += 1.0f;
4072- (float) maxForwardShieldLevel
4078- (float) maxAftShieldLevel
4084- (float) shieldRechargeRate
4086 return [
self hasMilitaryShieldEnhancer] ? 3.0f : 2.0f;
4090- (double) maxHyperspaceDistance
4095- (float) afterburnerFactor
4097 return afterburner_speed_factor;
4101- (float) afterburnerRate
4103 return afterburner_rate;
4107- (void) setAfterburnerFactor:(GLfloat)new
4109 afterburner_speed_factor =
new;
4113- (void) setAfterburnerRate:(GLfloat)new
4115 afterburner_rate =
new;
4125- (void) setMaxThrust:(GLfloat)new
4141- (void) behaviour_stop_still:(
double) delta_t
4146 [
self applySticks:delta_t];
4153- (void) behaviour_idle:(
double) delta_t
4156 if ((!isStation)&&(scanClass != CLASS_BUOY))
4162 stick_roll = flightRoll;
4164 if (scanClass != CLASS_BUOY)
4170 stick_pitch = flightPitch;
4172 [
self applySticks:delta_t];
4178- (void) behaviour_tumble:(
double) delta_t
4180 [
self applySticks:delta_t];
4186- (void) behaviour_tractored:(
double) delta_t
4188 desired_range = collision_radius * 2.0;
4190 if ((hauler)&&([hauler isShip]))
4193 double distance = [
self rangeToDestination];
4194 if (distance < desired_range)
4196 [
self performTumble];
4197 [
self setStatus:STATUS_IN_FLIGHT];
4203 Vector dv = vector_between([
self velocity], [hauler velocity]);
4204 GLfloat moment = delta_t * 0.25 * tf;
4205 velocity.x += moment * dv.
x;
4206 velocity.y += moment * dv.
y;
4207 velocity.z += moment * dv.z;
4210 HPVector dp = HPvector_between(position, _destination);
4211 moment = delta_t * 0.5 * tf;
4212 velocity.x += moment * dp.
x;
4213 velocity.y += moment * dp.
y;
4214 velocity.z += moment * dp.z;
4216 GLfloat d2 = HPmagnitude2(dp);
4217 moment = (d2 > 0.0)? delta_t * 5.0 * tf / d2 : 0.0;
4220 velocity.x += moment * dp.
x;
4221 velocity.y += moment * dp.
y;
4222 velocity.z += moment * dp.z;
4225 if ([
self status] == STATUS_BEING_SCOOPED)
4227 BOOL lost_contact = (distance > hauler->
collision_radius + collision_radius + 250.0f);
4228 if ([hauler isPlayer])
4246 [
self setStatus:STATUS_IN_FLIGHT];
4247 behaviour = BEHAVIOUR_IDLE;
4248 [
self setThrust:[
self maxThrust]];
4250 [
self setOwner:self];
4251 [shipAI exitStateMachineWithMessage:nil];
4254 else if ([hauler isPlayer])
4264 desired_speed = 0.0;
4271- (void) behaviour_track_target:(
double) delta_t
4273 if ([
self primaryTarget] ==
nil)
4275 [
self noteLostTargetAndGoIdle];
4278 [
self trackPrimaryTarget:delta_t:NO];
4279 if ([
self hasProximityAlertIgnoringTarget:YES])
4281 [
self avoidCollision];
4287- (void) behaviour_intercept_target:(
double) delta_t
4289 double range = [
self rangeToPrimaryTarget];
4290 if (behaviour == BEHAVIOUR_INTERCEPT_TARGET)
4292 desired_speed = maxFlightSpeed;
4293 if (range < desired_range)
4295 [shipAI reactToMessage:@"DESIRED_RANGE_ACHIEVED" context:@"BEHAVIOUR_INTERCEPT_TARGET"];
4296 [
self doScriptEvent:OOJSID("shipAchievedDesiredRange")];
4299 desired_speed = maxFlightSpeed * [
self trackPrimaryTarget:delta_t:NO];
4307 if (!target || [target scanClass] != CLASS_CARGO || [target cargoType] ==
CARGO_NOT_CARGO)
4309 [
self noteLostTargetAndGoIdle];
4312 double target_speed = [target
speed];
4313 double eta = range / (flightSpeed - target_speed);
4314 double last_success_factor = success_factor;
4315 double last_distance = last_success_factor;
4316 double distance = [
self rangeToDestination];
4317 success_factor = distance;
4320 double minTurnSpeedFactor = 0.005 * max_flight_pitch * max_flight_roll;
4322 if ((eta < slowdownTime)&&(flightSpeed > maxFlightSpeed * minTurnSpeedFactor))
4323 desired_speed = flightSpeed * 0.75;
4325 desired_speed = maxFlightSpeed;
4327 if (desired_speed < target_speed)
4329 desired_speed += target_speed;
4330 if (target_speed > maxFlightSpeed)
4332 [
self noteLostTargetAndGoIdle];
4336 if (desired_speed > maxFlightSpeed)
4338 desired_speed = maxFlightSpeed;
4343 [
self trackDestination: delta_t : NO];
4346 if (distance < last_distance)
4348 frustration -= delta_t;
4349 if (frustration < 0.0)
4354 frustration += delta_t * 0.9;
4355 if (frustration > 10.0)
4357 [
self noteFrustration:@"BEHAVIOUR_INTERCEPT_TARGET"];
4362 if ([
self hasProximityAlertIgnoringTarget:YES])
4364 [
self avoidCollision];
4371- (void) behaviour_attack_break_off_target:(
double) delta_t
4373 if (![
self canStillTrackPrimaryTarget])
4375 [
self noteLostTargetAndGoIdle];
4378 BOOL canBurn = [
self hasFuelInjection] && (fuel >
MIN_FUEL);
4379 float max_available_speed = maxFlightSpeed;
4380 double range = [
self rangeToPrimaryTarget];
4381 if (canBurn) max_available_speed *= [
self afterburnerFactor];
4383 desired_speed = max_available_speed;
4385 Entity* target = [
self primaryTarget];
4387 if (desired_speed > maxFlightSpeed)
4389 double target_speed = [target
speed];
4390 if (desired_speed > target_speed * 3.0)
4392 desired_speed = maxFlightSpeed;
4396 if (cloakAutomatic) [
self activateCloakingDevice];
4397 if ([
self hasProximityAlertIgnoringTarget:NO])
4399 [
self avoidCollision];
4403 frustration += delta_t;
4406 desired_speed = maxFlightSpeed / 2.0;
4408 double aspect = [
self approachAspectToPrimaryTarget];
4409 if (range > 3000.0 || ([target isShip] && [(
ShipEntity*)target primaryTarget] !=
self) || frustration - floor(frustration) > fmin(1.6/max_flight_roll,aspect))
4411 [
self trackPrimaryTarget:delta_t:YES];
4416 [
self evasiveAction:delta_t];
4421 behaviour = BEHAVIOUR_ATTACK_TARGET;
4425 behaviour = BEHAVIOUR_ATTACK_SLOW_DOGFIGHT;
4432 behaviour = BEHAVIOUR_ATTACK_SLOW_DOGFIGHT;
4436 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET;
4444- (void) behaviour_attack_slow_dogfight:(
double) delta_t
4446 if (![
self canStillTrackPrimaryTarget])
4448 [
self noteLostTargetAndGoIdle];
4451 if ([
self hasProximityAlertIgnoringTarget:YES])
4453 [
self avoidCollision];
4456 double range = [
self rangeToPrimaryTarget];
4458 double aspect = [
self approachAspectToPrimaryTarget];
4459 if (range < 2.5*(collision_radius+target->
collision_radius) && [
self proximityAlert] == target && aspect > 0) {
4460 desired_speed = maxFlightSpeed;
4461 [
self avoidCollision];
4466 behaviour = BEHAVIOUR_ATTACK_TARGET;
4468 else if (aspect < -0.5)
4471 desired_speed = fmin(maxFlightSpeed * 0.5,[target speed]*0.5);
4473 else if (aspect < 0.3)
4476 desired_speed = maxFlightSpeed * 0.1;
4481 desired_speed = maxFlightSpeed * fmin(aspect*2.5,1.0);
4485 behaviour = BEHAVIOUR_ATTACK_BREAK_OFF_TARGET;
4489 frustration += delta_t;
4493 frustration -= delta_t;
4495 if (frustration > 10.0)
4497 desired_speed /= 2.0;
4499 else if (frustration < 0.0)
4502 [
self trackPrimaryTarget:delta_t:NO];
4504 if (missiles) [
self considerFiringMissile:delta_t];
4506 if (cloakAutomatic) [
self activateCloakingDevice];
4511- (void) behaviour_evasive_action:(
double) delta_t
4513 BOOL canBurn = [
self hasFuelInjection] && (fuel >
MIN_FUEL);
4514 float max_available_speed = maxFlightSpeed;
4516 if (canBurn) max_available_speed *= [
self afterburnerFactor];
4517 desired_speed = max_available_speed;
4518 if (desired_speed > maxFlightSpeed)
4521 double target_speed = [target
speed];
4522 if (desired_speed > target_speed)
4524 desired_speed = maxFlightSpeed;
4528 if (cloakAutomatic) [
self activateCloakingDevice];
4529 if ([
self proximityAlert] !=
nil)
4531 [
self avoidCollision];
4535 [
self evasiveAction:delta_t];
4537 frustration += delta_t;
4539 if (frustration > 0.5)
4541 if (behaviour == BEHAVIOUR_FLEE_EVASIVE_ACTION)
4543 [
self setEvasiveJink:400.0];
4544 behaviour = BEHAVIOUR_FLEE_TARGET;
4548 behaviour = BEHAVIOUR_ATTACK_TARGET;
4555 [
self fireMainWeapon:[
self rangeToPrimaryTarget]];
4560- (void) behaviour_attack_target:(
double) delta_t
4562 double range = [
self rangeToPrimaryTarget];
4564 if (cloakAutomatic) [
self activateCloakingDevice];
4573 OOWeaponType forward_weapon_real_type = forward_weapon_type;
4574 GLfloat forward_weapon_real_temp = forward_weapon_temp;
4579 BOOL hasTurrets = NO;
4580 NSEnumerator *subEnum = [
self shipSubEntityEnumerator];
4582 while (
isWeaponNone(forward_weapon_real_type) && (se = [subEnum nextObject]))
4586 if (se->
behaviour == BEHAVIOUR_TRACK_AS_TURRET)
4591 if (
isWeaponNone(forward_weapon_real_type) && hasTurrets)
4598 if ([forward_weapon_real_type isTurretLaser])
4600 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET_TWELVE;
4611 BOOL weapons_heating = NO;
4612 if (!forward_weapon_ready && !aft_weapon_ready && !port_weapon_ready && !starboard_weapon_ready)
4614 weapons_heating = YES;
4621 Entity* target = [
self primaryTarget];
4622 double aspect = [
self approachAspectToPrimaryTarget];
4624 if (!forward_weapon_ready && !aft_weapon_ready && !port_weapon_ready && !starboard_weapon_ready)
4633 behaviour = BEHAVIOUR_ATTACK_FLY_FROM_TARGET;
4635 else if (aspect > 0)
4641 behaviour = BEHAVIOUR_EVASIVE_ACTION;
4645 behaviour = BEHAVIOUR_ATTACK_FLY_FROM_TARGET;
4651 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET;
4659 behaviour = BEHAVIOUR_ATTACK_FLY_FROM_TARGET;
4661 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET;
4669 float relativeSpeed = magnitude(vector_subtract([
self velocity], [target velocity]));
4670 [
self setEvasiveJink:(range + COMBAT_JINK_OFFSET - relativeSpeed / max_flight_pitch)];
4671 behaviour = BEHAVIOUR_ATTACK_FLY_FROM_TARGET;
4679 if (nearby && aft_weapon_ready)
4682 behaviour = BEHAVIOUR_RUNNING_DEFENSE;
4684 else if (nearby && (port_weapon_ready || starboard_weapon_ready))
4687 behaviour = BEHAVIOUR_ATTACK_BROADSIDE;
4700 float relativeSpeed = magnitude(vector_subtract([
self velocity], [target velocity]));
4701 [
self setEvasiveJink:(range + COMBAT_JINK_OFFSET - relativeSpeed / max_flight_pitch)];
4706 behaviour = BEHAVIOUR_ATTACK_BREAK_OFF_TARGET;
4710 behaviour = BEHAVIOUR_ATTACK_FLY_FROM_TARGET;
4713 else if (forward_weapon_ready)
4720 behaviour = BEHAVIOUR_ATTACK_SNIPER;
4725 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET_SIX;
4729 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET_TWELVE;
4733 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET;
4736 else if (port_weapon_ready || starboard_weapon_ready)
4739 behaviour = BEHAVIOUR_ATTACK_BROADSIDE;
4741 else if (aft_weapon_ready && midrange)
4744 behaviour = BEHAVIOUR_RUNNING_DEFENSE;
4749 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET;
4759- (void) behaviour_attack_broadside:(
double) delta_t
4761 BOOL canBurn = [
self hasFuelInjection] && (fuel >
MIN_FUEL);
4762 float max_available_speed = maxFlightSpeed;
4763 double range = [
self rangeToPrimaryTarget];
4764 if (canBurn) max_available_speed *= [
self afterburnerFactor];
4766 if (cloakAutomatic) [
self activateCloakingDevice];
4768 if (![
self canStillTrackPrimaryTarget])
4770 [
self noteLostTargetAndGoIdle];
4774 desired_speed = max_available_speed;
4777 behaviour = BEHAVIOUR_ATTACK_TARGET;
4781 if (port_weapon_temp < starboard_weapon_temp)
4785 behaviour = BEHAVIOUR_ATTACK_BROADSIDE_RIGHT;
4786 [
self setWeaponDataFromType:starboard_weapon_type];
4790 behaviour = BEHAVIOUR_ATTACK_BROADSIDE_LEFT;
4791 [
self setWeaponDataFromType:port_weapon_type];
4798 behaviour = BEHAVIOUR_ATTACK_BROADSIDE_RIGHT;
4799 [
self setWeaponDataFromType:starboard_weapon_type];
4803 behaviour = BEHAVIOUR_ATTACK_BROADSIDE_LEFT;
4804 [
self setWeaponDataFromType:port_weapon_type];
4808 if (weapon_damage == 0.0)
4810 behaviour = BEHAVIOUR_ATTACK_TARGET;
4812 else if (range > 0.9 * weaponRange)
4814 behaviour = BEHAVIOUR_CLOSE_TO_BROADSIDE_RANGE;
4825- (void) behaviour_attack_broadside_left:(
double) delta_t
4827 [
self behaviour_attack_broadside_target:delta_t leftside:YES];
4831- (void) behaviour_attack_broadside_right:(
double) delta_t
4833 [
self behaviour_attack_broadside_target:delta_t leftside:NO];
4837- (void) behaviour_attack_broadside_target:(
double) delta_t leftside:(BOOL) leftside
4839 BOOL canBurn = [
self hasFuelInjection] && (fuel >
MIN_FUEL);
4840 float max_available_speed = maxFlightSpeed;
4841 double range = [
self rangeToPrimaryTarget];
4842 if (canBurn) max_available_speed *= [
self afterburnerFactor];
4843 if ([
self primaryTarget] ==
nil)
4845 [
self noteLostTargetAndGoIdle];
4851 behaviour = BEHAVIOUR_CLOSE_TO_BROADSIDE_RANGE;
4858 if (![
self hasProximityAlertIgnoringTarget:YES])
4860 behaviour = BEHAVIOUR_ATTACK_TARGET;
4864 [
self avoidCollision];
4870 if (![
self canStillTrackPrimaryTarget])
4872 [
self noteLostTargetAndGoIdle];
4878 BOOL isUsingAfterburner = canBurn && (flightSpeed > maxFlightSpeed);
4879 double slow_down_range = currentWeaponRange *
COMBAT_WEAPON_RANGE_FACTOR * ((isUsingAfterburner)? 3.0 * [
self afterburnerFactor] : 1.0);
4881 if (range <= slow_down_range)
4882 desired_speed = fmin(0.8 * maxFlightSpeed, fmax((2.0-frustration)*maxFlightSpeed, 0.1 * maxFlightSpeed));
4884 desired_speed = max_available_speed;
4886 double last_success_factor = success_factor;
4887 success_factor = [
self trackSideTarget:delta_t:leftside];
4894 behaviour = BEHAVIOUR_ATTACK_BROADSIDE_RIGHT;
4898 behaviour = BEHAVIOUR_ATTACK_TARGET;
4905 behaviour = BEHAVIOUR_ATTACK_BROADSIDE_LEFT;
4909 behaviour = BEHAVIOUR_ATTACK_TARGET;
4915 if ((success_factor > 0.999)||(success_factor > last_success_factor))
4917 frustration -= delta_t;
4918 if (frustration < 0.0)
4923 frustration += delta_t;
4924 if (frustration > 3.0)
4927 [
self noteFrustration:@"BEHAVIOUR_ATTACK_BROADSIDE"];
4928 [
self setEvasiveJink:1000.0];
4929 behaviour = BEHAVIOUR_ATTACK_FLY_FROM_TARGET;
4931 desired_speed = maxFlightSpeed;
4935 if (missiles) [
self considerFiringMissile:delta_t];
4937 if (cloakAutomatic) [
self activateCloakingDevice];
4940 [
self firePortWeapon:range];
4944 [
self fireStarboardWeapon:range];
4951 behaviour = BEHAVIOUR_ATTACK_TARGET;
4956- (void) behaviour_close_to_broadside_range:(
double) delta_t
4958 double range = [
self rangeToPrimaryTarget];
4959 if ([
self proximityAlert] !=
nil)
4961 if ([
self proximityAlert] == [
self primaryTarget])
4963 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET;
4964 [
self behaviour_attack_fly_from_target: delta_t];
4968 [
self avoidCollision];
4972 if (![
self canStillTrackPrimaryTarget])
4974 [
self noteLostTargetAndGoIdle];
4978 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET_TWELVE;
4979 [
self behaviour_fly_to_target_six:delta_t];
4982 [
self setWeaponDataFromType:port_weapon_type];
4986 [
self setWeaponDataFromType:starboard_weapon_type];
4990 behaviour = BEHAVIOUR_ATTACK_BROADSIDE;
4994 behaviour = BEHAVIOUR_CLOSE_TO_BROADSIDE_RANGE;
4999- (void) behaviour_close_with_target:(
double) delta_t
5001 double range = [
self rangeToPrimaryTarget];
5002 if ([
self proximityAlert] !=
nil)
5004 if ([
self proximityAlert] == [
self primaryTarget])
5006 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET;
5007 [
self behaviour_attack_fly_from_target: delta_t];
5011 [
self avoidCollision];
5015 if (![
self canStillTrackPrimaryTarget])
5017 [
self noteLostTargetAndGoIdle];
5020 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET_TWELVE;
5021 double saved_frustration = frustration;
5022 [
self behaviour_fly_to_target_six:delta_t];
5023 frustration = saved_frustration;
5024 frustration += delta_t;
5025 if (range <= COMBAT_IN_RANGE_FACTOR * weaponRange || frustration > 5.0)
5027 behaviour = BEHAVIOUR_ATTACK_TARGET;
5031 behaviour = BEHAVIOUR_CLOSE_WITH_TARGET;
5038- (void) behaviour_attack_sniper:(
double) delta_t
5040 if (![
self canStillTrackPrimaryTarget])
5042 [
self noteLostTargetAndGoIdle];
5045 Entity* rawTarget = [
self primaryTarget];
5046 if (![rawTarget isShip])
5049 [
self noteLostTargetAndGoIdle];
5054 double range = [
self rangeToPrimaryTarget];
5055 float max_available_speed = maxFlightSpeed;
5059 behaviour = BEHAVIOUR_ATTACK_TARGET;
5063 if (range > weaponRange || range > scannerRange * 0.8)
5065 BOOL canBurn = [
self hasFuelInjection] && (fuel >
MIN_FUEL);
5066 if (canBurn && [target weaponRange] > weaponRange && range > weaponRange)
5070 max_available_speed *= [
self afterburnerFactor];
5072 desired_speed = max_available_speed;
5076 desired_speed = max_available_speed / 10.0f;
5079 double last_success_factor = success_factor;
5080 success_factor = [
self trackPrimaryTarget:delta_t:NO];
5082 if ((success_factor > 0.999)||(success_factor > last_success_factor))
5084 frustration -= delta_t;
5085 if (frustration < 0.0)
5090 frustration += delta_t;
5091 if (frustration > 3.0)
5093 [
self noteFrustration:@"BEHAVIOUR_ATTACK_SNIPER"];
5094 [
self setEvasiveJink:1000.0];
5095 behaviour = BEHAVIOUR_ATTACK_TARGET;
5097 desired_speed = maxFlightSpeed;
5103 if (missiles) [
self considerFiringMissile:delta_t];
5105 if (cloakAutomatic) [
self activateCloakingDevice];
5106 [
self fireMainWeapon:range];
5110 behaviour = BEHAVIOUR_ATTACK_TARGET;
5116- (void) behaviour_fly_to_target_six:(
double) delta_t
5118 BOOL canBurn = [
self hasFuelInjection] && (fuel >
MIN_FUEL);
5119 float max_available_speed = maxFlightSpeed;
5120 double range = [
self rangeToPrimaryTarget];
5121 if (canBurn) max_available_speed *= [
self afterburnerFactor];
5124 if ([
self proximityAlert] !=
nil)
5126 if ([
self proximityAlert] == [
self primaryTarget])
5128 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET;
5129 [
self behaviour_attack_fly_from_target: delta_t];
5133 [
self avoidCollision];
5137 if (![
self canStillTrackPrimaryTarget])
5139 [
self noteLostTargetAndGoIdle];
5144 BOOL isUsingAfterburner = canBurn && (flightSpeed > maxFlightSpeed);
5145 BOOL closeQuickly = (canBurn && range > weaponRange);
5146 double slow_down_range = weaponRange *
COMBAT_WEAPON_RANGE_FACTOR * ((isUsingAfterburner)? 3.0 * [
self afterburnerFactor] : 1.0);
5151 double back_off_range = weaponRange *
COMBAT_OUT_RANGE_FACTOR * ((isUsingAfterburner)? 3.0 * [
self afterburnerFactor] : 1.0);
5152 Entity* rawTarget = [
self primaryTarget];
5153 if (![rawTarget isShip])
5155 [
self noteLostTargetAndGoIdle];
5159 double target_speed = [target
speed];
5160 double last_success_factor = success_factor;
5161 double distance = [
self rangeToDestination];
5162 success_factor = distance;
5164 if (range < slow_down_range && (behaviour == BEHAVIOUR_ATTACK_FLY_TO_TARGET_SIX))
5166 if (range < back_off_range)
5168 desired_speed = fmax(0.9 * target_speed, 0.4 * maxFlightSpeed);
5172 desired_speed = fmax(target_speed * 1.2, maxFlightSpeed);
5176 if ((range < 0.5 * distance)&&(behaviour == BEHAVIOUR_ATTACK_FLY_TO_TARGET_SIX))
5177 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET_TWELVE;
5181 if (range < back_off_range)
5183 desired_speed = fmax(0.9 * target_speed, 0.8 * maxFlightSpeed);
5187 desired_speed = max_available_speed;
5194 if (distance < 750.0 || (target_speed < 0.2 && ![
self isThargoid] && ([
self universalID] & 14) > 4))
5196 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET;
5198 desired_speed = fmax(target_speed, 0.4 * maxFlightSpeed);
5202 if (behaviour == BEHAVIOUR_ATTACK_FLY_TO_TARGET_SIX)
5206 _destination = [target
distance_six:0.5 * weaponRange];
5209 if (behaviour == BEHAVIOUR_ATTACK_FLY_TO_TARGET_TWELVE)
5211 if ([forward_weapon_type isTurretLaser])
5216 GLfloat spacing = 2000.0;
5219 offset = accuracy * 750.0;
5220 spacing = 2000.0 + (accuracy * 500.0);
5222 if (entity_personality & 1)
5236 double confidenceFactor = [
self trackDestination:delta_t :NO];
5238 if(success_factor > last_success_factor || confidenceFactor < 0.85) frustration += delta_t;
5239 else if(frustration > 0.0) frustration -= delta_t * 0.75;
5241 double aspect = [
self approachAspectToPrimaryTarget];
5242 if(![forward_weapon_type isTurretLaser] && (frustration > 10 || aspect > 0.75))
5244 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET;
5248 if (missiles) [
self considerFiringMissile:delta_t];
5250 if (cloakAutomatic) [
self activateCloakingDevice];
5251 [
self fireMainWeapon:range];
5257 behaviour = BEHAVIOUR_ATTACK_TARGET;
5262- (void) behaviour_attack_mining_target:(
double) delta_t
5264 double range = [
self rangeToPrimaryTarget];
5265 if (![
self canStillTrackPrimaryTarget])
5267 [
self noteLostTargetAndGoIdle];
5268 desired_speed = maxFlightSpeed * 0.375;
5271 else if ((range < 650) || ([
self proximityAlert] !=
nil))
5275 desired_speed = range * maxFlightSpeed / (650.0 * 16.0);
5279 [
self avoidCollision];
5285 desired_speed = maxFlightSpeed * 0.875;
5288 [
self trackPrimaryTarget:delta_t:NO];
5296 [
self fireMainWeapon:range];
5303- (void) behaviour_attack_fly_to_target:(
double) delta_t
5305 BOOL canBurn = [
self hasFuelInjection] && (fuel >
MIN_FUEL);
5306 float max_available_speed = maxFlightSpeed;
5307 double range = [
self rangeToPrimaryTarget];
5308 if (canBurn) max_available_speed *= [
self afterburnerFactor];
5309 if ([
self primaryTarget] ==
nil)
5311 [
self noteLostTargetAndGoIdle];
5314 Entity* rawTarget = [
self primaryTarget];
5315 if (![rawTarget isShip])
5318 [
self noteLostTargetAndGoIdle];
5325 if (![
self hasProximityAlertIgnoringTarget:YES])
5327 behaviour = BEHAVIOUR_ATTACK_TARGET;
5331 [
self avoidCollision];
5337 if (![
self canStillTrackPrimaryTarget])
5339 [
self noteLostTargetAndGoIdle];
5346 BOOL isUsingAfterburner = canBurn && (flightSpeed > maxFlightSpeed);
5347 BOOL closeQuickly = (canBurn && [target
weaponRange] > weaponRange && range > weaponRange);
5348 double slow_down_range = weaponRange *
COMBAT_WEAPON_RANGE_FACTOR * ((isUsingAfterburner)? 3.0 * [
self afterburnerFactor] : 1.0);
5353 double back_off_range = 10000 *
COMBAT_OUT_RANGE_FACTOR * ((isUsingAfterburner)? 3.0 * [
self afterburnerFactor] : 1.0);
5354 double target_speed = [target
speed];
5355 double aspect = [
self approachAspectToPrimaryTarget];
5357 if (range <= slow_down_range)
5359 if (range < back_off_range)
5365 desired_speed = fmax(target_speed * 1.25, 0.8 * maxFlightSpeed);
5370 desired_speed = fmax(target_speed * 1.05, 0.25 * maxFlightSpeed);
5376 desired_speed = fmax(0.1 * target_speed, 0.1 * maxFlightSpeed);
5383 desired_speed = fmax(target_speed * 1.5, maxFlightSpeed);
5389 desired_speed = fmax(0.5 * target_speed, 0.5 * maxFlightSpeed);
5393 desired_speed = fmax(1.25 * target_speed, 0.5 * maxFlightSpeed);
5402 desired_speed = max_available_speed;
5406 desired_speed = fmax(maxFlightSpeed,fmin(3.0 * target_speed, max_available_speed));
5411 double last_success_factor = success_factor;
5412 success_factor = [
self trackPrimaryTarget:delta_t:NO];
5414 if ((success_factor > 0.999)||(success_factor > last_success_factor))
5416 frustration -= delta_t;
5417 if (frustration < 0.0)
5422 frustration += delta_t;
5423 if (frustration > 3.0)
5425 [
self noteFrustration:@"BEHAVIOUR_ATTACK_FLY_TO_TARGET"];
5426 [
self setEvasiveJink:1000.0];
5427 behaviour = BEHAVIOUR_ATTACK_TARGET;
5429 desired_speed = maxFlightSpeed;
5433 if (missiles) [
self considerFiringMissile:delta_t];
5435 if (cloakAutomatic) [
self activateCloakingDevice];
5436 [
self fireMainWeapon:range];
5451 behaviour = BEHAVIOUR_ATTACK_TARGET;
5458 if ([target behaviour] != BEHAVIOUR_FLEE_TARGET && [target behaviour] != BEHAVIOUR_FLEE_EVASIVE_ACTION)
5463 behaviour = BEHAVIOUR_EVASIVE_ACTION;
5470- (void) behaviour_attack_fly_from_target:(
double) delta_t
5472 double range = [
self rangeToPrimaryTarget];
5473 double last_success_factor = success_factor;
5474 success_factor = range;
5476 if ([
self primaryTarget] ==
nil)
5478 [
self noteLostTargetAndGoIdle];
5481 if (last_success_factor > success_factor)
5483 frustration += delta_t;
5487 frustration += delta_t / 4.0 ;
5490 if (frustration > 10.0)
5492 if (
randf() < 0.3) {
5493 desired_speed = maxFlightSpeed * (([
self hasFuelInjection] && (fuel >
MIN_FUEL)) ? [
self afterburnerFactor] : 1);
5497 behaviour = BEHAVIOUR_ATTACK_TARGET;
5503 desired_speed = flightSpeed * 2;
5505 [
self setEvasiveJink:z];
5509 if (desired_speed > maxFlightSpeed)
5512 double target_speed = [target
speed];
5513 if (desired_speed > target_speed * 2.0)
5515 desired_speed = maxFlightSpeed;
5518 else if (desired_speed < maxFlightSpeed * 0.5)
5520 desired_speed = maxFlightSpeed;
5524 flightSpeed > (scannerRange - range) * max_flight_pitch / 6.28)
5527 behaviour = BEHAVIOUR_ATTACK_TARGET;
5530 [
self trackPrimaryTarget:delta_t:YES];
5532 if (missiles) [
self considerFiringMissile:delta_t];
5534 if (cloakAutomatic) [
self activateCloakingDevice];
5535 if ([
self hasProximityAlertIgnoringTarget:YES])
5536 [
self avoidCollision];
5540 double aspect = [
self approachAspectToPrimaryTarget];
5543 if (aspect > 0.99999 || aspect < -0.999)
5546 behaviour = BEHAVIOUR_EVASIVE_ACTION;
5553- (void) behaviour_running_defense:(
double) delta_t
5555 if (![
self canStillTrackPrimaryTarget])
5557 [
self noteLostTargetAndGoIdle];
5561 double range = [
self rangeToPrimaryTarget];
5562 desired_speed = maxFlightSpeed;
5564 if (range > weaponRange || range > 0.8 * scannerRange || range == 0)
5566 behaviour = BEHAVIOUR_CLOSE_WITH_TARGET;
5567 if ([forward_weapon_type isTurretLaser])
5569 behaviour = BEHAVIOUR_ATTACK_FLY_TO_TARGET_TWELVE;
5573 [
self trackPrimaryTarget:delta_t:YES];
5574 if ([forward_weapon_type isTurretLaser])
5577 [
self fireMainWeapon:range];
5581 [
self fireAftWeapon:range];
5583 if (cloakAutomatic) [
self activateCloakingDevice];
5584 if ([
self hasProximityAlertIgnoringTarget:YES])
5585 [
self avoidCollision];
5589 behaviour = BEHAVIOUR_ATTACK_TARGET;
5595 [
self avoidCollision];
5602- (void) behaviour_flee_target:(
double) delta_t
5604 BOOL canBurn = [
self hasFuelInjection] && (fuel >
MIN_FUEL);
5605 float max_available_speed = maxFlightSpeed;
5606 double range = [
self rangeToPrimaryTarget];
5607 if ([
self primaryTarget] ==
nil)
5609 [
self noteLostTargetAndGoIdle];
5612 if (canBurn) max_available_speed *= [
self afterburnerFactor];
5614 double last_range = success_factor;
5615 success_factor = range;
5617 if (range > desired_range || range == 0)
5618 [shipAI message:@"REACHED_SAFETY"];
5620 desired_speed = max_available_speed;
5622 if (range > last_range)
5624 frustration -= 0.25 * delta_t;
5625 if (frustration < 0.0)
5630 frustration += delta_t;
5631 if (frustration > 15.0)
5633 [
self noteFrustration:@"BEHAVIOUR_FLEE_TARGET"];
5638 [
self trackPrimaryTarget:delta_t:YES];
5640 Entity *target = [
self primaryTarget];
5642 if (missiles && [target isShip] && [(
ShipEntity *)target primaryTarget] ==
self)
5644 [
self considerFiringMissile:delta_t];
5647 if (([
self hasCascadeMine]) && (range < 10000.0) && canBurn)
5649 float qbomb_chance = 0.01 * delta_t;
5650 if (
randf() < qbomb_chance)
5652 [
self launchCascadeMine];
5657 if ([forward_weapon_type isTurretLaser])
5659 [
self fireMainWeapon:range];
5662 if (cloakAutomatic) [
self activateCloakingDevice];
5667 [
self avoidCollision];
5673- (void) behaviour_fly_range_from_destination:(
double) delta_t
5675 double distance = [
self rangeToDestination];
5676 if (distance < desired_range)
5678 behaviour = BEHAVIOUR_FLY_FROM_DESTINATION;
5679 if (desired_speed < maxFlightSpeed)
5681 desired_speed = maxFlightSpeed;
5686 behaviour = BEHAVIOUR_FLY_TO_DESTINATION;
5688 if ([
self hasProximityAlertIgnoringTarget:YES])
5690 [
self avoidCollision];
5699- (void) behaviour_face_destination:(
double) delta_t
5702 double distance = [
self rangeToDestination];
5703 double old_pitch = flightPitch;
5704 desired_speed = 0.0;
5705 if (desired_range > 1.0 && distance > desired_range)
5707 max_cos = sqrt(1 - 0.90 * desired_range*desired_range/(distance * distance));
5709 double confidenceFactor = [
self trackDestination:delta_t:NO];
5710 if (confidenceFactor >= max_cos && flightPitch == 0.0)
5713 [shipAI message:@"FACING_DESTINATION"];
5714 [
self doScriptEvent:OOJSID("shipNowFacingDestination")];
5716 if(docking_match_rotation)
5718 behaviour = BEHAVIOUR_FLY_TO_DESTINATION;
5722 behaviour = BEHAVIOUR_IDLE;
5726 if(flightSpeed == 0) frustration += delta_t;
5727 if (frustration > 15.0 / max_flight_pitch)
5730 [
self noteFrustration:@"BEHAVIOUR_FACE_DESTINATION"];
5731 if(flightPitch == old_pitch) flightPitch = 0.5 * max_flight_pitch;
5739 if(flightSpeed == 0 && frustration > 5 && confidenceFactor > 0.5 && ((flightPitch > 0 && old_pitch < 0) || (flightPitch < 0 && old_pitch > 0)))
5741 flightPitch += 0.5 * old_pitch;
5744 if ([
self hasProximityAlertIgnoringTarget:YES])
5746 [
self avoidCollision];
5753- (void) behaviour_land_on_planet:(
double) delta_t
5756 desired_speed = 0.0;
5758 OOPlanetEntity* planet = [UNIVERSE entityForUniversalID:planetForLanding];
5760 if (![planet isPlanet])
5762 behaviour = BEHAVIOUR_IDLE;
5763 aiScriptWakeTime = 1;
5764 [shipAI message:@"NO_PLANET_NEARBY"];
5768 if (HPdistance(position, [planet position]) + [
self collisionRadius] < [planet radius])
5771 [
self landOnPlanet:planet];
5775 double confidenceFactor = [
self trackDestination:delta_t:YES];
5777 if (confidenceFactor >= max_cos && flightSpeed == 0.0)
5783 [
self adjustVelocity:vector_multiply_scalar([
self forwardVector], -max_thrust * delta_t)];
5788 if ([
self hasProximityAlertIgnoringTarget:YES])
5790 [
self avoidCollision];
5797- (void) behaviour_formation_form_up:(
double) delta_t
5801 double distance = [
self rangeToDestination];
5802 double eta = (distance - desired_range) / flightSpeed;
5803 if(eta < 0) eta = 0;
5804 if ((eta < 5.0)&&(leadShip)&&(leadShip->
isShip))
5805 desired_speed = [leadShip flightSpeed] * (1 + eta * 0.05);
5807 desired_speed = maxFlightSpeed;
5809 double last_distance = success_factor;
5810 success_factor = distance;
5813 [
self trackDestination:delta_t: NO];
5816 GLfloat slowdownTime = (thrust > 0.0)? flightSpeed / (thrust) : 4.0;
5817 GLfloat minTurnSpeedFactor = 0.05 * max_flight_pitch * max_flight_roll;
5819 if ((eta < slowdownTime)&&(flightSpeed > maxFlightSpeed * minTurnSpeedFactor))
5820 desired_speed = flightSpeed * 0.50;
5822 if (distance < last_distance)
5824 frustration -= 0.25 * delta_t;
5825 if (frustration < 0.0)
5830 frustration += delta_t;
5831 if (frustration > 15.0)
5833 if (!leadShip) [
self noteFrustration:@"BEHAVIOUR_FORMATION_FORM_UP"];
5834 else if (distance > 0.5 * scannerRange && !pitching_over)
5836 pitching_over = YES;
5841 if ([
self hasProximityAlertIgnoringTarget:YES])
5843 [
self avoidCollision];
5850- (void) behaviour_fly_to_destination:(
double) delta_t
5852 double distance = [
self rangeToDestination];
5854 if (distance < desired_range)
5857 [shipAI message:@"DESIRED_RANGE_ACHIEVED"];
5858 [
self doScriptEvent:OOJSID("shipAchievedDesiredRange")];
5860 if(!docking_match_rotation)
5862 behaviour = BEHAVIOUR_IDLE;
5863 desired_speed = 0.0;
5869 double last_distance = success_factor;
5870 success_factor = distance;
5873 double confidenceFactor = [
self trackDestination:delta_t: NO];
5874 if(confidenceFactor < 0.2) confidenceFactor = 0.2;
5887 GLfloat eta = ((distance + 1) - desired_range) / (0.51 * flightSpeed * confidenceFactor);
5888 GLfloat slowdownTime = (thrust > 0.0)? flightSpeed / (thrust) : 4.0;
5889 GLfloat minTurnSpeedFactor = 0.05 * max_flight_pitch * max_flight_roll;
5890 if (dockingInstructions !=
nil)
5892 minTurnSpeedFactor /= 10.0;
5893 if (minTurnSpeedFactor * maxFlightSpeed > 20.0)
5895 minTurnSpeedFactor /= 10.0;
5900 if (((eta < slowdownTime)&&(flightSpeed > maxFlightSpeed * minTurnSpeedFactor)) || (flightSpeed > max_flight_pitch * 5 * confidenceFactor * distance))
5902 desired_speed = flightSpeed * 0.50;
5907 if (docking_match_rotation && confidenceFactor >=
MAX_COS && dockingInstructions !=
nil && [dockingInstructions oo_intForKey:
@"docking_stage"] >= 7)
5912 if ((station_for_docking)&&(station_for_docking->
isStation))
5914 float rollMatch = dot_product([station_for_docking portUpVectorForShip:
self],[
self upVector]);
5915 if (rollMatch < MAX_COS && rollMatch > -
MAX_COS)
5918 desired_speed = 0.1;
5920 else if (desired_speed <= 0.2)
5923 desired_speed = [dockingInstructions oo_floatForKey:@"speed"];
5930 if (distance < last_distance)
5932 frustration -= 0.25 * delta_t;
5933 if (frustration < 0.0)
5938 frustration += delta_t;
5939 if ((frustration > slowdownTime * 10.0 && slowdownTime > 0)||(frustration > 15.0))
5941 [
self noteFrustration:@"BEHAVIOUR_FLY_TO_DESTINATION"];
5942 frustration -= slowdownTime * 5.0;
5946 if ([
self hasProximityAlertIgnoringTarget:YES])
5948 [
self avoidCollision];
5955- (void) behaviour_fly_from_destination:(
double) delta_t
5957 double distance = [
self rangeToDestination];
5958 if (distance > desired_range)
5961 [shipAI message:@"DESIRED_RANGE_ACHIEVED"];
5962 [
self doScriptEvent:OOJSID("shipAchievedDesiredRange")];
5964 behaviour = BEHAVIOUR_IDLE;
5966 desired_speed = 0.0;
5969 [
self trackDestination:delta_t:YES];
5970 if ([
self hasProximityAlertIgnoringTarget:YES])
5972 [
self avoidCollision];
5979- (void) behaviour_avoid_collision:(
double) delta_t
5981 double distance = [
self rangeToDestination];
5982 if (distance > desired_range)
5984 [
self resumePostProximityAlert];
5992 _destination = prox_ship->
position;
5994 double dq = [
self trackDestination:delta_t:YES];
5997 desired_speed = maxFlightSpeed * (0.5 * dq + 0.5);
6005- (void) behaviour_track_as_turret:(
double) delta_t
6010 if (turret_owner && turret_target && [turret_owner hasHostileTarget])
6012 aim = [
self ballTrackLeadingTarget:delta_t atTarget:turret_target];
6015 HPVector p = HPvector_subtract([turret_target position], [turret_owner position]);
6020 [
self fireTurretCannon:HPmagnitude(p) - cr];
6029 while ((target = [[targetEnum nextObject] weakRefUnderlyingObject]))
6032 if ([target scanClass] == CLASS_NO_DRAW || [(
ShipEntity *)target isCloaked] || [target energy] <= 0.0)
6039 if (range < weaponRange)
6041 aim = [
self ballTrackLeadingTarget:delta_t atTarget:target];
6044 HPVector p = HPvector_subtract([target position], [turret_owner position]);
6049 [
self fireTurretCannon:HPmagnitude(p) - cr];
6055 else if (range > scannerRange)
6067- (void) behaviour_fly_thru_navpoints:(
double) delta_t
6069 int navpoint_plus_index = (next_navpoint_index + 1) % number_of_navpoints;
6070 HPVector d1 = navpoints[next_navpoint_index];
6071 HPVector d2 = navpoints[navpoint_plus_index];
6073 HPVector rel = HPvector_between(d1, position);
6074 HPVector ref = HPvector_between(d2, d1);
6075 ref = HPvector_normal(ref);
6077 HPVector xp = make_HPvector(ref.y * rel.z - ref.z * rel.y, ref.z * rel.x - ref.x * rel.z, ref.x * rel.y - ref.y * rel.x);
6081 GLfloat r0 = HPdot_product(rel, ref);
6085 GLfloat r1 = HPmagnitude(xp);
6087 BOOL in_cone = (r0 > 0.5 * r1);
6090 r1 = 25.0 * flightSpeed;
6094 GLfloat dist2 = HPmagnitude2(rel);
6096 if (dist2 < desired_range * desired_range)
6099 [
self doScriptEvent:OOJSID("shipReachedNavPoint") andReactToAIMessage:@"NAVPOINT_REACHED"];
6100 if (navpoint_plus_index == 0)
6102 [
self doScriptEvent:OOJSID("shipReachedEndPoint") andReactToAIMessage:@"ENDPOINT_REACHED"];
6103 behaviour = BEHAVIOUR_IDLE;
6105 next_navpoint_index = navpoint_plus_index;
6109 double last_success_factor = success_factor;
6110 double last_dist2 = last_success_factor;
6111 success_factor = dist2;
6114 _destination = make_HPvector(d1.x + r1 * ref.x, d1.y + r1 * ref.y, d1.z + r1 * ref.z);
6119 GLfloat temp = desired_range;
6121 desired_range = 1.0;
6123 desired_range = 100.0;
6124 v0 = [
self trackDestination:delta_t: NO];
6125 desired_range = temp;
6127 if (dist2 < last_dist2)
6129 frustration -= 0.25 * delta_t;
6130 if (frustration < 0.0)
6135 frustration += delta_t;
6136 if (frustration > 15.0)
6138 [
self noteFrustration:@"BEHAVIOUR_FLY_THRU_NAVPOINTS"];
6139 frustration -= 15.0;
6146 GLfloat temp = desired_speed;
6147 desired_speed *= v0 * v0;
6149 desired_speed = temp;
6153- (void) behaviour_scripted_ai:(
double) delta_t
6157 jsval rval = JSVAL_VOID;
6158 jsval deltaJS = JSVAL_VOID;
6159 NSDictionary *result =
nil;
6161 BOOL OK = JS_NewNumberValue(context, delta_t, &deltaJS);
6164 OK = [[
self script] callMethod:OOJSID("scriptedAI")
6166 withArguments:&deltaJS
6173 OOLog(
@"ai.error",
@"Could not call scriptedAI in ship script of %@, reverting to idle",
self);
6174 behaviour = BEHAVIOUR_IDLE;
6179 if (!JSVAL_IS_OBJECT(rval))
6181 OOLog(
@"ai.error",
@"Invalid return value of scriptedAI in ship script of %@, reverting to idle",
self);
6182 behaviour = BEHAVIOUR_IDLE;
6191 if ([result objectForKey:
@"stickRollFactor"] !=
nil)
6193 stick_roll = [result oo_floatForKey:@"stickRollFactor"] * max_flight_roll;
6197 stick_roll = [result oo_floatForKey:@"stickRoll"];
6199 if (stick_roll > max_flight_roll)
6201 stick_roll = max_flight_roll;
6203 else if (stick_roll < -max_flight_roll)
6205 stick_roll = -max_flight_roll;
6209 if ([result objectForKey:
@"stickPitchFactor"] !=
nil)
6211 stick_pitch = [result oo_floatForKey:@"stickPitchFactor"] * max_flight_pitch;
6215 stick_pitch = [result oo_floatForKey:@"stickPitch"];
6217 if (stick_pitch > max_flight_pitch)
6219 stick_pitch = max_flight_pitch;
6221 else if (stick_pitch < -max_flight_pitch)
6223 stick_pitch = -max_flight_pitch;
6227 if ([result objectForKey:
@"stickYawFactor"] !=
nil)
6229 stick_yaw = [result oo_floatForKey:@"stickYawFactor"] * max_flight_yaw;
6233 stick_yaw = [result oo_floatForKey:@"stickYaw"];
6235 if (stick_yaw > max_flight_yaw)
6237 stick_yaw = max_flight_yaw;
6239 else if (stick_yaw < -max_flight_yaw)
6241 stick_yaw = -max_flight_yaw;
6245 [
self applySticks:delta_t];
6248 if ([result objectForKey:
@"desiredSpeedFactor"] !=
nil)
6250 desired_speed = [result oo_floatForKey:@"desiredSpeedFactor"] * maxFlightSpeed;
6254 desired_speed = [result oo_floatForKey:@"desiredSpeed"];
6257 if (desired_speed < 0.0)
6259 desired_speed = 0.0;
6263 if (behaviour == BEHAVIOUR_SCRIPTED_ATTACK_AI)
6265 NSString* chosen_weapon = [result oo_stringForKey:@"chosenWeapon" defaultValue:@"FORWARD"];
6266 double range = [
self rangeToPrimaryTarget];
6268 if ([chosen_weapon isEqualToString:
@"FORWARD"])
6270 [
self fireMainWeapon:range];
6272 else if ([chosen_weapon isEqualToString:
@"AFT"])
6274 [
self fireAftWeapon:range];
6276 else if ([chosen_weapon isEqualToString:
@"PORT"])
6278 [
self firePortWeapon:range];
6280 else if ([chosen_weapon isEqualToString:
@"STARBOARD"])
6282 [
self fireStarboardWeapon:range];
6287- (float) reactionTime
6289 return reactionTime;
6293- (void) setReactionTime: (
float) newReactionTime
6295 reactionTime = newReactionTime;
6299- (HPVector) calculateTargetPosition
6301 Entity *target = [
self primaryTarget];
6306 if (reactionTime <= 0.0)
6310 double t = [UNIVERSE getTime] - trackingCurveTimes[1];
6311 return HPvector_add(HPvector_add(trackingCurveCoeffs[0], HPvector_multiply_scalar(trackingCurveCoeffs[1],t)), HPvector_multiply_scalar(trackingCurveCoeffs[2],t*t));
6315- (void) startTrackingCurve
6317 Entity *target = [
self primaryTarget];
6323 trackingCurvePositions[0] = [target
position];
6324 trackingCurvePositions[1] = [target
position];
6325 trackingCurvePositions[2] = [target
position];
6326 trackingCurvePositions[3] = [target
position];
6327 trackingCurveTimes[0] = now;
6328 trackingCurveTimes[1] = now - reactionTime/3.0;
6329 trackingCurveTimes[2] = now - reactionTime*2.0/3.0;
6330 trackingCurveTimes[3] = now - reactionTime;
6331 [
self calculateTrackingCurve];
6336- (void) updateTrackingCurve
6338 Entity *target = [
self primaryTarget];
6340 if (target ==
nil || reactionTime <= 0.0 || trackingCurveTimes[0] + reactionTime/3.0 > now)
return;
6341 trackingCurvePositions[3] = trackingCurvePositions[2];
6342 trackingCurvePositions[2] = trackingCurvePositions[1];
6343 trackingCurvePositions[1] = trackingCurvePositions[0];
6353 trackingCurvePositions[0] = [target
position];
6355 trackingCurveTimes[3] = trackingCurveTimes[2];
6356 trackingCurveTimes[2] = trackingCurveTimes[1];
6357 trackingCurveTimes[1] = trackingCurveTimes[0];
6358 trackingCurveTimes[0] = now;
6359 [
self calculateTrackingCurve];
6363- (void) calculateTrackingCurve
6365 if (reactionTime <= 0.0)
6367 trackingCurveCoeffs[0] = trackingCurvePositions[0];
6372 double t1 = trackingCurveTimes[2] - trackingCurveTimes[1],
6373 t2 = trackingCurveTimes[3] - trackingCurveTimes[1];
6374 trackingCurveCoeffs[0] = trackingCurvePositions[1];
6375 trackingCurveCoeffs[1] = HPvector_add(HPvector_add(
6376 HPvector_multiply_scalar(trackingCurvePositions[1], -(t1+t2)/(t1*t2)),
6377 HPvector_multiply_scalar(trackingCurvePositions[2], -t2/(t1*(t1-t2)))),
6378 HPvector_multiply_scalar(trackingCurvePositions[3], t1/(t2*(t1-t2))));
6379 trackingCurveCoeffs[2] = HPvector_add(HPvector_add(
6380 HPvector_multiply_scalar(trackingCurvePositions[1], 1/(t1*t2)),
6381 HPvector_multiply_scalar(trackingCurvePositions[2], 1/(t1*(t1-t2)))),
6382 HPvector_multiply_scalar(trackingCurvePositions[3], -1/(t2*(t1-t2))));
6386- (void) drawImmediate:(
bool)immediate translucent:(
bool)translucent
6388 if ((no_draw_distance < cam_zero_distance) ||
6389 (cloaking_device_active &&
randf() > 0.10))
6396 [
super drawImmediate:immediate translucent:translucent];
6401 if (translucent) [
self drawDebugStuff];
6413 if ([
self subEntityCount] > 0)
6416 foreach (subEntity, [
self subEntities])
6418 NSAssert3([subEntity owner] ==
self,
@"Subentity ownership broke - %@ should be owned by %@ but is owned by %@.", subEntity,
self, [subEntity owner]);
6430 if (0 && reportAIMessages)
6435 Entity *pTarget = [
self primaryTarget];
6439 OODebugDrawColoredLine(HPVectorToVector([
self position]), HPVectorToVector([pTarget position]), [
OOColor colorWithRed:0.2 green:0.0 blue:0.0 alpha:1.0]);
6442 Entity *sTarget = [
self targetStation];
6443 if (sTarget != pTarget && [sTarget isStation])
6448 Entity *fTarget = [
self foundTarget];
6449 if (fTarget !=
nil && fTarget != pTarget && fTarget != sTarget)
6458- (void) drawSubEntityImmediate:(
bool)immediate translucent:(
bool)translucent
6462 if (cam_zero_distance > no_draw_distance)
6471 [
self drawImmediate:immediate translucent:translucent];
6498- (GLfloat *) scannerDisplayColorForShip:(
ShipEntity*)otherShip :(BOOL)isHostile :(BOOL)flash :(
OOColor *)scannerDisplayColor1 :(
OOColor *)scannerDisplayColor2 :(
OOColor *)scannerDisplayColorH1 :(
OOColor *)scannerDisplayColorH2
6505 if (scannerDisplayColorH1 || scannerDisplayColorH2)
6507 if (scannerDisplayColorH1 && !scannerDisplayColorH2)
6509 [scannerDisplayColorH1 getRed:&scripted_color[0] green:&scripted_color[1] blue:&scripted_color[2] alpha:&scripted_color[3]];
6512 if (!scannerDisplayColorH1 && scannerDisplayColorH2)
6514 [scannerDisplayColorH2
getRed:&scripted_color[0]
green:&scripted_color[1]
blue:&scripted_color[2]
alpha:&scripted_color[3]];
6517 if (scannerDisplayColorH1 && scannerDisplayColorH2)
6520 [scannerDisplayColorH1 getRed:&scripted_color[0] green:&scripted_color[1] blue:&scripted_color[2] alpha:&scripted_color[3]];
6522 [scannerDisplayColorH2
getRed:&scripted_color[0]
green:&scripted_color[1]
blue:&scripted_color[2]
alpha:&scripted_color[3]];
6530 if (scannerDisplayColor1 || scannerDisplayColor2)
6532 if (scannerDisplayColor1 && !scannerDisplayColor2)
6534 [scannerDisplayColor1 getRed:&scripted_color[0] green:&scripted_color[1] blue:&scripted_color[2] alpha:&scripted_color[3]];
6537 if (!scannerDisplayColor1 && scannerDisplayColor2)
6539 [scannerDisplayColor2 getRed:&scripted_color[0] green:&scripted_color[1] blue:&scripted_color[2] alpha:&scripted_color[3]];
6542 if (scannerDisplayColor1 && scannerDisplayColor2)
6545 [scannerDisplayColor1 getRed:&scripted_color[0] green:&scripted_color[1] blue:&scripted_color[2] alpha:&scripted_color[3]];
6547 [scannerDisplayColor2 getRed:&scripted_color[0] green:&scripted_color[1] blue:&scripted_color[2] alpha:&scripted_color[3]];
6554 if ([
self isJammingScanning])
6556 if (![otherShip hasMilitaryScannerFilter])
6577 case CLASS_THARGOID :
6582 case CLASS_MISSILE :
6584 case CLASS_STATION :
6592 case CLASS_MILITARY :
6593 if ((isHostile)&&(flash))
6610- (void)setScannerDisplayColor1:(
OOColor *)color
6612 DESTROY(scanner_display_color1);
6615 scanner_display_color1 = [color retain];
6619- (void)setScannerDisplayColor2:(
OOColor *)color
6621 DESTROY(scanner_display_color2);
6624 scanner_display_color2 = [color retain];
6628- (
OOColor *)scannerDisplayColor1
6630 return [[scanner_display_color1 retain] autorelease];
6634- (
OOColor *)scannerDisplayColor2
6636 return [[scanner_display_color2 retain] autorelease];
6640- (void)setScannerDisplayColorHostile1:(
OOColor *)color
6642 DESTROY(scanner_display_color_hostile1);
6645 scanner_display_color_hostile1 = [color retain];
6649- (void)setScannerDisplayColorHostile2:(
OOColor *)color
6651 DESTROY(scanner_display_color_hostile2);
6654 scanner_display_color_hostile2 = [color retain];
6658- (
OOColor *)scannerDisplayColorHostile1
6660 return [[scanner_display_color_hostile1 retain] autorelease];
6664- (
OOColor *)scannerDisplayColorHostile2
6666 return [[scanner_display_color_hostile2 retain] autorelease];
6672 return cloaking_device_active;
6678 return cloakPassive;
6682- (void)setCloaked:(BOOL)cloak
6684 if (cloak) [
self activateCloakingDevice];
6685 else [
self deactivateCloakingDevice];
6691 return cloakAutomatic;
6695- (void)setAutoCloak:(BOOL)automatic
6697 cloakAutomatic = !!automatic;
6701- (BOOL) isJammingScanning
6703 return ([
self hasMilitaryJammer] && military_jammer_active);
6707- (void) addSubEntity:(
Entity<OOSubEntity> *)sub
6709 if (sub ==
nil)
return;
6711 if (subEntities ==
nil) subEntities = [[NSMutableArray alloc] init];
6714 [subEntities addObject:sub];
6715 [sub setOwner:self];
6717 [
self addSubentityToCollisionRadius:sub];
6721- (void) setOwner:(
Entity *)who_owns_entity
6723 [
super setOwner:who_owns_entity];
6732 [[
self drawable] setBindingTarget:self];
6737- (void) applyThrust:(
double) delta_t
6740 BOOL canBurn = [
self hasFuelInjection] && (fuel >
MIN_FUEL);
6741 BOOL isUsingAfterburner = (canBurn && (flightSpeed > maxFlightSpeed) && (desired_speed >= flightSpeed));
6742 float max_available_speed = maxFlightSpeed;
6743 if (canBurn) max_available_speed *= [
self afterburnerFactor];
6748 GLfloat velmag = magnitude(velocity);
6751 GLfloat vscale = fmaxf((velmag - dt_thrust) / velmag, 0.0f);
6752 scale_vector(&velocity, vscale);
6756 if (behaviour == BEHAVIOUR_TUMBLE)
return;
6759 if (desired_speed > max_available_speed)
6760 desired_speed = max_available_speed;
6762 if (flightSpeed > desired_speed)
6764 [
self decrease_flight_speed: dt_thrust];
6765 if (flightSpeed < desired_speed) flightSpeed = desired_speed;
6767 if (flightSpeed < desired_speed)
6769 [
self increase_flight_speed: dt_thrust];
6770 if (flightSpeed > desired_speed) flightSpeed = desired_speed;
6772 [
self moveForward: delta_t*flightSpeed];
6775 if (isUsingAfterburner)
6777 fuel_accumulator -= delta_t * afterburner_rate;
6778 while (fuel_accumulator < 0.0)
6781 fuel_accumulator += 1.0;
6787- (void) orientationChanged
6789 [
super orientationChanged];
6797- (void) applyRoll:(GLfloat) roll1 andClimb:(GLfloat) climb1
6801 if (!roll1 && !climb1 && !hasRotated)
return;
6807 [
self orientationChanged];
6811- (void) applyRoll:(GLfloat) roll1 climb:(GLfloat) climb1 andYaw:(GLfloat) yaw1
6813 if ((roll1 == 0.0)&&(climb1 == 0.0)&&(yaw1 == 0.0)&&(!hasRotated))
6826 [
self orientationChanged];
6830- (void) applyAttitudeChanges:(
double) delta_t
6832 [
self applyRoll:flightRoll*delta_t climb:flightPitch*delta_t andYaw:flightYaw*delta_t];
6836- (void) avoidCollision
6838 if (scanClass == CLASS_MISSILE)
6845 if (previousCondition)
6847 [previousCondition release];
6848 previousCondition =
nil;
6851 previousCondition = [[NSMutableDictionary dictionaryWithCapacity:5] retain];
6853 [previousCondition oo_setInteger:behaviour forKey:@"behaviour"];
6854 if ([
self primaryTarget] !=
nil)
6857 [previousCondition setObject:[[
self primaryTarget] weakSelf] forKey:@"primaryTarget"];
6859 [previousCondition oo_setFloat:desired_range forKey:@"desired_range"];
6860 [previousCondition oo_setFloat:desired_speed forKey:@"desired_speed"];
6861 [previousCondition oo_setHPVector:_destination forKey:@"destination"];
6863 _destination = [prox_ship
position];
6864 _destination = OOHPVectorInterpolate(position, [prox_ship position], 0.5);
6868 behaviour = BEHAVIOUR_AVOID_COLLISION;
6869 pitching_over = YES;
6874- (void) resumePostProximityAlert
6876 if (!previousCondition)
return;
6878 behaviour = [previousCondition oo_intForKey:@"behaviour"];
6879 [_primaryTarget release];
6880 _primaryTarget = [[previousCondition objectForKey:@"primaryTarget"] weakRetain];
6881 [
self startTrackingCurve];
6882 desired_range = [previousCondition oo_floatForKey:@"desired_range"];
6883 desired_speed = [previousCondition oo_floatForKey:@"desired_speed"];
6884 _destination = [previousCondition oo_hpvectorForKey:@"destination"];
6886 [previousCondition release];
6887 previousCondition =
nil;
6896- (double) messageTime
6902- (void) setMessageTime:(
double) value
6904 messageTime = value;
6916 if (group != _group)
6918 if (_escortGroup != _group)
6920 if (
self == [_group leader]) [_group setLeader:nil];
6921 [_group removeShip:self];
6925 _group = [group retain];
6934 if (_escortGroup ==
nil)
6936 _escortGroup = [[
OOShipGroup alloc] initWithName:@"escort group"];
6937 [_escortGroup setLeader:self];
6940 return _escortGroup;
6946 if (group != _escortGroup)
6948 [_escortGroup release];
6949 _escortGroup = [group retain];
6951 [
self updateEscortFormation];
6959 return _escortGroup;
6968 _group = [[
OOShipGroup alloc] initWithName:@"station group"];
6969 [_group setLeader:self];
6978 if (_escortGroup ==
nil)
return NO;
6979 return [_escortGroup count] > 1;
6983- (NSEnumerator *) escortEnumerator
6985 if (_escortGroup ==
nil)
return [[NSArray array] objectEnumerator];
6986 return [[_escortGroup mutationSafeEnumerator] ooExcludingObject:self];
6990- (NSArray *) escortArray
6992 if (_escortGroup ==
nil)
return [NSArray array];
6993 return [[
self escortEnumerator] allObjects];
6997- (uint8_t) escortCount
6999 if (_escortGroup ==
nil)
return 0;
7000 return [_escortGroup count] - 1;
7004- (uint8_t) pendingEscortCount
7006 return _pendingEscortCount;
7010- (void) setPendingEscortCount:(uint8_t)count
7012 _pendingEscortCount =
MIN(
count, _maxEscortCount);
7016- (uint8_t) maxEscortCount
7018 return _maxEscortCount;
7022- (void) setMaxEscortCount:(uint8_t)newCount
7024 _maxEscortCount = newCount;
7028- (NSUInteger) turretCount
7030 NSUInteger
count = 0;
7031 NSEnumerator *subEnum = [
self shipSubEntityEnumerator];
7033 while ((se = [subEnum nextObject]))
7044- (
Entity*) proximityAlert
7046 Entity* prox = [_proximityAlert weakRefUnderlyingObject];
7055- (void) setProximityAlert:(
ShipEntity*) other
7063 if ([other mass] < 2000)
7071 if ((other->
isStation) && ([
self status] == STATUS_LAUNCHING ||
7072 dockingInstructions !=
nil))
7081 Vector vdiff = HPVectorToVector(HPvector_between(position, other->
position));
7082 GLfloat d_forward = dot_product(vdiff, v_forward);
7083 GLfloat d_up = dot_product(vdiff, v_up);
7084 GLfloat d_right = dot_product(vdiff, v_right);
7085 if ((d_forward > 0.0)&&(flightSpeed > 0.0))
7086 d_forward *= 0.25 * maxFlightSpeed / flightSpeed;
7087 double d2 = d_forward * d_forward + d_up * d_up + d_right * d_right;
7088 double cr2 = collision_radius * 2.0 + other->collision_radius; cr2 *= cr2;
7093 if (behaviour == BEHAVIOUR_AVOID_COLLISION)
7096 if ((prox)&&(prox != other))
7101 if (sa_prox < sa_other)
return;
7104 [_proximityAlert release];
7115- (NSString *) shipUniqueName
7117 return shipUniqueName;
7121- (NSString *) shipClassName
7123 return shipClassName;
7127- (NSString *) displayName
7129 if (displayName ==
nil || [displayName length] == 0)
7131 if ([shipUniqueName length] == 0)
7133 if (shipClassName ==
nil)
7139 return shipClassName;
7144 if (shipClassName ==
nil)
7146 return [NSString stringWithFormat:@"%@: %@",name,shipUniqueName];
7150 return [NSString stringWithFormat:@"%@: %@",shipClassName,shipUniqueName];
7159- (NSString *)scanDescriptionForScripting
7161 return scan_description;
7165- (NSString *)scanDescription
7167 if (scan_description !=
nil)
7169 return scan_description;
7173 NSString *desc =
nil;
7174 switch ([
self scanClass])
7178 int legal = [
self legalStatus];
7182 legal_i = (legal <= 50) ? 1 : 2;
7184 desc = [[[UNIVERSE descriptions] oo_arrayForKey:@"legal_status"] oo_stringAtIndex:legal_i];
7188 case CLASS_THARGOID:
7189 desc =
DESC(
@"legal-desc-alien");
7193 desc =
DESC(
@"legal-desc-system-vessel");
7196 case CLASS_MILITARY:
7197 desc =
DESC(
@"legal-desc-military-vessel");
7208- (void) setName:(NSString *)inName
7211 name = [inName copy];
7215- (void) setShipUniqueName:(NSString *)inName
7217 [shipUniqueName release];
7218 shipUniqueName = [inName copy];
7222- (void) setShipClassName:(NSString *)inName
7224 [shipClassName release];
7225 shipClassName = [inName copy];
7229- (void) setDisplayName:(NSString *)inName
7231 [displayName release];
7232 displayName = [inName copy];
7236- (void) setScanDescription:(NSString *)inName
7238 [scan_description release];
7239 scan_description = [inName copy];
7243- (NSString *) identFromShip:(
ShipEntity*) otherShip
7245 if ([
self isJammingScanning] && ![otherShip hasMilitaryScannerFilter])
7247 return DESC(
@"unknown-target");
7249 return [
self displayName];
7253- (BOOL) hasRole:(NSString *)role
7255 return [roleSet hasRole:role] || [role isEqual:primaryRole] || [role isEqual:[
self shipDataKeyAutoRole]];
7261 if (roleSet ==
nil) roleSet = [[
OORoleSet alloc] initWithRoleString:primaryRole];
7262 return [[roleSet roleSetWithAddedRoleIfNotSet:primaryRole probability:1.0] roleSetWithAddedRoleIfNotSet:[
self shipDataKeyAutoRole] probability:1.0];
7266- (void) addRole:(NSString *)role
7268 [
self addRole:role withProbability:0.0f];
7272- (void) addRole:(NSString *)role withProbability:(
float)probability
7274 if (![
self hasRole:role])
7277 if (roleSet !=
nil) newRoles = [roleSet roleSetWithAddedRole:role probability:probability];
7279 if (newRoles !=
nil)
7282 roleSet = [newRoles retain];
7288- (void) removeRole:(NSString *)role
7290 if ([
self hasRole:role])
7292 OORoleSet *newRoles = [roleSet roleSetWithRemovedRole:role];
7293 if (newRoles !=
nil)
7296 roleSet = [newRoles retain];
7302- (NSString *)primaryRole
7304 if (primaryRole ==
nil)
7306 primaryRole = [roleSet anyRole];
7307 if (primaryRole ==
nil) primaryRole =
@"trader";
7308 [primaryRole retain];
7309 OOLog(
@"ship.noPrimaryRole",
@"%@ had no primary role, randomly selected \"%@\
".", [
self name], primaryRole);
7317- (void)setPrimaryRole:(NSString *)role
7319 if (![role isEqual:primaryRole])
7321 [primaryRole release];
7322 primaryRole = [role copy];
7327- (BOOL)hasPrimaryRole:(NSString *)role
7329 return [[
self primaryRole] isEqual:role];
7336 return [
self scanClass] == CLASS_POLICE;
7341 return [
self scanClass] == CLASS_THARGOID;
7347 return [UNIVERSE role:[
self primaryRole] isInCategory:@"oolite-trader"];
7353 return [UNIVERSE role:[
self primaryRole] isInCategory:@"oolite-pirate"];
7359 return ([[
self primaryRole] hasSuffix:
@"MISSILE"] || [
self hasPrimaryRole:
@"missile"]);
7365 return [[
self primaryRole] hasSuffix:@"MINE"];
7371 return [
self isMissile] || [
self isMine];
7377 return [UNIVERSE role:[
self primaryRole] isInCategory:@"oolite-escort"];
7383 return [UNIVERSE role:[
self primaryRole] isInCategory:@"oolite-shuttle"];
7389 return behaviour == BEHAVIOUR_TRACK_AS_TURRET;
7393- (BOOL)isPirateVictim
7395 return [UNIVERSE roleIsPirateVictim:[
self primaryRole]];
7399- (BOOL)isExplicitlyUnpiloted
7401 return _explicitlyUnpiloted;
7407 return [
self isExplicitlyUnpiloted] || [
self isHulk] || [
self scanClass] == CLASS_ROCK || [
self scanClass] == CLASS_CARGO;
7415 case BEHAVIOUR_ATTACK_TARGET:
7416 case BEHAVIOUR_ATTACK_FLY_TO_TARGET:
7417 case BEHAVIOUR_ATTACK_FLY_FROM_TARGET:
7418 case BEHAVIOUR_RUNNING_DEFENSE:
7419 case BEHAVIOUR_FLEE_TARGET:
7420 case BEHAVIOUR_ATTACK_BREAK_OFF_TARGET:
7421 case BEHAVIOUR_ATTACK_SLOW_DOGFIGHT:
7422 case BEHAVIOUR_EVASIVE_ACTION:
7423 case BEHAVIOUR_FLEE_EVASIVE_ACTION:
7424 case BEHAVIOUR_ATTACK_FLY_TO_TARGET_SIX:
7426 case BEHAVIOUR_ATTACK_FLY_TO_TARGET_TWELVE:
7427 case BEHAVIOUR_ATTACK_BROADSIDE:
7428 case BEHAVIOUR_ATTACK_BROADSIDE_LEFT:
7429 case BEHAVIOUR_ATTACK_BROADSIDE_RIGHT:
7430 case BEHAVIOUR_CLOSE_TO_BROADSIDE_RANGE:
7431 case BEHAVIOUR_CLOSE_WITH_TARGET:
7432 case BEHAVIOUR_ATTACK_SNIPER:
7433 case BEHAVIOUR_SCRIPTED_ATTACK_AI:
7440 return 100 < behaviour && behaviour < 120;
7445- (BOOL) hasHostileTarget
7447 Entity *t = [
self primaryTarget];
7448 if (t ==
nil || ![t isShip])
7452 if ([
self isMissile])
7456 if ((behaviour == BEHAVIOUR_AVOID_COLLISION)&&(previousCondition))
7458 int old_behaviour = [previousCondition oo_intForKey:@"behaviour"];
7459 return IsBehaviourHostile(old_behaviour);
7461 return IsBehaviourHostile(behaviour);
7465- (BOOL) isHostileTo:(
Entity *)entity
7467 return ([
self hasHostileTarget] && [
self primaryTarget] == entity);
7470- (GLfloat) weaponRange
7476- (void) setWeaponRange: (GLfloat) value
7478 weaponRange = value;
7482- (void) setWeaponDataFromType: (
OOWeaponType) weapon_type
7490 if (default_laser_color ==
nil)
7495 [
self setLaserColor:wcol];
7502- (float) energyRechargeRate
7504 return energy_recharge_rate;
7508- (void) setEnergyRechargeRate:(GLfloat)new
7510 energy_recharge_rate =
new;
7514- (float) weaponRechargeRate
7516 return weapon_recharge_rate;
7520- (void) setWeaponRechargeRate:(
float)value
7522 weapon_recharge_rate = value;
7526- (void) setWeaponEnergy:(
float)value
7528 weapon_damage = value;
7534 return currentWeaponFacing;
7538- (GLfloat) scannerRange
7540 return scannerRange;
7544- (void) setScannerRange: (GLfloat) value
7546 scannerRange = value;
7556- (void) setReference:(Vector) v
7562- (BOOL) reportAIMessages
7564 return reportAIMessages;
7568- (void) setReportAIMessages:(BOOL) yn
7570 reportAIMessages = yn;
7574- (void) transitionToAegisNone
7576 if (!suppressAegisMessages && aegis_status !=
AEGIS_NONE)
7579 if (lastAegisLock !=
nil)
7581 [
self doScriptEvent:OOJSID("shipExitedPlanetaryVicinity") withArgument:lastAegisLock];
7583 if (lastAegisLock == [
UNIVERSE sun])
7585 [shipAI message:@"AWAY_FROM_SUN"];
7589 [shipAI message:@"AWAY_FROM_PLANET"];
7595 [shipAI message:@"AEGIS_NONE"];
7604 float centerDistance = HPmagnitude2(HPvector_subtract([stellar position], reference));
7605 float r = [stellar radius];
7609 return centerDistance - 1.35 * r * r;
7615 return SurfaceDistanceSqaredV([reference position], stellar);
7619NSComparisonResult ComparePlanetsBySurfaceDistance(
id i1,
id i2,
void* context)
7622 OOPlanetEntity* e1 = i1;
7623 OOPlanetEntity* e2 = i2;
7625 float p1 = SurfaceDistanceSqaredV(p, e1);
7626 float p2 = SurfaceDistanceSqaredV(p, e2);
7628 if (p1 < p2)
return NSOrderedAscending;
7629 if (p1 > p2)
return NSOrderedDescending;
7631 return NSOrderedSame;
7635- (OOPlanetEntity *) findNearestPlanet
7642 OOPlanetEntity *planet =
nil, *bestPlanet =
nil;
7643 float bestRange = INFINITY;
7644 HPVector myPosition = [
self position];
7649 foreach (planet, [
UNIVERSE planets])
7654 float range = SurfaceDistanceSqaredV(myPosition, planet);
7655 if (range < bestRange)
7657 bestPlanet = planet;
7666- (
Entity<OOStellarBody> *) findNearestStellarBody
7674 SurfaceDistanceSqared(
self, sun) < SurfaceDistanceSqared(
self, match))
7684- (OOPlanetEntity *) findNearestPlanetExcludingMoons
7686 OOPlanetEntity *result =
nil;
7687 OOPlanetEntity *planet =
nil;
7688 NSArray *bodies =
nil;
7689 NSArray *planets =
nil;
7692 bodies = [UNIVERSE planets];
7693 planets = [NSMutableArray arrayWithCapacity:[bodies count]];
7695 for (i=0; i < [bodies count]; i++)
7697 planet = [bodies objectAtIndex:i];
7699 planets = [planets arrayByAddingObject:planet];
7702 if ([planets
count] == 0)
return nil;
7704 planets = [planets sortedArrayUsingFunction:ComparePlanetsBySurfaceDistance context:self];
7705 result = [planets objectAtIndex:0];
7714 BOOL sunGoneNova = [[UNIVERSE sun] goneNova];
7721 [
self transitionToAegisNone];
7726 float cr = [nearest radius];
7727 float cr2 = cr * cr;
7729 float d2 = HPmagnitude2(HPvector_subtract([nearest position], [
self position]));
7734 unsigned wasNearPlanetSurface = isNearPlanetSurface;
7735 isNearPlanetSurface = (d2 - cr2) < (250000.0f + 1000.0f * cr);
7738 if (
EXPECT_NOT((wasNearPlanetSurface != isNearPlanetSurface) && !suppressAegisMessages))
7740 if (isNearPlanetSurface)
7742 [
self doScriptEvent:OOJSID("shipApproachingPlanetSurface") withArgument:nearest];
7743 [shipAI reactToMessage:@"APPROACHING_SURFACE" context:@"flight update"];
7747 [
self doScriptEvent:OOJSID("shipLeavingPlanetSurface") withArgument:nearest];
7748 [shipAI reactToMessage:@"LEAVING_SURFACE" context:@"flight update"];
7756 sd2 = HPmagnitude2(HPvector_subtract([the_station position], [
self position]));
7763 else if (
EXPECT_NOT(isNearPlanetSurface || d2 < cr2 * 9.0f))
7776 OOPlanetEntity *mainPlanet = [UNIVERSE planet];
7777 d2 = HPmagnitude2(HPvector_subtract([mainPlanet position], [
self position]));
7778 cr2 = [mainPlanet radius];
7780 if (d2 < cr2 * 9.0f)
7782 nearest = mainPlanet;
7797 if (
EXPECT(!suppressAegisMessages))
7802 [
self doScriptEvent:OOJSID("shipExitedStationAegis") withArgument:the_station];
7803 [shipAI message:@"AEGIS_LEAVING_DOCKING_RANGE"];
7808 [
self doScriptEvent:OOJSID("shipEnteredStationAegis") withArgument:the_station];
7809 [shipAI message:@"AEGIS_IN_DOCKING_RANGE"];
7811 if([
self lastAegisLock] ==
nil && !sunGoneNova)
7813 [
self doScriptEvent:OOJSID("shipEnteredPlanetaryVicinity") withArgument:[UNIVERSE planet]];
7814 [
self setLastAegisLock:[UNIVERSE planet]];
7819 if([
self lastAegisLock] ==
nil && !sunGoneNova)
7821 [
self setLastAegisLock:[UNIVERSE planet]];
7823 [
self transitionToAegisNone];
7828 if(aegis_status !=
AEGIS_NONE && [
self lastAegisLock] !=
nil)
7830 [
self doScriptEvent:OOJSID("shipExitedPlanetaryVicinity") withArgument:[
self lastAegisLock]];
7831 [shipAI message:@"AWAY_FROM_PLANET"];
7833 [
self doScriptEvent:OOJSID("shipEnteredPlanetaryVicinity") withArgument:nearest];
7834 [
self setLastAegisLock:nearest];
7838 [shipAI message:@"CLOSE_TO_SUN"];
7842 [shipAI message:@"CLOSE_TO_PLANET"];
7848 [shipAI message:@"AEGIS_CLOSE_TO_MAIN_PLANET"];
7852 [shipAI message:@"CLOSE_TO_MOON"];
7856 [shipAI message:@"CLOSE_TO_SECONDARY_PLANET"];
7865 [
self setLastAegisLock:nil];
7868 aegis_status = result;
7873- (void) forceAegisCheck
7875 _nextAegisCheck = -1.0f;
7879- (BOOL) withinStationAegis
7890 [_lastAegisLock release];
7891 _lastAegisLock =
nil;
7898- (void) setLastAegisLock:(
Entity<OOStellarBody> *)lastAegisLock
7900 [_lastAegisLock release];
7901 _lastAegisLock = [lastAegisLock weakRetain];
7913 return destination_system;
7925 destination_system = s;
7931 if ([
self status] == stat)
return;
7932 [
super setStatus:stat];
7933 if (stat == STATUS_LAUNCHING)
7935 launch_time = [UNIVERSE getTime];
7939- (void) setLaunchDelay:(
double)delay
7941 launch_delay = delay;
7951- (void) setCrew:(NSArray *)crewArray
7953 if ([
self isExplicitlyUnpiloted])
7963 crew = [crewArray copy];
7967- (void) setSingleCrewWithRole:(NSString *)crewRole
7969 if (![
self isUnpiloted])
7973 [
self setCrew:[NSArray arrayWithObject:crewMember]];
7978- (NSArray *) crewForScripting
7985 NSMutableArray *result = [NSMutableArray arrayWithCapacity:4];
7986 foreach (crewMember, crew)
7989 [result addObject:crewDict];
7996- (void) setStateMachine:(NSString *)smName
7998 [
self setAITo:smName];
8002- (void) setAI:(
AI *)ai
8007 [shipAI clearAllData];
8008 [shipAI autorelease];
8022 return [[
self shipInfoDictionary] oo_fuzzyBooleanForKey:@"auto_ai" defaultValue:YES];
8028 return [[[
self getAI] name] isEqualToString:@"nullAI.plist"];
8032- (BOOL) hasAutoWeapons
8034 return [[
self shipInfoDictionary] oo_fuzzyBooleanForKey:@"auto_weapons" defaultValue:NO];
8038- (void) setShipScript:(NSString *)script_name
8040 NSMutableDictionary *properties =
nil;
8041 NSArray *actions =
nil;
8043 properties = [NSMutableDictionary dictionary];
8044 [properties setObject:self forKey:@"ship"];
8046 [script autorelease];
8051 actions = [shipinfoDictionary oo_arrayForKey:@"launch_actions"];
8054 OOStandardsDeprecated([NSString stringWithFormat:
@"The launch_actions ship key is deprecated on %@.",[
self displayName]]);
8057 [properties setObject:actions forKey:@"legacy_launchActions"];
8061 actions = [shipinfoDictionary oo_arrayForKey:@"script_actions"];
8064 OOStandardsDeprecated([NSString stringWithFormat:
@"The script_actions ship key is deprecated on %@.",[
self displayName]]);
8067 [properties setObject:actions forKey:@"legacy_scriptActions"];
8071 actions = [shipinfoDictionary oo_arrayForKey:@"death_actions"];
8074 OOStandardsDeprecated([NSString stringWithFormat:
@"The death_actions ship key is deprecated on %@.",[
self displayName]]);
8077 [properties setObject:actions forKey:@"legacy_deathActions"];
8081 actions = [shipinfoDictionary oo_arrayForKey:@"setup_actions"];
8084 OOStandardsDeprecated([NSString stringWithFormat:
@"The setup_actions ship key is deprecated on %@.",[
self displayName]]);
8087 [properties setObject:actions forKey:@"legacy_setupActions"];
8098- (double)frustration
8112 if (amount > [
self fuelCapacity]) amount = [
self fuelCapacity];
8125- (GLfloat) fuelChargeRate
8129#if MASS_DEPENDENT_FUEL_PRICES
8133 rate = calcFuelChargeRate(mass);
8136 OOLog(
@"fuelPrices",
@"\"%@\
" fuel charge rate: %.2f (mass ratio: %.2f/%.2f)", [
self shipDataKey], rate, mass, [
PLAYER baseMass]);
8143- (void) applySticks:(
double)delta_t
8146 double rate1 = 2.0 * delta_t;
8147 double rate2 = 4.0 * delta_t;
8148 double rate3 = 4.0 * delta_t;
8150 if (((stick_roll > 0.0)&&(flightRoll < 0.0))||((stick_roll < 0.0)&&(flightRoll > 0.0)))
8152 if (((stick_pitch > 0.0)&&(flightPitch < 0.0))||((stick_pitch < 0.0)&&(flightPitch > 0.0)))
8154 if (((stick_yaw > 0.0)&&(flightYaw < 0.0))||((stick_yaw < 0.0)&&(flightYaw > 0.0)))
8159 if (stick_roll == 0.0)
8161 if (stick_pitch == 0.0)
8163 if (stick_yaw == 0.0)
8168 if (flightRoll < stick_roll - rate1)
8170 flightRoll = flightRoll + rate1;
8172 else if (flightRoll > stick_roll + rate1)
8174 flightRoll = flightRoll - rate1;
8178 flightRoll = stick_roll;
8181 if (flightPitch < stick_pitch - rate2)
8183 flightPitch = flightPitch + rate2;
8185 else if (flightPitch > stick_pitch + rate2)
8187 flightPitch = flightPitch - rate2;
8191 flightPitch = stick_pitch;
8194 if (flightYaw < stick_yaw - rate3)
8196 flightYaw = flightYaw + rate3;
8198 else if (flightYaw > stick_yaw + rate3)
8200 flightYaw = flightYaw - rate3;
8204 flightYaw = stick_yaw;
8210- (void) setRoll:(
double) amount
8212 flightRoll = amount *
M_PI / 2.0;
8216- (void) setRawRoll:(
double) amount
8218 flightRoll = amount;
8222- (void) setPitch:(
double) amount
8224 flightPitch = amount *
M_PI / 2.0;
8228- (void) setYaw:(
double) amount
8230 flightYaw = amount *
M_PI / 2.0;
8234- (void) setThrust:(
double) amount
8240- (void) setThrustForDemo:(
float) factor
8242 flightSpeed = factor * maxFlightSpeed;
8248 [
self setBounty:amount withReason:kOOLegalStatusReasonUnknown];
8254 if ([
self isSubEntity])
8256 [[
self parentEntity] setBounty:amount withReason:reason];
8260 if ((scanClass == CLASS_THARGOID || scanClass == CLASS_STATION) && reason != kOOLegalStatusReasonSetup && reason != kOOLegalStatusReasonByScript)
8264 if (scanClass == CLASS_POLICE && amount != 0)
8269 [
self setBounty:amount withReasonAsString:nReason];
8273- (void) setBounty:(
OOCreditsQuantity) amount withReasonAsString:(NSString*)reason
8275 if ([
self isSubEntity])
8277 [[
self parentEntity] setBounty:amount withReasonAsString:reason];
8283 jsval amountVal = JSVAL_VOID;
8284 JS_NewNumberValue(context, (
int)amount-(
int)bounty, &amountVal);
8290 ShipScriptEvent(context,
self,
"shipBountyChanged", amountVal, reasonVal);
8301 if ([
self isSubEntity])
8303 return [[
self parentEntity] bounty];
8314 if (scanClass == CLASS_THARGOID)
8315 return 5 * collision_radius;
8316 if (scanClass == CLASS_ROCK)
8318 return (
int)[
self bounty];
8329 NSString *tmp = [co_type copy];
8331 commodity_type = tmp;
8332 commodity_amount = co_amount;
8343 commodity_amount = 0;
8348 OOMassUnit unit = [[UNIVERSE commodityMarket] massUnitForGood:co_type];
8349 if (unit ==
UNITS_TONS && co_amount > 1) co_amount = 1;
8350 else if (unit ==
UNITS_KILOGRAMS && co_amount > 1000) co_amount = 1000;
8351 else if (unit ==
UNITS_GRAMS && co_amount > 1000000) co_amount = 1000000;
8352 [
self setCommodity:co_type andAmount:co_amount];
8358 return commodity_type;
8364 return commodity_amount;
8370 return max_cargo - equipment_weight;
8376 max_cargo =
new + equipment_weight;
8383 if (
EXPECT_NOT([
self cargoQuantityOnBoard] + equipment_weight >= max_cargo))
return 0;
8384 return [
self maxAvailableCargoSpace] - [
self cargoQuantityOnBoard];
8390 NSUInteger result = [[
self cargo] count];
8391 NSAssert(result < UINT32_MAX,
@"Cargo quantity out of bounds.");
8405- (NSMutableArray*) cargo
8411- (NSArray *) cargoListForScripting
8413 NSMutableArray *list = [NSMutableArray array];
8416 NSArray *goods = [[UNIVERSE commodityMarket] goods];
8417 NSUInteger i, j, commodityCount = [goods count];
8420 for (i = 0; i < commodityCount; i++)
8422 quantityInHold[i] = 0;
8424 for (i = 0; i < [cargo count]; i++)
8426 ShipEntity *container = [cargo objectAtIndex:i];
8428 quantityInHold[j] += [container commodityAmount];
8431 for (i = 0; i < commodityCount; i++)
8433 if (quantityInHold[i] > 0)
8435 NSMutableDictionary *commodity = [NSMutableDictionary dictionaryWithCapacity:4];
8436 good = [goods objectAtIndex:i];
8438 [commodity setObject:good forKey:@"commodity"];
8439 [commodity setObject:[NSNumber numberWithUnsignedInt:quantityInHold[i]] forKey:@"quantity"];
8440 [commodity setObject:[[UNIVERSE commodityMarket] nameForGood:good] forKey:@"displayName"];
8441 [commodity setObject:DisplayStringForMassUnitForCommodity(good) forKey:@"unit"];
8442 [list addObject:commodity];
8446 return [[list copy] autorelease];
8449- (void) setCargo:(NSArray *) some_cargo
8451 [cargo removeAllObjects];
8452 [cargo addObjectsFromArray:some_cargo];
8456- (BOOL) addCargo:(NSArray *) some_cargo
8458 if ([cargo
count] + [some_cargo
count] > [
self maxAvailableCargoSpace])
8464 [cargo addObjectsFromArray:some_cargo];
8474 foreach (pod, cargo)
8476 if ([[pod commodityType] isEqualToString:commodity])
8487 NSUInteger i = [cargo count] - 1;
8491 if ([[[cargo objectAtIndex:i] commodityType] isEqualToString:commodity])
8494 [cargo removeObjectAtIndex:i];
8505- (BOOL) showScoopMessage
8507 return hasScoopMessage;
8519 if (cargo_flag != flag)
8522 NSArray *newCargo =
nil;
8524 if (likely_cargo > 0)
8526 num = likely_cargo * (0.5+
randf());
8527 if (num > [
self maxAvailableCargoSpace])
8529 num = [
self maxAvailableCargoSpace];
8534 num = [
self maxAvailableCargoSpace];
8549 newCargo = [UNIVERSE getContainersOfCommodity:[shipinfoDictionary oo_stringForKey:@"cargo_carried"] :num];
8552 newCargo = [UNIVERSE getContainersOfGoods:num scarce:NO legal:YES];
8555 newCargo = [UNIVERSE getContainersOfGoods:num scarce:YES legal:YES];
8558 newCargo = [UNIVERSE getContainersOfCommodity:@"Narcotics" :num];
8561 newCargo = [UNIVERSE getContainersOfGoods:num scarce:YES legal:NO];
8564 newCargo = [UNIVERSE getContainersOfGoods:(Ranrot() % (1+num/2)) scarce:YES legal:NO];
8573 [
self setCargo:newCargo];
8578- (void) setSpeed:(
double) amount
8580 flightSpeed = amount;
8584- (void) setDesiredSpeed:(
double) amount
8586 desired_speed = amount;
8590- (double) desiredSpeed
8592 return desired_speed;
8596- (double) desiredRange
8598 return desired_range;
8602- (void) setDesiredRange:(
double) amount
8604 desired_range = amount;
8608- (double) cruiseSpeed
8614- (void) increase_flight_speed:(
double) delta
8616 double factor = 1.0;
8617 if (desired_speed > maxFlightSpeed && [
self hasFuelInjection] && fuel >
MIN_FUEL) factor = [
self afterburnerFactor];
8619 if (flightSpeed < maxFlightSpeed * factor)
8620 flightSpeed += delta * factor;
8622 flightSpeed = maxFlightSpeed * factor;
8626- (void) decrease_flight_speed:(
double) delta
8628 double factor = 1.0;
8629 if (flightSpeed > maxFlightSpeed)
8634 if (flightSpeed > factor * delta)
8636 flightSpeed -= factor * delta;
8645- (void) increase_flight_roll:(
double) delta
8647 flightRoll += delta;
8648 if (flightRoll > max_flight_roll)
8649 flightRoll = max_flight_roll;
8650 else if (flightRoll < -max_flight_roll)
8651 flightRoll = -max_flight_roll;
8655- (void) decrease_flight_roll:(
double) delta
8657 flightRoll -= delta;
8658 if (flightRoll > max_flight_roll)
8659 flightRoll = max_flight_roll;
8660 else if (flightRoll < -max_flight_roll)
8661 flightRoll = -max_flight_roll;
8665- (void) increase_flight_pitch:(
double) delta
8667 flightPitch += delta;
8668 if (flightPitch > max_flight_pitch)
8669 flightPitch = max_flight_pitch;
8670 else if (flightPitch < -max_flight_pitch)
8671 flightPitch = -max_flight_pitch;
8675- (void) decrease_flight_pitch:(
double) delta
8677 flightPitch -= delta;
8678 if (flightPitch > max_flight_pitch)
8679 flightPitch = max_flight_pitch;
8680 else if (flightPitch < -max_flight_pitch)
8681 flightPitch = -max_flight_pitch;
8685- (void) increase_flight_yaw:(
double) delta
8688 if (flightYaw > max_flight_yaw)
8689 flightYaw = max_flight_yaw;
8690 else if (flightYaw < -max_flight_yaw)
8691 flightYaw = -max_flight_yaw;
8695- (void) decrease_flight_yaw:(
double) delta
8698 if (flightYaw > max_flight_yaw)
8699 flightYaw = max_flight_yaw;
8700 else if (flightYaw < -max_flight_yaw)
8701 flightYaw = -max_flight_yaw;
8705- (GLfloat) flightRoll
8711- (GLfloat) flightPitch
8717- (GLfloat) flightYaw
8723- (GLfloat) flightSpeed
8729- (GLfloat) maxFlightPitch
8731 return max_flight_pitch;
8735- (GLfloat) maxFlightSpeed
8737 return maxFlightSpeed;
8741- (GLfloat) maxFlightRoll
8743 return max_flight_roll;
8747- (GLfloat) maxFlightYaw
8749 return max_flight_yaw;
8753- (void) setMaxFlightPitch:(GLfloat)new
8755 max_flight_pitch =
new;
8759- (void) setMaxFlightSpeed:(GLfloat)new
8761 maxFlightSpeed =
new;
8765- (void) setMaxFlightRoll:(GLfloat)new
8767 max_flight_roll =
new;
8771- (void) setMaxFlightYaw:(GLfloat)new
8773 max_flight_yaw =
new;
8777- (GLfloat) speedFactor
8779 if (maxFlightSpeed <= 0.0)
return 0.0;
8780 return flightSpeed / maxFlightSpeed;
8784- (GLfloat) temperature
8786 return ship_temperature;
8790- (void) setTemperature:(GLfloat) value
8792 ship_temperature = value;
8796- (float) randomEjectaTemperature
8798 return [
self randomEjectaTemperatureWithMaxFactor:0.99f];
8802- (float) randomEjectaTemperatureWithMaxFactor:(
float)factor
8804 const float kRange = 0.02f;
8807 float parentTemp = [
self temperature];
8808 float adjusted = parentTemp * (
bellf(5) * (kRange * 2.0f) - kRange + factor);
8821- (GLfloat) heatInsulation
8823 return _heatInsulation;
8827- (void) setHeatInsulation:(GLfloat) value
8829 _heatInsulation = value;
8835 return (
int)(100 - (100 * energy / maxEnergy));
8839- (void) dealEnergyDamage:(GLfloat) baseDamage atRange:(GLfloat) range withBias:(GLfloat) velocityBias
8844 OOLog(
@"missile.damage.calc",
@"Range: %f | Damage: %f | MaxRange: %f",range,baseDamage,maxRange);
8846 NSArray *targets = [UNIVERSE entitiesWithinRange:maxRange ofEntity:self];
8847 if ([targets
count] > 0)
8850 for (i = 0; i < [targets count]; i++)
8852 Entity *e2 = [targets objectAtIndex:i];
8853 Vector p2 = [
self vectorTo:e2];
8855 double d = (magnitude(p2) - ecr) / range;
8857 double localDamage = baseDamage;
8858 OOLog(
@"missile.damage.calc",
@"Base damage: %f",baseDamage);
8859 if (velocityBias > 0)
8861 Vector v2 = vector_subtract([
self velocity], [e2 velocity]);
8862 double vSign = dot_product(vector_normal([
self velocity]), vector_normal(p2));
8867 double vMag = vSign * magnitude(v2);
8868 if (vMag > 1000.0) {
8874 localDamage += vMag * velocityBias;
8875 OOLog(
@"missile.damage.calc",
@"Velocity magnitude + sign: %f , %f",magnitude(v2),vSign);
8876 OOLog(
@"missile.damage.calc",
@"Velocity magnitude factor: %f",vMag);
8877 OOLog(
@"missile.damage.calc",
@"Velocity corrected damage: %f",localDamage);
8879 double damage = (d > 1) ? localDamage / (d * d) : localDamage;
8880 OOLog(
@"missile.damage.calc",
@"%f at range %f (d=%f)",damage,magnitude(p2)-ecr,d);
8904 NSDictionary *explosion = [UNIVERSE explosionSetting:@"oolite-default-ship-explosion"];
8912- (void) dealEnergyDamageWithinDesiredRange
8914 OOStandardsDeprecated([NSString stringWithFormat:
@"dealEnergyDamageWithinDesiredRange is deprecated for %@",
self]);
8916 NSArray* targets = [UNIVERSE entitiesWithinRange:(desired_range < SCANNER_MAX_RANGE ? desired_range : SCANNER_MAX_RANGE) ofEntity:self];
8917 if ([targets
count] > 0)
8920 for (i = 0; i < [targets count]; i++)
8922 Entity *e2 = [targets objectAtIndex:i];
8923 Vector p2 = [
self vectorTo:e2];
8925 double d = (magnitude(p2) - ecr) * 2.6;
8926 double damage = (d > 0) ? weapon_damage * desired_range / (d * d) : weapon_damage;
8933- (void) dealMomentumWithinDesiredRange:(
double)amount
8935 NSArray* targets = [UNIVERSE entitiesWithinRange:desired_range ofEntity:self];
8936 if ([targets
count] > 0)
8939 for (i = 0; i < [targets count]; i++)
8942 if ([e2 isShip] && [e2 isInSpace])
8944 Vector p2 = [
self vectorTo:e2];
8946 double d2 = magnitude2(p2) - ecr * ecr;
8952 double moment = amount*desired_range/d2;
8966- (void) setHulk:(BOOL)isNowHulk
8968 if (![
self isSubEntity])
8977 if (amount < 0 || (amount == 0 && [[
UNIVERSE gameController] isGamePaused]))
return;
8981 jsval amountVal = JSVAL_VOID;
8982 JS_NewNumberValue(context, amount, &amountVal);
8986 ShipScriptEvent(context,
self,
"shipTakingDamage", amountVal, entityVal, typeVal);
8989 if ([entity isShip]) {
8991 if ([
self hasHostileTarget] && accuracy >=
COMBAT_AI_IS_SMART && (
randf()*10.0 < accuracy || desired_speed < 0.5 * maxFlightSpeed) && behaviour != BEHAVIOUR_EVASIVE_ACTION && behaviour != BEHAVIOUR_FLEE_EVASIVE_ACTION && behaviour != BEHAVIOUR_SCRIPTED_ATTACK_AI)
8993 if (behaviour == BEHAVIOUR_FLEE_TARGET)
8997 behaviour = BEHAVIOUR_FLEE_EVASIVE_ACTION;
9001 behaviour = BEHAVIOUR_EVASIVE_ACTION;
9012 if ([
self status] == STATUS_DEAD)
return;
9014 [PLAYER setScriptTarget:self];
9021 [
self setStatus:STATUS_DEAD];
9030 [
self setStatus:originalStatus];
9037 [
self noteKilledBy:whom damageType:type];
9038 [
self abortDocking];
9039 [
self becomeExplosion];
9043- (void) rescaleBy:(GLfloat)factor
9045 [
self rescaleBy:factor writeToCache:YES];
9049- (void) rescaleBy:(GLfloat)factor writeToCache:(BOOL)writeToCache
9051 _scaleFactor *= factor;
9054 NSDictionary *shipDict = [
self shipInfoDictionary];
9055 NSString *modelName = [shipDict oo_stringForKey:@"model"];
9056 if (modelName !=
nil)
9059 cacheKey:[NSString stringWithFormat:@"%@-%.3f",_shipKey,_scaleFactor]
9062 smooth:[shipDict oo_boolForKey:@"smooth" defaultValue:NO]
9068 if (mesh ==
nil)
return;
9069 [
self setMesh:mesh];
9074 foreach (se, [
self subEntities])
9077 [se rescaleBy:factor writeToCache:writeToCache];
9081 mass *= factor * factor * factor;
9085- (void) releaseCargoPodsDebris
9087 HPVector xposition = position;
9091 int speed_low = 200;
9093 NSArray *jetsam =
nil;
9094 unsigned cargo_chance = 70;
9095 jetsam = [NSArray arrayWithArray:cargo];
9096 [cargo removeAllObjects];
9097 unsigned limit = 15;
9099 NSUInteger n_jetsam = [jetsam count];
9101 for (i = 0; i < n_jetsam; i++)
9103 if (
Ranrot() % 100 < cargo_chance)
9106 if (cargo_chance > 10)
9118 ShipEntity* cargoObj = [jetsam objectAtIndex:i];
9119 ShipEntity* container = [UNIVERSE reifyCargoPod:cargoObj];
9123 HPVector rpos = xposition;
9125 rpos.x += rrand.
x; rpos.y += rrand.
y; rpos.z += rrand.z;
9130 v.x = 0.1 *((
ranrot_rand() % speed_low) - speed_low / 2);
9131 v.y = 0.1 *((
ranrot_rand() % speed_low) - speed_low / 2);
9132 v.z = 0.1 *((
ranrot_rand() % speed_low) - speed_low / 2);
9133 [container
setVelocity:vector_add(v,[
self velocity])];
9139 [UNIVERSE addEntity:container];
9141 AI *containerAI = [container
getAI];
9142 if ([containerAI hasSuspendedStateMachines])
9158- (void) setIsWreckage:(BOOL)isw
9170- (void) becomeExplosion
9178 HPVector this_pos = [
self absolutePositionForSubentity];
9182 [UNIVERSE addEntity:this_ship];
9184 [this_ship release];
9185 if ([parent isPlayer])
9188 [(
PlayerEntity *)
parent adjustTradeInFactorBy:-PLAYER_SHIP_SUBENTITY_TRADE_IN_VALUE];
9192 HPVector xposition = position;
9196 int speed_low = 200;
9197 GLfloat n_alloys = sqrtf(sqrtf(mass / 6000.0f));
9198 NSUInteger numAlloys = 0;
9199 BOOL canReleaseSubWreckage = isWreckage && ([UNIVERSE detailLevel] >=
DETAIL_LEVEL_EXTRAS);
9201 if ([
self status] == STATUS_DEAD)
9203 [UNIVERSE removeEntity:self];
9206 [
self setStatus:STATUS_DEAD];
9210 if ([
self isThargoid] && [roleSet hasRole:
@"thargoid-mothership"]) [
self broadcastThargoidDestroyed];
9212 if (!suppressExplosion && ([
self isVisible] || HPdistance2([
self position], [
PLAYER position]) <
SCANNER_MAX_RANGE2))
9214 if (!isWreckage && mass > 500000.0f &&
randf() < 0.25f)
9218 [ring
setVelocity:vector_multiply_scalar([
self velocity], 0.25f)];
9219 [UNIVERSE addEntity:ring];
9247 NSString *explosionKey =
@"oolite-default-ship-explosion";
9248 NSDictionary *explosion =
nil;
9249 if (explosionType ==
nil)
9251 explosion = [UNIVERSE explosionSetting:explosionKey];
9256 for (NSUInteger i=0;i<[explosionType count];i++)
9258 explosionKey = [explosionType oo_stringAtIndex:i defaultValue:nil];
9259 if (explosionKey !=
nil)
9262 if ([explosionKey isEqualToString:
@"oolite-builtin-flash"])
9266 else if ([explosionKey isEqualToString:
@"oolite-builtin-slowcloud"])
9270 else if ([explosionKey isEqualToString:
@"oolite-builtin-fastspark"])
9276 explosion = [UNIVERSE explosionSetting:explosionKey];
9290 [
self releaseCargoPodsDebris];
9293 if ([
self hasRole:
@"asteroid"] || [
self isBoulder])
9295 if (!noRocks && (being_mined ||
randf() < 0.20))
9297 NSString *defaultRole =
@"boulder";
9298 float defaultSpeed = 50.0;
9299 if ([
self isBoulder])
9301 defaultRole =
@"splinter";
9302 defaultSpeed = 20.0;
9303 if (likely_cargo == 0)
9308 else if ([[
self primaryAggressor] isPlayer])
9310 [PLAYER addRoleForMining];
9312 NSUInteger n_rocks = 2 + (
Ranrot() % (likely_cargo + 1));
9314 NSString *debrisRole = [[
self shipInfoDictionary] oo_stringForKey:@"debris_role" defaultValue:defaultRole];
9315 for (i = 0; i < n_rocks; i++)
9317 ShipEntity* rock = [UNIVERSE newShipWithRole:debrisRole];
9321 float cr = (collision_radius < rock->
collision_radius) ? collision_radius : 2 * rock->collision_radius;
9322 v.x = ((
randf() * r_speed) - r_speed / 2);
9323 v.y = ((
randf() * r_speed) - r_speed / 2);
9324 v.z = ((
randf() * r_speed) - r_speed / 2);
9326 HPVector rpos = HPvector_add(xposition,vectorToHPVector(vector_multiply_scalar(vector_normal(v),cr)));
9333 if ([
self isBoulder])
9338 if ([[rock shipInfoDictionary] oo_stringForKey:
@"cargo_carried"] ==
nil)
9346 [UNIVERSE addEntity:rock];
9356 if ((n_alloys && canFragment) || canReleaseSubWreckage)
9358 NSUInteger n_wreckage = 0;
9364 NSUInteger maxWrecks = 3;
9368 n_wreckage = (mass > 600.0 &&
randf() < 0.2)?2:0;
9372 n_wreckage = (n_alloys < maxWrecks)? floorf(
randf()*(n_alloys+2)) : maxWrecks;
9376 for (i = 0; i < n_wreckage; i++)
9378 Vector r1 = [octree randomPoint];
9380 HPVector rpos = HPvector_add(vectorToHPVector(dir), xposition);
9381 GLfloat lifetime = 750.0 *
randf() + 250.0 * i + 100.0;
9382 ShipEntity *wreck = [UNIVERSE addWreckageFrom:self withRole:@"wreckage" at:rpos scale:1.0 lifetime:lifetime/2];
9387 n_alloys =
randf() * n_alloys;
9396 else if (!add_debris)
9398 n_alloys = (n_alloys > 1.0) ? 1.0 : 0.0;
9402 numAlloys = floorf(n_alloys);
9406 for (i = 0; i < numAlloys; i++)
9408 ShipEntity* plate = [UNIVERSE newShipWithRole:@"alloy"];
9411 HPVector rpos = xposition;
9413 rpos.x += rrand.
x; rpos.y += rrand.
y; rpos.z += rrand.z;
9418 v.x = 0.1 *((
ranrot_rand() % speed_low) - speed_low / 2);
9419 v.y = 0.1 *((
ranrot_rand() % speed_low) - speed_low / 2);
9420 v.z = 0.1 *((
ranrot_rand() % speed_low) - speed_low / 2);
9421 [plate
setVelocity:vector_add(v,[
self velocity])];
9428 [UNIVERSE addEntity:plate];
9436 NSEnumerator *subEnum =
nil;
9438 for (subEnum = [
self shipSubEntityEnumerator]; (se = [subEnum nextObject]); )
9443 [
self clearSubEntities];
9446 if (!suppressExplosion)
9448 desired_range = collision_radius * 2.5f;
9449 [
self dealMomentumWithinDesiredRange:0.125f * mass];
9457 OOLog(
@"becomeExplosion.suspectedGhost.confirm",
@"%@",
@"Ship spotted with isPlayer set when not actually the player.");
9467 [UNIVERSE removeEntity:self];
9474- (void) becomeEnergyBlast
9477 [
self broadcastEnergyBlastImminent];
9478 [
self noteKilledBy:nil damageType:kOODamageTypeCascadeWeapon];
9479 [UNIVERSE removeEntity:self];
9484- (void) broadcastEnergyBlastImminent
9487 NSArray* targets = [UNIVERSE entitiesWithinRange:SCANNER_MAX_RANGE ofEntity:self];
9488 if ([targets
count] > 0)
9491 for (i = 0; i < [targets count]; i++)
9493 Entity *e2 = [targets objectAtIndex:i];
9508 [subEntities removeObject:exhaust];
9515 [subEntities removeObject:flasher];
9522 if ([
self subEntityTakingDamage] == sub) [
self setSubEntityTakingDamage:nil];
9528 [subEntities removeObject:sub];
9534 if ([
self subEntityTakingDamage] == sub) [
self setSubEntityTakingDamage:nil];
9536 if ([
self hasSubEntity:sub])
9538 OOLogERR(
@"shipEntity.bug.subEntityRetainUnderflow",
@"Subentity of %@ died while still in subentity list! This is bad. Leaking subentity list to avoid crash. %@",
self,
@"This is an internal error, please report it.");
9546- (Vector) positionOffsetForAlignment:(NSString*) align
9548 NSString* padAlign = [NSString stringWithFormat:@"%@---", align];
9550 switch ([padAlign characterAtIndex:0])
9554 result.x = 0.5 * (boundingBox.min.x + boundingBox.max.
x);
9557 result.x = boundingBox.max.x;
9560 result.x = boundingBox.min.x;
9563 switch ([padAlign characterAtIndex:1])
9567 result.y = 0.5 * (boundingBox.min.y + boundingBox.max.
y);
9570 result.y = boundingBox.max.y;
9573 result.y = boundingBox.min.y;
9576 switch ([padAlign characterAtIndex:2])
9580 result.z = 0.5 * (boundingBox.min.z + boundingBox.max.z);
9583 result.z = boundingBox.max.z;
9586 result.z = boundingBox.min.z;
9593Vector positionOffsetForShipInRotationToAlignment(
ShipEntity* ship, Quaternion q, NSString* align)
9595 NSString* padAlign = [NSString stringWithFormat:@"%@---", align];
9601 switch ([padAlign characterAtIndex:0])
9605 result.x = 0.5 * (arbb.min.x + arbb.max.x);
9608 result.x = arbb.max.x;
9611 result.x = arbb.min.x;
9614 switch ([padAlign characterAtIndex:1])
9618 result.y = 0.5 * (arbb.min.y + arbb.max.y);
9621 result.y = arbb.max.y;
9624 result.y = arbb.min.y;
9627 switch ([padAlign characterAtIndex:2])
9631 result.z = 0.5 * (arbb.min.z + arbb.max.z);
9634 result.z = arbb.max.z;
9637 result.z = arbb.min.z;
9644- (void) becomeLargeExplosion:(
double)factor
9647 if ([
self status] == STATUS_DEAD)
return;
9648 [
self setStatus:STATUS_DEAD];
9654 float how_many = factor;
9655 while (how_many > 0.5f)
9662 while (how_many > 0.5f)
9668 [
self releaseCargoPodsDebris];
9670 NSEnumerator *subEnum =
nil;
9672 for (subEnum = [
self shipSubEntityEnumerator]; (se = [subEnum nextObject]); )
9677 [
self clearSubEntities];
9682 if (!isPlayer) [UNIVERSE removeEntity:self];
9689 if ([other isPolice])
9691 [
self markAsOffender:64 withReason:kOOLegalStatusReasonAttackedPolice];
9696- (NSComparisonResult) compareBeaconCodeWith:(
Entity<OOBeaconEntity> *) other
9698 return [[
self beaconCode] compare:[other
beaconCode] options: NSCaseInsensitiveSearch];
9703- (GLfloat) weaponRecoveryTime
9705 float result = (weapon_recharge_rate - [
self shotTime]) / weapon_recharge_rate;
9706 return OOClamp_0_1_f(result);
9710- (GLfloat)laserHeatLevel
9713 return OOClamp_0_1_f(result);
9717- (GLfloat)laserHeatLevelAft
9720 return OOClamp_0_1_f(result);
9724- (GLfloat)laserHeatLevelForward
9730 NSEnumerator *subEnum = [
self shipSubEntityEnumerator];
9732 while (
isWeaponNone(forward_weapon_real_type) && (se = [subEnum nextObject]))
9741 return OOClamp_0_1_f(result);
9745- (GLfloat)laserHeatLevelPort
9748 return OOClamp_0_1_f(result);
9752- (GLfloat)laserHeatLevelStarboard
9755 return OOClamp_0_1_f(result);
9759- (GLfloat)hullHeatLevel
9762 return OOClamp_0_1_f(result);
9766- (GLfloat)entityPersonality
9772- (GLint)entityPersonalityInt
9774 return entity_personality;
9778- (uint32_t) randomSeedForShaders
9780 return entity_personality * 0x00010001;
9784- (void) setEntityPersonalityInt:(uint16_t)value
9788 entity_personality = value;
9789 [[
self mesh] rebindMaterials];
9794- (void)setSuppressExplosion:(BOOL)suppress
9796 suppressExplosion = !!suppress;
9800- (void) resetExhaustPlumes
9802 NSEnumerator *exEnum =
nil;
9805 for (exEnum = [
self exhaustEnumerator]; (exEnt = [exEnum nextObject]); )
9819- (void) checkScanner
9822 n_scanned_ships = 0;
9824 scan = z_previous;
while ((scan)&&(scan->
isShip == NO)) scan = scan->
z_previous;
9825 GLfloat scannerRange2 = scannerRange * scannerRange;
9829 if (scan->
isShip && ![(
ShipEntity*)scan isCloaked] && [
self isValidTarget:scan])
9831 distance2_scanned_ships[n_scanned_ships] = HPdistance2(position, scan->position);
9832 if (distance2_scanned_ships[n_scanned_ships] < scannerRange2)
9833 scanned_ships[n_scanned_ships++] = (
ShipEntity*)scan;
9835 scan = scan->z_previous;
while ((scan)&&(scan->isShip == NO)) scan = scan->z_previous;
9838 scan = z_next;
while ((scan)&&(scan->isShip == NO)) scan = scan->
z_next;
9839 while ((scan)&&(scan->position.z < position.z + scannerRange)&&(n_scanned_ships <
MAX_SCAN_NUMBER))
9841 if (scan->isShip && ![(
ShipEntity*)scan isCloaked] && [
self isValidTarget:scan])
9843 distance2_scanned_ships[n_scanned_ships] = HPdistance2(position, scan->position);
9844 if (distance2_scanned_ships[n_scanned_ships] < scannerRange2)
9845 scanned_ships[n_scanned_ships++] = (
ShipEntity*)scan;
9847 scan = scan->z_next;
while ((scan)&&(scan->isShip == NO)) scan = scan->z_next;
9850 scanned_ships[n_scanned_ships] =
nil;
9854- (void) checkScannerIgnoringUnpowered
9857 n_scanned_ships = 0;
9859 GLfloat scannerRange2 = scannerRange * scannerRange;
9869 distance2_scanned_ships[n_scanned_ships] = HPdistance2(position, scan->
position);
9870 if (distance2_scanned_ships[n_scanned_ships] < scannerRange2)
9871 scanned_ships[n_scanned_ships++] = (
ShipEntity*)scan;
9890 distance2_scanned_ships[n_scanned_ships] = HPdistance2(position, scan->
position);
9891 if (distance2_scanned_ships[n_scanned_ships] < scannerRange2)
9892 scanned_ships[n_scanned_ships++] = (
ShipEntity*)scan;
9901 scanned_ships[n_scanned_ships] =
nil;
9907 scanned_ships[n_scanned_ships] =
nil;
9908 return scanned_ships;
9912- (
int) numberOfScannedShips
9914 return n_scanned_ships;
9920 Entity *result = [_foundTarget weakRefUnderlyingObject];
9921 if (result ==
nil || ![
self isValidTarget:result])
9930- (void) setFoundTarget:(
Entity *) targetEntity
9932 [_foundTarget release];
9937- (
Entity *) primaryAggressor
9939 Entity *result = [_primaryAggressor weakRefUnderlyingObject];
9940 if (result ==
nil || ![
self isValidTarget:result])
9949- (void) setPrimaryAggressor:(
Entity *) targetEntity
9951 [_primaryAggressor release];
9952 _primaryAggressor = [targetEntity
weakRetain];
9956- (
Entity *) lastEscortTarget
9958 Entity *result = [_lastEscortTarget weakRefUnderlyingObject];
9959 if (result ==
nil || ![
self isValidTarget:result])
9968- (void) setLastEscortTarget:(
Entity *) targetEntity
9970 [_lastEscortTarget release];
9971 _lastEscortTarget = [targetEntity
weakRetain];
9977 Entity *result = [_thankedShip weakRefUnderlyingObject];
9978 if (result ==
nil || ![
self isValidTarget:result])
9987- (void) setThankedShip:(
Entity *) targetEntity
9989 [_thankedShip release];
9994- (
Entity *) rememberedShip
9996 Entity *result = [_rememberedShip weakRefUnderlyingObject];
9997 if (result ==
nil || ![
self isValidTarget:result])
10006- (void) setRememberedShip:(
Entity *) targetEntity
10008 [_rememberedShip release];
10009 _rememberedShip = [targetEntity
weakRetain];
10015 StationEntity *result = [_targetStation weakRefUnderlyingObject];
10016 if (result ==
nil || ![
self isValidTarget:result])
10025- (void) setTargetStation:(
Entity *) targetEntity
10027 [_targetStation release];
10034- (BOOL) isValidTarget:(
Entity *)target
10040 if ([target isShip])
10043 if (tstatus == STATUS_ENTERING_WITCHSPACE || tstatus == STATUS_IN_HOLD || tstatus == STATUS_DOCKED || tstatus == STATUS_DEAD)
10050 if ([target isWormhole] && [target scanClass] != CLASS_NO_DRAW)
10058- (void) addTarget:(
Entity *) targetEntity
10060 if (targetEntity ==
self)
return;
10061 if (targetEntity !=
nil)
10065 [
self startTrackingCurve];
10068 [[
self shipSubEntityEnumerator] makeObjectsPerformSelector:@selector(addTarget:) withObject:targetEntity];
10069 if (![
self isSubEntity]) [
self doScriptEvent:OOJSID("shipTargetAcquired") withArgument:targetEntity];
10073- (void) removeTarget:(
Entity *) targetEntity
10075 if(targetEntity !=
nil) [
self noteLostTarget];
10076 else DESTROY(_primaryTarget);
10080 [[
self shipSubEntityEnumerator] makeObjectsPerformSelector:@selector(removeTarget:) withObject:targetEntity];
10095- (BOOL) canStillTrackPrimaryTarget
10097 Entity *target = (
Entity *)[
self primaryTargetWithoutValidityCheck];
10102 if (![
self isValidTarget:target])
10106 double range2 = HPmagnitude2(HPvector_subtract([target position], position));
10107 if (range2 > scannerRange * scannerRange * 1.5625)
10121- (id) primaryTarget
10123 id result = [_primaryTarget weakRefUnderlyingObject];
10124 if ((result ==
nil && _primaryTarget !=
nil)
10125 || ![
self isValidTarget:result])
10144- (id) primaryTargetWithoutValidityCheck
10146 id result = [_primaryTarget weakRefUnderlyingObject];
10157- (BOOL) isFriendlyTo:(
ShipEntity *)otherShip
10159 BOOL isFriendly = NO;
10163 if ((otherShip ==
self) ||
10164 ([
self isPolice] && [otherShip isPolice]) ||
10165 ([
self isThargoid] && [otherShip isThargoid]) ||
10166 (myGroup !=
nil && otherGroup !=
nil && (myGroup == otherGroup || [otherGroup leader] ==
self)) ||
10167 ([
self scanClass] == CLASS_MILITARY && [otherShip scanClass] == CLASS_MILITARY))
10178 return [_shipHitByLaser weakRefUnderlyingObject];
10182- (void) setShipHitByLaser:(
ShipEntity *)ship
10184 if (ship != [
self shipHitByLaser])
10186 [_shipHitByLaser release];
10192- (void) noteLostTarget
10195 if ([
self primaryTarget] !=
nil)
10198 if ([
self isDefenseTarget:ship])
10200 [
self removeDefenseTarget:ship];
10205 target = (ship && ship->isShip && [
self isValidTarget:ship]) ? (
id)ship :
nil;
10206 if ([
self primaryAggressor] == ship)
10213 [
self doScriptEvent:OOJSID("shipTargetLost") withArgument:target];
10214 if (target ==
nil) [shipAI message:@"TARGET_LOST"];
10215 else [shipAI reactToMessage:@"TARGET_LOST" context:@"flight updates"];
10219- (void) noteLostTargetAndGoIdle
10221 behaviour = BEHAVIOUR_IDLE;
10223 [
self noteLostTarget];
10226- (void) noteTargetDestroyed:(
ShipEntity *)target
10228 [
self collectBountyFor:(ShipEntity *)target];
10229 if ([
self primaryTarget] == target)
10231 [
self removeTarget:target];
10232 [
self doScriptEvent:OOJSID("shipTargetDestroyed") withArgument:target];
10233 [shipAI message:@"TARGET_DESTROYED"];
10235 if ([
self isDefenseTarget:target])
10237 [
self removeDefenseTarget:target];
10238 [shipAI message:@"DEFENSE_TARGET_DESTROYED"];
10239 [
self doScriptEvent:OOJSID("defenseTargetDestroyed") withArgument:target];
10252 if (cond != behaviour)
10260- (HPVector) destination
10262 return _destination;
10265- (HPVector) coordinates
10267 return coordinates;
10270- (void) setCoordinate:(HPVector) coord
10272 coordinates = coord;
10275- (HPVector) distance_six: (GLfloat) dist
10277 HPVector six = position;
10278 six.x -= dist * v_forward.
x; six.y -= dist * v_forward.
y; six.z -= dist * v_forward.z;
10283- (HPVector) distance_twelve: (GLfloat) dist withOffset:(GLfloat)offset
10285 HPVector twelve = position;
10286 twelve.x += dist * v_up.
x; twelve.y += dist * v_up.
y; twelve.z += dist * v_up.z;
10287 twelve.x +=
offset * v_right.
x; twelve.y +=
offset * v_right.
y; twelve.z +=
offset * v_right.z;
10292- (void) trackOntoTarget:(
double) delta_t withDForward: (GLfloat) dp
10294 Vector vector_to_target;
10295 Quaternion q_minarc;
10297 Entity* target = [
self primaryTarget];
10302 vector_to_target = [
self vectorTo:target];
10304 GLfloat range2 = magnitude2(vector_to_target);
10306 GLfloat max_cos = sqrt(1 - targetRadius*targetRadius/range2);
10311 if (vector_to_target.x||vector_to_target.y||vector_to_target.z)
10312 vector_to_target = vector_normal(vector_to_target);
10314 vector_to_target.z = 1.0;
10319 [
self orientationChanged];
10330- (double) ballTrackLeadingTarget:(
double) delta_t atTarget:(
Entity *)target
10337 Vector vector_to_target;
10338 Vector axis_to_track_by;
10340 Vector my_ref = reference;
10341 double aim_cos, ref_cos;
10342 Vector leading = [target
velocity];
10345 HPVector my_position = [
self absolutePositionForSubentity];
10346 vector_to_target = HPVectorToVector(HPvector_subtract([target position], my_position));
10350 Entity *father = [
self parentEntity];
10353 while ((father)&&(father != last) && (father !=
NO_TARGET))
10365 if (![last isSubEntity])
break;
10366 father = [father
owner];
10368 q = quaternion_conjugate(q);
10376 if (magnitude(vector_to_target) > weaponRange * 1.01)
10383 vector_to_target = vector_add(vector_to_target, vector_multiply_scalar(leading, lead));
10384 vector_to_target = vector_normal_or_fallback(vector_to_target,
kBasisZVector);
10387 aim_cos = dot_product(vector_to_target, my_aim);
10388 ref_cos = dot_product(vector_to_target, my_ref);
10393 axis_to_track_by = cross_product(vector_to_target, my_aim);
10401 [
self orientationChanged];
10403 [
self setStatus:STATUS_ACTIVE];
10409- (void) setEvasiveJink:(GLfloat) z
10442- (void) evasiveAction:(
double) delta_t
10444 stick_roll = flightRoll;
10445 stick_pitch = flightPitch;
10450 [
self noteLostTargetAndGoIdle];
10454 double agreement = dot_product(v_right,target->
v_right);
10455 if (agreement > -0.3 && agreement < 0.3)
10461 if (stick_roll >= 0.0) {
10462 stick_roll = max_flight_roll;
10464 stick_roll = -max_flight_roll;
10467 if (stick_pitch >= 0.0) {
10468 stick_pitch = max_flight_pitch;
10470 stick_pitch = -max_flight_pitch;
10473 [
self applySticks:delta_t];
10477- (double) trackPrimaryTarget:(
double) delta_t :(BOOL) retreat
10479 Entity* target = [
self primaryTarget];
10483 [
self noteLostTargetAndGoIdle];
10487 if (![
self canStillTrackPrimaryTarget])
10489 [
self noteLostTargetAndGoIdle];
10497 if (scanClass == CLASS_MISSILE)
10498 return [
self missileTrackPrimaryTarget: delta_t];
10500 GLfloat d_forward, d_up, d_right;
10502 Vector relPos = HPVectorToVector(HPvector_subtract([
self calculateTargetPosition], position));
10504 double range2 = HPmagnitude2(HPvector_subtract([target position], position));
10514 vy = targetShip->
v_up;
10525 BOOL avoidCollision = NO;
10526 if (range2 < collision_radius * target->collision_radius * 100.0)
10529 if (!vector_equal(relPos,
kZeroVector)) targetDirection = vector_normal(relPos);
10530 avoidCollision = (dot_product(targetDirection, v_forward) > -0.1);
10533 GLfloat dist_adjust_factor = 1.0;
10536 double range = magnitude(relPos);
10537 if (range > 2000.0)
10539 dist_adjust_factor = range / 2000.0;
10542 dist_adjust_factor *= 3;
10545 if (jink.x == 0.0 && behaviour != BEHAVIOUR_RUNNING_DEFENSE)
10547 [
self setEvasiveJink:400.0];
10551 if (!avoidCollision)
10553 relPos.x += (jink.
x * vx.
x + jink.
y * vy.
x + jink.z * vz.
x) * dist_adjust_factor;
10554 relPos.y += (jink.
x * vx.
y + jink.
y * vy.
y + jink.z * vz.
y) * dist_adjust_factor;
10555 relPos.z += (jink.
x * vx.z + jink.
y * vy.z + jink.z * vz.z);
10560 if (!vector_equal(relPos,
kZeroVector)) relPos = vector_normal(relPos);
10561 else relPos.z = 1.0;
10563 double max_cos = [
self currentAimTolerance];
10568 double reverse = (retreat)? -1.0: 1.0;
10570 double min_d = 0.004;
10571 int max_factor = 8;
10572 double r_max_factor = 0.125;
10578 if (max_flight_pitch > 1.0)
10580 max_factor = floor(max_flight_pitch/0.125);
10581 r_max_factor = 1.0/max_factor;
10585 r_max_factor /= 3.0;
10590 min_d -= 0.0001 * [
self missedShots];
10595 r_max_factor /= 2.0;
10600 d_right = dot_product(relPos, v_right);
10601 d_up = dot_product(relPos, v_up);
10602 d_forward = dot_product(relPos, v_forward);
10604 if (d_forward * reverse > max_cos)
10614 if ((reverse * d_forward < -0.5) && !pitching_over)
10615 pitching_over = YES;
10619 if (reverse * d_up > 0)
10620 stick_pitch = -max_flight_pitch;
10622 stick_pitch = max_flight_pitch;
10623 pitching_over = (reverse * d_forward < 0.707);
10627 if ((d_forward < max_cos)||(retreat))
10629 if (d_forward < -max_cos)
10631 d_up = min_d * 2.0;
10636 int factor = sqrt(fabs(d_right) / fabs(min_d));
10637 if (factor > max_factor)
10638 factor = max_factor;
10639 if (d_right > min_d)
10640 stick_roll = - max_flight_roll * r_max_factor * factor;
10641 if (d_right < -min_d)
10642 stick_roll = + max_flight_roll * r_max_factor * factor;
10646 int factor = sqrt(fabs(d_right) / fabs(min_d));
10647 if (factor > max_factor)
10648 factor = max_factor;
10649 if (d_right > min_d)
10650 stick_roll = + max_flight_roll * r_max_factor * factor;
10651 if (d_right < -min_d)
10652 stick_roll = - max_flight_roll * r_max_factor * factor;
10655 if (stick_roll == 0.0)
10657 int factor = sqrt(fabs(d_up) / fabs(min_d));
10658 if (factor > max_factor)
10659 factor = max_factor;
10661 stick_pitch = - max_flight_pitch * reverse * r_max_factor * factor;
10663 stick_pitch = + max_flight_pitch * reverse * r_max_factor * factor;
10669 if (fabs(d_right) < fabs(stick_roll) * delta_t)
10671 stick_roll = fabs(d_right) / delta_t * (stick_roll<0 ? -1 : 1);
10673 if (fabs(d_up) < fabs(stick_pitch) * delta_t)
10675 stick_pitch = fabs(d_up) / delta_t * (stick_pitch<0 ? -0.9 : 0.9);
10696 [
self applySticks:delta_t];
10699 d_forward *= d_forward;
10701 if (d_forward < 0.0)
10704 if ((!flightRoll)&&(!flightPitch))
10711- (double) trackSideTarget:(
double) delta_t :(BOOL) leftside
10713 Entity* target = [
self primaryTarget];
10717 [
self noteLostTargetAndGoIdle];
10721 if (![
self canStillTrackPrimaryTarget])
10723 [
self noteLostTargetAndGoIdle];
10728 if (scanClass == CLASS_MISSILE)
10729 return [
self missileTrackPrimaryTarget: delta_t];
10731 GLfloat d_forward, d_up, d_right;
10733 Vector relPos = HPVectorToVector(HPvector_subtract([
self calculateTargetPosition], position));
10736 if (!vector_equal(relPos,
kZeroVector)) relPos = vector_normal(relPos);
10737 else relPos.z = 1.0;
10741 double max_cos = [
self currentAimTolerance];
10747 double reverse = (leftside)? -1.0: 1.0;
10749 double min_d = 0.004;
10754 int max_factor = 8;
10755 double r_max_factor = 0.125;
10757 d_right = dot_product(relPos, v_right);
10758 d_up = dot_product(relPos, v_up);
10759 d_forward = dot_product(relPos, v_forward);
10761 if (d_right * reverse > max_cos)
10763 return d_right * reverse;
10772 if ((d_right * reverse < max_cos))
10774 if (d_right < -max_cos)
10776 d_forward = min_d * 2.0;
10779 if (d_forward > min_d)
10781 int factor = sqrt(fabs(d_up) / fabs(min_d));
10782 if (factor > max_factor)
10783 factor = max_factor;
10785 stick_pitch = + max_flight_pitch * r_max_factor * factor;
10787 stick_pitch = - max_flight_pitch * r_max_factor * factor;
10789 if (d_forward < -min_d)
10791 int factor = sqrt(fabs(d_up) / fabs(min_d));
10792 if (factor > max_factor)
10793 factor = max_factor;
10795 stick_pitch = + max_flight_pitch * r_max_factor * factor;
10797 stick_pitch = - max_flight_pitch * r_max_factor * factor;
10800 if (fabs(stick_pitch) == 0.0 || fabs(d_forward) > 0.5)
10803 int factor = sqrt(fabs(d_forward) / fabs(min_d));
10804 if (factor > max_factor)
10805 factor = max_factor;
10806 if (d_forward > min_d)
10807 stick_yaw = - max_flight_yaw * reverse * r_max_factor * factor;
10808 if (d_forward < -min_d)
10810 if (factor < max_factor/2.0)
10812 stick_yaw = + max_flight_yaw * reverse * r_max_factor * factor;
10820 [
self applySticks:delta_t];
10822 if ((!flightPitch)&&(!flightYaw))
10825 return d_right * reverse;
10830- (double) missileTrackPrimaryTarget:(
double) delta_t
10833 GLfloat d_forward, d_up, d_right;
10835 BOOL inPursuit = YES;
10837 if (!target || ![target isShip])
10840 double damping = 0.5 * delta_t;
10846 relPos = [
self vectorTo:target];
10851 float missileSpeed = (float)[
self speed];
10857 if (missileSpeed > 0.01f && accuracy > 0.0f)
10859 inPursuit = (dot_product([target forwardVector], v_forward) > 0.0f);
10862 Vector leading = [target
velocity];
10863 float lead = magnitude(relPos) / missileSpeed;
10867 relPos.x += (lead * leading.
x * (accuracy / 10.0f));
10868 relPos.y += (lead * leading.
y * (accuracy / 10.0f));
10869 relPos.z += (lead * leading.z * (accuracy / 10.0f));
10873 if (!vector_equal(relPos,
kZeroVector)) relPos = vector_normal(relPos);
10874 else relPos.z = 1.0;
10876 d_right = dot_product(relPos, v_right);
10877 d_up = dot_product(relPos, v_up);
10878 d_forward = dot_product(relPos, v_forward);
10885 pitching_over = (stick_pitch != 0.0);
10887 if ((d_forward < -pitch_tolerance) && (!pitching_over))
10889 pitching_over = YES;
10891 stick_pitch = -max_flight_pitch;
10893 stick_pitch = max_flight_pitch;
10898 pitching_over = (d_forward < 0.5);
10902 stick_pitch = -max_flight_pitch * d_up;
10903 stick_roll = -max_flight_roll * d_right;
10909 if (flightRoll < 0)
10910 flightRoll += (flightRoll < -damping) ? damping : -flightRoll;
10911 if (flightRoll > 0)
10912 flightRoll -= (flightRoll > damping) ? damping : flightRoll;
10913 if (flightPitch < 0)
10914 flightPitch += (flightPitch < -damping) ? damping : -flightPitch;
10915 if (flightPitch > 0)
10916 flightPitch -= (flightPitch > damping) ? damping : flightPitch;
10919 [
self applySticks:delta_t];
10924 if (d_forward < 0.0)
10930- (double) trackDestination:(
double) delta_t :(BOOL) retreat
10933 GLfloat d_forward, d_up, d_right;
10935 BOOL we_are_docking = (
nil != dockingInstructions);
10941 double reverse = 1.0;
10942 double reversePlayer = 1.0;
10944 double min_d = 0.004;
10946 double precision = we_are_docking ? 0.25 : 0.9025;
10949 reverse = -reverse;
10953 reverse = -reverse;
10954 reversePlayer = -1;
10957 relPos = HPVectorToVector(HPvector_subtract(_destination, position));
10958 double range2 = magnitude2(relPos);
10959 double desired_range2 = desired_range*desired_range;
10967 if (range2 > desired_range2)
10969 max_cos = sqrt(1 - precision * desired_range2/range2);
10970 if (max_cos >= 0.99999)
10976 if (!vector_equal(relPos,
kZeroVector)) relPos = vector_normal(relPos);
10977 else relPos.z = 1.0;
10979 d_right = dot_product(relPos, v_right);
10980 d_up = dot_product(relPos, v_up);
10981 d_forward = dot_product(relPos, v_forward);
10991 if (reverse * d_up > 0)
10992 stick_pitch = -max_flight_pitch;
10994 stick_pitch = max_flight_pitch;
10995 pitching_over = (reverse * d_forward < 0.707);
10999 if ((d_forward < max_cos)||(retreat))
11002 if (d_forward <= -max_cos || (retreat && d_forward >= max_cos))
11004 d_up = min_d * 2.0;
11009 int factor = sqrt(fabs(d_right) / fabs(min_d));
11012 if (d_right > min_d)
11013 stick_roll = - max_flight_roll * reversePlayer * 0.125 * factor;
11014 if (d_right < -min_d)
11015 stick_roll = + max_flight_roll * reversePlayer * 0.125 * factor;
11016 if (fabs(d_right) < fabs(stick_roll) * delta_t)
11017 stick_roll = fabs(d_right) / delta_t * (stick_roll<0 ? -1 : 1);
11022 int factor = sqrt(fabs(d_right) / fabs(min_d));
11025 if (d_right > min_d)
11026 stick_roll = + max_flight_roll * reversePlayer * 0.125 * factor;
11027 if (d_right < -min_d)
11028 stick_roll = - max_flight_roll * reversePlayer * 0.125 * factor;
11029 if (fabs(d_right) < fabs(stick_roll) * delta_t)
11030 stick_roll = fabs(d_right) / delta_t * (stick_roll<0 ? -1 : 1);
11033 if (stick_roll == 0.0)
11035 int factor = sqrt(fabs(d_up) / fabs(min_d));
11039 stick_pitch = - max_flight_pitch * reverse * 0.125 * factor;
11041 stick_pitch = + max_flight_pitch * reverse * 0.125 * factor;
11042 if (fabs(d_up) < fabs(stick_pitch) * delta_t)
11043 stick_pitch = fabs(d_up) / delta_t * (stick_pitch<0 ? -1 : 1);
11046 if (stick_pitch == 0.0)
11050 stick_pitch = 0.01;
11054 if (we_are_docking && docking_match_rotation && (d_forward > max_cos))
11059 if ((station_for_docking)&&(station_for_docking->
isStation))
11067 [
self applySticks:delta_t];
11070 d_forward *= d_forward;
11072 if (d_forward < 0.0)
11075 if ((!flightRoll)&&(!flightPitch))
11082- (GLfloat) rollToMatchUp:(Vector)up_vec rotating:(GLfloat)match_roll
11084 GLfloat cosTheta = dot_product(up_vec, v_up);
11085 GLfloat sinTheta = dot_product(up_vec, v_right);
11089 match_roll = -match_roll;
11090 sinTheta = -sinTheta;
11093 if (cosTheta < 0.0f)
11095 cosTheta = -cosTheta;
11096 sinTheta = -sinTheta;
11099 if (sinTheta > 0.0f)
11102 return cosTheta * cosTheta * match_roll + sinTheta * sinTheta * max_flight_roll;
11107 return cosTheta * cosTheta * match_roll - sinTheta * sinTheta * max_flight_roll;
11112- (GLfloat) rangeToDestination
11114 return HPdistance(position, _destination);
11118- (NSArray *) collisionExceptions
11120 if (_collisionExceptions ==
nil)
11122 return [NSArray array];
11124 return [_collisionExceptions allObjects];
11128- (void) addCollisionException:(
ShipEntity *)ship
11130 if (_collisionExceptions ==
nil)
11133 _collisionExceptions = [[
OOWeakSet alloc] init];
11135 [_collisionExceptions addObject:ship];
11139- (void) removeCollisionException:(
ShipEntity *)ship
11141 if (_collisionExceptions !=
nil)
11143 [_collisionExceptions removeObject:ship];
11148- (BOOL) collisionExceptedFor:(
ShipEntity *)ship
11150 if (_collisionExceptions ==
nil)
11154 return [_collisionExceptions containsObject:ship];
11159- (NSUInteger) defenseTargetCount
11161 return [_defenseTargets count];
11165- (NSArray *) allDefenseTargets
11167 return [_defenseTargets allObjects];
11171- (NSEnumerator *) defenseTargetEnumerator
11173 return [_defenseTargets objectEnumerator];
11177- (BOOL) addDefenseTarget:(
Entity *)target
11184 if (target ==
nil || [
self isDefenseTarget:target] || ![target isShip])
11188 if (_defenseTargets ==
nil)
11191 _defenseTargets = [[
OOWeakSet alloc] init];
11194 [_defenseTargets addObject:target];
11199- (void) validateDefenseTargets
11201 if (_defenseTargets ==
nil)
11206 NSEnumerator *defTargets = [[
self allDefenseTargets] objectEnumerator];
11208 while ((target = [[defTargets nextObject] weakRefUnderlyingObject]))
11210 if ([target status] == STATUS_DEAD)
11212 [
self removeDefenseTarget:target];
11218- (BOOL) isDefenseTarget:(
Entity *)target
11220 return [_defenseTargets containsObject:target];
11225- (void) removeAllDefenseTargets
11227 [_defenseTargets removeAllObjects];
11231- (void) removeDefenseTarget:(
Entity *)target
11233 [_defenseTargets removeObject:target];
11237- (double) rangeToPrimaryTarget
11239 return [
self rangeToSecondaryTarget:[
self primaryTarget]];
11243- (double) rangeToSecondaryTarget:(
Entity *)target
11249 delta = HPVectorToVector(HPvector_subtract(target->
position, position));
11250 dist = magnitude(delta);
11251 dist -= target->collision_radius;
11252 dist -= collision_radius;
11257- (double) approachAspectToPrimaryTarget
11260 Entity *target = [
self primaryTarget];
11261 if (target ==
nil || ![target isShip])
11267 delta = HPVectorToVector(HPvector_subtract(position, target->
position));
11269 return dot_product(vector_normal(delta), ship_target->
v_forward);
11273- (BOOL) hasProximityAlertIgnoringTarget:(BOOL)ignore_target
11275 if (([
self proximityAlert] !=
nil)&&(!ignore_target || ([
self proximityAlert] != [
self primaryTarget])))
11285- (GLfloat) currentAimTolerance
11287 GLfloat basic_aim = aim_tolerance;
11288 GLfloat best_cos = 0.99999;
11292 best_cos = 0.999999;
11294 basic_aim /= 1.0 + ((GLfloat)[
self missedShots] / 4.0);
11299 best_cos = 0.9999999;
11311 basic_aim *= 1.3 +
randf();
11323 GLfloat sunGlareAngularSize = atan([sun radius]/HPdistance([
self position], [sun position])) * SUN_GLARE_MULT_FACTOR + (SUN_GLARE_ADD_FACTOR);
11324 GLfloat glareLevel = [
self lookingAtSunWithThresholdAngleCos:cos(sunGlareAngularSize)] * (1.0f - [
self sunGlareFilter]);
11325 if (glareLevel > 0.1f)
11328 basic_aim *= (1.0 + glareLevel*3.0);
11330 if (glareLevel > 0.5f)
11333 best_cos = 0.99999;
11340 GLfloat max_cos = sqrt(1-(basic_aim * basic_aim / 100000000.0));
11342 if (max_cos < best_cos)
11351- (GLfloat) lookingAtSunWithThresholdAngleCos:(GLfloat) thresholdAngleCos
11354 GLfloat measuredCos = 999.0f, measuredCosAbs;
11355 GLfloat sunBrightness = 0.0f;
11356 Vector relativePosition, unitRelativePosition;
11360 relativePosition = HPVectorToVector(HPvector_subtract([
self position], [sun position]));
11361 unitRelativePosition = vector_normal_or_zbasis(relativePosition);
11362 switch (currentWeaponFacing)
11365 measuredCos = -dot_product(unitRelativePosition, v_forward);
11368 measuredCos = +dot_product(unitRelativePosition, v_forward);
11371 measuredCos = +dot_product(unitRelativePosition, v_right);
11374 measuredCos = -dot_product(unitRelativePosition, v_right);
11379 measuredCosAbs = fabs(measuredCos);
11380 if (thresholdAngleCos <= measuredCosAbs && measuredCosAbs <= 1.0f)
11382 sunBrightness = (measuredCos - thresholdAngleCos) / (1.0f - thresholdAngleCos);
11383 if (sunBrightness < 0.0f) sunBrightness = 0.0f;
11385 return sunBrightness * sunBrightness * sunBrightness;
11393 GLfloat dq = -1.0f;
11394 GLfloat d2, radius, astq;
11395 Vector rel_pos, urp;
11396 if ([weapon_type isTurretLaser])
11401 Entity *target = [
self primaryTarget];
11402 if (target ==
nil)
return NO;
11403 if ([target status] == STATUS_DEAD)
return NO;
11405 if (isSunlit && (target->
isSunlit == NO) && (
randf() < 0.75))
11410 rel_pos = HPVectorToVector(HPvector_subtract([
self calculateTargetPosition], position));
11411 d2 = magnitude2(rel_pos);
11412 urp = vector_normal_or_zbasis(rel_pos);
11417 dq = +dot_product(urp, v_forward);
11421 dq = -dot_product(urp, v_forward);
11425 dq = -dot_product(urp, v_right);
11429 dq = +dot_product(urp, v_right);
11436 if (dq < 0.0)
return NO;
11438 GLfloat aim = [
self currentAimTolerance];
11439 if (dq > aim*aim)
return YES;
11444 astq = sqrt(1.0 - radius * radius / (d2 * 9));
11446 return (fabs(dq) >= astq);
11456 weapon_temp = forward_weapon_temp;
11460 weapon_temp = aft_weapon_temp;
11464 weapon_temp = port_weapon_temp;
11468 weapon_temp = starboard_weapon_temp;
11476 NSUInteger multiplier = 1;
11477 if (_multiplyWeapons)
11480 multiplier = [[
self laserPortOffset:direction] count];
11483 if (energy <= weapon_energy_use * multiplier)
return NO;
11484 if ([
self shotTime] < weapon_recharge_rate)
return NO;
11485 if (![weapon_type isTurretLaser])
11487 if (range >
randf() * weaponRange * (accuracy+7.5))
return NO;
11488 if (range > weaponRange)
return NO;
11490 if (![
self onTarget:direction withWeapon:weapon_type])
return NO;
11495 if ([weapon_type isTurretLaser])
11497 [
self fireDirectLaserShot:range];
11502 [
self fireLaserShotInDirection:direction weaponIdentifier:[weapon_type identifier]];
11509 energy -= weapon_energy_use * multiplier;
11513 forward_weapon_temp += weapon_shot_temperature * multiplier;
11517 aft_weapon_temp += weapon_shot_temperature * multiplier;
11521 port_weapon_temp += weapon_shot_temperature * multiplier;
11525 starboard_weapon_temp += weapon_shot_temperature * multiplier;
11536 NSEnumerator *subEnum =
nil;
11538 for (subEnum = [
self shipSubEntityEnumerator]; (se = [subEnum nextObject]); )
11540 if ([se fireSubentityLaserShot:range])
11547 if (fired && cloaking_device_active && cloakPassive)
11549 [
self deactivateCloakingDevice];
11556- (BOOL) fireMainWeapon:(
double)range
11561 [
self setWeaponDataFromType:forward_weapon_type];
11566 BOOL result = [
self fireWeapon:forward_weapon_type direction:WEAPON_FACING_FORWARD range:range];
11571 NSEnumerator *subEnum = [
self shipSubEntityEnumerator];
11574 BOOL hasTurrets = NO;
11575 while (
isWeaponNone(weapon_type) && (se = [subEnum nextObject]))
11579 if (se->
behaviour == BEHAVIOUR_TRACK_AS_TURRET)
11595 [
self setWeaponDataFromType:weapon_type];
11602- (BOOL) fireAftWeapon:(
double)range
11606 [
self setWeaponDataFromType:aft_weapon_type];
11608 return [
self fireWeapon:aft_weapon_type direction:WEAPON_FACING_AFT range:range];
11612- (BOOL) firePortWeapon:(
double)range
11616 [
self setWeaponDataFromType:port_weapon_type];
11618 return [
self fireWeapon:port_weapon_type direction:WEAPON_FACING_PORT range:range];
11622- (BOOL) fireStarboardWeapon:(
double)range
11626 [
self setWeaponDataFromType:starboard_weapon_type];
11628 return [
self fireWeapon:starboard_weapon_type direction:WEAPON_FACING_STARBOARD range:range];
11638- (void) resetShotTime
11644- (BOOL) fireTurretCannon:(
double) range
11646 if ([
self shotTime] < weapon_recharge_rate)
11648 if (range > weaponRange * 1.01)
11651 if ([root isPlayer] && ![
PLAYER weaponsOnline])
11654 if ([root isCloaked] && [root cloakPassive])
11661 HPVector
origin = [
self position];
11664 Entity *father = [
self parentEntity];
11669 while ((father)&&(father != last) && (father !=
NO_TARGET))
11675 if (![last isSubEntity])
break;
11676 father = [father
owner];
11679 origin = HPvector_add(
origin, vectorToHPVector(vector_multiply_scalar(vel, collision_radius + 0.5)));
11684 energy:weapon_damage
11685 duration:weaponRange/TURRET_SHOT_SPEED
11686 color:laser_color];
11688 [shot autorelease];
11689 [UNIVERSE addEntity:shot];
11690 [shot
setOwner:[
self rootShipEntity]];
11692 [
self resetShotTime];
11697- (void) setLaserColor:(
OOColor *) color
11701 [laser_color release];
11702 laser_color = [color retain];
11707- (void) setExhaustEmissiveColor:(
OOColor *) color
11711 [exhaust_emissive_color release];
11712 exhaust_emissive_color = [color retain];
11719 return [[laser_color retain] autorelease];
11723- (
OOColor *)exhaustEmissiveColor
11725 return [[exhaust_emissive_color retain] autorelease];
11729- (BOOL) fireSubentityLaserShot:(
double)range
11731 [
self setShipHitByLaser:nil];
11734 [
self setWeaponDataFromType:forward_weapon_type];
11737 NSAssert([parent isShipWithSubEntityShip:
self],
@"-fireSubentityLaserShot: called on ship which is not a subentity.");
11740 if ([parent energy] <= weapon_energy_use)
return NO;
11741 if ([
self shotTime] < weapon_recharge_rate)
return NO;
11743 if (range > weaponRange)
return NO;
11745 forward_weapon_temp += weapon_shot_temperature;
11748 GLfloat hitAtRange = weaponRange;
11750 ShipEntity *victim = [UNIVERSE firstShipHitByLaserFromShip:self inDirection:direction offset:kZeroVector gettingRangeFound:&hitAtRange];
11751 [
self setShipHitByLaser:victim];
11759 [
self adjustMissedShots:-1];
11761 if ([
self isPlayer])
11763 [PLAYER addRoleForAggression:victim];
11767 if (subent !=
nil && [victim isFrangible])
11774 if (hitAtRange < weaponRange)
11780 HPVector flash_pos = HPvector_add([shot position], vectorToHPVector(vector_multiply_scalar(vd, hitAtRange)));
11781 [UNIVERSE addLaserHitEffectsAt:flash_pos against:victim damage:weapon_damage color:laser_color];
11786 [
self adjustMissedShots:+1];
11789 if (![parent isCloaked])
11794 Vector victimDirection = vector_normal(HPVectorToVector(HPvector_subtract([victim position], [parent position])));
11795 if (dot_product(shotDirection, victimDirection) > 0.995)
11797 if ([
self isPlayer])
11799 [PLAYER addRoleForAggression:victim];
11810 [UNIVERSE addEntity:shot];
11811 [
self resetShotTime];
11817- (BOOL) fireDirectLaserShot:(
double)range
11819 Entity *my_target = [
self primaryTarget];
11820 if (my_target ==
nil)
return [
self fireDirectLaserDefensiveShot];
11821 if (range >
randf() * weaponRange * (accuracy+5.5))
return [
self fireDirectLaserDefensiveShot];
11822 if (range > weaponRange)
return [
self fireDirectLaserDefensiveShot];
11823 return [
self fireDirectLaserShotAt:my_target];
11827- (BOOL) fireDirectLaserDefensiveShot
11829 NSEnumerator *targetEnum = [
self defenseTargetEnumerator];
11831 while ((target = [[targetEnum nextObject] weakRefUnderlyingObject]))
11834 if ([target scanClass] == CLASS_NO_DRAW || [(
ShipEntity *)target isCloaked] || [target energy] <= 0.0)
11836 [
self removeDefenseTarget:target];
11840 double range = [
self rangeToSecondaryTarget:target];
11841 if (range < weaponRange)
11843 return [
self fireDirectLaserShotAt:target];
11845 else if (range > scannerRange)
11847 [
self removeDefenseTarget:target];
11855- (BOOL) fireDirectLaserShotAt:(
Entity *)my_target
11857 GLfloat hit_at_range;
11858 double range_limit2 = weaponRange*weaponRange;
11861 r_pos = vector_normal_or_zbasis([
self vectorTo:my_target]);
11865 GLfloat acc_factor = (10.0 - accuracy) * 0.001;
11867 q_laser.x += acc_factor * (
randf() - 0.5);
11868 q_laser.y += acc_factor * (
randf() - 0.5);
11869 q_laser.z += acc_factor * (
randf() - 0.5);
11870 quaternion_normalize(&q_laser);
11872 Quaternion q_save = orientation;
11873 orientation = q_laser;
11875 ShipEntity *victim = [UNIVERSE firstShipHitByLaserFromShip:self inDirection:WEAPON_FACING_FORWARD offset:kZeroVector gettingRangeFound:&hit_at_range];
11876 [
self setShipHitByLaser:victim];
11877 orientation = q_save;
11879 Vector vel = vector_multiply_scalar(v_forward, flightSpeed);
11892 if (subent !=
nil && [victim isFrangible])
11899 if (hit_at_range * hit_at_range < range_limit2)
11905 HPVector flash_pos = HPvector_add([shot position], vectorToHPVector(vector_multiply_scalar(vd, hit_at_range)));
11906 [UNIVERSE addLaserHitEffectsAt:flash_pos against:victim damage:weapon_damage color:laser_color];
11910 [UNIVERSE addEntity:shot];
11912 [
self resetShotTime];
11920 NSArray *laserPortOffset =
nil;
11925 laserPortOffset = forwardWeaponOffset;
11929 laserPortOffset = aftWeaponOffset;
11933 laserPortOffset = portWeaponOffset;
11937 laserPortOffset = starboardWeaponOffset;
11940 return laserPortOffset;
11944- (BOOL) fireLaserShotInDirection:(
OOWeaponFacing)direction weaponIdentifier:(NSString *)weaponIdentifier
11946 double range_limit2 = weaponRange * weaponRange;
11947 GLfloat hit_at_range;
11948 NSUInteger i, barrels;
11949 Vector vel = vector_multiply_scalar(v_forward, flightSpeed);
11950 NSArray *laserPortOffsets = [
self laserPortOffset:direction];
11953 barrels = [laserPortOffsets count];
11954 NSMutableArray *shotEntities = [NSMutableArray arrayWithCapacity:barrels];
11957 GLfloat effective_damage = weapon_damage;
11958 if (barrels > 1 && !_multiplyWeapons)
11961 effective_damage /= (GLfloat)barrels;
11964 for (i=0;i<barrels;i++)
11966 Vector laserPortOffset = [laserPortOffsets oo_vectorAtIndex:i];
11968 last_shot_time = [UNIVERSE getTime];
11970 ShipEntity *victim = [UNIVERSE firstShipHitByLaserFromShip:self inDirection:direction offset:laserPortOffset gettingRangeFound:&hit_at_range];
11971 [
self setShipHitByLaser:victim];
11974 if ([
self isPlayer])
11976 [shotEntities addObject:shot];
11985 [
self adjustMissedShots:-1];
11986 if ([
self isPlayer])
11988 [PLAYER addRoleForAggression:victim];
12000 if (subent !=
nil && [victim isFrangible])
12007 if (hit_at_range * hit_at_range < range_limit2)
12013 HPVector flash_pos = HPvector_add([shot position], vectorToHPVector(vector_multiply_scalar(vd, hit_at_range)));
12014 [UNIVERSE addLaserHitEffectsAt:flash_pos against:victim damage:effective_damage color:laser_color];
12019 [
self adjustMissedShots:+1];
12022 if (![
self isCloaked])
12024 victim = [
self primaryTarget];
12025 if ([victim isShip])
12040 if ([
self isPlayer])
12042 [PLAYER addRoleForAggression:victim];
12044 [victim setPrimaryAggressor:self];
12045 [victim setFoundTarget:self];
12046 [victim reactToAIMessage:@"ATTACKER_MISSED" context:@"attacker narrowly misses"];
12047 [victim doScriptEvent:OOJSID("shipBeingAttackedUnsuccessfully") withArgument:self];
12053 [UNIVERSE addEntity:shot];
12057 if ([
self isPlayer])
12062 [
self resetShotTime];
12068- (void) adjustMissedShots:(
int) delta
12070 if ([
self isSubEntity])
12072 [[
self owner] adjustMissedShots:delta];
12076 _missed_shots += delta;
12077 if (_missed_shots < 0)
12087 if ([
self isSubEntity])
12089 return [[
self owner] missedShots];
12093 return _missed_shots;
12098- (void) throwSparks
12102 randf() * (boundingBox.max.x - boundingBox.min.
x) + boundingBox.min.
x,
12103 randf() * (boundingBox.max.
y - boundingBox.min.
y) + boundingBox.min.
y,
12104 randf() * boundingBox.max.z + boundingBox.min.z
12108 float w = boundingBox.max.x - boundingBox.min.
x;
12109 float h = boundingBox.max.y - boundingBox.min.
y;
12110 float m = (w < h) ? 0.25 * w: 0.25 * h;
12114 Vector vel = vector_multiply_scalar(HPVectorToVector(HPvector_subtract(
origin, position)), 2.0);
12120 duration:2.0 + 3.0 * randf()
12125 [UNIVERSE addEntity:spark];
12128 next_spark_time =
randf();
12132- (void) considerFiringMissile:(
double)delta_t
12134 int missile_chance = 0;
12135 int rhs = 3.2 / delta_t;
12136 if (rhs) missile_chance = 1 + (
ranrot_rand() % rhs);
12138 double hurt_factor = 16 * pow(energy/maxEnergy, 4.0);
12139 if (missiles > missile_chance * hurt_factor)
12141 [
self fireMissile];
12146- (Vector) missileLaunchPosition
12151 start.y = boundingBox.min.y - 4.0f;
12152 start.z = boundingBox.max.z + 1.0f;
12155 start = [shipinfoDictionary oo_vectorForKey:@"missile_launch_position" defaultValue:start];
12158 start = vector_multiply_scalar(start,_scaleFactor);
12161 if (start.x == 0.0f && start.y == 0.0f && start.z <= 0.0f)
12163 OOLog(
@"ship.missileLaunch.invalidPosition",
@"***** ERROR: The missile_launch_position defines a position %@ behind the %@. In future versions such missiles may explode on launch because they have to travel through the ship.", VectorDescription(start), self);
12165 start.y = boundingBox.min.y - 4.0f;
12166 start.z = boundingBox.max.z + 1.0f;
12174 return [
self fireMissileWithIdentifier:nil andTarget:[
self primaryTarget]];
12178- (
ShipEntity *) fireMissileWithIdentifier:(NSString *) identifier andTarget:(
Entity *) target
12186 Vector start, v_eject;
12188 if ([
UNIVERSE getTime] < missile_launch_time)
return nil;
12190 start = [
self missileLaunchPosition];
12192 double throw_speed = 250.0f;
12194 if ((missiles <= 0)||(target ==
nil)||([target scanClass] == CLASS_NO_DRAW))
12197 if ([target isShip])
12200 if ([target_ship isCloaked])
12205 if (HPmagnitude2(HPvector_subtract([target_ship position], position)) > scannerRange * scannerRange)
12209 if (![
self hasMilitaryScannerFilter] && [target_ship isJammingScanning])
12216 if (identifier ==
nil)
12219 i = floor(
randf()*(
double)missiles);
12220 identifier = [missile_list[i] identifier];
12221 missile = [UNIVERSE newShipWithRole:identifier];
12225 while ( ++i < missiles ) missile_list[i - 1] = missile_list[i];
12230 missile = [UNIVERSE newShipWithRole:identifier];
12236 if (!isPlayer && ![
self removeExternalStore:[
OOEquipmentType equipmentTypeWithIdentifier:identifier]])
12243 v_eject = vector_normal(start);
12247 while ( (start.x > boundingBox.min.x - mcr)&&(start.x < boundingBox.max.x + mcr)&&
12248 (start.y > boundingBox.min.y - mcr)&&(start.y < boundingBox.max.y + mcr)&&
12249 (start.z > boundingBox.min.z - mcr)&&(start.z < boundingBox.max.z + mcr) )
12251 start = vector_add(start, vector_multiply_scalar(v_eject, mcr));
12254 vel = vector_add(vel, vector_multiply_scalar(v_forward, flightSpeed + throw_speed));
12256 Quaternion q1 = [
self normalOrientation];
12264 if ([missile scanClass] == CLASS_THARGOID)
12268 ShipEntity *thisGroupLeader = [_group leader];
12270 if ([thisGroupLeader escortGroup] != _group)
12277 if (![
self isMissileFlagSet]) [missile
setOwner:self];
12278 else [missile
setOwner:[
self owner]];
12290 missile_launch_time = [UNIVERSE getTime] + missile_load_time;
12292 [UNIVERSE addEntity:missile];
12296 if ([missile isMissile] && [target isShip])
12298 [
self doScriptEvent:OOJSID("shipFiredMissile") withArgument:missile andArgument:target_ship];
12302 if (cloaking_device_active && cloakPassive)
12305 [
self deactivateCloakingDevice];
12310 [
self doScriptEvent:OOJSID("shipReleasedEquipment") withArgument:missile];
12317- (BOOL) isMissileFlagSet
12323- (void) setIsMissileFlag:(BOOL)newValue
12325 isMissile = !!newValue;
12331 return missile_load_time;
12335- (void) setMissileLoadTime:(
OOTimeDelta)newMissileLoadTime
12337 missile_load_time = fmax(0.0, newMissileLoadTime);
12344 if (accuracy >=
COMBAT_AI_ISNT_AWFUL && missiles > 0 && [[missile_list[0] identifier] isEqualTo:
@"EQ_MISSILE"])
12348 missile_launch_time = [UNIVERSE getTime] + fmax(2.0,missile_load_time);
12356 if (![
self hasECM])
return NO;
12359 [UNIVERSE addEntity:ecmDevice];
12360 [ecmDevice release];
12365- (BOOL) activateCloakingDevice
12367 if (![
self hasCloakingDevice] || cloaking_device_active)
return cloaking_device_active;
12370 if (cloaking_device_active) [
self doScriptEvent:OOJSID("shipCloakActivated")];
12371 return cloaking_device_active;
12375- (void) deactivateCloakingDevice
12377 if ([
self hasCloakingDevice] && cloaking_device_active)
12379 cloaking_device_active = NO;
12380 [
self doScriptEvent:OOJSID("shipCloakDeactivated")];
12385- (BOOL) launchCascadeMine
12387 if (![
self hasCascadeMine])
return NO;
12388 [
self setSpeed: maxFlightSpeed + 300];
12389 ShipEntity* bomb = [UNIVERSE newShipWithRole:@"energy-bomb"];
12390 if (bomb ==
nil)
return NO;
12392 [
self removeEquipmentItem:@"EQ_QC_MINE"];
12394 double start = collision_radius + bomb->collision_radius;
12395 Quaternion random_direction;
12398 double random_roll =
randf() - 0.5;
12399 double random_pitch =
randf() - 0.5;
12402 rpos = HPvector_subtract([
self position], vectorToHPVector(vector_multiply_scalar(v_forward, start)));
12404 double eject_speed = -800.0;
12405 vel = vector_multiply_scalar(v_forward, [
self flightSpeed] + eject_speed);
12406 eject_speed *= 0.5 * (
randf() - 0.5);
12407 vel = vector_add(vel, vector_multiply_scalar(v_up, eject_speed));
12408 eject_speed *= 0.5 * (
randf() - 0.5);
12409 vel = vector_add(vel, vector_multiply_scalar(v_right, eject_speed));
12420 [UNIVERSE addEntity:bomb];
12423 if (cloaking_device_active && cloakPassive)
12425 [
self deactivateCloakingDevice];
12430 [
self addTarget:bomb];
12431 [
self setBehaviour:BEHAVIOUR_FLEE_TARGET];
12442 unsigned n_pods, i;
12443 NSMutableArray *passengers =
nil;
12451 n_pods = [shipinfoDictionary oo_unsignedIntForKey:@"has_escape_pod"];
12452 if (n_pods > 65) n_pods = 65;
12453 if (n_pods > 1) passengers = [NSMutableArray arrayWithCapacity:n_pods-1];
12458 for (i = 0; i < [crew count]; i++)
12463 mainPod = [
self launchPodWithCrew:crew];
12467 [
self setCrew:nil];
12468 [
self setHulk:YES];
12473 for (i = 1; i < n_pods; i++)
12477 [passengers addObject:passenger];
12480 if (mainPod) [
self doScriptEvent:
OOJSID(
"shipLaunchedEscapePod") withArgument:mainPod andArgument:passengers];
12489 ShipEntity *jetto = [
self dumpCargoItem:nil];
12506 if (([cargo
count] > 0)&&([
UNIVERSE getTime] - cargo_dump_time > 0.5))
12508 if (preferred ==
nil)
12510 jetto = [[[cargo objectAtIndex:0] retain] autorelease];
12515 for (i=0;i<[cargo count];i++)
12517 if ([[[cargo objectAtIndex:i] commodityType] isEqualToString:preferred])
12519 jetto = [[[cargo objectAtIndex:i] retain] autorelease];
12527 jetto = [[[cargo objectAtIndex:0] retain] autorelease];
12533 [
self dumpItem:jetto];
12534 [cargo removeObjectAtIndex:i];
12535 [
self broadcastAIMessage:@"CARGO_DUMPED"];
12538 [
self checkScannerIgnoringUnpowered];
12539 for (i = 0; i < n_scanned_ships ; i++)
12557 ShipEntity* jetto = [UNIVERSE reifyCargoPod:cargoObj];
12565 double eject_speed =
EXPECT_NOT([jetto crew] && [jetto isPlayer]) ? 20.0 : 100.0;
12566 double eject_reaction = -eject_speed * [jetto mass] / [
self mass];
12570 Vector vel, v_eject, v_eject_normal;
12571 HPVector rpos = [
self absolutePositionForSubentity];
12572 double jetto_roll = 0;
12573 double jetto_pitch = 0;
12578 start.z = boundingBox.min.z - jcr;
12581 start = [shipinfoDictionary oo_vectorForKey:@"aft_eject_position" defaultValue:start];
12583 v_eject = vector_normal(start);
12586 while ( (start.x > boundingBox.min.x - jcr)&&(start.x < boundingBox.max.x + jcr)&&
12587 (start.y > boundingBox.min.y - jcr)&&(start.y < boundingBox.max.y + jcr)&&
12588 (start.z > boundingBox.min.z - jcr)&&(start.z < boundingBox.max.z + jcr))
12590 start = vector_add(start, vector_multiply_scalar(v_eject, jcr));
12594 rpos = HPvector_add(rpos, vectorToHPVector(v_eject));
12595 v_eject = vector_normal(v_eject);
12596 v_eject_normal = v_eject;
12598 v_eject.x += (
randf() -
randf())/eject_speed;
12599 v_eject.y += (
randf() -
randf())/eject_speed;
12600 v_eject.z += (
randf() -
randf())/eject_speed;
12602 vel = vector_add(vector_multiply_scalar(v_forward, flightSpeed), vector_multiply_scalar(v_eject, eject_speed));
12603 velocity = vector_add(velocity, vector_multiply_scalar(v_eject, eject_reaction));
12614 jetto_roll = ((
ranrot_rand() % 1024) - 512.0)/1024.0;
12615 jetto_pitch = ((
ranrot_rand() % 1024) - 512.0)/1024.0;
12625 [UNIVERSE addEntity:jetto];
12627 jettoAI = [jetto
getAI];
12628 if ([jettoAI hasSuspendedStateMachines])
12632 [jettoAI exitStateMachineWithMessage:nil];
12635 [
self doScriptEvent:OOJSID("shipDumpedCargo") withArgument:jetto];
12637 cargo_dump_time = [UNIVERSE getTime];
12642- (void) manageCollisions
12649 while ([collidingEntities
count] > 0)
12652 ent = [[[collidingEntities objectAtIndex:0] retain] autorelease];
12653 [collidingEntities removeObjectAtIndex:0];
12659 [
self collideWithShip:other_ship];
12661 else if ([ent isStellarObject])
12663 [
self getDestroyedBy:ent damageType:[ent
isSun] ? kOODamageTypeHitASun : kOODamageTypeHitAPlanet];
12664 if (
self ==
PLAYER) [
self retain];
12666 else if ([ent isWormhole])
12668 if( [
self isPlayer] ) [
self enterWormhole:(WormholeEntity*)ent];
12669 else [
self enterWormhole:(WormholeEntity*)ent replacing:NO];
12686 BOOL otherIsStation = other == [UNIVERSE station];
12688 hploc = HPvector_normal_or_zbasis(HPvector_subtract([other absolutePositionForSubentity], position));
12689 loc = HPVectorToVector(hploc);
12692 if ([
self canScoop:other])
12694 [
self scoopIn:other];
12697 if ([other canScoop:
self])
12699 [other scoopIn:self];
12713 GLfloat m2 = [other mass];
12716 Vector v, vel1b = [
self velocity];
12718 if (otherParent !=
nil)
12729 v = [other velocity];
12732 v = vector_subtract(vel1b, v);
12734 GLfloat v2b = dot_product(v, loc);
12736 GLfloat v1a = sqrt(v2b * v2b * m2 / m1);
12737 if (v2b < 0.0f) v1a = -v1a;
12742 if (v2b < -1.0f)
return NO;
12745 position = HPvector_subtract(position, hploc);
12751 dam1 = m2 * v2b * v2b / 50000000;
12752 dam2 = m1 * v2b * v2b / 50000000;
12755 Vector vel1a = vector_multiply_scalar(loc, -v1a);
12756 Vector vel2a = vector_multiply_scalar(loc, v2b);
12758 if (magnitude2(v) <= 0.1)
12760 vel1a = vector_multiply_scalar(loc, -1);
12765 if (otherParent !=
nil)
12771 [other adjustVelocity:vel2a];
12774 [
self adjustVelocity:vel1a];
12776 BOOL selfDestroyed = (dam1 > energy);
12777 BOOL otherDestroyed = (dam2 > [other energy]) && !otherIsStation;
12781 [
self takeScrapeDamage: dam1 from:other];
12784 vel2a = vector_multiply_scalar(vel2a, -1);
12785 [other adjustVelocity:vel2a];
12791 if (otherParent !=
nil && ![otherParent isFrangible])
12797 [other takeScrapeDamage: dam2 from:self];
12800 if (otherDestroyed)
12802 vel1a = vector_multiply_scalar(vel1a, -1);
12803 [
self adjustVelocity:vel1a];
12807 if (!selfDestroyed && !otherDestroyed)
12809 float t = 10.0 * [UNIVERSE getTimeDelta];
12811 HPVector pos1a = HPvector_add([
self position], vectorToHPVector(vector_multiply_scalar(loc, t * v1a)));
12812 [
self setPosition:pos1a];
12814 if (!otherIsStation)
12816 HPVector pos2a = HPvector_add([other position], vectorToHPVector(vector_multiply_scalar(loc, t * v2b)));
12817 [other setPosition:pos2a];
12822 [[other collisionArray] removeObject:self];
12824 [
self doScriptEvent:OOJSID("shipCollided") withArgument:other andReactToAIMessage:@"COLLISION"];
12825 [other doScriptEvent:OOJSID("shipCollided") withArgument:self andReactToAIMessage:@"COLLISION"];
12831- (Vector) thrustVector
12833 return vector_multiply_scalar(v_forward, flightSpeed);
12839 return vector_add([super velocity], [
self thrustVector]);
12843- (void) setTotalVelocity:(Vector)vel
12845 [
self setVelocity:vector_subtract(vel, [
self thrustVector])];
12849- (void) adjustVelocity:(Vector) xVel
12851 velocity = vector_add(velocity, xVel);
12855- (void) addImpactMoment:(Vector) moment fraction:(GLfloat) howmuch
12857 velocity = vector_add(velocity, vector_multiply_scalar(moment, howmuch / mass));
12863 if (other ==
nil)
return NO;
12864 if (![
self hasCargoScoop])
return NO;
12865 if ([cargo
count] >= [
self maxAvailableCargoSpace])
return NO;
12866 if (scanClass == CLASS_CARGO)
return NO;
12867 if ([other scanClass] != CLASS_CARGO)
return NO;
12870 if ([other isStation])
return NO;
12872 HPVector loc = HPvector_between(position, [other position]);
12874 if (dot_product(v_forward, HPVectorToVector(loc)) < 0.0f)
return NO;
12875 if ([
self isPlayer] && dot_product(v_up, HPVectorToVector(loc)) > 0.0f)
return NO;
12883 if([
self status] == STATUS_BEING_SCOOPED)
return;
12884 desired_speed = 0.0;
12885 [
self setAITo:@"nullAI.plist"];
12886 behaviour = BEHAVIOUR_TRACTORED;
12887 [
self setStatus:STATUS_BEING_SCOOPED];
12888 [
self addTarget:other];
12889 [
self setOwner:other];
12892 [
self checkScannerIgnoringUnpowered];
12895 for (i = 0; i < n_scanned_ships ; i++)
12899 if (other != scooper && (
id)
self == [scooper primaryTarget])
12913- (void) suppressTargetLost
12921 [
self scoopUpProcess:other processEvents:YES processMessages:YES];
12925- (void) scoopUpProcess:(
ShipEntity *)other processEvents:(BOOL) procEvents processMessages:(BOOL) procMessages
12927 if (other ==
nil)
return;
12933 if (max_cargo && [cargo
count] >= [
self maxAvailableCargoSpace])
12939 switch ([other cargoType])
12956 if ([other commodityType] !=
nil)
12964 if (isPlayer && [other showScoopMessage] && procMessages)
12966 [UNIVERSE clearPreviousMessage];
12968 [UNIVERSE addMessage:OOExpandKey(@"scripted-item-scooped", shipName) forCount:4];
12991 if (co_type ==
nil && co_amount > 0)
12993 co_type = [UNIVERSE getRandomCommodity];
12994 co_amount = [UNIVERSE getRandomAmountOfCommodity:co_type];
13006 if ([other showScoopMessage] && procMessages)
13008 [UNIVERSE clearPreviousMessage];
13010 for (i = 0; i < [[other
crew] count]; i++)
13013 NSString *characterName = [rescuee
name];
13014 if ([rescuee legalStatus])
13016 [UNIVERSE addMessage:OOExpandKey(@"scoop-captured-character", characterName) forCount: 4.5];
13018 else if ([rescuee insuranceCredits])
13020 [UNIVERSE addMessage:OOExpandKey(@"scoop-rescued-character", characterName) forCount: 4.5];
13024 [UNIVERSE addMessage: DESC(@"scoop-got-slave") forCount: 4.5];
13035 if ([other showScoopMessage] && procMessages)
13037 [UNIVERSE clearPreviousMessage];
13038 [UNIVERSE addMessage:[UNIVERSE describeCommodity:co_type amount:co_amount] forCount:4.5];
13042 [cargo insertObject:other atIndex:0];
13045 [shipAI message:@"CARGO_SCOOPED"];
13046 if (max_cargo && [cargo
count] >= [
self maxAvailableCargoSpace]) [shipAI message:@"HOLD_FULL"];
13050 [
self doScriptEvent:OOJSID("shipScoopedOther") withArgument:other];
13055 if ([other status] != STATUS_IN_HOLD)
13057 if ([cargo containsObject:other])
13059 [cargo removeObject:other];
13068 [
self checkScannerIgnoringUnpowered];
13071 for (i = 0; i < n_scanned_ships ; i++)
13074 if (
self != scooper && (
id) other == [scooper primaryTargetWithoutValidityCheck])
13080 [
self suppressTargetLost];
13081 [UNIVERSE removeEntity:other];
13085- (BOOL) cascadeIfAppropriateWithDamageAmount:(
double)amount cascadeOwner:(
Entity *)owner
13088 switch ([
self scanClass])
13090 case CLASS_WORMHOLE:
13093 case CLASS_VISUAL_EFFECT:
13096 if ((fuel >
MIN_FUEL) || isStation)
13102 case CLASS_STATION:
13106 case CLASS_MILITARY:
13107 case CLASS_THARGOID:
13108 case CLASS_MISSILE:
13109 case CLASS_NOT_SET:
13110 case CLASS_NO_DRAW:
13111 case CLASS_NEUTRAL:
13114 if (energy < amount && energy > 10 && [
self countsAsKill])
13126- (void) takeEnergyDamage:(
double)amount from:(
Entity *)ent becauseOf:(
Entity *)other weaponIdentifier:(NSString *)weaponIdentifier
13128 if ([
self status] == STATUS_DEAD)
return;
13129 if (amount <= 0.0)
return;
13135 cascade = [
self cascadeIfAppropriateWithDamageAmount:amount cascadeOwner:[ent
owner]];
13156 if (hunter ==
nil && [other isShip]) hunter = (
ShipEntity *)other;
13159 if ((other)&&([other isShip]))
13164 if (hunter !=
nil && [
self owner] != hunter)
13166 if ([hunter isCloaked])
13168 [
self doScriptEvent:OOJSID("shipBeingAttackedByCloaked") andReactToAIMessage:@"ATTACKED_BY_CLOAKED"];
13183 BOOL iAmTheLaw = [
self isPolice];
13184 BOOL uAreTheLaw = [hunter
isPolice];
13188 [
self setPrimaryAggressor:hunter];
13189 [
self setFoundTarget:hunter];
13192 [
self broadcastHitByLaserFrom: hunter];
13197 [
self respondToAttackFrom:ent becauseOf:hunter];
13202 if (![
self hasNewAI])
13205 if (group !=
nil && group != [hunter group] && !(iAmTheLaw || uAreTheLaw))
13207 if ([
self isTrader] || [
self isEscort])
13210 if (groupLeader !=
self)
13218 if ([
self isPirate])
13220 NSEnumerator *groupEnum =
nil;
13223 for (groupEnum = [group mutationSafeEnumerator]; (otherPirate = [groupEnum nextObject]); )
13225 if (otherPirate !=
self &&
randf() < 0.5)
13233 else if (iAmTheLaw)
13235 NSEnumerator *groupEnum =
nil;
13238 for (groupEnum = [group mutationSafeEnumerator]; (otherPolice = [groupEnum nextObject]); )
13240 if (otherPolice !=
self)
13252 if (iAmTheLaw && !uAreTheLaw)
13255 if (![
self hasNewAI])
13261 if ((group !=
nil && [hunter group] == group) || (iAmTheLaw && uAreTheLaw))
13264 if ([hunter behaviour] == BEHAVIOUR_ATTACK_FLY_TO_TARGET)
13266 [hunter
setBehaviour:BEHAVIOUR_ATTACK_FLY_FROM_TARGET];
13274 if (suppressExplosion) damageType = kOODamageTypeRemoved;
13275 else if (energyMine) damageType = kOODamageTypeCascadeWeapon;
13277 if (!suppressExplosion)
13279 [
self noteTakingDamage:amount from:other type:damageType];
13280 if (cascade) energy = 0.0;
13290 [
self getDestroyedBy:other damageType:damageType];
13296 if (energy < maxEnergy * 0.25)
13298 [
self doScriptEvent:OOJSID("shipEnergyIsLow") andReactToAIMessage:@"ENERGY_LOW"];
13300 if ((energy < maxEnergy *0.125 || (energy < 64 && energy < amount*2)) && [
self hasEscapePod] && (
ranrot_rand() & 3) == 0)
13302 [
self abandonShip];
13308- (BOOL) abandonShip
13311 if ([
self isPlayer] && [(
PlayerEntity *)
self isDocked])
13313 OOLog(
@"ShipEntity.abandonShip.failed",
@"%@",
@"Player cannot abandon ship while docked.");
13317 if (![
self hasEscapePod])
13319 OOLog(
@"ShipEntity.abandonShip.failed",
@"Ship abandonment was requested for %@, but this ship does not carry escape pod(s).",
self);
13325 if (![
self isPlayer])
13329 while ([
self hasEquipmentItemProviding:
@"EQ_ESCAPE_POD"])
13331 [
self removeEquipmentItem:[
self equipmentItemProviding:@"EQ_ESCAPE_POD"]];
13333 [
self setAITo:@"nullAI.plist"];
13334 behaviour = BEHAVIOUR_IDLE;
13336 [
self setScanClass: CLASS_CARGO];
13337 thrust = thrust * 0.5;
13338 if (thrust > 5) thrust = 5;
13339 desired_speed = 0.0;
13340 if ([
self group]) [
self setGroup:nil];
13341 if (![
self isSubEntity] && [
self owner]) [
self setOwner:nil];
13342 if ([
self hasEscorts])
13345 NSEnumerator *escortEnum =
nil;
13348 for (escortEnum = [[
self escortArray] objectEnumerator]; (escort = [escortEnum nextObject]); )
13351 if ([escort group] == escortGroup) [escort
setGroup:nil];
13352 if ([escort owner] ==
self) [escort
setOwner:escort];
13356 [_escortGroup release];
13357 _escortGroup =
nil;
13361 else if (
EXPECT([
self isSubEntity]))
13365 while ([
self hasEquipmentItemProviding:
@"EQ_ESCAPE_POD"])
13367 [
self removeEquipmentItem:[
self equipmentItemProviding:@"EQ_ESCAPE_POD"]];
13374 OOLog(
@"ShipEntity.abandonShip.notPossible",
@"Ship %@ cannot be abandoned at this time.",
self);
13380- (void) takeScrapeDamage:(
double) amount from:(
Entity *)ent
13382 if ([
self status] == STATUS_DEAD)
return;
13384 if ([
self status] == STATUS_LAUNCHING|| [ent status] == STATUS_LAUNCHING)
13391 [
self noteTakingDamage:amount from:ent type:kOODamageTypeScrape];
13396 float frag_chance = [ent
mass]*10/[
self mass];
13403 if (
randf() < frag_chance)
13411 [
self getDestroyedBy:ent damageType:kOODamageTypeScrape];
13416 if (energy < maxEnergy * 0.25)
13418 [
self doScriptEvent:OOJSID("shipEnergyIsLow") andReactToAIMessage:@"ENERGY_LOW"];
13424- (void) takeHeatDamage:(
double)amount
13426 if ([
self status] == STATUS_DEAD)
return;
13428 if ([
self isSubEntity])
13431 if (![owner isFrangible])
13438 throw_sparks = YES;
13440 [
self noteTakingDamage:amount from:nil type:kOODamageTypeHeat];
13445 [
self getDestroyedBy:nil damageType:kOODamageTypeHeat];
13450 if (energy < maxEnergy * 0.25)
13452 [
self doScriptEvent:OOJSID("shipEnergyIsLow") andReactToAIMessage:@"ENERGY_LOW"];
13461 if (dockingInstructions !=
nil)
13463 [dockingInstructions autorelease];
13464 dockingInstructions =
nil;
13467 [
self doScriptEvent:OOJSID("shipWillDockWithStation") withArgument:station];
13468 [
self doScriptEvent:OOJSID("shipDockedWithStation") withArgument:station];
13469 [shipAI message:@"DOCKED"];
13471 [UNIVERSE removeEntity:self];
13478 if (station ==
nil)
return;
13487 [
self enterWormhole:w_hole replacing:YES];
13491- (void) enterWormhole:(
WormholeEntity *) w_hole replacing:(BOOL)replacing
13493 if (w_hole ==
nil)
return;
13494 if ([
self status] == STATUS_ENTERING_WITCHSPACE)
13503 [
self addTarget:w_hole];
13504 [
self setFoundTarget:w_hole];
13505 [shipAI reactToMessage:@"WITCHSPACE OKAY" context:@"performHyperSpaceExit"];
13508 if ([[
self escortArray]
count] > 1)
13511 [
self wormholeEscorts];
13514 if ([
self scriptedMisjump])
13516 [
self setScriptedMisjump:NO];
13518 [
self setScriptedMisjumpRange:0.5];
13524- (void) enterWitchspace
13526 [UNIVERSE addWitchspaceJumpEffectForShip:self];
13527 [shipAI message:@"ENTERED_WITCHSPACE"];
13532 [UNIVERSE witchspaceShipWithPrimaryRole:[
self primaryRole]];
13535 [UNIVERSE removeEntity:self];
13539- (void) leaveWitchspace
13546 GLfloat min_d1 = [UNIVERSE safeWitchspaceExitDistance];
13548 while (fabs(d1) < min_d1)
13554 HPVector exitposition = [UNIVERSE getWitchspaceExitPosition];
13555 exitposition.x += v1.
x * d1;
13556 exitposition.y += v1.
y * d1;
13557 exitposition.z += v1.z * d1;
13558 [
self setPosition:exitposition];
13559 [
self witchspaceLeavingEffects];
13563- (BOOL) witchspaceLeavingEffects
13566 orientation = [UNIVERSE getWitchspaceExitRotation];
13573 flightSpeed = 50.0;
13582 [
self setStatus:STATUS_EXITING_WITCHSPACE];
13583 [shipAI message:@"EXITED_WITCHSPACE"];
13585 [UNIVERSE addWitchspaceJumpEffectForShip:self];
13586 [
self setStatus:STATUS_IN_FLIGHT];
13591- (void) markAsOffender:(
int)offence_value
13593 [
self markAsOffender:offence_value withReason:kOOLegalStatusReasonUnknown];
13599 if (![
self isPolice] && ![
self isCloaked] &&
self != [
UNIVERSE station])
13601 if ([
self isSubEntity])
13603 [[
self parentEntity] markAsOffender:offence_value withReason:reason];
13607 if ((scanClass == CLASS_THARGOID || scanClass == CLASS_STATION) && reason != kOOLegalStatusReasonSetup && reason != kOOLegalStatusReasonByScript)
13614 jsval amountVal = JSVAL_VOID;
13615 JS_NewNumberValue(context, (bounty | offence_value)-bounty, &amountVal);
13617 bounty |= offence_value;
13621 ShipScriptEvent(context,
self,
"shipBountyChanged", amountVal, reasonVal);
13631- (void) switchLightsOn
13633 NSEnumerator *subEnum =
nil;
13637 _lightsActive = YES;
13639 for (subEnum = [
self flasherEnumerator]; (se = [subEnum nextObject]); )
13643 for (subEnum = [
self shipSubEntityEnumerator]; (sub = [subEnum nextObject]); )
13650- (void) switchLightsOff
13652 NSEnumerator *subEnum =
nil;
13656 _lightsActive = NO;
13658 for (subEnum = [
self flasherEnumerator]; (se = [subEnum nextObject]); )
13662 for (subEnum = [
self shipSubEntityEnumerator]; (sub = [subEnum nextObject]); )
13669- (BOOL) lightsActive
13671 return _lightsActive;
13675- (void) setDestination:(HPVector) dest
13677 _destination = dest;
13682- (void) setEscortDestination:(HPVector) dest
13684 _destination = dest;
13688- (BOOL) canAcceptEscort:(
ShipEntity *)potentialEscort
13690 if (dockingInstructions)
13694 if (scanClass != [potentialEscort scanClass])
13698 if ([
self bounty] == 0 && [potentialEscort bounty] != 0)
13702 if (![
self isEscort])
13704 return [potentialEscort
isEscort];
13710- (BOOL) acceptAsEscort:(
ShipEntity *) other_ship
13713 if (
self == other_ship)
return NO;
13716 if ([
self status] != STATUS_IN_FLIGHT)
return NO;
13720 if ([shipAI stackDepth] > 3)
13722 OOLog(
@"ship.escort.reject",
@"%@ rejecting escort %@ because AI stack depth is %lu.",
self, other_ship, [shipAI stackDepth]);
13726 if ([
self canAcceptEscort:other_ship])
13730 if ([escortGroup containsShip:other_ship])
return YES;
13734 if (_maxEscortCount == 0 && ([
self hasPrimaryRole:
@"police"] || [
self hasPrimaryRole:
@"hunter"] || [
self hasRole:
@"thargoid-mothership"]))
13739 NSUInteger maxEscorts = _maxEscortCount;
13740 NSUInteger escortCount = [escortGroup
count] - 1;
13742 if (escortCount < maxEscorts)
13744 [other_ship setGroup:escortGroup];
13745 if ([
self group] ==
nil)
13747 [
self setGroup:escortGroup];
13749 else if ([
self group] != escortGroup) [[
self group] addShip:other_ship];
13751 if (([other_ship maxFlightSpeed] < cruiseSpeed) && ([other_ship maxFlightSpeed] > cruiseSpeed * 0.3))
13753 cruiseSpeed = [other_ship maxFlightSpeed] * 0.99;
13756 OOLog(
@"ship.escort.accept",
@"%@ accepting escort %@.",
self, other_ship);
13758 [
self doScriptEvent:OOJSID("shipAcceptedEscort") withArgument:other_ship];
13759 [other_ship doScriptEvent:OOJSID("escortAccepted") withArgument:self];
13760 [shipAI message:@"ACCEPTED_ESCORT"];
13765 OOLog(
@"ship.escort.reject",
@"%@ already got max escorts(%ld). Escort rejected: %@.",
self, escortCount, other_ship);
13770 OOLog(
@"ship.escort.reject",
@"%@ failed canAcceptEscort for escort %@.",
self, other_ship);
13779- (void) updateEscortFormation
13781 _escortPositionsValid = NO;
13790- (void) refreshEscortPositions
13792 if (!_escortPositionsValid)
13796 jsval args[] = { INT_TO_JSVAL(0), INT_TO_JSVAL(_maxEscortCount) };
13800 _escortPositionsValid = YES;
13803 for (i = 0; i < _maxEscortCount; i++)
13805 args[0] = INT_TO_JSVAL(i);
13806 OK = [script callMethod:OOJSID("coordinatesForEscortPosition")
13808 withArguments:args count:sizeof args / sizeof *args
13821- (HPVector) coordinatesForEscortPosition:(
unsigned)idx
13840- (void) deployEscorts
13842 NSEnumerator *escortEnum =
nil;
13845 NSMutableSet *idleEscorts =
nil;
13846 unsigned deployCount;
13848 if ([
self primaryTarget] ==
nil || _escortGroup ==
nil)
return;
13851 NSUInteger escortCount = [escortGroup
count] - 1;
13852 if (escortCount == 0)
return;
13854 if ([
self group] ==
nil) [
self setGroup:escortGroup];
13856 if ([
self primaryTarget] == [
self lastEscortTarget])
13862 [
self setLastEscortTarget:[
self primaryTarget]];
13865 idleEscorts = [NSMutableSet set];
13866 for (escortEnum = [
self escortEnumerator]; (escort = [escortEnum nextObject]); )
13868 if (![[[escort getAI] name] isEqualToString:
@"interceptAI.plist"] && ![escort hasNewAI])
13870 [idleEscorts addObject:escort];
13872 else if ([escort hasNewAI])
13879 escortCount = [idleEscorts count];
13880 if (escortCount == 0)
return;
13885 target = [
self primaryTarget];
13886 for (escortEnum = [idleEscorts objectEnumerator]; (escort = [escortEnum nextObject]); )
13889 [escort
setAITo:@"interceptAI.plist"];
13892 if (--deployCount == 0)
break;
13895 [
self updateEscortFormation];
13900- (void) dockEscorts
13902 if (![
self hasEscorts])
return;
13905 NSEnumerator *escortEnum =
nil;
13910 for (escortEnum = [[
self escortArray] objectEnumerator]; (escort = [escortEnum nextObject]); )
13912 float delay = i++ * 3.0 + 1.5;
13916 if ([escort group] == escortGroup) [escort
setGroup:nil];
13917 if ([escort owner] ==
self) [escort
setOwner:escort];
13920 if (![escort hasNewAI])
13922 [escort
setAITo:@"dockingAI.plist"];
13929 [_escortGroup release];
13930 _escortGroup =
nil;
13934- (void) setTargetToNearestStationIncludingHostiles:(BOOL) includeHostiles
13937 Entity *mother = [[
self group] leader];
13938 if ([mother isStation])
13940 [
self addTarget:mother];
13941 [
self setTargetStation:mother];
13948 int ent_count =
UNIVERSE->n_entities;
13950 Entity *my_entities[ent_count];
13952 int station_count = 0;
13953 for (i = 0; i < ent_count; i++)
13954 if (uni_entities[i]->isStation)
13955 my_entities[station_count++] = [uni_entities[i] retain];
13959 for (i = 0; i < station_count; i++)
13962 range2 = HPdistance2(position, thing->
position);
13963 if (range2 < nearest2 && (includeHostiles || ![thing isHostileTo:
self]))
13969 for (i = 0; i < station_count; i++)
13970 [my_entities[i] release];
13974 [
self addTarget:station];
13975 [
self setTargetStation:station];
13979 [shipAI message:@"NO_STATION_FOUND"];
13985- (void) setTargetToNearestFriendlyStation
13987 [
self setTargetToNearestStationIncludingHostiles:NO];
13992- (void) setTargetToNearestStation
13994 [
self setTargetToNearestStationIncludingHostiles:YES];
13999- (void) setTargetToSystemStation
14003 if (!system_station)
14005 [shipAI message:@"NOTHING_FOUND"];
14006 [shipAI message:@"NO_STATION_FOUND"];
14008 [
self setTargetStation:nil];
14014 [shipAI message:
@"NOTHING_FOUND"];
14015 [shipAI message:
@"NO_STATION_FOUND"];
14016 DESTROY(_primaryTarget);
14017 [self setTargetStation:nil];
14021 [
self addTarget:system_station];
14022 [
self setTargetStation:system_station];
14027- (void) landOnPlanet:(OOPlanetEntity *)planet
14029 if (planet && [
self isShuttle])
14031 [planet welcomeShuttle:self];
14033 [
self doScriptEvent:OOJSID("shipLandedOnPlanet") withArgument:planet andReactToAIMessage:@"LANDED_ON_PLANET"];
14036 if ([
self reportAIMessages])
14038 OOLog(
@"planet.collide.shuttleLanded",
@"DEBUG: %@ landed on planet %@",
self, planet);
14042 [UNIVERSE removeEntity:self];
14047- (void) abortDocking
14049 [[UNIVERSE findEntitiesMatchingPredicate:IsStationPredicate
14053 makeObjectsPerformSelector:@selector(abortDockingForShip:) withObject:self];
14057- (NSDictionary *) dockingInstructions
14059 return dockingInstructions;
14063- (void) broadcastThargoidDestroyed
14065 [[UNIVERSE findShipsMatchingPredicate:HasRolePredicate
14066 parameter:@"tharglet"
14067 inRange:SCANNER_MAX_RANGE
14069 makeObjectsPerformSelector:@selector(sendAIMessage:) withObject:@"THARGOID_DESTROYED"];
14073static BOOL AuthorityPredicate(
Entity *entity,
void *parameter)
14078 if (entity == [
UNIVERSE station] && [victim withinStationAegis])
14084 if ([entity scanClass] == CLASS_POLICE &&
14095- (void) broadcastHitByLaserFrom:(
ShipEntity *) aggressor_ship
14099 if ([
self bounty])
return;
14100 if (!aggressor_ship)
return;
14102 if ( (scanClass == CLASS_NEUTRAL)||
14103 (scanClass == CLASS_STATION)||
14104 (scanClass == CLASS_BUOY)||
14105 (scanClass == CLASS_POLICE)||
14106 (scanClass == CLASS_MILITARY)||
14107 (scanClass == CLASS_PLAYER))
14109 NSArray *authorities =
nil;
14110 NSEnumerator *authEnum =
nil;
14113 authorities = [UNIVERSE findShipsMatchingPredicate:AuthorityPredicate
14117 authEnum = [authorities objectEnumerator];
14118 while ((auth = [authEnum nextObject]))
14128- (void) sendMessage:(NSString *) message_text toShip:(
ShipEntity*) other_ship withUnpilotedOverride:(BOOL)unpilotedOverride
14130 if (!other_ship || !message_text)
return;
14131 if (!crew && !unpilotedOverride)
return;
14133 double d2 = HPdistance2(position, [other_ship position]);
14134 if (d2 > scannerRange * scannerRange)
14137 NSString *expandedMessage =
OOExpand(message_text);
14141 [
self setCommsMessageColor];
14144 [UNIVERSE resetCommsLogColor];
14151- (void) sendExpandedMessage:(NSString *)message_text toShip:(
ShipEntity *)other_ship
14153 if (!other_ship || !crew)
14155 if ((lastRadioMessage) && (messageTime > 0.0) && [message_text isEqual:lastRadioMessage])
14157 [lastRadioMessage autorelease];
14158 lastRadioMessage = [message_text retain];
14160 double d2 = HPdistance2(position, [other_ship position]);
14161 if (d2 > scannerRange * scannerRange)
14168 very_random_seed.
a = rand() & 255;
14169 very_random_seed.
b = rand() & 255;
14170 very_random_seed.
c = rand() & 255;
14171 very_random_seed.
d = rand() & 255;
14172 very_random_seed.
e = rand() & 255;
14173 very_random_seed.
f = rand() & 255;
14176 NSDictionary *specials = [NSDictionary dictionaryWithObjectsAndKeys:
14177 [
self displayName], @"[self:name]",
14182 [
self sendMessage:expandedMessage toShip:other_ship withUnpilotedOverride:NO];
14186- (void) broadcastAIMessage:(NSString *) ai_message
14188 NSString *expandedMessage =
OOExpand(ai_message);
14190 [
self checkScanner];
14192 for (i = 0; i < n_scanned_ships ; i++)
14200- (void) broadcastMessage:(NSString *) message_text withUnpilotedOverride:(BOOL) unpilotedOverride
14202 NSString *expandedMessage =
OOExpand(message_text);
14205 if (!crew && !unpilotedOverride)
14208 [
self checkScanner];
14210 for (i = 0; i < n_scanned_ships ; i++)
14213 if (![ship isPlayer]) [ship receiveCommsMessage:expandedMessage from:
self];
14221 [
self setCommsMessageColor];
14224 [UNIVERSE resetCommsLogColor];
14229- (void) setCommsMessageColor
14231 float hue = 0.0625f * (universalID & 15);
14233 if (scanClass == CLASS_THARGOID)
14235 if (scanClass == CLASS_POLICE)
14240- (void) receiveCommsMessage:(NSString *) message_text from:(
ShipEntity *) other
14243 [
self doScriptEvent:OOJSID("commsMessageReceived") withArgument:message_text andArgument:other];
14247- (void) commsMessage:(NSString *)valueString withUnpilotedOverride:(BOOL)unpilotedOverride
14250 very_random_seed.
a = rand() & 255;
14251 very_random_seed.
b = rand() & 255;
14252 very_random_seed.
c = rand() & 255;
14253 very_random_seed.
d = rand() & 255;
14254 very_random_seed.
e = rand() & 255;
14255 very_random_seed.
f = rand() & 255;
14258 [
self broadcastMessage:valueString withUnpilotedOverride:unpilotedOverride];
14262- (BOOL) markedForFines
14264 return being_fined;
14268- (BOOL) markForFines
14272 being_fined = ([
self legalStatus] > 0);
14273 return being_fined;
14279 return ((behaviour == BEHAVIOUR_ATTACK_MINING_TARGET)&&([forward_weapon_type isMiningLaser]));
14283- (void) interpretAIMessage:(NSString *)ms
14290 int switcher_id = [(NSString*)[tokens objectAtIndex:1] intValue];
14291 Entity* switcher = [UNIVERSE entityForUniversalID:switcher_id];
14292 int rescuer_id = [(NSString*)[tokens objectAtIndex:2] intValue];
14293 Entity* rescuer = [UNIVERSE entityForUniversalID:rescuer_id];
14294 if ((switcher == [
self primaryAggressor])&&(switcher == [
self primaryTarget])&&(switcher)&&(rescuer)&&(rescuer->
isShip)&&([
self thankedShip] != rescuer)&&(scanClass != CLASS_THARGOID))
14298 if (scanClass == CLASS_POLICE)
14300 [
self sendExpandedMessage:@"[police-thanks-for-assist]" toShip:rescueShip];
14305 [
self sendExpandedMessage:@"[thanks-for-assist]" toShip:rescueShip];
14307 [
self setThankedShip:rescuer];
14313- (BoundingBox) findBoundingBoxRelativeTo:(
Entity *)other InVectors:(Vector) _i :(Vector) _j :(Vector) _k
14315 HPVector opv = other ? other->
position : position;
14316 return [
self findBoundingBoxRelativeToPosition:opv InVectors:_i :_j :_k];
14321- (void) spawn:(NSString *)roles_number
14324 NSString *roleString =
nil;
14325 NSString *numberString =
nil;
14328 if ([tokens
count] != 2)
14334 roleString = [tokens oo_stringAtIndex:0];
14335 numberString = [tokens oo_stringAtIndex:1];
14337 number = [numberString intValue];
14339 [
self spawnShipsWithRole:roleString count:number];
14343- (
int) checkShipsInVicinityForWitchJumpExit
14358 int ent_count =
UNIVERSE->n_entities;
14363 int ship_count = 0;
14364 for (i = 0; i < ent_count; i++)
14365 if ((uni_entities[i]->isShip)&&(uni_entities[i] !=
self))
14366 my_entities[ship_count++] = (
ShipEntity*)[uni_entities[i] retain];
14368 for (i = 0; (i < ship_count)&&(result ==
NO_TARGET) ; i++)
14371 HPVector delta = HPvector_between(position, ship->
position);
14372 GLfloat d2 = HPmagnitude2(delta);
14373 if (![ship isPlayer] || ![
PLAYER isDocked])
14379 for (i = 0; i < ship_count; i++)
14380 [my_entities[i] release];
14386- (BOOL) trackCloseContacts
14388 return trackCloseContacts;
14392- (void) setTrackCloseContacts:(BOOL) value
14394 if (value == (BOOL)trackCloseContacts)
return;
14396 trackCloseContacts = value;
14397 [closeContactsInfo release];
14399 if (trackCloseContacts)
14401 closeContactsInfo = [[NSMutableDictionary alloc] init];
14405 closeContactsInfo =
nil;
14410#if OO_SALVAGE_SUPPORT
14412- (void) claimAsSalvage
14416 OOLog(
@"claimAsSalvage.called",
@"claimAsSalvage called on %@ %@", [
self name], [
self roleSet]);
14419 if (![
self isHulk])
14421 OOLog(
@"claimAsSalvage.failed.notHulk",
@"claimAsSalvage failed because not a hulk");
14426 [
self setTargetToSystemStation];
14427 if ([
self primaryTarget] ==
nil)
14429 OOLog(
@"claimAsSalvage.failed.noStation",
@"claimAsSalvage failed because did not find a station");
14435 OOLog(
@"claimAsSalvage.requestingPilot",
@"claimAsSalvage asking station to launch a pilot boat");
14437 [
self setReportAIMessages:YES];
14438 OOLog(
@"claimAsSalvage.success",
@"claimAsSalvage setting own state machine to capturedShipAI.plist");
14439 [
self setAITo:@"capturedShipAI.plist"];
14443- (void) sendCoordinatesToPilot
14448 n_scanned_ships = 0;
14450 OOLog(
@"ship.pilotage",
@"searching for pilot boat");
14451 while (scan &&(scan->
isShip == NO))
14463 if ([
self hasRole:
@"pilot"] == YES)
14465 if ([scanShip primaryTarget] ==
nil)
14467 OOLog(
@"ship.pilotage",
@"found pilot boat with no target, will use this one");
14475 while (scan && (scan->
isShip == NO))
14483 OOLog(
@"ship.pilotage",
@"becoming pilot target and setting AI");
14486 [pilot
setAITo:@"pilotAI.plist"];
14487 [
self reactToAIMessage:@"FOUND_PILOT" context:@"flight update"];
14492- (void) pilotArrived
14495 [
self reactToAIMessage:@"PILOT_ARRIVED" context:@"flight update"];
14501- (void)dumpSelfState
14503 NSMutableArray *flags =
nil;
14504 NSString *flagsString =
nil;
14506 [
super dumpSelfState];
14508 OOLog(
@"dumpState.shipEntity",
@"Type: %@", [
self shipDataKey]);
14509 OOLog(
@"dumpState.shipEntity",
@"Name: %@", name);
14510 OOLog(
@"dumpState.shipEntity",
@"Display Name: %@", [
self displayName]);
14511 OOLog(
@"dumpState.shipEntity",
@"Roles: %@", [
self roleSet]);
14512 OOLog(
@"dumpState.shipEntity",
@"Primary role: %@", primaryRole);
14513 OOLog(
@"dumpState.shipEntity",
@"Script: %@", script);
14514 OOLog(
@"dumpState.shipEntity",
@"Subentity count: %lu", [
self subEntityCount]);
14516 id target = [
self primaryTarget];
14517 if (target ==
nil) target =
@"<none>";
14518 OOLog(
@"dumpState.shipEntity",
@"Target: %@", target);
14519 OOLog(
@"dumpState.shipEntity",
@"Destination: %@", HPVectorDescription(_destination));
14520 OOLog(
@"dumpState.shipEntity",
@"Other destination: %@", HPVectorDescription(coordinates));
14521 OOLog(
@"dumpState.shipEntity",
@"Waypoint count: %u", number_of_navpoints);
14522 OOLog(
@"dumpState.shipEntity",
@"Desired speed: %g", desired_speed);
14523 OOLog(
@"dumpState.shipEntity",
@"Thrust: %g", thrust);
14524 if ([
self escortCount] != 0)
OOLog(
@"dumpState.shipEntity",
@"Escort count: %u", [
self escortCount]);
14525 OOLog(
@"dumpState.shipEntity",
@"Fuel: %i", fuel);
14526 OOLog(
@"dumpState.shipEntity",
@"Fuel accumulator: %g", fuel_accumulator);
14527 OOLog(
@"dumpState.shipEntity",
@"Missile count: %u", missiles);
14531 OOLog(
@"dumpState.shipEntity.ai",
@"%@",
@"AI:");
14536 [shipAI dumpState];
14538 @catch (
id exception) {}
14541 OOLog(
@"dumpState.shipEntity",
@"Accuracy: %g", accuracy);
14542 OOLog(
@"dumpState.shipEntity",
@"Jink position: %@", VectorDescription(jink));
14543 OOLog(
@"dumpState.shipEntity",
@"Frustration: %g", frustration);
14544 OOLog(
@"dumpState.shipEntity",
@"Success factor: %g", success_factor);
14545 OOLog(
@"dumpState.shipEntity",
@"Shots fired: %u", shot_counter);
14546 OOLog(
@"dumpState.shipEntity",
@"Time since shot: %g", [
self shotTime]);
14547 OOLog(
@"dumpState.shipEntity",
@"Spawn time: %g (%g seconds ago)", [
self spawnTime], [
self timeElapsedSinceSpawn]);
14548 if ([
self isBeacon])
14550 OOLog(
@"dumpState.shipEntity",
@"Beacon code: %@", [
self beaconCode]);
14552 OOLog(
@"dumpState.shipEntity",
@"Hull temperature: %g", ship_temperature);
14553 OOLog(
@"dumpState.shipEntity",
@"Heat insulation: %g", [
self heatInsulation]);
14555 flags = [NSMutableArray array];
14556 #define ADD_FLAG_IF_SET(x) if (x) { [flags addObject:@#x]; }
14570 flagsString = [flags count] ? [flags componentsJoinedByString:@", "] : (NSString *)
@"none";
14571 OOLog(
@"dumpState.shipEntity",
@"Flags: %@", flagsString);
14582- (NSDictionary *)scriptInfo
14584 return (scriptInfo !=
nil) ? scriptInfo : (NSDictionary *)[NSDictionary dictionary];
14588- (void) overrideScriptInfo:(NSDictionary *)override
14590 if (scriptInfo ==
nil) scriptInfo = [override retain];
14591 else if (
override !=
nil)
14593 NSMutableDictionary *newInfo = [NSMutableDictionary dictionaryWithDictionary:scriptInfo];
14594 [newInfo addEntriesFromDictionary:override];
14595 [scriptInfo release];
14596 scriptInfo = [newInfo copy];
14601- (
Entity *)entityForShaderProperties
14603 return [
self rootShipEntity];
14606- (void) setDemoShip: (
OOScalar) rate
14608 demoStartOrientation = orientation;
14611 [
self setPitch: 0.0f];
14612 [
self setRoll: 0.0f];
14622 demoStartTime = time;
14627 return demoStartTime;
14631- (void) doScriptEvent:(jsid)message
14634 [
self doScriptEvent:message inContext:context withArguments:NULL count:0];
14639- (void) doScriptEvent:(jsid)message withArgument:(
id)argument
14644 [
self doScriptEvent:message inContext:context withArguments:&value count:1];
14650- (void) doScriptEvent:(jsid)message
14651 withArgument:(
id)argument1
14652 andArgument:(
id)argument2
14657 [
self doScriptEvent:message inContext:context withArguments:argv count:2];
14663- (void) doScriptEvent:(jsid)message withArguments:(NSArray *)arguments
14667 jsval *argv = NULL;
14670 argc = (uintN)[arguments
count];
14673 argv = malloc(
sizeof *argv * argc);
14676 for (i = 0; i != argc; ++i)
14678 argv[i] = [[arguments objectAtIndex:i] oo_jsValueInContext:context];
14685 [
self doScriptEvent:message inContext:context withArguments:argv count:argc];
14690 for (i = 0; i != argc; ++i)
14692 JS_RemoveValueRoot(context, &argv[i]);
14701- (void) doScriptEvent:(jsid)message withArguments:(jsval *)argv count:(uintN)argc
14704 [
self doScriptEvent:message inContext:context withArguments:argv count:argc];
14709- (void) doScriptEvent:(jsid)message inContext:(JSContext *)context withArguments:(jsval *)argv count:(uintN)argc
14712 [script callMethod:message inContext:context withArguments:argv count:argc result:NULL];
14713 [aiScript callMethod:message inContext:context withArguments:argv count:argc result:NULL];
14717- (void) reactToAIMessage:(NSString *)message context:(NSString *)debugContext
14719 [shipAI reactToMessage:message context:debugContext];
14723- (void) sendAIMessage:(NSString *)message
14725 [shipAI message:message];
14729- (void) doScriptEvent:(jsid)scriptEvent andReactToAIMessage:(NSString *)aiMessage
14731 [
self doScriptEvent:scriptEvent];
14732 [
self reactToAIMessage:aiMessage context:nil];
14736- (void) doScriptEvent:(jsid)scriptEvent withArgument:(
id)argument andReactToAIMessage:(NSString *)aiMessage
14738 [
self doScriptEvent:scriptEvent withArgument:argument];
14739 [
self reactToAIMessage:aiMessage context:nil];
14747 if ([
self status] == STATUS_DOCKED)
14751 if ([
self hasHostileTarget] || energy < maxEnergy / 4)
14761 if ([
self status] == STATUS_DOCKED)
14765 if ([
self hasHostileTarget])
14771 NSEnumerator *sEnum = [_defenseTargets objectEnumerator];
14773 double scanrange2 = scannerRange * scannerRange;
14774 while ((ship = [sEnum nextObject]))
14776 if ([ship hasHostileTarget] || ([ship isPlayer] && [
PLAYER weaponsOnline]))
14778 if (HPdistance2([ship position],position) < scanrange2)
14785 if ([
self hasHostileTarget])
14787 Entity *ptarget = [
self primaryTargetWithoutValidityCheck];
14788 if (ptarget !=
nil && [ptarget isShip])
14791 if ([ship hasHostileTarget] || ([ship isPlayer] && [
PLAYER weaponsOnline]))
14793 if (HPdistance2([ship position],position) < scanrange2 * 1.5625)
14802 sEnum = [_group objectEnumerator];
14803 while ((ship = [sEnum nextObject]))
14805 if ([ship hasHostileTarget] || ([ship isPlayer] && [
PLAYER weaponsOnline]))
14807 if (HPdistance2([ship position],position) < scanrange2)
14814 if (_escortGroup && _group != _escortGroup)
14816 sEnum = [_escortGroup objectEnumerator];
14817 while ((ship = [sEnum nextObject]))
14819 if ([ship hasHostileTarget] || ([ship isPlayer] && [
PLAYER weaponsOnline]))
14821 if (HPdistance2([ship position],position) < scanrange2)
14841- (NSString *) descriptionForObjDump
14843 NSString *desc = [
super descriptionForObjDump];
14844 desc = [NSString stringWithFormat:@"%@ mass %g", desc, [
self mass]];
14845 if (![
self isPlayer])
14847 desc = [NSString stringWithFormat:@"%@ AI: %@", desc, [[
self getAI] shortDescriptionComponents]];
14856@implementation Entity (SubEntityRelationship)
14858- (BOOL) isShipWithSubEntityShip:(
Entity *)other
14864- (void) drawSubEntityImmediate:(
bool)immediate translucent:(
bool)translucent
14872@implementation ShipEntity (SubEntityRelationship)
14874- (BOOL) isShipWithSubEntityShip:(
Entity *)other
14876 assert ([
self isShip]);
14878 if (![other isShip])
return NO;
14879 if (![other isSubEntity])
return NO;
14880 if ([other owner] !=
self)
return NO;
14884 if (![
self hasSubEntity:(
ShipEntity *)other])
14886 OOLogERR(
@"ship.subentity.sanityCheck.failed",
@"%@ thinks it's a subentity of %@, but the supposed parent does not agree. %@", [other shortDescription], [
self shortDescription],
@"This is an internal error, please report it.");
14900 static NSDictionary *macros =
nil;
14913 static NSSet *entityWhitelist =
nil;
14914 static NSSet *shipWhitelist =
nil;
14915 static NSSet *playerShipWhitelist =
nil;
14916 static NSSet *visualEffectWhitelist =
nil;
14918 if (entityWhitelist ==
nil)
14921 entityWhitelist = [[NSSet alloc] initWithArray:[wlDict oo_arrayForKey:@"shader_entity_binding_methods"]];
14922 shipWhitelist = [[NSSet alloc] initWithArray:[wlDict oo_arrayForKey:@"shader_ship_binding_methods"]];
14923 playerShipWhitelist = [[NSSet alloc] initWithArray:[wlDict oo_arrayForKey:@"shader_player_ship_binding_methods"]];
14924 visualEffectWhitelist = [[NSSet alloc] initWithArray:[wlDict oo_arrayForKey:@"shader_visual_effect_binding_methods"]];
14927 if ([bindingTarget isKindOfClass:[
Entity class]])
14929 if ([entityWhitelist containsObject:propertyName])
return YES;
14930 if ([bindingTarget isShip])
14932 if ([shipWhitelist containsObject:propertyName])
return YES;
14934 if ([bindingTarget isPlayerLikeShip])
14936 if ([playerShipWhitelist containsObject:propertyName])
return YES;
14938 if ([bindingTarget isVisualEffect])
14940 if ([visualEffectWhitelist containsObject:propertyName])
return YES;
14956 return weapon ==
nil || [[weapon
identifier] isEqualToString:@"EQ_WEAPON_NONE"];
#define NO_DRAW_DISTANCE_FACTOR
OOScanClass OOScanClassFromString(NSString *string) PURE_FUNC
#define SCANNER_MAX_RANGE
#define SCANNER_MAX_RANGE2
#define ADD_FLAG_IF_SET(x)
#define foreachkey(VAR, DICT)
OOINLINE jsval OOJSValueFromLegalStatusReason(JSContext *context, OOLegalStatusReason value)
OOINLINE jsval OOJSValueFromShipDamageType(JSContext *context, OOShipDamageType value)
OOCargoType StringToCargoType(NSString *string) PURE_FUNC
NSString * OOStringFromLegalStatusReason(OOLegalStatusReason reason)
OOINLINE void OODebugDrawColoredBoundingBox(BoundingBox box, OOColor *color)
OOINLINE void OODebugDrawBoundingBox(BoundingBox box)
void OODebugDrawColoredLine(Vector start, Vector end, OOColor *color)
void OODebugDrawPoint(Vector position, OOColor *color)
void OOStandardsDeprecated(NSString *message)
BOOL OOEnforceStandards(void)
const HPVector kZeroHPVector
HPVector OOHPVectorRandomSpatial(OOHPScalar maxLength)
#define OOJS_PROFILE_EXIT
#define OOJS_PROFILE_ENTER
BOOL JSValueToVector(JSContext *context, jsval value, Vector *outVector) NONNULL_FUNC
OOINLINE jsval OOJSValueFromNativeObject(JSContext *context, id object)
id OOJSNativeObjectFromJSObject(JSContext *context, JSObject *object)
OOINLINE JSContext * OOJSAcquireContext(void)
OOINLINE void OOJSRelinquishContext(JSContext *context)
#define OOJSAddGCValueRoot(context, root, name)
void OOLogPushIndent(void)
#define OOLogWARN(class, format,...)
#define OOLogERR(class, format,...)
void OOLogPopIndent(void)
BOOL OOLogWillDisplayMessagesInClass(NSString *inMessageClass)
#define OOLog(class, format,...)
HPVector OOHPVectorMultiplyMatrix(HPVector v, OOMatrix m)
const OOMatrix kIdentityMatrix
Vector OOVectorMultiplyMatrix(Vector v, OOMatrix m)
void OOGLPushModelView(void)
void OOGLTranslateModelView(Vector vector)
void OOGLMultModelView(OOMatrix matrix)
OOMatrix OOGLPopModelView(void)
#define OOVerifyOpenGLState()
Vector vector_up_from_quaternion(Quaternion quat)
void quaternion_rotate_about_x(Quaternion *quat, OOScalar angle)
Vector vector_right_from_quaternion(Quaternion quat)
Vector vector_forward_from_quaternion(Quaternion quat)
void quaternion_rotate_about_z(Quaternion *quat, OOScalar angle)
void quaternion_set_random(Quaternion *quat)
Vector quaternion_rotate_vector(Quaternion q, Vector v)
const Quaternion kIdentityQuaternion
Quaternion quaternion_rotation_between(Vector v0, Vector v1)
void quaternion_rotate_about_y(Quaternion *quat, OOScalar angle)
const Quaternion kZeroQuaternion
void quaternion_rotate_about_axis(Quaternion *quat, Vector axis, OOScalar angle)
Quaternion quaternion_multiply(Quaternion q1, Quaternion q2)
@ STELLAR_TYPE_NORMAL_PLANET
Random_Seed OOStringExpanderDefaultRandomSeed(void)
NSString * OOExpandDescriptionString(Random_Seed seed, NSString *string, NSDictionary *overrides, NSDictionary *legacyLocals, NSString *systemName, OOExpandOptions options)
#define OOExpand(string,...)
NSMutableArray * ScanTokensFromString(NSString *values)
BOOL ScanVectorFromString(NSString *xyzString, Vector *outVector)
uint8_t OOWeaponFacingSet
NSString * OOCommodityType
@ AEGIS_CLOSE_TO_MAIN_PLANET
@ AEGIS_CLOSE_TO_ANY_PLANET
uint64_t OOCreditsQuantity
#define VALID_WEAPON_FACINGS
@ CARGO_FLAG_FULL_CONTRABAND
@ CARGO_FLAG_FULL_PLENTIFUL
@ CARGO_FLAG_FULL_UNIFORM
@ CARGO_FLAG_FULL_MEDICAL
@ CARGO_FLAG_FULL_PASSENGERS
@ WEAPON_FACING_STARBOARD
const Vector kBasisYVector
const Vector kBasisZVector
Vector OORandomPositionInBoundingBox(BoundingBox bb)
const Vector kBasisXVector
static GLfloat scripted_color[4]
static BOOL isHitByOctree(Octree_details axialDetails, Octree_details otherDetails, Vector delta, Triangle other_ijk)
static NSString *const kOOLogSyntaxAddShips
#define HYPERSPEED_FACTOR
@ SCOOP_STATUS_NOT_INSTALLED
#define MIN_HYPERSPEED_FACTOR
#define INITIAL_SHOT_TIME
#define COMBAT_BROADSIDE_IN_RANGE_FACTOR
BOOL isWeaponNone(OOWeaponType weapon)
#define COMBAT_AI_TRACKS_CLOSER
#define SHIP_COOLING_FACTOR
#define CLOAKING_DEVICE_MIN_ENERGY
#define COMBAT_AI_WEAPON_TEMP_READY
OOWeaponType OOWeaponTypeFromEquipmentIdentifierStrict(NSString *string) PURE_FUNC
#define COMBAT_BROADSIDE_RANGE_FACTOR
#define COMBAT_AI_WEAPON_TEMP_USABLE
#define ShipScriptEventNoCx(ship, event,...)
#define TURRET_SHOT_RANGE
#define COMBAT_AI_ISNT_AWFUL
#define TURRET_MINIMUM_COS
NSDictionary * OODefaultShipShaderMacros(void)
#define SHIP_MIN_CABIN_TEMP
#define CLOAKING_DEVICE_START_ENERGY
#define NPC_MAX_WEAPON_TEMP
#define COMBAT_AI_FLEES_BETTER_2
#define MILITARY_JAMMER_ENERGY_RATE
#define CLOAKING_DEVICE_ENERGY_RATE
#define AIMS_AGGRESSOR_SWITCHED_TARGET
#define COMBAT_AI_DOGFIGHTER
#define WEAPON_COOLING_FACTOR
#define ENTITY_PERSONALITY_MAX
GLfloat getWeaponRangeFromType(OOWeaponType weapon_type)
#define SHIP_MAX_CABIN_TEMP
#define MILITARY_JAMMER_MIN_ENERGY
#define COMBAT_AI_FLEES_BETTER
#define TURRET_SHOT_SPEED
#define COMBAT_IN_RANGE_FACTOR
#define COMBAT_OUT_RANGE_FACTOR
#define COMBAT_WEAPON_RANGE_FACTOR
#define COMBAT_AI_CONFIDENCE_FACTOR
#define COMBAT_AI_IS_SMART
NSString * OOStringFromBehaviour(OOBehaviour behaviour) CONST_FUNC
OOWeaponType OOWeaponTypeFromEquipmentIdentifierSloppy(NSString *string) PURE_FUNC
#define BASELINE_SHIELD_LEVEL
#define SHIPENTITY_MAX_MISSILES
#define MAX_LANDING_SPEED2
#define SHIP_THRUST_FACTOR
OOWeaponType OOWeaponTypeFromString(NSString *string) PURE_FUNC
#define ShipScriptEvent(context, ship, event,...)
#define WEAPON_COOLING_CUTOUT
#define SHIP_INSULATION_FACTOR
#define SHIP_ENERGY_DAMAGE_TO_HEAT_FACTOR
static ShipEntity * doOctreesCollide(ShipEntity *prime, ShipEntity *other)
static GLfloat mascem_color1[4]
static GLfloat scripted_color[4]
static GLfloat neutral_color[4]
static GLfloat cargo_color[4]
static GLfloat hostile_color[4]
static NSString *const kOOLogEntityBehaviourChanged
static GLfloat missile_color[4]
static GLfloat police_color1[4]
BOOL OOUniformBindingPermitted(NSString *propertyName, id bindingTarget)
static GLfloat mascem_color2[4]
static GLfloat jammed_color[4]
static GLfloat friendly_color[4]
static GLfloat police_color2[4]
static NSString *const kOOLogSyntaxAddShips
#define PROXIMITY_AVOID_DISTANCE_FACTOR
Entity< OOStellarBody > * lastAegisLock()
void refreshEscortPositions()
void exitStateMachineWithMessage:(NSString *message)
void message:(NSString *ms)
void setState:afterDelay:(NSString *stateName,[afterDelay] NSTimeInterval delay)
void setState:(NSString *stateName)
OOUniversalID universalID
HPVector absolutePositionForSubentity()
NSMutableArray * collisionArray()
void setVelocity:(Vector vel)
void setOrientation:(Quaternion quat)
GLfloat collisionRadius()
void setOwner:(Entity *ent)
void setScanClass:(OOScanClass sClass)
void drawSubEntityImmediate:translucent:(bool immediate, [translucent] bool translucent)
void setDistanceTravelled:(GLfloat value)
ShipEntity * rootShipEntity()
void setEnergy:(GLfloat amount)
Quaternion normalOrientation()
ShipEntity * parentEntity()
void takeEnergyDamage:from:becauseOf:weaponIdentifier:(double amount,[from] Entity *ent,[becauseOf] Entity *other,[weaponIdentifier] NSString *weaponIdentifier)
void setPosition:(HPVector posn)
OOMatrix drawRotationMatrix()
id fragmentBurstFromEntity:(Entity *entity)
OOCharacter * characterWithDictionary:(NSDictionary *c_dict)
void setLegalStatus:(int value)
NSDictionary * infoForScripting()
OOCharacter * randomCharacterWithRole:andOriginalSystem:(NSString *c_role,[andOriginalSystem] OOSystemID s)
OOColor * colorWithRGBAComponents:(OORGBAComponents components)
OOColor * brightColorWithDescription:(id description)
OOColor * colorWithDescription:(id description)
void getRed:green:blue:alpha:(float *red,[green] float *green,[blue] float *blue,[alpha] float *alpha)
OOColor * colorWithHue:saturation:brightness:alpha:(float hue,[saturation] float saturation,[brightness] float brightness,[alpha] float alpha)
NSString * conditionScript()
GLfloat weaponEnergyUse()
GLfloat weaponShotTemperature()
void addEquipmentWithInfo:(NSArray *itemInfo)
void setMissileRegistryRole:forShip:(NSString *roles,[forShip] NSString *shipKey)
NSString * getMissileRegistryRoleForShip:(NSString *shipKey)
OOEquipmentType * equipmentTypeWithIdentifier:(NSString *identifier)
NSArray * allEquipmentTypes()
GLfloat weaponRechargeRate()
double findCollisionRadius()
id exhaustForShip:withDefinition:andScale:(ShipEntity *ship,[withDefinition] NSArray *definition,[andScale] float scale)
instancetype explosionCloudFromEntity:withSettings:(Entity *entity,[withSettings] NSDictionary *settings)
instancetype explosionCloudFromEntity:withSize:andSettings:(Entity *entity,[withSize] float size,[andSettings] NSDictionary *settings)
instancetype explosionFlashFromEntity:(Entity *entity)
instancetype flasherWithDictionary:(NSDictionary *dictionary)
void setActive:(BOOL active)
void rescaleBy:(GLfloat factor)
BOOL callMethod:inContext:withArguments:count:result:(jsid methodID,[inContext] JSContext *context,[withArguments] jsval *argv,[count] intN argc,[result] jsval *outResult)
void setRange:(GLfloat range)
void setColor:(OOColor *color)
instancetype laserFromShip:direction:offset:(ShipEntity *ship,[direction] OOWeaponFacing direction,[offset] Vector offset)
instancetype meshWithName:cacheKey:materialDictionary:shadersDictionary:smooth:shaderMacros:shaderBindingTarget:scaleFactor:cacheWriteable:(NSString *name,[cacheKey] NSString *cacheKey,[materialDictionary] NSDictionary *materialDict,[shadersDictionary] NSDictionary *shadersDict,[smooth] BOOL smooth,[shaderMacros] NSDictionary *macros,[shaderBindingTarget] id< OOWeakReferenceSupport > object,[scaleFactor] float factor,[cacheWriteable] BOOL cacheWriteable)
instancetype quiriumCascadeFromShip:(ShipEntity *ship)
instancetype ringFromEntity:(Entity *sourceEntity)
instancetype roleSetWithString:(NSString *roleString)
instancetype roleSetWithRole:probability:(NSString *role,[probability] float probability)
id jsScriptFromFileNamed:properties:(NSString *fileName,[properties] NSDictionary *properties)
BOOL addShip:(ShipEntity *ship)
void setLeader:(ShipEntity *leader)
instancetype groupWithName:(NSString *name)
NSDictionary * shipyardInfoForKey:(NSString *key)
OOShipRegistry * sharedRegistry()
id fragmentBurstFromEntity:(Entity *entity)
void setScriptTarget:(ShipEntity *ship)
void receiveCommsMessage:from:(NSString *message_text, [from] ShipEntity *other)
NSDictionary * materialDefaults()
NSDictionary * whitelistDictionary()
NSDictionary * dictionaryFromFilesNamed:inFolder:andMerge:(NSString *fileName,[inFolder] NSString *folderName,[andMerge] BOOL mergeFiles)
void setWeaponEnergy:(float value)
void setSuppressExplosion:(BOOL suppress)
HPVector absoluteTractorPosition()
void setTargetStation:(Entity *targetEntity)
HPVector distance_twelve:withOffset:(GLfloat dist,[withOffset] GLfloat offset)
void setBounty:withReason:(OOCreditsQuantity amount,[withReason] OOLegalStatusReason reason)
void addTarget:(Entity *targetEntity)
void setPrimaryAggressor:(Entity *targetEntity)
void setDesiredSpeed:(double amount)
NSDictionary * shipInfoDictionary()
void takeEnergyDamage:from:becauseOf:weaponIdentifier:(double amount, [from] Entity *ent, [becauseOf] Entity *other, [weaponIdentifier] NSString *weaponIdentifier)
void setStatus:(OOEntityStatus stat)
void setRoll:(double amount)
void setThrust:(double amount)
ShipEntity * subEntityTakingDamage()
void setSpeed:(double amount)
void takeScrapeDamage:from:(double amount,[from] Entity *ent)
void setSingleCrewWithRole:(NSString *crewRole)
void setGroup:(OOShipGroup *group)
void setEscortDestination:(HPVector dest)
void addImpactMoment:fraction:(Vector moment,[fraction] GLfloat howmuch)
static float SurfaceDistanceSqared(Entity *reference, Entity< OOStellarBody > *stellar)
void receiveCommsMessage:from:(NSString *message_text,[from] ShipEntity *other)
void setDisplayName:(NSString *inName)
double rangeToSecondaryTarget:(Entity *target)
void setPrimaryRole:(NSString *role)
void subEntityDied:(ShipEntity *sub)
NSEnumerator * defenseTargetEnumerator()
void setHeatInsulation:(GLfloat value)
void respondToAttackFrom:becauseOf:(Entity *from,[becauseOf] Entity *other)
void setPitch:(double amount)
HPVector distance_six:(GLfloat dist)
NSString * identFromShip:(ShipEntity *otherShip)
GLfloat forward_weapon_temp
void removeDefenseTarget:(Entity *target)
void setAITo:(NSString *aiString)
void noteTargetDestroyed:(ShipEntity *target)
void setWeaponRange:(GLfloat value)
void adjustVelocity:(Vector xVel)
void setWeaponRechargeRate:(float value)
void setEntityPersonalityInt:(uint16_t value)
void setReportAIMessages:(BOOL yn)
void updateEscortFormation()
void setSubIdx:(NSUInteger value)
OOCargoQuantity commodityAmount()
void overrideScriptInfo:(NSDictionary *override)
void update:(OOTimeDelta delta_t)
void setTemperature:(GLfloat value)
void setIsBoulder:(BOOL flag)
void setCrew:(NSArray *crewArray)
void setCommodityForPod:andAmount:(OOCommodityType co_type,[andAmount] OOCargoQuantity co_amount)
void markAsOffender:withReason:(int offence_value,[withReason] OOLegalStatusReason reason)
void setBehaviour:(OOBehaviour cond)
void doScriptEvent:withArgument:(jsid message,[withArgument] id argument)
void getTractoredBy:(ShipEntity *other)
void setIsMissileFlag:(BOOL newValue)
void scoopUp:(ShipEntity *other)
void switchAITo:(NSString *aiString)
Triangle absoluteIJKForSubentity()
void reactToAIMessage:context:(NSString *message,[context] NSString *debugContext)
void setFoundTarget:(Entity *targetEntity)
void setCommodity:andAmount:(OOCommodityType co_type,[andAmount] OOCargoQuantity co_amount)
OOWeaponType forward_weapon_type
NSMutableArray * subEntities
OOCommodityType commodityType()
void setOwner:(Entity *who_owns_entity)
BoundingBox findSubentityBoundingBox()
BoundingBox findBoundingBoxRelativeToPosition:InVectors:i:j:(HPVector opv,[InVectors] Vector,[i] Vector,[j] Vector k)
void setReference:(Vector v)
static float SurfaceDistanceSqaredV(HPVector reference, Entity< OOStellarBody > *stellar)
void doScriptEvent:withArgument:andArgument:(jsid message,[withArgument] id argument1,[andArgument] id argument2)
void launchShip:(ShipEntity *ship)
Vector portUpVectorForShip:(ShipEntity *ship)
void launchShipWithRole:(NSString *role)
void noteDockedShip:(ShipEntity *ship)
BOOL suckInShip:(ShipEntity *ship)
void setMisjumpWithRange:(GLfloat range)
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
void seed_RNG_only_for_planet_description(Random_Seed s_seed)