bool Actor::handleCollisionWith(Actor *actor, CollisionMode mode, Math::Vector3d *vec) const { if (actor->_collisionMode == CollisionOff) { return false; } Model *model1 = getCurrentCostume()->getModel(); Model *model2 = actor->getCurrentCostume()->getModel(); Math::Vector3d p1 = _pos + model1->_insertOffset; Math::Vector3d p2 = actor->_pos + model2->_insertOffset; float size1 = model1->_radius * _collisionScale; float size2 = model2->_radius * actor->_collisionScale; CollisionMode mode1 = mode; CollisionMode mode2 = actor->_collisionMode; if (mode1 == CollisionSphere && mode2 == CollisionSphere) { Math::Vector3d pos = p1 + *vec; float distance = (pos - p2).getMagnitude(); if (distance < size1 + size2) { // Move the destination point so that its distance from the // center of the circle is size1+size2. Math::Vector3d v = pos - p2; v.normalize(); v *= size1 + size2; *vec = v + p2 - p1; collisionHandlerCallback(actor); return true; } } else if (mode1 == CollisionBox && mode2 == CollisionBox) { warning("Collision between box and box not implemented!"); return false; } else { Math::Rect2d rect; Math::Vector3d bboxPos; Math::Vector3d size; float scale; Math::Vector3d pos; Math::Vector3d circlePos; Math::Angle yaw; Math::Vector2d circle; float radius; if (mode1 == CollisionBox) { pos = p1 + *vec; bboxPos = pos + model1->_bboxPos; size = model1->_bboxSize; scale = _collisionScale; yaw = _yaw; circle.setX(p2.x()); circle.setY(p2.y()); circlePos = p2; radius = size2; } else { pos = p2; bboxPos = p2 + model2->_bboxPos; size = model2->_bboxSize; scale = actor->_collisionScale; yaw = actor->_yaw; circle.setX(p1.x() + vec->x()); circle.setY(p1.y() + vec->y()); circlePos = p1; radius = size1; } rect._topLeft = Math::Vector2d(bboxPos.x(), bboxPos.y() + size.y()); rect._topRight = Math::Vector2d(bboxPos.x() + size.x(), bboxPos.y() + size.y()); rect._bottomLeft = Math::Vector2d(bboxPos.x(), bboxPos.y()); rect._bottomRight = Math::Vector2d(bboxPos.x() + size.x(), bboxPos.y()); rect.scale(scale); rect.rotateAround(Math::Vector2d(pos.x(), pos.y()), yaw); if (rect.intersectsCircle(circle, radius)) { Math::Vector2d center = rect.getCenter(); // Draw a line from the center of the rect to the place the character // would go to. Math::Vector2d v = circle - center; v.normalize(); Math::Segment2d edge; // That line intersects (usually) an edge rect.getIntersection(center, v, &edge); // Take the perpendicular of that edge Math::Line2d perpendicular = edge.getPerpendicular(circle); Math::Vector3d point; Math::Vector2d p; // If that perpendicular intersects the edge if (edge.intersectsLine(perpendicular, &p)) { Math::Vector2d direction = perpendicular.getDirection(); direction.normalize(); // Move from the intersection until we are at a safe distance Math::Vector2d point1(p - direction * radius); Math::Vector2d point2(p + direction * radius); if (center.getDistanceTo(point1) < center.getDistanceTo(point2)) { point = point2.toVector3d(); } else { point = point1.toVector3d(); } } else { //if not we're around a corner // Find the nearest vertex of the rect Math::Vector2d vertex = rect.getTopLeft(); float distance = vertex.getDistanceTo(circle); Math::Vector2d other = rect.getTopRight(); float otherDist = other.getDistanceTo(circle); if (otherDist < distance) { distance = otherDist; vertex = other; } other = rect.getBottomLeft(); otherDist = other.getDistanceTo(circle); if (otherDist < distance) { distance = otherDist; vertex = other; } other = rect.getBottomRight(); if (other.getDistanceTo(circle) < distance) { vertex = other; } // and move on a circle around it Math::Vector2d dst = circle - vertex; dst.normalize(); dst = dst * radius; point = (vertex + dst).toVector3d(); } float z = vec->z(); *vec = point - circlePos; vec->z() = z; collisionHandlerCallback(actor); return true; } } return false; }