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