Line data Source code
1 0 : /*
2 :
3 : DockEntity.m
4 :
5 : Oolite
6 : Copyright (C) 2004-2013 Giles C Williams and contributors
7 :
8 : This program is free software; you can redistribute it and/or
9 : modify it under the terms of the GNU General Public License
10 : as published by the Free Software Foundation; either version 2
11 : of the License, or (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program; if not, write to the Free Software
20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 : MA 02110-1301, USA.
22 :
23 : */
24 :
25 : #import "DockEntity.h"
26 : #import "StationEntity.h"
27 : #import "ShipEntityAI.h"
28 : #import "OOCollectionExtractors.h"
29 : #import "OOStringParsing.h"
30 : #import "OOStringExpander.h"
31 :
32 : #import "Universe.h"
33 : #import "HeadUpDisplay.h"
34 :
35 : #import "PlayerEntityLegacyScriptEngine.h"
36 : #import "OOLegacyScriptWhitelist.h"
37 : #import "OOPlanetEntity.h"
38 : #import "OOShipGroup.h"
39 : #import "OOQuiriumCascadeEntity.h"
40 :
41 : #import "AI.h"
42 : #import "OOCharacter.h"
43 :
44 : #import "OOJSScript.h"
45 : #import "OODebugGLDrawing.h"
46 : #import "OODebugFlags.h"
47 :
48 :
49 : @interface DockEntity (OOPrivate)
50 :
51 0 : - (void) clearIdLocks:(ShipEntity *)ship;
52 0 : - (void) clearAllIdLocks;
53 0 : - (void) autoDockShipsInQueue:(NSMutableDictionary *)queue;
54 0 : - (void) addShipToShipsOnApproach:(ShipEntity *)ship;
55 0 : - (void) pullInShipIfPermitted:(ShipEntity *)ship;
56 :
57 : @end
58 :
59 :
60 : @implementation DockEntity
61 :
62 : - (NSUInteger) pruneAndCountShipsOnApproach
63 : {
64 : // Remove dead entities.
65 : // Enumerate over allKeys explicitly because we mutate the dictionary.
66 : NSNumber *idObj = nil;
67 : foreach (idObj, [shipsOnApproach allKeys])
68 : {
69 : ShipEntity *ship = [UNIVERSE entityForUniversalID:[idObj unsignedIntValue]];
70 : /* Remove ships from the approach queue if they are dead, or
71 : * are more than 25.6km from the dock.
72 : */
73 : if (ship == nil || HPmagnitude2(HPvector_subtract([ship position],[self absolutePositionForSubentity])) > SCANNER_MAX_RANGE2)
74 : {
75 : [shipsOnApproach removeObjectForKey:idObj];
76 : if (ship != nil) {
77 : // notify ship if it's alive
78 : [ship sendAIMessage:@"DOCKING_ABORTED"];
79 : [ship doScriptEvent:OOJSID("stationWithdrewDockingClearance")];
80 : }
81 : }
82 : }
83 :
84 : if ([shipsOnApproach count] == 0)
85 : {
86 : if (last_launch_time < [UNIVERSE getTime])
87 : {
88 : last_launch_time = [UNIVERSE getTime];
89 : }
90 : }
91 :
92 : return [shipsOnApproach count];
93 : }
94 :
95 :
96 : - (void) abortAllDockings
97 : {
98 : double playerExtraTime = 0;
99 :
100 : no_docking_while_launching = YES;
101 :
102 : NSNumber *idObj = nil;
103 : foreach (idObj, [shipsOnApproach allKeys])
104 : {
105 : ShipEntity *ship = [UNIVERSE entityForUniversalID:[idObj unsignedIntValue]];
106 : if ([ship isShip])
107 : {
108 : [ship sendAIMessage:@"DOCKING_ABORTED"];
109 : [ship doScriptEvent:OOJSID("stationWithdrewDockingClearance")];
110 : }
111 : }
112 : [shipsOnApproach removeAllObjects];
113 :
114 : PlayerEntity *player = PLAYER;
115 : StationEntity *station = (StationEntity*)[self parentEntity];
116 : BOOL isDockingStation = (station == [player getTargetDockStation]) && ([station playerReservedDock] == self);
117 : if (isDockingStation && [player status] == STATUS_IN_FLIGHT &&
118 : [player getDockingClearanceStatus] >= DOCKING_CLEARANCE_STATUS_REQUESTED)
119 : {
120 : if (HPmagnitude2(HPvector_subtract([player position], [self absolutePositionForSubentity])) > 2250000) // within 1500m of the dock
121 : {
122 : [station sendExpandedMessage:@"[station-docking-clearance-abort-cancelled]" toShip:player];
123 : [player setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_NONE];
124 : [player doScriptEvent:OOJSID("stationWithdrewDockingClearance")];
125 : }
126 : else
127 : {
128 : playerExtraTime = 10; // when very close to the port, give the player a few seconds to react on the abort message.
129 : int seconds = round(playerExtraTime);
130 : [station sendExpandedMessage:OOExpandKey(@"station-docking-clearance-abort-cancelled-in-time", seconds) toShip:player];
131 : [player setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_TIMING_OUT];
132 : }
133 :
134 : }
135 :
136 : // mark docking queue flight pattern as clear
137 : [self clearAllIdLocks];
138 :
139 : last_launch_time = [UNIVERSE getTime] + playerExtraTime;
140 : }
141 :
142 :
143 0 : - (void) abortAllLaunches
144 : {
145 : no_docking_while_launching = NO;
146 : [launchQueue removeAllObjects];
147 : }
148 :
149 :
150 0 : - (void) autoDockShipsInQueue:(NSMutableDictionary *)queue
151 : {
152 : NSNumber *idObj = nil;
153 : foreach (idObj, [queue allKeys])
154 : {
155 : ShipEntity *ship = [UNIVERSE entityForUniversalID:[idObj unsignedIntValue]];
156 : if ([ship isShip])
157 : {
158 : [self pullInShipIfPermitted:ship];
159 : }
160 : }
161 :
162 : [queue removeAllObjects];
163 : }
164 :
165 :
166 : - (void) autoDockShipsOnApproach
167 : {
168 : [self autoDockShipsInQueue:shipsOnApproach];
169 : }
170 :
171 :
172 : - (BOOL) allowsDocking
173 : {
174 : return allow_docking;
175 : }
176 :
177 :
178 : - (void) setAllowsDocking:(BOOL)allowed
179 : {
180 : if (!allowed && allow_docking)
181 : {
182 : [self abortAllDockings];
183 : }
184 : allow_docking = allowed;
185 : }
186 :
187 :
188 : - (BOOL) disallowedDockingCollides
189 : {
190 : return disallowed_docking_collides;
191 : }
192 :
193 :
194 : - (BOOL) allowsLaunching
195 : {
196 : return allow_launching;
197 : }
198 :
199 :
200 : - (void) setAllowsLaunching:(BOOL)allowed
201 : {
202 : if (!allowed && allow_launching)
203 : {
204 : [self abortAllLaunches];
205 : }
206 : allow_launching = allowed;
207 : }
208 :
209 :
210 : - (void) setDisallowedDockingCollides:(BOOL)ddc
211 : {
212 : disallowed_docking_collides = ddc;
213 : }
214 :
215 :
216 : - (void) setVirtual
217 : {
218 : virtual_dock = YES;
219 : }
220 :
221 :
222 : - (NSString*) canAcceptShipForDocking:(ShipEntity *) ship
223 : {
224 : // First test permanent rejection reasons
225 : if (!allow_docking)
226 : {
227 : return @"DOCK_CLOSED"; // could be temp or perm reject
228 : }
229 : BoundingBox bb = [ship totalBoundingBox];
230 : if ((port_dimensions.x < (bb.max.x - bb.min.x) || port_dimensions.y < (bb.max.y - bb.min.y)) &&
231 : (port_dimensions.y < (bb.max.x - bb.min.x) || port_dimensions.x < (bb.max.y - bb.min.y)))
232 : {
233 : return @"TOO_BIG_TO_DOCK";
234 : }
235 :
236 : // callback to allow more complex filtering on accept/reject
237 : JSContext *context = OOJSAcquireContext();
238 : jsval rval = JSVAL_VOID;
239 : jsval args[] = { OOJSValueFromNativeObject(context, ship) };
240 : JSBool accept = YES;
241 :
242 : BOOL OK = [[self script] callMethod:OOJSID("acceptDockingRequestFrom") inContext:context withArguments:args count:1 result:&rval];
243 : if (OK) OK = JS_ValueToBoolean(context, rval, &accept);
244 : if (!OK) accept = YES; // default to permreject
245 : OOJSRelinquishContext(context);
246 :
247 : if (!accept)
248 : {
249 : return @"TOO_BIG_TO_DOCK";
250 : }
251 :
252 : // Second test temporary rejection reasons
253 : if (no_docking_while_launching)
254 : {
255 : return @"TRY_AGAIN_LATER";
256 : }
257 : // if there are pending launches, temporarily don't accept docking requests
258 : if (allow_launching && [launchQueue count])
259 : {
260 : return @"TRY_AGAIN_LATER";
261 : }
262 :
263 : return @"DOCKING_POSSIBLE";
264 : }
265 :
266 :
267 : - (BOOL) isOffCentre
268 : {
269 : if (fabs(position.x) + fabs(position.y) > 5.0)
270 : {
271 : return YES;
272 : }
273 : Vector dir = vector_forward_from_quaternion(orientation);
274 : if (fabs(dir.x) + fabs(dir.y) > 0.1)
275 : {
276 : return YES;
277 : }
278 : return NO;
279 : }
280 :
281 :
282 : - (NSDictionary *) dockingInstructionsForShip:(ShipEntity *)ship
283 : {
284 : if (ship == nil) return nil;
285 :
286 : OOUniversalID ship_id = [ship universalID];
287 : NSNumber *shipID = [NSNumber numberWithUnsignedShort:ship_id];
288 : StationEntity *station = (StationEntity *)[self parentEntity];
289 :
290 : HPVector launchVector = HPvector_forward_from_quaternion(quaternion_multiply(orientation, [station orientation]));
291 : HPVector temp = (fabs(launchVector.x) < 0.8)? make_HPvector(1,0,0) : make_HPvector(0,1,0);
292 : temp = HPcross_product(launchVector, temp); // 90 deg to launchVector & temp
293 : HPVector vi = HPcross_product(launchVector, temp);
294 : HPVector vj = HPcross_product(launchVector, vi);
295 : HPVector vk = launchVector;
296 :
297 : // check if this is a new ship on approach
298 : //
299 : if (![shipsOnApproach objectForKey:shipID])
300 : {
301 : HPVector delta = HPvector_subtract([ship position], [self absolutePositionForSubentity]);
302 : float ship_distance = HPmagnitude(delta);
303 :
304 : if (ship_distance > SCANNER_MAX_RANGE)
305 : {
306 : // too far away - don't claim a docking slot by not putting on approachlist for now.
307 : return OOMakeDockingInstructions(station, [self absolutePositionForSubentity], [ship maxFlightSpeed], 10000, @"APPROACH", nil, NO, -1);
308 : }
309 :
310 : [self addShipToShipsOnApproach: ship];
311 :
312 : if (ship_distance < 1000.0 + [station collisionRadius] + ship->collision_radius) // too close - back off
313 : return OOMakeDockingInstructions(station, [self absolutePositionForSubentity], [ship maxFlightSpeed], 5000, @"BACK_OFF", nil, NO, -1);
314 :
315 : float dot = HPdot_product(launchVector, delta);
316 : if (dot < 0) // approaching from the wrong side of the station - construct a vector to the side of the station.
317 : {
318 : HPVector approachVector = HPcross_product(HPvector_normal(delta), launchVector);
319 : approachVector = HPcross_product(launchVector, approachVector); // vector, 90 degr rotated from launchVector towards target.
320 : return OOMakeDockingInstructions(station, OOHPVectorTowards([self absolutePositionForSubentity], approachVector, [station collisionRadius] + 5000) , [ship maxFlightSpeed], 1000, @"APPROACH", nil, NO, -1);
321 : }
322 :
323 : if (ship_distance > 12500.0)
324 : {
325 : // long way off - approach more closely
326 : return OOMakeDockingInstructions(station, [self absolutePositionForSubentity], [ship maxFlightSpeed], 10000, @"APPROACH", nil, NO, -1);
327 : }
328 : }
329 :
330 : if (![shipsOnApproach objectForKey:shipID])
331 : {
332 : // some error has occurred - log it, and send the try-again message
333 : OOLogERR(@"station.issueDockingInstructions.failed", @"couldn't addShipToShipsOnApproach:%@ in %@, retrying later -- shipsOnApproach:\n%@", ship, self, shipsOnApproach);
334 :
335 : return OOMakeDockingInstructions(station, [ship position], 200, 100, @"TRY_AGAIN_LATER", nil, NO, -1);
336 : }
337 :
338 :
339 : // shipsOnApproach now has an entry for the ship.
340 : //
341 : NSMutableArray* coordinatesStack = [shipsOnApproach objectForKey:shipID];
342 :
343 : if ([coordinatesStack count] == 0)
344 : {
345 : OOLogERR(@"station.issueDockingInstructions.failed", @" -- coordinatesStack = %@", coordinatesStack);
346 :
347 : return OOMakeDockingInstructions(station, [ship position], 0, 100, @"HOLD_POSITION", nil, NO, -1);
348 : }
349 :
350 : // get the docking information from the instructions
351 : NSMutableDictionary *nextCoords = (NSMutableDictionary *)[coordinatesStack objectAtIndex:0];
352 : int docking_stage = [nextCoords oo_intForKey:@"docking_stage"];
353 : float speedAdvised = [nextCoords oo_floatForKey:@"speed"];
354 : float rangeAdvised = [nextCoords oo_floatForKey:@"range"];
355 :
356 : // calculate world coordinates from relative coordinates
357 : HPVector rel_coords;
358 : rel_coords.x = [nextCoords oo_doubleForKey:@"rx"];
359 : rel_coords.y = [nextCoords oo_doubleForKey:@"ry"];
360 : rel_coords.z = [nextCoords oo_doubleForKey:@"rz"];
361 : HPVector coords = [self absolutePositionForSubentity];
362 : coords.x += rel_coords.x * vi.x + rel_coords.y * vj.x + rel_coords.z * vk.x;
363 : coords.y += rel_coords.x * vi.y + rel_coords.y * vj.y + rel_coords.z * vk.y;
364 : coords.z += rel_coords.x * vi.z + rel_coords.y * vj.z + rel_coords.z * vk.z;
365 :
366 : // check if the ship is at the control point
367 : double max_allowed_range = 2.0f * rangeAdvised + ship->collision_radius; // maximum distance permitted from control point - twice advised range
368 : HPVector delta = HPvector_subtract(ship->position, coords);
369 :
370 : if (HPmagnitude2(delta) > max_allowed_range * max_allowed_range) // too far from the coordinates - do not remove them from the stack!
371 : {
372 : if ((docking_stage == 1) &&(HPmagnitude2(delta) < 1000000.0)) // 1km*1km
373 : speedAdvised *= 0.5; // half speed
374 :
375 : return OOMakeDockingInstructions(station, coords, speedAdvised, rangeAdvised, @"APPROACH_COORDINATES", nil, NO, docking_stage);
376 : }
377 :
378 : // else, reached the current coordinates okay..
379 :
380 : // get the NEXT coordinates
381 : nextCoords = (NSMutableDictionary *)[coordinatesStack oo_dictionaryAtIndex:1];
382 : if (nextCoords == nil)
383 : {
384 : return nil;
385 : }
386 :
387 : docking_stage = [nextCoords oo_intForKey:@"docking_stage"];
388 : speedAdvised = [nextCoords oo_floatForKey:@"speed"];
389 : rangeAdvised = [nextCoords oo_floatForKey:@"range"];
390 : BOOL match_rotation = [nextCoords oo_boolForKey:@"match_rotation"];
391 : NSString *comms_message = [nextCoords oo_stringForKey:@"comms_message"];
392 :
393 : if (comms_message)
394 : {
395 : [station sendExpandedMessage:comms_message toShip:ship];
396 : }
397 :
398 : // calculate world coordinates from relative coordinates
399 : rel_coords.x = [nextCoords oo_doubleForKey:@"rx"];
400 : rel_coords.y = [nextCoords oo_doubleForKey:@"ry"];
401 : rel_coords.z = [nextCoords oo_doubleForKey:@"rz"];
402 : coords = [self absolutePositionForSubentity];
403 : coords.x += rel_coords.x * vi.x + rel_coords.y * vj.x + rel_coords.z * vk.x;
404 : coords.y += rel_coords.x * vi.y + rel_coords.y * vj.y + rel_coords.z * vk.y;
405 : coords.z += rel_coords.x * vi.z + rel_coords.y * vj.z + rel_coords.z * vk.z;
406 :
407 : if([id_lock[docking_stage] weakRefUnderlyingObject] == nil &&
408 : [id_lock[docking_stage + 1] weakRefUnderlyingObject] == nil &&
409 : [id_lock[docking_stage + 2] weakRefUnderlyingObject] == nil) // check three stages ahead
410 : {
411 : // approach is clear - move to next position
412 : //
413 :
414 : // clear any previously owned docking stages
415 : [self clearIdLocks:ship];
416 :
417 : if (docking_stage > 1) // don't claim first docking stage
418 : {
419 : [id_lock[docking_stage] release];
420 : id_lock[docking_stage] = [ship weakRetain]; // otherwise - claim this docking stage
421 : }
422 :
423 : //remove the previous stage from the stack
424 : [coordinatesStack removeObjectAtIndex:0];
425 :
426 : return OOMakeDockingInstructions(station, coords, speedAdvised, rangeAdvised, @"APPROACH_COORDINATES", nil, match_rotation, docking_stage);
427 : }
428 :
429 : // else, approach isn't clear - hold position..
430 : //
431 : [[ship getAI] message:@"HOLD_POSITION"];
432 :
433 : if (![nextCoords objectForKey:@"hold_message_given"])
434 : {
435 : // COMM-CHATTER
436 : [UNIVERSE clearPreviousMessage];
437 : [self sendExpandedMessage: @"[station-hold-position]" toShip: ship];
438 : [nextCoords setObject:@"YES" forKey:@"hold_message_given"];
439 : }
440 :
441 : return OOMakeDockingInstructions(station, ship->position, 0, 100, @"HOLD_POSITION", nil, NO, -1);
442 : }
443 :
444 :
445 0 : - (void) addShipToShipsOnApproach:(ShipEntity *) ship
446 : {
447 : int corridor_distance[] = { -1, 1, 3, 5, 7, 9, 11, 12, 12};
448 : int corridor_offset[] = { 0, 0, 0, 0, 0, 0, 1, 3, 12};
449 : /* Eric's improvements to the docking flight code seem to have
450 : * made it safer to go quite a bit faster here. With the increased
451 : * numbers of ships which might need to dock at the main station,
452 : * faster docking will help avoid massive queues. Previous speed
453 : * was mostly 48 - CIM: 27/8/2013*/
454 : int corridor_speed[] = { 96, 96, 128, 128, 96, 128, 128, 256, 512}; // how fast to approach the next point
455 : int corridor_range[] = { 24, 12, 6, 4, 4, 6, 15, 38, 96}; // how close you have to get to the target point
456 : int corridor_rotate[] = { 1, 1, 1, 1, 0, 0, 0, 0, 0}; // whether to match the station rotation
457 : int corridor_count = 9;
458 : int corridor_final_approach = 3;
459 :
460 : NSNumber *shipID = [NSNumber numberWithUnsignedShort:[ship universalID]];
461 : StationEntity *station = (StationEntity *)[self parentEntity];
462 :
463 : HPVector launchVector = HPvector_forward_from_quaternion(quaternion_multiply(orientation, [station orientation]));
464 : HPVector temp = (fabs(launchVector.x) < 0.8)? make_HPvector(1,0,0) : make_HPvector(0,1,0);
465 : temp = HPcross_product(launchVector, temp); // 90 deg to launchVector & temp
466 : HPVector rightVector = HPcross_product(launchVector, temp);
467 : HPVector upVector = HPcross_product(launchVector, rightVector);
468 :
469 : // will select a direction for offset based on the entity personality (was ship ID)
470 : int offset_id = [ship entityPersonalityInt] & 0xf; // 16 point compass
471 : double c = cos(offset_id * M_PI * ONE_EIGHTH);
472 : double s = sin(offset_id * M_PI * ONE_EIGHTH);
473 :
474 : // test if this points at the ship
475 : HPVector point1 = [self absolutePositionForSubentity];
476 : point1.x += launchVector.x * corridor_offset[corridor_count - 1];
477 : point1.y += launchVector.x * corridor_offset[corridor_count - 1];
478 : point1.z += launchVector.x * corridor_offset[corridor_count - 1];
479 : HPVector alt1 = point1;
480 : point1.x += c * upVector.x * corridor_offset[corridor_count - 1] + s * rightVector.x * corridor_offset[corridor_count - 1];
481 : point1.y += c * upVector.y * corridor_offset[corridor_count - 1] + s * rightVector.y * corridor_offset[corridor_count - 1];
482 : point1.z += c * upVector.z * corridor_offset[corridor_count - 1] + s * rightVector.z * corridor_offset[corridor_count - 1];
483 : alt1.x -= c * upVector.x * corridor_offset[corridor_count - 1] + s * rightVector.x * corridor_offset[corridor_count - 1];
484 : alt1.y -= c * upVector.y * corridor_offset[corridor_count - 1] + s * rightVector.y * corridor_offset[corridor_count - 1];
485 : alt1.z -= c * upVector.z * corridor_offset[corridor_count - 1] + s * rightVector.z * corridor_offset[corridor_count - 1];
486 : if (HPdistance2(alt1, ship->position) < HPdistance2(point1, ship->position))
487 : {
488 : s = -s;
489 : c = -c; // turn 180 degrees
490 : }
491 :
492 : //
493 : NSMutableArray *coordinatesStack = [NSMutableArray arrayWithCapacity: MAX_DOCKING_STAGES];
494 : float port_depth = port_dimensions.z; // 250m deep standard port.
495 :
496 : int i;
497 : for (i = corridor_count - 1; i >= 0; i--)
498 : {
499 : NSMutableDictionary *nextCoords = [NSMutableDictionary dictionaryWithCapacity:3];
500 : int offset = corridor_offset[i];
501 : float corridor_length = port_depth * corridor_distance[i];
502 :
503 : float rx = s * port_depth * offset;
504 : float ry = c * port_depth * offset;
505 : float rz = corridor_length;
506 : // if there are many ships on approach, randomise coordinates a bit
507 : if ((i == corridor_count - 1) && [self countOfShipsInDockingQueue])
508 : {
509 : /* This used to try to just space the ships further out
510 : * along the 16 approach lanes - this had various problems
511 : * with putting ship coordinates on top of each other
512 : * and/or spacing them out all the way back to the
513 : * witchpoint. Instead, use a few more bits of
514 : * entityPersonalityInt to shuffle the holding coordinates
515 : * a bit more. It still doesn't guarantee two ships won't
516 : * want the same space, but it makes it considerably more
517 : * unlikely - I dropped 100 docking ships into the aegis
518 : * at once, and they all got allocated positions far
519 : * enough from the others to avoid collisions or near
520 : * misses - CIM: 27 May 2014 */
521 :
522 : int offset_id2 = ([ship entityPersonalityInt] & 0xf0)>>4; // 16 point compass
523 : int offset_id3 = ([ship entityPersonalityInt] & 0xf00)>>8; // 16 point step position
524 : float c2 = cos(offset_id2 * M_PI * ONE_EIGHTH);
525 : float s2 = sin(offset_id2 * M_PI * ONE_EIGHTH);
526 : float ssize = MAX(port_depth,1500.0);
527 : rx += c2 * ssize;
528 : ry += s2 * ssize;
529 : rz += ssize * ((float)offset_id3 / 4.0);
530 :
531 : // OOLog(@"docking.debug",@"Adjusted coordinates by %f x %f x %f",c2 * ssize,s2 * ssize,ssize * ((float)offset_id3 / 4.0));
532 : }
533 :
534 : // add the lenght inside the station to the corridor, except for the final position, inside the dock.
535 : if (corridor_distance[i] > 0) corridor_length += port_corridor;
536 :
537 : [nextCoords oo_setInteger:corridor_count - i forKey:@"docking_stage"];
538 : [nextCoords oo_setFloat:rx forKey:@"rx"];
539 : [nextCoords oo_setFloat:ry forKey:@"ry"];
540 : [nextCoords oo_setFloat:rz forKey:@"rz"];
541 : [nextCoords oo_setFloat:corridor_speed[i] forKey:@"speed"];
542 : [nextCoords oo_setFloat:corridor_range[i] forKey:@"range"];
543 :
544 : if (corridor_rotate[i])
545 : {
546 : [nextCoords setObject:@"YES" forKey:@"match_rotation"];
547 : }
548 :
549 : if (i == corridor_final_approach)
550 : {
551 : if (station == [UNIVERSE station])
552 : {
553 : [nextCoords setObject:@"[station-begin-final-aproach]" forKey:@"comms_message"];
554 : }
555 : else
556 : {
557 : [nextCoords setObject:@"[docking-begin-final-aproach]" forKey:@"comms_message"];
558 : }
559 : }
560 :
561 : [coordinatesStack addObject:nextCoords];
562 : }
563 :
564 : [shipsOnApproach setObject:coordinatesStack forKey:shipID];
565 :
566 :
567 : // COMM-CHATTER
568 : if (station == [UNIVERSE station])
569 : {
570 : [station sendExpandedMessage:@"[station-welcome]" toShip:ship];
571 : }
572 : else
573 : {
574 : [station sendExpandedMessage:@"[docking-welcome]" toShip:ship];
575 : }
576 : }
577 :
578 :
579 : - (void) noteDockingForShip:(ShipEntity *) ship
580 : {
581 : // safe to do this for now, as it just clears the ship from the docking queue
582 : [self abortDockingForShip:ship];
583 :
584 : // avoid clashes with outgoing ships
585 : last_launch_time = [UNIVERSE getTime];
586 :
587 : }
588 :
589 : - (void) abortDockingForShip:(ShipEntity *)ship
590 : {
591 : OOUniversalID ship_id = [ship universalID];
592 : NSNumber *shipID = [NSNumber numberWithUnsignedShort:ship_id];
593 :
594 : if ([shipsOnApproach objectForKey:shipID])
595 : {
596 : [shipsOnApproach removeObjectForKey:shipID];
597 : }
598 :
599 : if ([ship isPlayer])
600 : {
601 : PlayerEntity* player = PLAYER;
602 : if ([player status] == STATUS_IN_FLIGHT &&
603 : [player getDockingClearanceStatus] >= DOCKING_CLEARANCE_STATUS_REQUESTED)
604 : {
605 : if (HPmagnitude2(HPvector_subtract([player position], [self absolutePositionForSubentity])) > 2250000) // within 1500m of the dock
606 : {
607 : [[self parentEntity] sendExpandedMessage:@"[station-docking-clearance-abort-cancelled]" toShip:player];
608 : [player setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_NONE];
609 : }
610 : else
611 : {
612 : int seconds = 10; // when very close to the port, give the player a few seconds to react on the abort message.
613 : [[self parentEntity] sendExpandedMessage:OOExpandKey(@"station-docking-clearance-abort-cancelled-in-time", seconds) toShip:player];
614 : [player setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_TIMING_OUT];
615 : }
616 : }
617 : }
618 :
619 : // clear any previously owned docking stages
620 : [self clearIdLocks:ship];
621 : }
622 :
623 :
624 : - (Vector) portUpVectorForShipsBoundingBox:(BoundingBox)bb
625 : {
626 : BOOL twist = ((port_dimensions.x < port_dimensions.y) ^ (bb.max.x - bb.min.x < bb.max.y - bb.min.y));
627 :
628 : if (!twist)
629 : {
630 : return vector_up_from_quaternion(quaternion_multiply(orientation, [[self parentEntity] orientation]));
631 : }
632 : else
633 : {
634 : return vector_right_from_quaternion(quaternion_multiply(orientation, [[self parentEntity] orientation]));
635 : }
636 : }
637 :
638 :
639 : - (BOOL) shipIsInDockingQueue:(ShipEntity *)ship
640 : {
641 : if (![ship isShip]) return NO;
642 : if ([ship isPlayer] && [ship status] == STATUS_DEAD) return NO;
643 :
644 : OOUniversalID ship_id = [ship universalID];
645 : NSNumber *shipID = [NSNumber numberWithUnsignedShort:ship_id];
646 :
647 : if ([shipsOnApproach objectForKey:shipID])
648 : {
649 : return YES;
650 : }
651 : // player docking manually
652 : if ([ship isPlayer] && [[self owner] playerReservedDock] == self)
653 : {
654 : return YES;
655 : }
656 : return NO;
657 : }
658 :
659 :
660 : - (NSUInteger) countOfShipsInDockingQueue
661 : {
662 : return [shipsOnApproach count];
663 : }
664 :
665 :
666 : - (NSUInteger) countOfShipsInLaunchQueue
667 : {
668 : return [launchQueue count];
669 : }
670 :
671 :
672 : - (BOOL) shipIsInDockingCorridor:(ShipEntity *)ship
673 : {
674 : if (![ship isShip]) return NO;
675 : if ([ship isPlayer] && [ship status] == STATUS_DEAD) return NO;
676 :
677 : BOOL allow_docking_thisship = allow_docking || !disallowed_docking_collides;
678 : // ships can physically dock here, and this routine is mainly for
679 : // collision detection, but will never be directed here by traffic
680 : // control, if allow_docking is false but d_d_c is also false
681 :
682 : StationEntity *station = (StationEntity *)[self parentEntity];
683 : if ([station status] == STATUS_DEAD)
684 : {
685 : return NO;
686 : }
687 :
688 : Quaternion q0 = quaternion_multiply(orientation, [station orientation]);
689 : Vector vi = vector_right_from_quaternion(q0);
690 : Vector vj = vector_up_from_quaternion(q0);
691 : Vector vk = vector_forward_from_quaternion(q0);
692 :
693 : HPVector port_pos = [self absolutePositionForSubentity];
694 :
695 : BoundingBox shipbb = [ship boundingBox];
696 : BoundingBox arbb = [ship findBoundingBoxRelativeToPosition: port_pos InVectors: vi : vj : vk];
697 :
698 : // port dimensions..
699 : GLfloat ww = port_dimensions.x;
700 : GLfloat hh = port_dimensions.y;
701 : GLfloat dd = port_dimensions.z;
702 :
703 : BOOL rotatedPort = (ww >= hh) ? NO : YES;
704 : BOOL rotatedShip = ((shipbb.max.x - shipbb.min.x) >= (shipbb.max.y - shipbb.min.y)) ? NO : YES;
705 : BOOL rotationsMatch = (rotatedShip == rotatedPort);
706 : if (rotationsMatch)
707 : {
708 : // the ship and port are both bigger in x than y
709 : while (shipbb.max.x - shipbb.min.x > ww * 0.90) ww *= 1.25;
710 : while (shipbb.max.y - shipbb.min.y > hh * 0.90) hh *= 1.25;
711 : }
712 : else
713 : {
714 : // the ship and port have different x/y biggerness
715 : while (shipbb.max.y - shipbb.min.y > ww * 0.90) ww *= 1.25;
716 : while (shipbb.max.x - shipbb.min.x > hh * 0.90) hh *= 1.25;
717 : }
718 :
719 : ww *= 0.5;
720 : hh *= 0.5;
721 :
722 : #ifndef NDEBUG
723 : if ([ship isPlayer] && (gDebugFlags & DEBUG_DOCKING))
724 : {
725 : BOOL inLane;
726 : float range;
727 : unsigned laneFlags = 0;
728 :
729 : if (arbb.max.x < ww) laneFlags |= 1;
730 : if (arbb.min.x > -ww) laneFlags |= 2;
731 : if (arbb.max.y < hh) laneFlags |= 4;
732 : if (arbb.min.y > -hh) laneFlags |= 8;
733 : inLane = laneFlags == 0xF;
734 : range = 0.90 * arbb.max.z + 0.10 * arbb.min.z;
735 :
736 : OOLog(@"docking.debug", @"Normalised port dimensions are %g x %g x %g. Player bounding box is at %@-%@ -- %s (%X), range: %g",
737 : ww * 2.0, hh * 2.0, dd,
738 : VectorDescription(arbb.min), VectorDescription(arbb.max),
739 : inLane ? "in lane" : "out of lane", laneFlags,
740 : range);
741 : }
742 : #endif
743 :
744 : if ((arbb.max.x < ww * 3.0)&&(arbb.min.x > -ww * 3.0)&&(arbb.max.y < hh * 3.0)&&(arbb.min.y > -hh * 3.0))
745 : {
746 : if ([station requiresDockingClearance] && [ship isPlayer] && [ship status] != STATUS_LAUNCHING && [ship status] != STATUS_AUTOPILOT_ENGAGED && [PLAYER getDockingClearanceStatus] < DOCKING_CLEARANCE_STATUS_GRANTED)
747 : {
748 : if ((0.90 * arbb.max.z + 0.10 * arbb.min.z < 3000) && (dot_product(vk,[ship forwardVector]) < -0.9))
749 : {
750 : // player is in docking corridor and facing dock
751 : // and within 3km
752 : [UNIVERSE addMessage:DESC(@"oolite-station-docking-requires-clearance") forCount:3];
753 : }
754 : }
755 : }
756 :
757 : if (arbb.max.z < -dd)
758 : {
759 : return NO;
760 : }
761 :
762 : if ((arbb.max.x < ww)&&(arbb.min.x > -ww)&&(arbb.max.y < hh)&&(arbb.min.y > -hh))
763 : {
764 : if ([ship status] != STATUS_LAUNCHING && !allow_docking_thisship)
765 : { // launch-only dock: will collide!
766 : if (arbb.min.z < dd)
767 : {
768 : [ship takeScrapeDamage: 5 * [UNIVERSE getTimeDelta]*[ship flightSpeed] from:station];
769 : // and bounce
770 : HPVector rel = HPvector_subtract([ship position],port_pos);
771 : rel = HPvector_multiply_scalar(HPvector_normal(rel),[ship flightSpeed]*0.4);
772 : [ship adjustVelocity:HPVectorToVector(rel)];
773 : }
774 :
775 : if (arbb.max.z < 0.0)
776 : { // give some warning before exploding...
777 : return NO;
778 : }
779 : }
780 :
781 : // in lane
782 : if (0.90 * arbb.max.z + 0.10 * arbb.min.z < 0.0) // we're 90% in docking position!
783 : {
784 : [self pullInShipIfPermitted:ship];
785 : }
786 : return YES;
787 : }
788 :
789 : if ([ship status] == STATUS_LAUNCHING)
790 : {
791 : return YES;
792 : }
793 :
794 : // if close enough (within 50%) correct and add damage
795 : //
796 : GLfloat safety = 1.0+(50.0/100.0);
797 :
798 : if ((arbb.min.x > -safety * ww)&&(arbb.max.x < safety * ww)&&(arbb.min.y > -safety * hh)&&(arbb.max.y < safety * hh))
799 : {
800 : if (arbb.min.z < 0.0) // got our nose inside
801 : {
802 :
803 : if ((arbb.min.x < -ww && arbb.max.x > ww) || (arbb.min.y < -hh && arbb.max.y > hh))
804 : {
805 : /* No matter how much safety margin there is, if the
806 : * ship is going off opposite edges of the dock at
807 : * once, that's a fatal collision */
808 : return NO;
809 : }
810 :
811 : GLfloat correction_factor = -arbb.min.z / (arbb.max.z - arbb.min.z); // proportion of ship inside
812 :
813 : // damage the ship according to velocity - don't send collision messages to AIs to avoid problems.
814 : [ship takeScrapeDamage: 5 * [UNIVERSE getTimeDelta]*[ship flightSpeed] from:station];
815 : [station doScriptEvent:OOJSID("shipCollided") withArgument:ship]; // no COLLISION message to station AI, carriers would move away!
816 : [ship doScriptEvent:OOJSID("shipCollided") withArgument:station]; // no COLLISION message to ship AI, dockingAI.plist would abort.
817 :
818 : Vector delta;
819 : delta.x = 0.5f * (arbb.max.x + arbb.min.x) * correction_factor;
820 : delta.y = 0.5f * (arbb.max.y + arbb.min.y) * correction_factor;
821 :
822 : if (arbb.max.x < ww && arbb.min.x > -ww)
823 : {
824 : // x is okay - no need to correct
825 : delta.x = 0.0f;
826 : }
827 : if (arbb.max.y > hh && arbb.min.y > -hh)
828 : {
829 : // y is okay - no need to correct
830 : delta.y = 0.0f;
831 : }
832 :
833 : // adjust the ship back to the center of the port
834 : HPVector pos = [ship position];
835 : pos.x -= delta.y * vj.x + delta.x * vi.x;
836 : pos.y -= delta.y * vj.y + delta.x * vi.y;
837 : pos.z -= delta.y * vj.z + delta.x * vi.z;
838 : [ship setPosition:pos];
839 : }
840 :
841 : // if far enough in - dock
842 : if (0.90f * arbb.max.z + 0.10f * arbb.min.z < 0.0f)
843 : {
844 : [self pullInShipIfPermitted:ship];
845 : }
846 :
847 : return YES; // okay NOW we're in the docking corridor!
848 : }
849 :
850 : return NO;
851 : }
852 :
853 :
854 0 : - (void) pullInShipIfPermitted:(ShipEntity *)ship
855 : {
856 : // allow_docking: docking permitted and expected
857 : // disallowed_docking_collides: unauthorised docking does not result in explosion
858 : if (allow_docking || !disallowed_docking_collides)
859 : {
860 : [ship enterDock:(StationEntity*)[self parentEntity]];
861 : }
862 : }
863 :
864 :
865 : - (void) addShipToLaunchQueue:(ShipEntity *)ship withPriority:(BOOL)priority
866 : {
867 : [self pruneAndCountShipsOnApproach];
868 :
869 : if (ship == nil) return;
870 :
871 : if (launchQueue == nil)
872 : {
873 : launchQueue = [[NSMutableArray alloc] init]; // retained
874 : }
875 :
876 : [ship setStatus:STATUS_DOCKED];
877 : if (priority)
878 : {
879 : [launchQueue insertObject:ship atIndex:0];
880 : }
881 : else
882 : {
883 : [launchQueue addObject:ship];
884 : }
885 : }
886 :
887 :
888 : - (void) launchShip:(ShipEntity *) ship
889 : {
890 : if (![ship isShip]) return;
891 :
892 : BoundingBox bb = [ship boundingBox];
893 : StationEntity *station = (StationEntity *)[self parentEntity];
894 :
895 : HPVector launchPos = [self absolutePositionForSubentity];
896 : Vector launchVel = [station velocity];
897 : double launchSpeed = 0.5 * [ship maxFlightSpeed];
898 : if ([station maxFlightSpeed] > 0 && [station flightSpeed] > 0) // is self a carrier in flight.
899 : {
900 : launchSpeed = 0.5 * [ship maxFlightSpeed] * (1.0 + [station flightSpeed]/[station maxFlightSpeed]);
901 : }
902 : Quaternion q1 = [station orientation];
903 : q1 = quaternion_multiply(orientation, q1);
904 : Vector launchVector = vector_forward_from_quaternion(q1);
905 :
906 : // launch orientation
907 : if ((port_dimensions.x < port_dimensions.y) ^ (bb.max.x - bb.min.x < bb.max.y - bb.min.y))
908 : {
909 : quaternion_rotate_about_axis(&q1, launchVector, M_PI*0.5); // to account for the slot being at 90 degrees to vertical
910 : }
911 : [ship setNormalOrientation:q1];
912 : // launch position
913 : [ship setPosition:launchPos];
914 : if([ship pendingEscortCount] > 0) [ship setPendingEscortCount:0]; // Make sure no extra escorts are added after launch. (e.g. for miners etc.)
915 : if ([ship hasEscorts]) no_docking_while_launching = YES;
916 : // launch speed
917 : launchVel = vector_add(launchVel, vector_multiply_scalar(launchVector, launchSpeed));
918 : launchSpeed = magnitude(launchVel);
919 : [ship setSpeed:launchSpeed];
920 : [ship setVelocity:launchVel];
921 : // launch roll/pitch
922 : [ship setRoll:[station flightRoll]];
923 : [ship setPitch:0.0];
924 : [ship setYaw:0.0];
925 : [UNIVERSE addEntity:ship];
926 : [ship setStatus: STATUS_LAUNCHING];
927 : [ship setDesiredSpeed:launchSpeed]; // must be set after initialising the AI to correct any speed set by AI
928 : last_launch_time = [UNIVERSE getTime];
929 : double delay = (port_corridor + 2 * port_dimensions.z)/launchSpeed; // pause until 2 portlengths outside of the station.
930 : [ship setLaunchDelay:delay];
931 : [[ship getAI] setNextThinkTime:last_launch_time + delay]; // pause while launching
932 :
933 : [ship resetExhaustPlumes]; // resets stuff for tracking/exhausts
934 :
935 : [ship doScriptEvent:OOJSID("shipWillLaunchFromStation") withArgument:station];
936 : [station doScriptEvent:OOJSID("stationLaunchedShip") withArgument:ship andReactToAIMessage: @"STATION_LAUNCHED_SHIP"];
937 : }
938 :
939 :
940 : - (NSUInteger) countOfShipsInLaunchQueueWithPrimaryRole:(NSString *)role
941 : {
942 : NSUInteger count = 0;
943 : ShipEntity *ship = nil;
944 : foreach (ship, launchQueue)
945 : {
946 : if ([ship hasPrimaryRole:role]) count++;
947 : }
948 : return count;
949 : }
950 :
951 :
952 : - (BOOL) allowsLaunchingOf:(ShipEntity *) ship
953 : {
954 : if (![ship isShip]) return NO;
955 :
956 : BoundingBox bb = [ship totalBoundingBox];
957 : if ((port_dimensions.x < (bb.max.x - bb.min.x) || port_dimensions.y < (bb.max.y - bb.min.y)) &&
958 : (port_dimensions.y < (bb.max.x - bb.min.x) || port_dimensions.x < (bb.max.y - bb.min.y)) && ![ship isPlayer])
959 : {
960 : return NO;
961 : }
962 :
963 : // callback to allow more complex filtering on accept/reject
964 : JSContext *context = OOJSAcquireContext();
965 : jsval rval = JSVAL_VOID;
966 : jsval args[] = { OOJSValueFromNativeObject(context, ship) };
967 : JSBool accept = YES;
968 :
969 : BOOL OK = [[self script] callMethod:OOJSID("acceptLaunchingRequestFrom") inContext:context withArguments:args count:1 result:&rval];
970 : if (OK) OK = JS_ValueToBoolean(context, rval, &accept);
971 : if (!OK) accept = YES; // default to permreject
972 : OOJSRelinquishContext(context);
973 : if (!accept)
974 : {
975 : return NO;
976 : }
977 :
978 : return YES;
979 : }
980 :
981 :
982 : - (void) clear
983 : {
984 : [launchQueue removeAllObjects];
985 : [shipsOnApproach removeAllObjects];
986 : }
987 :
988 :
989 : - (BOOL) dockingCorridorIsEmpty
990 : {
991 : double unitime = [UNIVERSE getTime];
992 :
993 : if (unitime < last_launch_time + STATION_DELAY_BETWEEN_LAUNCHES)
994 : {
995 : // leave sufficient pause between launches
996 : return NO;
997 : }
998 :
999 :
1000 : StationEntity *station = (StationEntity *)[self parentEntity];
1001 : if ([station playerReservedDock] == self)
1002 : {
1003 : // player probably will not appreciate a ship launch right now
1004 : return NO;
1005 : }
1006 :
1007 : // check against all ships
1008 : BOOL isEmpty = YES;
1009 : int ent_count = UNIVERSE->n_entities;
1010 : Entity **uni_entities = UNIVERSE->sortedEntities; // grab the public sorted list
1011 : Entity *my_entities[ent_count];
1012 : int i;
1013 : int ship_count = 0;
1014 :
1015 : for (i = 0; i < ent_count; i++)
1016 : {
1017 : //on red alert, launch even if the player is trying block the corridor. Ignore cargopods or other small debris.
1018 : if ([uni_entities[i] isShip] && ([station alertLevel] < STATION_ALERT_LEVEL_RED || ![uni_entities[i] isPlayer]) && [uni_entities[i] mass] > 1000)
1019 : {
1020 : my_entities[ship_count++] = [uni_entities[i] retain]; // retained
1021 : }
1022 : }
1023 :
1024 : for (i = 0; (i < ship_count)&&(isEmpty); i++)
1025 : {
1026 : ShipEntity* ship = (ShipEntity*)my_entities[i];
1027 : double d2 = HPdistance2([station position], [ship position]);
1028 : if ((ship != station) && (d2 < 25000000)&&([ship status] != STATUS_DOCKED)) // within 5km
1029 : {
1030 : HPVector ppos = [self absolutePositionForSubentity];
1031 : d2 = HPdistance2(ppos, ship->position);
1032 : if (d2 < 4000000) // within 2km of the port entrance
1033 : {
1034 : Quaternion q1 = [station orientation];
1035 : q1 = quaternion_multiply([self orientation], q1);
1036 : //
1037 : HPVector v_out = HPvector_forward_from_quaternion(q1);
1038 : HPVector r_pos = make_HPvector(ship->position.x - ppos.x, ship->position.y - ppos.y, ship->position.z - ppos.z);
1039 : if (r_pos.x||r_pos.y||r_pos.z)
1040 : r_pos = HPvector_normal(r_pos);
1041 : else
1042 : r_pos.z = 1.0;
1043 : //
1044 : double vdp = HPdot_product(v_out, r_pos); //== cos of the angle between r_pos and v_out
1045 : //
1046 : if (vdp > 0.86)
1047 : {
1048 : isEmpty = NO;
1049 : last_launch_time = unitime - STATION_DELAY_BETWEEN_LAUNCHES + STATION_LAUNCH_RETRY_INTERVAL;
1050 : }
1051 : }
1052 : }
1053 : }
1054 :
1055 : for (i = 0; i < ship_count; i++)
1056 : {
1057 : [my_entities[i] release]; //released
1058 : }
1059 :
1060 : return isEmpty;
1061 : }
1062 :
1063 :
1064 : - (void) clearDockingCorridor
1065 : {
1066 : // check against all ships
1067 : StationEntity *station = (StationEntity *)[self parentEntity];
1068 : BOOL isClear = YES;
1069 : int ent_count = UNIVERSE->n_entities;
1070 : Entity **uni_entities = UNIVERSE->sortedEntities; // grab the public sorted list
1071 : Entity *my_entities[ent_count];
1072 : int i;
1073 : int ship_count = 0;
1074 :
1075 : for (i = 0; i < ent_count; i++)
1076 : {
1077 : if (uni_entities[i]->isShip)
1078 : {
1079 : my_entities[ship_count++] = [uni_entities[i] retain]; // retained
1080 : }
1081 : }
1082 :
1083 : for (i = 0; i < ship_count; i++)
1084 : {
1085 : ShipEntity *ship = (ShipEntity*)my_entities[i];
1086 : double d2 = HPdistance2([station position], [ship position]);
1087 : if ((ship != station)&&(d2 < 25000000)&&([ship status] != STATUS_DOCKED)) // within 5km
1088 : {
1089 : HPVector ppos = [self absolutePositionForSubentity];
1090 : float time_out = -15.00; // 15 secs
1091 : do
1092 : {
1093 : isClear = YES;
1094 : d2 = HPdistance2(ppos, ship->position);
1095 : if (d2 < 4000000) // within 2km of the port entrance
1096 : {
1097 : Quaternion q1 = [station orientation];
1098 : q1 = quaternion_multiply([self orientation], q1);
1099 : //
1100 : Vector v_out = vector_forward_from_quaternion(q1);
1101 : Vector r_pos = make_vector(ship->position.x - ppos.x, ship->position.y - ppos.y, ship->position.z - ppos.z);
1102 : if (r_pos.x||r_pos.y||r_pos.z)
1103 : r_pos = vector_normal(r_pos);
1104 : else
1105 : r_pos.z = 1.0;
1106 : //
1107 : float vdp = dot_product(v_out, r_pos); //== cos of the angle between r_pos and v_out
1108 : //
1109 : if (vdp > 0.86f)
1110 : {
1111 : isClear = NO;
1112 :
1113 : // okay it's in the way .. give it a wee nudge (0.25s)
1114 : [ship update: 0.25];
1115 : time_out += 0.25;
1116 : }
1117 : if (time_out > 0)
1118 : {
1119 : HPVector v1 = HPvector_forward_from_quaternion(orientation);
1120 : HPVector spos = ship->position;
1121 : spos.x += 3000.0 * v1.x; spos.y += 3000.0 * v1.y; spos.z += 3000.0 * v1.z;
1122 : [ship setPosition:spos]; // move 3km out of the way
1123 : }
1124 : }
1125 : } while (!isClear);
1126 : }
1127 : }
1128 :
1129 : for (i = 0; i < ship_count; i++)
1130 : {
1131 : [my_entities[i] release]; //released
1132 : }
1133 :
1134 : }
1135 :
1136 :
1137 : - (void)setDimensionsAndCorridor:(BOOL)docking :(BOOL)ddc :(BOOL)launching
1138 : {
1139 : StationEntity *station = (StationEntity*)[self parentEntity];
1140 : if (virtual_dock)
1141 : {
1142 : port_dimensions = [station virtualPortDimensions];
1143 : }
1144 : else
1145 : {
1146 : BoundingBox bb = [self boundingBox];
1147 : port_dimensions = make_vector(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z);
1148 : }
1149 :
1150 : HPVector vk = HPvector_forward_from_quaternion(orientation);
1151 :
1152 : BoundingBox stbb = [station boundingBox];
1153 : HPVector start = position;
1154 : while ((start.x > stbb.min.x)&&(start.x < stbb.max.x) &&
1155 : (start.y > stbb.min.y)&&(start.y < stbb.max.y) &&
1156 : (start.z > stbb.min.z)&&(start.z < stbb.max.z) )
1157 : {
1158 : start = HPvector_add(start, HPvector_multiply_scalar(vk, port_dimensions.z));
1159 : }
1160 : port_corridor = start.z - position.z;
1161 :
1162 : allow_docking = docking;
1163 : disallowed_docking_collides = ddc;
1164 : allow_launching = launching;
1165 : }
1166 :
1167 :
1168 :
1169 : //////////////////////////////////////////////// from superclass
1170 :
1171 0 : - (BOOL) isDock
1172 : {
1173 : return YES;
1174 : }
1175 :
1176 :
1177 0 : - (id)initWithKey:(NSString *)key definition:(NSDictionary *)dict
1178 : {
1179 : OOJS_PROFILE_ENTER
1180 :
1181 : self = [super initWithKey:key definition:dict];
1182 : if (self != nil)
1183 : {
1184 : shipsOnApproach = [[NSMutableDictionary alloc] init];
1185 : launchQueue = [[NSMutableArray alloc] init];
1186 : allow_docking = YES;
1187 : disallowed_docking_collides = NO;
1188 : allow_launching = YES;
1189 : virtual_dock = NO;
1190 : }
1191 :
1192 : return self;
1193 :
1194 : OOJS_PROFILE_EXIT
1195 : }
1196 :
1197 :
1198 0 : - (void) dealloc
1199 : {
1200 : DESTROY(shipsOnApproach);
1201 : DESTROY(launchQueue);
1202 : [self clearIdLocks:nil];
1203 :
1204 : [super dealloc];
1205 : }
1206 :
1207 :
1208 0 : - (void) clearIdLocks:(ShipEntity *)ship
1209 : {
1210 : int i;
1211 : for (i = 1; i < MAX_DOCKING_STAGES; i++)
1212 : {
1213 : if (ship == nil || ship == [id_lock[i] weakRefUnderlyingObject])
1214 : {
1215 : DESTROY(id_lock[i]);
1216 : }
1217 : }
1218 : }
1219 :
1220 :
1221 0 : - (void) clearAllIdLocks
1222 : {
1223 : int i;
1224 : for (i = 1; i < MAX_DOCKING_STAGES; i++)
1225 : {
1226 : DESTROY(id_lock[i]);
1227 : }
1228 : }
1229 :
1230 :
1231 0 : - (BOOL) setUpShipFromDictionary:(NSDictionary *) dict
1232 : {
1233 : OOJS_PROFILE_ENTER
1234 :
1235 : isShip = YES;
1236 : isStation = NO;
1237 :
1238 : if (![super setUpShipFromDictionary:dict]) return NO;
1239 :
1240 : return YES;
1241 :
1242 : OOJS_PROFILE_EXIT
1243 : }
1244 :
1245 :
1246 0 : - (void) update:(OOTimeDelta) delta_t
1247 : {
1248 : [super update:delta_t];
1249 :
1250 : if (([launchQueue count] > 0)&&([shipsOnApproach count] == 0)&&[self dockingCorridorIsEmpty])
1251 : {
1252 : ShipEntity *se=(ShipEntity *)[launchQueue objectAtIndex:0];
1253 : // check to make sure ship has not been destroyed in queue by script
1254 : if ([se status] == STATUS_DOCKED)
1255 : {
1256 : [self launchShip:se];
1257 : }
1258 : [launchQueue removeObjectAtIndex:0];
1259 : }
1260 : if (([launchQueue count] == 0) && no_docking_while_launching)
1261 : {
1262 : no_docking_while_launching = NO; // launching complete
1263 : }
1264 :
1265 : }
1266 :
1267 :
1268 : // avoid possibility of shooting the virtual dock damaging the station
1269 0 : - (void) noteTakingDamage:(double)amount from:(Entity *)entity type:(OOShipDamageType)type
1270 : {
1271 : if (virtual_dock) // can't be damaged
1272 : {
1273 : return;
1274 : }
1275 : [super noteTakingDamage:amount from:entity type:type];
1276 : }
1277 :
1278 :
1279 0 : - (void) takeEnergyDamage:(double)amount from:(Entity *)ent becauseOf:(Entity *)other weaponIdentifier:(NSString *)weaponIdentifier
1280 : {
1281 : if (virtual_dock) // can't be damaged
1282 : {
1283 : return;
1284 : }
1285 : [super takeEnergyDamage:amount from:ent becauseOf:other weaponIdentifier:weaponIdentifier];
1286 : }
1287 :
1288 :
1289 : // virtual docks are invisible
1290 0 : - (void) drawImmediate:(bool)immediate translucent:(bool)translucent
1291 : {
1292 : if (virtual_dock) // not drawn
1293 : {
1294 : return;
1295 : }
1296 : [super drawImmediate:immediate translucent:translucent];
1297 : }
1298 :
1299 : @end
|