diff options
author | Walter van Niftrik | 2009-05-08 11:08:53 +0000 |
---|---|---|
committer | Walter van Niftrik | 2009-05-08 11:08:53 +0000 |
commit | 8346af68eccf16e73b004ee78014a48db1c5ff8a (patch) | |
tree | 198a5d2d00f0dd01cc15eac8166d47541287fe2f /engines/sci | |
parent | 574dee8e1f57f126fc4e56e4e8ab839548a990a9 (diff) | |
download | scummvm-rg350-8346af68eccf16e73b004ee78014a48db1c5ff8a.tar.gz scummvm-rg350-8346af68eccf16e73b004ee78014a48db1c5ff8a.tar.bz2 scummvm-rg350-8346af68eccf16e73b004ee78014a48db1c5ff8a.zip |
SCI: AvoidPath: added support for multiple contained-access polygons (ECO1).
svn-id: r40378
Diffstat (limited to 'engines/sci')
-rw-r--r-- | engines/sci/engine/kpathing.cpp | 208 |
1 files changed, 141 insertions, 67 deletions
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index 097e4bec31..8956e63852 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -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; - - // Check for polygon containment - for (it = s->polygons.begin(); it != s->polygons.end(); ++it) { - if (contained(p, *it) != CONT_OUTSIDE) +/** + * 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); + + 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 (near_point(start, (*it), new_start) != PF_OK) { + delete new_start; + return NULL; + } - if (it != s->polygons.end()) { - Common::Point near_p; + if (type == POLY_BARRED_ACCESS) + warning("AvoidPath: start position at unreachable location"); - if ((*it)->type == POLY_TOTAL_ACCESS) { - // Remove totally accessible polygon if it contains p - - s->polygons.erase(it); - *ret = p; - return PF_OK; + // 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; + ++it; + } + + return new_start; +} - if (p != *ret) - *ret_pol = *it; +/** + * 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); - return PF_OK; + 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); + } } - return PF_FATAL; + ++it; } - // p is not contained in any polygon - *ret = p; - return PF_OK; + 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 + } + + 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 { - if (fix_point(pf_s, start, &new_start, &polygon) != PF_OK) { - sciprintf("[avoidpath] Error: couldn't fix start position for pathfinding\n"); + Common::Point *new_start = fixup_start_point(pf_s, start); + + if (!new_start) { + warning("AvoidPath: Couldn't fixup start position for pathfinding"); 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; - } 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_end = fixup_end_point(pf_s, end); - 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_end) { + warning("AvoidPath: Couldn't fixup end 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); + 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)); |