1 /*
2 * Created by Ian "Goober5000" Warfield for the FreeSpace2 Source Code Project.
3 * You may not sell or otherwise commercially exploit the source or things you
4 * create based on the source.
5 */
6
7
8
9 #include "globalincs/pstypes.h"
10 #include "object/objectdock.h"
11 #include "object/object.h"
12 #include "math/bitarray.h"
13 #include "ship/ship.h"
14 #include "math/vecmat.h"
15 #include "mission/missionparse.h"
16
17
18
19
20 // helper prototypes
21
22 void dock_evaluate_tree(object *objp, dock_function_info *infop, void (*function)(object *, dock_function_info *), ubyte *visited_bitstring);
23 void dock_move_docked_children_tree(object *objp, object *parent_objp);
24 void dock_count_total_docked_objects_helper(object *objp, dock_function_info *infop);
25 void dock_check_find_docked_object_helper(object *objp, dock_function_info *infop);
26 void dock_calc_docked_center_helper(object *objp, dock_function_info *infop);
27 void dock_calc_docked_center_of_mass_helper(object *objp, dock_function_info *infop);
28 void dock_calc_total_docked_mass_helper(object *objp, dock_function_info *infop);
29 void dock_calc_max_cross_sectional_radius_squared_perpendicular_to_line_helper(object *objp, dock_function_info *infop);
30 void dock_calc_max_semilatus_rectum_squared_parallel_to_directrix_helper(object *objp, dock_function_info *infop);
31 void dock_find_max_speed_helper(object *objp, dock_function_info *infop);
32 void dock_find_max_fspeed_helper(object *objp, dock_function_info *infop);
33
34
35 // management prototypes
36
37 bool dock_check_assume_hub();
38 object *dock_get_hub(object *objp);
39
40 void dock_add_instance(object *objp, int dockpoint, object *other_objp);
41 void dock_remove_instance(object *objp, object *other_objp);
42 dock_instance *dock_find_instance(object *objp, object *other_objp);
43 dock_instance *dock_find_instance(object *objp, int dockpoint);
44 int dock_count_instances(object *objp);
45
46
47
dock_get_first_docked_object(object * objp)48 object *dock_get_first_docked_object(object *objp)
49 {
50 // are we docked?
51 if (!object_is_docked(objp))
52 return NULL;
53
54 return objp->dock_list->docked_objp;
55 }
56
dock_check_docked_one_on_one(object * objp)57 bool dock_check_docked_one_on_one(object *objp)
58 {
59 // we must be docked
60 if (!object_is_docked(objp))
61 return false;
62
63 // our dock list must contain only one object
64 if (objp->dock_list->next != NULL)
65 return false;
66
67 // the other guy's dock list must contain only one object
68 if (dock_get_first_docked_object(objp)->dock_list->next != NULL)
69 return false;
70
71 // debug check to make sure that we're docked to each other
72 Assert(objp == dock_get_first_docked_object(objp)->dock_list->docked_objp);
73
74 // success
75 return true;
76 }
77
dock_count_direct_docked_objects(object * objp)78 int dock_count_direct_docked_objects(object *objp)
79 {
80 return dock_count_instances(objp);
81 }
82
dock_count_total_docked_objects(object * objp)83 int dock_count_total_docked_objects(object *objp)
84 {
85 dock_function_info dfi;
86
87 dock_evaluate_all_docked_objects(objp, &dfi, dock_count_total_docked_objects_helper);
88
89 return dfi.maintained_variables.int_value;
90 }
91
dock_check_find_direct_docked_object(object * objp,object * other_objp)92 bool dock_check_find_direct_docked_object(object *objp, object *other_objp)
93 {
94 return (dock_find_instance(objp, other_objp) != NULL);
95 }
96
dock_check_find_docked_object(object * objp,object * other_objp)97 bool dock_check_find_docked_object(object *objp, object *other_objp)
98 {
99 dock_function_info dfi;
100 dfi.parameter_variables.objp_value = other_objp;
101
102 dock_evaluate_all_docked_objects(objp, &dfi, dock_check_find_docked_object_helper);
103
104 return dfi.maintained_variables.bool_value;
105 }
106
dock_find_object_at_dockpoint(object * objp,int dockpoint)107 object *dock_find_object_at_dockpoint(object *objp, int dockpoint)
108 {
109 dock_instance *result = dock_find_instance(objp, dockpoint);
110
111 if (result == NULL)
112 return NULL;
113 else
114 return result->docked_objp;
115 }
116
dock_find_dockpoint_used_by_object(object * objp,object * other_objp)117 int dock_find_dockpoint_used_by_object(object *objp, object *other_objp)
118 {
119 dock_instance *result = dock_find_instance(objp, other_objp);
120
121 if (result == NULL)
122 return -1;
123 else
124 return result->dockpoint_used;
125 }
126
dock_calc_docked_center(vec3d * dest,object * objp)127 void dock_calc_docked_center(vec3d *dest, object *objp)
128 {
129 vm_vec_zero(dest);
130
131 dock_function_info dfi;
132 dfi.maintained_variables.vecp_value = dest;
133
134 dock_evaluate_all_docked_objects(objp, &dfi, dock_calc_docked_center_helper);
135
136 // overall center = sum of centers divided by sum of objects
137 vm_vec_scale(dest, (1.0f / (float) dfi.maintained_variables.int_value));
138 }
139
dock_calc_docked_center_of_mass(vec3d * dest,object * objp)140 void dock_calc_docked_center_of_mass(vec3d *dest, object *objp)
141 {
142 vm_vec_zero(dest);
143
144 dock_function_info dfi;
145 dfi.maintained_variables.vecp_value = dest;
146
147 dock_evaluate_all_docked_objects(objp, &dfi, dock_calc_docked_center_of_mass_helper);
148
149 // overall center of mass = weighted sum of centers of mass divided by total mass
150 vm_vec_scale(dest, (1.0f / dfi.maintained_variables.float_value));
151 }
152
dock_calc_total_docked_mass(object * objp)153 float dock_calc_total_docked_mass(object *objp)
154 {
155 dock_function_info dfi;
156
157 dock_evaluate_all_docked_objects(objp, &dfi, dock_calc_total_docked_mass_helper);
158
159 return dfi.maintained_variables.float_value;
160 }
161
dock_calc_max_cross_sectional_radius_perpendicular_to_axis(object * objp,axis_type axis)162 float dock_calc_max_cross_sectional_radius_perpendicular_to_axis(object *objp, axis_type axis)
163 {
164 vec3d local_line_end;
165 vec3d *world_line_start, world_line_end;
166 dock_function_info dfi;
167
168 // to calculate the cross-sectional radius, we need a line that will be perpendicular to the cross-section
169
170 // the first endpoint is simply the position of the object
171 world_line_start = &objp->pos;
172
173 // the second endpoint extends in the axis direction
174 vm_vec_zero(&local_line_end);
175 switch(axis)
176 {
177 case X_AXIS:
178 local_line_end.xyz.x = 1.0f;
179 break;
180
181 case Y_AXIS:
182 local_line_end.xyz.y = 1.0f;
183 break;
184
185 case Z_AXIS:
186 local_line_end.xyz.z = 1.0f;
187 break;
188
189 default:
190 Int3();
191 return 0.0f;
192 }
193
194 // rotate and move the endpoint to go through the axis of the actual object
195 vm_vec_rotate(&world_line_end, &local_line_end, &objp->orient);
196 vm_vec_add2(&world_line_end, &objp->pos);
197
198 // now we have a unit vector starting at the object's position and pointing along the chosen axis
199 // (although the length doesn't matter, as it's calculated as an endless line)
200
201 // now determine the cross-sectional radius
202
203 // set parameters and call function for the radius squared
204 dfi.parameter_variables.vecp_value = world_line_start;
205 dfi.parameter_variables.vecp_value2 = &world_line_end;
206 dock_evaluate_all_docked_objects(objp, &dfi, dock_calc_max_cross_sectional_radius_squared_perpendicular_to_line_helper);
207
208 // the radius is the square root of our result
209 return fl_sqrt(dfi.maintained_variables.float_value);
210 }
211
dock_calc_max_semilatus_rectum_parallel_to_axis(object * objp,axis_type axis)212 float dock_calc_max_semilatus_rectum_parallel_to_axis(object *objp, axis_type axis)
213 {
214 vec3d local_line_end;
215 vec3d *world_line_start, world_line_end;
216 dock_function_info dfi;
217
218 // to calculate the semilatus rectum, we need a directrix that will be parallel to the axis
219
220 // the first endpoint is simply the position of the object
221 world_line_start = &objp->pos;
222
223 // the second endpoint extends in the axis direction
224 vm_vec_zero(&local_line_end);
225 switch(axis)
226 {
227 case X_AXIS:
228 local_line_end.xyz.x = 1.0f;
229 break;
230
231 case Y_AXIS:
232 local_line_end.xyz.y = 1.0f;
233 break;
234
235 case Z_AXIS:
236 local_line_end.xyz.z = 1.0f;
237 break;
238
239 default:
240 Int3();
241 return 0.0f;
242 }
243
244 // rotate and move the endpoint to go through the axis of the actual object
245 vm_vec_rotate(&world_line_end, &local_line_end, &objp->orient);
246 vm_vec_add2(&world_line_end, &objp->pos);
247
248 // now we have a unit vector starting at the object's position and pointing along the chosen axis
249 // (although the length doesn't matter, as it's calculated as an endless line)
250
251 // now determine the semilatus rectum
252
253 // set parameters and call function for the semilatus rectum squared
254 dfi.parameter_variables.vecp_value = world_line_start;
255 dfi.parameter_variables.vecp_value2 = &world_line_end;
256 dock_evaluate_all_docked_objects(objp, &dfi, dock_calc_max_semilatus_rectum_squared_parallel_to_directrix_helper);
257
258 // the semilatus rectum is the square root of our result
259 return fl_sqrt(dfi.maintained_variables.float_value);
260 }
261
dock_calc_docked_fspeed(object * objp)262 float dock_calc_docked_fspeed(object *objp)
263 {
264 // *sigh*... the docked fspeed is simply the max fspeed of all docked objects
265 dock_function_info dfi;
266 dock_evaluate_all_docked_objects(objp, &dfi, dock_find_max_fspeed_helper);
267 return dfi.maintained_variables.float_value;
268 }
269
dock_calc_docked_speed(object * objp)270 float dock_calc_docked_speed(object *objp)
271 {
272 // ditto with speed
273 dock_function_info dfi;
274 dock_evaluate_all_docked_objects(objp, &dfi, dock_find_max_speed_helper);
275 return dfi.maintained_variables.float_value;
276 }
277
278
279 // functions to deal with all docked ships anywhere
280 // ---------------------------------------------------------------------------------------------------------------
281
282 // universal two functions
283 // -----------------------
284
285 // evaluate a certain function for all docked objects
dock_evaluate_all_docked_objects(object * objp,dock_function_info * infop,void (* function)(object *,dock_function_info *))286 void dock_evaluate_all_docked_objects(object *objp, dock_function_info *infop, void (*function)(object *, dock_function_info *))
287 {
288 Assert((objp != NULL) && (infop != NULL) && (function != NULL));
289
290 // not docked?
291 if (!object_is_docked(objp))
292 {
293 // call the function for just the one object
294 function(objp, infop);
295 return;
296 }
297
298 // we only have two objects docked
299 if (dock_check_docked_one_on_one(objp))
300 {
301 // call the function for the first object, and return if instructed
302 function(objp, infop);
303 if (infop->early_return_condition) return;
304
305 // call the function for the second object, and return if instructed
306 function(objp->dock_list->docked_objp, infop);
307 if (infop->early_return_condition) return;
308 }
309
310 // we have multiple objects docked and we're treating them as a hub
311 else if (dock_check_assume_hub())
312 {
313 // get the hub
314 object *hub_objp = dock_get_hub(objp);
315
316 // call the function for the hub, and return if instructed
317 function(hub_objp, infop);
318 if (infop->early_return_condition) return;
319
320 // iterate through all docked objects
321 for (dock_instance *ptr = hub_objp->dock_list; ptr != NULL; ptr = ptr->next)
322 {
323 // call the function for this object, and return if instructed
324 function(ptr->docked_objp, infop);
325 if (infop->early_return_condition) return;
326 }
327 }
328
329 // we have multiple objects docked and we must treat them as a tree
330 else
331 {
332 // create a bit array to mark the objects we check
333 ubyte *visited_bitstring = (ubyte *) vm_malloc(calculate_num_bytes(MAX_OBJECTS));
334
335 // clear it
336 memset(visited_bitstring, 0, calculate_num_bytes(MAX_OBJECTS));
337
338 // start evaluating the tree
339 dock_evaluate_tree(objp, infop, function, visited_bitstring);
340
341 // destroy the bit array
342 vm_free(visited_bitstring);
343 visited_bitstring = NULL;
344 }
345 }
346
dock_evaluate_tree(object * objp,dock_function_info * infop,void (* function)(object *,dock_function_info *),ubyte * visited_bitstring)347 void dock_evaluate_tree(object *objp, dock_function_info *infop, void (*function)(object *, dock_function_info *), ubyte *visited_bitstring)
348 {
349 // make sure we haven't visited this object already
350 if (get_bit(visited_bitstring, OBJ_INDEX(objp)))
351 return;
352
353 // mark as visited
354 set_bit(visited_bitstring, OBJ_INDEX(objp));
355
356 // call the function for this object, and return if instructed
357 function(objp, infop);
358 if (infop->early_return_condition) return;
359
360 // iterate through all docked objects
361 for (dock_instance *ptr = objp->dock_list; ptr != NULL; ptr = ptr->next)
362 {
363 // start another tree with the docked object as the root, and return if instructed
364 dock_evaluate_tree(ptr->docked_objp, infop, function, visited_bitstring);
365 if (infop->early_return_condition) return;
366 }
367 }
368
369 // special-case functions
370 // ----------------------
371
dock_move_docked_objects(object * objp)372 void dock_move_docked_objects(object *objp)
373 {
374 if ((objp->type != OBJ_SHIP) && (objp->type != OBJ_START))
375 return;
376
377 if (!object_is_docked(objp))
378 return;
379
380 // has this object (by extension, this group of docked objects) been handled already?
381 if (objp->flags & OF_DOCKED_ALREADY_HANDLED)
382 return;
383
384 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
385
386 dock_function_info dfi;
387 object *fastest_objp;
388
389 // in FRED, objp is the object everyone moves with
390 if (Fred_running)
391 {
392 fastest_objp = objp;
393 }
394 else
395 {
396 // find the object with the highest max speed
397 dock_evaluate_all_docked_objects(objp, &dfi, dock_find_max_speed_helper);
398 fastest_objp = dfi.maintained_variables.objp_value;
399
400 // if we have no max speed, just use the first one
401 if (fastest_objp == NULL)
402 fastest_objp = objp;
403 }
404
405 // start a tree with that object as the parent... do NOT use the �berfunction for this,
406 // because we must use a tree for the parent ancestry to work correctly
407
408 // we don't need a bit array because OF_DOCKED_ALREADY_HANDLED takes care of it
409 // and must persist for the entire game frame
410
411 // start evaluating the tree, starting with the fastest object having no parent
412 dock_move_docked_children_tree(fastest_objp, NULL);
413 }
414
dock_move_docked_children_tree(object * objp,object * parent_objp)415 void dock_move_docked_children_tree(object *objp, object *parent_objp)
416 {
417 // has this object been handled already?
418 if (objp->flags & OF_DOCKED_ALREADY_HANDLED)
419 return;
420
421 // mark as handled
422 objp->flags |= OF_DOCKED_ALREADY_HANDLED;
423
424 // if parent_objp exists
425 if (parent_objp != NULL)
426 {
427 // move this object to align with it
428 obj_move_one_docked_object(objp, parent_objp);
429 }
430
431 // iterate through all docked objects
432 for (dock_instance *ptr = objp->dock_list; ptr != NULL; ptr = ptr->next)
433 {
434 // start another tree with the docked object as the root and this object as the parent
435 dock_move_docked_children_tree(ptr->docked_objp, objp);
436 }
437 }
438
439
440 // helper functions
441 // ----------------
442
dock_count_total_docked_objects_helper(object * objp,dock_function_info * infop)443 void dock_count_total_docked_objects_helper(object *objp, dock_function_info *infop)
444 {
445 // increment count
446 infop->maintained_variables.int_value++;
447 }
448
dock_check_find_docked_object_helper(object * objp,dock_function_info * infop)449 void dock_check_find_docked_object_helper(object *objp, dock_function_info *infop)
450 {
451 // if object found, set to true and break
452 if (infop->parameter_variables.objp_value == objp)
453 {
454 infop->maintained_variables.bool_value = true;
455 infop->early_return_condition = true;
456 }
457 }
458
dock_calc_docked_center_helper(object * objp,dock_function_info * infop)459 void dock_calc_docked_center_helper(object *objp, dock_function_info *infop)
460 {
461 // add object position and increment count
462 vm_vec_add2(infop->maintained_variables.vecp_value, &objp->pos);
463 infop->maintained_variables.int_value++;
464 }
465
dock_calc_docked_center_of_mass_helper(object * objp,dock_function_info * infop)466 void dock_calc_docked_center_of_mass_helper(object *objp, dock_function_info *infop)
467 {
468 // add weighted object position and add mass
469 vm_vec_scale_add2(infop->maintained_variables.vecp_value, &objp->pos, objp->phys_info.mass);
470 infop->maintained_variables.float_value += objp->phys_info.mass;
471 }
472
dock_calc_total_docked_mass_helper(object * objp,dock_function_info * infop)473 void dock_calc_total_docked_mass_helper(object *objp, dock_function_info *infop)
474 {
475 // add mass
476 infop->maintained_variables.float_value += objp->phys_info.mass;
477 }
478
479 // What we're doing here is finding the distances between each extent of the object and the line, and then taking the
480 // maximum distance as the cross-sectional radius. We're actually maintaining the square of the distance rather than
481 // the actual distance, as it's faster to calculate and it gives the same result in a greater-than or less-than
482 // comparison. When we're done calculating everything for all objects (i.e. when we return to the parent function)
483 // we take the square root of the final value.
dock_calc_max_cross_sectional_radius_squared_perpendicular_to_line_helper(object * objp,dock_function_info * infop)484 void dock_calc_max_cross_sectional_radius_squared_perpendicular_to_line_helper(object *objp, dock_function_info *infop)
485 {
486 vec3d world_point, local_point[6], nearest;
487 polymodel *pm;
488 int i;
489 float dist_squared;
490
491 // line parameters
492 vec3d *line_start = infop->parameter_variables.vecp_value;
493 vec3d *line_end = infop->parameter_variables.vecp_value2;
494
495 // We must find world coordinates for each of the six endpoints on the three axes of the object. I looked up
496 // which axis is front/back, left/right, and up/down, as well as which endpoint is which. It doesn't really
497 // matter, though, as all we need are the distances.
498
499 // grab our model
500 Assert(objp->type == OBJ_SHIP);
501 pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
502
503 // set up the points we want to check
504 memset(local_point, 0, sizeof(vec3d) * 6);
505 local_point[0].xyz.x = pm->maxs.xyz.x; // right point (max x)
506 local_point[1].xyz.x = pm->mins.xyz.x; // left point (min x)
507 local_point[2].xyz.y = pm->maxs.xyz.y; // top point (max y)
508 local_point[3].xyz.y = pm->mins.xyz.y; // bottom point (min y)
509 local_point[4].xyz.z = pm->maxs.xyz.z; // front point (max z)
510 local_point[5].xyz.z = pm->mins.xyz.z; // rear point (min z)
511
512 // check points
513 for (i = 0; i < 6; i++)
514 {
515 // calculate position of point
516 vm_vec_rotate(&world_point, &local_point[i], &objp->orient);
517 vm_vec_add2(&world_point, &objp->pos);
518
519 // calculate square of distance to line
520 vm_vec_dist_squared_to_line(&world_point, line_start, line_end, &nearest, &dist_squared);
521
522 // update with farthest distance squared
523 if (dist_squared > infop->maintained_variables.float_value)
524 infop->maintained_variables.float_value = dist_squared;
525 }
526 }
527
528 // What we're doing here is projecting each object extent onto the directrix, calculating the distance between the
529 // projected point and the origin, and then taking the maximum distance as the semilatus rectum. We're actually
530 // maintaining the square of the distance rather than the actual distance, as it's faster to calculate and it gives
531 // the same result in a greater-than or less-than comparison. When we're done calculating everything for all
532 // objects (i.e. when we return to the parent function) we take the square root of the final value.
dock_calc_max_semilatus_rectum_squared_parallel_to_directrix_helper(object * objp,dock_function_info * infop)533 void dock_calc_max_semilatus_rectum_squared_parallel_to_directrix_helper(object *objp, dock_function_info *infop)
534 {
535 vec3d world_point, local_point[6], nearest;
536 polymodel *pm;
537 int i;
538 float temp, dist_squared;
539
540 // line parameters
541 vec3d *line_start = infop->parameter_variables.vecp_value;
542 vec3d *line_end = infop->parameter_variables.vecp_value2;
543
544 // We must find world coordinates for each of the six endpoints on the three axes of the object. I looked up
545 // which axis is front/back, left/right, and up/down, as well as which endpoint is which. It doesn't really
546 // matter, though, as all we need are the distances.
547
548 // grab our model
549 Assert(objp->type == OBJ_SHIP);
550 pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
551
552 // set up the points we want to check
553 memset(local_point, 0, sizeof(vec3d) * 6);
554 local_point[0].xyz.x = pm->maxs.xyz.x; // right point (max x)
555 local_point[1].xyz.x = pm->mins.xyz.x; // left point (min x)
556 local_point[2].xyz.y = pm->maxs.xyz.y; // top point (max y)
557 local_point[3].xyz.y = pm->mins.xyz.y; // bottom point (min y)
558 local_point[4].xyz.z = pm->maxs.xyz.z; // front point (max z)
559 local_point[5].xyz.z = pm->mins.xyz.z; // rear point (min z)
560
561 // check points
562 for (i = 0; i < 6; i++)
563 {
564 // calculate position of point
565 vm_vec_rotate(&world_point, &local_point[i], &objp->orient);
566 vm_vec_add2(&world_point, &objp->pos);
567
568 // find the nearest point along the line
569 vm_vec_dist_squared_to_line(&world_point, line_start, line_end, &nearest, &temp);
570
571 // find the distance squared between the origin of the line and the point on the line
572 dist_squared = vm_vec_dist_squared(line_start, &nearest);
573
574 // update with farthest distance squared
575 if (dist_squared > infop->maintained_variables.float_value)
576 infop->maintained_variables.float_value = dist_squared;
577 }
578 }
579
dock_find_max_fspeed_helper(object * objp,dock_function_info * infop)580 void dock_find_max_fspeed_helper(object *objp, dock_function_info *infop)
581 {
582 // check our fspeed against the running maximum
583 if (objp->phys_info.fspeed > infop->maintained_variables.float_value)
584 {
585 infop->maintained_variables.float_value = objp->phys_info.fspeed;
586 infop->maintained_variables.objp_value = objp;
587 }
588 }
589
dock_find_max_speed_helper(object * objp,dock_function_info * infop)590 void dock_find_max_speed_helper(object *objp, dock_function_info *infop)
591 {
592 // check our speed against the running maximum
593 if (objp->phys_info.speed > infop->maintained_variables.float_value)
594 {
595 infop->maintained_variables.float_value = objp->phys_info.speed;
596 infop->maintained_variables.objp_value = objp;
597 }
598 }
599 // ---------------------------------------------------------------------------------------------------------------
600 // end of �ber code block ----------------------------------------------------------------------------------------
601
602 // dock management functions -------------------------------------------------------------------------------------
dock_dock_objects(object * objp1,int dockpoint1,object * objp2,int dockpoint2)603 void dock_dock_objects(object *objp1, int dockpoint1, object *objp2, int dockpoint2)
604 {
605 #ifndef NDEBUG
606 if ((dock_find_instance(objp1, objp2) != NULL) || (dock_find_instance(objp2, objp1) != NULL))
607 {
608 Error(LOCATION, "Trying to dock an object that's already docked!\n");
609 }
610
611 if ((dock_find_instance(objp1, dockpoint1) != NULL) || (dock_find_instance(objp2, dockpoint2) != NULL))
612 {
613 Error(LOCATION, "Trying to dock to a dockpoint that's in use!\n");
614 }
615 #endif
616
617 // put objects on each others' dock lists
618 dock_add_instance(objp1, dockpoint1, objp2);
619 dock_add_instance(objp2, dockpoint2, objp1);
620 }
621
dock_undock_objects(object * objp1,object * objp2)622 void dock_undock_objects(object *objp1, object *objp2)
623 {
624 #ifndef NDEBUG
625 if ((dock_find_instance(objp1, objp2) == NULL) || (dock_find_instance(objp2, objp1) == NULL))
626 {
627 Error(LOCATION, "Trying to undock an object that isn't docked!\n");
628 }
629 #endif
630
631 // remove objects from each others' dock lists
632 dock_remove_instance(objp1, objp2);
633 dock_remove_instance(objp2, objp1);
634 }
635
636 // dock list functions -------------------------------------------------------------------------------------------
dock_check_assume_hub()637 bool dock_check_assume_hub()
638 {
639 // There are several ways of handling ships docking to other ships. Level 1, the simplest, is the one-docker, one-dockee
640 // model used in retail FS2. Level 2 is the hub model, where we stipulate that any given set of docked ships
641 // includes one ship to which all other ships are docked. No ship except for the hub ship can be docked to more than
642 // one ship. Level 3 is the daisy-chain model, where you can string ships along and make a rooted tree.
643 //
644 // The new code can handle level 3 ship formations, but it requires more overhead than level 2 or level 1. (Whether
645 // the additional overhead is significant or not has not been determined.) In the vast majority of cases, level 3
646 // is not needed. So this function is provided to allow the code to optimize itself for level 2, should level 1
647 // evaluation fail.
648
649 // Assume level 2 optimization unless the mission specifies level 3.
650 return !(The_mission.flags & MISSION_FLAG_ALLOW_DOCK_TREES);
651 }
652
dock_get_hub(object * objp)653 object *dock_get_hub(object *objp)
654 {
655 Assert(dock_check_assume_hub() && object_is_docked(objp));
656
657 // if our dock list contains only one object, it must be the hub
658 if (objp->dock_list->next == NULL)
659 {
660 return dock_get_first_docked_object(objp);
661 }
662 // otherwise we are the hub
663 else
664 {
665 return objp;
666 }
667 }
668
dock_add_instance(object * objp,int dockpoint,object * other_objp)669 void dock_add_instance(object *objp, int dockpoint, object *other_objp)
670 {
671 dock_instance *item;
672
673 // create item
674 item = (dock_instance *) vm_malloc(sizeof(dock_instance));
675 item->dockpoint_used = dockpoint;
676 item->docked_objp = other_objp;
677
678 // prepend item to existing list
679 item->next = objp->dock_list;
680 objp->dock_list = item;
681 }
682
dock_remove_instance(object * objp,object * other_objp)683 void dock_remove_instance(object *objp, object *other_objp)
684 {
685 int found = 0;
686 dock_instance *prev_ptr, *ptr;
687
688 prev_ptr = NULL;
689 ptr = objp->dock_list;
690
691 // iterate until item found
692 while (ptr != NULL)
693 {
694 // if found, exit loop
695 if (ptr->docked_objp == other_objp)
696 {
697 found = 1;
698 break;
699 }
700
701 // iterate
702 prev_ptr = ptr;
703 ptr = ptr->next;
704 }
705
706 // delete if found
707 if (found)
708 {
709 // special case... found at beginning of list
710 if (prev_ptr == NULL)
711 {
712 objp->dock_list = ptr->next;
713 }
714 // normal case
715 else
716 {
717 prev_ptr->next = ptr->next;
718 }
719
720 // delete it
721 vm_free(ptr);
722 }
723 }
724
725 // just free the list without worrying about undocking anything
dock_free_dock_list(object * objp)726 void dock_free_dock_list(object *objp)
727 {
728 while (objp->dock_list != NULL)
729 {
730 dock_instance *ptr = objp->dock_list;
731 objp->dock_list = ptr->next;
732 vm_free(ptr);
733 }
734 }
735
dock_find_instance(object * objp,object * other_objp)736 dock_instance *dock_find_instance(object *objp, object *other_objp)
737 {
738 dock_instance *ptr = objp->dock_list;
739
740 // iterate until item found
741 while (ptr != NULL)
742 {
743 // if found, return it
744 if (ptr->docked_objp == other_objp)
745 return ptr;
746
747 // iterate
748 ptr = ptr->next;
749 }
750
751 // not found
752 return NULL;
753 }
754
dock_find_instance(object * objp,int dockpoint)755 dock_instance *dock_find_instance(object *objp, int dockpoint)
756 {
757 dock_instance *ptr = objp->dock_list;
758
759 // iterate until item found
760 while (ptr != NULL)
761 {
762 // if found, return it
763 if (ptr->dockpoint_used == dockpoint)
764 return ptr;
765
766 // iterate
767 ptr = ptr->next;
768 }
769
770 // not found
771 return NULL;
772 }
773
dock_count_instances(object * objp)774 int dock_count_instances(object *objp)
775 {
776 int total_count = 0;
777
778 // count all instances in the list
779 dock_instance *ptr = objp->dock_list;
780 while (ptr != NULL)
781 {
782 // incrememnt for this object
783 total_count++;
784
785 // iterate
786 ptr = ptr->next;
787 }
788
789 // done
790 return total_count;
791 }
792