mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-01 16:35:20 +00:00
SCI: AvoidPath: added support for multiple contained-access polygons (ECO1).
svn-id: r40378
This commit is contained in:
parent
574dee8e1f
commit
8346af68ec
@ -1054,52 +1054,118 @@ static int nearest_intersection(PathfindingState *s, const Common::Point &p, con
|
||||
return find_free_point(isec, ipolygon, ret);
|
||||
}
|
||||
|
||||
static int fix_point(PathfindingState *s, const Common::Point &p, Common::Point *ret, Polygon **ret_pol) {
|
||||
// Checks a point for containment in any of the polygons in the polygon set.
|
||||
// If the point is contained in a totally accessible polygon that polygon
|
||||
// is removed from the set. If the point is contained in a polygon of another
|
||||
// type the near point is returned. Otherwise the original point is returned
|
||||
// Parameters: (const Common::Point &) p: The point
|
||||
// Returns : (int) PF_OK on success, PF_FATAL otherwise
|
||||
// (Common::Point) *ret: A valid input point for pathfinding
|
||||
// (Polygon *) *ret_pol: The polygon p was contained in if p
|
||||
// != *ret, NULL otherwise
|
||||
PolygonList::iterator it;
|
||||
*ret_pol = NULL;
|
||||
/**
|
||||
* Checks that the start point is in a valid position, and takes appropriate action if it's not.
|
||||
* @param s the pathfinding state
|
||||
* @param start the start point
|
||||
* @return a valid start point on success, NULL otherwise
|
||||
*/
|
||||
static Common::Point *fixup_start_point(PathfindingState *s, const Common::Point &start) {
|
||||
PolygonList::iterator it = s->polygons.begin();
|
||||
Common::Point *new_start = new Common::Point(start);
|
||||
|
||||
// Check for polygon containment
|
||||
for (it = s->polygons.begin(); it != s->polygons.end(); ++it) {
|
||||
if (contained(p, *it) != CONT_OUTSIDE)
|
||||
while (it != s->polygons.end()) {
|
||||
int cont = contained(start, *it);
|
||||
int type = (*it)->type;
|
||||
|
||||
switch (type) {
|
||||
case POLY_TOTAL_ACCESS:
|
||||
// Remove totally accessible polygons that contain the start point
|
||||
if (cont != CONT_OUTSIDE) {
|
||||
delete *it;
|
||||
it = s->polygons.erase(it);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case POLY_CONTAINED_ACCESS:
|
||||
// Remove contained access polygons that do not contain
|
||||
// the start point (containment test is inverted here).
|
||||
if (cont == CONT_INSIDE) {
|
||||
delete *it;
|
||||
it = s->polygons.erase(it);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case POLY_BARRED_ACCESS:
|
||||
case POLY_NEAREST_ACCESS:
|
||||
if (cont == CONT_INSIDE) {
|
||||
if (s->_prependPoint != NULL) {
|
||||
// We shouldn't get here twice
|
||||
warning("AvoidPath: start point is contained in multiple polygons");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it != s->polygons.end()) {
|
||||
Common::Point near_p;
|
||||
if (near_point(start, (*it), new_start) != PF_OK) {
|
||||
delete new_start;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((*it)->type == POLY_TOTAL_ACCESS) {
|
||||
// Remove totally accessible polygon if it contains p
|
||||
|
||||
s->polygons.erase(it);
|
||||
*ret = p;
|
||||
return PF_OK;
|
||||
if (type == POLY_BARRED_ACCESS)
|
||||
warning("AvoidPath: start position at unreachable location");
|
||||
|
||||
// The original start position is in an invalid location, so we
|
||||
// use the moved point and add the original one to the final path
|
||||
// later on.
|
||||
s->_prependPoint = new Common::Point(start);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, compute near point
|
||||
if (near_point(p, (*it), &near_p) == PF_OK) {
|
||||
*ret = near_p;
|
||||
|
||||
if (p != *ret)
|
||||
*ret_pol = *it;
|
||||
|
||||
return PF_OK;
|
||||
}
|
||||
|
||||
return PF_FATAL;
|
||||
++it;
|
||||
}
|
||||
|
||||
// p is not contained in any polygon
|
||||
*ret = p;
|
||||
return PF_OK;
|
||||
return new_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the end point is in a valid position, and takes appropriate action if it's not.
|
||||
* @param s the pathfinding state
|
||||
* @param end the end point
|
||||
* @return a valid end point on success, NULL otherwise
|
||||
*/
|
||||
static Common::Point *fixup_end_point(PathfindingState *s, const Common::Point &end) {
|
||||
PolygonList::iterator it = s->polygons.begin();
|
||||
Common::Point *new_end = new Common::Point(end);
|
||||
|
||||
while (it != s->polygons.end()) {
|
||||
int cont = contained(end, *it);
|
||||
int type = (*it)->type;
|
||||
|
||||
switch (type) {
|
||||
case POLY_TOTAL_ACCESS:
|
||||
// Remove totally accessible polygons that contain the end point
|
||||
if (cont != CONT_OUTSIDE) {
|
||||
delete *it;
|
||||
it = s->polygons.erase(it);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case POLY_CONTAINED_ACCESS:
|
||||
case POLY_BARRED_ACCESS:
|
||||
case POLY_NEAREST_ACCESS:
|
||||
if (cont == CONT_INSIDE) {
|
||||
if (s->_appendPoint != NULL) {
|
||||
// We shouldn't get here twice
|
||||
warning("AvoidPath: end point is contained in multiple polygons");
|
||||
continue;
|
||||
}
|
||||
|
||||
// The original end position is in an invalid location, so we move the point
|
||||
if (near_point(end, (*it), new_end) != PF_OK) {
|
||||
delete new_end;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// For near-point access polygons we need to add the original end point
|
||||
// to the path after pathfinding.
|
||||
if (type == POLY_NEAREST_ACCESS)
|
||||
s->_appendPoint = new Common::Point(end);
|
||||
}
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
return new_end;
|
||||
}
|
||||
|
||||
static Vertex *merge_point(PathfindingState *s, const Common::Point &v) {
|
||||
@ -1268,8 +1334,6 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co
|
||||
int err;
|
||||
int count = 0;
|
||||
PathfindingState *pf_s = new PathfindingState();
|
||||
Common::Point new_start = start;
|
||||
Common::Point new_end = end;
|
||||
|
||||
// Convert all polygons
|
||||
if (poly_list.segment) {
|
||||
@ -1300,52 +1364,62 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co
|
||||
}
|
||||
|
||||
if (opt == 0) {
|
||||
Common::Point intersection;
|
||||
|
||||
// Keyboard support
|
||||
// FIXME: We don't need to dijkstra for keyboard support as we currently do
|
||||
change_polygons_opt_0(pf_s);
|
||||
|
||||
// Find nearest intersection
|
||||
err = nearest_intersection(pf_s, start, end, &new_start);
|
||||
err = nearest_intersection(pf_s, start, end, &intersection);
|
||||
|
||||
if (err == PF_FATAL) {
|
||||
sciprintf("[avoidpath] Error: fatal error finding nearest intersecton\n");
|
||||
warning("AvoidPath: fatal error finding nearest intersecton");
|
||||
delete pf_s;
|
||||
return NULL;
|
||||
} else if (err == PF_OK)
|
||||
// Keep original start position if intersection was found
|
||||
pf_s->_prependPoint = new Common::Point(start);
|
||||
} else {
|
||||
if (fix_point(pf_s, start, &new_start, &polygon) != PF_OK) {
|
||||
sciprintf("[avoidpath] Error: couldn't fix start position for pathfinding\n");
|
||||
delete pf_s;
|
||||
return NULL;
|
||||
} else if (polygon) {
|
||||
// Start position has moved
|
||||
pf_s->_prependPoint = new Common::Point(start);
|
||||
if ((polygon->type != POLY_NEAREST_ACCESS))
|
||||
sciprintf("[avoidpath] Warning: start position at unreachable location\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (fix_point(pf_s, end, &new_end, &polygon) != PF_OK) {
|
||||
sciprintf("[avoidpath] Error: couldn't fix end position for pathfinding\n");
|
||||
delete pf_s;
|
||||
return NULL;
|
||||
if (err == PF_OK) {
|
||||
// Intersection was found, prepend original start position after pathfinding
|
||||
pf_s->_prependPoint = new Common::Point(start);
|
||||
// Merge new start point into polygon set
|
||||
pf_s->vertex_start = merge_point(pf_s, intersection);
|
||||
} else {
|
||||
// Otherwise we proceed with the original start point
|
||||
pf_s->vertex_start = merge_point(pf_s, start);
|
||||
}
|
||||
// Merge end point into polygon set
|
||||
pf_s->vertex_end = merge_point(pf_s, end);
|
||||
} else {
|
||||
// Keep original end position if it is contained in a
|
||||
// near-point accessible polygon
|
||||
if (polygon && (polygon->type == POLY_NEAREST_ACCESS))
|
||||
pf_s->_appendPoint = new Common::Point(end);
|
||||
}
|
||||
Common::Point *new_start = fixup_start_point(pf_s, start);
|
||||
|
||||
if (s->_gameName == "Longbow") {
|
||||
// FIXME: implement function to get current room number
|
||||
if ((KP_UINT(s->script_000->locals_block->locals[13]) == 210))
|
||||
fixLongbowRoom210(pf_s, new_start, new_end);
|
||||
}
|
||||
if (!new_start) {
|
||||
warning("AvoidPath: Couldn't fixup start position for pathfinding");
|
||||
delete pf_s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Merge start and end points into polygon set
|
||||
pf_s->vertex_start = merge_point(pf_s, new_start);
|
||||
pf_s->vertex_end = merge_point(pf_s, new_end);
|
||||
Common::Point *new_end = fixup_end_point(pf_s, end);
|
||||
|
||||
if (!new_end) {
|
||||
warning("AvoidPath: Couldn't fixup end position for pathfinding");
|
||||
delete pf_s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (s->_gameName == "Longbow") {
|
||||
// FIXME: implement function to get current room number
|
||||
if ((KP_UINT(s->script_000->locals_block->locals[13]) == 210))
|
||||
fixLongbowRoom210(pf_s, *new_start, *new_end);
|
||||
}
|
||||
|
||||
// Merge start and end points into polygon set
|
||||
pf_s->vertex_start = merge_point(pf_s, *new_start);
|
||||
pf_s->vertex_end = merge_point(pf_s, *new_end);
|
||||
|
||||
delete new_start;
|
||||
delete new_end;
|
||||
}
|
||||
|
||||
// Allocate and build vertex index
|
||||
pf_s->vertex_index = (Vertex**)sci_malloc(sizeof(Vertex *) * (count + 2));
|
||||
|
Loading…
x
Reference in New Issue
Block a user