Oolite 1.91.0.7604-240417-a536cbe
Loading...
Searching...
No Matches
DockEntity.m
Go to the documentation of this file.
1/*
2
3DockEntity.m
4
5Oolite
6Copyright (C) 2004-2013 Giles C Williams and contributors
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21MA 02110-1301, USA.
22
23*/
24
25#import "DockEntity.h"
26#import "StationEntity.h"
27#import "ShipEntityAI.h"
29#import "OOStringParsing.h"
30#import "OOStringExpander.h"
31
32#import "Universe.h"
33#import "HeadUpDisplay.h"
34
37#import "OOPlanetEntity.h"
38#import "OOShipGroup.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- (void) clearIdLocks:(ShipEntity *)ship;
52- (void) clearAllIdLocks;
53- (void) autoDockShipsInQueue:(NSMutableDictionary *)queue;
54- (void) addShipToShipsOnApproach:(ShipEntity *)ship;
55- (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- (void) abortAllLaunches
144{
145 no_docking_while_launching = NO;
146 [launchQueue removeAllObjects];
147}
148
149
150- (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- (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- (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;
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
1170
1171- (BOOL) isDock
1172{
1173 return YES;
1174}
1175
1176
1177- (id)initWithKey:(NSString *)key definition:(NSDictionary *)dict
1178{
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
1195}
1196
1197
1198- (void) dealloc
1199{
1200 DESTROY(shipsOnApproach);
1201 DESTROY(launchQueue);
1202 [self clearIdLocks:nil];
1203
1204 [super dealloc];
1205}
1206
1207
1208- (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- (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- (BOOL) setUpShipFromDictionary:(NSDictionary *) dict
1232{
1234
1235 isShip = YES;
1236 isStation = NO;
1237
1238 if (![super setUpShipFromDictionary:dict]) return NO;
1239
1240 return YES;
1241
1243}
1244
1245
1246- (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- (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- (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- (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
NSUInteger gDebugFlags
Definition main.m:7
#define SCANNER_MAX_RANGE
Definition Entity.h:51
#define SCANNER_MAX_RANGE2
Definition Entity.h:52
#define ONE_EIGHTH
#define DESTROY(x)
Definition OOCocoa.h:77
@ DEBUG_DOCKING
Definition OODebugFlags.h:8
#define OOJS_PROFILE_EXIT
#define OOJS_PROFILE_ENTER
OOINLINE jsval OOJSValueFromNativeObject(JSContext *context, id object)
OOINLINE JSContext * OOJSAcquireContext(void)
OOINLINE void OOJSRelinquishContext(JSContext *context)
#define OOLogERR(class, format,...)
Definition OOLogging.h:112
#define OOLog(class, format,...)
Definition OOLogging.h:88
#define MAX(A, B)
Definition OOMaths.h:114
#define M_PI
Definition OOMaths.h:73
unsigned count
return nil
Vector vector_up_from_quaternion(Quaternion quat)
HPVector HPvector_forward_from_quaternion(Quaternion quat)
Vector vector_right_from_quaternion(Quaternion quat)
Vector vector_forward_from_quaternion(Quaternion quat)
void quaternion_rotate_about_axis(Quaternion *quat, Vector axis, OOScalar angle)
Quaternion quaternion_multiply(Quaternion q1, Quaternion q2)
float y
float x
uint16_t OOUniversalID
Definition OOTypes.h:189
double OOTimeDelta
Definition OOTypes.h:224
@ DOCKING_CLEARANCE_STATUS_GRANTED
Definition OOTypes.h:171
@ DOCKING_CLEARANCE_STATUS_REQUESTED
Definition OOTypes.h:170
#define PLAYER
OOShipDamageType
Definition ShipEntity.h:183
#define STATION_LAUNCH_RETRY_INTERVAL
NSDictionary * OOMakeDockingInstructions(StationEntity *station, HPVector coords, float speed, float range, NSString *ai_message, NSString *comms_message, BOOL match_rotation, int docking_stage)
@ STATION_ALERT_LEVEL_RED
#define MAX_DOCKING_STAGES
#define STATION_DELAY_BETWEEN_LAUNCHES
#define UNIVERSE
Definition Universe.h:833
void setNextThinkTime:(OOTimeAbsolute ntt)
Definition AI.m:658
void message:(NSString *ms)
Definition AI.m:600
GLfloat collision_radius
Definition Entity.h:111
OOUniversalID universalID
Definition Entity.h:89
void setVelocity:(Vector vel)
Definition Entity.m:757
Quaternion orientation
Definition Entity.h:114
void setNormalOrientation:(Quaternion quat)
Definition Entity.m:744
BoundingBox boundingBox
Definition Entity.h:145
HPVector position
Definition Entity.h:112
void setPosition:(HPVector posn)
Definition Entity.m:647
StationEntity * getTargetDockStation()
void setDockingClearanceStatus:(OODockingClearanceStatus newValue)
void doScriptEvent:withArgument:andReactToAIMessage:(jsid scriptEvent,[withArgument] id argument,[andReactToAIMessage] NSString *aiMessage)
void setDesiredSpeed:(double amount)
void setStatus:(OOEntityStatus stat)
void setRoll:(double amount)
void setSpeed:(double amount)
void takeScrapeDamage:from:(double amount,[from] Entity *ent)
void doScriptEvent:(jsid message)
GLint entityPersonalityInt()
GLfloat flightSpeed
Definition ShipEntity.h:368
BoundingBox totalBoundingBox
Definition ShipEntity.h:213
void resetExhaustPlumes()
void setPitch:(double amount)
void setYaw:(double amount)
void setLaunchDelay:(double delay)
void adjustVelocity:(Vector xVel)
void setPendingEscortCount:(uint8_t count)
void sendExpandedMessage:toShip:(NSString *message_text,[toShip] ShipEntity *other_ship)
Vector velocity()
void update:(OOTimeDelta delta_t)
void doScriptEvent:withArgument:(jsid message,[withArgument] id argument)
void sendAIMessage:(NSString *message)
GLfloat flightRoll
Definition ShipEntity.h:369
GLfloat maxFlightSpeed
Definition ShipEntity.h:239
void enterDock:(StationEntity *station)
Vector virtualPortDimensions()
voidpf uLong offset
Definition ioapi.h:140