8b456c000b
git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@1 127b21dd-08f5-0310-b4b7-95ae10353056
1644 lines
50 KiB
Objective-C
1644 lines
50 KiB
Objective-C
//
|
|
// StationEntity.m
|
|
/*
|
|
*
|
|
* Oolite
|
|
*
|
|
* Created by Giles Williams on Sat Apr 03 2004.
|
|
* Copyright (c) 2004 for aegidian.org. All rights reserved.
|
|
*
|
|
|
|
Copyright (c) 2004, Giles C Williams
|
|
All rights reserved.
|
|
|
|
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License.
|
|
To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.0/
|
|
or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
|
|
|
|
You are free:
|
|
|
|
• to copy, distribute, display, and perform the work
|
|
• to make derivative works
|
|
|
|
Under the following conditions:
|
|
|
|
• Attribution. You must give the original author credit.
|
|
|
|
• Noncommercial. You may not use this work for commercial purposes.
|
|
|
|
• Share Alike. If you alter, transform, or build upon this work,
|
|
you may distribute the resulting work only under a license identical to this one.
|
|
|
|
For any reuse or distribution, you must make clear to others the license terms of this work.
|
|
|
|
Any of these conditions can be waived if you get permission from the copyright holder.
|
|
|
|
Your fair use and other rights are in no way affected by the above.
|
|
|
|
*/
|
|
|
|
#import "StationEntity.h"
|
|
#import "entities.h"
|
|
|
|
#import "AI.h"
|
|
|
|
|
|
@implementation StationEntity
|
|
|
|
- (void) acceptDistressMessageFrom:(ShipEntity *)other
|
|
{
|
|
if (self != [universe station])
|
|
{
|
|
//NSLog(@"DEBUG acceptDistressMessageFrom rejected from sub-station '%@'", name);
|
|
return;
|
|
}
|
|
|
|
//NSLog(@"DEBUG %@ %d responding to distress message from %@ %d", name, universal_id, [other name], [other universal_id]);
|
|
{
|
|
int police_target = [[other getPrimaryTarget] universal_id];
|
|
[(ShipEntity *)[universe entityForUniversalID:police_target] markAsOffender:8];
|
|
if (police_launched < STATION_MAX_POLICE)
|
|
{
|
|
ShipEntity *police_ship;
|
|
if (![universe entityForUniversalID:police_target])
|
|
{
|
|
[shipAI reactToMessage:@"TARGET_LOST"];
|
|
return;
|
|
}
|
|
|
|
//NSLog(@"DEBUG Launching Police Ship to intercept %@",[universe entityForUniversalID:police_target]);
|
|
|
|
police_ship = [universe getShipWithRole:@"police"]; // retain count = 1
|
|
[police_ship addTarget:[universe entityForUniversalID:police_target]];
|
|
[police_ship setScanClass: CLASS_POLICE];
|
|
|
|
//[police_ship setReportAImessages:YES]; // debug
|
|
|
|
[[police_ship getAI] setStateMachine:@"policeInterceptAI.plist"];
|
|
[self addShipToLaunchQueue:police_ship];
|
|
[police_ship release];
|
|
police_launched++;
|
|
}
|
|
no_docking_while_launching = YES;
|
|
[self abortAllDockings];
|
|
}
|
|
}
|
|
|
|
- (int) equivalent_tech_level
|
|
{
|
|
return equivalent_tech_level;
|
|
}
|
|
|
|
- (void) set_equivalent_tech_level:(int) value
|
|
{
|
|
equivalent_tech_level = value;
|
|
}
|
|
|
|
- (double) port_radius
|
|
{
|
|
return port_radius;
|
|
}
|
|
|
|
- (Vector) getPortPosition
|
|
{
|
|
Vector result = position;
|
|
result.x += port_position.x * v_right.x + port_position.y * v_up.x + port_position.z * v_forward.x;
|
|
result.y += port_position.x * v_right.y + port_position.y * v_up.y + port_position.z * v_forward.y;
|
|
result.z += port_position.x * v_right.z + port_position.y * v_up.z + port_position.z * v_forward.z;
|
|
return result;
|
|
}
|
|
|
|
- (double) equipment_price_factor
|
|
{
|
|
return equipment_price_factor;
|
|
}
|
|
|
|
|
|
- (NSMutableArray *) localMarket
|
|
{
|
|
return localMarket;
|
|
}
|
|
|
|
- (void) setLocalMarket:(NSArray *) some_market
|
|
{
|
|
if (localMarket)
|
|
[localMarket release];
|
|
localMarket = [[NSMutableArray alloc] initWithArray:some_market];
|
|
}
|
|
|
|
- (NSMutableArray *) localPassengers
|
|
{
|
|
return localPassengers;
|
|
}
|
|
|
|
- (void) setLocalPassengers:(NSArray *) some_market
|
|
{
|
|
if (localPassengers)
|
|
[localPassengers release];
|
|
localPassengers = [[NSMutableArray alloc] initWithArray:some_market];
|
|
}
|
|
|
|
- (NSMutableArray *) localContracts
|
|
{
|
|
return localContracts;
|
|
}
|
|
|
|
- (void) setLocalContracts:(NSArray *) some_market
|
|
{
|
|
if (localContracts)
|
|
[localContracts release];
|
|
localContracts = [[NSMutableArray alloc] initWithArray:some_market];
|
|
}
|
|
|
|
- (NSMutableArray *) localShipyard
|
|
{
|
|
return localShipyard;
|
|
}
|
|
|
|
- (void) setLocalShipyard:(NSArray *) some_market
|
|
{
|
|
if (localShipyard)
|
|
[localShipyard release];
|
|
localShipyard = [[NSMutableArray alloc] initWithArray:some_market];
|
|
}
|
|
|
|
- (NSMutableArray *) initialiseLocalMarketWithSeed: (Random_Seed) s_seed andRandomFactor: (int) random_factor
|
|
{
|
|
//NSLog(@"///// Initialising local market for station %@ with roles %@",self,[self roles]);
|
|
int rf = (random_factor ^ universal_id) & 0xff;
|
|
int economy = [(NSNumber *)[[universe generateSystemData:s_seed] objectForKey:KEY_ECONOMY] intValue];
|
|
if (localMarket)
|
|
[localMarket release];
|
|
localMarket = [[NSMutableArray alloc] initWithArray:[universe commodityDataForEconomy:economy andStation:self andRandomFactor:rf]];
|
|
return localMarket;
|
|
}
|
|
|
|
- (NSMutableArray *) initialiseLocalPassengersWithSeed: (Random_Seed) s_seed andRandomFactor: (int) random_factor
|
|
{
|
|
//NSLog(@"///// Initialising local market for station %@ with roles %@",self,[self roles]);
|
|
if (localPassengers)
|
|
[localPassengers release];
|
|
localPassengers = [[NSMutableArray alloc] initWithArray:[universe passengersForSystem:s_seed atTime:[[(PlayerEntity*)[universe entityZero] clock_number] intValue]]];
|
|
return localPassengers;
|
|
}
|
|
|
|
- (NSMutableArray *) initialiseLocalContractsWithSeed: (Random_Seed) s_seed andRandomFactor: (int) random_factor
|
|
{
|
|
//NSLog(@"///// Initialising local market for station %@ with roles %@",self,[self roles]);
|
|
if (localContracts)
|
|
[localContracts release];
|
|
localContracts = [[NSMutableArray alloc] initWithArray:[universe contractsForSystem:s_seed atTime:[[(PlayerEntity*)[universe entityZero] clock_number] intValue]]];
|
|
return localContracts;
|
|
}
|
|
|
|
- (void) setUniverse:(Universe *)univ
|
|
{
|
|
if (univ)
|
|
{
|
|
if (universe) [universe release];
|
|
universe = [univ retain];
|
|
}
|
|
else
|
|
{
|
|
if (universe) [universe release];
|
|
universe = nil;
|
|
[localMarket release];
|
|
localMarket = nil;
|
|
}
|
|
//
|
|
// if we have a universal id then we can proceed to set up any
|
|
// stuff that happens when we get added to the universe
|
|
//
|
|
if (universal_id != NO_TARGET)
|
|
{
|
|
// set up escorts
|
|
//
|
|
if (status == STATUS_IN_FLIGHT) // just popped into existence
|
|
{
|
|
if ((!escortsAreSetUp)&&(n_escorts > 0))
|
|
[self setUpEscorts];
|
|
}
|
|
else
|
|
{
|
|
escortsAreSetUp = YES; // we don't do this ourself!
|
|
}
|
|
}
|
|
|
|
//
|
|
// set subentities universe
|
|
//
|
|
if (sub_entities != nil)
|
|
{
|
|
int i;
|
|
for (i = 0; i < [sub_entities count]; i++)
|
|
{
|
|
[(Entity *)[sub_entities objectAtIndex:i] setUniverse:univ];
|
|
[(Entity *)[sub_entities objectAtIndex:i] setOwner:self];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void) setPlanet:(PlanetEntity *)planet_entity
|
|
{
|
|
if (planet_entity)
|
|
planet = [planet_entity universal_id];
|
|
else
|
|
planet = NO_TARGET;
|
|
}
|
|
|
|
- (PlanetEntity *) planet
|
|
{
|
|
return (PlanetEntity *)[universe entityForUniversalID:planet];
|
|
}
|
|
|
|
- (void) sanityCheckShipsOnApproach
|
|
{
|
|
int i;
|
|
NSArray* ships = [shipsOnApproach allKeys];
|
|
for (i = 0; i < [ships count]; i++)
|
|
{
|
|
int sid = [(NSString *)[ships objectAtIndex:i] intValue];
|
|
if ((sid == NO_TARGET)||(![universe entityForUniversalID:sid]))
|
|
{
|
|
[shipsOnApproach removeObjectForKey:[ships objectAtIndex:i]];
|
|
if ([shipsOnApproach count] == 0)
|
|
[shipAI message:@"DOCKING_COMPLETE"];
|
|
}
|
|
}
|
|
if ([shipsOnApproach count] == 0)
|
|
{
|
|
last_launch_time = [universe getTime];
|
|
approach_spacing = 0.0;
|
|
}
|
|
}
|
|
|
|
- (void) abortAllDockings
|
|
{
|
|
int i;
|
|
NSArray* ships = [shipsOnApproach allKeys];
|
|
for (i = 0; i < [ships count]; i++)
|
|
{
|
|
int sid = [(NSString *)[ships objectAtIndex:i] intValue];
|
|
if ([universe entityForUniversalID:sid])
|
|
[[(ShipEntity *)[universe entityForUniversalID:sid] getAI] message:@"DOCKING_ABORTED"];
|
|
[shipsOnApproach removeObjectForKey:[ships objectAtIndex:i]];
|
|
}
|
|
[shipAI message:@"DOCKING_COMPLETE"];
|
|
last_launch_time = [universe getTime];
|
|
approach_spacing = 0.0;
|
|
}
|
|
|
|
|
|
- (void) autoDockShipsOnApproach
|
|
{
|
|
int i;
|
|
NSArray* ships = [shipsOnApproach allKeys];
|
|
for (i = 0; i < [ships count]; i++)
|
|
{
|
|
int sid = [(NSString *)[ships objectAtIndex:i] intValue];
|
|
if ([universe entityForUniversalID:sid])
|
|
[(ShipEntity *)[universe entityForUniversalID:sid] enterDock:self];
|
|
[shipsOnApproach removeObjectForKey:[ships objectAtIndex:i]];
|
|
}
|
|
[shipAI message:@"DOCKING_COMPLETE"];
|
|
}
|
|
|
|
|
|
- (Vector) nextDockingCoordinatesForShip:(ShipEntity *) ship
|
|
{
|
|
Vector coords;
|
|
int ship_id = [ship universal_id];
|
|
NSString* shipID = [NSString stringWithFormat:@"%d", ship_id];
|
|
|
|
if (([ship isKindOfClass:[PlayerEntity class]])&&([ship legal_status] > 50))
|
|
{
|
|
[[ship getAI] message:@"DOCKING_REFUSED"];
|
|
[self sendExpandedMessage:@"[station-docking-refused-to-fugitive]" toShip:ship];
|
|
return [ship getPosition]; // hold position
|
|
}
|
|
|
|
|
|
if (![shipsOnApproach objectForKey:shipID])
|
|
{
|
|
// NSLog(@"DEBUG %@ %d noting docking request", name, universal_id);
|
|
[shipAI message:@"DOCKING_REQUESTED"]; // note the request.
|
|
}
|
|
|
|
if (no_docking_while_launching)
|
|
{
|
|
// NSLog(@"DEBUG %@ %d refusing because of no docking while launching", name, universal_id);
|
|
[[ship getAI] message:@"TRY_AGAIN_LATER"];
|
|
return [ship getPosition]; // hold position
|
|
}
|
|
|
|
if ((magnitude2(velocity) > 1.0)||
|
|
((scan_class != CLASS_STATION)&&((fabs(flight_pitch) > 0.01)||(fabs(flight_roll) > 0.01)))) // no docking while moving
|
|
{
|
|
// NSLog(@"DEBUG %@ %d refusing docking to %@ because of motion", name, universal_id, [ship name]);
|
|
[shipAI message:@"DOCKING_REQUESTED"]; // note the request.
|
|
[[ship getAI] message:@"HOLD_POSITION"];// send HOLD
|
|
return [ship getPosition]; // hold position
|
|
}
|
|
|
|
if (![shipsOnApproach objectForKey:shipID])
|
|
{
|
|
Vector v_off;
|
|
// will select a direction for offset based on the shipID
|
|
//
|
|
int offset_id = ship_id & 0xf; // 16 point compass
|
|
double c = cos(offset_id * PI * ONE_EIGHTH);
|
|
double s = sin(offset_id * PI * ONE_EIGHTH);
|
|
v_off.x = c * v_up.x + s * v_right.x;
|
|
v_off.y = c * v_up.y + s * v_right.y;
|
|
v_off.z = c * v_up.z + s * v_right.z;
|
|
//
|
|
NSMutableArray* coordinatesStack = [NSMutableArray arrayWithCapacity:3];
|
|
NSString* speedMessage = @"SLOW";
|
|
double offset = 6 * port_radius;
|
|
double offset2 = 6 * port_radius + approach_spacing;
|
|
approach_spacing += 500; // space out incoming ships by 500m
|
|
//
|
|
int docking_stage = 0;
|
|
//
|
|
while (offset >= 0.0)
|
|
{
|
|
//
|
|
NSMutableDictionary* nextCoords = [NSMutableDictionary dictionaryWithCapacity:3];
|
|
|
|
Vector rel_coords = make_vector( s * offset2, c * offset2, offset - port_radius);
|
|
|
|
Vector coords = [self getPortPosition]; // docking slit exit position
|
|
|
|
coords.x -= port_radius*v_forward.x; // correct back to 'center'
|
|
coords.y -= port_radius*v_forward.y; //
|
|
coords.z -= port_radius*v_forward.z; //
|
|
coords.x += offset*v_forward.x;
|
|
coords.y += offset*v_forward.y;
|
|
coords.z += offset*v_forward.z;
|
|
if (offset2 > 0)
|
|
{
|
|
Vector c0 = coords;
|
|
Vector c1 = coords;
|
|
c0.x += offset2*v_off.x;
|
|
c0.y += offset2*v_off.y;
|
|
c0.z += offset2*v_off.z;
|
|
c1.x -= offset2*v_off.x;
|
|
c1.y -= offset2*v_off.y;
|
|
c1.z -= offset2*v_off.z;
|
|
if (distance2(c0,[ship getPosition]) < distance2(c1,[ship getPosition]))
|
|
{
|
|
coords = c0;
|
|
}
|
|
else
|
|
{
|
|
coords = c1;
|
|
rel_coords = make_vector( -s * offset2, -c * offset2, offset - port_radius);
|
|
}
|
|
offset2 = 0; // only the first one is offset to one side
|
|
}
|
|
// //NSLog(@"docking coordinates [%d] = (%.2f, %.2f, %.2f)", 3-i, coords.x, coords.y, coords.z);
|
|
[nextCoords setObject:[NSNumber numberWithInt:docking_stage++] forKey:@"docking_stage"];
|
|
// //
|
|
[nextCoords setObject:[NSNumber numberWithFloat:rel_coords.x] forKey:@"rx"];
|
|
[nextCoords setObject:[NSNumber numberWithFloat:rel_coords.y] forKey:@"ry"];
|
|
[nextCoords setObject:[NSNumber numberWithFloat:rel_coords.z] forKey:@"rz"];
|
|
|
|
[nextCoords setObject:[NSString stringWithFormat:@"%@",speedMessage] forKey:@"speed"];
|
|
[coordinatesStack addObject:nextCoords];
|
|
offset -= port_radius;
|
|
if (offset <= 3 * port_radius)
|
|
speedMessage = @"DEAD_SLOW";
|
|
}
|
|
|
|
[shipsOnApproach setObject:coordinatesStack forKey:shipID];
|
|
|
|
// COMM-CHATTER
|
|
if (self == [universe station])
|
|
[self sendExpandedMessage: @"[station-welcome]" toShip: ship];
|
|
else
|
|
[self sendExpandedMessage: @"[docking-welcome]" toShip: ship];
|
|
|
|
}
|
|
|
|
//
|
|
// shipsOnApproach now has an entry for the ship.
|
|
//
|
|
if ([shipsOnApproach objectForKey:shipID])
|
|
{
|
|
NSMutableArray* coordinatesStack = (NSMutableArray *)[shipsOnApproach objectForKey:shipID];
|
|
// NSLog(@"DEBUG coordinatesStack = %@", [coordinatesStack description]);
|
|
|
|
if ([coordinatesStack count] == 0)
|
|
{
|
|
[[ship getAI] message:@"HOLD_POSITION"]; // not docked - try again
|
|
return [ship getPosition];
|
|
}
|
|
|
|
NSMutableDictionary* nextCoords = (NSMutableDictionary *)[coordinatesStack objectAtIndex:0];
|
|
int docking_stage = [(NSNumber *)[nextCoords objectForKey:@"docking_stage"] intValue];
|
|
NSString* speedMessage = (NSString *)[nextCoords objectForKey:@"speed"];
|
|
|
|
Vector rel_coords;
|
|
rel_coords.x = [(NSNumber *)[nextCoords objectForKey:@"rx"] floatValue];
|
|
rel_coords.y = [(NSNumber *)[nextCoords objectForKey:@"ry"] floatValue];
|
|
rel_coords.z = [(NSNumber *)[nextCoords objectForKey:@"rz"] floatValue];
|
|
|
|
// NSLog(@"DEBUG New system coordinates [%.3f, %.3f, %.3f]", rel_coords.x, rel_coords.y, rel_coords.z);
|
|
Vector vi = v_right;
|
|
Vector vj = v_up;
|
|
Vector vk = v_forward;
|
|
if (scan_class == CLASS_STATION)
|
|
{
|
|
Vector v0 = [[universe sun] getPosition];
|
|
vi = cross_product(vk,v0);
|
|
vj = cross_product(vk,vi);
|
|
}
|
|
coords = [self getPortPosition];
|
|
coords.x += rel_coords.x * vi.x + rel_coords.y * vj.x + rel_coords.z * vk.x;
|
|
coords.y += rel_coords.x * vi.y + rel_coords.y * vj.y + rel_coords.z * vk.y;
|
|
coords.z += rel_coords.x * vi.z + rel_coords.y * vj.z + rel_coords.z * vk.z;
|
|
|
|
double allowed_range = 100.0 + [ship collisionRadius];
|
|
|
|
Vector ship_position = [ship getPosition];
|
|
Vector delta = coords;
|
|
delta.x -= ship_position.x; delta.y -= ship_position.y; delta.z -= ship_position.z;
|
|
|
|
if (magnitude2(delta) > allowed_range * allowed_range) // further than 100m from the coordinates - do not remove them from the stack!
|
|
{
|
|
// NSLog(@"DEBUG ::::: %@ %d is %.1fm from its docking coordinates for docking stage %d.", [ship name], ship_id, sqrt(magnitude2(delta)), docking_stage);
|
|
// NSLog(@"DEBUG ::::: %@ %d Continue to given coordinates...", [ship name], ship_id);
|
|
|
|
// // debug
|
|
// [ship setReportAImessages:YES];
|
|
|
|
|
|
// NSLog(@">>:::: %@ %d docking stage %d", [ship name], [ship universal_id], docking_stage);
|
|
|
|
if (docking_stage == 0)
|
|
{
|
|
// // COMM-CHATTER
|
|
// if (self == [universe station])
|
|
// [self sendExpandedMessage: @"[station-welcome]" toShip: ship];
|
|
// else
|
|
// [self sendExpandedMessage: @"[docking-welcome]" toShip: ship];
|
|
//
|
|
[[ship getAI] message:@"APPROACH_START"];
|
|
}
|
|
else
|
|
{
|
|
if ([speedMessage isEqual:@"DEAD_SLOW"])
|
|
[[ship getAI] message:@"APPROACH_STATION"];
|
|
else
|
|
[[ship getAI] message:@"APPROACH_COORDINATES"];
|
|
}
|
|
|
|
return coords;
|
|
}
|
|
else
|
|
{
|
|
// save the current coordinates
|
|
Vector oldCoords = coords;
|
|
|
|
// NSLog(@"::>>:: %@ %d docking stage %d", [ship name], [ship universal_id], docking_stage);
|
|
|
|
if (docking_stage == 1)
|
|
{
|
|
// COMM-CHATTER
|
|
if (self == [universe station])
|
|
[self sendExpandedMessage: @"[station-begin-final-aproach]" toShip: ship];
|
|
else
|
|
[self sendExpandedMessage: @"[docking-begin-final-aproach]" toShip: ship];
|
|
}
|
|
|
|
if ([coordinatesStack count] < 2)
|
|
{
|
|
// NSLog(@"DEBUG ::::: %@ %d Final docking coordinates ---> (%.2f, %.2f, %.2f)", [ship name], ship_id, coords.x, coords.y, coords.z);
|
|
[[ship getAI] message:@"APPROACH_STATION"];
|
|
return coords;
|
|
}
|
|
|
|
// get the NEXT coordinates
|
|
nextCoords = (NSMutableDictionary *)[coordinatesStack objectAtIndex:1];
|
|
docking_stage = [(NSNumber *)[nextCoords objectForKey:@"docking_stage"] intValue];
|
|
speedMessage = (NSString *)[nextCoords objectForKey:@"speed"];
|
|
|
|
// set the docking coordinates
|
|
rel_coords.x = [(NSNumber *)[nextCoords objectForKey:@"rx"] floatValue];
|
|
rel_coords.y = [(NSNumber *)[nextCoords objectForKey:@"ry"] floatValue];
|
|
rel_coords.z = [(NSNumber *)[nextCoords objectForKey:@"rz"] floatValue];
|
|
Vector vi = v_right;
|
|
Vector vj = v_up;
|
|
Vector vk = v_forward;
|
|
if (scan_class == CLASS_STATION)
|
|
{
|
|
Vector v0 = [[universe sun] getPosition];
|
|
vi = cross_product(vk,v0);
|
|
vj = cross_product(vk,vi);
|
|
}
|
|
coords = [self getPortPosition];
|
|
coords.x += rel_coords.x * vi.x + rel_coords.y * vj.x + rel_coords.z * vk.x;
|
|
coords.y += rel_coords.x * vi.y + rel_coords.y * vj.y + rel_coords.z * vk.y;
|
|
coords.z += rel_coords.x * vi.z + rel_coords.y * vj.z + rel_coords.z * vk.z;
|
|
|
|
int i; // clear any previously owned docking stages
|
|
for (i = 1; i < MAX_DOCKING_STAGES; i++)
|
|
if ((id_lock[i] == ship_id)||([universe entityForUniversalID:id_lock[i]] == nil))
|
|
id_lock[i] = NO_TARGET;
|
|
|
|
if ((id_lock[docking_stage] == NO_TARGET)&&(id_lock[docking_stage + 1] == NO_TARGET)) // check two stages ahead
|
|
{
|
|
id_lock[docking_stage] = ship_id; // claim this docking stage
|
|
|
|
//remove the previous stage from the stack
|
|
[coordinatesStack removeObjectAtIndex:0];
|
|
|
|
// NSLog(@"DEBUG ::::: %@ %d Next docking coordinates ---> (%.2f, %.2f, %.2f)", [ship name], ship_id, coords.x, coords.y, coords.z);
|
|
|
|
//determine the approach speed advice
|
|
if ([speedMessage isEqual:@"DEAD_SLOW"])
|
|
[[ship getAI] message:@"APPROACH_STATION"];
|
|
else
|
|
[[ship getAI] message:@"APPROACH_COORDINATES"];
|
|
|
|
// show the locked approach positions
|
|
// NSLog(@"DEBUG ::::: %d :: %d :: %d :: %d :: %d :: %d :: %d :: %d :: %d :: %d <<",
|
|
// id_lock[10],id_lock[9],id_lock[8],id_lock[7],id_lock[6],id_lock[5],id_lock[4],id_lock[3],id_lock[2],id_lock[1]);
|
|
|
|
return coords;
|
|
}
|
|
else
|
|
{
|
|
// NSLog(@"DEBUG ::::: %@ %d Hold position ...", [ship name], ship_id);
|
|
[[ship getAI] message:@"HOLD_POSITION"];
|
|
|
|
if (![nextCoords objectForKey:@"hold_message_given"])
|
|
{
|
|
// COMM-CHATTER
|
|
[universe clearPreviousMessage];
|
|
[self sendExpandedMessage: @"[station-hold-position]" toShip: ship];
|
|
[nextCoords setObject:@"YES" forKey:@"hold_message_given"];
|
|
}
|
|
|
|
return oldCoords;
|
|
}
|
|
}
|
|
}
|
|
|
|
return coords;
|
|
}
|
|
|
|
- (double) approachSpeedForShip:(ShipEntity *) ship
|
|
{
|
|
int ship_id = [ship universal_id];
|
|
NSString* shipID = [NSString stringWithFormat:@"%d", ship_id];
|
|
|
|
double approach_speed = 50.0;
|
|
if ([shipsOnApproach objectForKey:shipID])
|
|
{
|
|
NSMutableArray* coordinatesStack = (NSMutableArray *)[shipsOnApproach objectForKey:shipID];
|
|
NSDictionary* nextCoords = (NSDictionary *)[coordinatesStack objectAtIndex:0];
|
|
NSString* speedMessage = (NSString *)[nextCoords objectForKey:@"speed"];
|
|
if ([coordinatesStack count] > 0)
|
|
{
|
|
int next_docking_stage = [(NSNumber *)[nextCoords objectForKey:@"docking_stage"] intValue];
|
|
|
|
if (id_lock[next_docking_stage] == NO_TARGET)
|
|
{
|
|
if ([speedMessage isEqual:@"DEAD_SLOW"])
|
|
approach_speed = 25.0;
|
|
else
|
|
approach_speed = 50.0;
|
|
}
|
|
else
|
|
approach_speed = 1.0; // the next docking stage is not clear - go slow
|
|
}
|
|
}
|
|
return approach_speed;
|
|
}
|
|
|
|
- (void) abortDockingForShip:(ShipEntity *) ship
|
|
{
|
|
int ship_id = [ship universal_id];
|
|
NSString* shipID = [NSString stringWithFormat:@"%d",ship_id];
|
|
if ([universe entityForUniversalID:[ship universal_id]])
|
|
[[(ShipEntity *)[universe entityForUniversalID:[ship universal_id]] getAI] message:@"DOCKING_ABORTED"];
|
|
if ([shipsOnApproach objectForKey:shipID])
|
|
{
|
|
[shipsOnApproach removeObjectForKey:shipID];
|
|
if ([shipsOnApproach count] == 0)
|
|
[shipAI message:@"DOCKING_COMPLETE"];
|
|
}
|
|
|
|
int i; // clear any previously owned docking stages
|
|
for (i = 1; i < MAX_DOCKING_STAGES; i++)
|
|
if ((id_lock[i] == ship_id)||([universe entityForUniversalID:id_lock[i]] == nil))
|
|
id_lock[i] = NO_TARGET;
|
|
|
|
// NSLog(@"DEBUG ::::: %@ %d Aborted docking", [ship name], ship_id);
|
|
// NSLog(@"DEBUG ::::: %d :: %d :: %d :: %d :: %d :: %d :: %d :: %d :: %d :: %d <<",
|
|
// id_lock[10],id_lock[9],id_lock[8],id_lock[7],id_lock[6],id_lock[5],id_lock[4],id_lock[3],id_lock[2],id_lock[1]);
|
|
|
|
}
|
|
|
|
- (Vector) portUpVector
|
|
{
|
|
if (scan_class == CLASS_STATION)
|
|
return v_right; // because the slot is horizontal dammit
|
|
|
|
Vector result = vector_right_from_quaternion( quaternion_multiply( port_qrotation, q_rotation));
|
|
|
|
// NSLog(@"portUpVector = [%.3f, %.3f, %.3f] v_up = [%.3f, %.3f, %.3f]",
|
|
// result.x, result.y, result.z, v_up.x, v_up.y, v_up.z);
|
|
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////// from superclass
|
|
|
|
- (id) init
|
|
{
|
|
self = [super init];
|
|
|
|
shipsOnApproach = [[NSMutableDictionary alloc] initWithCapacity:5]; // alloc retains
|
|
launchQueue = [[NSMutableArray alloc] initWithCapacity:16]; // retained
|
|
|
|
int i;
|
|
for (i = 0; i < MAX_DOCKING_STAGES; i++)
|
|
id_lock[i] = NO_TARGET;
|
|
|
|
alert_level = 0;
|
|
police_launched = 0;
|
|
last_launch_time = 0.0;
|
|
no_docking_while_launching = NO;
|
|
|
|
localMarket = nil;
|
|
|
|
// local specials
|
|
equivalent_tech_level = NSNotFound;
|
|
equipment_price_factor = 1.0;
|
|
port_radius = 500.0;
|
|
approach_spacing = 0.0;
|
|
|
|
max_scavengers = 3;
|
|
max_defense_ships = 3;
|
|
max_police = STATION_MAX_POLICE;
|
|
police_launched = 0;
|
|
scavengers_launched = 0;
|
|
|
|
docked_shuttles = ranrot_rand() % 4; // 0..3;
|
|
last_shuttle_launch_time = 0.0;
|
|
shuttle_launch_interval = 15.0 * 60.0; // every 15 minutes
|
|
last_shuttle_launch_time = - (ranrot_rand() % 60) * shuttle_launch_interval / 60.0;
|
|
|
|
docked_traders = 1 + (ranrot_rand() % 3); // 1..3;
|
|
last_trader_launch_time = 0.0;
|
|
trader_launch_interval = ((ranrot_rand() % 10) + 20.0) * 60.0; // every few minutes
|
|
last_trader_launch_time = 60.0 - trader_launch_interval; // in one minute's time
|
|
|
|
last_patrol_report_time = 0.0;
|
|
patrol_launch_interval = 300.0; // 5 minutes
|
|
last_patrol_report_time -= patrol_launch_interval;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) reinit
|
|
{
|
|
[super reinit];
|
|
|
|
if (localMarket) [localMarket release];
|
|
localMarket = nil;
|
|
|
|
if (localPassengers) [localPassengers release];
|
|
localPassengers = nil;
|
|
|
|
if (localContracts) [localContracts release];
|
|
localContracts = nil;
|
|
|
|
if (localShipyard) [localShipyard release];
|
|
localShipyard = nil;
|
|
|
|
if (shipsOnApproach) [shipsOnApproach release];
|
|
shipsOnApproach = [[NSMutableDictionary alloc] initWithCapacity:5]; // alloc retains
|
|
|
|
if (launchQueue) [launchQueue release];
|
|
launchQueue = [[NSMutableArray alloc] initWithCapacity:16]; // retained
|
|
|
|
int i;
|
|
for (i = 0; i < MAX_DOCKING_STAGES; i++)
|
|
id_lock[i] = NO_TARGET;
|
|
|
|
alert_level = 0;
|
|
police_launched = 0;
|
|
last_launch_time = 0.0;
|
|
no_docking_while_launching = NO;
|
|
}
|
|
|
|
- (id) initWithDictionary:(NSDictionary *) dict
|
|
{
|
|
port_radius = 500.0; // may be overwritten by [super initWithDictionary:dict]
|
|
|
|
self = [super initWithDictionary:dict];
|
|
|
|
//NSLog(@"DEBUG setting up station '%@' from dict:%@",name,[dict description]);
|
|
|
|
if ([dict objectForKey:@"equivalent_tech_level"])
|
|
equivalent_tech_level = [(NSNumber *)[dict objectForKey:@"equivalent_tech_level"] intValue];
|
|
else
|
|
equivalent_tech_level = NSNotFound;
|
|
|
|
if ([dict objectForKey:@"equipment_price_factor"])
|
|
equipment_price_factor = [(NSNumber *)[dict objectForKey:@"equipment_price_factor"] doubleValue];
|
|
else
|
|
equipment_price_factor = 1.0;
|
|
|
|
|
|
shipsOnApproach = [[NSMutableDictionary alloc] initWithCapacity:16]; // alloc retains
|
|
launchQueue = [[NSMutableArray alloc] initWithCapacity:16]; // retained
|
|
alert_level = 0;
|
|
police_launched = 0;
|
|
last_launch_time = 0.0;
|
|
approach_spacing = 0.0;
|
|
|
|
localMarket = nil;
|
|
|
|
docked_shuttles = ranrot_rand() % 4; // 0..3;
|
|
last_shuttle_launch_time = 0.0;
|
|
shuttle_launch_interval = 15.0 * 60.0; // every 15 minutes
|
|
last_shuttle_launch_time = - (ranrot_rand() % 60) * shuttle_launch_interval / 60.0;
|
|
|
|
docked_traders = 1 + (ranrot_rand() % 3); // 1..3;
|
|
last_trader_launch_time = 0.0;
|
|
trader_launch_interval = ((ranrot_rand() % 10) + 20.0) * 60.0; // every few minutes
|
|
last_trader_launch_time = 60.0 - trader_launch_interval; // in one minute's time
|
|
|
|
int i;
|
|
for (i = 0; i < MAX_DOCKING_STAGES; i++)
|
|
id_lock[i] = NO_TARGET;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) setUpShipFromDictionary:(NSDictionary *) dict
|
|
{
|
|
[super setUpShipFromDictionary:dict];
|
|
|
|
if ([dict objectForKey:@"port_radius"]) // this gets set for rock-hermits and other specials, otherwise it's 500m
|
|
port_radius = [(NSNumber *)[dict objectForKey:@"port_radius"] doubleValue];
|
|
else
|
|
port_radius = 500.0;
|
|
|
|
// set up a the docking port
|
|
//
|
|
port_position = make_vector( 0, 0, port_radius); // forward
|
|
quaternion_set_identity(&port_qrotation);
|
|
port_dimensions = make_vector( 69, 69, 250); // base port size (square)
|
|
if ([dict objectForKey:@"subentities"])
|
|
{
|
|
int i;
|
|
NSArray *subs = (NSArray *)[dict objectForKey:@"subentities"];
|
|
for (i = 0; i < [subs count]; i++)
|
|
{
|
|
NSArray* details = [(NSString *)[subs objectAtIndex:i] componentsSeparatedByString:@" "];
|
|
if (([details count] == 8)&&([(NSString *)[details objectAtIndex:0] isEqual:@"dock"]))
|
|
{
|
|
port_position.x = [(NSString *)[details objectAtIndex:1] floatValue];
|
|
port_position.y = [(NSString *)[details objectAtIndex:2] floatValue];
|
|
port_position.z = [(NSString *)[details objectAtIndex:3] floatValue];
|
|
port_qrotation.w = [(NSString *)[details objectAtIndex:4] floatValue];
|
|
port_qrotation.x = [(NSString *)[details objectAtIndex:5] floatValue];
|
|
port_qrotation.y = [(NSString *)[details objectAtIndex:6] floatValue];
|
|
port_qrotation.z = [(NSString *)[details objectAtIndex:7] floatValue];
|
|
port_dimensions = make_vector( 32, 96, 250); // coriolis/icos/dodec port size (oblong)
|
|
}
|
|
quaternion_normalise(&port_qrotation);
|
|
}
|
|
}
|
|
|
|
if ([dict objectForKey:@"port_dimensions"]) // this can be set for rock-hermits and other specials
|
|
{
|
|
NSArray* tokens = [(NSString*)[dict objectForKey:@"port_dimensions"] componentsSeparatedByString:@"x"];
|
|
if ([tokens count] == 3)
|
|
{
|
|
port_dimensions = make_vector( [(NSString*)[tokens objectAtIndex:0] floatValue],
|
|
[(NSString*)[tokens objectAtIndex:1] floatValue],
|
|
[(NSString*)[tokens objectAtIndex:2] floatValue]);
|
|
}
|
|
}
|
|
|
|
if ([dict objectForKey:@"equivalent_tech_level"])
|
|
equivalent_tech_level = [(NSNumber *)[dict objectForKey:@"equivalent_tech_level"] intValue];
|
|
else
|
|
equivalent_tech_level = NSNotFound;
|
|
|
|
if ([dict objectForKey:@"max_scavengers"])
|
|
max_scavengers = [(NSNumber *)[dict objectForKey:@"max_scavengers"] intValue];
|
|
else
|
|
max_scavengers = 3;
|
|
|
|
if ([dict objectForKey:@"max_defense_ships"])
|
|
max_defense_ships = [(NSNumber *)[dict objectForKey:@"max_defense_ships"] intValue];
|
|
else
|
|
max_defense_ships = 3;
|
|
|
|
if ([dict objectForKey:@"max_police"])
|
|
max_police = [(NSNumber *)[dict objectForKey:@"max_police"] intValue];
|
|
else
|
|
max_police = STATION_MAX_POLICE;
|
|
|
|
if ([dict objectForKey:@"equipment_price_factor"])
|
|
equipment_price_factor = [(NSNumber *)[dict objectForKey:@"equipment_price_factor"] doubleValue];
|
|
else
|
|
equipment_price_factor = 1.0;
|
|
|
|
police_launched = 0;
|
|
scavengers_launched = 0;
|
|
approach_spacing = 0.0;
|
|
[shipsOnApproach removeAllObjects];
|
|
[launchQueue removeAllObjects];
|
|
last_launch_time = 0.0;
|
|
|
|
int i;
|
|
for (i = 0; i < MAX_DOCKING_STAGES; i++)
|
|
id_lock[i] = NO_TARGET;
|
|
|
|
//localMarket = nil;
|
|
|
|
if (([roles isEqual:@"coriolis"])||([roles isEqual:@"dodecahedron"])||([roles isEqual:@"icosahedron"]))
|
|
{
|
|
docked_shuttles = ranrot_rand() % 4; // 0..3;
|
|
last_shuttle_launch_time = 0.0;
|
|
shuttle_launch_interval = 15.0 * 60.0; // every 15 minutes
|
|
last_shuttle_launch_time = - (ranrot_rand() % 60) * shuttle_launch_interval / 60.0;
|
|
docked_traders = 1 + (ranrot_rand() % 3); // 1..3;
|
|
last_trader_launch_time = 0.0;
|
|
trader_launch_interval = ((ranrot_rand() % 10) + 20.0) * 60.0; // every few minutes
|
|
last_trader_launch_time = 60.0 - trader_launch_interval; // in one minute's time
|
|
}
|
|
else
|
|
{
|
|
docked_shuttles = 0;
|
|
docked_traders = 0; // 1..3;
|
|
}
|
|
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
if (shipsOnApproach) [shipsOnApproach release];
|
|
if (launchQueue) [launchQueue release];
|
|
|
|
if (localMarket) [localMarket release];
|
|
if (localPassengers) [localPassengers release];
|
|
if (localContracts) [localContracts release];
|
|
if (localShipyard) [localShipyard release];
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
- (BOOL) checkCloseCollisionWith:(Entity *)other
|
|
{
|
|
// BOOL standard_port = ![roles isEqual:@"rockhermit"];
|
|
//
|
|
// check if other is within docking corridor
|
|
//
|
|
//NSLog(@"Checking Station CloseContact...");
|
|
//
|
|
if ([universe strict]&&(self != [universe station])&&[other isKindOfClass:[PlayerEntity class]])
|
|
{
|
|
// in a strict universe the player can only dock with the main station
|
|
return [super checkCloseCollisionWith:other];
|
|
}
|
|
//
|
|
if ([other isKindOfClass:[ShipEntity class]])
|
|
{
|
|
Vector rel_pos, delta, prt_pos;
|
|
// // port dimensions..
|
|
// double ww = (standard_port)? 32: 69;
|
|
// double hh = (standard_port)? 96: 69;
|
|
// double dd = (standard_port)? 250: 250;
|
|
// // reduced dimensions for fudging..
|
|
// double w1 = (standard_port)? 24: 51;
|
|
// double h1 = (standard_port)? 72: 51;
|
|
// port dimensions..
|
|
double ww = port_dimensions.x;
|
|
double hh = port_dimensions.y;
|
|
double dd = port_dimensions.z;
|
|
// reduced dimensions for fudging..
|
|
double w1 = ww * 0.75;
|
|
double h1 = hh * 0.75;
|
|
ShipEntity* ship = (ShipEntity *) other;
|
|
double radius = [ship collisionRadius];
|
|
|
|
// check if the ship is too big for the port and fudge things accordingly
|
|
BoundingBox shipbb = [ship getBoundingBox];
|
|
float ff = 1.0;
|
|
while ((shipbb.max_y * ff > w1)||(shipbb.min_y * ff < -w1)
|
|
||(shipbb.max_x * ff > h1)||(shipbb.min_x * ff < -h1))
|
|
ff /= 1.25;
|
|
|
|
//NSLog(@"DEBUG Checking docking corridor...");
|
|
prt_pos = [self getPortPosition];
|
|
rel_pos = [ship getPosition];
|
|
rel_pos.x -= prt_pos.x;
|
|
rel_pos.y -= prt_pos.y;
|
|
rel_pos.z -= prt_pos.z;
|
|
delta.x = dot_product(rel_pos, v_right);
|
|
delta.y = dot_product(rel_pos, v_up);
|
|
delta.z = dot_product(rel_pos, v_forward);
|
|
BOOL in_lane = YES;
|
|
|
|
if ((delta.x > boundingBox.max_x + radius)||(delta.x < boundingBox.min_x - radius))
|
|
in_lane = NO;
|
|
if ((delta.y > boundingBox.max_y + radius)||(delta.y < boundingBox.min_y - radius))
|
|
in_lane = NO;
|
|
if ((delta.z > boundingBox.max_z + radius)||(delta.z < boundingBox.min_z - radius))
|
|
in_lane = NO;
|
|
|
|
if (!in_lane)
|
|
return [super checkCloseCollisionWith:other];
|
|
//
|
|
// within bounding box at this point
|
|
//
|
|
// get bounding box relative to the port in this station's orientation
|
|
Quaternion q0 = quaternion_multiply(port_qrotation, q_rotation);
|
|
Vector vi = vector_right_from_quaternion(q0);
|
|
Vector vj = vector_up_from_quaternion(q0);
|
|
Vector vk = vector_forward_from_quaternion(q0);
|
|
BoundingBox arbb = [other findBoundingBoxRelativeToPosition:prt_pos InVectors: vi: vj: vk];
|
|
//
|
|
// apply fudge factor
|
|
arbb.max_x *= ff;
|
|
arbb.max_y *= ff;
|
|
arbb.min_x *= ff;
|
|
arbb.min_y *= ff;
|
|
//
|
|
// if (other == [universe entityZero])
|
|
// NSLog(@"DEBUG Docking corridor placement (%3.2f,%3.2f,%3.2f) <[%3.1f,%3.1f,%3.1f]-[%3.1f,%3.1f,%3.1f] (/%1.3f)>",
|
|
// delta.x, delta.y, delta.z,
|
|
// arbb.max_x, arbb.max_y, arbb.max_z,
|
|
// arbb.min_x, arbb.min_y, arbb.min_z,
|
|
// 1.0 / ff);
|
|
if ( (arbb.min_x > -ww)&& (arbb.max_x < ww) // docking slit is 90 degrees to vertical
|
|
&& (arbb.min_y > -hh)&& (arbb.max_y < hh) // docking slit is 90 degrees to vertical
|
|
&& (arbb.min_z > -dd))
|
|
{
|
|
if (arbb.max_z < 0)
|
|
[ship enterDock:self];
|
|
return NO;
|
|
}
|
|
else
|
|
{
|
|
// NSLog(@"DEBUG Outside the lane!");
|
|
if ( (arbb.min_x > -1.5 * ww)&& (arbb.max_x < 1.5 * ww)
|
|
&& (arbb.min_y > -1.5 * hh)&& (arbb.max_y < 1.5 * hh)
|
|
&& (arbb.min_z > -dd)&& (arbb.min_z < 0)) // inside the station
|
|
{
|
|
// damage the ship according to velocity but don't collide
|
|
[ship takeScrapeDamage: 5 * [universe getTimeDelta]*[ship flight_speed] from:self];
|
|
|
|
// adjust the ship back to the center of the port
|
|
if ((arbb.max_x < 32)&&(arbb.min_x > - 32))
|
|
delta.x = 0;
|
|
if ((arbb.max_y > 96)&&(arbb.min_x > - 96))
|
|
delta.y = 0;
|
|
Vector pos = [ship getPosition];
|
|
pos.x -= delta.y * v_up.x + delta.x * v_right.x;
|
|
pos.y -= delta.y * v_up.y + delta.x * v_right.y;
|
|
pos.z -= delta.y * v_up.z + delta.x * v_right.z;
|
|
[ship setPosition:pos];
|
|
|
|
// // give it some roll
|
|
// if ((flight_roll > 0.0)&&([ship flight_roll] < flight_roll))
|
|
// {
|
|
// double roll_adjust = 5 * [universe getTimeDelta] * (flight_roll - [ship flight_roll]);
|
|
// [ship setRoll:[ship flight_roll] + roll_adjust];
|
|
// }
|
|
|
|
// if far enough in - dock
|
|
if (arbb.max_z < 0)
|
|
[ship enterDock:self];
|
|
return NO;
|
|
}
|
|
}
|
|
// perform bounding box versus bounding box check
|
|
return [super checkCloseCollisionWith:other];
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
- (void) update:(double) delta_t
|
|
{
|
|
BOOL isRockHermit = (scan_class == CLASS_ROCK);
|
|
BOOL isMainStation = (self == [universe station]);
|
|
|
|
double unitime = [universe getTime];
|
|
|
|
// if (sub_entities)
|
|
// NSLog(@"DEBUG %@ sub_entities %@", [self name], [sub_entities description]);
|
|
|
|
[super update:delta_t];
|
|
|
|
if (([launchQueue count] > 0)&&([shipsOnApproach count] == 0)&&(unitime > last_launch_time + STATION_DELAY_BETWEEN_LAUNCHES))
|
|
{
|
|
[self launchShip:(ShipEntity *)[launchQueue objectAtIndex:0]];
|
|
[launchQueue removeObjectAtIndex:0];
|
|
}
|
|
if (([launchQueue count] == 0)&&(no_docking_while_launching))
|
|
no_docking_while_launching = NO; // launching complete
|
|
if (approach_spacing > 0.0)
|
|
{
|
|
approach_spacing -= delta_t * 10.0; // reduce by 10 m/s
|
|
if (approach_spacing < 0.0) approach_spacing = 0.0;
|
|
}
|
|
if ((docked_shuttles > 0)&&(!isRockHermit))
|
|
{
|
|
if (unitime > last_shuttle_launch_time + shuttle_launch_interval)
|
|
{
|
|
[self launchShuttle];
|
|
docked_shuttles--;
|
|
last_shuttle_launch_time = unitime;
|
|
}
|
|
}
|
|
if ((docked_traders > 0)&&(!isRockHermit))
|
|
{
|
|
if (unitime > last_trader_launch_time + trader_launch_interval)
|
|
{
|
|
//NSLog(@"O ---> %d docked traders at %.1f (%.1f and every %.1f)", docked_traders, [universe getTime], last_trader_launch_time + trader_launch_interval, trader_launch_interval);
|
|
[self launchTrader];
|
|
docked_traders--;
|
|
last_trader_launch_time = unitime;
|
|
}
|
|
}
|
|
|
|
// testing patrols
|
|
if ((unitime > last_patrol_report_time + patrol_launch_interval)&&(isMainStation))
|
|
{
|
|
// NSLog(@"%@ %d (%@) launching a patrol...", name, universal_id, roles);
|
|
if (![self launchPatrol])
|
|
last_patrol_report_time = unitime;
|
|
}
|
|
|
|
}
|
|
|
|
- (void) clear
|
|
{
|
|
if (launchQueue)
|
|
[launchQueue removeAllObjects];
|
|
if (shipsOnApproach)
|
|
[shipsOnApproach removeAllObjects];
|
|
}
|
|
|
|
- (void) addShipToLaunchQueue:(ShipEntity *) ship
|
|
{
|
|
[self sanityCheckShipsOnApproach];
|
|
if (!launchQueue)
|
|
launchQueue = [[NSMutableArray alloc] initWithCapacity:16]; // retained
|
|
if (ship)
|
|
[launchQueue addObject:ship];
|
|
}
|
|
|
|
- (int) countShipsInLaunchQueueWithRole:(NSString *) a_role
|
|
{
|
|
if ([launchQueue count] == 0)
|
|
return 0;
|
|
int i;
|
|
int result = 0;
|
|
for (i = 0; i < [launchQueue count]; i++)
|
|
{
|
|
if ([[(ShipEntity *)[launchQueue objectAtIndex:i] roles] isEqual:a_role])
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
- (void) launchShip:(ShipEntity *) ship
|
|
{
|
|
Vector launchPos = position;
|
|
Vector launchVel = velocity;
|
|
double launchSpeed = 0.5 * [ship max_flight_speed];
|
|
if ((max_flight_speed > 0)&&(flight_speed > 0))
|
|
launchSpeed = 0.5 * [ship max_flight_speed] * (1.0 + flight_speed/max_flight_speed);
|
|
Quaternion q1 = q_rotation;
|
|
q1 = quaternion_multiply(port_qrotation, q1);
|
|
quaternion_rotate_about_axis(&q1, vector_forward_from_quaternion(q1),PI*0.5); // to account for the slot being at 90 degrees to vertical
|
|
// launch position
|
|
launchPos.x += port_position.x * v_right.x + port_position.y * v_up.x + port_position.z * v_forward.x;
|
|
launchPos.y += port_position.x * v_right.y + port_position.y * v_up.y + port_position.z * v_forward.y;
|
|
launchPos.z += port_position.x * v_right.z + port_position.y * v_up.z + port_position.z * v_forward.z;
|
|
[ship setPosition:launchPos];
|
|
// launch speed
|
|
Vector launchVector = vector_forward_from_quaternion(q1);
|
|
launchVel.x += launchSpeed * launchVector.x; launchVel.y += launchSpeed * launchVector.y; launchVel.z += launchSpeed * launchVector.z;
|
|
[ship setSpeed:sqrt(magnitude2(launchVel))];
|
|
[ship setVelocity:launchVel];
|
|
// orientation
|
|
[ship setQRotation:q1];
|
|
[ship setRoll:flight_roll];
|
|
[ship setPitch:0.0];
|
|
[ship setStatus:STATUS_LAUNCHING];
|
|
[[ship getAI] reactToMessage:@"pauseAI: 2.0"]; // pause while launching
|
|
[universe addEntity:ship];
|
|
last_launch_time = [universe getTime];
|
|
}
|
|
|
|
- (void) noteDockedShip:(ShipEntity *) ship
|
|
{
|
|
// set last launch time to avoid clashes with outgoing ships
|
|
last_launch_time = [universe getTime];
|
|
if ([[ship roles] isEqual:@"shuttle"])
|
|
docked_shuttles++;
|
|
if ([[ship roles] isEqual:@"trader"])
|
|
docked_traders++;
|
|
if ([[ship roles] isEqual:@"police"])
|
|
police_launched--;
|
|
if ([[ship roles] isEqual:@"hermit-ship"])
|
|
police_launched--;
|
|
if ([[ship roles] isEqual:@"defense_ship"])
|
|
police_launched--;
|
|
if ([[ship roles] isEqual:@"scavenger"]||[[ship roles] isEqual:@"miner"]) // treat miners and scavengers alike!
|
|
scavengers_launched--;
|
|
|
|
int ship_id = [ship universal_id];
|
|
NSString* shipID = [NSString stringWithFormat:@"%d", ship_id];
|
|
[shipsOnApproach removeObjectForKey:shipID];
|
|
if ([shipsOnApproach count] == 0)
|
|
[shipAI message:@"DOCKING_COMPLETE"];
|
|
|
|
int i; // clear any previously owned docking stages
|
|
for (i = 0; i < MAX_DOCKING_STAGES; i++)
|
|
if ((id_lock[i] == ship_id)||([universe entityForUniversalID:id_lock[i]] == nil))
|
|
id_lock[i] = NO_TARGET;
|
|
|
|
if (ship == [universe entityZero]) // ie. the player
|
|
{
|
|
//scripting
|
|
if ([script_actions count])
|
|
{
|
|
int i;
|
|
[(PlayerEntity *)ship setScript_target:self];
|
|
for (i = 0; i < [script_actions count]; i++)
|
|
{
|
|
NSObject* action = [script_actions objectAtIndex:i];
|
|
if ([action isKindOfClass:[NSDictionary class]])
|
|
[(PlayerEntity *)ship checkCouplet:(NSDictionary *)action onEntity:ship];
|
|
if ([action isKindOfClass:[NSString class]])
|
|
[(PlayerEntity *)ship scriptAction:(NSString *)action onEntity:ship];
|
|
}
|
|
}
|
|
}
|
|
|
|
// NSLog(@"DEBUG ::::: %d :: %d :: %d :: %d :: %d :: %d :: %d :: %d :: %d :: %d <<",
|
|
// id_lock[10],id_lock[9],id_lock[8],id_lock[7],id_lock[6],id_lock[5],id_lock[4],id_lock[3],id_lock[2],id_lock[1]);
|
|
|
|
}
|
|
|
|
|
|
- (BOOL) collideWithShip:(ShipEntity *)other
|
|
{
|
|
[self abortAllDockings];
|
|
return [super collideWithShip:other];
|
|
}
|
|
|
|
- (BOOL) hasHostileTarget
|
|
{
|
|
if (primaryTarget == NO_TARGET)
|
|
return NO;
|
|
if ((condition == CONDITION_AVOID_COLLISION)&&(previousCondition))
|
|
{
|
|
int old_condition = [(NSNumber*)[previousCondition objectForKey:@"condition"] intValue];
|
|
return IS_CONDITION_HOSTILE(old_condition);
|
|
}
|
|
return IS_CONDITION_HOSTILE(condition)||(alert_level == STATION_ALERT_LEVEL_YELLOW)||(alert_level == STATION_ALERT_LEVEL_RED);
|
|
}
|
|
|
|
//////////////////////////////////////////////// extra AI routines
|
|
|
|
|
|
- (void) increaseAlertLevel
|
|
{
|
|
switch (alert_level)
|
|
{
|
|
case STATION_ALERT_LEVEL_GREEN :
|
|
alert_level = STATION_ALERT_LEVEL_YELLOW;
|
|
[shipAI reactToMessage:@"YELLOW_ALERT"];
|
|
break;
|
|
case STATION_ALERT_LEVEL_YELLOW :
|
|
alert_level = STATION_ALERT_LEVEL_RED;
|
|
[shipAI reactToMessage:@"RED_ALERT"];
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (void) decreaseAlertLevel
|
|
{
|
|
switch (alert_level)
|
|
{
|
|
case STATION_ALERT_LEVEL_RED :
|
|
alert_level = STATION_ALERT_LEVEL_YELLOW;
|
|
[shipAI reactToMessage:@"CONDITION_YELLOW"];
|
|
break;
|
|
case STATION_ALERT_LEVEL_YELLOW :
|
|
alert_level = STATION_ALERT_LEVEL_GREEN;
|
|
[shipAI reactToMessage:@"CONDITION_GREEN"];
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (void) launchPolice
|
|
{
|
|
int techlevel = [self equivalent_tech_level];
|
|
if (techlevel == NSNotFound)
|
|
techlevel = 6;
|
|
int police_target = primaryTarget;
|
|
int i;
|
|
for (i = 0; (i < 4)&&(police_launched < max_police) ; i++)
|
|
{
|
|
ShipEntity *police_ship;
|
|
if (![universe entityForUniversalID:police_target])
|
|
{
|
|
[shipAI reactToMessage:@"TARGET_LOST"];
|
|
return;
|
|
}
|
|
|
|
//NSLog(@"Launching Police Ship to intercept %@",[universe entityForUniversalID:police_target]);
|
|
|
|
if ((ranrot_rand() & 7) + 6 <= techlevel)
|
|
police_ship = [universe getShipWithRole:@"interceptor"]; // retain count = 1
|
|
else
|
|
police_ship = [universe getShipWithRole:@"police"]; // retain count = 1
|
|
[police_ship setRoles:@"police"];
|
|
[police_ship addTarget:[universe entityForUniversalID:police_target]];
|
|
[police_ship setScanClass: CLASS_POLICE];
|
|
[police_ship setBounty:0];
|
|
|
|
//[police_ship setReportAImessages:YES]; // debug
|
|
|
|
[[police_ship getAI] setStateMachine:@"policeInterceptAI.plist"];
|
|
[self addShipToLaunchQueue:police_ship];
|
|
[police_ship release];
|
|
police_launched++;
|
|
}
|
|
no_docking_while_launching = YES;
|
|
[self abortAllDockings];
|
|
}
|
|
|
|
- (void) launchDefenseShip
|
|
{
|
|
int defense_target = primaryTarget;
|
|
//int n_ships = [universe countShipsWithRole:@"hermit-ship"] + [self countShipsInLaunchQueueWithRole:@"hermit-ship"];
|
|
ShipEntity *defense_ship;
|
|
NSString* defense_ship_key = nil;
|
|
NSString* defense_ship_role_key = nil;
|
|
NSString* defense_ship_ai = nil;
|
|
|
|
if ([shipinfoDictionary objectForKey:@"defense_ship"])
|
|
{
|
|
// NSLog(@"DEBUG Defense ship key found: %@", [shipinfoDictionary objectForKey:@"defense_ship"]);
|
|
defense_ship_key = (NSString*)[shipinfoDictionary objectForKey:@"defense_ship"];
|
|
}
|
|
if ([shipinfoDictionary objectForKey:@"defense_ship_role"])
|
|
{
|
|
// NSLog(@"DEBUG Defense ship role key found: %@", [shipinfoDictionary objectForKey:@"defense_ship_role"]);
|
|
defense_ship_role_key = (NSString*)[shipinfoDictionary objectForKey:@"defense_ship_role"];
|
|
}
|
|
|
|
if (police_launched >= max_defense_ships) // shuttles are to rockhermits what police ships are to stations
|
|
return;
|
|
|
|
if (![universe entityForUniversalID:defense_target])
|
|
{
|
|
[shipAI reactToMessage:@"TARGET_LOST"];
|
|
return;
|
|
}
|
|
|
|
// NSLog(@"DEBUG Launching defense ship to intercept %@",[(ShipEntity *)[universe entityForUniversalID:defense_target] name]);
|
|
|
|
police_launched++;
|
|
|
|
if (defense_ship_key)
|
|
{
|
|
defense_ship = [universe getShip:defense_ship_key];
|
|
// NSLog(@"DEBUG launchDefenseShip Got ship with defense_ship '%@' : %@", defense_ship_key, defense_ship);
|
|
[defense_ship setRoles:@"defense_ship"];
|
|
}
|
|
else
|
|
{
|
|
if (defense_ship_role_key)
|
|
{
|
|
defense_ship = [universe getShipWithRole:defense_ship_role_key];
|
|
// NSLog(@"DEBUG launchDefenseShip Got ship with defense_ship_role '%@' : %@", defense_ship_role_key, defense_ship);
|
|
[defense_ship setRoles:@"defense_ship"];
|
|
}
|
|
else
|
|
{
|
|
defense_ship = [universe getShipWithRole:@"hermit-ship"]; // retain count = 1
|
|
defense_ship_ai = @"policeInterceptAI.plist";
|
|
}
|
|
}
|
|
|
|
[defense_ship setGroup_id:universal_id]; // who's your Daddy
|
|
[defense_ship addTarget:[universe entityForUniversalID:defense_target]];
|
|
|
|
if ((scan_class != CLASS_ROCK)&&(scan_class != CLASS_STATION))
|
|
[defense_ship setScanClass: scan_class]; // same as self
|
|
else
|
|
[defense_ship setScanClass: CLASS_NEUTRAL]; // or neutral
|
|
|
|
//[defense_ship setReportAImessages:YES]; // debug
|
|
|
|
// NSLog(@"DEBUG Launching defense ship %@ %@", defense_ship, [defense_ship name]);
|
|
|
|
if (defense_ship_ai)
|
|
[[defense_ship getAI] setStateMachine:defense_ship_ai];
|
|
[self addShipToLaunchQueue:defense_ship];
|
|
[defense_ship release];
|
|
no_docking_while_launching = YES;
|
|
[self abortAllDockings];
|
|
|
|
// NSLog(@"DEBUG Launchqueue : %@",[launchQueue description]);
|
|
|
|
}
|
|
|
|
- (void) launchScavenger
|
|
{
|
|
ShipEntity *scavenger_ship;
|
|
|
|
int scavs = [universe countShipsWithRole:@"scavenger" inRange:SCANNER_MAX_RANGE ofEntity:self] + [self countShipsInLaunchQueueWithRole:@"scavenger"];
|
|
|
|
if (scavs >= max_scavengers)
|
|
return;
|
|
|
|
if (scavengers_launched >= max_scavengers)
|
|
return;
|
|
|
|
//NSLog(@"Launching Scavenger");
|
|
|
|
scavengers_launched++;
|
|
|
|
scavenger_ship = [universe getShipWithRole:@"scavenger"]; // retain count = 1
|
|
[scavenger_ship setScanClass: CLASS_NEUTRAL];
|
|
|
|
//[scavenger_ship setReportAImessages:YES]; // debug
|
|
|
|
[scavenger_ship setGroup_id:universal_id]; // who's your Daddy
|
|
[[scavenger_ship getAI] setStateMachine:@"scavengerAI.plist"];
|
|
[self addShipToLaunchQueue:scavenger_ship];
|
|
[scavenger_ship release];
|
|
}
|
|
|
|
- (void) launchMiner
|
|
{
|
|
ShipEntity *miner_ship;
|
|
|
|
int n_miners = [universe countShipsWithRole:@"miner" inRange:SCANNER_MAX_RANGE ofEntity:self] + [self countShipsInLaunchQueueWithRole:@"miner"];
|
|
|
|
if (n_miners >= 1) // just the one
|
|
return;
|
|
|
|
// count miners as scavengers...
|
|
//
|
|
if (scavengers_launched >= max_scavengers)
|
|
return;
|
|
//
|
|
// NSLog(@"Launching Miner");
|
|
//
|
|
scavengers_launched++;
|
|
|
|
miner_ship = [universe getShipWithRole:@"miner"]; // retain count = 1
|
|
[miner_ship setScanClass: CLASS_NEUTRAL];
|
|
|
|
// [miner_ship setReportAImessages:YES]; // debug
|
|
|
|
[miner_ship setGroup_id:universal_id]; // who's your Daddy
|
|
[[miner_ship getAI] setStateMachine:@"minerAI.plist"];
|
|
[self addShipToLaunchQueue:miner_ship];
|
|
[miner_ship release];
|
|
}
|
|
|
|
/**Lazygun** added the following method. A complete rip-off of launchDefenseShip.
|
|
*/
|
|
- (void) launchPirateShip
|
|
{
|
|
//Pirate ships are launched from the same pool as defence ships.
|
|
int defense_target = primaryTarget;
|
|
ShipEntity *pirate_ship;
|
|
if (police_launched >= max_defense_ships) // shuttles are to rockhermits what police ships are to stations
|
|
return;
|
|
if (![universe entityForUniversalID:defense_target])
|
|
{
|
|
[shipAI reactToMessage:@"TARGET_LOST"];
|
|
return;
|
|
}
|
|
|
|
//NSLog(@"Launching pirate Ship to intercept %@",[(ShipEntity *)[universe entityForUniversalID:defense_target] name]); //debug
|
|
|
|
police_launched++;
|
|
|
|
// pirate_ship = [universe getShipWithRole:@"hermit-ship"]; // retain count = 1
|
|
// Yep! The standard hermit defence ships, even if they're the aggressor.
|
|
pirate_ship = [universe getShipWithRole:@"pirate"]; // retain count = 1
|
|
// Nope, use standard pirates in a generic method.
|
|
|
|
// set the owner of the ship to the station so that it can check back for docking later
|
|
[pirate_ship setOwner:self];
|
|
[pirate_ship setGroup_id:universal_id]; // who's your Daddy
|
|
|
|
[pirate_ship addTarget:[universe entityForUniversalID:defense_target]];
|
|
[pirate_ship setScanClass: CLASS_NEUTRAL];
|
|
//**Lazygun** added 30 Nov 04 to put a bounty on those pirates' heads.
|
|
[pirate_ship setBounty: 10 + floor(randf() * 20)]; // modified for variety
|
|
|
|
//[pirate_ship setReportAImessages:YES]; // debug
|
|
// //**Lazygun** changed name of the AI on 30 Nov 04 from "pirateAI.plist"
|
|
// [[pirate_ship getAI] setStateMachine:@"launchedPirateAI.plist"];
|
|
|
|
[self addShipToLaunchQueue:pirate_ship];
|
|
[pirate_ship release];
|
|
no_docking_while_launching = YES;
|
|
[self abortAllDockings];
|
|
}
|
|
|
|
|
|
- (void) launchShuttle
|
|
{
|
|
ShipEntity *shuttle_ship;
|
|
|
|
shuttle_ship = [universe getShipWithRole:@"shuttle"]; // retain count = 1
|
|
[shuttle_ship setScanClass: CLASS_NEUTRAL];
|
|
|
|
[shuttle_ship setCargoFlag:CARGO_FLAG_FULL_SCARCE];
|
|
|
|
// [shuttle_ship setReportAImessages:YES]; // debug
|
|
|
|
[[shuttle_ship getAI] setStateMachine:@"fallingShuttleAI.plist"];
|
|
[self addShipToLaunchQueue:shuttle_ship];
|
|
|
|
//NSLog(@"%@ Prepping shuttle: %@ %d for launch.", [self name], [shuttle_ship name], [shuttle_ship universal_id]);
|
|
|
|
[shuttle_ship release];
|
|
}
|
|
|
|
- (void) launchTrader
|
|
{
|
|
BOOL sunskimmer = (randf() < 0.1); // 10%
|
|
ShipEntity *trader_ship;
|
|
|
|
if (sunskimmer)
|
|
trader_ship = [universe getShipWithRole:@"trader"]; // retain count = 1
|
|
else
|
|
trader_ship = [universe getShipWithRole:@"sunskim_trader"]; // retain count = 1
|
|
|
|
[trader_ship setRoles:@"trader"];
|
|
[trader_ship setScanClass: CLASS_NEUTRAL];
|
|
[trader_ship setCargoFlag:CARGO_FLAG_FULL_PLENTIFUL];
|
|
|
|
//[trader_ship setReportAImessages:YES]; // debug
|
|
|
|
if (sunskimmer)
|
|
{
|
|
int escorts = [trader_ship n_escorts];
|
|
[[trader_ship getAI] setStateMachine:@"route2sunskimAI.plist"];
|
|
[trader_ship setN_escorts:0];
|
|
while (escorts--)
|
|
{
|
|
[self launchEscort];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
[[trader_ship getAI] setStateMachine:@"exitingTraderAI.plist"];
|
|
}
|
|
|
|
[self addShipToLaunchQueue:trader_ship];
|
|
|
|
//NSLog(@"%@ Prepping trader: %@ %d for launch.", [self name], [trader_ship name], [trader_ship universal_id]);
|
|
|
|
[trader_ship release];
|
|
}
|
|
|
|
- (void) launchEscort
|
|
{
|
|
ShipEntity *escort_ship;
|
|
|
|
escort_ship = [universe getShipWithRole:@"escort"]; // retain count = 1
|
|
[escort_ship setScanClass: CLASS_NEUTRAL];
|
|
|
|
[escort_ship setCargoFlag:CARGO_FLAG_FULL_PLENTIFUL];
|
|
|
|
//[escort_ship setReportAImessages:YES]; // debug
|
|
|
|
[[escort_ship getAI] setStateMachine:@"escortAI.plist"];
|
|
[self addShipToLaunchQueue:escort_ship];
|
|
|
|
//NSLog(@"%@ Prepping escort: %@ %d for launch.", [self name], [escort_ship name], [escort_ship universal_id]);
|
|
|
|
[escort_ship release];
|
|
}
|
|
|
|
- (BOOL) launchPatrol
|
|
{
|
|
|
|
if (police_launched < max_police)
|
|
{
|
|
ShipEntity *patrol_ship;
|
|
int techlevel = [self equivalent_tech_level];
|
|
if (techlevel == NSNotFound)
|
|
techlevel = 6;
|
|
|
|
police_launched++;
|
|
|
|
if ((ranrot_rand() & 7) + 6 <= techlevel)
|
|
patrol_ship = [universe getShipWithRole:@"interceptor"]; // retain count = 1
|
|
else
|
|
patrol_ship = [universe getShipWithRole:@"police"]; // retain count = 1
|
|
[patrol_ship switchLightsOff];
|
|
[patrol_ship setScanClass: CLASS_POLICE];
|
|
[patrol_ship setRoles:@"police"];
|
|
[patrol_ship setBounty:0];
|
|
|
|
// [patrol_ship setReportAImessages:YES]; // debug
|
|
|
|
[patrol_ship setGroup_id:universal_id]; // who's your Daddy
|
|
[[patrol_ship getAI] setStateMachine:@"planetPatrolAI.plist"];
|
|
[self addShipToLaunchQueue:patrol_ship];
|
|
|
|
[self acceptPatrolReportFrom:patrol_ship];
|
|
|
|
// NSLog(@"%@ Prepping patrol: %@ %d for launch.", [self name], [patrol_ship name], [patrol_ship universal_id]);
|
|
|
|
[patrol_ship release];
|
|
|
|
return YES;
|
|
}
|
|
else
|
|
return NO;
|
|
}
|
|
|
|
- (void) becomeExplosion
|
|
{
|
|
// launch docked ships if possible
|
|
PlayerEntity* player = (PlayerEntity*)[universe entityZero];
|
|
if (([player getStatus] == STATUS_DOCKED)&&([player docked_station] == self))
|
|
{
|
|
// undock the player!
|
|
[player leaveDock:self];
|
|
[universe setViewDirection:VIEW_FORWARD];
|
|
[universe setDisplayCursor:NO];
|
|
[player warnAboutHostiles]; // sound a klaxon
|
|
}
|
|
|
|
if (scan_class == CLASS_ROCK) // ie we're a rock hermit or similar
|
|
{
|
|
// set the roles so that we break up into rocks!
|
|
roles = @"asteroid";
|
|
being_mined = YES;
|
|
}
|
|
|
|
// finally bite the bullet
|
|
[super becomeExplosion];
|
|
}
|
|
|
|
- (void) acceptPatrolReportFrom:(ShipEntity*) patrol_ship
|
|
{
|
|
last_patrol_report_time = [universe getTime];
|
|
// NSLog(@"..... patrol report received from %@ %d at %3.2f", [patrol_ship name], [patrol_ship universal_id], [universe getTime]);
|
|
}
|
|
|
|
- (void) acceptDockingClearanceRequestFrom:(ShipEntity *)other
|
|
{
|
|
if (self != [universe station])
|
|
{
|
|
//NSLog(@"DEBUG acceptDistressMessageFrom rejected from sub-station '%@'", name);
|
|
return;
|
|
}
|
|
|
|
// check
|
|
//
|
|
if ([shipsOnApproach count])
|
|
{
|
|
[self sendExpandedMessage:@"Please wait until all ships have completed their approach." toShip:other];
|
|
return;
|
|
}
|
|
if ([launchQueue count])
|
|
{
|
|
[self sendExpandedMessage:@"Please wait until launching ships have cleared %H Station." toShip:other];
|
|
return;
|
|
}
|
|
if (last_launch_time < [universe getTime])
|
|
{
|
|
last_launch_time = [universe getTime] + 126;
|
|
[self sendExpandedMessage:@"You are cleared to dock within the next two minutes. Please proceeed." toShip:other];
|
|
}
|
|
}
|
|
|
|
- (NSString *) roles
|
|
{
|
|
NSString* all_roles = [shipinfoDictionary objectForKey:@"roles"];
|
|
return [[all_roles componentsSeparatedByString:@" "] objectAtIndex:0];
|
|
}
|
|
|
|
@end
|
|
|