void Item::setTransform(const MatrixF& mat) { Point3F pos; mat.getColumn(3,&pos); MatrixF tmat; if (!mRotate) { // Forces all rotation to be around the z axis VectorF vec; mat.getColumn(1,&vec); tmat.set(EulerF(0,0,-mAtan2(-vec.x,vec.y))); } else tmat.identity(); tmat.setColumn(3,pos); Parent::setTransform(tmat); if (!mStatic) { mAtRest = false; mAtRestCounter = 0; } if ( mPhysicsRep ) mPhysicsRep->setTransform( getTransform() ); setMaskBits(RotationMask | PositionMask | NoWarpMask); }
void FontRenderBatcher::render( F32 rot, const Point2F &offset ) { if( mLength == 0 ) return; GFX->setStateBlock(mFontSB); for(U32 i = 0; i < GFX->getNumSamplers(); i++) GFX->setTexture(i, NULL); MatrixF rotMatrix; bool doRotation = rot != 0.f; if(doRotation) rotMatrix.set( EulerF( 0.0, 0.0, mDegToRad( rot ) ) ); // Write verts out. U32 currentPt = 0; GFXVertexBufferHandle<GFXVertexPCT> verts(GFX, mLength * 6, GFXBufferTypeVolatile); verts.lock(); for( S32 i = 0; i < mSheets.size(); i++ ) { // Do some early outs... if(!mSheets[i]) continue; if(!mSheets[i]->numChars) continue; mSheets[i]->startVertex = currentPt; const GFXTextureObject *tex = mFont->getTextureHandle(i); for( S32 j = 0; j < mSheets[i]->numChars; j++ ) { // Get some general info to proceed with... const CharMarker &m = mSheets[i]->charIndex[j]; const PlatformFont::CharInfo &ci = mFont->getCharInfo( m.c ); // Where are we drawing it? F32 drawY = offset.y + mFont->getBaseline() - ci.yOrigin * TEXT_MAG; F32 drawX = offset.x + m.x + ci.xOrigin; // Figure some values. const F32 texWidth = (F32)tex->getWidth(); const F32 texHeight = (F32)tex->getHeight(); const F32 texLeft = (F32)(ci.xOffset) / texWidth; const F32 texRight = (F32)(ci.xOffset + ci.width) / texWidth; const F32 texTop = (F32)(ci.yOffset) / texHeight; const F32 texBottom = (F32)(ci.yOffset + ci.height) / texHeight; const F32 fillConventionOffset = GFX->getFillConventionOffset(); const F32 screenLeft = drawX - fillConventionOffset; const F32 screenRight = drawX - fillConventionOffset + ci.width * TEXT_MAG; const F32 screenTop = drawY - fillConventionOffset; const F32 screenBottom = drawY - fillConventionOffset + ci.height * TEXT_MAG; // Build our vertices. We NEVER read back from the buffer, that's // incredibly slow, so for rotation do it into tmp. This code is // ugly as sin. Point3F tmp; tmp.set( screenLeft, screenTop, 0.f ); if(doRotation) rotMatrix.mulP( tmp, &verts[currentPt].point); else verts[currentPt].point = tmp; verts[currentPt].color = m.color; verts[currentPt].texCoord.set( texLeft, texTop ); currentPt++; tmp.set( screenLeft, screenBottom, 0.f ); if(doRotation) rotMatrix.mulP( tmp, &verts[currentPt].point); else verts[currentPt].point = tmp; verts[currentPt].color = m.color; verts[currentPt].texCoord.set( texLeft, texBottom ); currentPt++; tmp.set( screenRight, screenBottom, 0.f ); if(doRotation) rotMatrix.mulP( tmp, &verts[currentPt].point); else verts[currentPt].point = tmp; verts[currentPt].color = m.color; verts[currentPt].texCoord.set( texRight, texBottom ); currentPt++; tmp.set( screenRight, screenBottom, 0.f ); if(doRotation) rotMatrix.mulP( tmp, &verts[currentPt].point); else verts[currentPt].point = tmp; verts[currentPt].color = m.color; verts[currentPt].texCoord.set( texRight, texBottom ); currentPt++; tmp.set( screenRight, screenTop, 0.f ); if(doRotation) rotMatrix.mulP( tmp, &verts[currentPt].point); else verts[currentPt].point = tmp; verts[currentPt].color = m.color; verts[currentPt].texCoord.set( texRight, texTop ); currentPt++; tmp.set( screenLeft, screenTop, 0.f ); if(doRotation) rotMatrix.mulP( tmp, &verts[currentPt].point); else verts[currentPt].point = tmp; verts[currentPt].color = m.color; verts[currentPt].texCoord.set( texLeft, texTop ); currentPt++; } } verts->unlock(); AssertFatal(currentPt <= mLength * 6, "FontRenderBatcher::render - too many verts for length of string!"); GFX->setVertexBuffer(verts); GFX->setupGenericShaders( GFXDevice::GSAddColorTexture ); // Now do an optimal render! for( S32 i = 0; i < mSheets.size(); i++ ) { if(!mSheets[i]) continue; if(!mSheets[i]->numChars ) continue; GFX->setTexture( 0, mFont->getTextureHandle(i) ); GFX->drawPrimitive(GFXTriangleList, mSheets[i]->startVertex, mSheets[i]->numChars * 2); } }
/** * This method calculates the moves for the AI player * * @param movePtr Pointer to move the move list into */ bool AIPlayer::getAIMove(Move *movePtr) { *movePtr = NullMove; // Use the eye as the current position. MatrixF eye; getEyeTransform(&eye); Point3F location = eye.getPosition(); Point3F rotation = getRotation(); // Orient towards the aim point, aim object, or towards // our destination. if (mAimObject || mAimLocationSet || mMoveState != ModeStop) { // Update the aim position if we're aiming for an object if (mAimObject) mAimLocation = mAimObject->getPosition() + mAimOffset; else if (!mAimLocationSet) mAimLocation = mMoveDestination; F32 xDiff = mAimLocation.x - location.x; F32 yDiff = mAimLocation.y - location.y; if (!mIsZero(xDiff) || !mIsZero(yDiff)) { // First do Yaw // use the cur yaw between -Pi and Pi F32 curYaw = rotation.z; while (curYaw > M_2PI_F) curYaw -= M_2PI_F; while (curYaw < -M_2PI_F) curYaw += M_2PI_F; // find the yaw offset F32 newYaw = mAtan2( xDiff, yDiff ); F32 yawDiff = newYaw - curYaw; // make it between 0 and 2PI if( yawDiff < 0.0f ) yawDiff += M_2PI_F; else if( yawDiff >= M_2PI_F ) yawDiff -= M_2PI_F; // now make sure we take the short way around the circle if( yawDiff > M_PI_F ) yawDiff -= M_2PI_F; else if( yawDiff < -M_PI_F ) yawDiff += M_2PI_F; movePtr->yaw = yawDiff; // Next do pitch. if (!mAimObject && !mAimLocationSet) { // Level out if were just looking at our next way point. Point3F headRotation = getHeadRotation(); movePtr->pitch = -headRotation.x; } else { // This should be adjusted to run from the // eye point to the object's center position. Though this // works well enough for now. F32 vertDist = mAimLocation.z - location.z; F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff); F32 newPitch = mAtan2( horzDist, vertDist ) - ( M_PI_F / 2.0f ); if (mFabs(newPitch) > 0.01f) { Point3F headRotation = getHeadRotation(); movePtr->pitch = newPitch - headRotation.x; } } } } else { // Level out if we're not doing anything else Point3F headRotation = getHeadRotation(); movePtr->pitch = -headRotation.x; } // Move towards the destination if (mMoveState != ModeStop) { F32 xDiff = mMoveDestination.x - location.x; F32 yDiff = mMoveDestination.y - location.y; // Check if we should mMove, or if we are 'close enough' if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) { mMoveState = ModeStop; throwCallback("onReachDestination"); } else { // Build move direction in world space if (mIsZero(xDiff)) movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; else if (mIsZero(yDiff)) movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; else if (mFabs(xDiff) > mFabs(yDiff)) { F32 value = mFabs(yDiff / xDiff); movePtr->y = (location.y > mMoveDestination.y) ? -value : value; movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; } else { F32 value = mFabs(xDiff / yDiff); movePtr->x = (location.x > mMoveDestination.x) ? -value : value; movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; } // Rotate the move into object space (this really only needs // a 2D matrix) Point3F newMove; MatrixF moveMatrix; moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw))); moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0.0f ), &newMove ); movePtr->x = newMove.x; movePtr->y = newMove.y; // Set movement speed. We'll slow down once we get close // to try and stop on the spot... if (mMoveSlowdown) { F32 speed = mMoveSpeed; F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff); F32 maxDist = 5.0f; if (dist < maxDist) speed *= dist / maxDist; movePtr->x *= speed; movePtr->y *= speed; mMoveState = ModeSlowing; } else { movePtr->x *= mMoveSpeed; movePtr->y *= mMoveSpeed; mMoveState = ModeMove; } if (mMoveStuckTestCountdown > 0) --mMoveStuckTestCountdown; else { // We should check to see if we are stuck... F32 locationDelta = (location - mLastLocation).len(); if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled) { // If we are slowing down, then it's likely that our location delta will be less than // our move stuck tolerance. Because we can be both slowing and stuck // we should TRY to check if we've moved. This could use better detection. if ( mMoveState != ModeSlowing || locationDelta == 0 ) { mMoveState = ModeStuck; throwCallback("onMoveStuck"); } } } } } // Test for target location in sight if it's an object. The LOS is // run from the eye position to the center of the object's bounding, // which is not very accurate. if (mAimObject) { MatrixF eyeMat; getEyeTransform(&eyeMat); eyeMat.getColumn(3,&location); Point3F targetLoc = mAimObject->getBoxCenter(); // This ray ignores non-static shapes. Cast Ray returns true // if it hit something. RayInfo dummy; if (getContainer()->castRay( location, targetLoc, StaticShapeObjectType | StaticObjectType | TerrainObjectType, &dummy)) { if (mTargetInLOS) { throwCallback( "onTargetExitLOS" ); mTargetInLOS = false; } } else if (!mTargetInLOS) { throwCallback( "onTargetEnterLOS" ); mTargetInLOS = true; } } // Replicate the trigger state into the move so that // triggers can be controlled from scripts. for( int i = 0; i < MaxTriggerKeys; i++ ) movePtr->trigger[i] = getImageTriggerState(i); mLastLocation = location; return true; }
/** * This method calculates the moves for the AI player * * @param movePtr Pointer to move the move list into */ bool AIPlayer::getAIMove(Move *movePtr) { *movePtr = NullMove; // Use the eye as the current position. MatrixF eye; getEyeTransform(&eye); Point3F location = eye.getPosition(); Point3F rotation = getRotation(); #ifdef TORQUE_NAVIGATION_ENABLED if(mDamageState == Enabled) { if(mMoveState != ModeStop) updateNavMesh(); if(!mFollowData.object.isNull()) { if(mPathData.path.isNull()) { if((getPosition() - mFollowData.object->getPosition()).len() > mFollowData.radius) followObject(mFollowData.object, mFollowData.radius); } else { if((mPathData.path->mTo - mFollowData.object->getPosition()).len() > mFollowData.radius) repath(); else if((getPosition() - mFollowData.object->getPosition()).len() < mFollowData.radius) { clearPath(); mMoveState = ModeStop; throwCallback("onTargetInRange"); } else if((getPosition() - mFollowData.object->getPosition()).len() < mAttackRadius) { throwCallback("onTargetInFiringRange"); } } } } #endif // TORQUE_NAVIGATION_ENABLED // Orient towards the aim point, aim object, or towards // our destination. if (mAimObject || mAimLocationSet || mMoveState != ModeStop) { // Update the aim position if we're aiming for an object if (mAimObject) mAimLocation = mAimObject->getPosition() + mAimOffset; else if (!mAimLocationSet) mAimLocation = mMoveDestination; F32 xDiff = mAimLocation.x - location.x; F32 yDiff = mAimLocation.y - location.y; if (!mIsZero(xDiff) || !mIsZero(yDiff)) { // First do Yaw // use the cur yaw between -Pi and Pi F32 curYaw = rotation.z; while (curYaw > M_2PI_F) curYaw -= M_2PI_F; while (curYaw < -M_2PI_F) curYaw += M_2PI_F; // find the yaw offset F32 newYaw = mAtan2( xDiff, yDiff ); F32 yawDiff = newYaw - curYaw; // make it between 0 and 2PI if( yawDiff < 0.0f ) yawDiff += M_2PI_F; else if( yawDiff >= M_2PI_F ) yawDiff -= M_2PI_F; // now make sure we take the short way around the circle if( yawDiff > M_PI_F ) yawDiff -= M_2PI_F; else if( yawDiff < -M_PI_F ) yawDiff += M_2PI_F; movePtr->yaw = yawDiff; // Next do pitch. if (!mAimObject && !mAimLocationSet) { // Level out if were just looking at our next way point. Point3F headRotation = getHeadRotation(); movePtr->pitch = -headRotation.x; } else { // This should be adjusted to run from the // eye point to the object's center position. Though this // works well enough for now. F32 vertDist = mAimLocation.z - location.z; F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff); F32 newPitch = mAtan2( horzDist, vertDist ) - ( M_PI_F / 2.0f ); if (mFabs(newPitch) > 0.01f) { Point3F headRotation = getHeadRotation(); movePtr->pitch = newPitch - headRotation.x; } } } } else { // Level out if we're not doing anything else Point3F headRotation = getHeadRotation(); movePtr->pitch = -headRotation.x; } // Move towards the destination if (mMoveState != ModeStop) { F32 xDiff = mMoveDestination.x - location.x; F32 yDiff = mMoveDestination.y - location.y; // Check if we should mMove, or if we are 'close enough' if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) { mMoveState = ModeStop; onReachDestination(); } else { // Build move direction in world space if (mIsZero(xDiff)) movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; else if (mIsZero(yDiff)) movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; else if (mFabs(xDiff) > mFabs(yDiff)) { F32 value = mFabs(yDiff / xDiff); movePtr->y = (location.y > mMoveDestination.y) ? -value : value; movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; } else { F32 value = mFabs(xDiff / yDiff); movePtr->x = (location.x > mMoveDestination.x) ? -value : value; movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; } // Rotate the move into object space (this really only needs // a 2D matrix) Point3F newMove; MatrixF moveMatrix; moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw))); moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0.0f ), &newMove ); movePtr->x = newMove.x; movePtr->y = newMove.y; // Set movement speed. We'll slow down once we get close // to try and stop on the spot... if (mMoveSlowdown) { F32 speed = mMoveSpeed; F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff); F32 maxDist = mMoveTolerance*2; if (dist < maxDist) speed *= dist / maxDist; movePtr->x *= speed; movePtr->y *= speed; mMoveState = ModeSlowing; } else { movePtr->x *= mMoveSpeed; movePtr->y *= mMoveSpeed; mMoveState = ModeMove; } if (mMoveStuckTestCountdown > 0) --mMoveStuckTestCountdown; else { // We should check to see if we are stuck... F32 locationDelta = (location - mLastLocation).len(); if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled) { // If we are slowing down, then it's likely that our location delta will be less than // our move stuck tolerance. Because we can be both slowing and stuck // we should TRY to check if we've moved. This could use better detection. if ( mMoveState != ModeSlowing || locationDelta == 0 ) { mMoveState = ModeStuck; onStuck(); } } } } } // Test for target location in sight if it's an object. The LOS is // run from the eye position to the center of the object's bounding, // which is not very accurate. if (mAimObject) { if (checkInLos(mAimObject.getPointer())) { if (!mTargetInLOS) { throwCallback( "onTargetEnterLOS" ); mTargetInLOS = true; } } else if (mTargetInLOS) { throwCallback( "onTargetExitLOS" ); mTargetInLOS = false; } } Pose desiredPose = mPose; if ( mSwimming ) desiredPose = SwimPose; else if ( mAiPose == 1 && canCrouch() ) desiredPose = CrouchPose; else if ( mAiPose == 2 && canProne() ) desiredPose = PronePose; else if ( mAiPose == 3 && canSprint() ) desiredPose = SprintPose; else if ( canStand() ) desiredPose = StandPose; setPose( desiredPose ); // Replicate the trigger state into the move so that // triggers can be controlled from scripts. for( U32 i = 0; i < MaxTriggerKeys; i++ ) movePtr->trigger[ i ] = getImageTriggerState( i ); #ifdef TORQUE_NAVIGATION_ENABLED if(mJump == Now) { movePtr->trigger[2] = true; mJump = None; } else if(mJump == Ledge) { // If we're not touching the ground, jump! RayInfo info; if(!getContainer()->castRay(getPosition(), getPosition() - Point3F(0, 0, 0.4f), StaticShapeObjectType, &info)) { movePtr->trigger[2] = true; mJump = None; } } #endif // TORQUE_NAVIGATION_ENABLED mLastLocation = location; return true; }
//----------------------------------------------------------------------------- // // VActorPhysicsController::postTickUpdate( pDelta ); // // ... // //----------------------------------------------------------------------------- void VActorPhysicsController::postTickUpdate( const F32 &pDelta ) { switch( mControlState ) { case k_PathControlState : { AssertFatal( isPathing(), "VActorPhysicsController::postTickUpdate() - Invalid Path State." ); // Fetch Mount Transform. MatrixF transform; mMountedPath->getMountTransform( mObject->getMountNode(), getTransform(), &transform ); // Fetch Mount Position. const Point3F &mountPosition = transform.getPosition(); // Update X & Y Position. Point3F position = getPosition(); position.x = mountPosition.x; position.y = mountPosition.y; // In Water? bool underWater = false; if ( isInWater() ) { // Fetch Body of Water. WaterObject *waterBody = getWaterObject(); // Fetch Surface Position. const F32 &waterSurfacePosition = waterBody->getSurfaceHeight( Point2F( position.x, position.y ) ); // Fetch Submersion Position. const F32 sumbersionPosition = waterSurfacePosition - ( mObject->getWorldBox().len_z() * mObject->getDataBlock()->getSumbergeCoverage() ); // Choose a Z Value. // Note: This is done so that the Actor will either path under the // water, or it will swim along the water's surface. position.z = getMin( mountPosition.z, sumbersionPosition ); // Under Water? underWater = ( position.z < sumbersionPosition ); } // Under Water? if ( !underWater ) { // Fetch Y Column. VectorF forwardVector; transform.getColumn( 1, &forwardVector ); // Determine Angle. const F32 &angle = -mAtan2( -forwardVector.x, forwardVector.y ); // Reset Transform. transform.set( EulerF( 0.f, 0.f, angle ) ); // In the air? if ( !isOnGround() ) { // Apply z-axis force. position.z += ( getVelocity().z * pDelta ); } } // Update Transform. transform.setPosition( position ); // Apply Update. setTransform( transform ); } break; default : { // Fetch Transform. MatrixF transform = getTransform(); // Determine the Post-Tick Position. Point3F postTickPosition = getPosition() + ( getVelocity() * pDelta ); // Set the Post Tick Position. transform.setPosition( postTickPosition ); // Apply the Transform. setTransform( transform ); } break; } // Push Delta. mInterpController.pushDelta( getTransform() ); }
/** * This method gets the move list for an object, in the case * of the AI, it actually calculates the moves, and then * sends them down the pipe. * * @param movePtr Pointer to move the move list into * @param numMoves Number of moves in the move list */ U32 AIClient::getMoveList( Move **movePtr,U32 *numMoves ) { //initialize the move structure and return pointers mMove = NullMove; *movePtr = &mMove; *numMoves = 1; // Check if we got a player mPlayer = NULL; mPlayer = dynamic_cast<Player *>( getControlObject() ); // We got a something controling us? if( !mPlayer ) return 1; // What is The Matrix? MatrixF moveMatrix; moveMatrix.set( EulerF( 0, 0, 0 ) ); moveMatrix.setColumn( 3, Point3F( 0, 0, 0 ) ); moveMatrix.transpose(); // Position / rotation variables F32 curYaw, curPitch; F32 newYaw, newPitch; F32 xDiff, yDiff; F32 moveSpeed = mMoveSpeed; switch( mMoveMode ) { case ModeStop: return 1; // Stop means no action break; case ModeStuck: // Fall through, so we still try to move case ModeMove: // Get my location MatrixF const& myTransform = mPlayer->getTransform(); myTransform.getColumn( 3, &mLocation ); // Set rotation variables Point3F rotation = mPlayer->getRotation(); Point3F headRotation = mPlayer->getHeadRotation(); curYaw = rotation.z; curPitch = headRotation.x; xDiff = mAimLocation.x - mLocation.x; yDiff = mAimLocation.y - mLocation.y; // first do Yaw if( !mIsZero( xDiff ) || !mIsZero( yDiff ) ) { // use the cur yaw between -Pi and Pi while( curYaw > M_2PI_F ) curYaw -= M_2PI_F; while( curYaw < -M_2PI_F ) curYaw += M_2PI_F; // find the new yaw newYaw = mAtan2( xDiff, yDiff ); // find the yaw diff F32 yawDiff = newYaw - curYaw; // make it between 0 and 2PI if( yawDiff < 0.0f ) yawDiff += M_2PI_F; else if( yawDiff >= M_2PI_F ) yawDiff -= M_2PI_F; // now make sure we take the short way around the circle if( yawDiff > M_2PI_F ) yawDiff -= M_2PI_F; else if( yawDiff < -M_2PI_F ) yawDiff += M_2PI_F; mMove.yaw = yawDiff; // set up the movement matrix moveMatrix.set( EulerF( 0.0f, 0.0f, newYaw ) ); } else moveMatrix.set( EulerF( 0.0f, 0.0f, curYaw ) ); // next do pitch F32 horzDist = Point2F( mAimLocation.x, mAimLocation.y ).len(); if( !mIsZero( horzDist ) ) { //we shoot from the gun, not the eye... F32 vertDist = mAimLocation.z; newPitch = mAtan2( horzDist, vertDist ) - ( M_2PI_F / 2.0f ); F32 pitchDiff = newPitch - curPitch; mMove.pitch = pitchDiff; } // finally, mMove towards mMoveDestination xDiff = mMoveDestination.x - mLocation.x; yDiff = mMoveDestination.y - mLocation.y; // Check if we should mMove, or if we are 'close enough' if( ( ( mFabs( xDiff ) > mMoveTolerance ) || ( mFabs( yDiff ) > mMoveTolerance ) ) && ( !mIsZero( mMoveSpeed ) ) ) { if( mIsZero( xDiff ) ) mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed ); else if( mIsZero( yDiff ) ) mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed ); else if( mFabs( xDiff ) > mFabs( yDiff ) ) { F32 value = mFabs( yDiff / xDiff ) * mMoveSpeed; mMove.y = ( mLocation.y > mMoveDestination.y ? -value : value ); mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed ); } else { F32 value = mFabs( xDiff / yDiff ) * mMoveSpeed; mMove.x = ( mLocation.x > mMoveDestination.x ? -value : value ); mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed ); } //now multiply the mMove vector by the transpose of the object rotation matrix moveMatrix.transpose(); Point3F newMove; moveMatrix.mulP( Point3F( mMove.x, mMove.y, 0.0f ), &newMove ); //and sub the result back in the mMove structure mMove.x = newMove.x; mMove.y = newMove.y; // We should check to see if we are stuck... if( mLocation.x == mLastLocation.x && mLocation.y == mLastLocation.y && mLocation.z == mLastLocation.z ) { // We're stuck...probably setMoveMode( ModeStuck ); } else setMoveMode( ModeMove ); } else { // Ok, we are close enough, lets stop // setMoveMode( ModeStop ); // DON'T use this, it'll throw the wrong callback mMoveMode = ModeStop; throwCallback( "onReachDestination" ); // Callback } break; } // Test for target location in sight RayInfo dummy; Point3F targetLoc = mMoveDestination; // Change this if( mPlayer ) { if( !mPlayer->getContainer()->castRay( mLocation, targetLoc, StaticShapeObjectType | StaticObjectType | TerrainObjectType, &dummy ) ) { if( !mTargetInLOS ) throwCallback( "onTargetEnterLOS" ); } else { if( mTargetInLOS ) throwCallback( "onTargetExitLOS" ); } } // Copy over the trigger status for( int i = 0; i < MaxTriggerKeys; i++ ) { mMove.trigger[i] = mTriggers[i]; mTriggers[i] = false; } return 1; }