private float CalcVelocity(float currentVelocity, RCT2TrackElements.RCT2TrackElement element, RCT2Qualifier qualifier, RCT2TrackElementProperty property) { //Check if it's a Chain lift or a station if (qualifier.IsChainLift) { return(Math.Max(currentVelocity, ChainLiftSpeed)); } else if (element == RCT2TrackElements.RCT2TrackElement.BeginStation || element == RCT2TrackElements.RCT2TrackElement.MiddleStation || element == RCT2TrackElements.RCT2TrackElement.EndStation) { return(Math.Max(currentVelocity, StationSpeed)); } float acceleration = 0; //Try to get current acceleration from the map try { acceleration = RCT2TrackElementProperty.TrackAccelerationMap[(int)-property.Displacement.Y]; } catch { Console.WriteLine($"Unable to find acceleration value for track piece {element.ToString()}"); } float newVelocity = currentVelocity + acceleration; if (newVelocity <= 0) { velocityGoesNegative = true; } return(newVelocity); }
private int CalculateFitness(RCT2RideData candidate) { int fitness = 0; //Get Displacement to End & Max Y Displacement Vector3 prevWorldPos = new Vector3(0.0f, 0.0f, 0.0f); int worldDirectionChange = 0; double maxVarianceFromStart = 0; int maxYVariance = 0; for (int i = 0; i < candidate.TrackData.TrackData.Count; i++) { RCT2TrackElements.RCT2TrackElement currentElement = candidate.TrackData.TrackData[i].TrackElement; RCT2TrackElementProperty property = RCT2TrackElements.TrackElementPropertyMap[currentElement]; Vector3 worldDisplacement = candidate.TrackData.LocalDisplacementToWorld(property.Displacement, worldDirectionChange); //Update World Position Changes prevWorldPos += worldDisplacement; if (prevWorldPos.Y >= maxYVariance) { maxYVariance = (int)prevWorldPos.Y; } if (prevWorldPos.Length() >= maxVarianceFromStart) { maxVarianceFromStart = prevWorldPos.Length(); } //Update World Direction Changes worldDirectionChange = candidate.TrackData.UpdateRotation(worldDirectionChange, property.DirectionChange); } maxYVariance -= (int)prevWorldPos.Y; maxVarianceFromStart -= prevWorldPos.Length(); Vector3 displacementToEnd = new Vector3(0, 0, 0); displacementToEnd.X = Math.Abs(prevWorldPos.X); displacementToEnd.Y = Math.Abs(prevWorldPos.Y); displacementToEnd.Z = Math.Abs(prevWorldPos.Z); //Console.WriteLine(maxYDisplacement); double displacementToEndLength = displacementToEnd.Length(); fitness = (int)(((candidate.ExcitementTimesTen / (1 + candidate.NauseaTimesTen)) + /*maxVarianceFromStart*/ +maxYVariance) * 1000); return(fitness); }
public Vector2 CalculateRequiredMapSpace() { Vector2 requiredMapSpace = new Vector2(0.0f, 0.0f); Vector2 posDisplacementCounter = new Vector2(0.0f, 0.0f); int currentDirectionOffset = 0; List <Vector3> usedCells = new List <Vector3>(); Vector3 prevWorldDisplacement = new Vector3(0.0f, 0.0f, 0.0f); for (int i = 0; i < TrackData.Count; i++) { RCT2TrackElements.RCT2TrackElement element = TrackData[i].TrackElement; RCT2TrackElementProperty property = RCT2TrackElements.TrackElementPropertyMap[element]; Vector3 worldDisplacement = LocalDisplacementToWorld(property.Displacement, currentDirectionOffset); prevWorldDisplacement += worldDisplacement; usedCells.Add(prevWorldDisplacement); currentDirectionOffset = UpdateRotation(currentDirectionOffset, property.DirectionChange); } //Find max X and Y distance of cells in list List <float> xVals = new List <float>(); List <float> yVals = new List <float>(); usedCells.ForEach(v => { xVals.Add(v.X); yVals.Add(v.Z); }); xVals.Sort(); yVals.Sort(); requiredMapSpace.X = Math.Abs(xVals.Last() - xVals.First()); requiredMapSpace.Y = Math.Abs(yVals.Last() - yVals.First()); return(requiredMapSpace); }
public int CalculateRideLengthInMeters() { //Approximate int rideLength = 0; for (int i = 0; i < TrackData.Count; i++) { RCT2TrackElements.RCT2TrackElement element = TrackData[i].TrackElement; RCT2TrackElementProperty property = RCT2TrackElements.TrackElementPropertyMap[element]; int curLength = 0; int counter = 0; if (property.Displacement.X != 0) { curLength += (int)Math.Abs(property.Displacement.X * HorizontalUnitsToMeters); counter++; } if (property.Displacement.Y != 0) { curLength += (int)Math.Abs(property.Displacement.Y * VerticalUnitsToMeters); counter++; } if (property.Displacement.Z != 0) { curLength += (int)Math.Abs(property.Displacement.Z * HorizontalUnitsToMeters); counter++; } if (counter > 0) { rideLength += (curLength / counter); } } return(rideLength); }
private RCT2TrackData GenerateWoodenRollerCoasterTrack() { RCT2TrackData trackData = new RCT2TrackData(); trackData.TrackData = new List <RCT2TrackPiece>(); Random random = new Random(); //Begin Station trackData.TrackData.Add(new RideData.RCT2TrackPiece() { TrackElement = RideData.RCT2TrackElements.RCT2TrackElement.BeginStation, Qualifier = new RideData.RCT2Qualifier() { IsChainLift = false, TrackColourSchemeNumber = 0, TrackRotation = RideData.RCT2Qualifier.RCT2QualifierRotation.Zero, AtTerminalStation = false, StationNumber = 0 } }); //End Station trackData.TrackData.Add(new RideData.RCT2TrackPiece() { TrackElement = RideData.RCT2TrackElements.RCT2TrackElement.EndStation, Qualifier = new RideData.RCT2Qualifier() { IsChainLift = false, TrackColourSchemeNumber = 0, TrackRotation = RideData.RCT2Qualifier.RCT2QualifierRotation.Zero, AtTerminalStation = false, StationNumber = 0 } }); //TODO: Marking valid track pieces as invalid // The prior connection check is failing for (int i = 2; i < length; i++) { List <RCT2TrackElements.RCT2TrackElement> candidates = RCT2TrackElements.FindValidSuccessors(whitelistedTracks, trackData.TrackData[i - 1].TrackElement); bool reselect = false; do { //If we have no candidates left if (candidates.Count <= 0) { return(null); //if (steppedBack) //{ // return null; //} //if (i < 3) //{ // throw new Exception("ERROR: Unable to create Coaster - Index stepped back too far"); //} ////Console.WriteLine("ERROR: No Valid Track Pieces Found, Stepping back and starting again"); //trackData.TrackData.Remove(trackData.TrackData.Last()); //i -= 2; //steppedBack = true; //TODO - Make this a better solution, right now it's a hack to make it work //break; } //Select our successor and remove it from the potential pool RCT2TrackElements.RCT2TrackElement successor = candidates[random.Next(candidates.Count)]; candidates.Remove(successor); RCT2TrackElementProperty property = RCT2TrackElements.TrackElementPropertyMap[successor]; //Console.WriteLine($"\tAttempting Track Piece {successor.ToString()}"); RCT2Qualifier qualifier; //Create our qualifier bit if (property.Displacement.Y > 0) { //TODO: Check if it should be a chain // Perform a lookup to see if previous track was chain lift // Or if our current velocity would be negative/0 // If not possible, do a lookup to see if previous X tracks contained a decline, if not we need one bool chainLift = trackData.TrackData[i - 1].Qualifier.IsChainLift; //If our current velocity would be negative or zero, we need a chain lift qualifier = new RCT2Qualifier() { IsChainLift = true, TrackColourSchemeNumber = 0, TrackRotation = RideData.RCT2Qualifier.RCT2QualifierRotation.Zero, AtTerminalStation = false, StationNumber = 0 }; } else { qualifier = new RCT2Qualifier() { IsChainLift = false, TrackColourSchemeNumber = 0, TrackRotation = RideData.RCT2Qualifier.RCT2QualifierRotation.Zero, AtTerminalStation = false, StationNumber = 0 }; } //Add to track trackData.TrackData.Add(new RCT2TrackPiece() { TrackElement = successor, Qualifier = qualifier }); //If it's invalid RCT2TrackData.InvalidityCode invalidityCode = trackData.CheckValidity(); if (invalidityCode != RCT2TrackData.InvalidityCode.Valid) { //Remove it and try again reselect = true; trackData.TrackData.Remove(trackData.TrackData.Last()); //Console.WriteLine($"\tInvalid Selection, Code: {invalidityCode.ToString()}"); } else { reselect = false; } } while (reselect); //Console.WriteLine($"{trackData.TrackData.Last().TrackElement.ToString()} Selected!"); } //for (int i = 0; i < trackData.TrackData.Count(); i++) //{ // Console.WriteLine("\t" + trackData.TrackData[i].TrackElement.ToString()); //} return(trackData); }
public InvalidityCode CheckValidity() { //Following Validity Checks: // - All Track Pieces connect to the one before it // - Track doesn't intersect with itself // - Track doesn't exceed max height // - Track doesn't go underground (Go lower than the station height) // - Track's velocity doesn't go negative // - Track has more than one OnRidePhoto bool hasRidePhoto = false; if (TrackData.Count <= 0) { return(InvalidityCode.NoTrack); } //Negative Velocity if (!velocityChecked) { PopulateRideStatistics(); } if (velocityGoesNegative) { return(InvalidityCode.NegativeVelocity); } float currentYDisplacement = 0; RCT2TrackElementProperty prevElementProperty = RCT2TrackElements.TrackElementPropertyMap[TrackData[0].TrackElement]; List <Vector3> UsedCells = new List <Vector3>(); Vector3 prevWorldPos = new Vector3(0.0f, 0.0f, 0.0f); int worldDirectionChange = 0; for (int i = 0; i < TrackData.Count; i++) { RCT2TrackElements.RCT2TrackElement currentElement = TrackData[i].TrackElement; RCT2TrackElementProperty property = RCT2TrackElements.TrackElementPropertyMap[currentElement]; // OnRidePhoto Checks if (currentElement == RCT2TrackElements.RCT2TrackElement.OnRidePhoto) { if (hasRidePhoto) { return(InvalidityCode.TwoRidePhotos); } else { hasRidePhoto = true; } } // Height Checks currentYDisplacement += property.Displacement.Y; if (currentYDisplacement >= MaxUnitsOfDisplacement) { return(InvalidityCode.ExceedMaxHeight); } if (currentYDisplacement < 0) { return(InvalidityCode.ExceedMinHeight); } //Prev Element Matching Test if (i > 0 && (prevElementProperty.OutputTrackBank != property.InputTrackBank || prevElementProperty.OutputTrackDegree != property.InputTrackDegree)) { return(InvalidityCode.DoesntConnectToPrior); } //Intersection Tests //Get the world version of our property displacement //ie, if the segment moves 1 forward, but is already rotated to the left // by 90°, then it actually moves right by 1 Vector3 worldDisplacement = LocalDisplacementToWorld(property.Displacement, worldDirectionChange); Vector3 testCell = prevWorldPos; //Check every tile used in this segment //Code is so gross Im so sorry for (int x = 1; x <= Math.Abs(worldDisplacement.X); x++) { testCell = prevWorldPos + new Vector3(Math.Sign(worldDisplacement.X) * (x), 0, 0); if (!UsedCells.Contains(testCell)) { UsedCells.Add(testCell); } else { return(InvalidityCode.Intersection); } } prevWorldPos = testCell; for (int y = 1; y <= Math.Abs(worldDisplacement.Y); y++) { testCell = prevWorldPos + new Vector3(0, Math.Sign(worldDisplacement.Y) * (y), 0); if (!UsedCells.Contains(testCell)) { UsedCells.Add(testCell); } else { return(InvalidityCode.Intersection); } } prevWorldPos = testCell; for (int z = 1; z <= Math.Abs(worldDisplacement.Z); z++) { testCell = prevWorldPos + new Vector3(0, 0, Math.Sign(worldDisplacement.Z) * (z)); if (!UsedCells.Contains(testCell)) { UsedCells.Add(testCell); } else { return(InvalidityCode.Intersection); } } prevWorldPos = testCell; //Update World Direction Changes worldDirectionChange = UpdateRotation(worldDirectionChange, property.DirectionChange); //Update variables prevElementProperty = property; } return(InvalidityCode.Valid); }
public void PopulateRideStatistics() { //Inversion Data int tempInvCount = 0; //Velocity Data float totalVelocity = 0; float vehicleVelocity = 0; float tempMaxSpeed = 0; //Drop Data int dropCount = 0; bool inDrop = false; float tempHighestDrop = 0; float curDropHeight = 0; int tempAirTimeInSeconds = 0; //G Forces float maxLatG = 0; float maxPosG = 0; float maxNegG = 0; float previousLatG = 0; float previousVertG = 0; for (int i = 0; i < TrackData.Count - 1; i++) { RCT2TrackElementProperty property = RCT2TrackElements.TrackElementPropertyMap[TrackData[i].TrackElement]; RCT2TrackElements.RCT2TrackElement element = TrackData[i].TrackElement; //----Check for ride inversions---- if (CheckInversion(property)) { tempInvCount++; } //----Velocity Calculations---- float tempVel = CalcVelocity(vehicleVelocity, element, TrackData[i].Qualifier, property); vehicleVelocity = tempVel; totalVelocity += tempVel; if (tempVel > tempMaxSpeed) { tempMaxSpeed = tempVel; } //----Drop Calculations---- //We've entered a drop if (property.Displacement.Y < 0 && !inDrop) { //Increment our counters inDrop = true; dropCount++; curDropHeight += property.Displacement.Y * VerticalUnitsToMeters; } //If we're still in a drop else if (property.Displacement.Y < 0) { //Add to our measurement of this drop curDropHeight += property.Displacement.Y * VerticalUnitsToMeters; } //If we end the drop else if (property.Displacement.Y >= 0 && inDrop) { //Reset our values inDrop = false; if (Math.Abs(curDropHeight) > tempHighestDrop) { tempHighestDrop = Math.Abs(curDropHeight); } curDropHeight = 0; } //----G Forces---- float latG = 0; float vertG = 0; TrackData[i].GetGForces(vehicleVelocity, out latG, out vertG); //Calc air time if (vertG < 0) { //TODO: Translate into seconds, not sure how to do that really! tempAirTimeInSeconds++; } //Get current G forces by averaging with previous ones vertG += previousVertG; vertG /= 2; previousVertG = vertG; latG += previousLatG; latG /= 2; previousLatG = latG; latG = Math.Abs(latG); //See if it's more than our current maX if (latG > maxLatG) { maxLatG = latG; } //Vertical Gs if (vertG < 0) { //Keep track of Max if (Math.Abs(vertG) > maxNegG) { maxNegG = Math.Abs(vertG); } } else { //Keep track of Max if (vertG > maxPosG) { maxPosG = vertG; } } } //Apply the new values to our global vars NumOfInversions = tempInvCount; NumOfDrops = dropCount; HighestDrop = (int)tempHighestDrop; AverageSpeed = (int)(totalVelocity / TrackData.Count); MaxSpeed = (int)tempMaxSpeed; velocityChecked = true; MaxLateralG = maxLatG; MaxPositiveG = maxPosG; MaxNegativeG = maxNegG; AirTimeInSeconds = tempAirTimeInSeconds; float localExcitement = CalculateExcitement(); float localIntensity = CalculateIntensity(); float localNausea = CalculateNausea(); ApplyPenalties(ref localExcitement, ref localIntensity, ref localNausea); Excitement = localExcitement; Intensity = localIntensity; Nausea = localNausea; }
/// <summary> /// Encode the RCT2RideData Structure into a Run-Length Unencoded Byte Array /// </summary> /// <param name="td6Data">The RCT2RideData Structure</param> /// <returns>A Byte array containing a full TD6 file</returns> public byte[] Encode(RCT2RideData td6Data) { List <byte> td6Bytes = new List <byte>(); // Go through the data structure described here and add the bytes in // http://freerct.github.io/RCTTechDepot-Archive/TD6.html byte dummyByte = 0; //00 Track Type td6Bytes.Add((byte)RCT2RideCode.RideNameCodeMap[td6Data.TrackType.RideType]); //01 ? td6Bytes.Add(dummyByte); td6Data.RideFeatures.Populate(td6Data.TrackData); //02 - Ride Features Byte 0 byte rideFeaturesByte0 = 0; //Straight Flat rideFeaturesByte0 |= (byte)((td6Data.RideFeatures.StraightFlat) ? 0x2 : 0x0); //Station Platform rideFeaturesByte0 |= (byte)((td6Data.RideFeatures.StationPlatform) ? 0x4 : 0x0); //Lift Chain rideFeaturesByte0 |= (byte)((td6Data.RideFeatures.LiftChain) ? 0x8 : 0x0); //Steep Lift Chain rideFeaturesByte0 |= (byte)((td6Data.RideFeatures.SteepLiftChain) ? 0x10 : 0x0); //Curve Lift Chain rideFeaturesByte0 |= (byte)((td6Data.RideFeatures.CurveLiftChain) ? 0x20 : 0x0); //Banking rideFeaturesByte0 |= (byte)((td6Data.RideFeatures.Banking) ? 0x40 : 0x0); //Vertical Loop rideFeaturesByte0 |= (byte)((td6Data.RideFeatures.VerticalLoop) ? 0x80 : 0x0); td6Bytes.Add(rideFeaturesByte0); //03 - Ride Features Byte 1 byte rideFeaturesByte1 = 0; //Normal Slope rideFeaturesByte1 |= (byte)((td6Data.RideFeatures.NormalSlope) ? 0x1 : 0x0); //Steep Slope rideFeaturesByte1 |= (byte)((td6Data.RideFeatures.SteepSlope) ? 0x2 : 0x0); //Flat-To-Steep rideFeaturesByte1 |= (byte)((td6Data.RideFeatures.FlatToSteep) ? 0x4 : 0x0); //Sloped Curves rideFeaturesByte1 |= (byte)((td6Data.RideFeatures.SlopedCurves) ? 0x8 : 0x0); //Steep Twist rideFeaturesByte1 |= (byte)((td6Data.RideFeatures.SteepTwist) ? 0x10 : 0x0); //S-Bends rideFeaturesByte1 |= (byte)((td6Data.RideFeatures.SBends) ? 0x20 : 0x0); //Small radius curves rideFeaturesByte1 |= (byte)((td6Data.RideFeatures.SmallRadiusCurves) ? 0x40 : 0x0); //Small radius banked rideFeaturesByte1 |= (byte)((td6Data.RideFeatures.SmallRadiusBanked) ? 0x80 : 0x0); td6Bytes.Add(rideFeaturesByte1); //04 - Ride Features Byte 2 byte rideFeaturesByte2 = 0; //Medium Radius Curves rideFeaturesByte2 |= (byte)((td6Data.RideFeatures.MediumRadiusCurves) ? 0x1 : 0x0); //Inline Twist rideFeaturesByte2 |= (byte)((td6Data.RideFeatures.InlineTwist) ? 0x2 : 0x0); //Half Loop rideFeaturesByte2 |= (byte)((td6Data.RideFeatures.HalfLoop) ? 0x4 : 0x0); //Half Corkscrew rideFeaturesByte2 |= (byte)((td6Data.RideFeatures.HalfCorkscrew) ? 0x8 : 0x0); //Tower Base/Vertical rideFeaturesByte2 |= (byte)((td6Data.RideFeatures.TowerBaseVertical) ? 0x10 : 0x0); //Helix Banked rideFeaturesByte2 |= (byte)((td6Data.RideFeatures.HelixBanked) ? 0x20 : 0x0); //Helix Banked rideFeaturesByte2 |= (byte)((td6Data.RideFeatures.HelixBanked) ? 0x40 : 0x0); //Helix Unbanked rideFeaturesByte2 |= (byte)((td6Data.RideFeatures.HelixUnbanked) ? 0x80 : 0x0); td6Bytes.Add(rideFeaturesByte2); //05 - Ride Features Byte 3 byte rideFeaturesByte3 = 0; //Brakes rideFeaturesByte3 |= (byte)((td6Data.RideFeatures.Brakes) ? 0x1 : 0x0); //Booster rideFeaturesByte3 |= (byte)((td6Data.RideFeatures.Booster) ? 0x2 : 0x0); //On-Ride-Photo rideFeaturesByte3 |= (byte)((td6Data.RideFeatures.OnRidePhoto) ? 0x4 : 0x0); //Water Splash rideFeaturesByte3 |= (byte)((td6Data.RideFeatures.WaterSplash) ? 0x8 : 0x0); //Vertical Track rideFeaturesByte3 |= (byte)((td6Data.RideFeatures.VerticalTrack) ? 0x10 : 0x0); //Barrel Roll rideFeaturesByte3 |= (byte)((td6Data.RideFeatures.BarrelRoll) ? 0x20 : 0x0); //Launched Lift Hill rideFeaturesByte3 |= (byte)((td6Data.RideFeatures.LaunchedLiftHill) ? 0x40 : 0x0); //Large Half Loop rideFeaturesByte3 |= (byte)((td6Data.RideFeatures.LargeHalfLoop) ? 0x80 : 0x0); td6Bytes.Add(rideFeaturesByte3); //06 Operating Mode td6Bytes.Add((byte)td6Data.OperatingMode); //07 Vehicle Colour Scheme //Third bit is set to 1 for RCT2 td6Bytes.Add((byte)(0x8 + td6Data.ColourScheme.ColourMode)); //08-47 32 sets of two byte colour specifiers for vehicles. //first byte = body colour, second byte = trim colour for (int i = 0; i < 32; i++) { if (i < td6Data.ColourScheme.BodyColours.Length) { td6Bytes.Add((byte)td6Data.ColourScheme.BodyColours[i]); } else { td6Bytes.Add((byte)RCT2VehicleColourScheme.RCT2Colour.Black); } if (i < td6Data.ColourScheme.TrimColours.Length) { td6Bytes.Add((byte)td6Data.ColourScheme.TrimColours[i]); } else { td6Bytes.Add((byte)RCT2VehicleColourScheme.RCT2Colour.Black); } } //48 ? td6Bytes.Add(dummyByte); //49 Entrance Style td6Bytes.Add((byte)td6Data.EntranceStyle); //4A Air Time (in seconds / 4) td6Bytes.Add((byte)(td6Data.AirTimeInSeconds / 4)); //4B Departure Control Flags //http://freerct.github.io/RCTTechDepot-Archive/controlFlags.html byte departureControlFlagsByte = 0; //Load Info if (td6Data.DepartureFlags.WaitForQuarterLoad) { departureControlFlagsByte = 0; } else if (td6Data.DepartureFlags.WaitForHalfLoad) { departureControlFlagsByte = 0x1; } else if (td6Data.DepartureFlags.WaitForThreeQuarterLoad) { departureControlFlagsByte = 0x2; } else if (td6Data.DepartureFlags.WaitForFullLoad) { departureControlFlagsByte = 0x3; } else if (td6Data.DepartureFlags.WaitForAnyLoad) { departureControlFlagsByte = 0x4; } //Departure Info if (td6Data.DepartureFlags.UseMaximumTime) { departureControlFlagsByte |= 0x80; } else if (td6Data.DepartureFlags.UseMinimumTime) { departureControlFlagsByte |= 0x40; } else if (td6Data.DepartureFlags.SyncWithAdjacentStation) { departureControlFlagsByte |= 0x20; } else if (td6Data.DepartureFlags.LeaveIfAnotherArrives) { departureControlFlagsByte |= 0x10; } else if (td6Data.DepartureFlags.WaitForLoad) { departureControlFlagsByte |= 0x8; } td6Bytes.Add(departureControlFlagsByte); //4C Number of Trains td6Bytes.Add((byte)td6Data.NumberOfTrains); //4D Number Of Cars per Train td6Bytes.Add((byte)td6Data.NumberOfCarsPerTrain); //4E Minimum Wait Time in Seconds td6Bytes.Add((byte)td6Data.MinWaitTimeInSeconds); //4F Maximum Wait Time in Seconds td6Bytes.Add((byte)td6Data.MaxWaitTimeInSeconds); //50 Speed (Powered Launch/Chairlift/Whoa Belly, or num of laps for go kart, or num of people in a maze) //1 bit = 3.616 km/hr 2.25 mph if (td6Data.SpeedOfPoweredLaunch != default(float)) { //Speed of powered launch td6Bytes.Add((byte)(Math.Round(td6Data.SpeedOfPoweredLaunch / MPHToBit))); } else if (td6Data.NumberOfGoKartLaps != default(int)) { //Max number of go kart laps td6Bytes.Add((byte)td6Data.NumberOfGoKartLaps); } else { //Max Number of people in maze td6Bytes.Add((byte)td6Data.MaxNumberOfPeopleMaze); } //51 Max Speed of Ride //1 bit = 3.616 km/hr 2.25 mph td6Bytes.Add((byte)(Math.Round(td6Data.MaxSpeedOfRide / MPHToBit))); //52 Average Speed of Ride //1 bit = 3.616 km/hr 2.25 mph td6Bytes.Add((byte)(Math.Round((td6Data.AverageSpeedOfRide / MPHToBit)))); //53-54 Ride Length in Meters td6Bytes.Add((byte)td6Data.RideLengthInMetres); td6Bytes.Add((byte)(td6Data.RideLengthInMetres >> 8)); //55 Positive G-Force //1 bit = 0.32g td6Bytes.Add((byte)(Math.Round((td6Data.PosGForce / GForceToBit)))); //56 Negative G-Force //1 bit = 0.32g td6Bytes.Add((byte)(Math.Round((td6Data.NegGForce / GForceToBit)))); //57 Lateral G-Force //1 bit = 0.32g td6Bytes.Add((byte)(Math.Round((td6Data.LatGForce / GForceToBit)))); //58 Number of Inversions //Max 31 td6Bytes.Add((byte)Math.Min(31, td6Data.NumberOfInversions)); //59 Number of drops //Max 63 td6Bytes.Add((byte)Math.Min(63, td6Data.NumberOfDrops)); //5A Highest Drop //1 bit = 3/4 meter td6Bytes.Add((byte)(Math.Round((td6Data.HighestDrop / DropMeterToBit)))); //5B Excitement td6Bytes.Add((byte)td6Data.ExcitementTimesTen); //5C Intensity td6Bytes.Add((byte)td6Data.IntensityTimesTen); //5D Nausea td6Bytes.Add((byte)td6Data.NauseaTimesTen); //5E ? td6Bytes.Add(dummyByte); //5F ? td6Bytes.Add(dummyByte); //60 Track Main Spine Color (Main) td6Bytes.Add((byte)td6Data.TrackMainColour); //61 Track Main Spine Color (Alt 1) td6Bytes.Add((byte)td6Data.TrackMainColourAlt1); //62 Track Main Spine Color (Alt 2) td6Bytes.Add((byte)td6Data.TrackMainColourAlt2); //63 Track Main Spine Color (Alt 3) td6Bytes.Add((byte)td6Data.TrackMainColourAlt3); //64 Track Additional Rail Color (Main) td6Bytes.Add((byte)td6Data.TrackAdditionalColour); //65 Track Additional Rail Color (Alt 1) td6Bytes.Add((byte)td6Data.TrackAdditionalColourAlt1); //66 Track Additional Rail Color (Alt 2) td6Bytes.Add((byte)td6Data.TrackAdditionalColourAlt2); //67 Track Additional Rail Color (Alt 3) td6Bytes.Add((byte)td6Data.TrackAdditionalColourAlt3); //68 Track Support Color (Main) td6Bytes.Add((byte)td6Data.TrackSupportColour); //69 Track Support Color (Alt 1) td6Bytes.Add((byte)td6Data.TrackSupportColourAlt1); //6A Track Support Color (Alt 2) td6Bytes.Add((byte)td6Data.TrackSupportColourAlt2); //6B Track Support Color (Alt 3) td6Bytes.Add((byte)td6Data.TrackSupportColourAlt3); //6C ? td6Bytes.Add(dummyByte); //6D ? td6Bytes.Add(dummyByte); //6E ? td6Bytes.Add(dummyByte); //6F - Six Flags Design if MSB is 1 td6Bytes.Add(td6Data.IsSixFlagsDesign ? (byte)0x80 : dummyByte); //70-73 DAT file flags //TODO //For now we'll cheat and always use ones for a PTCT1 Track td6Bytes.Add(0x80); td6Bytes.Add(0xB1); td6Bytes.Add(0x58); td6Bytes.Add(0x0C); //74-7B Vehicle (Alphanumeric - 8 characters with trailing spaces) //We're using Wooden Roller Coaster - 4 Seater (PTCT1) for our example //And so this will only work properly with PTCT1, as the DAT file flags / checksum will be different string vehicle = RCT2RideCode.RideNameVehicleMap[td6Data.TrackType.RideType]; for (int i = 0; i < 8; i++) { if (i < vehicle.Length) { td6Bytes.Add((byte)vehicle[i]); } else { td6Bytes.Add((byte)' '); } } //7C-7F DAT file checksum //TODO //For now we'll cheat and always use ones for a PTCT1 Track td6Bytes.Add(0xB2); td6Bytes.Add(0x81); td6Bytes.Add(0x15); td6Bytes.Add(0xFF); //80-81 Map Space Required (X and Y) td6Bytes.Add((byte)td6Data.RequiredMapSpace.X); td6Bytes.Add((byte)td6Data.RequiredMapSpace.Y); //82-A1 One-byte colour specifiers for vehicles additional colour for (int i = 0; i < 32; i++) { if (i < td6Data.ColourScheme.AdditionalColours.Length) { td6Bytes.Add((byte)td6Data.ColourScheme.AdditionalColours[i]); } else { td6Bytes.Add((byte)RCT2VehicleColourScheme.RCT2Colour.Black); } } //A2 - Low 5 bites = lift chain speed (1.6 km/hr per bit) // Upper 3 bits = number of circuits byte a2Byte = (byte)(td6Data.NumberOfCircuits & 0x7); a2Byte <<= 5; a2Byte |= (byte)((byte)td6Data.LiftChainSpeed & 0x1F); td6Bytes.Add(a2Byte); //A3 Beginning of track data //Each track element is the track segment, then the qualifier, ends with an FF for track segment for (int i = 0; i < td6Data.TrackData.TrackData.Count(); i++) { if (td6Data.TrackData.TrackData[i].TrackElement == RCT2TrackElements.RCT2TrackElement.EndOfTrack) { break; } RCT2TrackElements.RCT2TrackElement currentTrackPiece = td6Data.TrackData.TrackData[i].TrackElement; byte currentTrackByte = (byte)currentTrackPiece; byte qualifierByte = 0; //7 Bit - Chain lift if (td6Data.TrackData.TrackData[i].Qualifier.IsChainLift) { qualifierByte |= 0x80; } //6 Bit - Inverted if (RCT2TrackElements.TrackElementPropertyMap[currentTrackPiece].InputTrackBank == RCT2TrackElementProperty.RCT2TrackBank.Flipped || RCT2TrackElements.TrackElementPropertyMap[currentTrackPiece].OutputTrackBank == RCT2TrackElementProperty.RCT2TrackBank.Flipped) { qualifierByte |= 0x40; } //5 & 4 - Colour Scheme qualifierByte |= (byte)((td6Data.TrackData.TrackData[i].Qualifier.TrackColourSchemeNumber) << 4); //3, 2, 1 - Stations: if (td6Data.TrackData.TrackData[i].Qualifier.AtTerminalStation) { qualifierByte |= 0x8; qualifierByte |= (byte)td6Data.TrackData.TrackData[i].Qualifier.StationNumber; } else { //Rotation value qualifierByte |= (byte)td6Data.TrackData.TrackData[i].Qualifier.TrackRotation; } //Add to file Console.WriteLine("Adding Track Piece: " + currentTrackPiece.ToString() + ", with byte: " + currentTrackByte.ToString()); Console.WriteLine("Adding Qualifier: " + qualifierByte.ToString()); td6Bytes.Add(currentTrackByte); td6Bytes.Add(qualifierByte); } //End of track byte td6Bytes.Add(0xFF); //Add in the Entracne/Exit data //This is just entrance/exit data taken from an existing RCT2 TD6 file //With them next to each other on a two-station long area td6Bytes.Add(0x00); td6Bytes.Add(0x03); td6Bytes.Add(0x00); td6Bytes.Add(0x00); td6Bytes.Add(0x20); td6Bytes.Add(0x00); td6Bytes.Add(0x00); td6Bytes.Add(0x83); td6Bytes.Add(0xE0); td6Bytes.Add(0xFF); td6Bytes.Add(0x20); td6Bytes.Add(0x00); td6Bytes.Add(0xFF); td6Bytes.Add(0xFF); //Pad out to 24,735 which is the fixed length for (int i = td6Bytes.Count; i < TD6FileSize; i++) { td6Bytes.Add(dummyByte); } return(td6Bytes.ToArray()); }
public void Populate(RCT2TrackData track) { //If the track is empty, return if (track == null || track.TrackData == null || track.TrackData.Count <= 0) { return; } //Give all our bytes an initial value StraightFlat = false; StationPlatform = false; LiftChain = false; SteepLiftChain = false; CurveLiftChain = false; Banking = false; VerticalLoop = false; NormalSlope = false; SteepSlope = false; FlatToSteep = false; SlopedCurves = false; SteepTwist = false; SBends = false; SmallRadiusCurves = false; SmallRadiusBanked = false; MediumRadiusCurves = false; InlineTwist = false; HalfLoop = false; HalfCorkscrew = false; TowerBaseVertical = false; HelixBanked = false; HelixUnbanked = false; Brakes = false; Booster = false; OnRidePhoto = false; WaterSplash = false; VerticalTrack = false; BarrelRoll = false; LaunchedLiftHill = false; LargeHalfLoop = false; //Go through every track piece and populate our bytes RCT2TrackElements.RCT2TrackElement prevTrackPiece = track.TrackData[0].TrackElement; for (int i = 0; i < track.TrackData.Count; i++) { RCT2TrackElements.RCT2TrackElement currentTrack = track.TrackData[i].TrackElement; if (currentTrack == RCT2TrackElements.RCT2TrackElement.Flat) { StraightFlat = true; } if (currentTrack == RCT2TrackElements.RCT2TrackElement.BeginStation || currentTrack == RCT2TrackElements.RCT2TrackElement.MiddleStation || currentTrack == RCT2TrackElements.RCT2TrackElement.EndStation) { StationPlatform = true; } if (currentTrack == RCT2TrackElements.RCT2TrackElement.CableLiftHill || currentTrack == RCT2TrackElements.RCT2TrackElement.LeftCurvedLiftHill || currentTrack == RCT2TrackElements.RCT2TrackElement.PoweredLift || currentTrack == RCT2TrackElements.RCT2TrackElement.RightCurvedLiftHill) { LiftChain = true; } //Steep Lift Chain not needed for our chosen problem space if (currentTrack == RCT2TrackElements.RCT2TrackElement.LeftCurvedLiftHill || currentTrack == RCT2TrackElements.RCT2TrackElement.RightCurvedLiftHill) { CurveLiftChain = true; } if (RCT2TrackElements.TrackElementPropertyMap[currentTrack].InputTrackBank != RCT2TrackElementProperty.RCT2TrackBank.None || RCT2TrackElements.TrackElementPropertyMap[currentTrack].OutputTrackBank != RCT2TrackElementProperty.RCT2TrackBank.None) { Banking = true; } if (currentTrack == RCT2TrackElements.RCT2TrackElement.LeftVerticalLoop || currentTrack == RCT2TrackElements.RCT2TrackElement.RightVerticalLoop) { VerticalLoop = true; } if (RCT2TrackElements.TrackElementPropertyMap[currentTrack].InputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Up25 || RCT2TrackElements.TrackElementPropertyMap[currentTrack].OutputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Up25 || RCT2TrackElements.TrackElementPropertyMap[currentTrack].InputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Down25 || RCT2TrackElements.TrackElementPropertyMap[currentTrack].OutputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Down25) { NormalSlope = true; } if (RCT2TrackElements.TrackElementPropertyMap[currentTrack].InputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Up60 || RCT2TrackElements.TrackElementPropertyMap[currentTrack].OutputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Up60 || RCT2TrackElements.TrackElementPropertyMap[currentTrack].InputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Down60 || RCT2TrackElements.TrackElementPropertyMap[currentTrack].OutputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Down60) { SteepSlope = true; } if (RCT2TrackElements.TrackElementPropertyMap[currentTrack].InputTrackBank == RCT2TrackElementProperty.RCT2TrackBank.Flipped || RCT2TrackElements.TrackElementPropertyMap[currentTrack].OutputTrackBank == RCT2TrackElementProperty.RCT2TrackBank.Flipped) { SteepTwist = true; } if (currentTrack == RCT2TrackElements.RCT2TrackElement.LeftSBend || currentTrack == RCT2TrackElements.RCT2TrackElement.LeftSBendCovered || currentTrack == RCT2TrackElements.RCT2TrackElement.RightSBend || currentTrack == RCT2TrackElements.RCT2TrackElement.RightSBendCovered) { SBends = true; } if (currentTrack.ToString().ToLower().Contains("across1")) { SmallRadiusCurves = true; } if (currentTrack.ToString().ToLower().Contains("bank")) { SmallRadiusBanked = true; } if (currentTrack.ToString().ToLower().Contains("across3")) { MediumRadiusCurves = true; } if (currentTrack.ToString().ToLower().Contains("twist")) { InlineTwist = true; } if (currentTrack == RCT2TrackElements.RCT2TrackElement.HalfLoopUp || currentTrack == RCT2TrackElements.RCT2TrackElement.HalfLoopDown || currentTrack == RCT2TrackElements.RCT2TrackElement.FlyerHalfLoopUp || currentTrack == RCT2TrackElements.RCT2TrackElement.FlyerHalfLoopDown) { HalfLoop = true; } if (currentTrack.ToString().ToLower().Contains("corkscrew")) { HalfCorkscrew = true; } if (currentTrack == RCT2TrackElements.RCT2TrackElement.TowerBase || currentTrack == RCT2TrackElements.RCT2TrackElement.TowerSection) { TowerBaseVertical = true; } if (currentTrack.ToString().ToLower().Contains("bankedhelix")) { HelixBanked = true; } else if (currentTrack.ToString().ToLower().Contains("helix")) { HelixUnbanked = true; } if (currentTrack.ToString().ToLower().Contains("brake")) { Brakes = true; } if (currentTrack == RCT2TrackElements.RCT2TrackElement.OnRidePhoto) { OnRidePhoto = true; } if (currentTrack == RCT2TrackElements.RCT2TrackElement.Watersplash) { WaterSplash = true; } if (RCT2TrackElements.TrackElementPropertyMap[currentTrack].InputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Down90 || RCT2TrackElements.TrackElementPropertyMap[currentTrack].OutputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Down90 || RCT2TrackElements.TrackElementPropertyMap[currentTrack].InputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Up90 || RCT2TrackElements.TrackElementPropertyMap[currentTrack].OutputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Up90) { VerticalTrack = true; } if (currentTrack.ToString().ToLower().Contains("barrelroll")) { BarrelRoll = true; } if (currentTrack == RCT2TrackElements.RCT2TrackElement.PoweredLift) { LaunchedLiftHill = true; } if (currentTrack.ToString().ToLower().Contains("largehalfloop")) { LargeHalfLoop = true; } prevTrackPiece = currentTrack; } }