mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 16:18:19 -10:00
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.
298 lines
8.2 KiB
C++
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;
|
|
}
|