void Set::setSoundPosition(const char *soundName, const Math::Vector3d &pos, int minVol, int maxVol) { // TODO: The volume and pan needs to be updated when the setup changes. Math::Vector3d cameraPos = _currSetup->_pos; Math::Vector3d vector = pos - cameraPos; float distance = vector.getMagnitude(); float diffVolume = maxVol - minVol; //This 8.f is a guess, so it may need some adjusting int newVolume = (int)(8.f * diffVolume / distance); newVolume += minVol; if (newVolume > _maxVolume) newVolume = _maxVolume; g_sound->setVolume(soundName, newVolume); Math::Vector3d cameraVector =_currSetup->_interest - _currSetup->_pos; Math::Vector3d up(0,0,1); Math::Vector3d right; cameraVector.normalize(); float roll = -_currSetup->_roll * LOCAL_PI / 180.f; float cosr = cos(roll); // Rotate the up vector by roll. up = up * cosr + Math::Vector3d::crossProduct(cameraVector, up) * sin(roll) + cameraVector * Math::Vector3d::dotProduct(cameraVector, up) * (1 - cosr); right = Math::Vector3d::crossProduct(cameraVector, up); right.normalize(); float angle = atan2(Math::Vector3d::dotProduct(vector, right), Math::Vector3d::dotProduct(vector, cameraVector)); float pan = sin(angle); g_sound->setPan(soundName, (int)((pan + 1.f) / 2.f * 127.f + 0.5f)); }
void Actor::updateWalk() { if (_path.empty()) { return; } Math::Vector3d destPos = _path.back(); Math::Vector3d dir = destPos - _pos; float dist = dir.getMagnitude(); _walkedCur = true; float walkAmt = g_grim->getPerSecond(_walkRate); if (walkAmt >= dist) { _path.pop_back(); if (_path.empty()) { _walking = false; _pos = destPos; // It seems that we need to allow an already active turning motion to // continue or else turning actors away from barriers won't work right _turning = false; return; } } destPos = handleCollisionTo(_pos, destPos); Math::Angle y = getYawTo(destPos); turnTo(_pitch, y, _roll); dir = destPos - _pos; dir.normalize(); _pos += dir * walkAmt; }
void SoundTrack::updatePosition() { if (!_positioned) return; Set *set = g_grim->getCurrSet(); Set::Setup *setup = set->getCurrSetup(); Math::Vector3d cameraPos = setup->_pos; Math::Vector3d vector = _pos - cameraPos; float distance = vector.getMagnitude(); _attenuation = MAX(0.0f, 1.0f - distance / (_volume * 100.0f / Audio::Mixer::kMaxChannelVolume)); if (!isfinite(_attenuation)) { _attenuation = 0.0f; } Math::Matrix4 worldRot = setup->_rot; Math::Vector3d relPos = (_pos - setup->_pos); Math::Vector3d p(relPos); worldRot.inverseRotate(&p); float angle = atan2(p.x(), p.z()); float pan = sin(angle); _balance = (int)(pan * 127.0f); if (_handle) { g_system->getMixer()->setChannelBalance(*_handle, _balance); g_system->getMixer()->setChannelVolume(*_handle, (byte)getEffectiveVolume()); } }
void Sector::shrink(float radius) { if ((getType() & WalkType) == 0 || _shrinkRadius == radius) return; _shrinkRadius = radius; if (!_origVertices) { _origVertices = _vertices; _vertices = new Math::Vector3d[_numVertices + 1]; } // Move each vertex inwards by the given amount. for (int j = 0; j < _numVertices; j++) { Math::Vector3d shrinkDir; for (int k = 0; k < g_grim->getCurrSet()->getSectorCount(); k++) { Sector *other = g_grim->getCurrSet()->getSectorBase(k); if ((other->getType() & WalkType) == 0) continue; for (int l = 0; l < other->_numVertices; l++) { Math::Vector3d *otherVerts = other->_vertices; if (other->_origVertices) otherVerts = other->_origVertices; if ((otherVerts[l] - _origVertices[j]).getMagnitude() < 0.01f) { Math::Vector3d e1 = otherVerts[l + 1] - otherVerts[l]; Math::Vector3d e2; if (l - 1 >= 0) e2 = otherVerts[l] - otherVerts[l - 1]; else e2 = otherVerts[l] - otherVerts[other->_numVertices - 1]; e1.normalize(); e2.normalize(); Math::Vector3d bisector = (e1 - e2); bisector.normalize(); shrinkDir += bisector; } } } if (shrinkDir.getMagnitude() > 0.1f) { shrinkDir.normalize(); _vertices[j] = _origVertices[j] + shrinkDir * radius; } else { _vertices[j] = _origVertices[j]; } } _vertices[_numVertices] = _vertices[0]; // Make sure the sector is still convex. for (int j = 0; j < _numVertices; j++) { Math::Vector3d e1 = _vertices[j + 1] - _vertices[j]; Math::Vector3d e2; if (j - 1 >= 0) e2 = _vertices[j] - _vertices[j - 1]; else e2 = _vertices[j] - _vertices[_numVertices - 1]; if (e1.x() * e2.y() > e1.y() * e2.x()) { // Not convex, so mark the sector invalid. _invalid = true; delete[] _vertices; _vertices = _origVertices; _origVertices = nullptr; break; } } }
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(); }
void Actor::walkForward() { float dist = g_grim->getPerSecond(_walkRate); // Limit the amount of the movement per frame, otherwise with low fps // scripts that use WalkActorForward and proximity may break. if ((dist > 0 && dist > _walkRate / 5.f) || (dist < 0 && dist < _walkRate / 5.f)) dist = _walkRate / 5.f; _walking = false; if (! _constrain) { Math::Vector3d forwardVec(-_moveYaw.getSine() * _pitch.getCosine(), _moveYaw.getCosine() * _pitch.getCosine(), _pitch.getSine()); // EMI: Y is up-down, sectors use an X-Z plane for movement if (g_grim->getGameType() == GType_MONKEY4) { float temp = forwardVec.z(); forwardVec.z() = forwardVec.y(); forwardVec.y() = temp; } _pos += forwardVec * dist; _walkedCur = true; return; } bool backwards = false; if (dist < 0) { dist = -dist; backwards = true; } int tries = 0; while (dist > 0.0f) { Math::Vector3d forwardVec(-_moveYaw.getSine() * _pitch.getCosine(), _moveYaw.getCosine() * _pitch.getCosine(), _pitch.getSine()); // EMI: Y is up-down, sectors use an X-Z plane for movement if (g_grim->getGameType() == GType_MONKEY4) { float temp = forwardVec.z(); forwardVec.z() = forwardVec.y(); forwardVec.y() = temp; } if (backwards) forwardVec = -forwardVec; Sector *currSector = NULL, *prevSector = NULL, *startSector = NULL; Sector::ExitInfo ei; g_grim->getCurrSet()->findClosestSector(_pos, &currSector, &_pos); if (!currSector) { // Shouldn't happen... moveTo(_pos + forwardVec * dist); _walkedCur = true; return; } startSector = currSector; float oldDist = dist; while (currSector) { prevSector = currSector; Math::Vector3d puckVec = currSector->getProjectionToPuckVector(forwardVec); puckVec /= puckVec.getMagnitude(); currSector->getExitInfo(_pos, puckVec, &ei); float exitDist = (ei.exitPoint - _pos).getMagnitude(); if (dist < exitDist) { moveTo(_pos + puckVec * dist); _walkedCur = true; return; } _pos = ei.exitPoint; dist -= exitDist; if (exitDist > 0.0001) _walkedCur = true; // Check for an adjacent sector which can continue // the path currSector = g_grim->getCurrSet()->findPointSector(ei.exitPoint + (float)0.0001 * puckVec, Sector::WalkType); // EMI: some sectors are significantly higher/lower than others. if (currSector && g_grim->getGameType() == GType_MONKEY4) { float planeDist = currSector->distanceToPoint(_pos); if (fabs(planeDist) < 1.f) _pos -= planeDist * currSector->getNormal(); } if (currSector == prevSector || currSector == startSector) break; } int turnDir = 1; if (ei.angleWithEdge > 90) { ei.angleWithEdge = 180 - ei.angleWithEdge; ei.edgeDir = -ei.edgeDir; turnDir = -1; } if (ei.angleWithEdge > _reflectionAngle) return; ei.angleWithEdge += (float)1.0f; turnTo(0, _moveYaw + ei.angleWithEdge * turnDir, 0); if (oldDist <= dist + 0.001f) { // If we didn't move at all, keep trying a couple more times // in case we can move in the new direction. tries++; if (tries > 3) break; } } }