/* * Created by Ian "Goober5000" Warfield for the FreeSpace2 Source Code Project. * You may not sell or otherwise commercially exploit the source or things you * create based on the source. */ #include "globalincs/pstypes.h" #include "math/bitarray.h" #include "mission/missionparse.h" #include "object/parseobjectdock.h" // helper prototypes void dock_evaluate_tree(p_object *objp, p_dock_function_info *infop, void (*function)(p_object *, p_dock_function_info *), ubyte *visited_bitstring); void dock_dock_docked_children_tree(p_object *objp, p_object *parent_objp); // management prototypes void dock_add_instance(p_object *objp, char *dockpoint, p_object *other_objp); p_dock_instance *dock_find_instance(p_object *objp, p_object *other_objp); p_dock_instance *dock_find_instance(p_object *objp, char *dockpoint); bool object_is_docked(p_object *objp) { return (objp->dock_list != NULL); } p_object *dock_get_first_docked_object(p_object *objp) { // are we docked? if (!object_is_docked(objp)) return NULL; return objp->dock_list->docked_objp; } bool dock_check_docked_one_on_one(p_object *objp) { // we must be docked if (!object_is_docked(objp)) return false; // our dock list must contain only one object if (objp->dock_list->next != NULL) return false; // the other guy's dock list must contain only one object if (dock_get_first_docked_object(objp)->dock_list->next != NULL) return false; // debug check to make sure that we're docked to each other Assert(objp == dock_get_first_docked_object(objp)->dock_list->docked_objp); // success return true; } bool dock_check_find_direct_docked_object(p_object *objp, p_object *other_objp) { return (dock_find_instance(objp, other_objp) != NULL); } p_object *dock_find_object_at_dockpoint(p_object *objp, char *dockpoint) { p_dock_instance *result = dock_find_instance(objp, dockpoint); if (result == NULL) return NULL; else return result->docked_objp; } char *dock_find_dockpoint_used_by_object(p_object *objp, p_object *other_objp) { p_dock_instance *result = dock_find_instance(objp, other_objp); if (result == NULL) return NULL; else return result->dockpoint_used; } // functions to deal with all docked objects anywhere // --------------------------------------------------------------------------------------------------------------- // universal two functions // ----------------------- // evaluate a certain function for all docked objects void dock_evaluate_all_docked_objects(p_object *objp, p_dock_function_info *infop, void (*function)(p_object *, p_dock_function_info *)) { Assert((objp != NULL) && (infop != NULL) && (function != NULL)); // not docked? if (!object_is_docked(objp)) { // call the function for just the one object function(objp, infop); return; } // we only have two objects docked if (dock_check_docked_one_on_one(objp)) { // call the function for the first object, and return if instructed function(objp, infop); if (infop->early_return_condition) return; // call the function for the second object, and return if instructed function(objp->dock_list->docked_objp, infop); if (infop->early_return_condition) return; } // NOTE - never treat a group of parse objects as a hub... it cuts down on bugs, and it's // not needed because it's not time-critical // we have multiple objects docked and we must treat them as a tree else { // create a bit array to mark the objects we check ubyte *visited_bitstring = (ubyte *) vm_malloc(calculate_num_bytes(Parse_objects.size())); // clear it memset(visited_bitstring, 0, calculate_num_bytes(Parse_objects.size())); // start evaluating the tree dock_evaluate_tree(objp, infop, function, visited_bitstring); // destroy the bit array vm_free(visited_bitstring); visited_bitstring = NULL; } } void dock_evaluate_tree(p_object *objp, p_dock_function_info *infop, void (*function)(p_object *, p_dock_function_info *), ubyte *visited_bitstring) { // make sure we haven't visited this object already if (get_bit(visited_bitstring, POBJ_INDEX(objp))) return; // mark as visited set_bit(visited_bitstring, POBJ_INDEX(objp)); // call the function for this object, and return if instructed function(objp, infop); if (infop->early_return_condition) return; // iterate through all docked objects for (p_dock_instance *ptr = objp->dock_list; ptr != NULL; ptr = ptr->next) { // start another tree with the docked object as the root, and return if instructed dock_evaluate_tree(ptr->docked_objp, infop, function, visited_bitstring); if (infop->early_return_condition) return; } } // special-case functions // ---------------------- void dock_dock_docked_objects(p_object *objp) { if (!object_is_docked(objp)) return; // has this object (by extension, this group of docked objects) been handled already? if (objp->flags[Mission::Parse_Object_Flags::Already_handled]) return; Assert(objp->flags[Mission::Parse_Object_Flags::SF_Dock_leader]); p_dock_function_info dfi; // start a tree with that object as the parent... do NOT use the überfunction for this, // because we must use a tree for the parent ancestry to work correctly // we don't need a bit array because P2_ALREADY_HANDLED takes care of it // start evaluating the tree, starting with the dock leader dock_dock_docked_children_tree(objp, NULL); } void dock_dock_docked_children_tree(p_object *objp, p_object *parent_objp) { // has this object been handled already? if (objp->flags[Mission::Parse_Object_Flags::Already_handled]) return; // mark as handled objp->flags.set(Mission::Parse_Object_Flags::Already_handled); // if parent_objp exists if (parent_objp != NULL) { // dock this object to it parse_dock_one_docked_object(objp, parent_objp); } // iterate through all docked objects for (p_dock_instance *ptr = objp->dock_list; ptr != NULL; ptr = ptr->next) { // start another tree with the docked object as the root and this object as the parent dock_dock_docked_children_tree(ptr->docked_objp, objp); } } // --------------------------------------------------------------------------------------------------------------- // end of über code block ---------------------------------------------------------------------------------------- // dock management functions ------------------------------------------------------------------------------------- void dock_dock_objects(p_object *objp1, char *dockpoint1, p_object *objp2, char *dockpoint2) { #ifndef NDEBUG if ((dock_find_instance(objp1, objp2) != NULL) || (dock_find_instance(objp2, objp1) != NULL)) { Error(LOCATION, "Trying to dock an object that's already docked!\n"); } if ((dock_find_instance(objp1, dockpoint1) != NULL) || (dock_find_instance(objp2, dockpoint2) != NULL)) { Error(LOCATION, "Trying to dock to a dockpoint that's in use!\n"); } #endif // put objects on each others' dock lists dock_add_instance(objp1, dockpoint1, objp2); dock_add_instance(objp2, dockpoint2, objp1); } // dock list functions ------------------------------------------------------------------------------------------- void dock_add_instance(p_object *objp, char *dockpoint, p_object *other_objp) { p_dock_instance *item; // create item item = (p_dock_instance *) vm_malloc(sizeof(p_dock_instance)); strcpy_s(item->dockpoint_used, dockpoint); item->docked_objp = other_objp; // prepend item to existing list item->next = objp->dock_list; objp->dock_list = item; } // just free the list without worrying about undocking anything void dock_free_dock_list(p_object *objp) { while (objp->dock_list != NULL) { p_dock_instance *ptr = objp->dock_list; objp->dock_list = ptr->next; vm_free(ptr); } } p_dock_instance *dock_find_instance(p_object *objp, p_object *other_objp) { p_dock_instance *ptr = objp->dock_list; // iterate until item found while (ptr != NULL) { // if found, return it if (ptr->docked_objp == other_objp) return ptr; // iterate ptr = ptr->next; } // not found return NULL; } p_dock_instance *dock_find_instance(p_object *objp, char *dockpoint) { p_dock_instance *ptr = objp->dock_list; // iterate until item found while (ptr != NULL) { // if found, return it if (!strcmp(ptr->dockpoint_used, dockpoint)) return ptr; // iterate ptr = ptr->next; } // not found return NULL; }