/************************************************************ * * * flying.c * * * * * LPGE 1997 * * * * Copyright (C) 1996 Sony Computer Entertainment Inc. * * All Rights Reserved * * * ***********************************************************/ /**************************************************************************** includes ****************************************************************************/ #include "sys_libs.h" #include "object.h" #include "trig.h" #include "flying.h" #include "tunnel.h" #include "tunnel2.h" #include "main.h" /**************************************************************************** macros ****************************************************************************/ #define ValidID(id) ( (id >= 0) && (id < NumberTunnelSections) ) #define ANTI_DIRECTION(direction) (direction) == BACKWARDS ? FORWARDS : BACKWARDS #define SIGN_OF(x) ( ((x) >= 0) ? 1 : -1 ) /**************************************************************************** functions ****************************************************************************/ // if speedFactor is fixed at a power of 2, // lots of multiplies and divides can be replaced by shifts void HandleSplinePathFollower (ObjectHandler* object) { int sectionID, offset, newSectionID; int nextSectionID, previousSectionID; TunnelSection *section, *nextSection, *previousSection; int offsetAngle; VECTOR splineVector, offsetVector; int shipAngle, curveAngle; int previousAngle; assert(object != NULL); previousAngle = object->angle; if (object->startFrame == -1) { object->startFrame = frameNumber; object->currentPositionIndex = 0; } sectionID = object->currentPositionIndex >> 6; assert(ValidID(sectionID)); offset = object->currentPositionIndex % SPLINE_PATH_FOLLOWER_SPEED_FACTOR; section = &TheTunnel[sectionID]; nextSectionID = GetRelativeSection(sectionID, 1, FORWARDS); nextSection = &TheTunnel[nextSectionID]; previousSectionID = GetRelativeSection(sectionID, 1, BACKWARDS); previousSection = &TheTunnel[previousSectionID]; FindSectionSplineVector(sectionID, &splineVector); offsetVector.vx = (splineVector.vx * offset) >> 6; offsetVector.vy = (splineVector.vy * offset) >> 6; offsetVector.vz = (splineVector.vz * offset) >> 6; if (offset < HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR) // first half of section { switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: curveAngle = section->thetaY; break; case Y_Z_PLANE_ONLY: curveAngle = section->thetaX; break; default: assert(FALSE); } if (curveAngle == 0) { // straight shipAngle = section->totalAngle; setVECTOR( &object->position, section->splinePoint.vx + offsetVector.vx, section->splinePoint.vy + offsetVector.vy, section->splinePoint.vz + offsetVector.vz); } else { offsetAngle = (curveAngle * (offset + HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR)) >> 6; shipAngle = previousSection->totalAngle + offsetAngle; ProperSortObjectPositionOnSplineCurve(object, curveAngle, previousSection, offset, SPLINE_PATH_FOLLOWER_SPEED_FACTOR); } } else // second half of section { switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: curveAngle = nextSection->thetaY; break; case Y_Z_PLANE_ONLY: curveAngle = nextSection->thetaX; break; default: assert(FALSE); } if (curveAngle == 0) { // straight shipAngle = section->totalAngle; setVECTOR( &object->position, section->splinePoint.vx + offsetVector.vx, section->splinePoint.vy + offsetVector.vy, section->splinePoint.vz + offsetVector.vz); } else { offsetAngle = (curveAngle * (offset - HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR)) >> 6; shipAngle = section->totalAngle + offsetAngle; ProperSortObjectPositionOnSplineCurve(object, curveAngle, section, offset, SPLINE_PATH_FOLLOWER_SPEED_FACTOR); } } object->angle = -shipAngle; while (object->angle < 0) object->angle += ONE; while (object->angle >= ONE) object->angle -= ONE; // make spline followers tilt object->tilt = object->angle - previousAngle; while (object->tilt < 0) object->tilt += ONE; while (object->tilt >= ONE) object->tilt -= ONE; SortMatrixFromAngle(object); // lastly: move onward for next frame object->currentPositionIndex += object->discreteSpeed; if (object->currentPositionIndex >= (NumberTunnelSections << 6)) object->currentPositionIndex -= (NumberTunnelSections << 6); else if (object->currentPositionIndex < 0) object->currentPositionIndex += (NumberTunnelSections << 6); newSectionID = object->currentPositionIndex >> 6; if (newSectionID >= NumberTunnelSections) newSectionID -= NumberTunnelSections; else if (newSectionID < 0) newSectionID += NumberTunnelSections; assert(ValidID(newSectionID)); object->tunnelSection = newSectionID; if (sectionID != newSectionID) { if (object->racingFlag == TRUE) { SortShipAdvancing(object, sectionID, newSectionID); } } } void ProperSortObjectPositionOnSplineCurve (ObjectHandler *object, int curveAngle, TunnelSection *section, int offset, int speed) { VECTOR circleCentre; int circleRadius, angleOnCircle, offsetAngle, initialCircleAngle; VECTOR tunnelPosition, absoluteOffset; int denom; assert(curveAngle != 0); assert(speed != 0); assert(TunnelSectionLength != 0); assert(speed == SPLINE_PATH_FOLLOWER_SPEED_FACTOR); denom = 2 * rtan(curveAngle/2); if (denom == 0) denom = 1; circleRadius = (TunnelSectionLength * ONE) / denom; circleRadius = abs(circleRadius); switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: if (curveAngle > 0) // turn to left { setVECTOR( &circleCentre, -circleRadius, 0, TunnelSectionLength/2); initialCircleAngle = 0; if (offset < HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR) { offsetAngle = (curveAngle * (offset+HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR)) >> 6; } else { offsetAngle = (curveAngle * (offset-HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR)) >> 6; } } else // turn to right { setVECTOR( &circleCentre, circleRadius, 0, TunnelSectionLength/2); initialCircleAngle = 2048; if (offset < HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR) { offsetAngle = (curveAngle * (offset+HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR)) >> 6; } else { offsetAngle = (curveAngle * (offset-HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR)) >> 6; } } angleOnCircle = initialCircleAngle + offsetAngle; setVECTOR( &tunnelPosition, circleCentre.vx + ((circleRadius * rcos(angleOnCircle)) / ONE), object->position.vy, // same as before circleCentre.vz + ((circleRadius * rsin(angleOnCircle)) / ONE) ); break; case Y_Z_PLANE_ONLY: if (curveAngle > 0) // turn down { setVECTOR( &circleCentre, 0, circleRadius, TunnelSectionLength/2); initialCircleAngle = 1024; if (offset < HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR) { offsetAngle = (curveAngle * (offset+HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR)) >> 6; } else { offsetAngle = (curveAngle * (offset-HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR)) >> 6; } } else // turn up { setVECTOR( &circleCentre, 0, -circleRadius, TunnelSectionLength/2); initialCircleAngle = 3072; if (offset < HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR) { offsetAngle = (curveAngle * (offset+HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR)) >> 6; } else { offsetAngle = (curveAngle * (offset-HALF_SPLINE_PATH_FOLLOWER_SPEED_FACTOR)) >> 6; } } angleOnCircle = initialCircleAngle - offsetAngle; setVECTOR( &tunnelPosition, object->position.vx, // same as before circleCentre.vy - ((circleRadius * rsin(angleOnCircle)) / ONE), circleCentre.vz + ((circleRadius * rcos(angleOnCircle)) / ONE) ); break; default: assert(FALSE); } ExpressSubPointInSuper( &tunnelPosition, §ion->coordinateData, &absoluteOffset); setVECTOR( &object->position, section->splinePoint.vx + absoluteOffset.vx, section->splinePoint.vy + absoluteOffset.vy, section->splinePoint.vz + absoluteOffset.vz); } void HandleSplinePathDeviator (ObjectHandler *object) { // do biz } // finds point on spline path // DOES compensate for curvature, ie the total set of these points // curves smoothly around bends void GetPointOnSplinePath (int sectionID, int offsetRatio, VECTOR *point) { int nextSectionID, previousSectionID; TunnelSection *section, *nextSection, *previousSection; VECTOR splineVector, offsetVector; int curveAngle; assert(ValidID(sectionID)); assert(offsetRatio >= 0); assert(offsetRatio < ONE); section = &TheTunnel[sectionID]; nextSectionID = GetRelativeSection(sectionID, 1, FORWARDS); nextSection = &TheTunnel[nextSectionID]; previousSectionID = GetRelativeSection(sectionID, 1, BACKWARDS); previousSection = &TheTunnel[previousSectionID]; FindSectionSplineVector(sectionID, &splineVector); offsetVector.vx = (splineVector.vx * offsetRatio) >> 12; offsetVector.vy = (splineVector.vy * offsetRatio) >> 12; offsetVector.vz = (splineVector.vz * offsetRatio) >> 12; if (offsetRatio < 2048) // first half of section { switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: curveAngle = section->thetaY; break; case Y_Z_PLANE_ONLY: curveAngle = section->thetaX; break; default: assert(FALSE); } if (curveAngle == 0) { // straight setVECTOR(point, section->splinePoint.vx + offsetVector.vx, section->splinePoint.vy + offsetVector.vy, section->splinePoint.vz + offsetVector.vz); } else // curve { GetPointOnSplineCurve(curveAngle, previousSection, offsetRatio, point); } } else // second half of section { switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: curveAngle = nextSection->thetaY; break; case Y_Z_PLANE_ONLY: curveAngle = nextSection->thetaX; break; default: assert(FALSE); } if (curveAngle == 0) { // straight setVECTOR(point, section->splinePoint.vx + offsetVector.vx, section->splinePoint.vy + offsetVector.vy, section->splinePoint.vz + offsetVector.vz); } else // curve { GetPointOnSplineCurve(curveAngle, section, offsetRatio, point); } } } void GetPointOnSplineCurve (int curveAngle, TunnelSection *section, int offsetRatio, VECTOR *point) { VECTOR circleCentre; int circleRadius, angleOnCircle, offsetAngle, initialCircleAngle; VECTOR tunnelPosition, absoluteOffset; int denom; assert(curveAngle != 0); assert(section != NULL); assert(offsetRatio >= 0); assert(offsetRatio < ONE); assert(TunnelSectionLength != 0); denom = 2 * rtan(curveAngle/2); if (denom == 0) denom = 1; circleRadius = (TunnelSectionLength * ONE) / denom; circleRadius = abs(circleRadius); switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: if (curveAngle > 0) // turn to left { setVECTOR( &circleCentre, -circleRadius, 0, TunnelSectionLength/2); initialCircleAngle = 0; if (offsetRatio < 2048) { offsetAngle = (curveAngle * (offsetRatio+2048)) >> 12; } else { offsetAngle = (curveAngle * (offsetRatio-2048)) >> 12; } } else // turn to right { setVECTOR( &circleCentre, circleRadius, 0, TunnelSectionLength/2); initialCircleAngle = 2048; if (offsetRatio < 2048) { offsetAngle = (curveAngle * (offsetRatio+2048)) >> 12; } else { offsetAngle = (curveAngle * (offsetRatio-2048)) >> 12; } } angleOnCircle = initialCircleAngle + offsetAngle; setVECTOR( &tunnelPosition, circleCentre.vx + ((circleRadius * rcos(angleOnCircle)) >> 12), 0, // same as before circleCentre.vz + ((circleRadius * rsin(angleOnCircle)) >> 12) ); break; case Y_Z_PLANE_ONLY: if (curveAngle > 0) // turn down { setVECTOR( &circleCentre, 0, circleRadius, TunnelSectionLength/2); initialCircleAngle = 1024; if (offsetRatio < 2048) { offsetAngle = (curveAngle * (offsetRatio+2048)) >> 12; } else { offsetAngle = (curveAngle * (offsetRatio-2048)) >> 12; } } else // turn up { setVECTOR( &circleCentre, 0, -circleRadius, TunnelSectionLength/2); initialCircleAngle = 3072; if (offsetRatio < 2048) { offsetAngle = (curveAngle * (offsetRatio+2048)) >> 12; } else { offsetAngle = (curveAngle * (offsetRatio-2048)) >> 12; } } angleOnCircle = initialCircleAngle - offsetAngle; setVECTOR( &tunnelPosition, 0, // same as before circleCentre.vy - ((circleRadius * rsin(angleOnCircle)) >> 12), circleCentre.vz + ((circleRadius * rcos(angleOnCircle)) >> 12) ); break; default: assert(FALSE); } ExpressSubPointInSuper( &tunnelPosition, §ion->coordinateData, &absoluteOffset); setVECTOR(point, section->splinePoint.vx + absoluteOffset.vx, section->splinePoint.vy + absoluteOffset.vy, section->splinePoint.vz + absoluteOffset.vz); } int FindDirectionShipPointsInTunnel (ObjectHandler *ship) { int direction; VECTOR zAxis, relativeZaxis; int sectionID; TunnelSection *thisSection; sectionID = ship->tunnelSection; assert(ValidID(sectionID)); thisSection = &TheTunnel[sectionID]; setVECTOR( &zAxis, ship->coord.coord.m[2][0], ship->coord.coord.m[2][1], ship->coord.coord.m[2][2]); ExpressSuperPointInSub( &zAxis, &thisSection->coordinateData, &relativeZaxis); if (relativeZaxis.vz > 0) direction = FORWARDS; else direction = BACKWARDS; switch (ship->modelFlag) { case TMD_RIGHT_WAY: break; case TMD_WRONG_WAY: direction = ANTI_DIRECTION(direction); break; default: assert(FALSE); } return direction; } void HandleProperDynamicFlier2 (ObjectHandler *object) { int sectionID; TunnelSection *section; int onCurveFlag; int sectionAngle, shipAngle, differenceAngle; sectionID = object->tunnelSection; assert(ValidID(sectionID)); section = &TheTunnel[sectionID]; sectionAngle = 4096 - section->totalAngle; // section angles inferred differently while (sectionAngle >= ONE) sectionAngle -= ONE; shipAngle = object->angle; differenceAngle = sectionAngle - shipAngle; // if +ve, ship needs to curve right if (frameNumber % 4 == 0) { printf("sectionAngle %d\n", sectionAngle); printf("shipAngle %d\n", shipAngle); printf("differenceAngle %d\n", differenceAngle); } onCurveFlag = SectionIsCurveSection(sectionID); if (onCurveFlag == FALSE) // on a straight { if (abs(differenceAngle) > abs(object->rotationPower.vx)) { object->tilt += (abs(object->rotationPower.vx)) * SIGN_OF(differenceAngle); object->velocity.vz -= object->accelerationSpeed; if (frameNumber % 4 == 0) { printf("straight: way off spline path\n"); printf("diff angle %d\n", differenceAngle); } } else { object->tilt += differenceAngle; object->velocity.vz += object->accelerationSpeed; if (frameNumber % 4 == 0) { printf("straight: Close enough to spline path\n"); printf("diff angle %d\n", differenceAngle); } } } else { if (abs(differenceAngle) > abs(object->rotationPower.vx)) { object->tilt += (abs(object->rotationPower.vx)) * SIGN_OF(differenceAngle); object->velocity.vz -= object->accelerationSpeed; if (frameNumber % 4 == 0) { printf("Curve: more than can take\n"); printf("diff angle %d\n", differenceAngle); } } else { object->tilt += differenceAngle; object->velocity.vz += object->accelerationSpeed; if (frameNumber % 4 == 0) { printf("Curve: Can take\n"); printf("diff angle %d\n", differenceAngle); } } } } int SectionIsCurveSection (int sectionID) { int baseAngle; assert(ValidID(sectionID)); switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: baseAngle = TheTunnel[sectionID].thetaY; break; case Y_Z_PLANE_ONLY: baseAngle = TheTunnel[sectionID].thetaX; break; default: assert(FALSE); } if (baseAngle == 0) return FALSE; else return TRUE; } int FindRequiredTurnAnglePerFrameGivenSpeed (int speed, int sectionID) { TunnelSection *section; int baseAngle; int anglePerFrame; assert(TunnelSectionLength > 0); assert(ValidID(sectionID)); //assert(speed > 0); if (speed == 0) return 0; assert(speed < (TunnelSectionLength/2)); section = &TheTunnel[sectionID]; switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: baseAngle = section->thetaY; break; case Y_Z_PLANE_ONLY: baseAngle = section->thetaX; break; default: assert(FALSE); } if (baseAngle == 0) { anglePerFrame = 0; } else { // timePerSection would be TunnelSectionLength / speed // anglePerFrame would be baseAngle / timePerSection anglePerFrame = (baseAngle * speed) / TunnelSectionLength; } return -anglePerFrame; } int GetNewShipSpeedGivenAction (ObjectHandler *ship, int accelerationAction) { int newSpeed = -1; VECTOR velocityCopy; setVECTOR( &velocityCopy, ship->velocity.vx, ship->velocity.vy, ship->velocity.vz); switch(accelerationAction) { case MAX_ACCELERATION: velocityCopy.vz += ship->accelerationSpeed; ScaleVector( &velocityCopy, ONE - ship->movementFrictionCoefficient); newSpeed = abs(velocityCopy.vz); break; case MAINTAIN_SPEED: newSpeed = abs(ship->velocity.vz); break; case MAX_BRAKE: velocityCopy.vz -= ship->accelerationSpeed; ScaleVector( &velocityCopy, ONE - ship->movementFrictionCoefficient); newSpeed = abs(velocityCopy.vz); break; default: assert(FALSE); } return newSpeed; } void HandleProperDynamicFlier3 (ObjectHandler *object) { int sectionID, offsetRatio; TunnelSection *section; int sectionAngle, shipAngle, differenceAngle; int requiredTurnThisFrame; sectionID = object->tunnelSection; assert(ValidID(sectionID)); section = &TheTunnel[sectionID]; offsetRatio = GetOffsetOfPoint( &object->position, sectionID); sectionAngle = GetTotalAngleAtPoint (sectionID, offsetRatio); sectionAngle = ONE - sectionAngle; while (sectionAngle >= ONE) sectionAngle -= ONE; while (sectionAngle < 0) sectionAngle += ONE; shipAngle = object->angle; differenceAngle = sectionAngle - shipAngle; // if +ve, ship needs to curve right if (differenceAngle >= 2048) differenceAngle -= ONE; else if (differenceAngle <= -2048) differenceAngle += ONE; assert(abs(differenceAngle) <= 2048); if (abs(object->tilt) >= 512) { printf("object->tilt %d\n", object->tilt); assert(FALSE); } requiredTurnThisFrame = differenceAngle - object->tilt; if (abs(requiredTurnThisFrame) > abs(object->rotationPower.vx)) { object->velocity.vz -= object->brakingSpeed; object->tilt += (abs(object->rotationPower.vx)) * SIGN_OF(differenceAngle); } else { object->velocity.vz += object->accelerationSpeed; object->tilt += requiredTurnThisFrame; } } // credit to Stuart Ashley for this one // see blackpsx\newcode\car\main.c // Currentl x_z only, easy to adapt (just first turning part) void HandleDubiousBlagger (ObjectHandler *ship) { int sectionID, offsetRatio, currentSection; TunnelSection *section; int sectionAngle, shipAngle, differenceAngle; VECTOR worldMoveInSixteenFrames, worldPos, tunnelPosition, temp; int speed, effectiveRadius; VECTOR tunnelXaxis, tunnelYaxis; sectionID = ship->tunnelSection; assert(ValidID(sectionID)); section = &TheTunnel[sectionID]; offsetRatio = GetOffsetOfPoint( &ship->position, sectionID); sectionAngle = GetTotalAngleAtPoint (sectionID, offsetRatio); sectionAngle = ONE - sectionAngle; shipAngle = ship->angle; differenceAngle = sectionAngle - shipAngle; // if +ve, ship needs to curve right if (differenceAngle > 0) { ship->tilt += ship->rotationPower.vy; } else if (differenceAngle < 0) { ship->tilt -= ship->rotationPower.vy; } speed = jbVectorMeasure( &ship->velocity); if (speed < 10) { // find vector moved in 16 frames, in world coords ApplyMatrixLV(&ship->matrix, &ship->velocity, &worldMoveInSixteenFrames); // hence new position setVECTOR( &worldPos, ship->position.vx + worldMoveInSixteenFrames.vx, ship->position.vy + worldMoveInSixteenFrames.vy, ship->position.vz + worldMoveInSixteenFrames.vz); currentSection = sectionID; setVECTOR( &temp, worldPos.vx, worldPos.vy, worldPos.vz); temp.vx -= section->splinePoint.vx; temp.vy -= section->splinePoint.vy; temp.vz -= section->splinePoint.vz; ExpressSuperPointInSub( &temp, §ion->coordinateData, &tunnelPosition); //assert(tunnelPosition.vz >= 0); if (tunnelPosition.vz < 0) { currentSection = GetRelativeSection(currentSection, 1, BACKWARDS); setVECTOR( &temp, worldPos.vx, worldPos.vy, worldPos.vz); temp.vx -= TheTunnel[currentSection].splinePoint.vx; temp.vy -= TheTunnel[currentSection].splinePoint.vy; temp.vz -= TheTunnel[currentSection].splinePoint.vz; ExpressSuperPointInSub( &temp, &TheTunnel[currentSection].coordinateData, &tunnelPosition); assert(tunnelPosition.vz >= 0); } else { while (tunnelPosition.vz >= TunnelSectionLength) { currentSection = GetRelativeSection(currentSection, 1, FORWARDS); setVECTOR( &temp, worldPos.vx, worldPos.vy, worldPos.vz); temp.vx -= TheTunnel[currentSection].splinePoint.vx; temp.vy -= TheTunnel[currentSection].splinePoint.vy; temp.vz -= TheTunnel[currentSection].splinePoint.vz; ExpressSuperPointInSub( &temp, &TheTunnel[currentSection].coordinateData, &tunnelPosition); } } // circle which ship is OK inside effectiveRadius = TunnelMiddleRadius - ship->collisionRadius; // standard circle-centred-at-origin comparison // ie x**2 + y**2 >= r**2 ?? if ( ((tunnelPosition.vx * tunnelPosition.vx) + (tunnelPosition.vy * tunnelPosition.vy)) >= (effectiveRadius * effectiveRadius) ) { ship->velocity.vz -= ship->brakingSpeed; if (ship->velocity.vz < 0) ship->velocity.vz = 0; } else { ship->velocity.vz += ship->accelerationSpeed; } } else { ship->velocity.vz += ship->accelerationSpeed; } // get actual tunnel-relative position setVECTOR( &worldPos, ship->position.vx, ship->position.vy, ship->position.vz); worldPos.vx -= section->splinePoint.vx; worldPos.vy -= section->splinePoint.vy; worldPos.vz -= section->splinePoint.vz; ExpressSuperPointInSub( &worldPos, §ion->coordinateData, &tunnelPosition); setVECTOR( &tunnelXaxis, section->coordinateData.coord.m[0][0], section->coordinateData.coord.m[0][1], section->coordinateData.coord.m[0][2]); setVECTOR( &tunnelYaxis, section->coordinateData.coord.m[1][0], section->coordinateData.coord.m[1][1], section->coordinateData.coord.m[1][2]); // move ship back towards spline path switch(TheTunnelDescription.twoDflag) { case X_Z_PLANE_ONLY: if (tunnelPosition.vx > 0) { ship->position.vx -= (tunnelXaxis.vx / 512); ship->position.vy -= (tunnelXaxis.vy / 5129); ship->position.vz -= (tunnelXaxis.vz / 512); } else if (tunnelPosition.vx < 0) { ship->position.vx += (tunnelXaxis.vx / 512); ship->position.vy += (tunnelXaxis.vy / 512); ship->position.vz += (tunnelXaxis.vz / 512); } break; case Y_Z_PLANE_ONLY: if (tunnelPosition.vy > 0) { ship->velocity.vy -= 2; ship->position.vx -= (tunnelYaxis.vx >> 9); ship->position.vy -= (tunnelYaxis.vy >> 9); ship->position.vz -= (tunnelYaxis.vz >> 9); } else if (tunnelPosition.vy < 0) { ship->velocity.vy += 2; ship->position.vx -= (tunnelYaxis.vx >> 9); ship->position.vy -= (tunnelYaxis.vy >> 9); ship->position.vz -= (tunnelYaxis.vz >> 9); } break; default: assert(FALSE); } }