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 "math/bitarray.h"
11 #include "mission/missionparse.h"
12 #include "object/parseobjectdock.h"
13 
14 
15 
16 
17 // helper prototypes
18 
19 void dock_evaluate_tree(p_object *objp, p_dock_function_info *infop, void (*function)(p_object *, p_dock_function_info *), ubyte *visited_bitstring);
20 void dock_dock_docked_children_tree(p_object *objp, p_object *parent_objp);
21 
22 
23 // management prototypes
24 
25 void dock_add_instance(p_object *objp, char *dockpoint, p_object *other_objp);
26 p_dock_instance *dock_find_instance(p_object *objp, p_object *other_objp);
27 p_dock_instance *dock_find_instance(p_object *objp, char *dockpoint);
28 
29 
object_is_docked(p_object * objp)30 bool object_is_docked(p_object *objp)
31 {
32 	return (objp->dock_list != NULL);
33 }
34 
dock_get_first_docked_object(p_object * objp)35 p_object *dock_get_first_docked_object(p_object *objp)
36 {
37 	// are we docked?
38 	if (!object_is_docked(objp))
39 		return NULL;
40 
41 	return objp->dock_list->docked_objp;
42 }
43 
dock_check_docked_one_on_one(p_object * objp)44 bool dock_check_docked_one_on_one(p_object *objp)
45 {
46 	// we must be docked
47 	if (!object_is_docked(objp))
48 		return false;
49 
50 	// our dock list must contain only one object
51 	if (objp->dock_list->next != NULL)
52 		return false;
53 
54 	// the other guy's dock list must contain only one object
55 	if (dock_get_first_docked_object(objp)->dock_list->next != NULL)
56 		return false;
57 
58 	// debug check to make sure that we're docked to each other
59 	Assert(objp == dock_get_first_docked_object(objp)->dock_list->docked_objp);
60 
61 	// success
62 	return true;
63 }
64 
dock_check_find_direct_docked_object(p_object * objp,p_object * other_objp)65 bool dock_check_find_direct_docked_object(p_object *objp, p_object *other_objp)
66 {
67 	return (dock_find_instance(objp, other_objp) != NULL);
68 }
69 
dock_find_object_at_dockpoint(p_object * objp,char * dockpoint)70 p_object *dock_find_object_at_dockpoint(p_object *objp, char *dockpoint)
71 {
72 	p_dock_instance *result = dock_find_instance(objp, dockpoint);
73 
74 	if (result == NULL)
75 		return NULL;
76 	else
77 		return result->docked_objp;
78 }
79 
dock_find_dockpoint_used_by_object(p_object * objp,p_object * other_objp)80 char *dock_find_dockpoint_used_by_object(p_object *objp, p_object *other_objp)
81 {
82 	p_dock_instance *result = dock_find_instance(objp, other_objp);
83 
84 	if (result == NULL)
85 		return NULL;
86 	else
87 		return result->dockpoint_used;
88 }
89 
90 // functions to deal with all docked objects anywhere
91 // ---------------------------------------------------------------------------------------------------------------
92 
93 // universal two functions
94 // -----------------------
95 
96 // evaluate a certain function for all docked objects
dock_evaluate_all_docked_objects(p_object * objp,p_dock_function_info * infop,void (* function)(p_object *,p_dock_function_info *))97 void dock_evaluate_all_docked_objects(p_object *objp, p_dock_function_info *infop, void (*function)(p_object *, p_dock_function_info *))
98 {
99 	Assert((objp != NULL) && (infop != NULL) && (function != NULL));
100 
101 	// not docked?
102 	if (!object_is_docked(objp))
103 	{
104 		// call the function for just the one object
105 		function(objp, infop);
106 		return;
107 	}
108 
109 	// we only have two objects docked
110 	if (dock_check_docked_one_on_one(objp))
111 	{
112 		// call the function for the first object, and return if instructed
113 		function(objp, infop);
114 		if (infop->early_return_condition) return;
115 
116 		// call the function for the second object, and return if instructed
117 		function(objp->dock_list->docked_objp, infop);
118 		if (infop->early_return_condition) return;
119 	}
120 
121 	// NOTE - never treat a group of parse objects as a hub... it cuts down on bugs, and it's
122 	// not needed because it's not time-critical
123 
124 	// we have multiple objects docked and we must treat them as a tree
125 	else
126 	{
127 		// create a bit array to mark the objects we check
128 		ubyte *visited_bitstring = (ubyte *) vm_malloc(calculate_num_bytes(Parse_objects.size()));
129 
130 		// clear it
131 		memset(visited_bitstring, 0, calculate_num_bytes(Parse_objects.size()));
132 
133 		// start evaluating the tree
134 		dock_evaluate_tree(objp, infop, function, visited_bitstring);
135 
136 		// destroy the bit array
137 		vm_free(visited_bitstring);
138 		visited_bitstring = NULL;
139 	}
140 }
141 
dock_evaluate_tree(p_object * objp,p_dock_function_info * infop,void (* function)(p_object *,p_dock_function_info *),ubyte * visited_bitstring)142 void dock_evaluate_tree(p_object *objp, p_dock_function_info *infop, void (*function)(p_object *, p_dock_function_info *), ubyte *visited_bitstring)
143 {
144 	// make sure we haven't visited this object already
145 	if (get_bit(visited_bitstring, POBJ_INDEX(objp)))
146 		return;
147 
148 	// mark as visited
149 	set_bit(visited_bitstring, POBJ_INDEX(objp));
150 
151 	// call the function for this object, and return if instructed
152 	function(objp, infop);
153 	if (infop->early_return_condition) return;
154 
155 	// iterate through all docked objects
156 	for (p_dock_instance *ptr = objp->dock_list; ptr != NULL; ptr = ptr->next)
157 	{
158 		// start another tree with the docked object as the root, and return if instructed
159 		dock_evaluate_tree(ptr->docked_objp, infop, function, visited_bitstring);
160 		if (infop->early_return_condition) return;
161 	}
162 }
163 
164 // special-case functions
165 // ----------------------
166 
dock_dock_docked_objects(p_object * objp)167 void dock_dock_docked_objects(p_object *objp)
168 {
169 	if (!object_is_docked(objp))
170 		return;
171 
172 	// has this object (by extension, this group of docked objects) been handled already?
173 	if (objp->flags[Mission::Parse_Object_Flags::Already_handled])
174 		return;
175 
176 	Assert(objp->flags[Mission::Parse_Object_Flags::SF_Dock_leader]);
177 
178 	p_dock_function_info dfi;
179 
180 	// start a tree with that object as the parent... do NOT use the �berfunction for this,
181 	// because we must use a tree for the parent ancestry to work correctly
182 
183 	// we don't need a bit array because P2_ALREADY_HANDLED takes care of it
184 
185 	// start evaluating the tree, starting with the dock leader
186 	dock_dock_docked_children_tree(objp, NULL);
187 }
188 
dock_dock_docked_children_tree(p_object * objp,p_object * parent_objp)189 void dock_dock_docked_children_tree(p_object *objp, p_object *parent_objp)
190 {
191 	// has this object been handled already?
192 	if (objp->flags[Mission::Parse_Object_Flags::Already_handled])
193 		return;
194 
195 	// mark as handled
196 	objp->flags.set(Mission::Parse_Object_Flags::Already_handled);
197 
198 	// if parent_objp exists
199 	if (parent_objp != NULL)
200 	{
201 		// dock this object to it
202 		parse_dock_one_docked_object(objp, parent_objp);
203 	}
204 
205 	// iterate through all docked objects
206 	for (p_dock_instance *ptr = objp->dock_list; ptr != NULL; ptr = ptr->next)
207 	{
208 		// start another tree with the docked object as the root and this object as the parent
209 		dock_dock_docked_children_tree(ptr->docked_objp, objp);
210 	}
211 }
212 // ---------------------------------------------------------------------------------------------------------------
213 // end of �ber code block ----------------------------------------------------------------------------------------
214 
215 // dock management functions -------------------------------------------------------------------------------------
dock_dock_objects(p_object * objp1,char * dockpoint1,p_object * objp2,char * dockpoint2)216 void dock_dock_objects(p_object *objp1, char *dockpoint1, p_object *objp2, char *dockpoint2)
217 {
218 #ifndef NDEBUG
219 	if ((dock_find_instance(objp1, objp2) != NULL) || (dock_find_instance(objp2, objp1) != NULL))
220 	{
221 		Error(LOCATION, "Trying to dock an object that's already docked!\n");
222 	}
223 
224 	if ((dock_find_instance(objp1, dockpoint1) != NULL) || (dock_find_instance(objp2, dockpoint2) != NULL))
225 	{
226 		Error(LOCATION, "Trying to dock to a dockpoint that's in use!\n");
227 	}
228 #endif
229 
230 	// put objects on each others' dock lists
231 	dock_add_instance(objp1, dockpoint1, objp2);
232 	dock_add_instance(objp2, dockpoint2, objp1);
233 }
234 
235 // dock list functions -------------------------------------------------------------------------------------------
dock_add_instance(p_object * objp,char * dockpoint,p_object * other_objp)236 void dock_add_instance(p_object *objp, char *dockpoint, p_object *other_objp)
237 {
238 	p_dock_instance *item;
239 
240 	// create item
241 	item = (p_dock_instance *) vm_malloc(sizeof(p_dock_instance));
242 	strcpy_s(item->dockpoint_used, dockpoint);
243 	item->docked_objp = other_objp;
244 
245 	// prepend item to existing list
246 	item->next = objp->dock_list;
247 	objp->dock_list = item;
248 }
249 
250 // just free the list without worrying about undocking anything
dock_free_dock_list(p_object * objp)251 void dock_free_dock_list(p_object *objp)
252 {
253 	while (objp->dock_list != NULL)
254 	{
255 		p_dock_instance *ptr = objp->dock_list;
256 		objp->dock_list = ptr->next;
257 		vm_free(ptr);
258 	}
259 }
260 
dock_find_instance(p_object * objp,p_object * other_objp)261 p_dock_instance *dock_find_instance(p_object *objp, p_object *other_objp)
262 {
263 	p_dock_instance *ptr = objp->dock_list;
264 
265 	// iterate until item found
266 	while (ptr != NULL)
267 	{
268 		// if found, return it
269 		if (ptr->docked_objp == other_objp)
270 			return ptr;
271 
272 		// iterate
273 		ptr = ptr->next;
274 	}
275 
276 	// not found
277 	return NULL;
278 }
279 
dock_find_instance(p_object * objp,char * dockpoint)280 p_dock_instance *dock_find_instance(p_object *objp, char *dockpoint)
281 {
282 	p_dock_instance *ptr = objp->dock_list;
283 
284 	// iterate until item found
285 	while (ptr != NULL)
286 	{
287 		// if found, return it
288 		if (!strcmp(ptr->dockpoint_used, dockpoint))
289 			return ptr;
290 
291 		// iterate
292 		ptr = ptr->next;
293 	}
294 
295 	// not found
296 	return NULL;
297 }
298