Files
fs2open.github.com/code/object/parseobjectdock.cpp
Fabian Woltermann 770fce8de5 Bitset flags -- take 2 (#775)
This introduces a new mechanism for handling bit flags. Instead of using an int (or two, or three) to write bits into, this uses the std::bitset in a wrapper class that accepts an enum class as input. This means that certain classes of errors we've had a few of in the engine, where a coder used the wrong set of flags or checked them against the wrong variable (SF_STEALTH vs SIF_STEALTH, for example) can now be detected at a compiler level rather than not at all.
2016-08-12 16:13:50 -07:00

298 lines
8.2 KiB
C++

/*
* 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;
}