uint16 translate_projectile( short type, world_point3d *old_location, short old_polygon_index, world_point3d *new_location, short *new_polygon_index, short owner_index, short *obstruction_index) { struct projectile_definition *definition= get_projectile_definition(type); struct polygon_data *old_polygon; world_point3d intersection; world_distance media_height; short line_index; size_t intersected_object_count; short contact; uint16 flags= 0; *obstruction_index= NONE; contact= _hit_nothing; IntersectedObjects.clear(); old_polygon= get_polygon_data(old_polygon_index); if (new_polygon_index) *new_polygon_index= old_polygon_index; do { // LP note: "penetrates media boundary" means "hit media surface and continue"; // create a hack for enabling a projectile with this flag to continue // Idiot-proofing of media handling media_data *media = get_media_data(old_polygon->media_index); media_height= (old_polygon->media_index==NONE || definition->flags&_penetrates_media || !media) ? INT16_MIN : media->height; // This flag says it can bool traveled_underneath = (definition->flags&_penetrates_media_boundary) && (old_location->z <= media_height); /* add this polygonÕs monsters to our non-redundant list of possible intersections */ possible_intersecting_monsters(&IntersectedObjects, GLOBAL_INTERSECTING_MONSTER_BUFFER_SIZE, old_polygon_index, true); intersected_object_count = IntersectedObjects.size(); line_index= find_line_crossed_leaving_polygon(old_polygon_index, (world_point2d *)old_location, (world_point2d *)new_location); if (line_index!=NONE) { /* we crossed a line: if the line is solid, we detonate immediately on the wall, otherwise we calculate our Z at the line and compare it to the ceiling and floor heights of the old and new polygon to see if we hit a wall between the polygons, or the floor or ceiling somewhere in the old polygon */ struct line_data *line= get_line_data(line_index); find_line_intersection(&get_endpoint_data(line->endpoint_indexes[0])->vertex, &get_endpoint_data(line->endpoint_indexes[1])->vertex, old_location, new_location, &intersection); // LP change: workaround for Pfhorte map bug: check to see if there is a polygon // on the other side short adjacent_polygon_index= find_adjacent_polygon(old_polygon_index, line_index); if ((!LINE_IS_SOLID(line) || LINE_HAS_TRANSPARENT_SIDE(line)) && (adjacent_polygon_index != NONE)) { // short adjacent_polygon_index= find_adjacent_polygon(old_polygon_index, line_index); struct polygon_data *adjacent_polygon= get_polygon_data(adjacent_polygon_index); // LP change: if PMB is set, then a projectile can travel as if the liquid did not exist, // but it will be able to run into a media surface. // Here, test for whether the projectile is above the floor or the media surface; // ignore the latter test if PMB is set. if ((traveled_underneath || intersection.z>media_height) && intersection.z>old_polygon->floor_height) { // If PMB was set, check to see if the projectile hit the media surface. if ((!traveled_underneath || intersection.zceiling_height) { if (intersection.z>adjacent_polygon->floor_height&&intersection.zceiling_height) { if (!LINE_HAS_TRANSPARENT_SIDE(line) || (!new_polygon_index && (definition->flags&(_usually_pass_transparent_side|_sometimes_pass_transparent_side))) || ((definition->flags&_usually_pass_transparent_side) && (global_random()&3)) || ((definition->flags&_sometimes_pass_transparent_side) && !(global_random()&3))) { /* no intersections, successfully entered new polygon */ if (new_polygon_index) *new_polygon_index= adjacent_polygon_index; old_polygon_index= adjacent_polygon_index; old_polygon= adjacent_polygon; } else { /* hit and could not pass transparent texture */ contact= _hit_wall; } } else { /* hit wall created by ceiling or floor of new polygon; test to see if we toggle a control panel */ if (new_polygon_index && (definition->flags&_can_toggle_control_panels)) try_and_toggle_control_panel(old_polygon_index, line_index); contact= _hit_wall; } } else { /* hit ceiling of old polygon */ *obstruction_index= old_polygon_index; if (adjacent_polygon->ceiling_transfer_mode==_xfer_landscape) flags|= _projectile_hit_landscape; // LP change: if PMB was set, check to see if hit media contact= (!traveled_underneath || old_polygon->ceiling_heightfloor_transfer_mode==_xfer_landscape) flags|= _projectile_hit_landscape; // LP change: suppress media-hit test only if PMB was set and the projectile was underneath the media contact= (traveled_underneath || old_polygon->floor_height>media_height) ? _hit_floor : _hit_media; } } else { /* hit wall created by solid line; test to see if we toggle a control panel */ if (new_polygon_index && (definition->flags&_can_toggle_control_panels)) try_and_toggle_control_panel(old_polygon_index, line_index); if (line_is_landscaped(old_polygon_index, line_index, intersection.z)) flags|= _projectile_hit_landscape; contact= _hit_wall; } } else { /* make sure we didnÕt hit the ceiling or floor in this polygon */ // LP change: if PMB is set, then a projectile can travel as if the liquid did not exist, // but it will be able to run into a media surface. // Here, test for whether the projectile is above the floor or the media surface; // ignore the latter test if PMB is set. if ((traveled_underneath || new_location->z>media_height) && new_location->z>old_polygon->floor_height) { // If PMB was set, check to see if the projectile hit the media surface. if ((!traveled_underneath || new_location->zzceiling_height) { /* weÕre staying in this polygon and weÕre finally done screwing around; the caller can look in *new_polygon_index to find out where we ended up */ } else { /* hit ceiling of current polygon */ *obstruction_index= old_polygon_index; if (old_polygon->ceiling_transfer_mode==_xfer_landscape) flags|= _projectile_hit_landscape; // LP change: if PMB was set, check to see if hit media contact= (!traveled_underneath || old_polygon->ceiling_heightfloor_transfer_mode==_xfer_landscape) flags|= _projectile_hit_landscape; // LP change: suppress media-hit test only if PMB was set and the projectile was underneath the media contact= (traveled_underneath || old_polygon->floor_height>media_height) ? _hit_floor : _hit_media; } } } while (line_index!=NONE&&contact==_hit_nothing); /* ceilings and floor intersections still donÕt have accurate intersection points, so calculate them */ if (contact!=_hit_nothing) { switch (contact) { case _hit_media: find_floor_or_ceiling_intersection(media_height, old_location, new_location, &intersection); break; case _hit_floor: find_floor_or_ceiling_intersection(old_polygon->floor_height, old_location, new_location, &intersection); break; case _hit_ceiling: find_floor_or_ceiling_intersection(old_polygon->ceiling_height, old_location, new_location, &intersection); break; } /* change new_location to the point of intersection with the ceiling, floor, or wall */ *new_location= intersection; } /* check our object list and find the best intersection ... if we find an intersection at all, then we hit this before we hit the wall, because the object list is checked against the clipped new_location. */ { world_distance best_intersection_distance = 0; world_distance distance_traveled; world_distance best_radius = 0; short best_intersection_object = NONE; distance_traveled= distance2d((world_point2d *)old_location, (world_point2d *)new_location); for (size_t i=0;ilocation, (world_point2d *)old_location, (world_point2d *)new_location); world_distance radius, height; if (object->permutation!=owner_index) /* donÕt hit ourselves */ { long radius_squared; switch (GET_OBJECT_OWNER(object)) { case _object_is_monster: get_monster_dimensions(object->permutation, &radius, &height); break; case _object_is_scenery: get_scenery_dimensions(object->permutation, &radius, &height); break; default: assert(false); break; } radius_squared= (radius+definition->radius)*(radius+definition->radius); if (separationlocation); world_distance projectile_z= distance_traveled ? old_location->z + (distance*(new_location->z-old_location->z))/distance_traveled : old_location->z; if ((height>0 && projectile_z>=object->location.z && projectile_z<=object->location.z+height) || (height<0 && projectile_z>=object->location.z+height && projectile_z<=object->location.z)) { if (best_intersection_object==NONE || distancepermutation)) && monster_index_to_player_index(object->permutation)==current_player_index) { flags|= _flyby_of_current_player; } } } } } if (best_intersection_object!=NONE) /* if we hit something, take it */ { struct object_data *object= get_object_data(best_intersection_object); *obstruction_index= best_intersection_object; if (distance_traveled) { world_distance actual_distance_to_hit; actual_distance_to_hit= distance2d((world_point2d *)old_location, (world_point2d *) &object->location); actual_distance_to_hit-= best_radius; new_location->x= old_location->x + (actual_distance_to_hit*(new_location->x-old_location->x))/distance_traveled; new_location->y= old_location->y + (actual_distance_to_hit*(new_location->y-old_location->y))/distance_traveled; new_location->z= old_location->z + (actual_distance_to_hit*(new_location->z-old_location->z))/distance_traveled; if (new_polygon_index) *new_polygon_index= find_new_object_polygon((world_point2d *) &object->location, (world_point2d *) new_location, object->polygon); } else { *new_location= *old_location; } } } switch (contact) { case _hit_monster: flags|= _projectile_hit|_projectile_hit_monster; break; case _hit_floor: flags|= _projectile_hit|_projectile_hit_floor; break; case _hit_media: flags|= _projectile_hit|_projectile_hit_media; break; case _hit_scenery: flags|= _projectile_hit|_projectile_hit_scenery; break; case _hit_nothing: break; default: flags|= _projectile_hit; break; } /* returns true if we hit something, false otherwise */ return flags; }