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