void GameInterface::walkTo(const Common::Point &mouse) { Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor(); Resources::ModelItem *april = StarkGlobal->getCurrent()->getInteractive(); if (!floor || !april) { return; } Math::Ray mouseRay = StarkScene->makeRayFromMouse(mouse); // First look for a direct intersection with the floor Math::Vector3d destinationPosition; int32 destinationFloorFaceIndex = floor->findFaceHitByRay(mouseRay, destinationPosition); // Otherwise fall back to the floor face center closest to the ray if (destinationFloorFaceIndex < 0) { destinationFloorFaceIndex = floor->findFaceClosestToRay(mouseRay, destinationPosition); } if (destinationFloorFaceIndex < 0) { // No destination was found return; } Walk *walk = new Walk(april); walk->setDestination(destinationPosition); walk->start(); april->setMovement(walk); }
void Walk::updatePath() const { Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor(); Math::Vector3d startPosition = _item3D->getPosition3D(); int32 startFloorFaceIndex = floor->findFaceContainingPoint(startPosition); if (startFloorFaceIndex == -1) { startFloorFaceIndex = 0; } Resources::FloorFace *startFloorFace = floor->getFace(startFloorFaceIndex); Resources::FloorEdge *startFloorEdge = startFloorFace->findNearestEdge(startPosition); int32 destinationFloorFaceIndex = floor->findFaceContainingPoint(_destination); Resources::FloorFace *destinationFloorFace = floor->getFace(destinationFloorFaceIndex); Resources::FloorEdge *destinationFloorEdge = destinationFloorFace->findNearestEdge(_destination); ShortestPath pathSearch; ShortestPath::NodeList edgePath = pathSearch.search(startFloorEdge, destinationFloorEdge); _path->reset(); for (ShortestPath::NodeList::const_iterator it = edgePath.begin(); it != edgePath.end(); it++) { _path->addStep((*it)->getPosition()); } _path->addStep(_destination); }
Math::Vector3d StringPullingPath::computeWalkTarget(const Math::Vector3d &fromPosition) { Current *current = StarkGlobal->getCurrent(); Resources::Floor *floor = current->getFloor(); // HACK: Sometimes the character gets stuck because of rounding errors // If we detect the character is stuck on a step, just make it go to the next one. // TODO: Improve the string pulling code so that the targets can also be points between two steps. if (fromPosition.getDistanceTo(_steps[_targetStep]) < 1.0 && _targetStep < _steps.size() - 1) { _targetStep++; } for (uint i = _targetStep + 1; i < _steps.size(); i++) { Math::Line3d testSegment = Math::Line3d(fromPosition, _steps[i]); if (!floor->isSegmentInside(testSegment)) { break; } _targetStep = i; } return _steps[_targetStep]; }
void Walk::onGameLoop() { if (!_path->hasSteps()) { // There is no path to the destination stop(); return; } Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor(); // Get the target to walk to Math::Vector3d currentPosition = _item3D->getPosition3D(); Math::Vector3d target = _path->computeWalkTarget(currentPosition); // Compute the direction to walk into Math::Vector3d direction = target - currentPosition; direction.z() = 0; direction.normalize(); // Compute the angle with the current character direction Math::Vector3d currentDirection = _item3D->getDirectionVector(); float directionDeltaAngle = computeAngleBetweenVectorsXYPlane(currentDirection, direction); // If the angle between the current direction and the new one is too high, // make the character turn on itself until the angle is low enough if (ABS(directionDeltaAngle) > getAngularSpeed() + 0.1f) { _turnDirection = directionDeltaAngle < 0 ? kTurnLeft : kTurnRight; } else { _turnDirection = kTurnNone; } float distancePerGameloop = computeDistancePerGameLoop(); Math::Vector3d newPosition; if (_turnDirection == kTurnNone) { // Compute the new position using the distance per gameloop if (currentPosition.getDistanceTo(target) > distancePerGameloop) { newPosition = currentPosition + direction * distancePerGameloop; } else { newPosition = target; } } else { // The character does not change position when it is turning newPosition = currentPosition; direction = currentDirection; Math::Matrix3 rot; rot.buildAroundZ(_turnDirection == kTurnLeft ? -getAngularSpeed() : getAngularSpeed()); rot.transformVector(&direction); } // Some scripts expect the character position to be the exact destination if (newPosition == _destination) { _reachedDestination = true; stop(); } // Update the new position's height according to the floor int32 newFloorFaceIndex = floor->findFaceContainingPoint(newPosition); if (newFloorFaceIndex >= 0) { floor->computePointHeightInFace(newPosition, newFloorFaceIndex); } else { warning("Item %s is walking off the floor", _item->getName().c_str()); } // Update the item's properties _item3D->setPosition3D(newPosition); if (direction.getMagnitude() != 0.0) { _item3D->setDirection(computeAngleBetweenVectorsXYPlane(direction, Math::Vector3d(1.0, 0.0, 0.0))); } if (newFloorFaceIndex >= 0) { // When unable to find the face containing the new position, keep the previous one // to prevent draw order glitches. _item3D->setFloorFaceIndex(newFloorFaceIndex); } changeItemAnim(); }