[
  {
    "path": ".editorconfig",
    "content": "root = true\r\n\r\n[*]\r\nindent_size = 4\r\ntab_width = 4\r\nindent_style = tab"
  },
  {
    "path": ".gitignore",
    "content": "*.suo\r\n*.user\r\n_ReSharper.*\r\nbin\r\nobj\r\ndeploy\r\n"
  },
  {
    "path": "Engine/AverageValueVector3.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace GameEngine\r\n{\r\n\tpublic class AverageValueVector3\r\n\t{\r\n\t\tint _nbrValues;\r\n\t\tList<Vector3> _values = new List<Vector3>();\r\n\r\n\t\tpublic AverageValueVector3(int nbrVaues)\r\n\t\t{\r\n\t\t\t_nbrValues = nbrVaues;\r\n\t\t}\r\n\r\n        public void Reset(int nbrValues)\r\n        {\r\n            _nbrValues = nbrValues;\r\n            _values.Clear();\r\n        }\r\n\r\n\t\tpublic void AddValue(Vector3 value)\r\n\t\t{\r\n\t\t\t_values.Add(value);\r\n\t\t\tif (_values.Count > _nbrValues)\r\n\t\t\t\t_values.RemoveAt(0);\r\n\t\t}\r\n\r\n\t\tpublic Vector3 GetAveragedValue()\r\n\t\t{\r\n\t\t\tVector3 average = new Vector3();\r\n\t\t\tforeach (Vector3 value in _values)\r\n\t\t\t{\r\n\t\t\t\taverage += value;\r\n\t\t\t}\r\n\t\t\treturn average / _values.Count;\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Engine/ChaseCamera.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing System.Diagnostics;\r\n\r\nnamespace GameEngine\r\n{\r\n    \r\n    public class ChaseCamera : ICamera\r\n    {\r\n\t\tprivate Vector3 _chasePosition;\r\n\t\tprivate Vector3 _chaseDirection = new Vector3(0, 0, -1);\r\n\t\tprivate Vector3 _up = Vector3.Up;\r\n\t\tprivate Vector3 _desiredPositionOffset = new Vector3(0, 2.0f, 2.0f);\r\n\t\tprivate Vector3 _desiredPosition;\r\n\t\tprivate Vector3 _lookAtOffset = new Vector3(0, 2.8f, 0);\r\n\t\tprivate Vector3 _lookAt;\r\n\t\tprivate float _stiffness = 1800.0f;\r\n\t\tprivate float _zstiffness = 2.0f;\r\n\t\tprivate float _damping = 600.0f;\r\n\t\tprivate float _mass = 50.0f;\r\n\t\tprivate Vector3 _position;\r\n\t\tprivate Vector3 _velocity;\r\n\t\tprivate float _fieldOfView = MathHelper.ToRadians(45.0f);\r\n\t\tprivate float _nearPlaneDistance = 1.0f;\r\n\t\tprivate float _farPlaneDistance = 15000.0f;\r\n\t\tprivate Matrix _view;\r\n\t\tprivate Matrix _projection;\r\n\r\n        \r\n        /// <summary>\r\n        /// Position of object being chased.\r\n        /// </summary>\r\n        public Vector3 ChasePosition\r\n        {\r\n            get { return _chasePosition; }\r\n            set { _chasePosition = value; }\r\n        }        \r\n\r\n        public void FollowObject(GameObject obj)\r\n        {\r\n            _chasePosition = obj.Position;\r\n            _chaseDirection = obj.Orientation;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Direction the chased object is facing.\r\n        /// </summary>\r\n        public Vector3 ChaseDirection\r\n        {\r\n            get { return _chaseDirection; }\r\n            set { _chaseDirection = value; }\r\n        }        \r\n\r\n        /// <summary>\r\n        /// Chased object's Up vector.\r\n        /// </summary>\r\n        public Vector3 Up\r\n        {\r\n            get { return _up; }\r\n            set { _up = value; }\r\n        }\r\n\r\n        /// <summary>\r\n        /// Desired camera position in the chased object's coordinate system.\r\n        /// </summary>\r\n        public Vector3 DesiredPositionOffset\r\n        {\r\n            get { return _desiredPositionOffset; }\r\n            set { _desiredPositionOffset = value; }\r\n        }\r\n        \r\n        /// <summary>\r\n        /// Desired camera position in world space.\r\n        /// </summary>\r\n        public Vector3 DesiredPosition\r\n        {\r\n            get\r\n            {\r\n                // Ensure correct value even if update has not been called this frame\r\n                UpdateWorldPositions();\r\n\r\n                return _desiredPosition;\r\n            }\r\n        }\r\n\t\t\r\n        /// <summary>\r\n        /// Look at point in the chased object's coordinate system.\r\n        /// </summary>\r\n        public Vector3 LookAtOffset\r\n        {\r\n            get { return _lookAtOffset; }\r\n            set { _lookAtOffset = value; }\r\n        }\r\n\t\t\r\n        /// <summary>\r\n        /// Look at point in world space.\r\n        /// </summary>\r\n        public Vector3 LookAt\r\n        {\r\n            get\r\n            {\r\n                // Ensure correct value even if update has not been called this frame\r\n                UpdateWorldPositions();\r\n\r\n                return _lookAt;\r\n            }\r\n        }\r\n\t\t\r\n        /// <summary>\r\n        /// Physics coefficient which controls the influence of the camera's position\r\n        /// over the spring force. The stiffer the spring, the closer it will stay\r\n        /// the chased object.\r\n        /// </summary>\r\n        public float Stiffness\r\n        {\r\n            get { return _stiffness; }\r\n            set { _stiffness = value; }\r\n        }\r\n\t\t\r\n\t\t/// <summary>\r\n\t\t/// Controls how hard the camera tries to keep up with the chased object \r\n\t\t/// </summary>\r\n        public float ZStiffness\r\n        {\r\n            get { return _zstiffness; }\r\n            set { _zstiffness = value; }\r\n        }\r\n\t\t\r\n        /// <summary>\r\n        /// Physics coefficient which approximates internal friction of the spring.\r\n        /// Sufficient damping will prevent the spring from oscillating infinitely.\r\n        /// </summary>\r\n        public float Damping\r\n        {\r\n            get { return _damping; }\r\n            set { _damping = value; }\r\n        }\r\n\t\t\r\n        /// <summary>\r\n        /// Mass of the camera body. Heaver objects require stiffer springs with less\r\n        /// damping to move at the same rate as lighter objects.\r\n        /// </summary>\r\n        public float Mass\r\n        {\r\n            get { return _mass; }\r\n            set { _mass = value; }\r\n        }\r\n\t\t\r\n\r\n        /// <summary>\r\n        /// Position of camera in world space.\r\n        /// </summary>\r\n        public Vector3 Position\r\n        {\r\n            get { return _position; }\r\n        }\r\n\t\t\r\n        /// <summary>\r\n        /// Velocity of camera.\r\n        /// </summary>\r\n        public Vector3 Velocity\r\n        {\r\n            get { return _velocity; }\r\n        }\r\n\t\t\r\n\r\n        #region Perspective properties\r\n\r\n\t\t\r\n        /// <summary>\r\n        /// Perspective field of view.\r\n        /// </summary>\r\n        public float FieldOfView\r\n        {\r\n            get { return _fieldOfView; }\r\n            set { _fieldOfView = value; }\r\n        }\r\n\t\t\r\n        /// <summary>\r\n        /// Distance to the near clipping plane.\r\n        /// </summary>\r\n        public float NearPlaneDistance\r\n        {\r\n            get { return _nearPlaneDistance; }\r\n            set { _nearPlaneDistance = value; }\r\n        }\r\n\t\t\r\n        /// <summary>\r\n        /// Distance to the far clipping plane.\r\n        /// </summary>\r\n        public float FarPlaneDistance\r\n        {\r\n            get { return _farPlaneDistance; }\r\n            set { _farPlaneDistance = value; }\r\n        }\r\n\t\t\r\n        #endregion\r\n\r\n\r\n        /// <summary>\r\n        /// View transform matrix.\r\n        /// </summary>\r\n        public Matrix View\r\n        {\r\n            get { return _view; }\r\n        }\r\n\t\t\r\n        /// <summary>\r\n        /// Projecton transform matrix.\r\n        /// </summary>\r\n        public Matrix Projection\r\n        {\r\n            get { return _projection; }\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// Rebuilds object space values in world space. Invoke before publicly\r\n        /// returning or privately accessing world space values.\r\n        /// </summary>\r\n        private void UpdateWorldPositions()\r\n        {\r\n            Matrix transform = Matrix.Identity;\r\n            transform.Forward = ChaseDirection;\r\n            transform.Up = Up;\r\n            transform.Right = Vector3.Cross(Up, ChaseDirection);\r\n\r\n            // Calculate desired camera properties in world space\r\n            _desiredPosition = ChasePosition + Vector3.TransformNormal(DesiredPositionOffset, transform);\r\n            _lookAt = ChasePosition + Vector3.TransformNormal(LookAtOffset, transform);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Rebuilds camera's view and projection matricies.\r\n        /// </summary> \r\n        private void UpdateMatrices()\r\n        {\r\n            _view = Matrix.CreateLookAt(this.Position, this.LookAt, this.Up);\r\n            _projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView,\r\n                Engine.Instance.AspectRatio, NearPlaneDistance, FarPlaneDistance);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Forces camera to be at desired position and to stop moving. The is useful\r\n        /// when the chased object is first created or after it has been teleported.\r\n        /// Failing to call this after a large change to the chased object's position\r\n        /// will result in the camera quickly flying across the world.\r\n        /// </summary>\r\n        public void Reset()\r\n        {\r\n            UpdateWorldPositions();\r\n\r\n            // Stop motion\r\n            _velocity = Vector3.Zero;\r\n\r\n            // Force desired position\r\n            _position = _desiredPosition;\r\n\r\n            UpdateMatrices();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Animates the camera from its current position towards the desired offset\r\n        /// behind the chased object. The camera's animation is controlled by a simple\r\n        /// physical spring attached to the camera and anchored to the desired position.\r\n        /// </summary>\r\n        public void Update(GameTime gameTime)\r\n        {\r\n            \r\n            UpdateWorldPositions();\r\n\r\n            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;\r\n\r\n            // Calculate spring force\r\n            Vector3 stretch = (_position - _desiredPosition);\r\n            \r\n            Vector3 force = -_stiffness * stretch - _damping * _velocity;\r\n\r\n            // Apply acceleration\r\n            Vector3 acceleration = force / _mass;\r\n            _velocity += acceleration * elapsed;\r\n\r\n            // Apply velocity\r\n            _position += _velocity * elapsed;\r\n\r\n\t\t\t// Keep up with chased object\r\n            //if (Vector3.Distance(_chasePosition, _position) > 50)\r\n            //{\r\n\r\n            //_position += _chaseDirection * elapsed * (Vector3.Distance(_chasePosition, _position)) * _zstiffness;\r\n            \r\n            //}\r\n\r\n            UpdateMatrices();\r\n        }\r\n\r\n        public void SetPosition(Vector3 position)\r\n        {\r\n            _position = position;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/Engine.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing System.Diagnostics;\r\nusing Microsoft.Xna.Framework.Content;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing GameEngine;\r\n\r\nnamespace GameEngine\r\n{\r\n    public class Engine : DrawableGameComponent\r\n    {\r\n                \r\n        private static Engine _instance;\r\n        private ContentManager _contentManager;\r\n        private ICamera _camera;\r\n        private InputProvider _inputProvider;\r\n        private GraphicsUtilities _graphicsUtils;\r\n        private IWorld _world;\r\n        public GraphicsDeviceManager _graphics;\r\n        private SpriteBatch _spriteBatch;\r\n        public Vector2 ScreenSize;\r\n\t\tpublic float FrameTime;\r\n                \r\n        public static Engine Instance\r\n        {\r\n            get\r\n            {\r\n                return _instance;\r\n            }\r\n        }\r\n\r\n        public static void Create(Game game, GraphicsDeviceManager graphics)\r\n        {\r\n            Debug.Assert(_instance == null);\r\n            _instance = new Engine(game);            \r\n            _instance.EngineStartup(graphics);\r\n        }\r\n\r\n        \r\n        private Engine(Game game)\r\n            : base(game)\r\n        {\r\n             \r\n        }\r\n\r\n        private void EngineStartup(GraphicsDeviceManager graphics)\r\n        {\r\n            _graphics = graphics;\r\n\r\n            _contentManager = new ContentManager(base.Game.Services);\r\n\r\n            //Game bits\r\n            _inputProvider = new InputProvider(base.Game);\r\n\t\t\tvar defaultFont = Engine.Instance.ContentManager.Load<SpriteFont>(\"Content\\\\ArialBlack\");\r\n            _graphicsUtils = new GraphicsUtilities(defaultFont);\r\n            _spriteBatch = new SpriteBatch(Device);\r\n            base.Game.Components.Add(this);\r\n        }\r\n\r\n        public float AspectRatio\r\n        {\r\n\t\t\tget\r\n\t\t\t{\r\n\r\n\t\t\t\treturn (float)Device.Viewport.Width / (float)Device.Viewport.Height;\r\n\t\t\t}\r\n        }\r\n        \r\n\r\n        public override void Update(GameTime gameTime)\r\n        {\r\n\t\t\t\r\n            base.Update(gameTime);\r\n\r\n\t\t\tFrameTime = (float)gameTime.ElapsedGameTime.TotalSeconds;\r\n\t\t\tGameConsole.Clear();\r\n            \r\n            _inputProvider.Update(gameTime);\r\n            SoundEngine2.Instance.Update(gameTime);\r\n            Screen.Update(gameTime);\r\n\r\n            ScreenEffects.Instance.Update(gameTime);\r\n\r\n            _graphicsUtils.Update(gameTime);\r\n        }\r\n\r\n        public override void Draw(GameTime gameTime)\r\n        {\r\n            Screen.Draw();\r\n            _graphicsUtils.Draw();\r\n            ScreenEffects.Instance.Draw();\r\n            _graphicsUtils.DrawText();\r\n        }\r\n\r\n        public ContentManager ContentManager\r\n        {\r\n            get { return _contentManager; }\r\n        }\r\n\r\n        public GraphicsDevice Device\r\n        {\r\n            get { return _graphics.GraphicsDevice; }\r\n        }\r\n        \r\n\t\t\t\t//public BasicEffect CurrentEffect\r\n\t\t\t\t//{\r\n\t\t\t\t//\t\tget { return _currentEffect; }\r\n\t\t\t\t//\t\tset { _currentEffect = value; }\r\n\t\t\t\t//}\r\n\r\n        public GraphicsUtilities GraphicsUtils\r\n        {\r\n            get { return _graphicsUtils; }\r\n        }\r\n\r\n        public IWorld World\r\n        {\r\n            get { return _world; }\r\n            set { _world = value; }\r\n        }\r\n\r\n        public ICamera Camera\r\n        {\r\n            get { return _camera; }\r\n            set { _camera = value; }\r\n        }\r\n\r\n        public InputProvider Input\r\n        {\r\n            get { return _inputProvider; }\r\n            set { _inputProvider = value; }\r\n        }\r\n\r\n        public IGameScreen Screen {get; set; }\r\n        \r\n\r\n        public SpriteBatch SpriteBatch\r\n        {\r\n            get { return _spriteBatch; }\r\n        }\r\n\r\n\t\tRandom _random = new Random();\r\n\r\n\t\tpublic Random Random\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\treturn _random;\r\n\t\t\t}\r\n\t\t}\r\n\r\n        \r\n\r\n        //public bool EnableBloom\r\n        //{\r\n        //    set\r\n        //    {\r\n        //        if (value)\r\n        //        {\r\n        //            _game.Components.Add(new BloomComponent(_game));\r\n        //        }\r\n        //        else\r\n        //        {\r\n        //            foreach (IGameComponent component in _game.Components)\r\n        //            {\r\n        //                if (component is BloomComponent)\r\n        //                {\r\n        //                    _game.Components.Remove(component);\r\n        //                    break;\r\n        //                }\r\n        //            }\r\n        //        }\r\n        //    }\r\n        //}\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/FPSCamera.cs",
    "content": "//-----------------------------------------------------------------------------\r\n// Copyright (c) 2007 dhpoware. All Rights Reserved.\r\n//\r\n// Permission is hereby granted, free of charge, to any person obtaining a\r\n// copy of this software and associated documentation files (the \"Software\"),\r\n// to deal in the Software without restriction, including without limitation\r\n// the rights to use, copy, modify, merge, publish, distribute, sublicense,\r\n// and/or sell copies of the Software, and to permit persons to whom the\r\n// Software is furnished to do so, subject to the following conditions:\r\n//\r\n// The above copyright notice and this permission notice shall be included in\r\n// all copies or substantial portions of the Software.\r\n//\r\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\r\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r\n// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\r\n// IN THE SOFTWARE.\r\n//-----------------------------------------------------------------------------\r\n\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Input;\r\nusing GameEngine;\r\n\r\n\r\nnamespace OneAmEngine\r\n{\r\n\t/// <summary>\r\n\t/// The FirstPersonCamera class implements the logic for a first person\r\n\t/// style 3D camera. This class also handles player input that is used\r\n\t/// to control the camera. To use this class, create an instance of the\r\n\t/// FirstPersonCamera class and then call the Update() method once a\r\n\t/// frame from your game's main loop. The FirstPersonCamera's Update()\r\n\t/// method will process mouse and keyboard input used to manipulate the\r\n\t/// camera. To change the default movement key bindings call the\r\n\t/// MapActionToKey() method. Most of the code in this class is used to\r\n\t/// simulate camera view bobbing, crouching, and jumping.\r\n\t/// </summary>\r\n\tpublic class FPSCamera : ICamera\r\n\t{\r\n\r\n\t\tpublic const float DEFAULT_FOVX = 60.0f;\r\n\t\tpublic const float DEFAULT_ROTATION_SPEED = 0.25f;\r\n\r\n\t\tpublic const float DEFAULT_ZNEAR = 0.1f;\r\n\r\n\t\tprivate const float GRAVITY = -9.8f;\r\n\t\tprivate const float DECELERATION = -0.5f;\r\n\t\tprivate const float STRAFE_SPEED_MULTIPLIER = 15.5f;\r\n\r\n\t\tprivate const float VelocityInversionMultiplier = 20.0f;\r\n\t\tprivate const float Acceleration = 5.0f;\r\n\t\tprivate const float Deceleration = -5.0f;\r\n\t\tprivate const float JumpVelocity = 0.23f;\r\n\t\tprivate const float MaxSpeed = 1.5f;\r\n\t\t\r\n\t\tprivate float _strafeDelta, _forwardDelta, _velocity;\r\n\r\n\t\tprivate Vector3 _orientation, _position;\r\n\r\n\t\tpublic Vector3 Position\r\n\t\t{\r\n\t\t\tget { return _position; }\r\n\t\t\tset { _position = value; }\r\n\t\t}\r\n\t\tpublic Vector3 Orientation\r\n\t\t{\r\n\t\t\tget { return _orientation; }\r\n\t\t\tset { _orientation = value; }\r\n\t\t}\r\n\r\n\t\tpublic float DrawDistance { get; set; }\r\n\r\n\t\tpublic Matrix View { get; private set; }\r\n\t\tpublic Matrix Projection { get; private set; }\r\n\r\n\t\tpublic FPSCamera()\r\n\t\t{\r\n\t\t\tProjection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(DEFAULT_FOVX), Engine.Instance.AspectRatio, DEFAULT_ZNEAR, 15000);\r\n\t\t\tView = Matrix.Identity;\r\n\t\t}\r\n\r\n\r\n\t\tpublic void Update(GameTime gt)\r\n\t\t{\r\n\t\t\tInputProvider input = Engine.Instance.Input;\r\n\t\t\tfloat elapsedTime = Engine.Instance.FrameTime;\r\n\r\n\t\t\t_forwardDelta = input.MoveForward * elapsedTime * Acceleration;\r\n\r\n\t\t\t_strafeDelta = input.Strafe * elapsedTime * Acceleration;\r\n\r\n\t\t\tfloat speed = 0.5f;\r\n\r\n\t\t\tif (input.IsKeyDown(Keys.Home))\r\n\t\t\t{\r\n\t\t\t\t_orientation.Y -= speed * elapsedTime;\r\n\t\t\t}\r\n\t\t\tif (input.IsKeyDown(Keys.End))\r\n\t\t\t{\r\n\t\t\t\t_orientation.Y += speed * elapsedTime;\r\n\t\t\t}\r\n\t\t\tif (input.IsKeyDown(Keys.Delete))\r\n\t\t\t{\r\n\t\t\t\t_orientation.X += speed * elapsedTime;\r\n\t\t\t}\r\n\t\t\tif (input.IsKeyDown(Keys.PageDown))\r\n\t\t\t{\r\n\t\t\t\t_orientation.X -= speed * elapsedTime;\r\n\t\t\t}\r\n\r\n\t\t\tUpdateVelocity();\r\n\t\t\tMoveForward();\r\n\r\n\t\t\t_position.X += (float)(Math.Cos(_orientation.X) * input.Strafe);\r\n\t\t\t_position.Z -= (float)(Math.Sin(_orientation.X) * input.Strafe);\r\n\t\t\t\r\n\t\t\tMatrix view = Matrix.CreateTranslation(-Position);\r\n\t\t\tview *= Matrix.CreateRotationY(-_orientation.X);\r\n\t\t\tview *= Matrix.CreateRotationX(_orientation.Y);\r\n\t\t\tview *= Matrix.CreateRotationZ(_orientation.Z);\r\n\r\n\t\t\tView = view;\r\n\t\t\tProjection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(DEFAULT_FOVX), Engine.Instance.AspectRatio, DEFAULT_ZNEAR, 15000);\r\n\t\t}\r\n\r\n\t\tprivate void UpdateVelocity()\r\n\t\t{\r\n\t\t\tfloat elapsedTimeSec = Engine.Instance.FrameTime;\r\n\r\n\t\t\t// Accelerate or decelerate as camera is moved forward or backward.\r\n\t\t\tfloat acceleration = Acceleration;\r\n\r\n\t\t\tif (_forwardDelta != 0.0f)\r\n\t\t\t{\r\n\t\t\t\t// Speed up the transition from moving backwards to moving\r\n\t\t\t\t// forwards and vice versa. Otherwise there will be too much\r\n\t\t\t\t// of a delay as the camera slows down and then accelerates.\r\n\t\t\t\tif ((_forwardDelta > 0.0f && _velocity < 0.0f) ||\r\n\t\t\t\t\t(_forwardDelta < 0.0f && _velocity > 0.0f))\r\n\t\t\t\t{\r\n\t\t\t\t\tacceleration *= VelocityInversionMultiplier;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t_velocity += _forwardDelta * acceleration;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\r\n\t\t\t\tif (_velocity > 0.0f)\r\n\t\t\t\t{\r\n\t\t\t\t\t_velocity += Deceleration * elapsedTimeSec;\r\n\r\n\t\t\t\t\tif (_velocity < 0.0f)\r\n\t\t\t\t\t\t_velocity = 0.0f;\r\n\t\t\t\t}\r\n\t\t\t\telse if (_velocity < 0.0f)\r\n\t\t\t\t{\r\n\t\t\t\t\t_velocity -= Deceleration * elapsedTimeSec;\r\n\r\n\t\t\t\t\tif (_velocity > 0.0f)\r\n\t\t\t\t\t\t_velocity = 0.0f;\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t\tif (_velocity > MaxSpeed)\r\n\t\t\t{\r\n\t\t\t\t_velocity = MaxSpeed;\r\n\t\t\t\tacceleration = 0;\r\n\t\t\t}\r\n\r\n\t\t\tif (_velocity < -MaxSpeed)\r\n\t\t\t{\r\n\t\t\t\t_velocity = -MaxSpeed;\r\n\t\t\t\tacceleration = 0;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void MoveForward()\r\n\t\t{\r\n\t\t\t_position.X -= (float)((Math.Sin(_orientation.X) * Math.Cos(_orientation.Y)) * _velocity);\r\n\t\t\t_position.Z -= (float)((Math.Cos(_orientation.X) * Math.Cos(_orientation.Y)) * _velocity);\r\n\t\t\t_position.Y -= _orientation.Y * _velocity;\r\n\t\t}\r\n\r\n\t\tpublic void SetPosition(Vector3 pos)\r\n\t\t{\r\n\r\n\t\t}\r\n\r\n\t\tpublic void FollowObject(GameObject obj)\r\n\t\t{\r\n\r\n\t\t}\r\n\t}\r\n}"
  },
  {
    "path": "Engine/FPSCounter.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\n\r\nnamespace GameEngine\r\n{\r\n    public class FrameRateCounter : DrawableGameComponent\r\n    {\r\n        \r\n        int frameRate = 0;\r\n        int frameCounter = 0;\r\n        TimeSpan elapsedTime = TimeSpan.Zero;\r\n        \r\n        public FrameRateCounter()\r\n            : base(Engine.Instance.Game)\r\n        {\r\n        }\r\n\r\n        public override void Update(GameTime gameTime)\r\n        {\r\n            elapsedTime += gameTime.ElapsedGameTime;\r\n\r\n            if (elapsedTime > TimeSpan.FromSeconds(1))\r\n            {\r\n                elapsedTime -= TimeSpan.FromSeconds(1);\r\n                frameRate = frameCounter;\r\n                frameCounter = 0;\r\n            }\r\n        }\r\n\r\n        public override void Draw(GameTime gameTime)\r\n        {\r\n            frameCounter++;\r\n\r\n            string fps = string.Format(\"fps: {0}\", frameRate);\r\n            GameConsole.WriteLine(fps);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/FixedChaseCamera.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\n\r\nnamespace GameEngine\r\n{\r\n    /// <summary>\r\n    /// Camera that stays a fixed distance behind an object but swings freely\r\n    /// </summary>\r\n    public class FixedChaseCamera : ICamera\r\n    {\r\n        public FixedChaseCamera()\r\n\t\t{\r\n\t\t}\r\n\r\n        public Vector3 RightVec = Vector3.Right;\r\n        public Vector3 UpVector = Vector3.Up;\r\n        AverageValueVector3 _lookAt = new AverageValueVector3(40);\r\n\r\n\t\t\r\n\t\t/// <summary>\r\n\t\t/// Position of camera in world space.\r\n\t\t/// </summary>\r\n\t\tpublic Vector3 Position\r\n\t\t{\r\n\t\t\tget { return _position; }\r\n\t\t\tset { _position = value; }\r\n\t\t}\r\n\r\n\t\tprivate Vector3 _position;\r\n\r\n        public Vector3 ChaseDirection\r\n        {\r\n            set {\r\n                \r\n                //_lookAt.AddValue(value);\r\n                _chaseDirection = value;\r\n            }\r\n        }\r\n        private Vector3 _chaseDirection;\r\n\r\n        public float ChaseOffset { get; set; }\r\n\r\n\t\t\r\n\t\t/// <summary>\r\n\t\t/// Perspective field of view.\r\n\t\t/// </summary>\r\n\t\tpublic float FieldOfView\r\n\t\t{\r\n\t\t\tget { return fieldOfView; }\r\n\t\t\tset { fieldOfView = value; }\r\n\t\t}\r\n\t\tprivate float fieldOfView = MathHelper.ToRadians(45.0f);\r\n\r\n\t\t/// <summary>\r\n\t\t/// Distance to the near clipping plane.\r\n\t\t/// </summary>\r\n\t\tpublic float NearPlaneDistance\r\n\t\t{\r\n\t\t\tget { return nearPlaneDistance; }\r\n\t\t\tset { nearPlaneDistance = value; }\r\n\t\t}\r\n\t\tprivate float nearPlaneDistance = 1.0f;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Distance to the far clipping plane.\r\n\t\t/// </summary>\r\n\t\tpublic float FarPlaneDistance\r\n\t\t{\r\n\t\t\tget { return farPlaneDistance; }\r\n\t\t\tset { farPlaneDistance = value; }\r\n\t\t}\r\n\t\tprivate float farPlaneDistance = 15000.0f;\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// View transform matrix.\r\n\t\t/// </summary>\r\n\t\tpublic Matrix View\r\n\t\t{\r\n\t\t\tget { return _view; }\r\n\t\t}\r\n\t\tprivate Matrix _view;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Projecton transform matrix.\r\n\t\t/// </summary>\r\n\t\tpublic Matrix Projection\r\n\t\t{\r\n\t\t\tget { return _projection; }\r\n\t\t}\r\n\t\tprivate Matrix _projection;\r\n\r\n\r\n\t\tpublic void Update(GameTime gameTime)\r\n\t\t{\r\n            _lookAt.AddValue(new Vector3(ChaseOffset, ChaseHeight, 0) + (-_chaseDirection * new Vector3(ChaseDistance, ChaseDistance, ChaseDistance)));\r\n            Vector3 avgLookAt = _lookAt.GetAveragedValue();\r\n            Vector3 cameraPosition = _position +avgLookAt;\r\n            _view = Matrix.CreateLookAt(cameraPosition, cameraPosition - avgLookAt + new Vector3(0,13,0), Vector3.Up);\r\n            _projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, Engine.Instance.AspectRatio, NearPlaneDistance, FarPlaneDistance);\r\n\t\t}\r\n\r\n\t\tpublic void SetPosition(Vector3 position)\r\n\t\t{\r\n\t\t\t_position = position;\r\n\t\t}\r\n\r\n\t\tpublic void FollowObject(GameObject obj)\r\n\t\t{\r\n\t\t}\r\n\r\n        public float ChaseDistance { get; set; }\r\n        public float ChaseHeight { get; set; }\r\n\r\n        \r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/GameConsole.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\n\r\nnamespace GameEngine\r\n{\r\n    public static class GameConsole\r\n    {\r\n\t\tstatic int _row = 0;\r\n\t\tpublic static void Clear()\r\n\t\t{\r\n\t\t\t_row = 0;\r\n\t\t}\r\n\r\n        public static void WriteLine(object o)\r\n        {\r\n\t\t\tEngine.Instance.GraphicsUtils.AddText(new Vector2(21, _row * 18 + 101), o.ToString(), Justify.MIDDLE_LEFT, Color.Black);\r\n\t\t\tEngine.Instance.GraphicsUtils.AddText(new Vector2(20, _row * 18 + 100), o.ToString(), Justify.MIDDLE_LEFT, Color.White);\r\n\t\t\t_row++;\r\n        }\r\n\r\n\t\tpublic static void WriteLine(Vector3 vec)\r\n\t\t{\r\n\t\t\tvec.X = (float)Math.Round(vec.X, 3);\r\n\t\t\tvec.Y = (float)Math.Round(vec.Y, 3);\r\n\t\t\tvec.Z = (float)Math.Round(vec.Z, 3);\r\n\t\t\tWriteLine(vec.ToString());\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Engine/GameEngine.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <PropertyGroup>\r\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\r\n    <Platform Condition=\" '$(Platform)' == '' \">x86</Platform>\r\n    <ProductVersion>8.0.30703</ProductVersion>\r\n    <SchemaVersion>2.0</SchemaVersion>\r\n    <ProjectGuid>{F66B2F9A-AF38-40F9-A094-522C823D04EE}</ProjectGuid>\r\n    <OutputType>Library</OutputType>\r\n    <AppDesignerFolder>Properties</AppDesignerFolder>\r\n    <RootNamespace>GameEngine</RootNamespace>\r\n    <AssemblyName>GameEngine</AssemblyName>\r\n    <FileAlignment>512</FileAlignment>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|x86' \">\r\n    <PlatformTarget>x86</PlatformTarget>\r\n    <DebugSymbols>true</DebugSymbols>\r\n    <DebugType>full</DebugType>\r\n    <Optimize>false</Optimize>\r\n    <OutputPath>bin\\WindowsGL\\Debug\\</OutputPath>\r\n    <DefineConstants>DEBUG;TRACE;WINDOWS</DefineConstants>\r\n    <ErrorReport>prompt</ErrorReport>\r\n    <WarningLevel>4</WarningLevel>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|x86' \">\r\n    <PlatformTarget>x86</PlatformTarget>\r\n    <DebugType>pdbonly</DebugType>\r\n    <Optimize>true</Optimize>\r\n    <OutputPath>bin\\WindowsGL\\Release\\</OutputPath>\r\n    <DefineConstants>TRACE;WINDOWS</DefineConstants>\r\n    <ErrorReport>prompt</ErrorReport>\r\n    <WarningLevel>4</WarningLevel>\r\n  </PropertyGroup>\r\n  <PropertyGroup>\r\n    <ApplicationIcon>Icon.ico</ApplicationIcon>\r\n  </PropertyGroup>\r\n  <PropertyGroup>\r\n    <StartupObject />\r\n  </PropertyGroup>\r\n  <ItemGroup>\r\n    <Compile Include=\"AverageValueVector3.cs\" />\r\n    <Compile Include=\"ChaseCamera.cs\" />\r\n    <Compile Include=\"Engine.cs\" />\r\n    <Compile Include=\"FixedChaseCamera.cs\" />\r\n    <Compile Include=\"FPSCamera.cs\" />\r\n    <Compile Include=\"FPSCounter.cs\" />\r\n    <Compile Include=\"GameConsole.cs\" />\r\n    <Compile Include=\"GameObject.cs\" />\r\n    <Compile Include=\"GraphicsUtilities.cs\" />\r\n    <Compile Include=\"ICamera.cs\" />\r\n    <Compile Include=\"IDrawableObject.cs\" />\r\n    <Compile Include=\"IGameScreen.cs\" />\r\n    <Compile Include=\"InputProvider.cs\" />\r\n    <Compile Include=\"IWorld.cs\" />\r\n    <Compile Include=\"ParticleSystem\\ParticleEmitter.cs\" />\r\n    <Compile Include=\"ParticleSystem\\ParticleSettings.cs\" />\r\n    <Compile Include=\"ParticleSystem\\ParticleSystem.cs\" />\r\n    <Compile Include=\"ParticleSystem\\ParticleVertex.cs\" />\r\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\r\n    <Compile Include=\"ScreenEffects.cs\" />\r\n    <Compile Include=\"SimpleCamera.cs\" />\r\n    <Compile Include=\"SkyBox.cs\" />\r\n    <Compile Include=\"SoundEngine2.cs\" />\r\n    <Compile Include=\"Utility.cs\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Reference Include=\"MonoGame.Framework, Version=3.1.2.0, Culture=neutral, processorArchitecture=MSIL\">\r\n      <SpecificVersion>False</SpecificVersion>\r\n      <HintPath>..\\lib\\MonoGame.Framework.dll</HintPath>\r\n    </Reference>\r\n    <Reference Include=\"System\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Content Include=\"Icon.ico\" />\r\n  </ItemGroup>\r\n  <ItemGroup />\r\n  <ItemGroup>\r\n    <None Include=\"packages.config\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\r\n  <Import Project=\"..\\packages\\MonoGame.Binaries.3.2.0\\build\\net40\\MonoGame.Binaries.targets\" Condition=\"Exists('..\\packages\\MonoGame.Binaries.3.2.0\\build\\net40\\MonoGame.Binaries.targets')\" />\r\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \r\n       Other similar extension points exist, see Microsoft.Common.targets.\r\n  <Target Name=\"BeforeBuild\">\r\n  </Target>\r\n  <Target Name=\"AfterBuild\">\r\n  </Target>\r\n  -->\r\n</Project>"
  },
  {
    "path": "Engine/GameObject.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace GameEngine\r\n{\r\n    public abstract class GameObject\r\n    {\r\n\r\n        protected Vector3 _position, _lastPosition, _size, _orientation;\r\n        protected float _velocity;\r\n        protected bool _visible;\r\n        protected bool _lockToGround;\r\n        \r\n        public GameObject()\r\n        {\r\n            _visible = true;\r\n        }\r\n\r\n        public Vector3 Position\r\n        {\r\n            get { return _position; }\r\n            set { _position = value; }\r\n        }\r\n\r\n        public Vector3 Orientation\r\n        {\r\n            get { return _orientation; }\r\n            set { _orientation = value; }\r\n        }\r\n\r\n        public Vector3 Size\r\n        {\r\n            get { return _size; }\r\n            set { _size = value; }\r\n        }\r\n\r\n        public float Velocity\r\n        {\r\n            get { return _velocity; }\r\n            set { _velocity = value; }\r\n        }\r\n\r\n        public bool Visible\r\n        {\r\n            get { return _visible; }\r\n            set { _visible = value; }\r\n        }\r\n\r\n        public bool LockToGround\r\n        {\r\n            get { return _lockToGround; }\r\n            set { _lockToGround = value; }\r\n        }\r\n\r\n        public void SetRotation(float rotation)\r\n        {\r\n            _orientation.X = rotation;\r\n        }\r\n\r\n        public void MoveForward()\r\n        {\r\n            _lastPosition = _position;\r\n            _position.X -= (float)((Math.Sin(_orientation.X) * Math.Cos(_orientation.Y)) * _velocity);\r\n            _position.Z -= (float)((Math.Cos(_orientation.X) * Math.Cos(_orientation.Y)) * _velocity);\r\n            if (!_lockToGround)\r\n                _position.Y -= _orientation.Y * _velocity;\r\n        }\r\n\r\n        public Vector3 GetLookAt(float distance)\r\n        {\r\n            Vector3 lookAt = _position;\r\n            lookAt.X -= (float)((Math.Sin(_orientation.X) * Math.Cos(_orientation.Y)) * distance);\r\n            lookAt.Z -= (float)((Math.Cos(_orientation.X) * Math.Cos(_orientation.Y)) * distance);\r\n            lookAt.Y -= _orientation.Y * distance;\r\n            return lookAt;\r\n        }\r\n\r\n        public Vector3 GetLookAt(Vector3 orientation, float distance)\r\n        {\r\n            Vector3 lookAt = _position;\r\n            lookAt.X -= (float)((Math.Sin(orientation.X) * Math.Cos(orientation.Y)) * distance);\r\n            lookAt.Z -= (float)((Math.Cos(orientation.X) * Math.Cos(orientation.Y)) * distance);\r\n            lookAt.Y -= orientation.Y * distance;\r\n            return lookAt;\r\n        }\r\n\r\n        public void Strafe(float amount)\r\n        {\r\n            _position.X += (float)(Math.Cos(_orientation.X) * amount);\r\n            _position.Z -= (float)(Math.Sin(_orientation.X) * amount);\r\n        }\r\n\r\n        public Matrix WorldTransform\r\n        {\r\n            get\r\n            {\r\n                Matrix world = Matrix.CreateFromYawPitchRoll(_orientation.X, -_orientation.Y, _orientation.Z);\r\n                world *= Matrix.CreateScale(_size);\r\n                world *= Matrix.CreateTranslation(_position);\r\n                return world;\r\n            }\r\n        }\r\n      \r\n        \r\n        /// <summary>\r\n        /// Moves the camera. The dx, dy, and dz parameters determine how\r\n        /// far to move the camera forwards, upwards, and sideways.\r\n        /// </summary>\r\n        /// <param name=\"dx\">Sideways movement amount.</param>\r\n        /// <param name=\"dy\">Upwards movement amount.</param>\r\n        /// <param name=\"dz\">Forwads movement amount.</param>\r\n        public void Move(float dx, float dy, float dz)\r\n        {\r\n            _position.X += dx;\r\n            _position.Y += dy;\r\n            _position.Z += dz;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Moves the camera along the given direction.\r\n        /// </summary>\r\n        /// <param name=\"direction\">The direction to move.</param>\r\n        /// <param name=\"velocity\">How far to move along direction.</param>\r\n        public void Move(Vector3 direction, float amount)\r\n        {\r\n            _position += direction * amount;\r\n        }\r\n\r\n        public void Rotate(float amount)\r\n        {\r\n            _orientation.X += amount;\r\n        }\r\n\r\n        public void Pitch(float amount)\r\n        {\r\n            _orientation.Y += amount;\r\n        }\r\n        \r\n\r\n        public virtual Vector3 GetCameraPosition()\r\n        {\r\n            return _position;\r\n        }\r\n\r\n        public BoundingSphere BoundingSphere\r\n        {\r\n            get\r\n            {\r\n                return new BoundingSphere(_position, _size.X);\r\n            }\r\n        }\r\n\r\n        public abstract void Update(GameTime gameTime);\r\n        public abstract void Render();\r\n\r\n        public virtual void OnPlayerSelect() { }\r\n\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/GraphicsUtilities.cs",
    "content": "#region Using Statements\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Content;\r\nusing Microsoft.Xna.Framework.Graphics;\r\n#endregion\r\n\r\nnamespace GameEngine\r\n{\r\n    /// <summary>\r\n    /// Used for text justification.\r\n    /// </summary>\r\n    public enum Justify\r\n    {\r\n        TOP_LEFT,\r\n        TOP_CENTER,\r\n        TOP_RIGHT,\r\n        MIDDLE_LEFT,\r\n        MIDDLE_CENTER,\r\n        MIDDLE_RIGHT,\r\n        BOTTOM_LEFT,\r\n        BOTTOM_CENTER,\r\n        BOTTOM_RIGHT\r\n    }\r\n\r\n\r\n    /// <summary>\r\n    /// Type of shape to draw.\r\n    /// </summary>\r\n    public enum ShapeType\r\n    {\r\n        Cube\r\n    }\r\n\r\n    /// <summary>\r\n    /// GraphicsUtilities\r\n    ///   DrawableGameComponent for debug-graphics functionality.\r\n    ///   Currently supports 3D lines, text, and basic solid shapes.\r\n    ///   Registers self as service provider - IGraphicsUtilitiesService.\r\n    ///   \r\n    /// To use:\r\n    ///   Create an instance of GraphicsUtilities\r\n    ///   Add it to the list of components\r\n    ///   Set its view/projection matrices every frame\r\n    ///   Add lines/text/shapes every frame\r\n    /// \r\n    /// </summary>\r\n    public class GraphicsUtilities : IDrawableObject\r\n    {\r\n        #region Creation / Initialization\r\n        public GraphicsUtilities(SpriteFont font)\r\n        {\r\n            CreateLineEffect();\r\n            CreateShapeEffect();\r\n            CreateCube();\r\n\r\n\t\t\tmFont1 = font;\r\n            mSpriteBatch = new SpriteBatch(Engine.Instance.Device);\r\n\r\n        }\r\n\r\n        public void Update(GameTime gameTime)\r\n        {\r\n\t\t\tif (Engine.Instance.Camera == null) return;\r\n            SetViewMatrix(Engine.Instance.Camera.View);\r\n            SetProjectionMatrix(Engine.Instance.Camera.Projection);\r\n        }\r\n       \r\n        /// <summary>\r\n        /// Draw utility graphics waiting to be rendered this pass.\r\n        /// </summary>\r\n        public void Draw()\r\n        {\r\n\t\t\t\t\tEngine.Instance.Device.DepthStencilState = DepthStencilState.Default;\r\n            // Draw shapes\r\n            if (sShapeList.Count > 0)\r\n            {\r\n                int nbrPrimitives = 0;\r\n                foreach (ShapeData shapeData in sShapeList)\r\n                {\r\n\r\n                    switch (shapeData.mType)\r\n                    {\r\n                        case ShapeType.Cube:\r\n\t\t\t\t\t\t\tEngine.Instance.Device.SetVertexBuffer(mCubeVertexBuffer);\r\n                            nbrPrimitives = 12;\r\n\t\t\t\t\t\t\tEngine.Instance.Device.RasterizerState = RasterizerState.CullClockwise;\r\n                            break;\r\n                    }\r\n\r\n                    \r\n                    mBasicShapeEffect.DiffuseColor = shapeData.mColor.ToVector3() * 0.5f;\r\n                    mBasicShapeEffect.SpecularColor = shapeData.mColor.ToVector3();\r\n\r\n                    mBasicShapeEffect.TextureEnabled = false;\r\n\r\n                    mBasicShapeEffect.World = shapeData.mWorldMatrix;\r\n                    mBasicShapeEffect.View = mViewMatrix;\r\n                    mBasicShapeEffect.Projection = mProjectionMatrix;\r\n                    \r\n                    foreach (EffectPass pass in mBasicShapeEffect.CurrentTechnique.Passes)\r\n                    {\r\n\t\t\t\t\t\tpass.Apply();\r\n                        Engine.Instance.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, nbrPrimitives);\r\n                    }\r\n                }\r\n            }\r\n            ClearShapes();\r\n            \r\n\r\n            // Draw lines\r\n            if (sLinesList.Count > 0)\r\n            {\r\n\t\t\t\tmLineVertexBuffer = new VertexBuffer(Engine.Instance.Device, typeof(VertexPositionColor), sLinesList.Count, BufferUsage.WriteOnly);\r\n\r\n                mLineVertexBuffer.SetData<VertexPositionColor>(sLinesList.ToArray());\r\n                Engine.Instance.Device.SetVertexBuffer(mLineVertexBuffer);\r\n\r\n                mBasicLineEffect.View = mViewMatrix;\r\n                mBasicLineEffect.Projection = mProjectionMatrix;\r\n\r\n                foreach (EffectPass pass in mBasicLineEffect.CurrentTechnique.Passes)\r\n                {\r\n                    pass.Apply();\r\n                    Engine.Instance.Device.DrawPrimitives(PrimitiveType.LineList, 0, sLinesList.Count / 2);\r\n                }\r\n            }\r\n            ClearLines();\r\n        }\r\n\r\n        public void DrawText()\r\n        {\r\n            // Draw text\r\n            if (sTextList.Count > 0)\r\n            {\r\n                mSpriteBatch.Begin();\r\n                foreach (TextData textData in sTextList)\r\n                {\r\n                    Vector2 screenPos = new Vector2(textData.mPos.X, textData.mPos.Y);\r\n                    if (!textData.mIsTransformed)\r\n                    {\r\n                        // If text was specified in 3D, transform it to 2D coordinates\r\n                        Vector3 transformed = Engine.Instance.Device.Viewport.Project(textData.mPos,\r\n                                                                              mProjectionMatrix,\r\n                                                                              mViewMatrix,\r\n                                                                              Matrix.Identity);\r\n\r\n                        // Don't draw text for positions behind the camera\r\n                        if (transformed.Z < 0.0f)\r\n                        {\r\n                            continue;\r\n                        }\r\n\r\n                        screenPos.X = transformed.X;\r\n                        screenPos.Y = transformed.Y;\r\n                    }\r\n\r\n                    // Draw each string\r\n                    JustifyText(mFont1, textData.mText, textData.mJustify, screenPos, out screenPos);\r\n                    mSpriteBatch.DrawString(mFont1, textData.mText, screenPos, textData.mColor);\r\n                }\r\n                mSpriteBatch.End();\r\n            }\r\n            ClearText();\r\n        }\r\n\r\n        #endregion\r\n\r\n\r\n        #region Utilities (Line/Text/Object drawing)\r\n\r\n        public void AddCube(Matrix worldTransform, Color color)\r\n        {\r\n            if (sShapeList.Count >= MAX_SHAPES)\r\n            {\r\n                return;\r\n            }\r\n            ShapeData shapeData = new ShapeData();\r\n            shapeData.mType = ShapeType.Cube;\r\n            shapeData.mWorldMatrix = worldTransform;\r\n            shapeData.mColor = color;\r\n            shapeData.mTexture = null;\r\n            sShapeList.Add(shapeData);\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// Add 3D line.\r\n        /// </summary>\r\n        /// <param name=\"startPos\">3D world-space start position</param>\r\n        /// <param name=\"endPos\">3D world-space end position</param>\r\n        /// <param name=\"color\">Color of line</param>\r\n        public void AddLine(Vector3 startPos, Vector3 endPos, Color color)\r\n        {\r\n            if (sLinesList.Count >= MAX_LINES * 2)\r\n            {\r\n                return;\r\n            }\r\n            VertexPositionColor lineVert = new VertexPositionColor();\r\n            lineVert.Position = startPos;\r\n            lineVert.Color = color;\r\n            sLinesList.Add(lineVert);\r\n            lineVert.Position = endPos;\r\n            lineVert.Color = color;\r\n            sLinesList.Add(lineVert);\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// Add text at 2D position.\r\n        /// </summary>\r\n        /// <param name=\"pos\">XY screen coordinates (pixels)</param>\r\n        /// <param name=\"text\">Text to draw</param>\r\n        /// <param name=\"color\">Color of text</param>\r\n        public void AddText(Vector2 pos, String text, Justify justify, Color color)\r\n        {\r\n            if (sTextList.Count >= MAX_TEXT_LINES)\r\n            {\r\n                return;\r\n            }\r\n\r\n            TextData textData = new TextData();\r\n            textData.mPos.X = pos.X;\r\n            textData.mPos.Y = pos.Y;\r\n            textData.mPos.Z = 0.0f;\r\n            textData.mText = text;\r\n            textData.mColor = color;\r\n            textData.mJustify = justify;\r\n            textData.mIsTransformed = true;\r\n            sTextList.Add(textData);\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// Add text at 3D position.\r\n        /// </summary>\r\n        /// <param name=\"worldPos\">3D world-space position for text</param>\r\n        /// <param name=\"text\">Text to draw</param>\r\n        /// <param name=\"color\">Color of text</param>\r\n        public void AddText(Vector3 worldPos, String text, Justify justify, Color color)\r\n        {\r\n            if (sTextList.Count >= MAX_TEXT_LINES)\r\n            {\r\n                return;\r\n            }\r\n\r\n            TextData textData = new TextData();\r\n            textData.mPos = worldPos;\r\n            textData.mText = text;\r\n            textData.mColor = color;\r\n            textData.mJustify = justify;\r\n            textData.mIsTransformed = false;\r\n            sTextList.Add(textData);\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// Add coordinate axis using the specified transformation.\r\n        /// </summary>\r\n        /// <param name=\"worldTransform\">World transformation matrix</param>\r\n        /// <param name=\"scale\">Scale on drawn lines (1.0 units by default).</param>\r\n        public void AddAxis(Matrix worldTransform, float scale)\r\n        {\r\n            AddLine(worldTransform.Translation, worldTransform.Translation + worldTransform.Forward, Color.Red);\r\n            AddLine(worldTransform.Translation, worldTransform.Translation + worldTransform.Left, Color.Green);\r\n            AddLine(worldTransform.Translation, worldTransform.Translation + worldTransform.Up, Color.Blue);\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// Add a cube using lines.\r\n        /// </summary>\r\n        /// <param name=\"worldTransform\">World transformation matrix, specifies the center of the cube.</param>\r\n        /// <param name=\"color\">Color</param>\r\n        public void AddWireframeCube(Matrix worldTransform, Color color)\r\n        {\r\n            Vector3 forwardVector = worldTransform.Forward / 2.0f;\r\n            Vector3 leftVector = worldTransform.Left / 2.0f;\r\n            Vector3 upVector = worldTransform.Up / 2.0f;\r\n\r\n            Vector3 centerPosition = worldTransform.Translation;\r\n            Vector3 forwardLeftUp = centerPosition + forwardVector + leftVector + upVector;\r\n            Vector3 forwardRightUp = centerPosition + forwardVector - leftVector + upVector;\r\n            Vector3 backwardLeftUp = centerPosition - forwardVector + leftVector + upVector;\r\n            Vector3 backwardRightUp = centerPosition - forwardVector - leftVector + upVector;\r\n\r\n            Vector3 forwardLeftDown = centerPosition + forwardVector + leftVector - upVector;\r\n            Vector3 forwardRightDown = centerPosition + forwardVector - leftVector - upVector;\r\n            Vector3 backwardLeftDown = centerPosition - forwardVector + leftVector - upVector;\r\n            Vector3 backwardRightDown = centerPosition - forwardVector - leftVector - upVector;\r\n\r\n            // Draw top\r\n            AddLine(forwardLeftUp, forwardRightUp, color);\r\n            AddLine(forwardRightUp, backwardRightUp, color);\r\n            AddLine(backwardRightUp, backwardLeftUp, color);\r\n            AddLine(backwardLeftUp, forwardLeftUp, color);\r\n\r\n            // Draw bottom\r\n            AddLine(forwardLeftDown, forwardRightDown, color);\r\n            AddLine(forwardRightDown, backwardRightDown, color);\r\n            AddLine(backwardRightDown, backwardLeftDown, color);\r\n            AddLine(backwardLeftDown, forwardLeftDown, color);\r\n\r\n            // Draw sides\r\n            AddLine(forwardLeftUp, forwardLeftDown, color);\r\n            AddLine(forwardRightUp, forwardRightDown, color);\r\n            AddLine(backwardRightUp, backwardRightDown, color);\r\n            AddLine(backwardLeftUp, backwardLeftDown, color);\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// Add a square grid using lines.\r\n        /// </summary>\r\n        /// <param name=\"worldTransform\">World transformation matrix, specifies the center of the grid.</param>\r\n        /// <param name=\"numRows\">Number of rows (and columns).</param>\r\n        /// <param name=\"color\">Color.</param>\r\n        public void AddSquareGrid(Matrix worldTransform, int numRows, Color color)\r\n        {\r\n            if (0 < numRows)\r\n            {\r\n                float scale = worldTransform.Forward.Length();\r\n                Vector3 forwardVector = worldTransform.Forward / 2.0f;\r\n                Vector3 leftVector = worldTransform.Left / 2.0f;\r\n                Vector3 backwardsNormalizedVector = -forwardVector / forwardVector.Length();\r\n                Vector3 rightNormalizedVector = -leftVector / leftVector.Length();\r\n\r\n                Vector3 centerPosition = worldTransform.Translation;\r\n                Vector3 forwardLeft = centerPosition + forwardVector + leftVector;\r\n                Vector3 forwardRight = centerPosition + forwardVector - leftVector;\r\n                Vector3 backwardLeft = centerPosition - forwardVector + leftVector;\r\n                Vector3 backwardRight = centerPosition - forwardVector - leftVector;\r\n\r\n                // Draw outline of the grid\r\n                AddLine(forwardLeft, forwardRight, color);\r\n                AddLine(forwardRight, backwardRight, color);\r\n                AddLine(backwardRight, backwardLeft, color);\r\n                AddLine(backwardLeft, forwardLeft, color);\r\n\r\n                // Draw interior grid lines\r\n                float stepSize = 1.0f / (float)(numRows);\r\n                for (int ii = 1; ii < numRows; ++ii)\r\n                {\r\n                    float percentageAcross = (float)(ii) * stepSize;\r\n                    // Front-to-back line\r\n                    AddLine(forwardLeft + (rightNormalizedVector * percentageAcross * scale),\r\n                              backwardLeft + (rightNormalizedVector * percentageAcross * scale),\r\n                              color);\r\n\r\n                    // Left-to-right line\r\n                    AddLine(forwardLeft + (backwardsNormalizedVector * percentageAcross * scale),\r\n                              forwardRight + (backwardsNormalizedVector * percentageAcross * scale),\r\n                              color);\r\n                }\r\n            }\r\n        }\r\n        #endregion\r\n\r\n\r\n        #region Data Access (used to set view/projection matrices)\r\n\r\n        public void SetViewMatrix(Matrix view)\r\n        {\r\n            mViewMatrix = view;\r\n        }\r\n\r\n        public void SetProjectionMatrix(Matrix proj)\r\n        {\r\n            mProjectionMatrix = proj;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Clear list of shapes waiting to be rendered.\r\n        /// </summary>\r\n        public void ClearShapes()\r\n        {\r\n            sShapeList.Clear();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Clear list of lines waiting to be rendered.\r\n        /// </summary>\r\n        public void ClearLines()\r\n        {\r\n            sLinesList.Clear();\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// Clear list of text waiting to be rendered.\r\n        /// </summary>\r\n        public void ClearText()\r\n        {\r\n            sTextList.Clear();\r\n        }\r\n        #endregion\r\n\r\n\r\n        #region Private Data & Methods\r\n\r\n        /// <summary>\r\n        /// Justify text based on enumerated value.\r\n        /// </summary>\r\n        private void JustifyText(SpriteFont font, String text, Justify justify, Vector2 inputPos, out Vector2 resultPos)\r\n        {\r\n            Vector2 textSize = font.MeasureString(text);\r\n\r\n            // Default text to upper-left\r\n            resultPos = inputPos;\r\n\r\n            switch (justify)\r\n            {\r\n                case Justify.TOP_LEFT:\r\n                    break;\r\n\r\n                case Justify.TOP_CENTER:\r\n                    resultPos.X -= (textSize.X / 2);\r\n                    break;\r\n\r\n                case Justify.TOP_RIGHT:\r\n                    resultPos.X -= textSize.X;\r\n                    break;\r\n\r\n                case Justify.MIDDLE_LEFT:\r\n                    resultPos.Y -= (textSize.Y / 2);\r\n                    break;\r\n\r\n                case Justify.MIDDLE_CENTER:\r\n                    resultPos.X -= (textSize.X / 2);\r\n                    resultPos.Y -= (textSize.Y / 2);\r\n                    break;\r\n\r\n                case Justify.MIDDLE_RIGHT:\r\n                    resultPos.X -= textSize.X;\r\n                    resultPos.Y -= (textSize.Y / 2);\r\n                    break;\r\n\r\n                case Justify.BOTTOM_LEFT:\r\n                    resultPos.Y -= textSize.Y;\r\n                    break;\r\n\r\n                case Justify.BOTTOM_CENTER:\r\n                    resultPos.X -= (textSize.X / 2);\r\n                    resultPos.Y -= textSize.Y;\r\n                    break;\r\n\r\n                case Justify.BOTTOM_RIGHT:\r\n                    resultPos.X -= textSize.X;\r\n                    resultPos.Y -= textSize.Y;\r\n                    break;\r\n            }\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// Create the BasicEffect to be used by Lines.\r\n        /// </summary>\r\n        private void CreateLineEffect()\r\n        {\r\n            mBasicLineEffect = new BasicEffect(Engine.Instance.Device);\r\n            mBasicLineEffect.VertexColorEnabled = true;\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// Create the BasicEffect to be used by shapes.\r\n        /// </summary>\r\n        private void CreateShapeEffect()\r\n        {\r\n            mBasicShapeEffect = new BasicEffect(Engine.Instance.Device);\r\n            mBasicShapeEffect.Alpha = 1.0f;\r\n            mBasicShapeEffect.DiffuseColor = new Vector3(0.5f, 0.5f, 0.5f);\r\n            mBasicShapeEffect.SpecularColor = new Vector3(1.0f, 1.0f, 1.0f);\r\n            mBasicShapeEffect.SpecularPower = 3.0f;\r\n            mBasicShapeEffect.AmbientLightColor = new Vector3(0.75f, 0.75f, 0.75f);\r\n\r\n            mBasicShapeEffect.DirectionalLight0.Enabled = true;\r\n            mBasicShapeEffect.DirectionalLight0.DiffuseColor = Vector3.One;\r\n            mBasicShapeEffect.DirectionalLight0.Direction = Vector3.Normalize(new Vector3(1.0f, -1.0f, -1.0f));\r\n            mBasicShapeEffect.DirectionalLight0.SpecularColor = Vector3.One;\r\n\r\n            mBasicShapeEffect.DirectionalLight1.Enabled = true;\r\n            mBasicShapeEffect.DirectionalLight1.DiffuseColor = new Vector3(0.5f, 0.5f, 0.5f);\r\n            mBasicShapeEffect.DirectionalLight1.Direction = Vector3.Normalize(new Vector3(-1.0f, -1.0f, 1.0f));\r\n            mBasicShapeEffect.DirectionalLight1.SpecularColor = new Vector3(0.5f, 0.5f, 0.5f);\r\n\r\n            mBasicShapeEffect.LightingEnabled = true;\r\n\r\n            \r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// Create vertices and vertex buffer for drawing a solid cube.\r\n        /// </summary>\r\n        private void CreateCube()\r\n        {\r\n            mCubeVertices = new VertexPositionNormalTexture[36];\r\n\r\n            Vector3 topLeftFront = new Vector3(-0.5f, 0.5f, 0.5f);\r\n            Vector3 bottomLeftFront = new Vector3(-0.5f, -0.5f, 0.5f);\r\n            Vector3 topRightFront = new Vector3(0.5f, 0.5f, 0.5f);\r\n            Vector3 bottomRightFront = new Vector3(0.5f, -0.5f, 0.5f);\r\n            Vector3 topLeftBack = new Vector3(-0.5f, 0.5f, -0.5f);\r\n            Vector3 topRightBack = new Vector3(0.5f, 0.5f, -0.5f);\r\n            Vector3 bottomLeftBack = new Vector3(-0.5f, -0.5f, -0.5f);\r\n            Vector3 bottomRightBack = new Vector3(0.5f, -0.5f, -0.5f);\r\n\r\n            Vector2 textureTopLeft = new Vector2(0.0f, 0.0f);\r\n            Vector2 textureTopRight = new Vector2(1.0f, 0.0f);\r\n            Vector2 textureBottomLeft = new Vector2(0.0f, 1.0f);\r\n            Vector2 textureBottomRight = new Vector2(1.0f, 1.0f);\r\n\r\n            Vector3 frontNormal = new Vector3(0.0f, 0.0f, 1.0f);\r\n            Vector3 backNormal = new Vector3(0.0f, 0.0f, -1.0f);\r\n            Vector3 topNormal = new Vector3(0.0f, 1.0f, 0.0f);\r\n            Vector3 bottomNormal = new Vector3(0.0f, -1.0f, 0.0f);\r\n            Vector3 leftNormal = new Vector3(-1.0f, 0.0f, 0.0f);\r\n            Vector3 rightNormal = new Vector3(1.0f, 0.0f, 0.0f);\r\n\r\n\r\n            // Front face.\r\n            mCubeVertices[0] = new VertexPositionNormalTexture(topLeftFront, frontNormal, textureTopLeft);\r\n            mCubeVertices[1] = new VertexPositionNormalTexture(bottomLeftFront, frontNormal, textureBottomLeft);\r\n            mCubeVertices[2] = new VertexPositionNormalTexture(topRightFront, frontNormal, textureTopRight);\r\n            mCubeVertices[3] = new VertexPositionNormalTexture(bottomLeftFront, frontNormal, textureBottomLeft);\r\n            mCubeVertices[4] = new VertexPositionNormalTexture(bottomRightFront, frontNormal, textureBottomRight);\r\n            mCubeVertices[5] = new VertexPositionNormalTexture(topRightFront, frontNormal, textureTopRight);\r\n\r\n            // Back face.\r\n            mCubeVertices[6] = new VertexPositionNormalTexture(topLeftBack, backNormal, textureTopRight);\r\n            mCubeVertices[7] = new VertexPositionNormalTexture(topRightBack, backNormal, textureTopLeft);\r\n            mCubeVertices[8] = new VertexPositionNormalTexture(bottomLeftBack, backNormal, textureBottomRight);\r\n            mCubeVertices[9] = new VertexPositionNormalTexture(bottomLeftBack, backNormal, textureBottomRight);\r\n            mCubeVertices[10] = new VertexPositionNormalTexture(topRightBack, backNormal, textureTopLeft);\r\n            mCubeVertices[11] = new VertexPositionNormalTexture(bottomRightBack, backNormal, textureBottomLeft);\r\n\r\n            // Top face.\r\n            mCubeVertices[12] = new VertexPositionNormalTexture(topLeftFront, topNormal, textureBottomLeft);\r\n            mCubeVertices[13] = new VertexPositionNormalTexture(topRightBack, topNormal, textureTopRight);\r\n            mCubeVertices[14] = new VertexPositionNormalTexture(topLeftBack, topNormal, textureTopLeft);\r\n            mCubeVertices[15] = new VertexPositionNormalTexture(topLeftFront, topNormal, textureBottomLeft);\r\n            mCubeVertices[16] = new VertexPositionNormalTexture(topRightFront, topNormal, textureBottomRight);\r\n            mCubeVertices[17] = new VertexPositionNormalTexture(topRightBack, topNormal, textureTopRight);\r\n\r\n            // Bottom face. \r\n            mCubeVertices[18] = new VertexPositionNormalTexture(bottomLeftFront, bottomNormal, textureTopLeft);\r\n            mCubeVertices[19] = new VertexPositionNormalTexture(bottomLeftBack, bottomNormal, textureBottomLeft);\r\n            mCubeVertices[20] = new VertexPositionNormalTexture(bottomRightBack, bottomNormal, textureBottomRight);\r\n            mCubeVertices[21] = new VertexPositionNormalTexture(bottomLeftFront, bottomNormal, textureTopLeft);\r\n            mCubeVertices[22] = new VertexPositionNormalTexture(bottomRightBack, bottomNormal, textureBottomRight);\r\n            mCubeVertices[23] = new VertexPositionNormalTexture(bottomRightFront, bottomNormal, textureTopRight);\r\n\r\n            // Left face.\r\n            mCubeVertices[24] = new VertexPositionNormalTexture(topLeftFront, leftNormal, textureTopRight);\r\n            mCubeVertices[25] = new VertexPositionNormalTexture(bottomLeftBack, leftNormal, textureBottomLeft);\r\n            mCubeVertices[26] = new VertexPositionNormalTexture(bottomLeftFront, leftNormal, textureBottomRight);\r\n            mCubeVertices[27] = new VertexPositionNormalTexture(topLeftBack, leftNormal, textureTopLeft);\r\n            mCubeVertices[28] = new VertexPositionNormalTexture(bottomLeftBack, leftNormal, textureBottomLeft);\r\n            mCubeVertices[29] = new VertexPositionNormalTexture(topLeftFront, leftNormal, textureTopRight);\r\n\r\n            // Right face. \r\n            mCubeVertices[30] = new VertexPositionNormalTexture(topRightFront, rightNormal, textureTopLeft);\r\n            mCubeVertices[31] = new VertexPositionNormalTexture(bottomRightFront, rightNormal, textureBottomLeft);\r\n            mCubeVertices[32] = new VertexPositionNormalTexture(bottomRightBack, rightNormal, textureBottomRight);\r\n            mCubeVertices[33] = new VertexPositionNormalTexture(topRightBack, rightNormal, textureTopRight);\r\n            mCubeVertices[34] = new VertexPositionNormalTexture(topRightFront, rightNormal, textureTopLeft);\r\n            mCubeVertices[35] = new VertexPositionNormalTexture(bottomRightBack, rightNormal, textureBottomRight);\r\n\r\n\t\t\tmCubeVertexBuffer = new VertexBuffer(Engine.Instance.Device, typeof(VertexPositionNormalTexture), mCubeVertices.Length,\r\n\t\t\t\t\t\t\t\t\t\t\t\t BufferUsage.WriteOnly);\r\n\r\n            mCubeVertexBuffer.SetData<VertexPositionNormalTexture>(mCubeVertices);\r\n        }\r\n\r\n        \r\n\r\n        // Internal data types\r\n        struct TextData\r\n        {\r\n            public Vector3 mPos;\r\n            public String mText;\r\n            public Color mColor;\r\n            public Justify mJustify;\r\n            public bool mIsTransformed;\r\n        }\r\n\r\n        struct ShapeData\r\n        {\r\n            public ShapeType mType;\r\n            public Matrix mWorldMatrix;\r\n            public Color mColor;\r\n            public Texture2D mTexture;\r\n        }\r\n\r\n        Matrix mViewMatrix = Matrix.Identity;\r\n        Matrix mProjectionMatrix = Matrix.CreateLookAt(Vector3.One, Vector3.Zero, Vector3.Up);\r\n\r\n        // Lists of data\r\n        private List<VertexPositionColor> sLinesList = new List<VertexPositionColor>();\r\n        private List<TextData> sTextList = new List<TextData>();\r\n        private List<ShapeData> sShapeList = new List<ShapeData>();\r\n\r\n        // Shape-drawing data\r\n        BasicEffect mBasicShapeEffect;\r\n        VertexPositionNormalTexture[] mCubeVertices;\r\n        VertexBuffer mCubeVertexBuffer;\r\n        const int MAX_SHAPES = 200;\r\n\r\n        // Line-drawing data\r\n        VertexBuffer mLineVertexBuffer;\r\n        BasicEffect mBasicLineEffect;\r\n        const int MAX_LINES = 1024;\r\n\r\n        // Text-drawing data\r\n        SpriteBatch mSpriteBatch;\r\n        SpriteFont mFont1;\r\n        const int MAX_TEXT_LINES = 50;\r\n        #endregion\r\n\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/ICamera.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\n\r\nnamespace GameEngine\r\n{\r\n    public interface ICamera\r\n    {\r\n        /// <summary>\r\n        /// Returns the camera's current view matrix.\r\n        /// </summary>\r\n        Matrix View { get; }\r\n\r\n        /// <summary>\r\n        /// Returns the camera's current perspective projection matrix.\r\n        /// </summary>\r\n        Matrix Projection { get; }\r\n\r\n        void FollowObject(GameObject obj);\r\n\r\n        void Update(GameTime time);\r\n\r\n        Vector3 Position { get;}\r\n\r\n        void SetPosition(Vector3 position);\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/IDrawableObject.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace GameEngine\r\n{\r\n    public interface IDrawableObject\r\n    {\r\n        void Update(GameTime gameTime);\r\n        void Draw();        \r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/IGameScreen.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\n\r\nnamespace GameEngine\r\n{\r\n    public interface IGameScreen : IDrawableObject\r\n    {\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/IWorld.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace GameEngine\r\n{\r\n    public interface IWorld : IDrawableObject\r\n    {\r\n        float GetHeightAtPoint(Vector3 position);\r\n        void Reset();\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/InputProvider.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Input;\r\nusing System.Diagnostics;\r\n\r\nnamespace GameEngine\r\n{\r\n    public enum Actions\r\n    {\r\n        MoveForwardsPrimary,\r\n        MoveForwardsAlternate,\r\n        MoveBackwardsPrimary,\r\n        MoveBackwardsAlternate,\r\n        StrafeRightPrimary,\r\n        StrafeRightAlternate,\r\n        StrafeLeftPrimary,\r\n        StrafeLeftAlternate,\r\n        RunPrimary,\r\n        RunAlternate,\r\n        CrouchPrimary,\r\n        CrouchAlternate,\r\n        JumpPrimary,\r\n        JumpAlternate\r\n    };\r\n\r\n    public enum MouseInputMode\r\n    {\r\n        FPS,\r\n        FreeMouse\r\n    }\r\n\r\n    public class InputProvider : GameComponent\r\n    {\r\n        private GamePadState _gamePadState, _previousGamePadState;\r\n\r\n        private KeyboardState _keyboardState, _previousKeyboardState;\r\n        \r\n        private const float SENSITIVITY = 1.0f;\r\n        \r\n        private float _perFrameMultiplier;\r\n        \r\n\r\n        public InputProvider(Game game)\r\n            : base(game)\r\n        {\r\n            \r\n        }\r\n\r\n        public GamePadState GamePadState\r\n        {\r\n            get { return _gamePadState; }\r\n        }\r\n\r\n\r\n        public override void Update(GameTime gameTime)\r\n        {\r\n            float frameTime = (float)gameTime.ElapsedGameTime.TotalSeconds;\r\n            _perFrameMultiplier = frameTime * SENSITIVITY;\r\n            \r\n            _previousKeyboardState = _keyboardState;\r\n            _previousGamePadState = _gamePadState;\r\n            _keyboardState = Keyboard.GetState();\r\n            _gamePadState = GamePad.GetState(PlayerIndex.One);\r\n\r\n            if (_previousKeyboardState == null)\r\n                _previousKeyboardState = _keyboardState;\r\n            \r\n            base.Update(gameTime);\r\n        }\r\n\r\n        private Vector2 GetScreenCenter()\r\n        {\r\n            GameWindow window = Engine.Instance.Game.Window;\r\n            return new Vector2(window.ClientBounds.Width / 2, window.ClientBounds.Height / 2);\r\n        }\r\n\r\n        public float MoveForward\r\n        {\r\n            get\r\n            {\r\n                if (_gamePadState.ThumbSticks.Left.Y != 0)\r\n                    return _gamePadState.ThumbSticks.Left.Y * _perFrameMultiplier;\r\n                else if (_keyboardState.IsKeyDown(Keys.W))\r\n                    return 1.0f * _perFrameMultiplier;\r\n                else if (_keyboardState.IsKeyDown(Keys.S))\r\n                    return -1.0f * _perFrameMultiplier;\r\n                else\r\n                    return 0.0f;\r\n            }\r\n        }\r\n        public float Strafe\r\n        {\r\n            get\r\n            {\r\n                if (_gamePadState.ThumbSticks.Left.X != 0)\r\n                    return _gamePadState.ThumbSticks.Left.X * _perFrameMultiplier;\r\n                else if (_keyboardState.IsKeyDown(Keys.A))\r\n                    return -1.0f * _perFrameMultiplier;\r\n                else if (_keyboardState.IsKeyDown(Keys.D))\r\n                    return 1.0f * _perFrameMultiplier;\r\n                else\r\n                    return 0.0f;\r\n            }\r\n        }\r\n\r\n        public bool WasPressed(Keys key)\r\n        {\r\n            return _previousKeyboardState.IsKeyDown(key) && !_keyboardState.IsKeyDown(key);\r\n        }\r\n\r\n        public bool WasPressed(Buttons button)\r\n        {\r\n            return _previousGamePadState.IsButtonDown(button) && !_gamePadState.IsButtonDown(button);\r\n        }\r\n\r\n        public bool IsKeyDown(Keys key)\r\n        {\r\n            return _keyboardState.IsKeyDown(key);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/ParticleSystem/ParticleEmitter.cs",
    "content": "#region File Description\r\n//-----------------------------------------------------------------------------\r\n// ParticleEmitter.cs\r\n//\r\n// Microsoft XNA Community Game Platform\r\n// Copyright (C) Microsoft Corporation. All rights reserved.\r\n//-----------------------------------------------------------------------------\r\n#endregion\r\n\r\n#region Using Statements\r\nusing System;\r\nusing Microsoft.Xna.Framework;\r\nusing System.Collections.Generic;\r\n#endregion\r\n\r\nnamespace GameEngine\r\n{\r\n    /// <summary>\r\n    /// Helper for objects that want to leave particles behind them as they\r\n    /// move around the world. This emitter implementation solves two related\r\n    /// problems:\r\n    /// \r\n    /// If an object wants to create particles very slowly, less than once per\r\n    /// frame, it can be a pain to keep track of which updates ought to create\r\n    /// a new particle versus which should not.\r\n    /// \r\n    /// If an object is moving quickly and is creating many particles per frame,\r\n    /// it will look ugly if these particles are all bunched up together. Much\r\n    /// better if they can be spread out along a line between where the object\r\n    /// is now and where it was on the previous frame. This is particularly\r\n    /// important for leaving trails behind fast moving objects such as rockets.\r\n    /// \r\n    /// This emitter class keeps track of a moving object, remembering its\r\n    /// previous position so it can calculate the velocity of the object. It\r\n    /// works out the perfect locations for creating particles at any frequency\r\n    /// you specify, regardless of whether this is faster or slower than the\r\n    /// game update rate.\r\n    /// </summary>\r\n    public class ParticleEmitter\r\n    {\r\n        #region Fields\r\n\r\n        public ParticleSystem ParticleSystem { get; set; }\r\n        float _timeBetweenParticles;\r\n        Vector3 _previousPosition;\r\n        float _timeLeftOver;\r\n        public float ParticlesPerSecond;\r\n        public float DumpsPerSecond = 0.2f;\r\n\r\n        #endregion\r\n\r\n        public float LastDumpTime;\r\n        public bool Enabled = true;\r\n        public List<ParticleSystem> ParticleSystems = new List<ParticleSystem>();\r\n\r\n        public static List<ParticleEmitter> AllEmitters = new List<ParticleEmitter>();\r\n\r\n        /// <summary>\r\n        /// Constructs a new particle emitter object.\r\n        /// </summary>\r\n        public ParticleEmitter(ParticleSystem particleSystem, float particlesPerSecond, Vector3 initialPosition)\r\n        {\r\n            ParticleSystem = particleSystem;\r\n\r\n            _timeBetweenParticles = 1.0f / particlesPerSecond;\r\n            ParticlesPerSecond = particlesPerSecond;\r\n            _previousPosition = initialPosition;\r\n            \r\n            AllEmitters.Add(this);\r\n        }\r\n\r\n        public void Update(Vector3 newPosition)\r\n        {\r\n            Update(newPosition, ParticleSystem);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Updates the emitter, creating the appropriate number of particles\r\n        /// in the appropriate positions.\r\n        /// </summary>\r\n        public void Update(Vector3 newPosition, ParticleSystem particleSystem)\r\n        {\r\n\t\t\tfloat elapsedSeconds = Engine.Instance.FrameTime;\r\n            \r\n            // Work out how much time has passed since the previous update.\r\n\r\n            if (elapsedSeconds > 0 && Enabled)\r\n            {\r\n                // Work out how fast we are moving.\r\n                Vector3 velocity = (newPosition - _previousPosition) / elapsedSeconds;\r\n\r\n                // If we had any time left over that we didn't use during the\r\n                // previous update, add that to the current elapsed time.\r\n                float timeToSpend = _timeLeftOver + elapsedSeconds;\r\n                \r\n                // Counter for looping over the time interval.\r\n                float currentTime = -_timeLeftOver;\r\n\r\n                // Create particles as long as we have a big enough time interval.\r\n                while (timeToSpend > _timeBetweenParticles)\r\n                {\r\n                    currentTime += _timeBetweenParticles;\r\n                    timeToSpend -= _timeBetweenParticles;\r\n\r\n                    // Work out the optimal position for this particle. This will produce\r\n                    // evenly spaced particles regardless of the object speed, particle\r\n                    // creation frequency, or game update rate.\r\n                    float mu = currentTime / elapsedSeconds;\r\n\r\n                    Vector3 position = Vector3.Lerp(_previousPosition, newPosition, mu);\r\n\r\n                    // Create the particle.\r\n                    particleSystem.AddParticle(position, velocity);\r\n                }\r\n\r\n                // Store any time we didn't use, so it can be part of the next update.\r\n                _timeLeftOver = timeToSpend;\r\n            }\r\n\r\n            _previousPosition = newPosition;\r\n        }\r\n\r\n        \r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/ParticleSystem/ParticleSettings.cs",
    "content": "#region File Description\r\n//-----------------------------------------------------------------------------\r\n// ParticleSettings.cs\r\n//\r\n// Microsoft XNA Community Game Platform\r\n// Copyright (C) Microsoft Corporation. All rights reserved.\r\n//-----------------------------------------------------------------------------\r\n#endregion\r\n\r\n#region Using Statements\r\nusing System;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\n#endregion\r\n\r\nnamespace GameEngine\r\n{\r\n    /// <summary>\r\n    /// Settings class describes all the tweakable options used\r\n    /// to control the appearance of a particle system.\r\n    /// </summary>\r\n    public class ParticleSettings\r\n    {\r\n        // Name of the texture used by this particle system.\r\n        public Texture2D Texture = null;\r\n\r\n\r\n        // Maximum number of particles that can be displayed at one time.\r\n        public int MaxParticles = 100;\r\n\r\n\r\n        // How long these particles will last.\r\n        public TimeSpan Duration = TimeSpan.FromSeconds(1);\r\n\r\n\r\n        // If greater than zero, some particles will last a shorter time than others.\r\n        public float DurationRandomness = 0;\r\n\r\n\r\n        // Controls how much particles are influenced by the velocity of the object\r\n        // which created them. You can see this in action with the explosion effect,\r\n        // where the flames continue to move in the same direction as the source\r\n        // projectile. The projectile trail particles, on the other hand, set this\r\n        // value very low so they are less affected by the velocity of the projectile.\r\n        public float EmitterVelocitySensitivity = 1;\r\n\r\n\r\n        // Range of values controlling how much X and Z axis velocity to give each\r\n        // particle. Values for individual particles are randomly chosen from somewhere\r\n        // between these limits.\r\n        public float MinHorizontalVelocity = 0;\r\n        public float MaxHorizontalVelocity = 0;\r\n\r\n\r\n        // Range of values controlling how much Y axis velocity to give each particle.\r\n        // Values for individual particles are randomly chosen from somewhere between\r\n        // these limits.\r\n        public float MinVerticalVelocity = 0;\r\n        public float MaxVerticalVelocity = 0;\r\n\r\n\r\n        // Direction and strength of the gravity effect. Note that this can point in any\r\n        // direction, not just down! The fire effect points it upward to make the flames\r\n        // rise, and the smoke plume points it sideways to simulate wind.\r\n        public Vector3 Gravity = Vector3.Zero;\r\n\r\n\r\n        // Controls how the particle velocity will change over their lifetime. If set\r\n        // to 1, particles will keep going at the same speed as when they were created.\r\n        // If set to 0, particles will come to a complete stop right before they die.\r\n        // Values greater than 1 make the particles speed up over time.\r\n        public float EndVelocity = 1;\r\n\r\n\r\n        // Range of values controlling the particle color and alpha. Values for\r\n        // individual particles are randomly chosen from somewhere between these limits.\r\n        public Color MinColor = Color.White;\r\n        public Color MaxColor = Color.White;\r\n\r\n\r\n        // Range of values controlling how fast the particles rotate. Values for\r\n        // individual particles are randomly chosen from somewhere between these\r\n        // limits. If both these values are set to 0, the particle system will\r\n        // automatically switch to an alternative shader technique that does not\r\n        // support rotation, and thus requires significantly less GPU power. This\r\n        // means if you don't need the rotation effect, you may get a performance\r\n        // boost from leaving these values at 0.\r\n        public float MinRotateSpeed = 0;\r\n        public float MaxRotateSpeed = 0;\r\n\r\n\r\n        // Range of values controlling how big the particles are when first created.\r\n        // Values for individual particles are randomly chosen from somewhere between\r\n        // these limits.\r\n        public float MinStartSize = 100;\r\n        public float MaxStartSize = 100;\r\n\r\n\r\n        // Range of values controlling how big particles become at the end of their\r\n        // life. Values for individual particles are randomly chosen from somewhere\r\n        // between these limits.\r\n        public float MinEndSize = 100;\r\n        public float MaxEndSize = 100;\r\n\r\n\r\n        // Alpha blending settings.\r\n        public Blend SourceBlend = Blend.SourceAlpha;\r\n        public Blend DestinationBlend = Blend.InverseSourceAlpha;\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/ParticleSystem/ParticleSystem.cs",
    "content": "#region File Description\r\n//-----------------------------------------------------------------------------\r\n// ParticleSystem.cs\r\n//\r\n// Microsoft XNA Community Game Platform\r\n// Copyright (C) Microsoft Corporation. All rights reserved.\r\n//-----------------------------------------------------------------------------\r\n#endregion\r\n\r\n#region Using Statements\r\nusing System;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Content;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework.Graphics.PackedVector;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\nusing System.IO;\r\n#endregion\r\n\r\nnamespace GameEngine\r\n{\r\n\t/// <summary>\r\n\t/// The main component in charge of displaying particles.\r\n\t/// </summary>\r\n\tpublic abstract class ParticleSystem\r\n\t{\r\n\t\t#region Fields\r\n\r\n\r\n\t\t// Settings class controls the appearance and animation of this particle system.\r\n\t\tParticleSettings settings = new ParticleSettings();\r\n\r\n\t\t// Custom effect for drawing particles. This computes the particle\r\n\t\t// animation entirely in the vertex shader: no per-particle CPU work required!\r\n\t\tEffect particleEffect;\r\n\r\n\r\n\t\t// Shortcuts for accessing frequently changed effect parameters.\r\n\t\tEffectParameter effectViewParameter;\r\n\t\tEffectParameter effectProjectionParameter;\r\n\t\tEffectParameter effectViewportScaleParameter;\r\n\t\tEffectParameter effectTimeParameter;\r\n\r\n\r\n\t\t// An array of particles, treated as a circular queue.\r\n\t\tParticleVertex[] particles;\r\n\r\n\r\n\t\t// A vertex buffer holding our particles. This contains the same data as\r\n\t\t// the particles array, but copied across to where the GPU can access it.\r\n\t\tDynamicVertexBuffer vertexBuffer;\r\n\r\n\r\n\t\t// Index buffer turns sets of four vertices into particle quads (pairs of triangles).\r\n\t\tIndexBuffer indexBuffer;\r\n\r\n\r\n\t\t// The particles array and vertex buffer are treated as a circular queue.\r\n\t\t// Initially, the entire contents of the array are free, because no particles\r\n\t\t// are in use. When a new particle is created, this is allocated from the\r\n\t\t// beginning of the array. If more than one particle is created, these will\r\n\t\t// always be stored in a consecutive block of array elements. Because all\r\n\t\t// particles last for the same amount of time, old particles will always be\r\n\t\t// removed in order from the start of this active particle region, so the\r\n\t\t// active and free regions will never be intermingled. Because the queue is\r\n\t\t// circular, there can be times when the active particle region wraps from the\r\n\t\t// end of the array back to the start. The queue uses modulo arithmetic to\r\n\t\t// handle these cases. For instance with a four entry queue we could have:\r\n\t\t//\r\n\t\t//      0\r\n\t\t//      1 - first active particle\r\n\t\t//      2 \r\n\t\t//      3 - first free particle\r\n\t\t//\r\n\t\t// In this case, particles 1 and 2 are active, while 3 and 4 are free.\r\n\t\t// Using modulo arithmetic we could also have:\r\n\t\t//\r\n\t\t//      0\r\n\t\t//      1 - first free particle\r\n\t\t//      2 \r\n\t\t//      3 - first active particle\r\n\t\t//\r\n\t\t// Here, 3 and 0 are active, while 1 and 2 are free.\r\n\t\t//\r\n\t\t// But wait! The full story is even more complex.\r\n\t\t//\r\n\t\t// When we create a new particle, we add them to our managed particles array.\r\n\t\t// We also need to copy this new data into the GPU vertex buffer, but we don't\r\n\t\t// want to do that straight away, because setting new data into a vertex buffer\r\n\t\t// can be an expensive operation. If we are going to be adding several particles\r\n\t\t// in a single frame, it is faster to initially just store them in our managed\r\n\t\t// array, and then later upload them all to the GPU in one single call. So our\r\n\t\t// queue also needs a region for storing new particles that have been added to\r\n\t\t// the managed array but not yet uploaded to the vertex buffer.\r\n\t\t//\r\n\t\t// Another issue occurs when old particles are retired. The CPU and GPU run\r\n\t\t// asynchronously, so the GPU will often still be busy drawing the previous\r\n\t\t// frame while the CPU is working on the next frame. This can cause a\r\n\t\t// synchronization problem if an old particle is retired, and then immediately\r\n\t\t// overwritten by a new one, because the CPU might try to change the contents\r\n\t\t// of the vertex buffer while the GPU is still busy drawing the old data from\r\n\t\t// it. Normally the graphics driver will take care of this by waiting until\r\n\t\t// the GPU has finished drawing inside the VertexBuffer.SetData call, but we\r\n\t\t// don't want to waste time waiting around every time we try to add a new\r\n\t\t// particle! To avoid this delay, we can specify the SetDataOptions.NoOverwrite\r\n\t\t// flag when we write to the vertex buffer. This basically means \"I promise I\r\n\t\t// will never try to overwrite any data that the GPU might still be using, so\r\n\t\t// you can just go ahead and update the buffer straight away\". To keep this\r\n\t\t// promise, we must avoid reusing vertices immediately after they are drawn.\r\n\t\t//\r\n\t\t// So in total, our queue contains four different regions:\r\n\t\t//\r\n\t\t// Vertices between firstActiveParticle and firstNewParticle are actively\r\n\t\t// being drawn, and exist in both the managed particles array and the GPU\r\n\t\t// vertex buffer.\r\n\t\t//\r\n\t\t// Vertices between firstNewParticle and firstFreeParticle are newly created,\r\n\t\t// and exist only in the managed particles array. These need to be uploaded\r\n\t\t// to the GPU at the start of the next draw call.\r\n\t\t//\r\n\t\t// Vertices between firstFreeParticle and firstRetiredParticle are free and\r\n\t\t// waiting to be allocated.\r\n\t\t//\r\n\t\t// Vertices between firstRetiredParticle and firstActiveParticle are no longer\r\n\t\t// being drawn, but were drawn recently enough that the GPU could still be\r\n\t\t// using them. These need to be kept around for a few more frames before they\r\n\t\t// can be reallocated.\r\n\r\n\t\tint firstActiveParticle;\r\n\t\tint firstNewParticle;\r\n\t\tint firstFreeParticle;\r\n\t\tint firstRetiredParticle;\r\n\r\n\r\n\t\t// Store the current time, in seconds.\r\n\t\tfloat currentTime;\r\n\r\n\r\n\t\t// Count how many times Draw has been called. This is used to know\r\n\t\t// when it is safe to retire old particles back into the free list.\r\n\t\tint drawCounter;\r\n\r\n\r\n\t\t// Shared random number generator.\r\n\t\tstatic Random random = new Random();\r\n\r\n\r\n\t\t#endregion\r\n\r\n\t\tpublic static List<ParticleSystem> AllParticleSystems = new List<ParticleSystem>();\r\n\r\n\t\t#region Initialization\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Initializes the component.\r\n\t\t/// </summary>\r\n\t\tpublic void InitializeSystem()\r\n\t\t{\r\n\t\t\tInitializeSettings(settings);\r\n\r\n\t\t\t// Allocate the particle array, and fill in the corner fields (which never change).\r\n\t\t\tparticles = new ParticleVertex[settings.MaxParticles * 4];\r\n\r\n\t\t\tfor (int i = 0; i < settings.MaxParticles; i++)\r\n\t\t\t{\r\n\t\t\t\tparticles[i * 4 + 0].Corner = new Short2(-1, -1);\r\n\t\t\t\tparticles[i * 4 + 1].Corner = new Short2(1, -1);\r\n\t\t\t\tparticles[i * 4 + 2].Corner = new Short2(1, 1);\r\n\t\t\t\tparticles[i * 4 + 3].Corner = new Short2(-1, 1);\r\n\t\t\t}\r\n\r\n\t\t\tLoadContent();\r\n\r\n\t\t\tAllParticleSystems.Add(this);\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Derived particle system classes should override this method\r\n\t\t/// and use it to initalize their tweakable settings.\r\n\t\t/// </summary>\r\n\t\tprotected abstract void InitializeSettings(ParticleSettings settings);\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Loads graphics for the particle system.\r\n\t\t/// </summary>\r\n\t\tprotected void LoadContent()\r\n\t\t{\r\n\t\t\tLoadParticleEffect();\r\n\r\n\t\t\tvertexBuffer = new DynamicVertexBuffer(Engine.Instance.Device, ParticleVertex.VertexDeclaration, \r\n\t\t\t\tsettings.MaxParticles * 4, BufferUsage.WriteOnly);\r\n\r\n\t\t\t// Create and populate the index buffer.\r\n\t\t\tushort[] indices = new ushort[settings.MaxParticles * 6];\r\n\r\n\t\t\tfor (int i = 0; i < settings.MaxParticles; i++)\r\n\t\t\t{\r\n\t\t\t\tindices[i * 6 + 0] = (ushort)(i * 4 + 0);\r\n\t\t\t\tindices[i * 6 + 1] = (ushort)(i * 4 + 1);\r\n\t\t\t\tindices[i * 6 + 2] = (ushort)(i * 4 + 2);\r\n\r\n\t\t\t\tindices[i * 6 + 3] = (ushort)(i * 4 + 0);\r\n\t\t\t\tindices[i * 6 + 4] = (ushort)(i * 4 + 2);\r\n\t\t\t\tindices[i * 6 + 5] = (ushort)(i * 4 + 3);\r\n\t\t\t}\r\n\r\n\t\t\tindexBuffer = new IndexBuffer(Engine.Instance.Device, typeof(ushort), indices.Length, BufferUsage.WriteOnly);\r\n\r\n\t\t\tindexBuffer.SetData(indices);\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Helper for loading and initializing the particle effect.\r\n\t\t/// </summary>\r\n\t\tvoid LoadParticleEffect()\r\n\t\t{\r\n\t\t\t//Effect effect = Engine.Instance.ContentManager.Load<Effect>(\"Content/ParticleEffect\");\r\n\r\n\t\t\t// If we have several particle systems, the content manager will return\r\n\t\t\t// a single shared effect instance to them all. But we want to preconfigure\r\n\t\t\t// the effect with parameters that are specific to this particular\r\n\t\t\t// particle system. By cloning the effect, we prevent one particle system\r\n\t\t\t// from stomping over the parameter settings of another.\r\n\r\n\t\t\t//particleEffect = new Effect(Engine.Instance.Device, File.ReadAllBytes(\"Content\\\\ParticleEffect.mgfx\"));\r\n\t\t\tparticleEffect = Engine.Instance.ContentManager.Load<Effect>(\"Content/ParticleEffect\");\r\n\r\n\t\t\tEffectParameterCollection parameters = particleEffect.Parameters;\r\n\r\n\t\t\t// Look up shortcuts for parameters that change every frame.\r\n\t\t\teffectViewParameter = parameters[\"View\"];\r\n\t\t\teffectProjectionParameter = parameters[\"Projection\"];\r\n\t\t\teffectViewportScaleParameter = parameters[\"ViewportScale\"];\r\n\t\t\teffectTimeParameter = parameters[\"CurrentTime\"];\r\n\r\n\t\t\t// Set the values of parameters that do not change.\r\n\t\t\tparameters[\"Duration\"].SetValue((float)settings.Duration.TotalSeconds);\r\n\t\t\tparameters[\"DurationRandomness\"].SetValue(settings.DurationRandomness);\r\n\t\t\tparameters[\"Gravity\"].SetValue(settings.Gravity);\r\n\t\t\tparameters[\"EndVelocity\"].SetValue(settings.EndVelocity);\r\n\t\t\tparameters[\"MinColor\"].SetValue(settings.MinColor.ToVector4());\r\n\t\t\tparameters[\"MaxColor\"].SetValue(settings.MaxColor.ToVector4());\r\n\r\n\t\t\tparameters[\"RotateSpeed\"].SetValue(\r\n\t\t\t\tnew Vector2(settings.MinRotateSpeed, settings.MaxRotateSpeed));\r\n\r\n\t\t\tparameters[\"StartSize\"].SetValue(\r\n\t\t\t\tnew Vector2(settings.MinStartSize, settings.MaxStartSize));\r\n\r\n\t\t\tparameters[\"EndSize\"].SetValue(\r\n\t\t\t\tnew Vector2(settings.MinEndSize, settings.MaxEndSize));\r\n\r\n\t\t\tparameters[\"Texture\"].SetValue(settings.Texture);\r\n\t\t}\r\n\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Update and Draw\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Updates the particle system.\r\n\t\t/// </summary>\r\n\t\tpublic void Update()\r\n\t\t{\r\n\t\t\tcurrentTime += Engine.Instance.FrameTime;\r\n\r\n\t\t\tRetireActiveParticles();\r\n\t\t\tFreeRetiredParticles();\r\n\r\n\t\t\t// If we let our timer go on increasing for ever, it would eventually\r\n\t\t\t// run out of floating point precision, at which point the particles\r\n\t\t\t// would render incorrectly. An easy way to prevent this is to notice\r\n\t\t\t// that the time value doesn't matter when no particles are being drawn,\r\n\t\t\t// so we can reset it back to zero any time the active queue is empty.\r\n\r\n\t\t\tif (firstActiveParticle == firstFreeParticle)\r\n\t\t\t\tcurrentTime = 0;\r\n\r\n\t\t\tif (firstRetiredParticle == firstActiveParticle)\r\n\t\t\t\tdrawCounter = 0;\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Helper for checking when active particles have reached the end of\r\n\t\t/// their life. It moves old particles from the active area of the queue\r\n\t\t/// to the retired section.\r\n\t\t/// </summary>\r\n\t\tvoid RetireActiveParticles()\r\n\t\t{\r\n\t\t\tfloat particleDuration = (float)settings.Duration.TotalSeconds;\r\n\r\n\t\t\twhile (firstActiveParticle != firstNewParticle)\r\n\t\t\t{\r\n\t\t\t\t// Is this particle old enough to retire?\r\n\t\t\t\t// We multiply the active particle index by four, because each\r\n\t\t\t\t// particle consists of a quad that is made up of four vertices.\r\n\t\t\t\tfloat particleAge = currentTime - particles[firstActiveParticle * 4].Time;\r\n\r\n\t\t\t\tif (particleAge < particleDuration)\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t// Remember the time at which we retired this particle.\r\n\t\t\t\tparticles[firstActiveParticle * 4].Time = drawCounter;\r\n\r\n\t\t\t\t// Move the particle from the active to the retired queue.\r\n\t\t\t\tfirstActiveParticle++;\r\n\r\n\t\t\t\tif (firstActiveParticle >= settings.MaxParticles)\r\n\t\t\t\t\tfirstActiveParticle = 0;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Helper for checking when retired particles have been kept around long\r\n\t\t/// enough that we can be sure the GPU is no longer using them. It moves\r\n\t\t/// old particles from the retired area of the queue to the free section.\r\n\t\t/// </summary>\r\n\t\tvoid FreeRetiredParticles()\r\n\t\t{\r\n\t\t\twhile (firstRetiredParticle != firstActiveParticle)\r\n\t\t\t{\r\n\t\t\t\t// Has this particle been unused long enough that\r\n\t\t\t\t// the GPU is sure to be finished with it?\r\n\t\t\t\t// We multiply the retired particle index by four, because each\r\n\t\t\t\t// particle consists of a quad that is made up of four vertices.\r\n\t\t\t\tint age = drawCounter - (int)particles[firstRetiredParticle * 4].Time;\r\n\r\n\t\t\t\t// The GPU is never supposed to get more than 2 frames behind the CPU.\r\n\t\t\t\t// We add 1 to that, just to be safe in case of buggy drivers that\r\n\t\t\t\t// might bend the rules and let the GPU get further behind.\r\n\t\t\t\tif (age < 3)\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t// Move the particle from the retired to the free queue.\r\n\t\t\t\tfirstRetiredParticle++;\r\n\r\n\t\t\t\tif (firstRetiredParticle >= settings.MaxParticles)\r\n\t\t\t\t\tfirstRetiredParticle = 0;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Draws the particle system.\r\n\t\t/// </summary>\r\n\t\tpublic void Render()\r\n\t\t{\r\n\t\t\tGraphicsDevice device = Engine.Instance.Device;\r\n\t\t\t\r\n\t\t\tICamera camera = Engine.Instance.Camera;\r\n\t\t\teffectViewParameter.SetValue(camera.View);\r\n\t\t\teffectProjectionParameter.SetValue(camera.Projection);\r\n\r\n\t\t\t// Restore the vertex buffer contents if the graphics device was lost.\r\n\t\t\tif (vertexBuffer.IsContentLost)\r\n\t\t\t{\r\n\t\t\t\tvertexBuffer.SetData(particles);\r\n\t\t\t}\r\n\r\n\t\t\t// If there are any particles waiting in the newly added queue,\r\n\t\t\t// we'd better upload them to the GPU ready for drawing.\r\n\t\t\tif (firstNewParticle != firstFreeParticle)\r\n\t\t\t{\r\n\t\t\t\tAddNewParticlesToVertexBuffer();\r\n\t\t\t}\r\n\r\n\t\t\t// If there are any active particles, draw them now!\r\n\t\t\tif (firstActiveParticle != firstFreeParticle)\r\n\t\t\t{\r\n\t\t\t\tSetParticleRenderStates();\r\n\r\n\t\t\t\t// Set an effect parameter describing the viewport size. This is\r\n\t\t\t\t// needed to convert particle sizes into screen space point sizes.\r\n\t\t\t\teffectViewportScaleParameter.SetValue(new Vector2(0.5f / device.Viewport.AspectRatio, -0.5f));\r\n\r\n\t\t\t\t// Set an effect parameter describing the current time. All the vertex\r\n\t\t\t\t// shader particle animation is keyed off this value.\r\n\t\t\t\teffectTimeParameter.SetValue(currentTime);\r\n\r\n\t\t\t\t// Set the particle vertex and index buffer.\r\n\t\t\t\tdevice.SetVertexBuffer(vertexBuffer);\r\n\t\t\t\tdevice.Indices = indexBuffer;\r\n\r\n\t\t\t\t// Activate the particle effect.\r\n\t\t\t\tforeach (EffectPass pass in particleEffect.CurrentTechnique.Passes)\r\n\t\t\t\t{\r\n\t\t\t\t\tpass.Apply();\r\n\r\n\t\t\t\t\tif (firstActiveParticle < firstFreeParticle)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// If the active particles are all in one consecutive range,\r\n\t\t\t\t\t\t// we can draw them all in a single call.\r\n\t\t\t\t\t\tdevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t /*firstActiveParticle * 4*/ 0, (firstFreeParticle),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t firstActiveParticle * 6, (firstFreeParticle - firstActiveParticle) * 2);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// If the active particle range wraps past the end of the queue\r\n\t\t\t\t\t\t// back to the start, we must split them over two draw calls.\r\n\t\t\t\t\t\tdevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t /*firstActiveParticle * 4*/ 0, (settings.MaxParticles),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t firstActiveParticle * 6, (settings.MaxParticles - firstActiveParticle) * 2);\r\n\r\n\t\t\t\t\t\tif (firstFreeParticle > 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tdevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t 0, firstFreeParticle * 4,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t 0, firstFreeParticle * 2);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Reset some of the renderstates that we changed,\r\n\t\t\t\t// so as not to mess up any other subsequent drawing.\r\n\t\t\t\tdevice.DepthStencilState = DepthStencilState.Default;\r\n\t\t\t\tdevice.BlendState = BlendState.Opaque;\r\n\t\t\t}\r\n\r\n\t\t\tdrawCounter++;\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Helper for uploading new particles from our managed\r\n\t\t/// array to the GPU vertex buffer.\r\n\t\t/// </summary>\r\n\t\tvoid AddNewParticlesToVertexBuffer()\r\n\t\t{\r\n\t\t\tint stride = ParticleVertex.SizeInBytes;\r\n\r\n\t\t\tif (firstNewParticle < firstFreeParticle)\r\n\t\t\t{\r\n\t\t\t\t// If the new particles are all in one consecutive range,\r\n\t\t\t\t// we can upload them all in a single call.\r\n\t\t\t\tvertexBuffer.SetData(firstNewParticle * stride * 4, particles,\r\n\t\t\t\t\t\t\t\t\t firstNewParticle * 4,\r\n\t\t\t\t\t\t\t\t\t (firstFreeParticle - firstNewParticle) * 4,\r\n\t\t\t\t\t\t\t\t\t stride, SetDataOptions.NoOverwrite);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// If the new particle range wraps past the end of the queue\r\n\t\t\t\t// back to the start, we must split them over two upload calls.\r\n\t\t\t\tvertexBuffer.SetData(firstNewParticle * stride * 4, particles,\r\n\t\t\t\t\t\t\t\t\t firstNewParticle * 4,\r\n\t\t\t\t\t\t\t\t\t (settings.MaxParticles - firstNewParticle) * 4,\r\n\t\t\t\t\t\t\t\t\t stride, SetDataOptions.NoOverwrite);\r\n\r\n\t\t\t\tif (firstFreeParticle > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tvertexBuffer.SetData(0, particles,\r\n\t\t\t\t\t\t\t\t\t\t 0, firstFreeParticle * 4,\r\n\t\t\t\t\t\t\t\t\t\t stride, SetDataOptions.NoOverwrite);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Move the particles we just uploaded from the new to the active queue.\r\n\t\t\tfirstNewParticle = firstFreeParticle;\r\n\t\t}\r\n\r\n\r\n\t\t#endregion\r\n\r\n\t\t#region Public Methods\r\n\r\n\r\n\t\t\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Adds a new particle to the system.\r\n\t\t/// </summary>\r\n\t\tpublic void AddParticle(Vector3 position, Vector3 velocity)\r\n\t\t{\r\n\t\t\t// Figure out where in the circular queue to allocate the new particle.\r\n\t\t\tint nextFreeParticle = firstFreeParticle + 1;\r\n\r\n\t\t\tif (nextFreeParticle >= settings.MaxParticles)\r\n\t\t\t\tnextFreeParticle = 0;\r\n\r\n\t\t\t// If there are no free particles, we just have to give up.\r\n\t\t\tif (nextFreeParticle == firstRetiredParticle)\r\n\t\t\t{\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Adjust the input velocity based on how much\r\n\t\t\t// this particle system wants to be affected by it.\r\n\t\t\tvelocity *= settings.EmitterVelocitySensitivity;\r\n\r\n\t\t\t// Add in some random amount of horizontal velocity.\r\n\t\t\tfloat horizontalVelocity = MathHelper.Lerp(settings.MinHorizontalVelocity,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t   settings.MaxHorizontalVelocity,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t   (float)random.NextDouble());\r\n\r\n\t\t\tdouble horizontalAngle = random.NextDouble() * MathHelper.TwoPi;\r\n\r\n\t\t\tvelocity.X += horizontalVelocity * (float)Math.Cos(horizontalAngle);\r\n\t\t\tvelocity.Z += horizontalVelocity * (float)Math.Sin(horizontalAngle);\r\n\r\n\t\t\t// Add in some random amount of vertical velocity.\r\n\t\t\tvelocity.Y += MathHelper.Lerp(settings.MinVerticalVelocity,\r\n\t\t\t\t\t\t\t\t\t\t  settings.MaxVerticalVelocity,\r\n\t\t\t\t\t\t\t\t\t\t  (float)random.NextDouble());\r\n\r\n\t\t\t// Choose four random control values. These will be used by the vertex\r\n\t\t\t// shader to give each particle a different size, rotation, and color.\r\n\t\t\tColor randomValues = new Color((byte)random.Next(255),\r\n\t\t\t\t\t\t\t\t\t\t   (byte)random.Next(255),\r\n\t\t\t\t\t\t\t\t\t\t   (byte)random.Next(255),\r\n\t\t\t\t\t\t\t\t\t\t   (byte)random.Next(255));\r\n\r\n\t\t\t// Fill in the particle vertex structure.\r\n\t\t\tfor (int i = 0; i < 4; i++)\r\n\t\t\t{\r\n\t\t\t\tparticles[firstFreeParticle * 4 + i].Position = position;\r\n\t\t\t\tparticles[firstFreeParticle * 4 + i].Velocity = velocity;\r\n\t\t\t\tparticles[firstFreeParticle * 4 + i].Random = randomValues;\r\n\t\t\t\tparticles[firstFreeParticle * 4 + i].Time = currentTime;\r\n\t\t\t}\r\n\r\n\t\t\tfirstFreeParticle = nextFreeParticle;\r\n\t\t}\r\n\r\n\r\n\t\t#endregion\r\n\r\n\t\tvoid SetParticleRenderStates()\r\n\t\t{\r\n\t\t\tEngine.Instance.Device.BlendState = BlendState.NonPremultiplied;\r\n\t\t\t// Set the alpha blend mode.\r\n\t\t\t//renderState.AlphaBlendEnable = true;\r\n\t\t\t//renderState.AlphaBlendOperation = BlendFunction.Add;\r\n\t\t\t//renderState.SourceBlend = settings.SourceBlend;\r\n\t\t\t//renderState.DestinationBlend = settings.DestinationBlend;\r\n\r\n\t\t\t// Set the alpha test mode.\r\n\t\t\t//renderState.AlphaTestEnable = true;\r\n\t\t\t//renderState.AlphaFunction = CompareFunction.Greater;\r\n\t\t\t//renderState.ReferenceAlpha = 0;\r\n\r\n\t\t\t// Enable the depth buffer (so particles will not be visible through\r\n\t\t\t// solid objects like the ground plane), but disable depth writes\r\n\t\t\t// (so particles will not obscure other particles).\r\n\t\t\tEngine.Instance.Device.DepthStencilState = DepthStencilState.DepthRead;\r\n\t\t}\r\n\r\n\t\tpublic void Clear()\r\n\t\t{\r\n\t\t\tfirstActiveParticle = 0;\r\n\t\t\tfirstNewParticle = 0;\r\n\t\t\tfirstFreeParticle = 0;\r\n\t\t\tfirstRetiredParticle = 0;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Sets the camera view and projection matrices\r\n\t\t/// that will be used to draw this particle system.\r\n\t\t/// </summary>\r\n\t\tpublic void SetCamera(ICamera camera)\r\n\t\t{\r\n\t\t\teffectViewParameter.SetValue(camera.View);\r\n\t\t\teffectProjectionParameter.SetValue(camera.Projection);\r\n\t\t}\r\n\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Engine/ParticleSystem/ParticleVertex.cs",
    "content": "#region File Description\r\n//-----------------------------------------------------------------------------\r\n// ParticleVertex.cs\r\n//\r\n// Microsoft XNA Community Game Platform\r\n// Copyright (C) Microsoft Corporation. All rights reserved.\r\n//-----------------------------------------------------------------------------\r\n#endregion\r\n\r\n#region Using Statements\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework.Graphics.PackedVector;\r\n#endregion\r\n\r\nnamespace GameEngine\r\n{\r\n    /// <summary>\r\n    /// Custom vertex structure for drawing point sprite particles.\r\n    /// </summary>\r\n    struct ParticleVertex\r\n    {\r\n\t\t// Stores which corner of the particle quad this vertex represents.\r\n\t\tpublic Short2 Corner;\r\n\r\n\t\t// Stores the starting position of the particle.\r\n\t\tpublic Vector3 Position;\r\n\r\n\t\t// Stores the starting velocity of the particle.\r\n\t\tpublic Vector3 Velocity;\r\n\r\n\t\t// Four random values, used to make each particle look slightly different.\r\n\t\tpublic Color Random;\r\n\r\n\t\t// The time (in seconds) at which this particle was created.\r\n\t\tpublic float Time;\r\n\r\n\r\n        // Describe the layout of this vertex structure.\r\n\t\tpublic static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration\r\n\t\t(\r\n\t\t\tnew VertexElement(0, VertexElementFormat.Short2,\r\n\t\t\t\t\t\t\t\t VertexElementUsage.Position, 0),\r\n\r\n\t\t\tnew VertexElement(4, VertexElementFormat.Vector3,\r\n\t\t\t\t\t\t\t\t VertexElementUsage.Position, 1),\r\n\r\n\t\t\tnew VertexElement(16, VertexElementFormat.Vector3,\r\n\t\t\t\t\t\t\t\t  VertexElementUsage.Normal, 0),\r\n\r\n\t\t\tnew VertexElement(28, VertexElementFormat.Color,\r\n\t\t\t\t\t\t\t\t  VertexElementUsage.Color, 0),\r\n\r\n\t\t\tnew VertexElement(32, VertexElementFormat.Single,\r\n\t\t\t\t\t\t\t\t  VertexElementUsage.TextureCoordinate, 0)\r\n\t\t);\r\n\r\n\r\n        // Describe the size of this vertex structure.\r\n        public const int SizeInBytes = 36;\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\r\nusing System.Runtime.CompilerServices;\r\nusing System.Runtime.InteropServices;\r\n\r\n// General Information about an assembly is controlled through the following \r\n// set of attributes. Change these attribute values to modify the information\r\n// associated with an assembly.\r\n[assembly: AssemblyTitle(\"Engine\")]\r\n[assembly: AssemblyProduct(\"Engine\")]\r\n[assembly: AssemblyConfiguration(\"\")]\r\n[assembly: AssemblyDescription(\"\")]\r\n[assembly: AssemblyCompany(\"\")]\r\n[assembly: AssemblyCopyright(\"Copyright ©  2013\")]\r\n[assembly: AssemblyTrademark(\"\")]\r\n[assembly: AssemblyCulture(\"\")]\r\n\r\n// Setting ComVisible to false makes the types in this assembly not visible \r\n// to COM components.  If you need to access a type in this assembly from \r\n// COM, set the ComVisible attribute to true on that type.\r\n[assembly: ComVisible(false)]\r\n\r\n// The following GUID is for the ID of the typelib if this project is exposed to COM\r\n[assembly: Guid(\"45cbce85-4b7b-4ec4-b30b-8489311f3329\")]\r\n\r\n// Version information for an assembly consists of the following four values:\r\n//\r\n//      Major Version\r\n//      Minor Version \r\n//      Build Number\r\n//      Revision\r\n//\r\n// You can specify all the values or you can default the Build and Revision Numbers \r\n// by using the '*' as shown below:\r\n// [assembly: AssemblyVersion(\"1.0.*\")]\r\n[assembly: AssemblyVersion(\"1.0.0.0\")]\r\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\r\n"
  },
  {
    "path": "Engine/ScreenEffects.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace GameEngine\r\n{\r\n    public class ScreenEffects : IDrawableObject\r\n    {\r\n        public event EventHandler FadeCompleted;\r\n\r\n        private enum FadeDirection\r\n        {\r\n            None,\r\n            FadeIn,\r\n            FadeOut\r\n        }\r\n\r\n        private static ScreenEffects _instance;\r\n        public static ScreenEffects Instance\r\n        {\r\n            get\r\n            {\r\n                if (_instance == null)\r\n                    _instance = new ScreenEffects();\r\n                return _instance;\r\n            }\r\n        }\r\n\r\n        private float _alpha;\r\n        private FadeDirection _fadeDirection;\r\n        private Texture2D _fadeTexture;\r\n        public float FadeSpeed = 350;\r\n\r\n        private ScreenEffects()\r\n        {\r\n            _fadeDirection = FadeDirection.None;\r\n\r\n            _fadeTexture = new Texture2D(Engine.Instance.Device, 1, 1);\r\n            _fadeTexture.SetData<Color>(new Color[] { Color.Black });\r\n        }\r\n\r\n        /// <summary>\r\n        /// Helper draws a translucent black fullscreen sprite, used for fading\r\n        /// screens in and out, and for darkening the background behind popups.\r\n        /// </summary>\r\n        public void FadeScreen()\r\n        {\r\n            _alpha = 0;\r\n            _fadeDirection = FadeDirection.FadeOut;\r\n        }\r\n\r\n        public void UnFadeScreen()\r\n        {\r\n            _alpha = 255;\r\n            _fadeDirection = FadeDirection.FadeIn;\r\n        }\r\n\r\n        #region IDrawableObject Members\r\n\r\n        public void Update(GameTime gameTime)\r\n        {\r\n            if (_fadeDirection == FadeDirection.FadeOut)\r\n            {\r\n                _alpha += FadeSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;\r\n                if (_alpha >= 255)\r\n                    CompleteFade();\r\n            }\r\n            else if (_fadeDirection == FadeDirection.FadeIn)\r\n            {\r\n                _alpha -= FadeSpeed * 0.5f * (float)gameTime.ElapsedGameTime.TotalSeconds;\r\n                if (_alpha <= 0)\r\n                    CompleteFade();\r\n            }\r\n\r\n        }\r\n\r\n        public void Draw()\r\n        {\r\n            if (_fadeDirection != FadeDirection.None)\r\n            {\r\n                Viewport viewport = Engine.Instance.Device.Viewport;\r\n\r\n                Engine.Instance.SpriteBatch.Begin();\r\n\t\t\t\t\t\r\n                Engine.Instance.SpriteBatch.Draw(_fadeTexture,\r\n                                 new Rectangle(0, 0, viewport.Width, viewport.Height),\r\n                                 new Color(255, 255, 255, (byte)_alpha));\r\n\r\n                Engine.Instance.SpriteBatch.End();\r\n            }\r\n        }\r\n\r\n        private void CompleteFade()\r\n        {\r\n            _fadeDirection = FadeDirection.None;\r\n            if (FadeCompleted != null)\r\n            {\r\n                FadeCompleted(this, null);\r\n            }\r\n        }\r\n\r\n        #endregion\r\n\r\n        public static Texture2D TakeScreenshot()\r\n        {\r\n            int w = Engine.Instance.Device.PresentationParameters.BackBufferWidth;\r\n            int h = Engine.Instance.Device.PresentationParameters.BackBufferHeight;\r\n\r\n\t\t\tint[] backBuffer = new int[w * h];\r\n\t\t\t\r\n\t\t\t//copy into a texture \r\n\t\t\tTexture2D texture = new Texture2D(Engine.Instance.Device, w, h, false, Engine.Instance.Device.PresentationParameters.BackBufferFormat);\r\n\t\t\ttexture.SetData(backBuffer);\r\n\r\n            return texture;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/SimpleCamera.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\n\r\nnamespace GameEngine\r\n{\r\n\r\n\tpublic class SimpleCamera : ICamera\r\n\t{\r\n\r\n\t\tpublic SimpleCamera()\r\n\t\t{\r\n\t\t}\r\n\r\n        public Vector3 RightVec = Vector3.Right;\r\n        public Vector3 UpVector = Vector3.Up;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Look at point in world space.\r\n\t\t/// </summary>\r\n\t\tpublic Vector3 LookAt\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\treturn lookAt;\r\n\t\t\t}\r\n\t\t\tset\r\n\t\t\t{\r\n\t\t\t\tlookAt = value;\r\n\t\t\t}\r\n\t\t}\r\n\t\tprivate Vector3 lookAt;\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Position of camera in world space.\r\n\t\t/// </summary>\r\n\t\tpublic Vector3 Position\r\n\t\t{\r\n\t\t\tget { return _position; }\r\n\t\t\tset { _position = value; }\r\n\t\t}\r\n\t\tprivate Vector3 _position;\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Perspective field of view.\r\n\t\t/// </summary>\r\n\t\tpublic float FieldOfView\r\n\t\t{\r\n\t\t\tget { return fieldOfView; }\r\n\t\t\tset { fieldOfView = value; }\r\n\t\t}\r\n\t\tprivate float fieldOfView = MathHelper.ToRadians(45.0f);\r\n\r\n\t\t/// <summary>\r\n\t\t/// Distance to the near clipping plane.\r\n\t\t/// </summary>\r\n\t\tpublic float NearPlaneDistance\r\n\t\t{\r\n\t\t\tget { return nearPlaneDistance; }\r\n\t\t\tset { nearPlaneDistance = value; }\r\n\t\t}\r\n\t\tprivate float nearPlaneDistance = 1.0f;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Distance to the far clipping plane.\r\n\t\t/// </summary>\r\n\t\tpublic float FarPlaneDistance\r\n\t\t{\r\n\t\t\tget { return farPlaneDistance; }\r\n\t\t\tset { farPlaneDistance = value; }\r\n\t\t}\r\n        private float farPlaneDistance = 15000.0f;\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// View transform matrix.\r\n\t\t/// </summary>\r\n\t\tpublic Matrix View\r\n\t\t{\r\n\t\t\tget { return view; }\r\n\t\t}\r\n\t\tprivate Matrix view;\r\n\r\n\t\t/// <summary>\r\n\t\t/// Projecton transform matrix.\r\n\t\t/// </summary>\r\n\t\tpublic Matrix Projection\r\n\t\t{\r\n\t\t\tget { return projection; }\r\n\t\t}\r\n\t\tprivate Matrix projection;\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// Animates the camera from its current position towards the desired offset\r\n\t\t/// behind the chased object. The camera's animation is controlled by a simple\r\n\t\t/// physical spring attached to the camera and anchored to the desired position.\r\n\t\t/// </summary>\r\n\t\tpublic void Update(GameTime gameTime)\r\n\t\t{\r\n            view = Matrix.CreateLookAt(this.Position, this.LookAt, UpVector);\r\n\t\t\tprojection = Matrix.CreatePerspectiveFieldOfView(FieldOfView,\r\n\t\t\t\tEngine.Instance.AspectRatio, NearPlaneDistance, FarPlaneDistance);\r\n\t\t}\r\n\r\n\t\tpublic void SetPosition(Vector3 position)\r\n\t\t{\r\n\t\t\t_position = position;\r\n\t\t}\r\n\r\n\t\tpublic void FollowObject(GameObject obj)\r\n\t\t{\r\n\t\t} \r\n\t}\r\n}\r\n"
  },
  {
    "path": "Engine/SkyBox.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Content;\r\nusing GameEngine;\r\nusing System.IO;\r\n\r\nnamespace GameEngine\r\n{\r\n    public class SkyBox : IDrawableObject\r\n    {\r\n\r\n        Texture2D[] textures = new Texture2D[6];\r\n\r\n        public Texture2D[] Textures\r\n        {\r\n            get { return textures; }\r\n            set { textures = value; }\r\n        }\r\n\t\tAlphaTestEffect _effect;\r\n\r\n        VertexBuffer vertices;\r\n        IndexBuffer indices;\r\n\r\n\t\tfloat YOffset = 15f;\r\n\r\n        public SkyBox()\r\n        {\r\n            LoadResources();\r\n        }\r\n\r\n        \r\n        public void LoadResources()\r\n        {\r\n\t\t\t_effect = new AlphaTestEffect(Engine.Instance.Device);\r\n           \r\n            vertices = new VertexBuffer(Engine.Instance.Device,\r\n                                typeof(VertexPositionTexture),\r\n                                4 * 6,\r\n                                BufferUsage.WriteOnly);\r\n\r\n            VertexPositionTexture[] data = new VertexPositionTexture[4 * 6];\r\n\r\n            float y = 0.3f;\r\n\r\n            #region Define Vertexes\r\n            Vector3 vExtents = new Vector3(1400, 450, 800);\r\n            //back\r\n            data[0].Position = new Vector3(vExtents.X, -vExtents.Y * y, -vExtents.Z);\r\n            data[0].TextureCoordinate.X = 1.5f; data[0].TextureCoordinate.Y = 1.0f;\r\n            data[1].Position = new Vector3(vExtents.X, vExtents.Y, -vExtents.Z);\r\n            data[1].TextureCoordinate.X = 1.5f; data[1].TextureCoordinate.Y = 0.0f;\r\n            data[2].Position = new Vector3(-vExtents.X, vExtents.Y, -vExtents.Z);\r\n            data[2].TextureCoordinate.X = 0.5f; data[2].TextureCoordinate.Y = 0.0f;\r\n            data[3].Position = new Vector3(-vExtents.X, -vExtents.Y * y, -vExtents.Z);\r\n            data[3].TextureCoordinate.X = 0.5f; data[3].TextureCoordinate.Y = 1.0f;\r\n\r\n            //front\r\n            data[4].Position = new Vector3(-vExtents.X, -vExtents.Y * y, vExtents.Z);\r\n            data[4].TextureCoordinate.X = 1.5f; data[4].TextureCoordinate.Y = 1.0f;\r\n            data[5].Position = new Vector3(-vExtents.X, vExtents.Y, vExtents.Z);\r\n            data[5].TextureCoordinate.X = 1.5f; data[5].TextureCoordinate.Y = 0.0f;\r\n            data[6].Position = new Vector3(vExtents.X, vExtents.Y, vExtents.Z);\r\n            data[6].TextureCoordinate.X = 0.5f; data[6].TextureCoordinate.Y = 0.0f;\r\n            data[7].Position = new Vector3(vExtents.X, -vExtents.Y * y, vExtents.Z);\r\n            data[7].TextureCoordinate.X = 0.5f; data[7].TextureCoordinate.Y = 1.0f;\r\n\r\n            //bottom\r\n            data[8].Position = new Vector3(-vExtents.X, -vExtents.Y * y, -vExtents.Z);\r\n            data[8].TextureCoordinate.X = 1.5f; data[8].TextureCoordinate.Y = 0.0f;\r\n            data[9].Position = new Vector3(-vExtents.X, -vExtents.Y * y, vExtents.Z);\r\n            data[9].TextureCoordinate.X = 1.5f; data[9].TextureCoordinate.Y = 1.0f;\r\n            data[10].Position = new Vector3(vExtents.X, -vExtents.Y * y, vExtents.Z);\r\n            data[10].TextureCoordinate.X = 0.5f; data[10].TextureCoordinate.Y = 1.0f;\r\n            data[11].Position = new Vector3(vExtents.X, -vExtents.Y * y, -vExtents.Z);\r\n            data[11].TextureCoordinate.X = 0.5f; data[11].TextureCoordinate.Y = 0.0f;\r\n\r\n            //top\r\n            data[12].Position = new Vector3(vExtents.X, vExtents.Y, -vExtents.Z);\r\n            data[12].TextureCoordinate.X = 0.5f; data[12].TextureCoordinate.Y = 0.0f;\r\n            data[13].Position = new Vector3(vExtents.X, vExtents.Y, vExtents.Z);\r\n            data[13].TextureCoordinate.X = 0.5f; data[13].TextureCoordinate.Y = 1.0f;\r\n            data[14].Position = new Vector3(-vExtents.X, vExtents.Y, vExtents.Z);\r\n            data[14].TextureCoordinate.X = 1.5f; data[14].TextureCoordinate.Y = 1.0f;\r\n            data[15].Position = new Vector3(-vExtents.X, vExtents.Y, -vExtents.Z);\r\n            data[15].TextureCoordinate.X = 1.5f; data[15].TextureCoordinate.Y = 0.0f;\r\n\r\n\r\n            //left\r\n            data[16].Position = new Vector3(-vExtents.X, vExtents.Y, -vExtents.Z);\r\n            data[16].TextureCoordinate.X = 1.5f; data[16].TextureCoordinate.Y = 0.0f;\r\n            data[17].Position = new Vector3(-vExtents.X, vExtents.Y, vExtents.Z);\r\n            data[17].TextureCoordinate.X = 0.5f; data[17].TextureCoordinate.Y = 0.0f;\r\n            data[18].Position = new Vector3(-vExtents.X, -vExtents.Y * y, vExtents.Z);\r\n            data[18].TextureCoordinate.X = 0.5f; data[18].TextureCoordinate.Y = 1.0f;\r\n            data[19].Position = new Vector3(-vExtents.X, -vExtents.Y * y, -vExtents.Z);\r\n            data[19].TextureCoordinate.X = 1.5f; data[19].TextureCoordinate.Y = 1.0f;\r\n\r\n            //right\r\n            data[20].Position = new Vector3(vExtents.X, -vExtents.Y * y, -vExtents.Z);\r\n            data[20].TextureCoordinate.X = 0.5f; data[20].TextureCoordinate.Y = 1.0f;\r\n            data[21].Position = new Vector3(vExtents.X, -vExtents.Y * y, vExtents.Z);\r\n            data[21].TextureCoordinate.X = 1.5f; data[21].TextureCoordinate.Y = 1.0f;\r\n            data[22].Position = new Vector3(vExtents.X, vExtents.Y, vExtents.Z);\r\n            data[22].TextureCoordinate.X = 1.5f; data[22].TextureCoordinate.Y = 0.0f;\r\n            data[23].Position = new Vector3(vExtents.X, vExtents.Y, -vExtents.Z);\r\n            data[23].TextureCoordinate.X = 0.5f; data[23].TextureCoordinate.Y = 0.0f;\r\n\r\n            vertices.SetData<VertexPositionTexture>(data);\r\n\r\n\r\n            indices = new IndexBuffer(Engine.Instance.Device,\r\n                                typeof(short), 6 * 6,\r\n                                BufferUsage.WriteOnly);\r\n\r\n            short[] ib = new short[6 * 6];\r\n\r\n            for (int x = 0; x < 6; x++)\r\n            {\r\n                ib[x * 6 + 0] = (short)(x * 4 + 0);\r\n                ib[x * 6 + 2] = (short)(x * 4 + 1);\r\n                ib[x * 6 + 1] = (short)(x * 4 + 2);\r\n\r\n                ib[x * 6 + 3] = (short)(x * 4 + 2);\r\n                ib[x * 6 + 5] = (short)(x * 4 + 3);\r\n                ib[x * 6 + 4] = (short)(x * 4 + 0);\r\n            }\r\n\r\n            indices.SetData<short>(ib);\r\n            #endregion\r\n\r\n        }\r\n\r\n        public void Update(GameTime gameTime)\r\n        {\r\n\t\t\tvar pos = Engine.Instance.Camera.Position;\r\n\t\t\tpos.Y += YOffset;\r\n\t\t\t_effect.World = Matrix.CreateTranslation(pos);\r\n            _effect.Projection = Engine.Instance.Camera.Projection;\r\n            _effect.View = Engine.Instance.Camera.View;\r\n        }\r\n\r\n\r\n        public void Draw()\r\n        {\r\n            if (vertices == null)\r\n                return;\r\n\r\n\t\t\tGraphicsDevice device = Engine.Instance.Device;\r\n\r\n\t\t\tvar ds = DepthStencilState.None;\r\n\t\t\t\r\n\t\t\tdevice.DepthStencilState = ds;\r\n\t\t\tdevice.SamplerStates[0] = SamplerState.LinearWrap;\r\n\t\t\tdevice.RasterizerState = RasterizerState.CullCounterClockwise;\r\n\t\t\tdevice.SetVertexBuffer(vertices);\r\n\t\t\tdevice.Indices = indices;\r\n\t\t\t_effect.CurrentTechnique.Passes[0].Apply();\r\n\r\n\t\t\tfor (int x = 0; x < 6; x++)\r\n\t\t\t{\r\n\t\t\t\tif (textures[x] == null) continue;\r\n\r\n\t\t\t\t_effect.Texture = textures[x];\r\n\t\t\t\t_effect.Techniques[0].Passes[0].Apply();\r\n\t\t\t\tdevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,\r\n\t\t\t\t\t0, 0, vertices.VertexCount, x * 6, 2);\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tdevice.DepthStencilState = DepthStencilState.Default;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/SoundEngine2.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Audio;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace GameEngine\r\n{\r\n    class SoundEffectDescriptor \r\n    {\r\n        public SoundEffectInstance Effect;\r\n        public float RemainingDuration;\r\n    }\r\n\r\n    public class SoundEngine2\r\n    {\r\n        private static SoundEngine2 _instance;\r\n\r\n        public static SoundEngine2 Instance\r\n        {\r\n            get\r\n            {\r\n                if (_instance == null)\r\n                    _instance = new SoundEngine2();\r\n                return _instance;\r\n            }\r\n        }\r\n\r\n        private List<SoundEffectDescriptor> _effects = new List<SoundEffectDescriptor>(); \r\n\r\n        private SoundEngine2()\r\n        {\r\n        }\r\n\r\n        public void PlayEffect(SoundEffectInstance effect, float duration)\r\n        {\r\n            SoundEffectDescriptor sd = new SoundEffectDescriptor();\r\n            sd.Effect = effect;\r\n            sd.RemainingDuration = duration;\r\n            _effects.Add(sd);\r\n        }\r\n\r\n        public void Update(GameTime gameTime)\r\n        {\r\n            for (int i = _effects.Count - 1; i >= 0; i--)\r\n            {\r\n                _effects[i].RemainingDuration -= (float)gameTime.ElapsedGameTime.TotalSeconds;\r\n                if (_effects[i].RemainingDuration <= 0)\r\n                {\r\n                    _effects[i].Effect.Stop();\r\n                    _effects.RemoveAt(i);\r\n                }\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "Engine/Utility.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\n\r\nnamespace GameEngine\r\n{\r\n    public class Triangle\r\n    {\r\n        public Vector3 V1, V2, V3;\r\n        public Triangle(Vector3 v1, Vector3 v2, Vector3 v3)\r\n        {\r\n            V1 = v1;\r\n            V2 = v2;\r\n            V3 = v3;\r\n        }\r\n\r\n    }\r\n    public class Utility\r\n    {\r\n        public static float Epsilon = 1E-5f; //for numerical imprecision\r\n        public static Random RandomGenerator = new Random();\r\n\r\n        public static bool FindRayTriangleIntersection(ref Vector3 rayOrigin, Vector3 rayDirection, float maximumLength, ref Vector3 a, ref Vector3 b, ref Vector3 c, out Vector3 hitLocation, out float t)\r\n        {\r\n            hitLocation = new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);\r\n            \r\n            t = float.NegativeInfinity;\r\n            Vector3 vector = b - a;\r\n            Vector3 vector2 = c - a;\r\n            Vector3 vector4 = Vector3.Cross(rayDirection, vector2);\r\n            float num = Vector3.Dot(vector, vector4);\r\n            if ((num > -1E-07f) && (num < 1E-07f))\r\n            {\r\n                return false;\r\n            }\r\n            float num2 = 1f / num;\r\n            Vector3 vector3 = rayOrigin - a;\r\n            float num3 = Vector3.Dot(vector3, vector4) * num2;\r\n            if ((num3 < -0.01f) || (num3 > 1.01f))\r\n            {\r\n                return false;\r\n            }\r\n            Vector3 vector5 = Vector3.Cross(vector3, vector);\r\n            float num4 = Vector3.Dot(rayDirection, vector5) * num2;\r\n            if ((num4 < -0.01f) || ((num3 + num4) > 1.01f))\r\n            {\r\n                return false;\r\n            }\r\n            t = Vector3.Dot(vector2, vector5) * num2;\r\n            if ((t > maximumLength) || (t < 0f))\r\n            {\r\n                t = float.NegativeInfinity;\r\n                return false;\r\n            }\r\n            hitLocation = rayOrigin + (t * rayDirection);\r\n            return true;\r\n        }\r\n        \r\n\r\n        public static Vector3 RotatePoint(Vector2 point, float degrees)\r\n        {\r\n            float cDegrees = ((float)Math.PI * degrees) / 180.0f; //Radians\r\n            float cosDegrees = (float)Math.Cos(cDegrees);\r\n            float sinDegrees = (float)Math.Sin(cDegrees);\r\n\r\n            float x = (point.X * cosDegrees) + (point.Y * sinDegrees);\r\n            float z = (point.X * -sinDegrees) + (point.Y * cosDegrees);\r\n            return new Vector3(x, 0, -z);\r\n        }\r\n\r\n\t\tpublic static bool IsLeftOfLine(Vector3 line1, Vector3 line2, Vector3 pos)\r\n\t\t{\r\n\t\t\treturn ((line2.X - line1.X) * (pos.Z - line1.Z) - (line2.Z - line1.Z) * (pos.X - line1.X)) > 0;\r\n\t\t}\r\n\r\n\t\tpublic static Vector3 GetClosestPointOnLine(Vector3 line1, Vector3 line2, Vector3 pos)\r\n\t\t{\r\n\t\t\tVector3 lineDir = line2 - line1;\r\n\t\t\tlineDir.Normalize();\r\n\r\n\t\t\tfloat d = Vector3.Distance(line1, line2);\r\n\r\n\t\t\tVector3 v1 = pos - line1;\r\n\t\t\tfloat t = Vector3.Dot(lineDir, v1);\r\n\r\n\t\t\tif (t <= 0)\r\n\t\t\t\treturn line1;\r\n\t\t\tif (t >= d)\r\n\t\t\t\treturn line2;\r\n\r\n\t\t\treturn line1 + lineDir * t;\r\n\t\t}\r\n\r\n\t\tpublic static float GetSignedAngleBetweenVectors(Vector3 from, Vector3 to, bool ignoreY)\r\n\t\t{\r\n\t\t\tif (ignoreY) from.Y = to.Y = 0;\r\n\t\t\tfrom.Normalize();\r\n\t\t\tto.Normalize();\r\n\t\t\tVector3 toRight = Vector3.Cross(to, Vector3.Up);\r\n\t\t\ttoRight.Normalize();\r\n\r\n\t\t\tfloat forwardDot = Vector3.Dot(from, to);\r\n\t\t\tfloat rightDot = Vector3.Dot(from, toRight);\r\n\r\n\t\t\t// Keep dot in range to prevent rounding errors\r\n\t\t\tforwardDot = MathHelper.Clamp(forwardDot, -1.0f, 1.0f);\r\n\r\n\t\t\tdouble angleBetween = Math.Acos(forwardDot);\r\n\r\n\t\t\tif (rightDot < 0.0f)\r\n\t\t\t\tangleBetween *= -1.0f;\r\n\r\n\t\t\treturn (float)angleBetween;\r\n\t\t}\r\n    }\r\n}"
  },
  {
    "path": "Installer/OpenNFS1.nsi",
    "content": "!include LogicLib.nsh\n\n; The name of the installer\nName \"OpenNFS1\"\n\n; The file to write\nOutFile \"OpenNFS1_Install-v1.2.exe\"\n\n; The default installation directory\nInstallDir $PROGRAMFILES\\OpenNFS1\n\n; Registry key to check for directory (so if you install again, it will \n; overwrite the old one automatically)\nInstallDirRegKey HKLM \"Software\\OpenNFS1\" \"Install_Dir\"\n\n; Request application privileges for Windows Vista\nRequestExecutionLevel admin\n\n;--------------------------------\n\n; Pages\n\nPage components\nPage directory\nPage instfiles\n\nUninstPage uninstConfirm\nUninstPage instfiles\n\n;--------------------------------\n\n; The stuff to install\nSection \"OpenNFS1 (required)\"\n\n  SectionIn RO\n    \n  ; Set output path to the installation directory.\n  SetOutPath $INSTDIR\n  \n  ; Include all files in the deploy folder\n  File /r \"deploy\\*.*\"\n  File /r \"3rdparty\"\n  File \"readme.txt\"\n  \n  Call CheckAndInstallDotNet\n  Call InstallOAL\n  \n  ; Write the installation path into the registry\n  WriteRegStr HKLM SOFTWARE\\OpenNFS1 \"Install_Dir\" \"$INSTDIR\"\n  \n  ; Write the uninstall keys for Windows\n  WriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenNFS1\" \"DisplayName\" \"OpenNFS1\"\n  WriteRegStr HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenNFS1\" \"UninstallString\" '\"$INSTDIR\\uninstall.exe\"'\n  WriteRegDWORD HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenNFS1\" \"NoModify\" 1\n  WriteRegDWORD HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenNFS1\" \"NoRepair\" 1\n  WriteUninstaller \"uninstall.exe\"\n  \nSectionEnd\n\n; Optional section (can be disabled by the user)\nSection \"Start Menu Shortcuts\"\n\n  CreateDirectory \"$SMPROGRAMS\\OpenNFS1\"\n  CreateShortcut \"$SMPROGRAMS\\OpenNFS1\\Uninstall.lnk\" \"$INSTDIR\\uninstall.exe\" \"\" \"$INSTDIR\\uninstall.exe\" 0\n  CreateShortcut \"$SMPROGRAMS\\OpenNFS1\\OpenNFS1.lnk\" \"$INSTDIR\\OpenNFS1.exe\" \"\" \"$INSTDIR\\OpenNFS1.exe\" 0\n  \nSectionEnd\n\n;--------------------------------\n\n; Uninstaller\n\nSection \"Uninstall\"\n  \n  ; Remove registry keys\n  DeleteRegKey HKLM \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenNFS1\"\n  DeleteRegKey HKLM SOFTWARE\\OpenNFS1\n  \n  ; Remove shortcuts\n  Delete \"$SMPROGRAMS\\OpenNFS1\\*.*\"\n\n  ; Remove directories used\n  RMDir \"$SMPROGRAMS\\OpenNFS1\"\n  RMDir /R \"$INSTDIR\"\n\nSectionEnd\n\nFunction CheckAndInstallDotNet\n    ; Installer dotNetFx45_Full_setup.exe avalible from http://msdn.microsoft.com/en-us/library/5a4x27ek.aspx\n    ; Magic numbers from http://msdn.microsoft.com/en-us/library/ee942965.aspx\n    ClearErrors\n    ReadRegDWORD $0 HKLM \"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full\" \"Release\"\n    IfErrors NotDetected\n    ${If} $0 >= 978389\n        DetailPrint \"Microsoft .NET Framework 4.5 is installed ($0)\"\n    ${Else}\n    NotDetected:\n        MessageBox MB_YESNO|MB_ICONQUESTION \".NET Framework 4.5+ is required, \\\n            do you want to launch the web installer? This requires a valid internet connection.\" IDYES InstallDotNet IDNO Cancel \n        Cancel:\n            MessageBox MB_ICONEXCLAMATION \"To install, Microsoft's .NET Framework v4.5 \\\n                (or higher) must be installed. Cannot proceed with the installation!\"\n            # ${OpenURL} \"${WWW_MS_DOTNET4_5}\"\n            RMDir /r \"$INSTDIR\" \n            SetOutPath \"$PROGRAMFILES\"\n            RMDir \"$INSTDIR\" \n            Abort\n\n        ; Install .NET4.5.\n        InstallDotNet:\n            DetailPrint \"Installing Microsoft .NET Framework 4.5\"\n            SetDetailsPrint listonly\n            ExecWait '\"$INSTDIR\\3rdparty\\dotNetFx45_Full_setup.exe\" /passive /norestart' $0\n            ${If} $0 == 3010 \n            ${OrIf} $0 == 1641\n                DetailPrint \"Microsoft .NET Framework 4.5 installer requested reboot.\"\n                SetRebootFlag true \n            ${EndIf}\n            SetDetailsPrint lastused\n            DetailPrint \"Microsoft .NET Framework 4.5 installer returned $0\"\n    ${EndIf}\n\nFunctionEnd\n\n\nFunction InstallOAL\n\tExecWait '\"$INSTDIR\\3rdparty\\oalinst.exe\" /s' $0\nFunctionEnd"
  },
  {
    "path": "Installer/build-installer.bat",
    "content": "rmdir /S /Q deploy\r\nmkdir deploy\r\nset src=..\\OpenNFS1\\bin\\WindowsGL\\Debug\r\nset dest=.\\deploy\r\nxcopy %src%\\Content %dest%\\Content\\ /s /e\r\ncopy %src%\\gameconfig.json %dest%\r\ncopy %src%\\OpenNFS1.exe %dest%\r\ncopy %src%\\*.dll %dest%\r\ncopy %src%\\*.pdb %dest%\r\ndel %dest%\\IgnoreMe.dll\r\n\r\n\"C:\\Program Files (x86)\\NSIS\\makensis\" /V1 OpenNFS1.nsi"
  },
  {
    "path": "Installer/readme.txt",
    "content": "OpenNFS1 v1.2\r\n----------------\r\nWeb: http://www.1amstudios.com/projects/opennfs1\r\nSrc: https://github.com/jeff-1amstudios/OpenNFS1\r\nEmail: jeff@1amstudios.com\r\n\r\n\r\nOpenNFS1 is a ground-up remake of the original EA Need for Speed 1. The code is all written from scratch without reverse engineering executables, and it uses the original data files that were on the CD back in 1995! The format of the various binary data files was worked out by Ian Brown, Denis Auroux and myself.\r\n\r\n\r\nMain features:\r\n\r\nWritten in C# and XNA. (Now converted to Monogame)\r\n16 tracks with scenery, animations, tunnels\r\n9 drivable cars with animated dashboards\r\nCan drive tracks backwards (not allowed in the original)\r\nCan drive past the finish signs on open road stages to see the real end of the track (probably never seen by anyone except by the original developers!)\r\n\r\n\r\nKeys:\r\n\r\nC - Change camera\r\nUp - Accelerate\r\nDown - Brake\r\nLeft / Right - Steer\r\nSpace - Handbrake\r\nA / Z - Gear up down in manual gearbox mode\r\nF1 - Toggle debug mode.  This enables an FPS camera which can be moved independantly of the car\r\n\r\n\r\n\r\nModels, textures, tracks, cars by Pioneer Productions / EA Seattle (C) 1995. OpenNFS1 is not affiated in any way with EA or Pioneer Productions"
  },
  {
    "path": "NFSSpecs.txt",
    "content": "======================================================================\r\nTHE UNOFFICIAL NEED FOR SPEED FILE FORMAT SPECIFICATIONS - Version 0.2\r\n  Copyright (c) 1995-96, Denis AUROUX (MXK) - auroux@clipper.ens.fr\r\n======================================================================\r\n\r\nLast updated : February 13, 1996\r\n\r\nThis file is available from the following URL :\r\n\r\n     http://www.eleves.ens.fr:8080/home/auroux/nfsspecs.txt\r\n\r\nDisclaimer : NO WARRANTY of any kind comes with this file.\r\n\r\n\r\nAdded in version 0.2 :\r\n- TRI files (section B.6) : all you need to write a track editor.\r\n\r\nA - GAMEDATA directory\r\n    ==================\r\n\r\nA.1 GAMEDATA\\CONFIG\\PATHS.DAT\r\n    -------------------------\r\n\r\nThis file contains 18 null-terminated strings of 80 characters each\r\n(total 1440 bytes), describing the paths where NFS looks for its files.\r\nFor minimum install, here are the contents (with hex offset):\r\n\r\n000 gamedata/config/              2D0 F:\\simdata\\misc\\\r\n050 gamedata/savegame/            320 F:\\simdata\\trackfam\\\r\n0A0 F:\\frontend\\speech\\           370 F:\\simdata\\slides\\\r\n0F0 F:\\simdata\\soundbnk\\          3C0 F:\\simdata\\carFams\\\r\n140 F:\\frontend\\music\\            410 F:\\simdata\\soundbnk\\\r\n190 F:\\frontend\\art\\              460 F:\\simdata\\carspecs\\\r\n1E0 gamedata/modem/               4B0 F:\\simdata\\dash\\\r\n230 F:\\frontend\\movielow\\         500 F:\\simdata\\misc\\\r\n280 gamedata/replay/              550 F:\\frontend\\show\\\r\n\r\nNote that simdata\\soundbnk and simdata\\misc are present twice.\r\nAlso note that in the CD-ROM, directories whose name starts with a 'G'\r\ncorrespond to the German version of the game. Don't know why the car\r\ndashboards had to be translated, though :)\r\n\r\nModify these entries if you want to copy some of the CD files to your hard\r\ndisk, for example if you want to modify them. Remember that modifying the\r\nfiles can be hazardous, and that because the file formats are not fully\r\nknown it is better to modify an existing file than to create one from scratch.\r\n\r\nA.2 GAMEDATA\\SAVEGAME\\*.SAV\r\n    -----------------------\r\n\r\nThese are the tournament save files (840 bytes each).\r\n\r\n- Offset 0 : your name (null-terminated string followed by zeros)\r\n- Offset 28 : 'Opponent'               (\")\r\n- Offset EC : 'Player 2'               (\")\r\n- Offset 114 : 'Opponent 2'            (\")\r\n- Offset 1D8 : name of the tournament  (\")\r\n- Offset 20A : 'REPLAY.RPL'            (\")\r\n- Offsets 23C-26F : ???\r\n- Offset 270 : name of the tournament  (\")\r\n- Offsets 30E-337 : ???\r\n- Offsets 338, 33A, 33C, 33E, 340, 342, 344 : these contain 01 if the\r\n  corresponding race has been won, otherwise 00. The first three bytes\r\n  are for open-road races, while the other correspond to closed tracks.\r\n  For instance, in order to access the bonus track, save a tournament,\r\n  then set all these bytes to 01 except 33E to 00 (Rusty Springs), then\r\n  reload the tournament and win Rusty Springs. The odd offsets 339 to 345\r\n  are zeros.\r\n- Offsets 346-347 : ???\r\n\r\nThe other offsets contains zeros.\r\n\r\nA.3 GAMEDATA\\CONFIG\\CONFIG.DAT\r\n    --------------------------\r\n\r\nThis file (25552 bytes) contains all the relevant configuration data\r\n(current car, track, display & sound options, ...), as well as the current\r\nscores (best times, top speeds, laps, ...). It also describes whether you\r\nhave access to the bonus track and the bonus car, etc...\r\nAnybody got anything more detailed to put here ?\r\n\r\n\r\nB - SIMDATA directory\r\n    =================\r\n\r\nB.1 SIMDATA\\CARSPECS\\*.PBS\r\n    ----------------------\r\n\r\nThese files (one per player car) describe the car performance.\r\nUsually, installing these files to the hard disk (by editing PATHS.DAT)\r\nmakes Need for Speed inoperable (no acceleration is possible) because the\r\ngame modifies SIMDATA\\CARSPECS\\BY_R&T. However this can be prevented by\r\nmaking the check file read-only (run ATTRIB +R BY_R&T).\r\n\r\nNote that copying one of these files on top of another one will affect the\r\ncar performance and handling, but not the engine sound. So if the max rpm's\r\nof the two engines are not the same, the sound can get badly altered because\r\nthe correct sound sample is not available.\r\n\r\nThese files are somehow compressed, because by modifying a single byte in\r\nthe file one can reach various results such as : an instant system crash ;\r\na car that materializes under the road and cannot move, with 11 gears (!) ;\r\na car without front wheels, that keeps spinning at its initial position...\r\n\r\nB.2 SIMDATA\\DASH\\*.FMM\r\n    ------------------\r\n\r\nThese small files (two per player car, corresponding to the hi-res and\r\nlow-res dashboards) describe the shape of the car's windshield in each\r\ndisplay resolution. The files are slightly different for 320x200 and 640x480.\r\n\r\na) Low-res .FMMs (*DL.FMM) :\r\n\r\nThe file structure is the following : first a 24-byte header, then four\r\nchunks.\r\n\r\nThe header format is the following :\r\n\r\noffset len data\r\n------ --- ----\r\n00      4  'wwww'\r\n04      4   4     (number of chunks)\r\n08      4  18h    (offset of the first chunk)\r\n0C      4  offset of the second chunk\r\n10      4  offset of the third chunk\r\n14      4  offset of the fourth chunk\r\n\r\nThe chunks in low-res .FMMs have the following header (22 bytes) :\r\n\r\noffset len data\r\n------ --- ----\r\n00      4  'BAMF'\r\n04      2  length of the chunk in bytes, minus two\r\n06      4  ? (usually equals 10000h or 8000h : maybe an image size)\r\n0A      2  maximum \"length\" value in the chunk's records\r\n0C      4  ? (usually equals 10000h or 8000h : maybe an image size)\r\n10      4  ? (usually equals 10000h or 8000h : maybe an image size)\r\n14      2  number of records in the chunk (length minus 18h, div 8)\r\n\r\nThis header is in each chunk immediately followed by the 8-byte records.\r\nEach record has the following structure :\r\n\r\noffset len data\r\n------ --- ----\r\n00      4  baddr\r\n04      2  vaddr\r\n06      2  length\r\n\r\nThe last record is followed by a 16-bit value (purpose unknown).\r\n\r\nThe vaddr and length values are interpreted as follows : starting from\r\naddress \"vaddr\" in video memory (i.e. of the form 320y+x for pixel (x,y)),\r\n\"length\" pixels do not belong to the dashboard, and must instead be drawn\r\nbecause they correspond to screen portions where the player must see the\r\nroad through the windshield.\r\nThe baddr field corresponds to an internal buffer offset, describing\r\nwhere the data to be displayed at \"vaddr\" are to be found in the buffer\r\nwhere NFS draws the view.\r\n\r\n- The first chunk corresponds to high detail : vaddr and baddr are equal,\r\nand each line corresponds to 320 bytes in the buffer (every pixel is stored).\r\n\r\n- The second chunk corresponds to medium detail : each line corresponds to\r\n320 bytes in the buffer, but only half of the pixels are stored, thus only\r\nthe first 160 bytes contain relevant data, the following 160 being unused.\r\nTo vaddr=320y+x corresponds baddr=320y+(x>>1) : the horizontal resolution\r\nis decreased.\r\n\r\n- The third chunk corresponds to low detail : two consecutive lines have the\r\nsame baddr values, and these two lines correspond to 320 bytes in the buffer.\r\nAs before, only half of these 320 bytes are used. Only a quarter of the\r\npixels are stored. vaddr=320y+x corresponds to baddr=320.(y>>1)+(x>>1) :\r\nboth horizontal and vertical resolutions are decreased.\r\n\r\n- The fourth chunk corresponds to the interlaced mode : only half of the\r\nscreen lines are referenced ; as in low detail, two screen lines correspond\r\nto 320 bytes, half of which are unused. vaddr=320y+x still corresponds to\r\nbaddr=320.(y>>1)+(x>>1), but only the even-numbered lines are present.\r\n\r\nb) Hi-res .FMMs (*DH.FMM) :\r\n\r\nThe chunk format is slightly different because the video memory is now\r\nsegmented in several 64K pages. (Note that the values at offsets 06, 0C and\r\n10 in the chunk header are now larger, e.g. 40000h and 10000h)\r\n\r\nOffset 14h in the chunk now contains, instead of the total number of records\r\nin the chunk, only the number of records that correspond to the first page\r\nof video memory (i.e. the first 64K). It is followed by the corresponding\r\nrecords.\r\nAfter these records, another 16-bit value describes the number of records\r\nthat are located in the second page of video memory (the following 64K);\r\nthe records for the second page follow (note that the 32-bit baddr values\r\nat last become useful ; only the 16 last bits of the vaddr are stored,\r\nsince it is known to be located in the second page).\r\nThe chunk continues with the following pages, until the number of records\r\nfor a given page is zero. This zero value is followed by a 16-bit value\r\n(purpose unknown) as in the low-res files.\r\n\r\nThe four chunks correspond respectively to high, medium, low and interlaced\r\ndetail levels, as described above (but the lines are now each 640 pixels\r\nlong ; when not in high resolution, only 320 of the 640 bytes are used).\r\n\r\nB.3 SIMDATA\\DASH\\*.FSH\r\n    ------------------\r\n\r\nThese store the different bitmaps that make up the dashboard.\r\nThe 16-byte header format is the following :\r\n\r\noffset len data\r\n------ --- ----\r\n00      4  'SHPI'\r\n04      4  length of the file in bytes\r\n08      4  number of objects in the directory\r\n0C      4  directory identifier string\r\n\r\nThe directory identifier is 'GIMX' in .FSH dashboard files.\r\n\r\nThis header is followed by the directory entries, each consisting of a\r\n4-byte identifier string, and a 4-byte offset inside the file pointing to\r\nthe beginning of the corresponding data.\r\n\r\nEach entry in the directory represents a piece of the dashboard.\r\nThere are gaps between the directory and the first bitmap, and between\r\nconsecutive bitmaps (significance unknown).\r\n\r\nEach directory entry points to a bitmap block with the following structure :\r\n\r\noffset len data\r\n------ --- ----\r\n00      1  7Bh\r\n01      3  size of the block (= width x length + 10h)\r\n04      2  width of the bitmap in pixels\r\n06      2  heigth of the bitmap in pixels\r\n08      4  ?\r\n0C      2  x position to display the bitmap\r\n0E      2  y position to display the bitmap\r\n10     w.h bitmap data : 1 byte per pixel\r\n\r\nNote that the object called 'dash' in the directory takes the whole screen\r\n(320x200 or 640x480, at position x=0, y=0).\"\r\n\r\nThe various objects, depending on their 4-letter identifier, represent :\r\nthe dashboard itself, the steering wheel in various positions, the radar\r\ndetector lights, the gauges, and also pieces of the steering wheel to redraw\r\nover the gauges when necessary. Note that value FF in the bitmaps stands for\r\nthe background : this is useful when a bitmap is drawn on top of another one.\r\n\r\nAlso note that some SHPI bitmap directories contain entries that actually\r\ndescribe the palette to be used with the bitmaps. Typically, entries with\r\nnames like '!PAL', and with bitmap dimension 256x3, correspond to palettes.\r\nThe palette data consists of 256 3-byte records, each record containing the\r\nred, green and blue components of the corresponding color (1 byte each).\r\n\r\nB.4 SIMDATA\\CARFAMS\\*.CFM\r\n    ---------------------\r\n\r\nThese files describe the exterior look of the cars during the race. There\r\nseem to be two different kinds of files :\r\n- full CFMs that can be used to describe any car\r\n- restricted CFMs that can only describe traffic or opponents - note that\r\n  yourself and your head-to-head opponent must have full CFMs, whereas\r\n  \"single race\" opponents can be either full or restricted CFMs.\r\n\r\na) Full CFMs on the CD :\r\n  Standard cars : ANSX.CFM, CZR1.CFM, DVIPER.CFM, F512TR.CFM, LDIABL.CFM,\r\n                  MRX7.CFM, P911.CFM, TSUPRA.CFM, TRAFFC.CFM (Warrior)\r\n  More or less bugged previous versions of these cars :\r\n    F512M.CFM, P911T.CFM, WARIOR.CFM, WARRIOR.CFM\r\n\r\nb) Restricted CFMs on the CD :\r\n  Vans/jeeps/pickups : JEEP.CFM, PICKUP.CFM, RODEO.CFM, VANDURA.CFM\r\n  Station-wagons : AXXESS.CFM, WAGON.CFM\r\n  Usual cars : BMW.CFM, JETTA.CFM, LEMANS.CFM, SUNBIRD.CFM\r\n  Sports cars : CRX.CFM, PROBE.CFM\r\n  The cop's car : COPMUST.CFM\r\n  Restricted versions of the player cars (loaded for single race opponents):\r\n      MANSX.CFM, MCZR1.CFM, MDVIPER.CFM, MF512TR.CFM, MLDIABL.CFM,\r\n      MMRX7.CFM, MP911.CFM, MTSUPRA.CFM, MTRAFFC.CFM\r\n  Bugged versions of the player cars :\r\n      LDIABLO.CFM, MF512M.CFM, MP911T.CFM, TESTA.CFM\r\n\r\nThese files are 'wwww' files containing four chunks. The header is as in\r\n.FMM files (see B.2). The first and second chunk correspond to high-detail\r\ncar structure, while the third and fourth chunk correspond to low-detail.\r\n\r\nThe first and third chunks have header 'ORIP', and describe how the car is\r\nto be drawn (position and orientation of each car element). The second\r\nand fourth chunks are 'SHPI' bitmap directories (see B.3), with directory\r\nidentifier 'WRAP'. They provide the bitmaps referenced by the ORIP chunks.\r\nNote that the offsets specified in the bitmap directories are relative to the\r\n'SHPI' header.\r\n\r\nAn 'ORIP' chunk consists of a header followed by nine blocks.\r\nThe header has length 112 (70h) and the following structure :\r\n\r\noffset len data\r\n------ --- ----\r\n00      4  'ORIP'\r\n04      4  chunk length in bytes\r\n08      4  2BCh (=700)\r\n0C      4  0\r\n10      4  size of block 8 in 12-byte records\r\n14      4  ? the previous minus 10h\r\n18      4  offset of block 8 in the ORIP (in bytes)\r\n1C      4  size of block 2 in 8-byte records\r\n20      4  offset of block 2\r\n24      4  size of block 1 in 12-byte records\r\n28      4  offset of block 1 (=70h)\r\n2C     12  identifier string\r\n38      4  size of block 3 in 20-byte records\r\n3C      4  offset of block 3\r\n40      4  size of block 4 in 20-byte records\r\n44      4  offset of block 4\r\n48      4  size of block 5 in 28-byte records\r\n4C      4  offset of block 5\r\n50      4  offset of block 9\r\n54      4  size of block 6 in 12-byte records\r\n58      4  offset of block 6\r\n5C      4  size of block 7 in 12-byte records\r\n60      4  offset of block 7\r\n64      4  0\r\n68      4  70h\r\n6C      4  0\r\n\r\nThe first block consists of 12-byte records and describes the polygons that\r\nare used to draw the car:\r\n\r\noffset len data\r\n------ --- ----\r\n00      1  83h for triangle, 84h for quadrangle, 8Ch when quadrangle/null\r\n01      1  ?\r\n02      1  texture number\r\n03      1  ?\r\n04      4  polygon offset for pol1\r\n08      4  polygon offset for pol2\r\n\r\nThe texture number corresponds to a record position in block 3, which is\r\nconverted to one of the textures in the SHPI chunk that follows the ORIP.\r\nThe polygon offsets correspond to positions in the last block, where the\r\npolygons are listed consecutively (first pol2 and then pol1, for each record).\r\nThe first polygon has pol2 starting at 0. (The polygon offsets are given\r\nin 4-byte units). In the last block, vertices belonging to a pol1 are\r\nexpanded to 3D points by lookup in block 8, while vertices belonging to a\r\npol2 are expanded to 2D points by lookup in block 2. The portion of the\r\nrelevant texture corresponding to pol2 is mapped onto the 3D polygon pol1.\r\n\r\nWhen the first byte is 83h, pol1=pol2+3 and next polygon's pol2 equals pol1+3.\r\nWhen it is 84h, pol1=pol2+4 and next pol2 equals pol1+4. When it is 8Ch,\r\npol1=pol2 and next pol2 equals pol1+4.\r\n\r\nThe second block is made of 8-byte records, which describe the (x,y)\r\ncoordinates inside the textures referenced by pol2.\r\n\r\noffset len data\r\n------ --- ----\r\n00      4  x coordinate\r\n04      4  y coordinate\r\n\r\nThird block (20-byte records):\r\n\r\noffset len data\r\n------ --- ----\r\n00      8  identifier string 1\r\n08      4  identifier string 2\r\n0C      8  ? 00 00 00 81 F5 00 00 00\r\n\r\nThis block describes the link between the texture numbers of block 1 and\r\nthe textures stored in the SHPI chunk. Identifier string 2, when non-void,\r\ncorresponds to one of the SHPI directory entries.\r\n\r\nFourth block (20-byte records):\r\n\r\noffset len data\r\n------ --- ----\r\n00      8  identifier string ('left_tur', 'right_tu')\r\n08      4  number of vertices\r\n0C      4  polygon offset (in block 9)\r\n10      4  ?\r\n\r\nFifth block (28-byte records):\r\n\r\noffset len data\r\n------ --- ----\r\n00     12  identifier string ('NON-SORT', 'inside', 'surface', 'outside')\r\n0C      4  ?\r\n10      4  polygon offset (in block 9)\r\n14      4  number of vertices\r\n18      4  0\r\n\r\nSixth and seventh blocks (12-byte records):\r\n\r\noffset len data\r\n------ --- ----\r\n00      8  identifier string\r\n08      4  ?\r\n\r\nThe eighth block consists of 12-byte records which describe the position in\r\nspace of each vertex (using signed long ints, centered at (0,0,0)).\r\n\r\noffset len data\r\n------ --- ----\r\n00      4  x coord. (x axis is lateral)\r\n04      4  z coord. (z axis is vertical, z increases when going up)\r\n08      4  y coord. (y axis is longitudinal, increases when going forward)\r\n\r\nThe last block is a succession of long integers, each representing a vertex\r\nnumber. This block describes the vertices each polygon consists of, and is\r\nreferenced by the index tables of the first, fourth and fifth blocks. Each\r\nvertex number referenced in a pol1 polygon is converted to a point in space\r\nusing block 8, while each vertex number referenced in a pol2 polygon is\r\nconverted to a position in a texture bitmap using block 2.\r\n\r\nB.5 SIMDATA\\MISC\\*.QFS, SIMDATA\\SLIDES\\*.QFS, ...\r\n    ---------------------------------------------\r\n\r\nThis seems to be EAC's favorite bitmap format... Basically, it is similar\r\nto the .FSH files described in B.3, however the data are now compressed\r\nusing some kind of Huffman algorithm. Outside of this compression thing,\r\nthe file still contains a 'SHPI' header, and a directory. The directory\r\nitself is much shorter than in the dashboard FSH's : usually one entry\r\n(the bitmap itself), sometimes two (when a palette is specified).\r\nThe big trouble is in finding the details of the decompression algorithm.\r\n\r\nThe directory header is 'LN32', indicating 32768-color mode. (see C.2).\r\n\r\nB.6 SIMDATA\\MISC\\*.TRI\r\n    ------------------\r\n\r\nDon't know why these are in the MISC directory... they are quite important\r\nindeed ! They describe the track itself, including the shape of the road\r\nand the position of the scenery items. The objects are referenced by numbers\r\nwhich correspond to entries in the corresponding file in SIMDATA\\TRACKFAM.\r\n\r\nTheir structure is fairly complex, so I recommend you play with the files\r\nin order to get acquainted with their subtleties. The information contained\r\nin this section was used to develop the first NFS track editor, called\r\nTRACKED, and available at the following URL :\r\n\r\n         http://www.eleves.ens.fr:8080/home/auroux/nfs/tracked.zip\r\n\r\nIn order to understand the structure of TRI files you have to know that a\r\ntrack is the superposition of three structures :\r\n\r\n- first, a 'virtual road' : this is a sequence of points in space,\r\n  which will be called 'nodes'. These points correspond to successive\r\n  positions along the track, and all the cars have to pass near each of\r\n  these positions. During the game, the virtual road is invisible, but\r\n  you have to stay close to it.\r\n\r\n- second, the scenery : this is a collection of points in space, which\r\n  will be called 'vertices', together with textures which are mapped onto\r\n  the polygons defined by consecutive vertices. These textures are used\r\n  to draw the road, the roadside and part of the landscape. Thus it is\r\n  of course preferable that the scenery remain close to the virtual road !\r\n\r\n- and last, the objects : points in space together with bitmaps, which\r\n  are used for road signs, buildings, trees, etc... Some of them are\r\n  plain 2D bitmaps, but others have a more sophisticated polygonal 3D\r\n  structure.\r\n\r\nThe 3D coordinates x,y,z will always be used with the following meaning :\r\n\r\n- x is an axis which is transversal to the starting line. Positive x values\r\n  correspond to points which are on the right of the starting position.\r\n  (i.e. if you start looking to the north, x points to the east)\r\n\r\n- y is an axis which is parallel to the starting line. Positive y values\r\n  correspond to points which are ahead of the starting position.\r\n  (i.e. y points to the north)\r\n\r\n- z is a vertical axis. Positive z values correspond to points higher than\r\n  the starting position.\r\n\r\na) TRI files begin with 12F8h bytes of headers and index tables. These are\r\nas follows :\r\n\r\noffset len data\r\n------ --- ----\r\n00      4   ?\r\n04      8   ?\r\n0C      4   x coordinate of the first node\r\n10      4   z coordinate of the first node\r\n14      4   y coordinate of the first node\r\n18      4   ?\r\n1C      4   ?\r\n20      4   ?\r\n24      4   length of the scenery data (in bytes : 554h per record)\r\n28      4   ?\r\n2C    960h  first index table\r\n98C   960h  second index table\r\n\r\nThe first index table is a succession of 32-bit offsets. It follows an\r\narithmetic progression by 548h as a general rule. This means the first\r\nvalue is 0, followed by 548h, A90h, etc... On closed tracks, when the\r\nend is reached, the offsets start again with 0, 548h, A90h, etc...\r\n(so that the end of a lap is connected with the beginning of the following\r\none !). The table is filled to 960h bytes (600 entries) with zero values.\r\nThis table is probably only used as a lookup table in memory during the\r\ngame, since there are no 548h-byte structures in the file.\r\n\r\nThe second index table is also a succession of 32-bit offsets, but these\r\noffsets are inside the TRI file. This one follows an arithmetic progression\r\nby 554h. It corresponds to the offsets of the successive records for\r\nscenery data, which starts at offset 1B000h in the scenery file. So this\r\ntable starts with 1B000h, 1B554h, 1BAA8h, etc... until the end of the\r\nTRI file is reached. It is filled to 600 entries with zero values.\r\n\r\nb) The virtual road data follows, and is constituted of 36-byte records\r\n(one for each node). The first record is at offset 12F8h, and the last\r\nallowed record is at offset 16468h (this leaves room for 2400 records, and\r\nsince a scenery block corresponds to four nodes, both capacities are equal).\r\nThe unused records (after the last node) are filled with zero values.\r\nThe structure of each record is the following :\r\n\r\noffset len data\r\n------ --- ----\r\n00      1  a0\r\n01      1  a1\r\n02      1  a2\r\n03      1  a3\r\n04      1  b0\r\n05      1  b1\r\n06      1  b2\r\n07      1  b3\r\n08      4  x coordinate\r\n0C      4  z coordinate\r\n10      4  y coordinate\r\n14      2  slope\r\n16      2  slant-A\r\n18      2  orientation\r\n1A      2  0\r\n1C      2  y-orientation\r\n1E      2  slant-B\r\n20      2  x-orientation\r\n22      2  0\r\n\r\n- a0,a1,a2,a3 are 8-bit values whose purpose is unknown : possibly the\r\n  width of the road on each side ?\r\n\r\n- b0,b1,b2,b3 are 8-bit values of unknown purpose.\r\n\r\n- x, z and y coordinates are signed long values.\r\n\r\n- slope is a value indicating the slope at the current node, i.e. the\r\n  difference between the z coordinates of two consecutive nodes.\r\n  A good approximation is : slope(i) = (z(i+1) - z(i))/152.\r\n  However, to complicate things, it is stored as a signed 14-bit value,\r\n  complemented to 4000h. This means -1 is stored as 3FFFh, -2 as 3FFEh, ...\r\n  So in fact you must perform a logical and with 3FFFh before storing !\r\n\r\n- slant-A is a value indicating how the road is slanted to the left or\r\n  to the right (as in the turns in Autumn Valley or Lost Vegas). It is\r\n  a signed 14-bit value, like slope. The value is positive if the road\r\n  is slanted to the right, negative if it is slanted to the left.\r\n\r\n- slant-B has the same purpose, but is a standard signed 16-bit value.\r\n  Its value is positive for the left, negative for the right.\r\n  The approximative relation between slant-A and slant-B is\r\n  slant-B = -12.3 slant-A (remember that slant-A is 14-bit, though)\r\n\r\n- orientation is a 14-bit value, and is equal to 0 for north (increasing y),\r\n  1000h for east (increasing x), 2000h for south (decreasing y), 3000h for\r\n  west (decreasing x), and back to 3FFFh for north.\r\n\r\n- y-orientation is a signed 16-bit value, which is proportional to the\r\n  y coordinate variation. Meanwhile, x-orientation is proportional to the\r\n  *opposite* of the x coordinate variation. This means that the couple\r\n  (-xorientation,yorientation) gives the orientation of the track.\r\n  The norm (square root of xorient^2+yorient^2) is usually around 32000\r\n  (a little less than 8000h, to avoid numeric overflows), but can fluctuate\r\n  with the only condition that (-xor,yor) gives the correct orientation.\r\n\r\nc) The objects data comes next. There are first several distinct zones,\r\nmany of which seem to be unused (?) :\r\n\r\noffset len  data\r\n------ ---  ----\r\n16478  708h  3-byte records (there are 600... as many as scenery blocks ?)\r\n16B80   4    40h (?)\r\n16B84   4    3E8h = 1000 (size of the main block in records)\r\n16B88   4    'OBJS'\r\n16B8C   4    428Ch (total length of the remaining blocks)\r\n16B90  400h  16-byte records (unknown purpose)\r\n16F90   4    ?\r\n16F94 3E80h  object data : 1000 records of 16 bytes (one per object)\r\n1AE14  1ECh  0\r\n\r\nThe object data itself consists of a 16-byte record per object. The record\r\nstructure is the following :\r\n\r\noffset len data\r\n------ --- ----\r\n00      4  reference node\r\n04      1  bitmap number\r\n05      1  flip\r\n06      4  flags (unknown purpose)\r\n0A      2  relative x coordinate\r\n0C      2  relative z coordinate\r\n0E      2  relative y coordinate\r\n\r\nEach object is related to a reference node in the virtual road. The x,z,y\r\ncoordinates are then expressed as signed 16-bit values relative to the\r\ncoordinates of the reference node. Beware that the axes are simply translated\r\nbut not rotated ! (i.e. the x and y axes are still pointing east and north)\r\nThe objects are sorted in the order of increasing reference nodes.\r\nA reference node value of -1 indicates that the record is unused (i.e. after\r\nthe end of the used records). Also note that the coordinates are not\r\nexpressed in the same unit as the 32-bit absolute coordinates seen above\r\n(the units are much larger, so that the value fits in 16 bits).\r\n\r\nThe 8-bit flip value is equal to 0 for an object that is perfectly perpendi-\r\ncular to the track (e.g. a road sign), larger values for objects that are\r\nslightly turned, until 64 for an object that is mapped along the track\r\n(e.g. an ad on the side of the road), then up to 128 which is the perfectly\r\nreversed position (since the objects have no \"back\", this is the common\r\nway of reversing a road sign for a turn in the other direction), then\r\nup to 192 which is again longitudinal mapping (the other way) and until\r\n255 which is back to the normal position.\r\n\r\nThe bitmap number corresponds to a texture in the corresponding .FAM file\r\n(see B.8). The relevant bitmaps are in the second chunk of the .FAM file.\r\nTwo cases can occur :\r\n\r\n- closed tracks : the second chunk is a 'wwww' structure containing a\r\nsingle subchunk which is in turn a SHPI directory where the entry corres-\r\nponding to bitmap #n is called \"nn00\" where nn is n written in decimal.\r\n(e.g. bitmap #18 is \"1800\"). Furthermore the object called \"!pal\" or \"!PAL\",\r\nwhen it exists, is the corresponding palette (256 3-byte entries) ; FFh is\r\ntransparent.\r\n\r\n- open roads : the second chunk contains a subchunk per bitmap, and each\r\nsubchunk is a SHPI containing at least the object \"0000\" (the bitmap), and\r\npossibly a palette (\"!pal\" or \"!PAL\"). Object #n is then the bitmap \"0000\"\r\nin the subchunk #n (the first subchunk is #0).\r\n\r\nOne must add to these 2D objects (plain bitmaps) the 3D objects described\r\nin the fourth chunk of the .FAM file. They usually correspond to numbers\r\nabove the last 2D object ; however it happens, in closed tracks, that some\r\nof the 3D objects are given numbers inside the range used by 2D objects.\r\nIn that case, the numbers describing 2D objects are shifted upwards.\r\n(i.e. the bitmap \"4400\" corresponds to object #45 or #46). This phenomenon\r\napparently does not occur for open roads, where the 3D objects always follow\r\nthe 2D objects.\r\n\r\nFurthermore, certain consecutive bitmaps represent successive states of an\r\nanimated object. In that case, the game will display successively the\r\nrelevant bitmaps. Note that if the second bitmap is given instead of the\r\nfirst, the animation does not occur.\r\n\r\nd) The scenery data starts at offset 1B000h. It is made up of records of\r\nsize 554h, each corresponding to four nodes in the virtual road. The\r\nrecords are consecutive and the last record ends the .TRI file.\r\nEach record has the following structure :\r\n\r\noffset len data\r\n------ --- ----\r\n000     4   'TRKD'\r\n004     4   548h = length of the record contents\r\n008     4   number of this record (0 for the first, then 1, etc...)\r\n00C     2   ?\r\n00E    10   textures\r\n018    12   reference point\r\n024    12   ?\r\n030    12   point A0\r\n03C    12   point A1\r\n...    ..   ........\r\n09C    12   point A9\r\n0A8    12   point A10\r\n0B4    12   ?\r\n0C0    12   point B0\r\n0CC    12   point B1\r\n...    ..   ........\r\n12C    12   point B9\r\n138    12   point B10\r\n144    12   ?\r\n150    12   point C0\r\n15C    12   point C1\r\n...    ..   ........\r\n1BC    12   point C9\r\n1C8    12   point C10\r\n1D4    12   ?\r\n1E0    12   point D0\r\n1EC    12   point D1\r\n...    ..   ........\r\n24C    12   point D9\r\n258    12   point D10\r\n264    12   ?\r\n270    12   ?\r\n27C    12   point E0\r\n288    12   point E1\r\n...    ..   ........\r\n2E8    12   point E9\r\n2F4    12   point E10\r\n300   516   ? unused (room for 43 points)\r\n504    80   ? unused\r\n\r\nEach point is given by three signed 32-bit absolute coordinates (x,z,y as\r\nusual). The coordinates are the same as in the virtual road data.\r\nThe reference point is unused in the game and usually equal to point A0.\r\n\r\nThe points A0,...,A10 in record #n (starting with 0) correspond to the node\r\n#4n (starting with 0) in the virtual road data. B0,...,B10 correspond to\r\nnode #4n+1, C0...C10 to node #4n+2, D0...D10 to node #4n+3 and E0...E10 to\r\nnode #4n+4. Thus the points E0...E10 are identical to the points A0...A10\r\nof the following record.\r\n\r\nThe eleven point series (0 to 10) are arranged as follows : A0-E0 are near\r\nthe middle of the road, and thus close to the corresponding nodes.\r\nA1-E1 are a little to the right, A2-E2 further right, ... until A5-E5.\r\nA6-E6 are a little to the left, A7-E7 further left, ... until A10-E10.\r\n(In tunnels, the points A5-E5 and A10-E10 get back to the center, consti-\r\ntuting the ceiling).\r\n\r\nTo each record correspond ten textures (coded at the beginning), each given\r\nby a 8-bit value, T1,T2,...,T10. T1 is used between A0-E0 and A1-E1,\r\nT2 between A1-E1 and A2-E2, ..., T5 between A4-E4 and A5-E5 ; meanwhile,\r\nT6 is used between A0-E0 and A6-E6, T7 between A6-E6 and A7-E7, ...,\r\nT10 between A9-E9 and A10-E10. This is summarized on the following diagram :\r\n\r\nE10---E9---E8---E7---E6---E0---E1---E2---E3---E4---E5  node 4n+4\r\n |    |    |    |    |    ||    |    |    |    |    |\r\n |    |    |    |    |    ||    |    |    |    |    |\r\nD10   D9   D8   D7   D6   D0   D1   D2   D3   D4   D5  node 4n+3\r\n |  T |  T |  T |  T |  T || T  | T  | T  | T  | T  |\r\n |    |    |    |    |    ||    |    |    |    |    |\r\nC10   C9   C8   C7   C6   C0   C1   C2   C3   C4   C5  node 4n+2\r\n | 10 |  9 |  8 |  7 |  6 || 1  | 2  | 3  | 4  | 5  |\r\n |    |    |    |    |    ||    |    |    |    |    |\r\nB10   B9   B8   B7   B6   B0   B1   B2   B3   B4   B5  node 4n+1\r\n |    |    |    |    |    ||    |    |    |    |    |\r\n |    |    |    |    |    ||    |    |    |    |    |\r\nA10---A9---A8---A7---A6---A0---A1---A2---A3---A4---A5  node 4n\r\n                          ^\r\n       the nodes are here |\r\n\r\nThe texture numbers are converted to bitmaps in the first chunk of the\r\n.FAM file (see B.8). There are two different cases :\r\n\r\n- closed tracks : the first chunk is a 'wwww' structure which contains a \r\nsingle subchunk which is in turn a SHPI bitmap directory, possibly with a \r\npalette '!PAL' or '!pal'. There is also often a bitmap called 'ga00' or \r\n'GA00' (unknown interpretation). The names have the structure \"xxls\", where \r\nxx is a decimal value indicating the texture group, l is 'A', 'B' or 'C', \r\nand s indicates a scale ('0' is the largest, while '3'&'4' are very small). \r\nThe various scales are here to speed up the texture-mapping algorithm,\r\nanyway the only texture that is always present is with s=0.\r\nThe xx and l values correspond to a texture number n in the following way :\r\nn=3xx if l='A', 3xx+1 if l='B' and 3xx+2 if l='C'. Note that there are holes\r\nin the numbering : many numbers do not have a bitmap.\r\nExamples : bitmap \"03C0\" corresponds to texture #11 (3x3+2) at the largest\r\nscale; bitmap \"14A1\" corresponds to texture #42 (3x14) at the second scale\r\navailable.\r\n\r\n- open roads : the first chunk contains a subchunk per texture group (i.e.\r\nthe xx value is now the number of the subchunk, starting with 0). Each\r\nsubchunk is a SHPI directory containing potentially a palette, and bitmaps\r\nlabelled \"l00s\", where l is 'A','B' or 'C' and s is the scale.\r\nAs before, n=3xx if l='A', 3xx+1 if l='B', 3xx+2 if l='C', and there are\r\nholes in the numbering.\r\nExamples : texture #11 at scale '0' is now the bitmap \"C000\" in subchunk #3.\r\nTexture #42 at scale '1' is now the bitmap called \"A001\" in subchunk #14.\r\n\r\nB.7 SIMDATA\\SOUNDBNK\\*.BNK\r\n    ----------------------\r\n\r\nThese file store the sound samples for the engines, the crashes, etc...\r\nThe filenames ending with 'W' correspond to 16-bit sound, while 'B' stands\r\nfor 8-bit sound. Logically, 'S' should stand for stereo and 'M' for mono,\r\nbut it seems that, at least for cars, the file that gets loaded in 8-bit\r\nmono mode is the one whose name ends with 'SB'. I don't know why. I'm\r\nbeginning to think that EAC recruited Microsoft programmers ;-)\r\n\r\nAlso remember that the 8 bit sound samples are signed data. If you plan to\r\nuse WAV files as samples, don't forget to change this.\r\n\r\nThe file format is the following :\r\n\r\n- the first 512 bytes contain 128 long integers, which are either zero if\r\nthe corresponding sample is not present, or the offset of a sample header.\r\nCar files contain four samples with header offsets 200h, 248h, 290h and 2D8h,\r\ndeclared at offsets 04, 08, 0C and 80h respectively. The two first samples\r\nare the engine sound, the third is the car horn, the fourth is the sound\r\nthat gets produced when you change gears.\r\n\r\n- each sample header consists of 72 bytes (48h). The format is the following:\r\n\r\noffset len data\r\n------ --- ----\r\n00      4  ? flags (for 8-bit car sounds, these are 1, 2, 4000h and 10h)\r\n04      4  header's offset + 28h (i.e. offset of the 'EACS' marker)\r\n08      4  ? (0 for cars)\r\n0C      4  ? (0 for cars except gear change)\r\n10      4  ? (0 for cars)\r\n14      1  ?\r\n15      1  ? (80h for cars)\r\n16      1  ? (0 for cars)\r\n17      1  ?\r\n18      4  ? (7F40h for cars)\r\n1C      4  ? (0 for cars)\r\n20      4  ? (0 for cars)\r\n24      4  ? (0 for cars)\r\n28      4  'EACS'\r\n2C      4  3E80h (sampling rate : 16 kHz)\r\n30      1  8/16 bit flag : 1 for 8-bit, 2 for 16-bit\r\n31      1  mono/stereo flag : 1 for mono, 2 for stereo\r\n32      1  ? (0 for cars)\r\n33      1  ? (FFh for cars)\r\n34      4  beginning of the repeat loop (in samples : 1, 2 or 4 bytes)\r\n38      4  length of the repeat loop (in samples)\r\n3C      4  length of the sound (in samples)\r\n40      4  offset of the raw sound data in the .BNK file\r\n44      4  ? (0 for cars)\r\n\r\nB.8 SIMDATA\\TRACKFAM\\*.FAM\r\n    ----------------------\r\n\r\nThese files contain the bitmaps that are used for displaying the correspon-\r\nding track or track segment. They make use of the 'wwww' file format, using\r\nthe usual header (see B.2) : first 'wwww', then the number of chunks (as a\r\nlong integer), then the offsets of each chunk header (long integers).\r\n\r\nThe .FAM file itself is a 'wwww' file containing four chunks. The first chunk\r\ncorresponds to the background bitmaps, making up the terrain and the road,\r\nwhile the second chunk is devoted to foreground bitmaps (road signs, etc...)\r\nEach of these two chunks is itself in 'wwww' format, containing sub-chunks.\r\nNote that the offsets of the sub-chunks listed in the headers are relative\r\nto the beginning of the chunks. In the open road .FAM's, the chunks are very\r\nsubdivided and contain many small subchunks, while in the closed tracks,\r\nthere is only one big subchunk.\r\n\r\nNow the subchunks in each of the first two chunks are themselves 'SHPI'\r\nbitmap directories (see B.3), whose elements are a palette and several\r\nbitmaps. Keep in mind that the offsets in the directories are relative to\r\nthe beginning of the subchunk (i.e. the 'SHPI' header).\r\n\r\nThe third chunk of the .FAM file is a SHPI file describing the horizon :\r\nthe SHPI directory contains two entries, one for the palette and one for\r\nthe bitmap itself.\r\n\r\nThe fourth chunk describes the three-dimensional objects in the scenery.\r\nIt is a 'wwww' structure, containing a subchunk per object. Now each of\r\nthese subchunks is quite similar to a .CFM file (see B.4) : it is itself a\r\n'wwww' structure containing two subsubchunks ! The first subsubchunk is an\r\n'ORIP' structure describing the shape of the object, while the second is\r\na 'SHPI' structure containing the bitmaps referenced by the 'ORIP' part.\r\n\r\nIf you didn't catch everything, don't worry... look at NFSVIEW.PAS (see D.1)\r\n\r\n\r\nC - FRONTEND directory\r\n    ==================\r\n\r\nC.1 FRONTEND\\ART\\*.QFS, FRONTEND\\SHOW\\*.QFS : see B.5\r\n    ---------------------------------------\r\n\r\nC.2 FRONTEND\\ART\\*.INV\r\n    ------------------\r\n\r\nThese files describe how to reduce the palette from hi-color mode to 256\r\ncolors. The corresponding .QFS files (compressed SHPI bitmaps) have directory\r\nidentifier 'LN32', indicating the bitmaps are in 32768-color mode (each\r\nred, blue and green component is coded on 5 bits (0 to 31), and the 15 bits\r\ndescribing the color are stored in a 16-bit word). Since many video cards do\r\nnot support these modes, information is provided for palette reduction to\r\n256-color modes. This information is partly stored as a palette object in the\r\nQFS file, describing the RGB components of the 256 colors. In order to help\r\nNeed for Speed with the palette conversion, the .INV file (length 32768 bytes)\r\nstores, for each 15-bit value, which palette entry is relevant.\r\n\r\nDisplaying the 'LN32' bitmaps in 256-color is thus done by first expanding\r\nthe .QFS file, then setting the specified palette, and converting each\r\n15-bit pixel in the bitmap to the 8-bit value located at the corresponding\r\noffset in the .INV file.\r\n\r\nC.3 FRONTEND\\MUSIC\\*.ASF\r\n    --------------------\r\n\r\nThese files contain the music for the front end. The 40-byte header is as\r\nfollows (note a consistency with the EACS structures described in B.7) :\r\n\r\noffset len data\r\n------ --- ----\r\n00      4  '1SNh'\r\n04      4  ?\r\n08      4  'EACS'\r\n0C      4  3E80h (sampling rate 16 kHz)\r\n10      1  2 (16-bit mode flag)\r\n11      1  2 (stereo mode flag)\r\n12      1  ? (0 usually)\r\n13      1  ? (0 usually)\r\n14      4  length of the wave data (expressed in 4-byte samples)\r\n18      4  beginning of the repeat loop (in samples)\r\n1C      4  length of the repeat loop (in samples)\r\n20      4  0 (the start offset is not specified : should be 28h)\r\n24      4  ?\r\n\r\nSigned 16-bit stereo wave data follows.\r\n\r\nC.4 FRONTEND\\SPEECH\\*.EAS\r\n    ---------------------\r\n\r\nThese file contain the speeches, as an EACS structure (see B.7 and C.3).\r\nThe 32-byte header is now as follows :\r\n\r\noffset len data\r\n------ --- ----\r\n00      4  'EACS'\r\n04      4  3E80h (sampling rate 16 kHz)\r\n08      1  1 (8-bit mode flag)\r\n09      1  1 (mono mode flag)\r\n0A      1  ? (0 usually)\r\n0B      1  ? (FFh usually)\r\n0C      4  length of wave data (expressed in 1-byte units)\r\n10      4  FFFFFFFFh (no repeat loop)\r\n14      4  0 (no repeat length)\r\n18      4  20h (offset of the wave data)\r\n1C      4  ?\r\n\r\nSigned 8-bit mono wave data follows.\r\n\r\n\r\nD - Appendix\r\n    ========\r\n\r\nD.1 NFSVIEW.PAS\r\n    -----------\r\n\r\n{NFS file viewer : recurse into 'wwww' structures and view 'SHPI' bitmaps}\r\n\r\nuses crt;\r\nvar f:file;\r\n    scr:array[0..199,0..319] of byte absolute $A000:0;\r\n    pal:array[0..767] of byte;\r\n    s:string[5];\r\n\r\nprocedure setpalette; assembler;\r\nasm\r\n   mov dx,3c8h\r\n   xor cl,cl\r\n   mov si,offset pal\r\n   cld\r\n@bcl:\r\n   mov al,cl\r\n   out dx,al\r\n   inc dx\r\n   lodsb\r\n   out dx,al\r\n   lodsb\r\n   out dx,al\r\n   lodsb\r\n   out dx,al\r\n   dec dx\r\n   inc cl\r\n   jnz @bcl\r\nend;\r\n\r\nfunction st(x:longint):string;\r\nvar s:string[20];\r\nbegin\r\n     str(x,s);\r\n     st:=s;\r\nend;\r\n\r\nprocedure viewshpi(pre:string;off:longint); {process a SHPI}\r\nvar ni,i:integer;\r\n    w,h,xp,yp,y:word;\r\n    l:longint;\r\n    foundpal:boolean;\r\nbegin\r\n     seek(f,off+8);\r\n     blockread(f,ni,2);\r\n     writeln(pre,'SHPI ',ni,' bitmaps'#10#13'Looking for a palette...');\r\n     foundpal:=false;\r\n     for i:=0 to ni-1 do begin {look for a palette}\r\n       seek(f,off+20+8*i);\r\n       blockread(f,l,4);\r\n       seek(f,off+l+4);\r\n       blockread(f,w,2);\r\n       blockread(f,h,2);\r\n       if (w=256) and (h=3) then begin\r\n        blockread(f,pal,8);\r\n        blockread(f,pal,768);\r\n        foundpal:=true;\r\n       end;\r\n     end;\r\n     if foundpal then writeln('Found !') else writeln('No palette.');\r\n     readkey;\r\n     asm\r\n      mov ax,13h\r\n      int 10h\r\n     end;\r\n     if foundpal then setpalette;\r\n     for i:=0 to ni-1 do begin    {display the bitmaps}\r\n       fillchar(scr,64000,0);\r\n       seek(f,off+20+8*i);\r\n       blockread(f,l,4);\r\n       seek(f,off+l+4);\r\n       blockread(f,w,2);\r\n       blockread(f,h,2);\r\n       blockread(f,l,4);\r\n       blockread(f,xp,2);\r\n       blockread(f,yp,2);\r\n       if (w<>256) or (h<>3) then begin\r\n        if (xp+w>320) or (yp+h>200) then begin yp:=0; xp:=0; end;\r\n        for y:=yp to yp+h-1 do\r\n         blockread(f,scr[y,xp],w);\r\n        readkey; end;\r\n     end;\r\n     asm\r\n      mov ax,3\r\n      int 10h\r\n     end;\r\nend;\r\n\r\nprocedure viewwwww(pre:string;off:longint); {process a wwww block}\r\nvar n,i:integer;\r\n    l:longint;\r\n    s:string[5];\r\nbegin\r\n     seek(f,off+4);\r\n     blockread(f,n,2);\r\n     for i:=1 to n do begin {process each chunk}\r\n      seek(f,off+4+4*i);\r\n      blockread(f,l,4);\r\n      s[0]:=#4;\r\n      seek(f,off+l);\r\n      blockread(f,s[1],4);  {read 4-char id}\r\n      if s='wwww' then\r\n        viewwwww(pre+'chunk '+st(i)+'/'+st(n)+' sub',off+l)\r\n      else if s='SHPI' then\r\n        viewshpi(pre+'chunk '+st(i)+'/'+st(n)+' ',off+l)\r\n      else begin\r\n       writeln(pre,'chunk ',i,'/',n,' unrecognized header ',s);\r\n       readkey;\r\n      end;\r\n     end;\r\nend;\r\n\r\nbegin\r\n     if paramcount=0 then begin\r\n       writeln('Need For Speed wwww/SHPI Viewer 1.0 (c) Denis Auroux 1995');\r\n       writeln('Syntax : NFSVIEW filename'#10#13);\r\n       exit;\r\n     end;\r\n     clrscr;\r\n     assign(f,paramstr(1));\r\n     reset(f,1);\r\n     s[0]:=#4;\r\n     blockread(f,s[1],4); {read 4-char id}\r\n     if s='wwww' then viewwwww('',0)\r\n     else if s='SHPI' then viewshpi('',0)\r\n     else writeln('Unrecognized header ',s,#10#13);\r\n     close(f);\r\nend.\r\n\r\n============================================================================\r\nFor any comments and additions to this file, mail to auroux@clipper.ens.fr.\r\n============================================================================\r\n"
  },
  {
    "path": "OpenNFS1/Audio/BnkVehicleAudioProvider.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\n\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Audio;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework;\r\nusing OpenNFS1.Physics;\r\nusing System.Diagnostics;\r\nusing OpenNFS1.Audio;\r\nusing OpenNFS1.Parsers.Audio;\r\n\r\nnamespace OpenNFS1\r\n{\r\n\tclass VehicleAudioProvider2\r\n\t{\r\n\t\tconst int GRASS_SLIDE_INDEX = 5;\r\n\t\tDrivableVehicle _car;\r\n\r\n\t\tSoundEffect _engineOnEffect, _engineOffEffect, _grassSlide;\r\n\t\tSoundEffectInstance _engineOn, _engineOff, _skidInstance, _grassSlideInstance;\r\n\t\tSoundEffect _gearChange;\r\n\t\tList<SoundEffect> _skids = new List<SoundEffect>();\r\n\t\tbool _isActive = false;\r\n\r\n\t\tpublic VehicleAudioProvider2(DrivableVehicle car)\r\n\t\t{\r\n\t\t\t_car = car;\r\n\t\t}\r\n\r\n\r\n\t\tpublic void Initialize()\r\n\t\t{\r\n\t\t\tif (_engineOn != null)\r\n\t\t\t{\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t_isActive = true;\r\n\r\n\t\t\tBnkFile bnk = new BnkFile(_car.Descriptor.SoundBnkFile);\r\n\t\t\tvar sample = bnk.Samples[0];\r\n\t\t\t_engineOnEffect = new SoundEffect(sample.PCMData, sample.SampleRate, sample.NbrChannels == 2 ? AudioChannels.Stereo : AudioChannels.Mono);\r\n\t\t\t_engineOn = _engineOnEffect.CreateInstance();\r\n\t\t\tsample = bnk.Samples[1];\r\n\t\t\t_engineOffEffect = new SoundEffect(sample.PCMData, sample.SampleRate, sample.NbrChannels == 2 ? AudioChannels.Stereo : AudioChannels.Mono);\r\n\t\t\t_engineOff = _engineOffEffect.CreateInstance();\r\n\t\t\tsample = bnk.Samples[3];\r\n\t\t\t_gearChange = new SoundEffect(sample.PCMData, sample.SampleRate, sample.NbrChannels == 2 ? AudioChannels.Stereo : AudioChannels.Mono);\r\n\t\t\t\r\n\r\n\t\t\t//temp = Engine.Instance.ContentManager.Load<SoundEffect>(String.Format(\"Content/Audio/Vehicles/{0}/engine-on-low\", _car.Descriptor.Name));\r\n\t\t\t//_engineOnLow = temp.CreateInstance();\r\n\t\t\t////.Play(0.3f, 0, 0);\r\n\t\t\t//temp = Engine.Instance.ContentManager.Load<SoundEffect>(String.Format(\"Content/Audio/Vehicles/{0}/engine-on-high\", _car.Descriptor.Name));\r\n\t\t\t//_engineOn = temp.CreateInstance(); // temp.Play(0.3f, 0, 0);\r\n\t\t\t//temp = Engine.Instance.ContentManager.Load<SoundEffect>(String.Format(\"Content/Audio/Vehicles/{0}/engine-off-low\", _car.Descriptor.Name));\r\n\t\t\t//_engineOffLow = temp.CreateInstance(); //temp.Play(0.3f, 0, 0);\r\n\t\t\t//temp = Engine.Instance.ContentManager.Load<SoundEffect>(String.Format(\"Content/Audio/Vehicles/{0}/engine-off-high\", _car.Descriptor.Name));\r\n\t\t\t//_engineOff = temp.CreateInstance(); //temp.Play(0.3f, 0, 0);\r\n\r\n\t\t\tBnkFile envBnk = new BnkFile(\"COLL_SW.BNK\");\r\n\t\t\tsample = envBnk.Samples[GRASS_SLIDE_INDEX];\r\n\t\t\t_grassSlide = new SoundEffect(sample.PCMData, sample.SampleRate, sample.NbrChannels == 2 ? AudioChannels.Stereo : AudioChannels.Mono);\r\n\t\t\t_grassSlideInstance = _grassSlide.CreateInstance();\r\n\r\n\t\t\t_skids.Add(Engine.Instance.ContentManager.Load<SoundEffect>(\"Content/Audio/Vehicles/common/skid1\"));\r\n\t\t\t_skids.Add(Engine.Instance.ContentManager.Load<SoundEffect>(\"Content/Audio/Vehicles/common/skid2\"));\r\n\t\t\t_skids.Add(Engine.Instance.ContentManager.Load<SoundEffect>(\"Content/Audio/Vehicles/common/skid3\"));\r\n\t\t}\r\n\r\n\t\tpublic void UpdateEngine()\r\n\t\t{\r\n\t\t\tif (!_isActive) return;\r\n\t\t\tfloat engineRpmFactor = ((_car.Motor.Rpm - 0.8f) / _car.Motor.RedlineRpm) - 0.5f;\r\n\t\t\tif (_car.Motor.Throttle == 0 && !_car.Motor.AtRedline)\r\n\t\t\t{\r\n\t\t\t\t_engineOn.Pause();\r\n\t\t\t\t_engineOff.Resume();\r\n\t\t\t\t_engineOff.Pitch = engineRpmFactor;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t_engineOn.Resume();\r\n\t\t\t\t_engineOff.Pause();\r\n\t\t\t\t_engineOn.Pitch = engineRpmFactor;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void PlaySkid(bool play)\r\n\t\t{\r\n\t\t\tif (!_isActive) return;\r\n\t\t\tif (play)\r\n\t\t\t{\r\n\t\t\t\tif (_skidInstance != null && _skidInstance.State == SoundState.Playing)\r\n\t\t\t\t\treturn;\r\n\t\t\t\tif (_skidInstance != null && _skidInstance.State != SoundState.Playing)\r\n\t\t\t\t{\r\n\t\t\t\t\t_skidInstance.Resume();\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t_skidInstance = _skids[Engine.Instance.Random.Next(_skids.Count)].CreateInstance(); //.Play(0.3f, 0, 0);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (_skidInstance != null && _skidInstance.State == SoundState.Playing)\r\n\t\t\t\t{\r\n\t\t\t\t\t_skidInstance.Stop();\r\n\t\t\t\t\t_skidInstance = null;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void ChangeGear()\r\n\t\t{\r\n\t\t\tif (!_isActive) return;\r\n\t\t\t_gearChange.Play();\r\n\t\t}\r\n\r\n\t\tpublic void HitGround()\r\n\t\t{\r\n\t\t\tif (!_isActive) return;\r\n\t\t\tEnvironmentAudioProvider.Instance.PlayCollision(2);\r\n\t\t\tSoundEngine2.Instance.PlayEffect(_skids[2].CreateInstance(), 0.2f);\r\n\t\t}\r\n\r\n\r\n\t\tpublic void PlayOffRoad(bool play)\r\n\t\t{\r\n\t\t\tif (!_isActive) return;\r\n\t\t\tif (play)\r\n\t\t\t{\r\n\t\t\t\t_grassSlideInstance.Volume = 0.4f;\r\n\t\t\t\tif (_grassSlideInstance.State == SoundState.Playing)\r\n\t\t\t\t\treturn;\r\n\t\t\t\telse\r\n\t\t\t\t\t_grassSlideInstance.Resume();\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t_grassSlideInstance.Pause();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void StopAll()\r\n\t\t{\r\n\t\t\tif (!_isActive) return;\r\n\t\t\tif (_skidInstance != null)\r\n\t\t\t\t_skidInstance.Stop();\r\n\t\t\t_engineOff.Stop();\r\n\t\t\t_engineOn.Stop();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Audio/EnvironmentAudioProvider.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Audio;\r\nusing GameEngine;\r\n\r\nnamespace OpenNFS1.Audio\r\n{\r\n    class EnvironmentAudioProvider\r\n    {\r\n        private static EnvironmentAudioProvider _instance;\r\n\r\n        public static EnvironmentAudioProvider Instance\r\n        {\r\n            get\r\n            {\r\n                if (_instance == null)\r\n                {\r\n                    _instance = new EnvironmentAudioProvider();\r\n                }\r\n                return _instance;\r\n            }\r\n        }\r\n\r\n        List<SoundEffect> _collisions;\r\n        SoundEffectInstance _collisionInstance;\r\n\r\n\t\tpublic string BasePath { get { return GameConfig.CdDataPath + \"/ConvertedAudio\"; } }\r\n        \r\n        private EnvironmentAudioProvider()\r\n        {\r\n            _collisions = new List<SoundEffect>();\r\n            _collisions.Add(Engine.Instance.ContentManager.Load<SoundEffect>(BasePath + \"/Environment/collision1\"));\r\n\t\t\t_collisions.Add(Engine.Instance.ContentManager.Load<SoundEffect>(BasePath + \"/Environment/collision2\"));\r\n\t\t\t_collisions.Add(Engine.Instance.ContentManager.Load<SoundEffect>(BasePath + \"/Environment/collision3\"));\r\n        }\r\n\r\n        public void PlayVehicleFenceCollision()\r\n        {\r\n            if (_collisionInstance != null && _collisionInstance.State == SoundState.Playing)\r\n            {\r\n                return;\r\n            }\r\n            _collisionInstance = _collisions[Utility.RandomGenerator.Next(_collisions.Count)].CreateInstance();\r\n\t\t\t_collisionInstance.Play();\r\n        }\r\n\r\n        public void PlayCollision(int index)\r\n        {\r\n            _collisions[index].Play(0.5f, 0, 0);\r\n        }\r\n\r\n        \r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Audio/VehicleAudioProvider.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\n\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Audio;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework;\r\nusing OpenNFS1.Physics;\r\nusing System.Diagnostics;\r\nusing OpenNFS1.Audio;\r\n\r\nnamespace OpenNFS1\r\n{\r\n    class VehicleAudioProvider\r\n    {\r\n        DrivableVehicle _car;\r\n\r\n        SoundEffectInstance _engineOnLow, _engineOnHigh, _engineOffLow, _engineOffHigh, _skidInstance, _offRoadInstance;\r\n        SoundEffect _gearChange;\r\n        List<SoundEffect> _skids = new List<SoundEffect>();\r\n\t\tbool _isActive = false;\r\n                \r\n        public VehicleAudioProvider(DrivableVehicle car)\r\n        {\r\n            _car = car;\r\n        }\r\n\r\n\t\tpublic string BasePath { get { return GameConfig.CdDataPath + \"/ConvertedAudio\"; } }\r\n\r\n\r\n        public void Initialize()\r\n        {\r\n\t\t\tif (_engineOnLow != null)\r\n            {\r\n                return;\r\n            }\r\n\r\n\t\t\t_isActive = true;\r\n            \r\n            SoundEffect temp;\r\n\t\t\ttemp = Engine.Instance.ContentManager.Load<SoundEffect>(String.Format(BasePath + \"/Vehicles/{0}/engine-on-low\", _car.Descriptor.Name));\r\n            _engineOnLow = temp.CreateInstance();\r\n\t\t\t//.Play(0.3f, 0, 0);\r\n\t\t\ttemp = Engine.Instance.ContentManager.Load<SoundEffect>(String.Format(BasePath + \"/Vehicles/{0}/engine-on-high\", _car.Descriptor.Name));\r\n\t\t\t_engineOnHigh = temp.CreateInstance(); // temp.Play(0.3f, 0, 0);\r\n\t\t\ttemp = Engine.Instance.ContentManager.Load<SoundEffect>(String.Format(BasePath + \"/Vehicles/{0}/engine-off-low\", _car.Descriptor.Name));\r\n\t\t\t_engineOffLow = temp.CreateInstance(); //temp.Play(0.3f, 0, 0);\r\n\t\t\ttemp = Engine.Instance.ContentManager.Load<SoundEffect>(String.Format(BasePath + \"/Vehicles/{0}/engine-off-high\", _car.Descriptor.Name));\r\n\t\t\t_engineOffHigh = temp.CreateInstance(); //temp.Play(0.3f, 0, 0);\r\n\r\n\t\t\ttemp = Engine.Instance.ContentManager.Load<SoundEffect>(BasePath + \"/Vehicles/common/grass_slide\");\r\n\t\t\t_offRoadInstance = temp.CreateInstance(); //temp.Play(0.3f, 0, 0, true);\r\n            _offRoadInstance.Pause();\r\n\r\n\t\t\t_skids.Add(Engine.Instance.ContentManager.Load<SoundEffect>(BasePath + \"/Vehicles/common/skid1\"));\r\n\t\t\t_skids.Add(Engine.Instance.ContentManager.Load<SoundEffect>(BasePath + \"/Vehicles/common/skid2\"));\r\n\t\t\t_skids.Add(Engine.Instance.ContentManager.Load<SoundEffect>(BasePath + \"/Vehicles/common/skid3\"));\r\n\r\n            _engineOnLow.Pause();\r\n            _engineOnHigh.Pause();\r\n            _engineOffLow.Pause();\r\n            _engineOffHigh.Pause();\r\n\r\n\t\t\t_gearChange = Engine.Instance.ContentManager.Load<SoundEffect>(String.Format(BasePath + \"/Vehicles/{0}/gear-change\", _car.Descriptor.Name));\r\n\r\n        }\r\n\r\n        public void UpdateEngine()\r\n        {\r\n\t\t\tif (!_isActive) return;\r\n            float engineRpmFactor = (_car.Motor.Rpm - 0.8f) / _car.Motor.RedlineRpm;\r\n            SoundEffectInstance low, high;\r\n\r\n            if (_car.Motor.Throttle == 0 && !_car.Motor.AtRedline)\r\n            {\r\n                _engineOnHigh.Pause();\r\n                _engineOnLow.Pause();\r\n                _engineOffHigh.Resume();\r\n                _engineOffLow.Resume();\r\n                low = _engineOffLow;\r\n                high = _engineOffHigh;\r\n            }\r\n            else\r\n            {\r\n                _engineOnHigh.Resume();\r\n                _engineOnLow.Resume();\r\n                _engineOffHigh.Pause();\r\n                _engineOffLow.Pause();\r\n                low = _engineOnLow;\r\n                high = _engineOnHigh;\r\n            }\r\n\r\n            low.Volume = engineRpmFactor > 0.55f ? 0 : 0.3f;\r\n            high.Volume = engineRpmFactor < 0.45f ? 0 : 0.3f;\r\n\r\n            if (engineRpmFactor < 0.1f)\r\n            {\r\n                low.Volume = MathHelper.Lerp(0.1f, 0.3f, (engineRpmFactor) * 10);\r\n            }\r\n            \r\n            if (engineRpmFactor > 0.45f && engineRpmFactor < 0.55f)\r\n            {\r\n                low.Volume = MathHelper.Lerp(0.3f, 0f, (engineRpmFactor - 0.45f) * 10);\r\n                high.Volume = MathHelper.Lerp(0, 0.3f, (engineRpmFactor - 0.45f) * 10);\r\n            }\r\n\r\n            low.Pitch = engineRpmFactor - 0.1f;\r\n            high.Pitch = engineRpmFactor - 0.5f;\r\n        }\r\n\r\n        public void PlaySkid(bool play)\r\n        {\r\n\t\t\tif (!_isActive) return;\r\n            if (play)\r\n            {\r\n                if (_skidInstance != null && _skidInstance.State == SoundState.Playing)\r\n                    return;\r\n                if (_skidInstance != null && _skidInstance.State != SoundState.Playing)\r\n                {\r\n                    _skidInstance.Resume();\r\n                }\r\n                else\r\n                {\r\n                    _skidInstance = _skids[Engine.Instance.Random.Next(_skids.Count)].CreateInstance(); //.Play(0.3f, 0, 0);\r\n                }\r\n            }\r\n            else\r\n            {\r\n                if (_skidInstance != null && _skidInstance.State == SoundState.Playing)\r\n                {\r\n                    _skidInstance.Stop();\r\n                    _skidInstance = null;\r\n                }\r\n            }                \r\n        }\r\n\r\n        public void ChangeGear()\r\n        {\r\n\t\t\tif (!_isActive) return;\r\n            _gearChange.Play();\r\n        }\r\n\r\n        public void HitGround()\r\n        {\r\n\t\t\tif (!_isActive) return;\r\n            EnvironmentAudioProvider.Instance.PlayCollision(2);\r\n            SoundEngine2.Instance.PlayEffect(_skids[2].CreateInstance(), 0.2f);\r\n        }\r\n\r\n\r\n        public void PlayOffRoad(bool play)\r\n        {\r\n\t\t\tif (!_isActive) return;\r\n            if (play)\r\n            {\r\n                _offRoadInstance.Volume = 0.4f;\r\n                if (_offRoadInstance.State == SoundState.Playing)\r\n                    return;\r\n                else\r\n                    _offRoadInstance.Resume();\r\n            }\r\n            else\r\n            {\r\n                _offRoadInstance.Pause();\r\n            }\r\n        }\r\n\r\n        public void StopAll()\r\n        {\r\n\t\t\tif (!_isActive) return;\r\n            if (_skidInstance != null)\r\n                _skidInstance.Stop();\r\n            _engineOffHigh.Stop();\r\n            _engineOffLow.Stop();\r\n            _engineOnHigh.Stop();\r\n            _engineOnLow.Stop();\r\n            \r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/AverageValue.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace OpenNFS1\r\n{\r\n    class AverageValue\r\n    {\r\n        int _nbrValues;\r\n        List<float> _values = new List<float>();\r\n\r\n        public AverageValue(int nbrVaues)\r\n        {\r\n            _nbrValues = nbrVaues;\r\n        }\r\n\r\n        public void AddValue(float value)\r\n        {\r\n            _values.Add(value);\r\n            if (_values.Count > _nbrValues)\r\n                _values.RemoveAt(0);\r\n        }\r\n\r\n        public float GetAveragedValue()\r\n        {\r\n            float average = 0;\r\n            foreach (float value in _values)\r\n            {\r\n                average += value;\r\n            }\r\n            return average / _values.Count;\r\n        }\r\n\r\n        public void Clear()\r\n        {\r\n            _values.Clear();\r\n        }\r\n    }\r\n\r\n    \r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Content/ArialBlack-Italic.spritefont",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nThis file contains an xml description of a font, and will be read by the XNA\nFramework Content Pipeline. Follow the comments to customize the appearance\nof the font in your game, and to change the characters which are available to draw\nwith.\n-->\n<XnaContent xmlns:Graphics=\"Microsoft.Xna.Framework.Content.Pipeline.Graphics\">\n  <Asset Type=\"Graphics:FontDescription\">\n\n    <!--\n    Modify this string to change the font that will be imported.\n    -->\n    <FontName>Arial Black</FontName>\n\n    <!--\n    Size is a float value, measured in points. Modify this value to change\n    the size of the font.\n    -->\n    <Size>26</Size>\n\n    <!--\n    Spacing is a float value, measured in pixels. Modify this value to change\n    the amount of spacing in between characters.\n    -->\n    <Spacing>0</Spacing>\n\n    <!--\n    UseKerning controls the layout of the font. If this value is true, kerning information\n    will be used when placing characters.\n    -->\n    <UseKerning>true</UseKerning>\n\n    <!--\n    Style controls the style of the font. Valid entries are \"Regular\", \"Bold\", \"Italic\",\n    and \"Bold, Italic\", and are case sensitive.\n    -->\n    <Style>Italic</Style>\n\n    <!--\n    If you uncomment this line, the default character will be substituted if you draw\n    or measure text that contains characters which were not included in the font.\n    -->\n    <!-- <DefaultCharacter>*</DefaultCharacter> -->\n\n    <!--\n    CharacterRegions control what letters are available in the font. Every\n    character from Start to End will be built and made available for drawing. The\n    default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin\n    character set. The characters are ordered according to the Unicode standard.\n    See the documentation for more information.\n    -->\n    <CharacterRegions>\n      <CharacterRegion>\n        <Start>&#32;</Start>\n        <End>&#126;</End>\n      </CharacterRegion>\n    </CharacterRegions>\n  </Asset>\n</XnaContent>\n"
  },
  {
    "path": "OpenNFS1/Content/ArialBlack.spritefont",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nThis file contains an xml description of a font, and will be read by the XNA\nFramework Content Pipeline. Follow the comments to customize the appearance\nof the font in your game, and to change the characters which are available to draw\nwith.\n-->\n<XnaContent xmlns:Graphics=\"Microsoft.Xna.Framework.Content.Pipeline.Graphics\">\n  <Asset Type=\"Graphics:FontDescription\">\n\n    <!--\n    Modify this string to change the font that will be imported.\n    -->\n    <FontName>Arial Black</FontName>\n\n    <!--\n    Size is a float value, measured in points. Modify this value to change\n    the size of the font.\n    -->\n    <Size>26</Size>\n\n    <!--\n    Spacing is a float value, measured in pixels. Modify this value to change\n    the amount of spacing in between characters.\n    -->\n    <Spacing>0</Spacing>\n\n    <!--\n    UseKerning controls the layout of the font. If this value is true, kerning information\n    will be used when placing characters.\n    -->\n    <UseKerning>true</UseKerning>\n\n    <!--\n    Style controls the style of the font. Valid entries are \"Regular\", \"Bold\", \"Italic\",\n    and \"Bold, Italic\", and are case sensitive.\n    -->\n    <Style>Regular</Style>\n\n    <!--\n    If you uncomment this line, the default character will be substituted if you draw\n    or measure text that contains characters which were not included in the font.\n    -->\n    <!-- <DefaultCharacter>*</DefaultCharacter> -->\n\n    <!--\n    CharacterRegions control what letters are available in the font. Every\n    character from Start to End will be built and made available for drawing. The\n    default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin\n    character set. The characters are ordered according to the Unicode standard.\n    See the documentation for more information.\n    -->\n    <CharacterRegions>\n      <CharacterRegion>\n        <Start>&#32;</Start>\n        <End>&#126;</End>\n      </CharacterRegion>\n    </CharacterRegions>\n  </Asset>\n</XnaContent>\n"
  },
  {
    "path": "OpenNFS1/Content/ParticleEffect.fx",
    "content": "//-----------------------------------------------------------------------------\n// ParticleEffect.fx\n//\n// Microsoft XNA Community Game Platform\n// Copyright (C) Microsoft Corporation. All rights reserved.\n//-----------------------------------------------------------------------------\n\n\n// Camera parameters.\nfloat4x4 View;\nfloat4x4 Projection;\nfloat2 ViewportScale;\n\n\n// The current time, in seconds.\nfloat CurrentTime;\n\n\n// Parameters describing how the particles animate.\nfloat Duration;\nfloat DurationRandomness;\nfloat3 Gravity;\nfloat EndVelocity;\nfloat4 MinColor;\nfloat4 MaxColor;\n\n\n// These float2 parameters describe the min and max of a range.\n// The actual value is chosen differently for each particle,\n// interpolating between x and y by some random amount.\nfloat2 RotateSpeed;\nfloat2 StartSize;\nfloat2 EndSize;\n\n\n// Particle texture and sampler.\ntexture Texture;\n\nsampler Sampler2 = sampler_state\n{\n\tTexture = <Texture>;\n\n\tMinFilter = Linear;\n\tMagFilter = Linear;\n\tMipFilter = Point;\n\n\tAddressU = Clamp;\n\tAddressV = Clamp;\n};\n\n\n// Vertex shader input structure describes the start position and\n// velocity of the particle, and the time at which it was created,\n// along with some random values that affect its size and rotation.\nstruct VertexShaderInput\n{\n\tfloat2 Corner : POSITION0;\n\tfloat3 Position : POSITION1;\n\tfloat3 Velocity : NORMAL0;\n\tfloat4 Random : COLOR0;\n\tfloat Time : TEXCOORD0;\n};\n\n\n// Vertex shader output structure specifies the position and color of the particle.\nstruct VertexShaderOutput\n{\n\tfloat4 Position : POSITION0;\n\tfloat4 Color : COLOR0;\n\tfloat2 TextureCoordinate : COLOR1;\n};\n\n\n// Vertex shader helper for computing the position of a particle.\nfloat4 ComputeParticlePosition(float3 position, float3 velocity,\n\tfloat age, float normalizedAge)\n{\n\tfloat startVelocity = length(velocity);\n\n\t// Work out how fast the particle should be moving at the end of its life,\n\t// by applying a constant scaling factor to its starting velocity.\n\tfloat endVelocity = startVelocity * EndVelocity;\n\n\t// Our particles have constant acceleration, so given a starting velocity\n\t// S and ending velocity E, at time T their velocity should be S + (E-S)*T.\n\t// The particle position is the sum of this velocity over the range 0 to T.\n\t// To compute the position directly, we must integrate the velocity\n\t// equation. Integrating S + (E-S)*T for T produces S*T + (E-S)*T*T/2.\n\n\tfloat velocityIntegral = startVelocity * normalizedAge +\n\t\t(endVelocity - startVelocity) * normalizedAge *\n\t\tnormalizedAge / 2;\n\n\tposition += normalize(velocity) * velocityIntegral * Duration;\n\n\t// Apply the gravitational force.\n\tposition += Gravity * age * normalizedAge;\n\n\t// Apply the camera view and projection transforms.\n\treturn mul(mul(float4(position, 1), View), Projection);\n}\n\n\n// Vertex shader helper for computing the size of a particle.\nfloat ComputeParticleSize(float randomValue, float normalizedAge)\n{\n\t// Apply a random factor to make each particle a slightly different size.\n\tfloat startSize = lerp(StartSize.x, StartSize.y, randomValue);\n\tfloat endSize = lerp(EndSize.x, EndSize.y, randomValue);\n\n\t// Compute the actual size based on the age of the particle.\n\tfloat size = lerp(startSize, endSize, normalizedAge);\n\n\t// Project the size into screen coordinates.\n\treturn size * Projection._m11;\n}\n\n\n// Vertex shader helper for computing the color of a particle.\nfloat4 ComputeParticleColor(float4 projectedPosition,\n\tfloat randomValue, float normalizedAge)\n{\n\t// Apply a random factor to make each particle a slightly different color.\n\tfloat4 color = lerp(MinColor, MaxColor, randomValue);\n\n\t\t// Fade the alpha based on the age of the particle. This curve is hard coded\n\t\t// to make the particle fade in fairly quickly, then fade out more slowly:\n\t\t// plot x*(1-x)*(1-x) for x=0:1 in a graphing program if you want to see what\n\t\t// this looks like. The 6.7 scaling factor normalizes the curve so the alpha\n\t\t// will reach all the way up to fully solid.\n\n\t\tcolor.a *= normalizedAge * (1 - normalizedAge) * (1 - normalizedAge) * 6.7;\n\n\treturn color;\n}\n\n\n// Vertex shader helper for computing the rotation of a particle.\nfloat2x2 ComputeParticleRotation(float randomValue, float age)\n{\n\t// Apply a random factor to make each particle rotate at a different speed.\n\tfloat rotateSpeed = lerp(RotateSpeed.x, RotateSpeed.y, randomValue);\n\n\tfloat rotation = rotateSpeed * age;\n\n\t// Compute a 2x2 rotation matrix.\n\tfloat c = cos(rotation);\n\tfloat s = sin(rotation);\n\n\treturn float2x2(c, -s, s, c);\n}\n\n\n// Custom vertex shader animates particles entirely on the GPU.\nVertexShaderOutput ParticleVertexShader(VertexShaderInput input)\n{\n\tVertexShaderOutput output;\n\n\t// Compute the age of the particle.\n\tfloat age = CurrentTime - input.Time;\n\n\t// Apply a random factor to make different particles age at different rates.\n\tage *= 1 + input.Random.x * DurationRandomness;\n\n\t// Normalize the age into the range zero to one.\n\tfloat normalizedAge = saturate(age / Duration);\n\n\t// Compute the particle position, size, color, and rotation.\n\toutput.Position = ComputeParticlePosition(input.Position, input.Velocity,\n\t\tage, normalizedAge);\n\n\tfloat size = ComputeParticleSize(input.Random.y, normalizedAge);\n\tfloat2x2 rotation = ComputeParticleRotation(input.Random.w, age);\n\n\t\toutput.Position.xy += mul(input.Corner, rotation) * size * ViewportScale;\n\n\toutput.Color = ComputeParticleColor(output.Position, input.Random.z, normalizedAge);\n\toutput.TextureCoordinate = (input.Corner + 1) / 2;\n\n\treturn output;\n}\n\n\n// Pixel shader for drawing particles.\nfloat4 ParticlePixelShader(VertexShaderOutput input) : COLOR0\n{\n\tfloat2 tex = input.TextureCoordinate;\n\n\tfloat4 v = tex2D(Sampler2, tex);\n\n\treturn v * input.Color;\n}\n\n\n// Effect technique for drawing particles.\ntechnique Particles\n{\n\tpass P0\n\t{\n\t\tVertexShader = compile vs_2_0 ParticleVertexShader();\n\t\tPixelShader = compile ps_2_0 ParticlePixelShader();\n\t}\n}\n"
  },
  {
    "path": "OpenNFS1/Content/common.fxh",
    "content": "//-----------------------------------------------------------------------------\n// Common.fxh\n//\n// Microsoft XNA Community Game Platform\n// Copyright (C) Microsoft Corporation. All rights reserved.\n//-----------------------------------------------------------------------------\n\n\nfloat ComputeFogFactor(float4 position)\n{\n    return saturate(dot(position, FogVector));\n}\n\n\nvoid ApplyFog(inout float4 color, float fogFactor)\n{\n    color.rgb = lerp(color.rgb, FogColor * color.a, fogFactor);\n}\n\n\nvoid AddSpecular(inout float4 color, float3 specular)\n{\n    color.rgb += specular * color.a;\n}\n\n\nstruct CommonVSOutput\n{\n    float4 Pos_ps;\n    float4 Diffuse;\n    float3 Specular;\n    float  FogFactor;\n};\n\n\nCommonVSOutput ComputeCommonVSOutput(float4 position)\n{\n    CommonVSOutput vout;\n    \n    vout.Pos_ps = mul(position, WorldViewProj);\n    vout.Diffuse = DiffuseColor;\n    vout.Specular = 0;\n    vout.FogFactor = ComputeFogFactor(position);\n    \n    return vout;\n}\n\n\n#define SetCommonVSOutputParams \\\n    vout.PositionPS = cout.Pos_ps; \\\n    vout.Diffuse = cout.Diffuse; \\\n    vout.Specular = float4(cout.Specular, cout.FogFactor);\n\n\n#define SetCommonVSOutputParamsNoFog \\\n    vout.PositionPS = cout.Pos_ps; \\\n    vout.Diffuse = cout.Diffuse;"
  },
  {
    "path": "OpenNFS1/Content/macros.fxh",
    "content": "//-----------------------------------------------------------------------------\n// Macros.fxh\n//\n// Microsoft XNA Community Game Platform\n// Copyright (C) Microsoft Corporation. All rights reserved.\n//-----------------------------------------------------------------------------\n\n#ifdef SM4\n\n// Macros for targetting shader model 4.0 (DX11)\n\n#define TECHNIQUE(name, vsname, psname ) \\\n\ttechnique name { pass { VertexShader = compile vs_4_0_level_9_1 vsname (); PixelShader = compile ps_4_0_level_9_1 psname(); } }\n\n#define BEGIN_CONSTANTS     cbuffer Parameters : register(b0) {\n#define MATRIX_CONSTANTS\n#define END_CONSTANTS       };\n\n#define _vs(r)\n#define _ps(r)\n#define _cb(r)\n\n#define DECLARE_TEXTURE(Name, index) \\\n    Texture2D<float4> Name : register(t##index); \\\n    sampler Name##Sampler : register(s##index)\n\n#define DECLARE_CUBEMAP(Name, index) \\\n    TextureCube<float4> Name : register(t##index); \\\n    sampler Name##Sampler : register(s##index)\n\n#define SAMPLE_TEXTURE(Name, texCoord)  Name.Sample(Name##Sampler, texCoord)\n#define SAMPLE_CUBEMAP(Name, texCoord)  Name.Sample(Name##Sampler, texCoord)\n\n\n#else\n\n\n// Macros for targetting shader model 2.0 (DX9)\n\n#define TECHNIQUE(name, vsname, psname ) \\\n\ttechnique name { pass { VertexShader = compile vs_2_0 vsname (); PixelShader = compile ps_2_0 psname(); } }\n\n#define BEGIN_CONSTANTS\n#define MATRIX_CONSTANTS\n#define END_CONSTANTS\n\n#define _vs(r)  : register(vs, r)\n#define _ps(r)  : register(ps, r)\n#define _cb(r)\n\n#define DECLARE_TEXTURE(Name, index) \\\n    sampler2D Name : register(s##index);\n\n#define DECLARE_CUBEMAP(Name, index) \\\n    samplerCUBE Name : register(s##index);\n\n#define SAMPLE_TEXTURE(Name, texCoord)  tex2D(Name, texCoord)\n#define SAMPLE_CUBEMAP(Name, texCoord)  texCUBE(Name, texCoord)\n\n\n#endif"
  },
  {
    "path": "OpenNFS1/Content/structures.fxh",
    "content": "//-----------------------------------------------------------------------------\n// Structurs.fxh\n//\n// Microsoft XNA Community Game Platform\n// Copyright (C) Microsoft Corporation. All rights reserved.\n//-----------------------------------------------------------------------------\n\n\n// Vertex shader input structures.\n\nstruct VSInput\n{\n    float4 Position : SV_Position;\n};\n\nstruct VSInputVc\n{\n    float4 Position : SV_Position;\n    float4 Color    : COLOR;\n};\n\nstruct VSInputTx\n{\n    float4 Position : SV_Position;\n    float2 TexCoord : TEXCOORD0;\n};\n\nstruct VSInputTxVc\n{\n    float4 Position : SV_Position;\n    float2 TexCoord : TEXCOORD0;\n    float4 Color    : COLOR;\n};\n\nstruct VSInputNm\n{\n    float4 Position : SV_Position;\n    float3 Normal   : NORMAL;\n};\n\nstruct VSInputNmVc\n{\n    float4 Position : SV_Position;\n    float3 Normal   : NORMAL;\n    float4 Color    : COLOR;\n};\n\nstruct VSInputNmTx\n{\n    float4 Position : SV_Position;\n    float3 Normal   : NORMAL;\n    float2 TexCoord : TEXCOORD0;\n};\n\nstruct VSInputNmTxVc\n{\n    float4 Position : SV_Position;\n    float3 Normal   : NORMAL;\n    float2 TexCoord : TEXCOORD0;\n    float4 Color    : COLOR;\n};\n\nstruct VSInputTx2\n{\n    float4 Position  : SV_Position;\n    float2 TexCoord  : TEXCOORD0;\n    float2 TexCoord2 : TEXCOORD1;\n};\n\nstruct VSInputTx2Vc\n{\n    float4 Position  : SV_Position;\n    float2 TexCoord  : TEXCOORD0;\n    float2 TexCoord2 : TEXCOORD1;\n    float4 Color     : COLOR;\n};\n\nstruct VSInputNmTxWeights\n{\n    float4 Position : SV_Position;\n    float3 Normal   : NORMAL;\n    float2 TexCoord : TEXCOORD0;\n    int4   Indices  : BLENDINDICES0;\n    float4 Weights  : BLENDWEIGHT0;\n};\n\n\n\n// Vertex shader output structures.\n\nstruct VSOutput\n{\n    float4 PositionPS : SV_Position;\n    float4 Diffuse    : COLOR0;\n    float4 Specular   : COLOR1;\n};\n\nstruct VSOutputNoFog\n{\n    float4 PositionPS : SV_Position;\n    float4 Diffuse    : COLOR0;\n};\n\nstruct VSOutputTx\n{\n    float4 PositionPS : SV_Position;\n    float4 Diffuse    : COLOR0;\n    float4 Specular   : COLOR1;\n    float2 TexCoord   : TEXCOORD0;\n};\n\nstruct VSOutputTxNoFog\n{\n    float4 PositionPS : SV_Position;\n    float4 Diffuse    : COLOR0;\n    float2 TexCoord   : TEXCOORD0;\n};\n\nstruct VSOutputPixelLighting\n{\n    float4 PositionPS : SV_Position;\n    float4 PositionWS : TEXCOORD0;\n    float3 NormalWS   : TEXCOORD1;\n    float4 Diffuse    : COLOR0;\n};\n\nstruct VSOutputPixelLightingTx\n{\n    float4 PositionPS : SV_Position;\n    float2 TexCoord   : TEXCOORD0;\n    float4 PositionWS : TEXCOORD1;\n    float3 NormalWS   : TEXCOORD2;\n    float4 Diffuse    : COLOR0;\n};\n\nstruct VSOutputTx2\n{\n    float4 PositionPS : SV_Position;\n    float4 Diffuse    : COLOR0;\n    float4 Specular   : COLOR1;\n    float2 TexCoord   : TEXCOORD0;\n    float2 TexCoord2  : TEXCOORD1;\n};\n\nstruct VSOutputTx2NoFog\n{\n    float4 PositionPS : SV_Position;\n    float4 Diffuse    : COLOR0;\n    float2 TexCoord   : TEXCOORD0;\n    float2 TexCoord2  : TEXCOORD1;\n};\n\nstruct VSOutputTxEnvMap\n{\n    float4 PositionPS : SV_Position;\n    float4 Diffuse    : COLOR0;\n    float4 Specular   : COLOR1;\n    float2 TexCoord   : TEXCOORD0;\n    float3 EnvCoord   : TEXCOORD1;\n};\n"
  },
  {
    "path": "OpenNFS1/Dashboards/Dashboard.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\n\r\nusing System.Text;\r\nusing System.IO;\r\nusing OpenNFS1.Parsers;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing OpenNFS1.Physics;\r\nusing System.Diagnostics;\r\nusing Microsoft.Xna.Framework.Input;\r\nusing OpenNFS1.Vehicles;\r\n\r\nnamespace OpenNFS1.Dashboards\r\n{\r\n\tclass Dashboard\r\n\t{\r\n        protected DrivableVehicle _car;\r\n        //protected Texture2D _instrumentLine;\r\n        GearboxAnimation _gearBoxAnimation;\r\n        public bool IsVisible { get; set; }\r\n\t\tDashboardDescription _descriptor;\r\n        \r\n        BitmapEntry Dash, GearGate, GearKnob, Wstr, Wl06, Wl14, Wl22, Wl32, Wl45, Wr06, Wr14, Wr22, Wr32, Wr45;\r\n\t\tBitmapEntry Leather1, Leather2, Leather3;\r\n\r\n\t\tstatic Texture2D _tachLineTexture;\r\n\r\n\t\tpublic Dashboard(DrivableVehicle car, DashboardDescription descriptor)\r\n        {\r\n            _car = car;\r\n\t\t\t_descriptor = descriptor;\r\n            _car.Motor.Gearbox.GearChangeStarted += new EventHandler(Gearbox_GearChangeStarted);\r\n            \r\n            _gearBoxAnimation = new GearboxAnimation();\r\n            \r\n            //_instrumentLine = Engine.Instance.ContentManager.Load<Texture2D>(\"Content\\\\SpeedoLine\");\r\n\t\t\tif (_tachLineTexture == null)\r\n\t\t\t{\r\n\t\t\t\t_tachLineTexture = new Texture2D(Engine.Instance.Device, (int)3, 25);\r\n\t\t\t\tColor[] pixels = new Color[_tachLineTexture.Width * _tachLineTexture.Height];\r\n\t\t\t\tfor (int i = 0; i < pixels.Length; i++) pixels[i] = Color.Red;\r\n\t\t\t\t_tachLineTexture.SetData<Color>(pixels);\r\n\t\t\t}\r\n\r\n\t\t\tFshFile fsh = new FshFile(Path.Combine(@\"SIMDATA\\DASH\", descriptor.Filename));\r\n\t\t\tvar bitmaps = fsh.Header;\r\n\r\n            Dash = bitmaps.FindByName(\"dash\");\r\n            GearGate = bitmaps.FindByName(\"gate\");\r\n            GearKnob = bitmaps.FindByName(\"nob1\");\r\n\r\n            Leather1 = bitmaps.FindByName(\"lth1\");\r\n            Leather2 = bitmaps.FindByName(\"lth2\");\r\n            Leather3 = bitmaps.FindByName(\"lth3\");\r\n\r\n\t\t\t//steering wheel images, from straight to left, then to the right.  Not all cars have all steering angles\r\n            Wstr = bitmaps.FindByName(\"wstr\");\r\n            Wl06 = bitmaps.FindByName(\"wl06\");\r\n            Wl14 = bitmaps.FindByName(\"wl14\");\r\n            Wl22 = bitmaps.FindByName(\"wl22\");\r\n            Wl32 = bitmaps.FindByName(\"wl32\");\r\n            if (Wl32 == null)\r\n                Wl32 = Wl22;\r\n            Wl45 = bitmaps.FindByName(\"wl45\");\r\n            if (Wl45 == null)\r\n                Wl45 = Wl32;\r\n            Wr06 = bitmaps.FindByName(\"wr06\");\r\n            Wr14 = bitmaps.FindByName(\"wr14\");\r\n            Wr22 = bitmaps.FindByName(\"wr22\");\r\n            Wr32 = bitmaps.FindByName(\"wr32\");\r\n            if (Wr32 == null)\r\n                Wr32 = Wr22;\r\n            Wr45 = bitmaps.FindByName(\"wr45\");\r\n            if (Wr45 == null)\r\n                Wr45 = Wr32;\r\n        }\r\n\r\n\r\n        void Gearbox_GearChangeStarted(object sender, EventArgs e)\r\n        {\r\n            if (IsVisible)\r\n            {\r\n                _gearBoxAnimation.Current = _car.Motor.Gearbox.CurrentGear + 1;\r\n                _gearBoxAnimation.Next = _car.Motor.Gearbox.NextGear + 1;\r\n            }\r\n        }\r\n\r\n\t\tpublic void Update(GameTime gameTime)\r\n\t\t{\r\n\t\t\t_gearBoxAnimation.Update(gameTime);\r\n\t\t\tif (Engine.Instance.Input.IsKeyDown(Keys.I))\r\n\t\t\t\t_descriptor.TachPosition.Y += Engine.Instance.FrameTime * 20;\r\n\t\t\tif (Engine.Instance.Input.IsKeyDown(Keys.K))\r\n\t\t\t\t_descriptor.TachPosition.Y -= Engine.Instance.FrameTime * 20;\r\n\t\t\tif (Engine.Instance.Input.IsKeyDown(Keys.J))\r\n\t\t\t\t_descriptor.TachPosition.X -= Engine.Instance.FrameTime * 20;\r\n\t\t\tif (Engine.Instance.Input.IsKeyDown(Keys.L))\r\n\t\t\t\t_descriptor.TachPosition.X += Engine.Instance.FrameTime * 20;\r\n\t\t\tif (Engine.Instance.Input.IsKeyDown(Keys.O))\r\n\t\t\t\t_descriptor.TachNeedleLength -= Engine.Instance.FrameTime;\r\n\r\n\t\t\tDebug.WriteLine((int)_descriptor.TachPosition.X + \", \" + (int)_descriptor.TachPosition.Y + \", \" + _descriptor.TachNeedleLength);\r\n\t\t}\r\n\r\n        public void Render()\r\n        {\r\n            Engine.Instance.SpriteBatch.Draw(Dash.Texture, Dash.GetDisplayAt(), Color.White);\r\n\r\n            if (_gearBoxAnimation.IsAnimating)\r\n            {\r\n                RenderGearstick();\r\n            }\r\n\r\n\t\t\tColor color = new Color(165, 0, 0, 255);\r\n\t\t\tfloat rpmFactor = _car.Motor.Rpm / _car.Motor.RedlineRpm;\r\n\t\t\tVector2 revCounterPosition = _descriptor.TachPosition;\r\n\t\t\tfloat rotation = (float)(rpmFactor * Math.PI * _descriptor.TachRpmMultiplier) - _descriptor.TachIdlePosition;\r\n\r\n\t\t\tRenderSteeringWheel();\r\n\r\n\t\t\tEngine.Instance.SpriteBatch.Draw(_tachLineTexture, revCounterPosition, null, color, rotation, new Vector2(1.5f, 25), new Vector2(0.8f, _descriptor.TachNeedleLength), SpriteEffects.None, 0);\r\n        }\r\n\r\n        public void RenderGearstick()\r\n        {\r\n\t\t\tVector2 gatePos = GearGate.GetDisplayAt();\r\n\t\t\tVector2 gateCenter = gatePos + new Vector2(GearGate.Texture.Width, GearGate.Texture.Height) / 2;\r\n\t\t\tEngine.Instance.SpriteBatch.Draw(GearGate.Texture, gatePos, Color.White);\r\n\r\n\t\t\tvar gearAnim = _gearBoxAnimation.CurrentPosition;\r\n\t\t\tVector2 offset;\r\n\r\n\t\t\t// leather gearstick boot\r\n\t\t\tif (Leather1 != null)\r\n\t\t\t{\t\r\n\t\t\t\toffset = new Vector2(Leather1.Texture.Width, Leather1.Texture.Height) / 2;\r\n\t\t\t\tEngine.Instance.SpriteBatch.Draw(Leather1.Texture, gateCenter - offset + gearAnim * 0.1f, Color.White);\r\n\t\t\t\toffset = new Vector2(Leather2.Texture.Width, Leather2.Texture.Height) / 2;\r\n\t\t\t\tEngine.Instance.SpriteBatch.Draw(Leather2.Texture, gateCenter - offset + gearAnim * 0.4f, Color.White);\r\n\t\t\t\toffset = new Vector2(Leather3.Texture.Width, Leather3.Texture.Height) / 2;\r\n\t\t\t\tEngine.Instance.SpriteBatch.Draw(Leather3.Texture, gateCenter - offset + gearAnim * 0.7f, Color.White);\r\n\t\t\t}\r\n\r\n\t\t\toffset = new Vector2(GearKnob.Texture.Width, GearKnob.Texture.Height) / 2;\t\r\n\t\t\tEngine.Instance.SpriteBatch.Draw(GearKnob.Texture, gateCenter - offset + gearAnim, Color.White);\r\n        }\r\n\r\n        public void RenderSteeringWheel()\r\n        {\r\n            float steeringFactor = _car._steeringWheel / Vehicle.MaxSteeringLock;\r\n\r\n            if (steeringFactor < -0.8f)\r\n                Engine.Instance.SpriteBatch.Draw(Wl45.Texture, Wl45.GetDisplayAt(), Color.White);\r\n            else if (steeringFactor < -0.64f)\r\n                Engine.Instance.SpriteBatch.Draw(Wl32.Texture, Wl32.GetDisplayAt(), Color.White);\r\n            else if (steeringFactor < -0.48f)\r\n                Engine.Instance.SpriteBatch.Draw(Wl22.Texture, Wl22.GetDisplayAt(), Color.White);\r\n            else if (steeringFactor < -0.32f)\r\n                Engine.Instance.SpriteBatch.Draw(Wl14.Texture, Wl14.GetDisplayAt(), Color.White);\r\n            else if (steeringFactor < -0.16f)\r\n                Engine.Instance.SpriteBatch.Draw(Wl06.Texture, Wl06.GetDisplayAt(), Color.White);\r\n\r\n            else if (steeringFactor > 0.8f)\r\n                Engine.Instance.SpriteBatch.Draw(Wr45.Texture, Wr45.GetDisplayAt(), Color.White);\r\n            else if (steeringFactor > 0.64f)\r\n                Engine.Instance.SpriteBatch.Draw(Wr32.Texture, Wr32.GetDisplayAt(), Color.White);\r\n            else if (steeringFactor > 0.48f)\r\n                Engine.Instance.SpriteBatch.Draw(Wr22.Texture, Wr22.GetDisplayAt(), Color.White);\r\n            else if (steeringFactor > 0.32f)\r\n                Engine.Instance.SpriteBatch.Draw(Wr14.Texture, Wr14.GetDisplayAt(), Color.White);\r\n            else if (steeringFactor > 0.16f)\r\n                Engine.Instance.SpriteBatch.Draw(Wr06.Texture, Wr06.GetDisplayAt(), Color.White);\r\n            else\r\n                Engine.Instance.SpriteBatch.Draw(Wstr.Texture, Wstr.GetDisplayAt(), Color.White);\r\n        }\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Dashboards/DashboardDescription.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace OpenNFS1.Dashboards\r\n{\r\n\tclass DashboardDescription\r\n\t{\r\n\t\tpublic string Filename;\r\n\t\tpublic Vector2 TachPosition;\r\n\t\tpublic float TachRpmMultiplier;\r\n\t\tpublic float TachIdlePosition;\r\n\t\tpublic float TachNeedleLength;\r\n\r\n\t\tpublic static List<DashboardDescription> Descriptions = new List<DashboardDescription>();\r\n\r\n\t\tstatic DashboardDescription()\r\n\t\t{\r\n\t\t\t//ZR1\r\n\t\t\tDescriptions.Add(new DashboardDescription\r\n\t\t\t{\r\n\t\t\t\tFilename = \"czr1dh.fsh\",\r\n\t\t\t\tTachPosition = new Vector2(277, 398),\r\n\t\t\t\tTachRpmMultiplier = 0.54f,\r\n\t\t\t\tTachIdlePosition = 1.9f,\r\n\t\t\t\tTachNeedleLength = 1.7f\r\n\t\t\t});\r\n\r\n\t\t\t//Diablo\r\n\t\t\tDescriptions.Add(new DashboardDescription\r\n\t\t\t{\r\n\t\t\t\tFilename = \"ldiabldh.fsh\",\r\n\t\t\t\tTachPosition = new Vector2(277, 398),\r\n\t\t\t\tTachRpmMultiplier = 1.25f,\r\n\t\t\t\tTachIdlePosition = 2.42f,\r\n\t\t\t\tTachNeedleLength = 1.8f\r\n\t\t\t});\r\n\t\t\t//Viper\r\n\t\t\tDescriptions.Add(new DashboardDescription\r\n\t\t\t{\r\n\t\t\t\tFilename = \"dviperdh.fsh\",\r\n\t\t\t\tTachPosition = new Vector2(417, 364),\r\n\t\t\t\tTachRpmMultiplier = 1f,\r\n\t\t\t\tTachIdlePosition = 2.62f,\r\n\t\t\t\tTachNeedleLength = 1.26f\r\n\t\t\t});\r\n\t\t\t//F512\r\n\t\t\tDescriptions.Add(new DashboardDescription\r\n\t\t\t{\r\n\t\t\t\tFilename = \"f512trdh.fsh\",\r\n\t\t\t\tTachPosition = new Vector2(382, 363),\r\n\t\t\t\tTachRpmMultiplier = 1.3f,\r\n\t\t\t\tTachIdlePosition = 2.52f,\r\n\t\t\t\tTachNeedleLength = 0.9f\r\n\t\t\t});\r\n\t\t\t//NSX\r\n\t\t\tDescriptions.Add(new DashboardDescription\r\n\t\t\t{\r\n\t\t\t\tFilename = \"ansxdh.fsh\",\r\n\t\t\t\tTachPosition = new Vector2(288, 362),\r\n\t\t\t\tTachRpmMultiplier = 1.3f,\r\n\t\t\t\tTachIdlePosition = 2.6f,\r\n\t\t\t\tTachNeedleLength = 1.1f\r\n\t\t\t});\r\n\t\t\t//911\r\n\t\t\tDescriptions.Add(new DashboardDescription\r\n\t\t\t{\r\n\t\t\t\tFilename = \"p911dh.fsh\",\r\n\t\t\t\tTachPosition = new Vector2(321, 361),\r\n\t\t\t\tTachRpmMultiplier = 1.37f,\r\n\t\t\t\tTachIdlePosition = 2.61f,\r\n\t\t\t\tTachNeedleLength = 1f\r\n\t\t\t});\r\n\t\t\t//RX7\r\n\t\t\tDescriptions.Add(new DashboardDescription\r\n\t\t\t{\r\n\t\t\t\tFilename = \"mrx7dh.fsh\",\r\n\t\t\t\tTachPosition = new Vector2(317, 361),\r\n\t\t\t\tTachRpmMultiplier = 1.5f,\r\n\t\t\t\tTachIdlePosition = 2.62f,\r\n\t\t\t\tTachNeedleLength = 0.95f\r\n\t\t\t});\r\n\t\t\t//Supra\r\n\t\t\tDescriptions.Add(new DashboardDescription\r\n\t\t\t{\r\n\t\t\t\tFilename = \"tsupradh.fsh\",\r\n\t\t\t\tTachPosition = new Vector2(324, 354),\r\n\t\t\t\tTachRpmMultiplier = 1.28f,\r\n\t\t\t\tTachIdlePosition = 2.4f,\r\n\t\t\t\tTachNeedleLength = 1f\r\n\t\t\t});\r\n\t\t\t//Warrior\r\n\t\t\tDescriptions.Add(new DashboardDescription\r\n\t\t\t{\r\n\t\t\t\tFilename = \"traffcdh.fsh\",\r\n\t\t\t\tTachPosition = new Vector2(319, 376),\r\n\t\t\t\tTachRpmMultiplier = 0.63f,\r\n\t\t\t\tTachIdlePosition = 1.8f,\r\n\t\t\t\tTachNeedleLength = 1.35f\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Dashboards/GearboxAnimation.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\n\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace OpenNFS1.Dashboards\r\n{\r\n    enum AnimationStatus\r\n    {\r\n        ToNeutral,\r\n        ToX,\r\n        ToY\r\n    }\r\n\r\n    class GearboxAnimation\r\n    {\r\n        List<Vector2> _gearPositions;\r\n        public int Current, Next;\r\n        Vector2 _lastPos, _currentPosition;\r\n        float _amount = 0;\r\n        AnimationStatus _status;\r\n        float _waitAtEndOfAnimationTime;\r\n\r\n        public GearboxAnimation()\r\n        {\r\n\t\t\tfloat boundX = 20, boundY = 30;\r\n            _gearPositions = new List<Vector2>();\r\n            _gearPositions.Add(new Vector2(boundX, boundY));\r\n            _gearPositions.Add(new Vector2(0, 0));\r\n            _gearPositions.Add(new Vector2(-boundX, -boundY));\r\n            _gearPositions.Add(new Vector2(-boundX, boundY));\r\n            _gearPositions.Add(new Vector2(0, -boundY));\r\n            _gearPositions.Add(new Vector2(0, boundY));\r\n            _gearPositions.Add(new Vector2(boundX, -boundY));\r\n            _gearPositions.Add(new Vector2(boundX, boundY));\r\n\r\n            _currentPosition = _gearPositions[2];\r\n            Current = 2;\r\n            Next = 2;\r\n        }\r\n\r\n        public Vector2 CurrentPosition\r\n        {\r\n            get { return _currentPosition; }\r\n            set { _currentPosition = value; }\r\n        }\r\n        public bool IsAnimating\r\n        {\r\n            get { return Current != Next || _waitAtEndOfAnimationTime > 0; }\r\n        }\r\n\r\n        public void Update(GameTime gameTime)\r\n        {\r\n            if (Current != Next)\r\n            {\r\n                if (_currentPosition.Y != 0 && _status == AnimationStatus.ToNeutral)\r\n                {\r\n                    _currentPosition.Y = MathHelper.Lerp(_gearPositions[Current].Y, 0, _amount);\r\n                    if (_amount >= 1)\r\n                    {\r\n                        _currentPosition.Y = 0;\r\n                        _amount = 0;\r\n                        _lastPos = _currentPosition;\r\n                    }\r\n                }\r\n                else if (_currentPosition.X != _gearPositions[Next].X)\r\n                {\r\n                    _currentPosition.X = MathHelper.Lerp(_lastPos.X, _gearPositions[Next].X, _amount);\r\n                    if (_amount >= 1)\r\n                    {\r\n                        _currentPosition.X = _gearPositions[Next].X;\r\n                        _amount = 0;\r\n                        _lastPos = _currentPosition;\r\n                    }\r\n                }\r\n\r\n                else if (_currentPosition.Y != _gearPositions[Next].Y)\r\n                {\r\n                    _status = AnimationStatus.ToY;\r\n                    _currentPosition.Y = MathHelper.Lerp(_lastPos.Y, _gearPositions[Next].Y, _amount);\r\n                    if (_amount >= 1)\r\n                    {\r\n                        _currentPosition.Y = _gearPositions[Next].Y;\r\n                        _amount = 0;\r\n                    }\r\n                }\r\n                else if (_currentPosition.X == _gearPositions[Next].X\r\n                    && _currentPosition.Y == _gearPositions[Next].Y)\r\n                {\r\n                    _status = AnimationStatus.ToNeutral;\r\n                    Current = Next;\r\n                    _waitAtEndOfAnimationTime = 0.5f;\r\n                }\r\n\r\n                _amount += (float)gameTime.ElapsedGameTime.TotalSeconds * (_gearPositions[Current].X != _gearPositions[Next].X ? 7.5f : 5.5f);\r\n            }\r\n\r\n            if (_waitAtEndOfAnimationTime >= 0)\r\n                _waitAtEndOfAnimationTime -= (float)gameTime.ElapsedGameTime.TotalSeconds;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Game1.cs",
    "content": "using System;\r\nusing System.IO;\r\nusing System.Runtime.InteropServices;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing GameEngine;\r\nusing OpenNFS1.UI.Screens;\r\n\r\nnamespace OpenNFS1\r\n{\r\n\t/// <summary>\r\n\t/// This is the main type for your game\r\n\t/// </summary>\r\n\tclass Game1 : Microsoft.Xna.Framework.Game\r\n\t{\r\n\t\tGraphicsDeviceManager _graphics;\r\n\t\tRenderTarget2D _renderTarget;\r\n\r\n\t\t[DllImport(\"user32.dll\")]\r\n\t\tprivate static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndIntertAfter,\r\n\t\t\tint X, int Y, int cx, int cy, int uFlags);\r\n\t\t\r\n\t\tpublic Game1()\r\n\t\t{\r\n\t\t\t_graphics = new GraphicsDeviceManager(this);\r\n\t\t\tContent.RootDirectory = \"Content\";\r\n\r\n\t\t\tGameConfig.Load();\r\n\r\n\t\t\tif (GameConfig.FullScreen)\r\n\t\t\t{\r\n\t\t\t\t_graphics.PreferredBackBufferWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;\r\n\t\t\t\t_graphics.PreferredBackBufferHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t_graphics.PreferredBackBufferWidth = 800;\r\n\t\t\t\t_graphics.PreferredBackBufferHeight = 600;\r\n\t\t\t}\r\n\t\t\t_graphics.PreferredDepthStencilFormat = DepthFormat.Depth24;\r\n            _graphics.PreferMultiSampling = true;\r\n\t\t\t_graphics.IsFullScreen = false;\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Allows the game to perform any initialization it needs to before starting to run.\r\n\t\t/// This is where it can query for any required services and load any non-graphic\r\n\t\t/// related content.  Calling base.Initialize will enumerate through any components\r\n\t\t/// and initialize them as well.\r\n\t\t/// </summary>\r\n\t\tprotected override void Initialize()\r\n\t\t{\r\n\t\t\tbase.Initialize();\r\n\r\n\t\t\tif (GameConfig.FullScreen)\r\n\t\t\t{\r\n\t\t\t\t// hack to move window to top-left..\r\n\t\t\t\tType type = typeof(OpenTKGameWindow);\r\n\t\t\t\tSystem.Reflection.FieldInfo field = type.GetField(\"window\", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);\r\n\t\t\t\tOpenTK.GameWindow window = (OpenTK.GameWindow)field.GetValue(this.Window);\r\n\t\t\t\tthis.Window.IsBorderless = true;\r\n\t\t\t\twindow.X = 0;\r\n\t\t\t\twindow.Y = 0;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\t/// <summary>\r\n\t\t/// LoadContent will be called once per game and is the place to load\r\n\t\t/// all of your content.\r\n\t\t/// </summary>\r\n\t\tprotected override void LoadContent()\r\n\t\t{\r\n\t\t\tEngine.Create(this, _graphics);\r\n\r\n\t\t\t_renderTarget = new RenderTarget2D(Engine.Instance.Device, 640, 480, false, _graphics.GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24, 1, RenderTargetUsage.DiscardContents);\r\n\t\t\tEngine.Instance.Device.SetRenderTarget(_renderTarget);\r\n\t\t\tEngine.Instance.ScreenSize = new Vector2(GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width, GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height);\r\n\r\n\t\t\tEngine.Instance.Screen = new OpenNFS1SplashScreen();\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// UnloadContent will be called once per game and is the place to unload\r\n\t\t/// all content.\r\n\t\t/// </summary>\r\n\t\tprotected override void UnloadContent()\r\n\t\t{\r\n\t\t\tEngine.Instance.ContentManager.Unload();\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// Allows the game to run logic such as updating the world,\r\n\t\t/// checking for collisions, gathering input, and playing audio.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"gameTime\">Provides a snapshot of timing values.</param>\r\n\t\tprotected override void Update(GameTime gameTime)\r\n\t\t{\r\n\t\t\tbase.Update(gameTime);\r\n\t\t}\r\n\r\n\t\t/// <summary>\r\n\t\t/// This is called when the game should draw itself.\r\n\t\t/// </summary>\r\n\t\t/// <param name=\"gameTime\">Provides a snapshot of timing values.</param>\r\n\t\tprotected override void Draw(GameTime gameTime)\r\n\t\t{\r\n\t\t\tEngine.Instance.Device.SetRenderTarget(_renderTarget);\r\n\r\n\t\t\tColor c = new Color(0.15f, 0.15f, 0.15f);\r\n\t\t\t_graphics.GraphicsDevice.Clear(c);\r\n\r\n\t\t\tbase.Draw(gameTime);\r\n\r\n\t\t\tEngine.Instance.Device.SetRenderTarget(null);\r\n\r\n\t\t\tusing (SpriteBatch sprite = new SpriteBatch(Engine.Instance.Device))\r\n\t\t\t{\r\n\t\t\t\tRectangle r = Window.ClientBounds;\r\n\t\t\t\tif (GameConfig.FullScreen)\r\n\t\t\t\t{\r\n\t\t\t\t\t// retain original 4:3 aspect ratio\r\n\t\t\t\t\tint originalWith = r.Width;\r\n\t\t\t\t\tint w = (int)((4f / 3f) * r.Height);\r\n\t\t\t\t\tr.Width = w;\r\n\t\t\t\t\tr.X = originalWith / 2 - w / 2;\r\n\t\t\t\t}\r\n\t\t\t\tsprite.Begin();\r\n\t\t\t\tsprite.Draw(_renderTarget, r, Color.White);\r\n\t\t\t\tsprite.End();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/GameConfig.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\n\r\nusing System.Text;\r\nusing OpenNFS1.Physics;\r\nusing Newtonsoft.Json;\r\nusing Newtonsoft.Json.Linq;\r\nusing System.IO;\r\nusing Microsoft.Xna.Framework;\r\nusing OpenNFS1.Vehicles;\r\nusing OpenNFS1.Parsers.Track;\r\nusing Microsoft.Xna.Framework.Graphics;\r\n\r\nnamespace OpenNFS1\r\n{\r\n    static class GameConfig\r\n    {\r\n\t\t// constants, shouldnt be here really\r\n\t\tpublic const float MeshScale = 0.040f;\r\n\t\tpublic const float TerrainScale = 0.000080f;\r\n\t\tpublic static readonly float FOV = MathHelper.ToRadians(65);\r\n\t\tpublic const float MaxSegmentRenderCount = 50;\r\n\t\tpublic static readonly SamplerState WrapSampler = SamplerState.AnisotropicWrap;\r\n\r\n\t\t// Set while navigating through menus\r\n        public static VehicleDescription SelectedVehicle;\r\n        public static TrackDescription SelectedTrackDescription;\r\n\t\tpublic static Track CurrentTrack;\r\n\t\tpublic static bool ManualGearbox { get; set; }\r\n\t\tpublic static bool AlternativeTimeOfDay { get; set; }\r\n\r\n\t\t// Loaded from config file\r\n\t\tpublic static bool FullScreen { get; set; }\r\n\t\tpublic static string CdDataPath { get; set; }\r\n        public static bool Render2dScenery = true, Render3dScenery = true;\r\n        public static bool RenderOnlyPhysicalTrack = true;\r\n        public static int DrawDistance { get; set; }\r\n\t\tpublic static bool RespectOpenRoadCheckpoints { get; set; }\r\n\t\tpublic static bool DrawDebugInfo { get; set; }\r\n\r\n\t\t\r\n\r\n        static GameConfig()\r\n        {\r\n\t\t\t\r\n        }\r\n\r\n\t\tpublic static void Load()\r\n\t\t{\r\n\t\t\tJObject o1 = JObject.Parse(File.ReadAllText(@\"gameconfig.json\"));\r\n\t\t\tFullScreen = o1.Value<bool>(\"fullScreen\");\r\n\t\t\tCdDataPath = o1.Value<string>(\"cdDataPath\");\r\n\t\t\tDrawDistance = o1.Value<int>(\"drawDistance\");\r\n\t\t\tRespectOpenRoadCheckpoints = o1.Value<bool>(\"respectOpenRoadCheckpoints\");\r\n\t\t\tDrawDebugInfo = o1.Value<bool>(\"drawDebugInfo\");\r\n\t\t}\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Mesh.cs",
    "content": "﻿using Microsoft.Xna.Framework.Graphics;\r\nusing OpenNFS1.Parsers;\r\nusing GameEngine;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace OpenNFS1\r\n{\r\n\tpublic static class MeshCache\r\n\t{\r\n\r\n\t}\r\n\r\n\tclass Mesh\r\n\t{\r\n\t\tprotected List<Polygon> _polys { get; set; }\r\n\t\tprotected VertexBuffer _vertexBuffer;\r\n\t\tpublic string Identifier { get; private set; }\r\n\r\n\t\tpublic BoundingBox BoundingBox {get; private set;}\r\n\r\n\t\tpublic Mesh(MeshChunk meshChunk, BitmapChunk bmpChunk)\r\n\t\t{\r\n\t\t\t_polys = meshChunk.Polygons;\r\n\t\t\tIdentifier = meshChunk.Identifier;\r\n\t\t\tResolve(bmpChunk);\r\n\t\t\tBoundingBox = GetBoundingBox();\t\t\t\r\n\t\t}\r\n\r\n\t\tpublic void Resolve(BitmapChunk bitmapChunk)\r\n\t\t{\r\n\t\t\tif (_vertexBuffer != null)\r\n\t\t\t\treturn; //already resolved\r\n\r\n\t\t\tint vertCount = 0;\r\n\r\n\t\t\tList<VertexPositionTexture> allVerts = new List<VertexPositionTexture>();\r\n\t\t\tforeach (Polygon poly in _polys)\r\n\t\t\t{\r\n\t\t\t\tif (poly.TextureName != null)\r\n\t\t\t\t{\r\n\t\t\t\t\tpoly.ResolveTexture(bitmapChunk.FindByName(poly.TextureName));\r\n\t\t\t\t}\r\n\r\n\t\t\t\tpoly.VertexBufferIndex = vertCount;\r\n\t\t\t\tvertCount += poly.VertexCount;\r\n\t\t\t\tallVerts.AddRange(poly.GetVertices());\r\n\t\t\t}\r\n\r\n\t\t\t_vertexBuffer = new VertexBuffer(Engine.Instance.Device, typeof(VertexPositionTexture), vertCount, BufferUsage.WriteOnly);\r\n\t\t\t_vertexBuffer.SetData<VertexPositionTexture>(allVerts.ToArray());\r\n\t\t}\r\n\r\n\t\tpublic virtual void Render(Effect effect)\r\n\t\t{\r\n\t\t\tEngine.Instance.Device.SetVertexBuffer(_vertexBuffer);\r\n\r\n\t\t\teffect.CurrentTechnique.Passes[0].Apply();\r\n\r\n\t\t\tforeach (Polygon poly in _polys)\r\n\t\t\t{\r\n\t\t\t\tif (poly.VertexBufferIndex < 0)\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\tEngine.Instance.Device.Textures[0] = poly.Texture;\r\n\t\t\t\tEngine.Instance.Device.DrawPrimitives(PrimitiveType.TriangleList, poly.VertexBufferIndex, poly.VertexCount / 3);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate BoundingBox GetBoundingBox()\r\n\t\t{\r\n\t\t\tBoundingBox bb = new BoundingBox();\r\n\t\t\tbb.Min = new Microsoft.Xna.Framework.Vector3(float.MaxValue);\r\n\t\t\tbb.Max = new Microsoft.Xna.Framework.Vector3(-float.MaxValue);\r\n\t\t\tforeach (var poly in _polys)\r\n\t\t\t{\r\n\t\t\t\tforeach (var vert in poly.Vertices)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (vert.X < bb.Min.X) bb.Min.X = vert.X;\r\n\t\t\t\t\tif (vert.Y < bb.Min.Y) bb.Min.Y = vert.Y;\r\n\t\t\t\t\tif (vert.Z < bb.Min.Z) bb.Min.Z = vert.Z;\r\n\r\n\t\t\t\t\tif (vert.X > bb.Max.X) bb.Max.X = vert.X;\r\n\t\t\t\t\tif (vert.Y > bb.Max.Y) bb.Max.Y = vert.Y;\r\n\t\t\t\t\tif (vert.Z > bb.Max.Z) bb.Max.Z = vert.Z;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn bb;\r\n\t\t}\r\n\r\n\t\tpublic void Dispose()\r\n\t\t{\r\n\t\t\t_vertexBuffer.Dispose();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/ObjectShadow.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace OpenNFS1\r\n{\r\n\tclass ObjectShadow\r\n\t{\r\n\t\tstatic BasicEffect _effect;\r\n\t\tstatic VertexPositionColor[] _verts = new VertexPositionColor[4];\r\n\t\tstatic ObjectShadow()\r\n\t\t{\r\n\t\t\t_effect = new BasicEffect(Engine.Instance.Device);\r\n\t\t\t_effect.TextureEnabled = false;\r\n\t\t\t_effect.VertexColorEnabled = true;\r\n\r\n\t\t\tColor shadowColor = new Color(10, 10, 10, 150);\r\n\t\t\tfor (int i = 0; i < 4; i++)\r\n\t\t\t{\r\n\t\t\t\t_verts[i] = new VertexPositionColor(Vector3.Zero, shadowColor);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic static void Render(Vector3[] points, bool ignoreDepthBuffer)\r\n\t\t{\r\n\t\t\tfor (int i = 0; i < 4; i++)\r\n\t\t\t{\r\n\t\t\t\t_verts[i].Position = points[i];\r\n\t\t\t}\r\n\r\n\t\t\tGraphicsDevice device = Engine.Instance.Device;\r\n\r\n\t\t\t_effect.World = Matrix.Identity;\r\n\t\t\t_effect.View = Engine.Instance.Camera.View;\r\n\t\t\t_effect.Projection = Engine.Instance.Camera.Projection;\r\n\r\n\t\t\tdevice.DepthStencilState = ignoreDepthBuffer ? DepthStencilState.None : DepthStencilState.Default;\r\n\t\t\tdevice.BlendState = BlendState.AlphaBlend;\r\n\t\t\tdevice.RasterizerState = RasterizerState.CullCounterClockwise;\r\n\r\n\t\t\t_effect.CurrentTechnique.Passes[0].Apply();\r\n\r\n\t\t\tdevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleStrip, _verts, 0, 2);\r\n\r\n\t\t\t//device.RenderState.AlphaBlendEnable = false;\r\n\t\t\tdevice.BlendState = BlendState.Opaque;\r\n\t\t\tdevice.DepthStencilState = DepthStencilState.Default;\r\n\t\t\tdevice.RasterizerState = RasterizerState.CullNone;\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/OpenNFS1.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <PropertyGroup>\r\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\r\n    <Platform Condition=\" '$(Platform)' == '' \">x86</Platform>\r\n    <ProductVersion>8.0.30703</ProductVersion>\r\n    <SchemaVersion>2.0</SchemaVersion>\r\n    <ProjectGuid>{2BBFF57F-1C9D-430E-8343-D2662437114D}</ProjectGuid>\r\n    <OutputType>WinExe</OutputType>\r\n    <AppDesignerFolder>Properties</AppDesignerFolder>\r\n    <RootNamespace>OpenNFS1</RootNamespace>\r\n    <AssemblyName>OpenNFS1</AssemblyName>\r\n    <FileAlignment>512</FileAlignment>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|x86' \">\r\n    <PlatformTarget>x86</PlatformTarget>\r\n    <DebugSymbols>true</DebugSymbols>\r\n    <DebugType>full</DebugType>\r\n    <Optimize>false</Optimize>\r\n    <OutputPath>bin\\WindowsGL\\Debug\\</OutputPath>\r\n    <DefineConstants>DEBUG;TRACE;WINDOWS</DefineConstants>\r\n    <ErrorReport>prompt</ErrorReport>\r\n    <WarningLevel>4</WarningLevel>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|x86' \">\r\n    <PlatformTarget>x86</PlatformTarget>\r\n    <DebugType>pdbonly</DebugType>\r\n    <Optimize>true</Optimize>\r\n    <OutputPath>bin\\WindowsGL\\Release\\</OutputPath>\r\n    <DefineConstants>TRACE;WINDOWS</DefineConstants>\r\n    <ErrorReport>prompt</ErrorReport>\r\n    <WarningLevel>4</WarningLevel>\r\n  </PropertyGroup>\r\n  <PropertyGroup>\r\n    <ApplicationIcon>Icon.ico</ApplicationIcon>\r\n  </PropertyGroup>\r\n  <ItemGroup>\r\n    <Compile Include=\"Audio\\EnvironmentAudioProvider.cs\" />\r\n    <Compile Include=\"Audio\\VehicleAudioProvider.cs\" />\r\n    <Compile Include=\"Audio\\BnkVehicleAudioProvider.cs\" />\r\n    <Compile Include=\"AverageValue.cs\" />\r\n    <Compile Include=\"Dashboards\\Dashboard.cs\" />\r\n    <Compile Include=\"Dashboards\\DashboardDescription.cs\" />\r\n    <Compile Include=\"Dashboards\\GearboxAnimation.cs\" />\r\n    <Compile Include=\"Race\\PlayerRaceStats.cs\" />\r\n    <Compile Include=\"PlayerUI.cs\" />\r\n    <Compile Include=\"Game1.cs\" />\r\n    <Compile Include=\"GameConfig.cs\" />\r\n    <Compile Include=\"Parsers\\OpenRoadTrackfamFile.cs\" />\r\n    <Compile Include=\"Tracks\\TrackAssembler.cs\" />\r\n    <Compile Include=\"Parsers\\TrackfamFile.cs\" />\r\n    <Compile Include=\"Mesh.cs\" />\r\n    <Compile Include=\"ObjectShadow.cs\" />\r\n    <Compile Include=\"Parsers\\Audio\\BnkFile.cs\" />\r\n    <Compile Include=\"Parsers\\Audio\\WavWriter.cs\" />\r\n    <Compile Include=\"Parsers\\BaseChunk.cs\" />\r\n    <Compile Include=\"Parsers\\BitmapChunk.cs\" />\r\n    <Compile Include=\"Parsers\\BitmapLoader.cs\" />\r\n    <Compile Include=\"Parsers\\CfmFile.cs\" />\r\n    <Compile Include=\"Parsers\\FshFile.cs\" />\r\n    <Compile Include=\"Parsers\\HeaderChunk.cs\" />\r\n    <Compile Include=\"Parsers\\MeshChunk.cs\" />\r\n    <Compile Include=\"Polygon.cs\" />\r\n    <Compile Include=\"Parsers\\QfsFile.cs\" />\r\n    <Compile Include=\"Parsers\\TriFile.cs\" />\r\n    <Compile Include=\"Physics\\AutoGearbox.cs\" />\r\n    <Compile Include=\"Physics\\BaseGearbox.cs\" />\r\n    <Compile Include=\"Physics\\ManualGearbox.cs\" />\r\n    <Compile Include=\"Physics\\Motor.cs\" />\r\n    <Compile Include=\"Physics\\Spring.cs\" />\r\n    <Compile Include=\"Physics\\Vector3Helper.cs\" />\r\n    <Compile Include=\"Physics\\DrivableVehicle.cs\" />\r\n    <Compile Include=\"Physics\\VehicleFenceCollision.cs\" />\r\n    <Compile Include=\"Physics\\VehicleWheel.cs\" />\r\n    <Compile Include=\"Program.cs\" />\r\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\r\n    <Compile Include=\"Race\\Race.cs\" />\r\n    <Compile Include=\"Tracks\\SceneryObject.cs\" />\r\n    <Compile Include=\"Tracks\\TerrainRow.cs\" />\r\n    <Compile Include=\"Tracks\\TerrainSegment.cs\" />\r\n    <Compile Include=\"Tracks\\Track.cs\" />\r\n    <Compile Include=\"Tracks\\TrackBillboardModel.cs\" />\r\n    <Compile Include=\"Tracks\\TrackDescription.cs\" />\r\n    <Compile Include=\"Tracks\\TrackNode.cs\" />\r\n    <Compile Include=\"Tracks\\TrackObjectDescriptor.cs\" />\r\n    <Compile Include=\"Tracks\\TrackSkybox.cs\" />\r\n    <Compile Include=\"UI\\Screens\\ChooseDataDownloadScreen.cs\" />\r\n    <Compile Include=\"UI\\Screens\\DataDownloadScreen.cs\" />\r\n    <Compile Include=\"UI\\Screens\\OpenNFS1SplashScreen.cs\" />\r\n    <Compile Include=\"UI\\Screens\\RaceOptionsScreen.cs\" />\r\n    <Compile Include=\"Vehicles\\TyreSmokeParticleSystem.cs\" />\r\n    <Compile Include=\"UIController.cs\" />\r\n    <Compile Include=\"Race\\RaceUI.cs\" />\r\n    <Compile Include=\"UI\\Screens\\BaseUIScreen.cs\" />\r\n    <Compile Include=\"UI\\Screens\\DoRaceScreen.cs\" />\r\n    <Compile Include=\"UI\\Screens\\LoadRaceScreen.cs\" />\r\n    <Compile Include=\"UI\\Screens\\HomeScreen.cs\" />\r\n    <Compile Include=\"UI\\Screens\\RaceFinishedScreen.cs\" />\r\n    <Compile Include=\"UI\\Screens\\RacePausedScreen.cs\" />\r\n    <Compile Include=\"VehicleController.cs\" />\r\n    <Compile Include=\"Vehicles\\AI\\AIDriver.cs\" />\r\n    <Compile Include=\"Vehicles\\AI\\IDriver.cs\" />\r\n    <Compile Include=\"Vehicles\\PlayerDriver.cs\" />\r\n    <Compile Include=\"Vehicles\\AI\\TrafficDriver.cs\" />\r\n    <Compile Include=\"Vehicles\\CarMesh.cs\" />\r\n    <Compile Include=\"Vehicles\\CarModelCache.cs\" />\r\n    <Compile Include=\"Vehicles\\Vehicle.cs\" />\r\n    <Compile Include=\"Vehicles\\Traffic\\TrafficController.cs\" />\r\n    <Compile Include=\"Vehicles\\VehicleDescription.cs\" />\r\n    <Compile Include=\"Views\\BaseExternalView.cs\" />\r\n    <Compile Include=\"Views\\BumperView.cs\" />\r\n    <Compile Include=\"Views\\ChaseView.cs\" />\r\n    <Compile Include=\"Views\\DashboardView.cs\" />\r\n    <Compile Include=\"Views\\DebugView.cs\" />\r\n    <Compile Include=\"Views\\DropCameraView.cs\" />\r\n    <Compile Include=\"Views\\IView.cs\" />\r\n    <Compile Include=\"Vehicles\\WheelModel.cs\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Reference Include=\"Ionic.Zip.Reduced\">\r\n      <HintPath>..\\lib\\Ionic.Zip.Reduced.dll</HintPath>\r\n    </Reference>\r\n    <Reference Include=\"MonoGame.Framework, Version=3.1.2.0, Culture=neutral, processorArchitecture=MSIL\">\r\n      <SpecificVersion>False</SpecificVersion>\r\n      <HintPath>..\\lib\\MonoGame.Framework.dll</HintPath>\r\n    </Reference>\r\n    <Reference Include=\"Newtonsoft.Json\">\r\n      <HintPath>..\\lib\\Newtonsoft.Json.dll</HintPath>\r\n    </Reference>\r\n    <Reference Include=\"OpenTK, Version=1.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL\">\r\n      <SpecificVersion>False</SpecificVersion>\r\n      <HintPath>..\\lib\\OpenTK.dll</HintPath>\r\n    </Reference>\r\n    <Reference Include=\"System\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Content Include=\"..\\lib\\SDL.dll\">\r\n      <Link>SDL.dll</Link>\r\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\r\n    </Content>\r\n    <Content Include=\"Content\\smoke.png\">\r\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\r\n    </Content>\r\n    <Content Include=\"Content\\splash-screen.jpg\">\r\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\r\n    </Content>\r\n    <Content Include=\"Icon.ico\" />\r\n  </ItemGroup>\r\n  <ItemGroup />\r\n  <ItemGroup>\r\n    <None Include=\"gameconfig.json\">\r\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\r\n    </None>\r\n    <None Include=\"packages.config\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ProjectReference Include=\"..\\Engine\\GameEngine.csproj\">\r\n      <Project>{F66B2F9A-AF38-40F9-A094-522C823D04EE}</Project>\r\n      <Name>GameEngine</Name>\r\n    </ProjectReference>\r\n  </ItemGroup>\r\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\r\n  <Import Project=\"..\\packages\\MonoGame.Binaries.3.2.0\\build\\net40\\MonoGame.Binaries.targets\" Condition=\"Exists('..\\packages\\MonoGame.Binaries.3.2.0\\build\\net40\\MonoGame.Binaries.targets')\" />\r\n  <PropertyGroup>\r\n    <PreBuildEvent>\r\n    </PreBuildEvent>\r\n  </PropertyGroup>\r\n  <PropertyGroup>\r\n    <PostBuildEvent>copy /Y $(SolutionDir)PreBuilt_Content\\*.xnb $(TargetDir)Content</PostBuildEvent>\r\n  </PropertyGroup>\r\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \r\n       Other similar extension points exist, see Microsoft.Common.targets.\r\n  <Target Name=\"BeforeBuild\">\r\n  </Target>\r\n  <Target Name=\"AfterBuild\">\r\n  </Target>\r\n  -->\r\n</Project>"
  },
  {
    "path": "OpenNFS1/Parsers/Audio/BnkFile.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing System.IO;\r\nusing System.Diagnostics;\r\n\r\nnamespace OpenNFS1.Parsers.Audio\r\n{\r\n\tclass BnkSample\r\n\t{\r\n\t\tpublic int NbrSamples;\r\n\t\tpublic Int16 NbrChannels;\r\n\t\tpublic byte[] PCMData;\r\n\t\tpublic int SampleRate;\r\n\t\tpublic int LoopStart;\r\n\t}\r\n\r\n\t// Bnk files (in SOUNDBNK folder) contain audio samples\r\n\tclass BnkFile\r\n\t{\r\n\t\tstring _filename;\r\n\t\tpublic bool ForceMono = true;\r\n\t\tpublic List<BnkSample> Samples { get; private set; }\r\n\r\n\t\tpublic BnkFile(string filename)\r\n\t\t{\r\n\t\t\t_filename = filename;\r\n\t\t\tSamples = new List<BnkSample>();\r\n\t\t\tfilename = Path.Combine(GameConfig.CdDataPath, @\"Simdata\\Soundbnk\\\" + filename);\r\n\t\t\tBinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open));\r\n\t\t\tParse(reader);\r\n\t\t\treader.Close();\r\n\t\t}\r\n\r\n\t\tprivate void Parse(BinaryReader reader)\r\n\t\t{\r\n\t\t\tList<int> sampleOffsets = new List<int>();\r\n\t\t\tfor (int i = 0; i < 128; i++)\r\n\t\t\t{\r\n\t\t\t\tint sampleOffset = reader.ReadInt32();\r\n\t\t\t\tif (sampleOffset != 0)\r\n\t\t\t\t\tsampleOffsets.Add(sampleOffset);\r\n\t\t\t}\r\n\r\n\t\t\tint count = 0;\r\n\t\t\tforeach (int sampleOffset in sampleOffsets)\r\n\t\t\t{\r\n\t\t\t\treader.BaseStream.Position = sampleOffset;\r\n\t\t\t\tReadSample(reader, count++);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void ReadSample(BinaryReader reader, int sampleIndex)\r\n\t\t{\r\n\t\t\tBnkSample sample = new BnkSample();\r\n\t\t\treader.BaseStream.Position += 0x28;\r\n\t\t\tstring id = new string(reader.ReadChars(4));\r\n\t\t\tsample.SampleRate = reader.ReadInt32();\r\n\t\t\tbyte bytesPerSample = reader.ReadByte();\r\n\t\t\tsample.NbrChannels = reader.ReadByte();\r\n\t\t\tbyte compression = reader.ReadByte();\r\n\t\t\tbyte type = reader.ReadByte();\r\n\r\n\t\t\tint totalLength = reader.ReadInt32();\r\n\t\t\tsample.LoopStart = reader.ReadInt32();\r\n\t\t\tint loopLength = reader.ReadInt32();\r\n\t\t\tint dataOffset = reader.ReadInt32();\r\n\t\t\tint unk3 = reader.ReadInt32();\r\n\r\n\t\t\t// HACKS to avoid popping sounds.. Not sure why we need to do this.. maybe NFS finds values near\r\n\t\t\t// loop start/end which are the best match\r\n\t\t\t//if (_filename == \"SUPRA_SW.bnk\" && sampleIndex == 0)\r\n\t\t\t//\tloopLength += 10;\r\n\r\n\t\t\t//Debug.WriteLine(String.Format(\"{0}: sampleRate:{5} bps: {1} loopStart: {2} loopLen: {3} totalLen: {4}, offset: {6}, channels: {7}\",\r\n\t\t\t//\theaderIndex, bytesPerSample, sample.LoopStart, loopLength, sample.NbrSamples, sample.SampleRate, dataOffset, sample.NbrChannels));\r\n\r\n\t\t\treader.BaseStream.Position = dataOffset + (bytesPerSample * sample.NbrChannels * sample.LoopStart);\r\n\t\t\tsample.NbrSamples = loopLength == 0 ? totalLength : loopLength;\r\n\t\t\tbyte[] soundData = reader.ReadBytes(bytesPerSample * sample.NbrChannels * sample.NbrSamples);\r\n\r\n\t\t\tif (bytesPerSample == 1)  //convert 8 bit signed PCM data to unsigned\r\n\t\t\t{\r\n\t\t\t\tfor (int i = 0; i < soundData.Length; i++)\r\n\t\t\t\t\tsoundData[i] = (byte)(soundData[i] + 0x80);\r\n\t\t\t}\r\n\r\n\t\t\tif (ForceMono && sample.NbrChannels == 2)\r\n\t\t\t{\r\n\t\t\t\tMemoryStream ms = new MemoryStream();\r\n\t\t\t\tfor (int i = 0; i < soundData.Length; i += bytesPerSample * 2)\r\n\t\t\t\t{\r\n\t\t\t\t\tms.Write(soundData, i, bytesPerSample);\r\n\t\t\t\t}\r\n\t\t\t\tsoundData = ms.ToArray();\r\n\t\t\t\tsample.NbrChannels = 1;\r\n\t\t\t}\r\n\r\n\t\t\t/*\r\n\t\t\tif (sample.NbrChannels == 2)\r\n\t\t\t{\r\n\t\t\t\tMemoryStream ms = new MemoryStream();\r\n\t\t\t\tfor (int i = 0; i < soundData.Length - 4; i += 4)\r\n\t\t\t\t{\r\n\t\t\t\t\tms.Write(soundData, i, 2);\r\n\t\t\t\t}\r\n\t\t\t\tsoundData = ms.ToArray();\r\n\t\t\t\tsample.NbrChannels = 1;\r\n\r\n\t\t\t\tshort[] sdata = new short[soundData.Length / 2];\r\n\t\t\t\tBuffer.BlockCopy(soundData, 0, sdata, 0, soundData.Length);\r\n\t\t\t\tsoundData = new byte[sdata.Length * 2];\r\n\t\t\t\tBuffer.BlockCopy(sdata, 0, soundData, 0, loopLength * 2);\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif (sample.NbrChannels == 3)\r\n\t\t\t{\r\n\t\t\t\tshort lowestValue = short.MaxValue;\r\n\t\t\t\tint lowestValueSample = 0;\r\n\t\t\t\tshort[] sdata = new short[soundData.Length / 2];\r\n\t\t\t\tBuffer.BlockCopy(soundData, 0, sdata, 0, soundData.Length);\r\n\t\t\t\tfor (int i = loopLength; i < loopLength + 20; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (Math.Abs(sdata[i]) < Math.Abs(lowestValue))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tlowestValue = sdata[i];\r\n\t\t\t\t\t\tlowestValueSample = i;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tDebug.WriteLine(sdata[i]);\r\n\t\t\t\t}\r\n\t\t\t\tDebug.WriteLine(\"was \" + loopLength + \", now \" + lowestValueSample);\r\n\t\t\t\tloopLength = lowestValueSample;\r\n\t\t\t\tsoundData = new byte[lowestValueSample * 2];\r\n\t\t\t\tBuffer.BlockCopy(sdata, 0, soundData, 0, lowestValueSample * 2);\r\n\t\t\t}*/\r\n\r\n\t\t\tsample.PCMData = soundData;\r\n\t\t\tSamples.Add(sample);\r\n\t\t\t\r\n\t\t\t//WavWriter.Write(String.Format(\"c:\\\\temp\\\\test{0}.wav\", headerIndex),\r\n\t\t\t//\tnbrSamples, channels, soundData);\r\n\t\t\t\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Parsers/Audio/WavWriter.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing System.IO;\r\n\r\nnamespace OpenNFS1.Parsers.Audio\r\n{\r\n\tclass WavWriter\r\n\t{\r\n\t\tpublic WavWriter()\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tpublic static void Write(string filename, int samples, short channels, byte[] soundData)\r\n\t\t{\r\n\t\t\tFileStream stream = new FileStream(filename, FileMode.Create);\r\n\t\t\tBinaryWriter writer = new BinaryWriter(stream);\r\n\t\t\tint RIFF = 0x46464952;\r\n\t\t\tint WAVE = 0x45564157;\r\n\t\t\tint DATA = 0x61746164;\r\n\r\n\t\t\tint formatChunkSize = 16;\r\n\t\t\tint headerSize = 8;\r\n\t\t\tint format = 0x20746D66;\r\n\t\t\tshort formatType = 1;\r\n\r\n\t\t\tint samplesPerSecond = 16000; // 44100;\r\n\t\t\tshort bitsPerSample = 16;\r\n\t\t\tshort frameSize = (short)(channels * ((bitsPerSample + 7) / 8));\r\n\t\t\tint bytesPerSecond = samplesPerSecond * frameSize;\r\n\t\t\tint waveSize = 4;\t\t\t\r\n\t\t\tint dataChunkSize = samples * frameSize;\r\n\t\t\tint fileSize = waveSize + headerSize + formatChunkSize + headerSize + dataChunkSize;\r\n\r\n\t\t\twriter.Write(RIFF);\r\n\t\t\twriter.Write(fileSize);\r\n\t\t\twriter.Write(WAVE);\r\n\t\t\twriter.Write(format);\r\n\t\t\twriter.Write(formatChunkSize);\r\n\t\t\twriter.Write(formatType);\r\n\t\t\twriter.Write(channels);\r\n\t\t\twriter.Write(samplesPerSecond);\r\n\t\t\twriter.Write(bytesPerSecond);\r\n\t\t\twriter.Write(frameSize);\r\n\t\t\twriter.Write(bitsPerSample);\r\n\t\t\twriter.Write(DATA);\r\n\t\t\twriter.Write(dataChunkSize);\r\n\r\n\t\t\twriter.Write(soundData);\r\n\t\t\t\r\n\t\t\twriter.Close();\t\t\t\r\n\t\t}\r\n\t}\r\n}"
  },
  {
    "path": "OpenNFS1/Parsers/BaseChunk.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing System.IO;\r\n\r\nnamespace OpenNFS1.Parsers\r\n{\r\n    class BaseChunk\r\n    {\r\n        private long _offset;\r\n\r\n        public long Offset\r\n        {\r\n            get { return _offset; }\r\n            set { _offset = value; }\r\n        }\r\n\r\n        protected int _length;\r\n\r\n        public void Load(BinaryReader reader)\r\n        {\r\n            reader.BaseStream.Position = _offset;\r\n            Read(reader);\r\n\r\n        }\r\n        public virtual void Read(BinaryReader reader)\r\n        {\r\n            _offset = reader.BaseStream.Position - 4;\r\n            _length = reader.ReadInt32();\r\n        }\r\n\r\n        public void SkipHeader(BinaryReader reader)\r\n        {\r\n            reader.BaseStream.Position += 4;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Parsers/BitmapChunk.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing System.IO;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing GameEngine;\r\nusing System.Runtime.InteropServices;\r\nusing System.Diagnostics;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace OpenNFS1.Parsers\r\n{\r\n\tenum BitmapEntryType\r\n\t{\r\n\t\tUnknown = 0,\r\n\t\tTexture = 0x7b,\r\n\t\tDosPalette = 0x22,\r\n\t\tPalette = 0x24,\r\n\t\tTextureTrailer = 0x7c\r\n\t}\r\n\r\n\tclass BitmapEntry\r\n\t{\r\n\t\tpublic string Id;\r\n\t\tpublic int Offset;\r\n\t\tpublic int Length;\r\n\t\tpublic BitmapEntryType Type;\r\n\t\tpublic Texture2D Texture;\r\n\t\tpublic short[] Misc = new short[4];  //different purposes for different types of bitmaps\r\n\r\n\t\tpublic override string ToString()\r\n\t\t{\r\n\t\t\treturn Id;\r\n\t\t}\r\n\r\n\t\t// these misc fields are often used by UI bitmaps to define where they should be displayed\r\n\t\tpublic Vector2 GetDisplayAt()\r\n\t\t{\r\n\t\t\treturn new Vector2(Misc[2], Misc[3]);\r\n\t\t}\r\n\t}\r\n\r\n\tclass BitmapChunk : BaseChunk\r\n\t{\r\n\t\tconst int EntryHeaderLength = 16;\r\n\r\n\t\tList<BitmapEntry> _bitmaps = new List<BitmapEntry>();\r\n\r\n\t\tinternal List<BitmapEntry> Bitmaps\r\n\t\t{\r\n\t\t\tget { return _bitmaps; }\r\n\t\t}\r\n\r\n\t\tpublic int Index { get; set; }\r\n\t\t\r\n\t\tpublic bool EnableTextureAttachments { get; set; }  //fsh files use an extension to store detail alongside bitmaps, such as palettes etc.\r\n\r\n\t\tpublic delegate void TextureGeneratedHandler(BitmapEntry entry, byte[] palette, byte[] pixelData);\r\n\t\tpublic event TextureGeneratedHandler TextureGenerated;\r\n\r\n\t\tpublic static byte[] _palette;\r\n\t\tprivate static byte[] _lastPalette;  //some files don't have their own palettes, in that case, use the last palette we loaded\r\n\r\n\t\tpublic override void Read(BinaryReader reader)\r\n\t\t{\r\n\t\t\tbase.Read(reader);\r\n\r\n\t\t\tint itemCount = reader.ReadInt32();\r\n\t\t\tstring directoryName = new string(reader.ReadChars(4));\r\n\r\n\t\t\tDebug.WriteLine(\">> Loading bitmap chunk \" + directoryName);\r\n\r\n\t\t\tfor (int i = 0; i < itemCount; i++)\r\n\t\t\t{\r\n\t\t\t\tBitmapEntry entry = new BitmapEntry();\r\n\t\t\t\tentry.Id = new string(reader.ReadChars(4));\r\n\t\t\t\tentry.Offset = reader.ReadInt32();\r\n\t\t\t\t_bitmaps.Add(entry);\r\n\t\t\t}\r\n\r\n\t\t\t//Load global palette first\r\n\t\t\tforeach (BitmapEntry entry in _bitmaps)\r\n\t\t\t{\r\n\t\t\t\tif (entry.Id.ToUpper() == \"!PAL\")\r\n\t\t\t\t{\r\n\t\t\t\t\treader.BaseStream.Position = Offset + entry.Offset;\r\n\t\t\t\t\tReadEntry(reader, entry);\r\n\t\t\t\t\t_palette = _lastPalette;\r\n\t\t\t\t\tDebug.WriteLine(\"\\tLoaded global palette\");\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tfor (int i = 0; i < _bitmaps.Count; i++)\r\n\t\t\t{\r\n\t\t\t\tBitmapEntry entry = _bitmaps[i];\r\n\t\t\t\tif (entry.Type == BitmapEntryType.Unknown)\r\n\t\t\t\t{\r\n\t\t\t\t\treader.BaseStream.Position = Offset + entry.Offset;\r\n\t\t\t\t\tReadEntry(reader, entry);\r\n\t\t\t\t\tDebug.WriteLine(\"\\tLoaded bitmap \" + entry.Id);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void ReadEntry(BinaryReader reader, BitmapEntry entry)\r\n\t\t{\r\n\t\t\tint iCode = reader.ReadInt32();\r\n\r\n\t\t\tentry.Type = (BitmapEntryType)(iCode & 0x7F);\r\n\r\n\t\t\tentry.Length = iCode >> 8;\r\n\r\n\t\t\tint width = reader.ReadInt16();\r\n\t\t\tint height = reader.ReadInt16();\r\n\r\n\t\t\tif (entry.Type == BitmapEntryType.TextureTrailer)\r\n\t\t\t\treturn;\r\n\r\n\t\t\tentry.Misc[0] = reader.ReadInt16();\r\n\t\t\tentry.Misc[1] = reader.ReadInt16();\r\n\t\t\tentry.Misc[2] = reader.ReadInt16();\r\n\t\t\tentry.Misc[3] = reader.ReadInt16();\r\n\r\n\t\t\tswitch (entry.Type)\r\n\t\t\t{\r\n\t\t\t\tcase BitmapEntryType.DosPalette:\r\n\t\t\t\tcase BitmapEntryType.Palette:\r\n\t\t\t\t\t_lastPalette = LoadPalette(reader, entry.Type);\r\n\t\t\t\t\tentry.Type = BitmapEntryType.Palette;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase BitmapEntryType.Texture:\r\n\t\t\t\t\tReadTexture(reader, entry, width, height);\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void ReadTexture(BinaryReader reader, BitmapEntry entry, int width, int height)\r\n\t\t{\r\n\t\t\tbyte[] pixelData = reader.ReadBytes(width * height);\r\n\r\n\t\t\tif (EnableTextureAttachments)\r\n\t\t\t{\t\t\t\t\r\n\t\t\t\twhile (entry.Length > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar childEntry = new BitmapEntry();\r\n\t\t\t\t\tReadEntry(reader, childEntry);\r\n\t\t\t\t\tif (childEntry.Type == BitmapEntryType.DosPalette || childEntry.Type == BitmapEntryType.Palette)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t_palette = _lastPalette;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (childEntry.Length == 0)\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (_palette == null)\r\n\t\t\t{\r\n\t\t\t\t_palette = _lastPalette;\r\n\t\t\t}\r\n\t\t\tTextureGenerator tg = new TextureGenerator(_palette);\r\n\t\t\tentry.Texture = tg.Generate(pixelData, width, height);\r\n\t\t\tentry.Type = BitmapEntryType.Texture;\r\n\t\t\tif (TextureGenerated != null) TextureGenerated(entry, _palette, pixelData);\r\n\t\t}\r\n\r\n\r\n\t\tbyte[] LoadPalette(BinaryReader reader, BitmapEntryType type)\r\n\t\t{\r\n\t\t\tbyte[] pal = reader.ReadBytes(3 * 256);\r\n\r\n\t\t\tswitch (type)\r\n\t\t\t{\r\n\t\t\t\tcase BitmapEntryType.DosPalette:\r\n\t\t\t\t\t// 0x22 palettes are DOS-style. r,g,b values in range 0..64\r\n\t\t\t\t\tfor (int i = 0; i < 768; i++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tpal[i] = (byte)(pal[i] << 2);\r\n\t\t\t\t\t}\r\n\t\t\t\t\treturn pal;\r\n\r\n\r\n\t\t\t\tcase BitmapEntryType.Palette:\r\n\t\t\t\t\t// 0x24 palettes have r,g,b in range 0..255.  Nothing to do\r\n\t\t\t\t\treturn pal;\r\n\r\n\t\t\t\tdefault:\r\n\t\t\t\t\t//throw new NotImplementedException();\r\n\t\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic BitmapEntry FindByName(string name)\r\n\t\t{\r\n\t\t\treturn _bitmaps.Find(a => a.Id.Equals(name, StringComparison.InvariantCultureIgnoreCase));\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Parsers/BitmapLoader.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\nusing System.IO;\r\n\r\nnamespace OpenNFS1.Parsers\r\n{\r\n    class TextureGenerator\r\n    {\r\n        byte[] _palette;\r\n\r\n        public TextureGenerator(byte[] palette)\r\n        {\r\n            _palette = palette;\r\n        }\r\n\r\n\t\tpublic Texture2D Generate(byte[] pixelData, int width, int height)\r\n\t\t{\r\n\t\t\tTexture2D newTexture = new Texture2D(Engine.Instance.Device, width, height);\r\n\t\t\tnewTexture.SetData<byte>(GenerateImageData(pixelData, width, height));\r\n\t\t\treturn newTexture;\r\n\t\t}\r\n\r\n        private byte[] GenerateImageData(byte[] pixelData, int width, int height)\r\n        {\r\n\t\t\tVector3 mostUsed = Vector3.Zero;\r\n\r\n            int overhang = 0; // (4 - ((width * 4) % 4));\r\n            int stride = (width * 4) + overhang;\r\n\r\n            byte[] imgData = new byte[stride * height];\r\n            int curPosition = 0;\r\n            for (int i = 0; i < height; i++)\r\n            {\r\n                for (int x = 0; x < width; x++)\r\n                {\r\n                    byte pixel = pixelData[width * i + x];\r\n\r\n                    if (pixel == 0xFF)\r\n                    {\r\n                        //byte[] rgb = GetRGBForPixel(mostUsed);\r\n                        imgData[curPosition] = (byte)mostUsed.Z; // rgb[2];\r\n                        imgData[curPosition + 1] = (byte)mostUsed.Y; // rgb[1];\r\n                        imgData[curPosition + 2] = (byte)mostUsed.X; // rgb[0];\r\n                        imgData[curPosition + 3] = 0;\r\n                    }\r\n                    else\r\n                    {\r\n                        byte[] rgb = GetRGBForPixel(pixel);\r\n                        imgData[curPosition] = rgb[0];\r\n                        imgData[curPosition + 1] = rgb[1];\r\n                        imgData[curPosition + 2] = rgb[2];\r\n                        imgData[curPosition + 3] = 0xFF;\r\n                    }\r\n                    curPosition += 4;\r\n                }\r\n                curPosition += overhang;\r\n            }\r\n\r\n            return imgData;\r\n        }\r\n\r\n        private Vector3 GetMostUsedColour(string id, byte[] pixels)\r\n        {\r\n\t\t\tif (_palette == null) return new Vector3(255, 0, 255);\r\n\r\n            if (id.StartsWith(\"tyr\"))\r\n            {\r\n                return Vector3.Zero;\r\n            }\r\n\r\n            byte[] coloursUsed = new byte[256];\r\n            foreach (byte pixel in pixels)\r\n            {\r\n                if (pixel != 0xFF)\r\n                    coloursUsed[pixel]++;\r\n            }\r\n\r\n            int coloursUsedCount = 1;\r\n            Vector3 avgColour = Vector3.Zero;\r\n            for (int i = 0; i < 255; i++)\r\n            {\r\n                if (coloursUsed[i] > 0)\r\n                {\r\n                    avgColour += new Vector3(_palette[i * 3], _palette[i * 3 + 1], (int)_palette[i * 3 + 2]);\r\n                    coloursUsedCount++;\r\n                }\r\n            }\r\n            return avgColour / coloursUsedCount;\r\n        }\r\n\r\n        private byte[] GetRGBForPixel(int pixel)\r\n        {\r\n            if (_palette == null)\r\n                return new byte[] { 255, 0, 255 };\r\n\r\n            byte[] rgb = new byte[3];\r\n            rgb[0] = _palette[pixel * 3];\r\n            rgb[1] = _palette[pixel * 3 + 1];\r\n            rgb[2] = _palette[pixel * 3 + 2];\r\n            return rgb;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Parsers/CfmFile.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing System.IO;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework;\r\nusing OpenNFS1.Vehicles;\r\n\r\nnamespace OpenNFS1.Parsers\r\n{\r\n\r\n\t// .CFM files contain the vertices and textures for cars.\r\n    class CfmFile\r\n    {\r\n\t\tpublic CarMesh Mesh { get; private set; }\r\n\t\tprivate Color _brakeColor;\r\n\r\n\r\n\t\t// A CFM file can contain either a 'full' model (drivable, has wheel definitions etc), or a traffic model which is \r\n\t\t// only a static model and not drivable\r\n        public CfmFile(string filename)\r\n        {\r\n\t\t\tParse(filename);\r\n        }\r\n\r\n\t\tprivate void Parse(string filename)\r\n\t\t{\r\n\t\t\tstring carFile = Path.Combine(GameConfig.CdDataPath, filename);\r\n\t\t\tBinaryReader br = new BinaryReader(File.Open(carFile, FileMode.Open));\r\n\t\t\tHeaderChunk rootChunk = new HeaderChunk();\r\n\t\t\trootChunk.Read(br, true);\r\n\r\n\t\t\t// Cfm files contain a high-res model + bitmaps at index 0, and a low-res model + bitmaps at index 1.  We only use the high-res resources.\r\n\t\t\trootChunk.MeshChunks[0].Load(br);\r\n\t\t\trootChunk.BitmapChunks[0].TextureGenerated += CfmFile_TextureGenerated;\r\n\t\t\trootChunk.BitmapChunks[0].Load(br);\r\n\r\n\t\t\tbr.Close();\r\n\r\n\t\t\tMesh = new CarMesh(rootChunk.MeshChunks[0], rootChunk.BitmapChunks[0], _brakeColor);\r\n\t\t}\r\n\r\n\r\n\t\t// The brakes are painted with palette color #254. Remember what color that maps to now so we can generate brake on/off textures later\r\n\t\tvoid CfmFile_TextureGenerated(BitmapEntry entry, byte[] palette, byte[] pixelData)\r\n\t\t{\r\n\t\t\tif (entry.Id == \"rsid\")\r\n\t\t\t{\r\n\t\t\t\t_brakeColor = new Color(palette[254 * 3], palette[254 * 3 + 1], palette[254 * 3 + 2]);\r\n\t\t\t}\r\n\t\t}\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Parsers/FshFile.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing OpenNFS1;\r\nusing OpenNFS1.Parsers;\r\n\r\nnamespace OpenNFS1.Parsers\r\n{\r\n\r\n\t// A FSH file holds 1 or many bitmap entries\r\n\tclass FshFile\r\n\t{\r\n\t\tpublic BitmapChunk Header { get; private set; }\r\n\r\n\t\tpublic FshFile(string filename)\r\n\t\t{\r\n\t\t\tfilename = Path.Combine(GameConfig.CdDataPath, filename);\r\n\t\t\tParse(File.Open(filename, FileMode.Open));\r\n\t\t}\r\n\r\n\t\tpublic FshFile(byte[] contents)\r\n\t\t{\r\n\t\t\tParse(new MemoryStream(contents));\r\n\t\t}\r\n\r\n\t\tprivate void Parse(Stream contents)\r\n\t\t{\r\n\t\t\tBinaryReader br = new BinaryReader(contents);\r\n\t\t\tHeader = new BitmapChunk();\r\n\t\t\tHeader.EnableTextureAttachments = true;\r\n\t\t\tHeader.SkipHeader(br);\r\n\t\t\tHeader.Read(br);\r\n\t\t\tbr.Close();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Parsers/HeaderChunk.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing System.IO;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing System.Diagnostics;\r\n\r\nnamespace OpenNFS1.Parsers\r\n{\r\n    class HeaderChunk : BaseChunk\r\n    {\r\n        private List<MeshChunk> _meshChunks = new List<MeshChunk>();\r\n        private List<BitmapChunk> _bitmapChunks = new List<BitmapChunk>();\r\n        private List<HeaderChunk> _headerChunks = new List<HeaderChunk>();\r\n\r\n        private static int _index;\r\n\r\n        public override void Read(BinaryReader reader)\r\n        {\r\n            Read(reader, false, false);\r\n        }\r\n        public void Read(BinaryReader reader, bool readHeadersOnly)\r\n        {\r\n            Read(reader, true, readHeadersOnly);\r\n        }\r\n\r\n        public void Read(BinaryReader reader, bool readIdentifier, bool readHeadersOnly)\r\n        {\r\n            Debug.WriteLine(\">> Reading header chunk\");\r\n\r\n            if (readIdentifier)\r\n            {\r\n                char[] identifier = reader.ReadChars(4);\r\n            }\r\n            long offset = reader.BaseStream.Position - 4;  //-4 for identifier\r\n\r\n            int chunkCount = reader.ReadInt32();\r\n\r\n            List<int> chunkOffsets = new List<int>();\r\n            for (int i = 0; i < chunkCount; i++)\r\n            {\r\n                chunkOffsets.Add(reader.ReadInt32());\r\n            }\r\n            for (int i = 0; i < chunkCount; i++)\r\n            {\r\n                reader.BaseStream.Position = offset + chunkOffsets[i];\r\n                ReadChunk(reader, readHeadersOnly);\r\n            }\r\n        }\r\n\r\n        private void ReadChunk(BinaryReader reader, bool readHeaderOnly)\r\n        {\r\n            string identifier = new string(reader.ReadChars(4));\r\n\r\n            if (identifier == \"ORIP\")\r\n            {\r\n                MeshChunk meshChunk = new MeshChunk();\r\n                if (readHeaderOnly)\r\n                    meshChunk.Offset = reader.BaseStream.Position;\r\n                else\r\n                {\r\n                    meshChunk.Read(reader);\r\n                }\r\n                _meshChunks.Add(meshChunk);\r\n            }\r\n            else if (identifier == \"SHPI\")\r\n            {\r\n                BitmapChunk bitmapChunk = new BitmapChunk();\r\n                bitmapChunk.Index = _index++;\r\n                if (readHeaderOnly)\r\n                    bitmapChunk.Offset = reader.BaseStream.Position;\r\n                else\r\n                    bitmapChunk.Read(reader);\r\n                _bitmapChunks.Add(bitmapChunk);\r\n            }\r\n            else if (identifier == \"wwww\")\r\n            {\r\n                HeaderChunk header = new HeaderChunk();\r\n                if (readHeaderOnly)\r\n                    header.Offset = reader.BaseStream.Position;\r\n                else\r\n                    header.Read(reader, false, false);\r\n                _headerChunks.Add(header);\r\n            }\r\n            else\r\n            {\r\n                throw new NotImplementedException();\r\n            }\r\n        }\r\n\r\n        internal List<MeshChunk> MeshChunks\r\n        {\r\n            get { return _meshChunks; }\r\n        }\r\n\r\n        internal List<BitmapChunk> BitmapChunks\r\n        {\r\n            get { return _bitmapChunks; }\r\n        }\r\n\r\n        internal List<HeaderChunk> HeaderChunks\r\n        {\r\n            get { return _headerChunks; }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Parsers/MeshChunk.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing System.IO;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing GameEngine;\r\nusing System.Diagnostics;\r\nusing OpenNFS1;\r\n\r\nnamespace OpenNFS1.Parsers\r\n{\r\n\tclass MeshChunk : BaseChunk\r\n\t{\r\n\t\tList<Vector3> _vertices = new List<Vector3>();\r\n\t\tList<Vector2> _vertexTextureMap = new List<Vector2>();\r\n\t\tList<Polygon> _polygons = new List<Polygon>();\r\n\t\tprivate List<string> _textureNames = new List<string>();\r\n\t\tpublic string Identifier;\r\n\r\n\t\tpublic List<Polygon> Polygons\r\n\t\t{\r\n\t\t\tget { return _polygons;  }\r\n\t\t}\r\n\r\n\t\tpublic override void Read(BinaryReader reader)\r\n\t\t{\r\n\t\t\tbase.Read(reader);\r\n\r\n\t\t\tDebug.WriteLine(\">> Loading mesh chunk\");\r\n\r\n\t\t\tint unk = reader.ReadInt32();\r\n\t\t\tunk = reader.ReadInt32();\r\n\t\t\tint vertexCount = reader.ReadInt32();\r\n\t\t\tunk = reader.ReadInt32();\r\n\t\t\tint vertexBlockOffset = reader.ReadInt32();\r\n\t\t\tint texturePointsCount = reader.ReadInt32();\r\n\t\t\tint texturePointsBlockOffset = reader.ReadInt32();\r\n\t\t\tint polygonCount = reader.ReadInt32();\r\n\t\t\tint polygonBlockOffset = reader.ReadInt32();\r\n\r\n\t\t\tstring identifer = new string(reader.ReadChars(12));\r\n\t\t\tIdentifier = identifer.Substring(0, identifer.IndexOf('\\0'));\r\n\t\t\tDebug.WriteLine(\"Loading mesh \" + Identifier);\r\n\t\t\tint textureNameCount = reader.ReadInt32();\r\n\t\t\tint textureNameBlockOffset = reader.ReadInt32();\r\n\r\n\t\t\treader.ReadBytes(16);  //section 4,5 \r\n\r\n\t\t\tint polygonVertexMapBlockOffset = reader.ReadInt32();\r\n\r\n\t\t\treader.ReadBytes(8); //section 6\r\n\r\n\t\t\tint labelCount = reader.ReadInt32();\r\n\t\t\tint polygonLabelBlockOffset = reader.ReadInt32();\r\n\r\n\t\t\treader.BaseStream.Position = Offset + polygonVertexMapBlockOffset;\r\n\t\t\tMemoryStream ms = new MemoryStream(reader.ReadBytes(_length - polygonVertexMapBlockOffset));\r\n\t\t\tBinaryReader polygonVertexMapReader = new BinaryReader(ms);\r\n\r\n\t\t\treader.BaseStream.Position = Offset + vertexBlockOffset;\r\n\t\t\tReadVertexBlock(reader, vertexCount);\r\n\r\n\t\t\treader.BaseStream.Position = Offset + texturePointsBlockOffset;\r\n\t\t\tReadTextureMapBlock(reader, texturePointsCount);\r\n\r\n\t\t\treader.BaseStream.Position = Offset + textureNameBlockOffset;\r\n\t\t\tReadTextureNameBlock(reader, textureNameCount);\r\n\r\n\t\t\treader.BaseStream.Position = Offset + polygonBlockOffset;\r\n\t\t\tReadPolygonBlock(reader, polygonCount, polygonVertexMapReader);\r\n\t\t\tpolygonVertexMapReader.Close();\r\n\r\n\t\t\treader.BaseStream.Position = Offset + polygonLabelBlockOffset;\r\n\t\t\tReadPolygonLabelBlock(reader, labelCount);\r\n\t\t}\r\n\r\n\t\tprivate void ReadVertexBlock(BinaryReader reader, int vertexCount)\r\n\t\t{\r\n\t\t\tfor (int i = 0; i < vertexCount; i++)\r\n\t\t\t{\r\n\t\t\t\tfloat x = reader.ReadInt32();\r\n\t\t\t\tfloat y = reader.ReadInt32();\r\n\t\t\t\tfloat z = reader.ReadInt32(); \r\n\t\t\t\tVector3 vertex = new Vector3(x, y, -z) * GameConfig.MeshScale;\r\n\t\t\t\t_vertices.Add(vertex);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void ReadTextureMapBlock(BinaryReader reader, int texturePointsCount)\r\n\t\t{\r\n\t\t\tfor (int i = 0; i < texturePointsCount; i++)\r\n\t\t\t{\r\n\t\t\t\tint tU = reader.ReadInt32();\r\n\t\t\t\tint tV = reader.ReadInt32();\r\n\t\t\t\t_vertexTextureMap.Add(new Vector2(tU, tV));\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void ReadTextureNameBlock(BinaryReader reader, int textureNameCount)\r\n\t\t{\r\n\t\t\tfor (int i = 0; i < textureNameCount; i++)\r\n\t\t\t{\r\n\t\t\t\treader.ReadBytes(8);\r\n\t\t\t\tstring id2 = new string(reader.ReadChars(4));\r\n\t\t\t\tif (id2 == \"\\0\\0\\0\\0\")\r\n\t\t\t\t\tid2 = null;\r\n\t\t\t\t_textureNames.Add(id2);\r\n\t\t\t\treader.ReadBytes(8);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void ReadPolygonBlock(BinaryReader reader, int polygonCount, BinaryReader polygonVertexMap)\r\n\t\t{\r\n\t\t\tfor (int i = 0; i < polygonCount; i++)\r\n\t\t\t{\r\n\t\t\t\tbyte typeFlag = reader.ReadByte();\r\n\t\t\t\tint shapeId = typeFlag & (0xff >> 5); // type = 3 or 4.  Held in the first 3 bits\r\n\t\t\t\t//bool computeTextureUVs = (typeFlag & (0x1 << 3)) != 0; // bit 3 is set if there are *no* texture co-ords (and we should infer them?)\r\n\r\n\t\t\t\tbyte b1 = reader.ReadByte();\r\n\t\t\t\tbyte textureNumber = reader.ReadByte();\r\n\t\t\t\tbyte b2 = reader.ReadByte();\r\n\t\t\t\tint verticesIndex = reader.ReadInt32();\r\n\t\t\t\tint textureCoordsIndex = reader.ReadInt32();\r\n\r\n\t\t\t\tif (shapeId != 3 && shapeId != 4)\r\n\t\t\t\t{\r\n\t\t\t\t\t// something in Burnt Sienna has a value of 2.  Haven't investigated yet.\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\t\r\n\r\n\t\t\t\t// if these 2 indexes are the same, there are no texture coords\r\n\t\t\t\tbool computeTextureUVs = verticesIndex == textureCoordsIndex;\r\n\r\n\t\t\t\tPolygon polygon = new Polygon((PolygonShape)shapeId, computeTextureUVs);\r\n\t\t\t\tpolygon.TextureName = _textureNames[textureNumber];\t\t\r\n\t\t\t\t\r\n\t\t\t\tif (polygon.TextureName == \"dkfr\")\r\n\t\t\t\t{\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\t//Vertices for polygon\r\n\t\t\t\tpolygonVertexMap.BaseStream.Position = verticesIndex * sizeof(int);\r\n\t\t\t\tint v1 = polygonVertexMap.ReadInt32();\r\n\t\t\t\tint v2 = polygonVertexMap.ReadInt32();\r\n\t\t\t\tint v3 = polygonVertexMap.ReadInt32();\r\n\t\t\t\tint v4 = polygonVertexMap.ReadInt32();\r\n\r\n\t\t\t\t//Debug.WriteLine(String.Format(\"Poly {8} {0} {9} ({7}): {1},{2},{3},{4}, / {5} {6} computeUvs: {10}\", i, v1, v2, v3, v4, b1, b2, polygon.TextureName, polygon.Shape, typeFlag.ToString(\"X\"), computeTextureUVs));\r\n\r\n\t\t\t\t//Texture co-ords for vertices\r\n\t\t\t\tif (!computeTextureUVs)\r\n\t\t\t\t{\r\n\t\t\t\t\tpolygonVertexMap.BaseStream.Position = textureCoordsIndex * sizeof(int);\r\n\t\t\t\t\tint t1 = polygonVertexMap.ReadInt32();\r\n\t\t\t\t\tint t2 = polygonVertexMap.ReadInt32();\r\n\t\t\t\t\tint t3 = polygonVertexMap.ReadInt32();\r\n\t\t\t\t\tint t4 = polygonVertexMap.ReadInt32();\r\n\r\n\t\t\t\t\tpolygon.Vertices[0] = _vertices[v1];\r\n\t\t\t\t\tpolygon.Vertices[1] = _vertices[v2];\r\n\t\t\t\t\tpolygon.Vertices[2] = _vertices[v3];\r\n\t\t\t\t\tpolygon.TextureUVs[0] = _vertexTextureMap[t1];\r\n\t\t\t\t\tpolygon.TextureUVs[1] = _vertexTextureMap[t2];\r\n\t\t\t\t\tpolygon.TextureUVs[2] = _vertexTextureMap[t3];\r\n\t\t\t\t\t\r\n\t\t\t\t\tif (polygon.Shape == PolygonShape.Quad)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tpolygon.Vertices[3] = _vertices[v1];\r\n\t\t\t\t\t\tpolygon.Vertices[4] = _vertices[v3];\r\n\t\t\t\t\t\tpolygon.Vertices[5] = _vertices[v4];\r\n\t\t\t\t\t\tpolygon.TextureUVs[3] = _vertexTextureMap[t1];\r\n\t\t\t\t\t\tpolygon.TextureUVs[4] = _vertexTextureMap[t3];\r\n\t\t\t\t\t\tpolygon.TextureUVs[5] = _vertexTextureMap[t4];\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tif (polygon.Shape == PolygonShape.Quad)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tpolygon.Vertices[0] = _vertices[v1];\r\n\t\t\t\t\t\tpolygon.Vertices[1] = _vertices[v2];\r\n\t\t\t\t\t\tpolygon.Vertices[2] = _vertices[v3];\r\n\t\t\t\t\t\tpolygon.Vertices[3] = _vertices[v1];\r\n\t\t\t\t\t\tpolygon.Vertices[4] = _vertices[v3];\r\n\t\t\t\t\t\tpolygon.Vertices[5] = _vertices[v4];\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tthrow new NotImplementedException();\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\t_polygons.Add(polygon);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void ReadPolygonLabelBlock(BinaryReader reader, int labelCount)\r\n\t\t{\r\n\t\t\tfor (int i = 0; i < labelCount; i++)\r\n\t\t\t{\r\n\t\t\t\tstring label = new string(reader.ReadChars(8));\r\n\t\t\t\tlabel = label.Substring(0, label.IndexOf(\"\\0\"));\r\n\t\t\t\tint polyIndex = reader.ReadInt32();\r\n\t\t\t\t_polygons[polyIndex].Label = label;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Parsers/OpenRoadTrackfamFile.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Graphics;\r\n\r\nnamespace OpenNFS1.Parsers\r\n{\r\n\tclass OpenRoadTrackfamFile : TrackfamFile\r\n\t{\r\n\t\tpublic OpenRoadTrackfamFile(string trackFile, bool alternateTimeOfDay)\r\n\t\t\t: base(trackFile, alternateTimeOfDay)\r\n\t\t{\r\n\t\t}\r\n\r\n        public override Texture2D GetGroundTexture(int textureNbr)\r\n        {\r\n            int groupId = textureNbr / 3;\r\n            int remainder = textureNbr % 3;\r\n\r\n            string id = null;\r\n            if (remainder == 0)\r\n                id = \"A000\";\r\n            else if (remainder == 1)\r\n                id = \"B000\";\r\n            else if (remainder == 2)\r\n                id = \"C000\";\r\n            else\r\n                throw new NotImplementedException();\r\n\r\n\t\t\tBitmapEntry found = _root.HeaderChunks[TERRAIN_CHUNK].BitmapChunks[groupId].Bitmaps.Find(a => a.Id == id);\r\n\r\n\t\t\tif (found == null)\r\n\t\t\t{\r\n\t\t\t\tDebug.WriteLine(\"Warning: Ground texture not found: \" + textureNbr);\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\treturn found.Texture;\r\n\t\t\t}\r\n        }\r\n\r\n        public override Texture2D GetSceneryTexture(int textureNbr)\r\n        {\r\n            textureNbr /= 4;\r\n\t\t\tBitmapEntry found = _root.HeaderChunks[SCENERY_CHUNK].BitmapChunks[textureNbr].Bitmaps.Find(a => a.Id == \"0000\");\r\n\r\n\t\t\tif (found == null)\r\n\t\t\t{\r\n\t\t\t\tDebug.WriteLine(\"Warning: Scenery texture not found: \" + textureNbr);\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\treturn found.Texture;\r\n\t\t\t}\r\n        }\r\n\r\n        public override Texture2D GetFenceTexture(int textureNbr)\r\n        {\r\n\t\t\tvar tex = GetGroundTexture(textureNbr);\r\n\t\t\tif (tex == null)\r\n\t\t\t{  \r\n\t\t\t\t//nasty hack: handle Alpine 1's initial fence textureIds which don't seem to match any other tracks...\r\n\t\t\t\ttex = GetGroundTexture(textureNbr * 3);\r\n\t\t\t}\r\n\t\t\treturn tex;\r\n        }\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Parsers/QfsFile.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing OpenNFS1;\r\n\r\nnamespace OpenNFS1.Parsers\r\n{\r\n\t// A QFS file is a compressed FSH file.  A FSH file holds bitmaps\r\n\tclass QfsFile\r\n\t{\r\n\t\tpublic FshFile Content { get; private set; }\r\n\r\n\t\tpublic QfsFile(string filename)\r\n\t\t{\r\n\t\t\tfilename = Path.Combine(GameConfig.CdDataPath, filename);\r\n\t\t\tbyte[] decompressedData = Decompress(filename);\r\n\t\t\tContent = new FshFile(decompressedData);\r\n\t\t}\r\n\t\t\r\n\t\t\r\n\t\t// Apparently this LZ77 compression variant was figured out by an 'Ian Brown' (can't find any contact info for him).\r\n\t\t// This method was ported from Dennis Auroux's fshtool.c source. (http://www-math.mit.edu/~auroux/software/index.html)\r\n\t\tprivate byte[] Decompress(string filename)\r\n\t\t{\r\n\t\t\tbyte[] inbuf = File.ReadAllBytes(filename);\r\n\t\t\tint buflen = inbuf.Length;\r\n\t\t\tbyte[] outbuf;\r\n\t\t\t\t\t\t\r\n\t\t\tbyte packcode;\r\n\t\t\tint a,b,c,len,offset;\r\n\t\t\tint inlen,outlen,inpos,outpos;\r\n  \r\n\t\t\t/* length of data */\r\n\t\t\tinlen=buflen;\r\n\t\t\toutlen=(inbuf[2]<<16)+(inbuf[3]<<8)+inbuf[4];\r\n\t\t\toutbuf= new byte[outlen];\r\n  \r\n\t\t\t/* position in file */\r\n\t\t\tif ((inbuf[0] & 0x01) != 0) inpos=8; else inpos=5;\r\n\t\t\toutpos=0;\r\n  \r\n\t\t\t/* main decoding loop */\r\n\t\t\twhile ((inpos<inlen)&&(inbuf[inpos]<0xFC))\r\n\t\t\t{\r\n\t\t\tpackcode=inbuf[inpos];\r\n\t\t\ta=inbuf[inpos+1];\r\n\t\t\tb=inbuf[inpos+2];\r\n    \r\n\t\t\tif ((packcode&0x80) == 0) {\r\n\t\t\t\tlen=packcode&3;\r\n\t\t\t\tmmemcpy(outbuf,outpos,inbuf,inpos+2,len);\r\n\t\t\t\tinpos+=len+2;\r\n\t\t\t\toutpos+=len;\r\n\t\t\t\tlen=((packcode&0x1c)>>2)+3;\r\n\t\t\t\toffset=((packcode>>5)<<8)+a+1;\r\n\t\t\t\tmmemcpy(outbuf,outpos,outbuf,outpos-offset,len);\r\n\t\t\t\toutpos+=len;\r\n\t\t\t}\r\n\t\t\telse if ((packcode&0x40) == 0) {\r\n\t\t\t\tlen=(a>>6)&3; \r\n\t\t\t\tmmemcpy(outbuf,outpos,inbuf,inpos+3,len);\r\n\t\t\t\tinpos+=len+3;\r\n\t\t\t\toutpos+=len;\r\n\t\t\t\tlen=(packcode&0x3f)+4;\r\n\t\t\t\toffset=(a&0x3f)*256+b+1;\r\n\t\t\t\tmmemcpy(outbuf,outpos,outbuf,outpos-offset,len);\r\n\t\t\t\toutpos+=len;\r\n\t\t\t}  \r\n\t\t\telse if ((packcode&0x20) == 0) {\r\n\t\t\t\tc=inbuf[inpos+3];\r\n\t\t\t\tlen=packcode&3; \r\n\t\t\t\tmmemcpy(outbuf,outpos,inbuf,inpos+4,len);\r\n\t\t\t\tinpos+=len+4;\r\n\t\t\t\toutpos+=len;\r\n\t\t\t\tlen=((packcode>>2)&3)*256+c+5;\r\n\t\t\t\toffset=((packcode&0x10)<<12)+256*a+b+1;\r\n\t\t\t\tmmemcpy(outbuf,outpos,outbuf,outpos-offset,len);\r\n\t\t\t\toutpos+=len;\r\n\t\t\t}  \r\n\t\t\telse {\r\n\t\t\t\tlen=(packcode&0x1f)*4+4;\r\n\t\t\t\tmmemcpy(outbuf,outpos,inbuf,inpos+1,len);\r\n\t\t\t\tinpos+=len+1;\r\n\t\t\t\toutpos+=len;\r\n\t\t\t}\r\n\t\t\t}\r\n  \r\n\t\t\t/* trailing bytes */\r\n\t\t\tif ((inpos<inlen)&&(outpos<outlen)) {\r\n\t\t\tmmemcpy(outbuf,outpos,inbuf,inpos+1,inbuf[inpos]&3);\r\n\t\t\toutpos+=inbuf[inpos]&3;\r\n\t\t\t}\r\n\r\n\t\t\tif (outpos != outlen)\r\n\t\t\t{\r\n\t\t\t\tDebug.WriteLine(\"Warning: bad length ? {0} instead of {1}\\n\", outpos, outlen);\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\treturn outbuf;\r\n\t\t}\r\n\r\n\t\tvoid mmemcpy(byte[] dest, int pDest, byte[] src, int pSrc, int len) /* LZ-compatible memcopy */\r\n\t\t{\r\n\t\t\t//while (len--) *(dest++) = *(src++);\r\n\t\t\twhile (len-- != 0)\r\n\t\t\t{\r\n\t\t\t\tdest[pDest++] = src[pSrc++];\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Parsers/TrackfamFile.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing System.IO;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing System.Diagnostics;\r\nusing OpenNFS1;\r\n\r\nnamespace OpenNFS1.Parsers\r\n{\r\n\r\n\t// .FAM files in the TRACKFAM folder contain textures and models for the track terrain and scenery objects\r\n    class TrackfamFile\r\n    {\r\n        protected HeaderChunk _root;\r\n\t\tprivate Mesh[] _meshCache;\r\n\r\n\t\tprotected const int TERRAIN_CHUNK = 0;\r\n\t\tprotected const int SCENERY_CHUNK = 1;\r\n\t\tprotected const int MESH_CHUNK = 2;\r\n\r\n        public TrackfamFile(string trackFile, bool alternateTimeOfDay)\r\n        {\r\n\t\t\tstring textureFilePath;\r\n\t\t\tif (!alternateTimeOfDay)\r\n\t\t\t\ttextureFilePath = \"SIMDATA\\\\ETRACKFM\\\\\" + Path.GetFileNameWithoutExtension(trackFile) + \"_001.fam\";\r\n\t\t\telse\r\n\t\t\t\ttextureFilePath = \"SIMDATA\\\\NTRACKFM\\\\\" + Path.GetFileNameWithoutExtension(trackFile) + \"_T01.fam\";\r\n            ReadFamFile(textureFilePath);\r\n\r\n\t\t\t_meshCache = new Mesh[_root.HeaderChunks[2].HeaderChunks.Count];\r\n        }\r\n\r\n        private void ReadFamFile(string filename)\r\n        {\r\n\t\t\tfilename = Path.Combine(GameConfig.CdDataPath, filename);\r\n            BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open));\r\n            _root = new HeaderChunk();\r\n            _root.Read(reader, false);  //we want to load everything up front\r\n            reader.Close();\r\n        }\r\n\r\n\r\n        public virtual Texture2D GetGroundTexture(int textureNbr)\r\n        {\r\n\t\t\tint groupId2 = textureNbr / 3;\r\n\t\t\tint remainder = textureNbr % 3;\r\n\r\n\t\t\tstring id = null;\r\n\t\t\tif (remainder == 0)\r\n\t\t\t\tid = groupId2.ToString(\"00\") + \"A0\";\r\n\t\t\telse if (remainder == 1)\r\n\t\t\t\tid = groupId2.ToString(\"00\") + \"B0\";\r\n\t\t\telse if (remainder == 2)\r\n\t\t\t\tid = groupId2.ToString(\"00\") + \"C0\";\r\n\t\t\telse\r\n\t\t\t\tthrow new NotImplementedException();\r\n\r\n\t\t\tBitmapEntry found = _root.HeaderChunks[TERRAIN_CHUNK].BitmapChunks[0].Bitmaps.Find(a => a.Id == id);\r\n\r\n            if (found == null)\r\n            {\r\n                Debug.WriteLine(\"Warning: Ground texture not found: \" + textureNbr);\r\n                return null;\r\n            }\r\n            else\r\n                return found.Texture;\r\n        }\r\n\r\n        public virtual Texture2D GetSceneryTexture(int textureNbr)\r\n        {\r\n            textureNbr /= 4;\r\n            string texture = textureNbr.ToString(\"00\") + \"00\";\r\n\t\t\tBitmapEntry found = _root.HeaderChunks[SCENERY_CHUNK].BitmapChunks[0].Bitmaps.Find(a => a.Id == texture);\r\n\r\n\t\t\tif (found == null)\r\n\t\t\t{\r\n\t\t\t\tDebug.WriteLine(\"Warning: Scenery texture not found: \" + textureNbr);\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\treturn found.Texture;\r\n\t\t\t}\r\n        }\r\n\r\n        public Texture2D HorizonTexture\r\n        {\r\n            get\r\n            {\r\n                return _root.BitmapChunks[0].FindByName(\"horz\").Texture;\r\n            }\r\n        }\r\n\r\n        public virtual Texture2D GetFenceTexture(int textureNbr)\r\n        {\r\n\t\t\treturn _root.HeaderChunks[TERRAIN_CHUNK].BitmapChunks[0].FindByName(\"ga00\").Texture;\r\n        }\r\n\r\n\t\t// Mesh data (vertices + textures) are stored in the 3rd chunk of a FAM file\r\n\t\tpublic Mesh GetMesh(int index)\r\n\t\t{\r\n\t\t\tif (_meshCache[index] == null)\r\n\t\t\t{\r\n\t\t\t\tvar meshChunk = _root.HeaderChunks[MESH_CHUNK].HeaderChunks[index].MeshChunks[0];\r\n\t\t\t\tvar bmpChunk = _root.HeaderChunks[MESH_CHUNK].HeaderChunks[index].BitmapChunks[0];\r\n\t\t\t\t_meshCache[index] = new Mesh(meshChunk, bmpChunk);\r\n\t\t\t}\r\n\t\t\treturn _meshCache[index];\r\n\t\t}\r\n\r\n\t\tpublic void Dispose()\r\n\t\t{\r\n\t\t\tforeach (var bm in _root.HeaderChunks[TERRAIN_CHUNK].BitmapChunks[0].Bitmaps)\r\n\t\t\t{\r\n\t\t\t\tif (bm.Texture != null) bm.Texture.Dispose();\r\n\t\t\t}\r\n\t\t\tforeach (var bm in _root.HeaderChunks[SCENERY_CHUNK].BitmapChunks[0].Bitmaps)\r\n\t\t\t{\r\n\t\t\t\tif (bm.Texture != null) bm.Texture.Dispose();\r\n\t\t\t}\r\n\r\n\t\t\tforeach (var bm in _root.HeaderChunks[MESH_CHUNK].BitmapChunks)\r\n\t\t\t{\r\n\t\t\t\tforeach (var bm2 in bm.Bitmaps)\r\n\t\t\t\t\tif (bm2.Texture != null) bm2.Texture.Dispose();\r\n\t\t\t}\r\n\r\n\t\t\tforeach (var mesh in _meshCache)\r\n\t\t\t{\r\n\t\t\t\tif (mesh != null) mesh.Dispose();\r\n\t\t\t}\r\n\t\t}\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Parsers/TriFile.cs",
    "content": "﻿using Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing OpenNFS1;\r\nusing OpenNFS1.Parsers.Track;\r\nusing OpenNFS1.Tracks;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Text;\r\n\r\nnamespace OpenNFS1.Parsers\r\n{\r\n\tenum SceneryType\r\n\t{\r\n\t\tModel = 1,\r\n\t\tBitmap = 4,\r\n\t\tTwoSidedBitmap = 6\r\n\t}\r\n\r\n\t[Flags]\r\n\tenum SceneryFlags\r\n\t{\r\n\t\tNone = 0,\r\n\t\tUnk1 = 1,\r\n\t\tAnimated = 4\r\n\t}\r\n\r\n\tclass SceneryObjectDescriptor\r\n\t{\r\n\t\tpublic int Id;\r\n\t\tpublic float Width, Height;\r\n\t\tpublic SceneryFlags Flags;\r\n\t\tpublic SceneryType Type;\r\n\t\tpublic int ResourceId, Resource2Id;\r\n\t\tpublic int AnimationFrameCount;\r\n\t}\r\n\r\n\tclass SceneryObject\r\n\t{\r\n\t\tpublic SceneryObjectDescriptor Descriptor;\r\n\t\tpublic int ReferenceNode;\r\n\t\tpublic float Orientation;\r\n\t\tpublic Vector3 RelativePosition;\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Tri files contain the track vertices, physical road data, scenery item placement\r\n\t/// </summary>\r\n\tclass TriFile\r\n\t{\r\n\t\tpublic const int NbrTerrainPointsPerSide = 6;  //includes the middle-point (duplicated on both sides)\r\n\t\tpublic const int NbrRowsPerSegment = 4;\r\n\r\n\t\tpublic List<TrackNode> Nodes { get; private set; }\r\n\t\tpublic List<SceneryObject> Scenery { get; private set; }\r\n\t\tpublic List<SceneryObjectDescriptor> ObjectDescriptors { get; private set; }\r\n\t\tpublic List<TerrainSegment> Segments { get; private set; }\r\n\t\tpublic bool IsOpenRoad { get; private set; }\r\n\t\tpublic string FileName { get; private set; }\r\n\t\tpublic int FenceTextureId { get; private set; }\r\n\r\n\t\tpublic TriFile(string filename)\r\n\t\t{\r\n\t\t\tFileName = Path.GetFileName(filename);\r\n\t\t\tfilename = Path.Combine(GameConfig.CdDataPath, filename);\r\n\t\t\tBinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open));\r\n\r\n\t\t\tbyte trackVersion = reader.ReadByte();\r\n\t\t\tif (trackVersion != 0x11)\r\n\t\t\t{\r\n\t\t\t\tthrow new Exception(\"Tri file version not supported: \" + trackVersion);\r\n\t\t\t}\r\n\r\n\t\t\tNodes = new List<TrackNode>();\r\n\t\t\tObjectDescriptors = new List<SceneryObjectDescriptor>();\r\n\t\t\tScenery = new List<SceneryObject>();\r\n\t\t\tSegments = new List<TerrainSegment>();\r\n\r\n\t\t\tParseTrackNodesBlock(reader);\r\n\t\t\tParseSceneryObjectsBlock(reader);\r\n\t\t\tParseTerrainBlock(reader);\r\n\t\t\tComputeAbsoluteTerrainPoints();\r\n\t\t\t\r\n\t\t\treader.Close();\r\n\t\t}\r\n\r\n\t\tvoid ParseTrackNodesBlock(BinaryReader reader)\r\n\t\t{\r\n\t\t\treader.BaseStream.Position = 2444;  //start of node list\r\n\r\n\t\t\tTrackNode prevNode = null;\r\n\r\n\t\t\tfor (int i = 0; i < 2400; i++)\r\n\t\t\t{\r\n\t\t\t\tvar node = new TrackNode();\r\n\t\t\t\tnode.Number = Nodes.Count;\r\n\t\t\t\tfloat vergeScale = 8000;\r\n\r\n\t\t\t\tnode.DistanceToLeftVerge = reader.ReadByte();\r\n\t\t\t\tnode.DistanceToLeftVerge *= GameConfig.TerrainScale * vergeScale;\r\n\t\t\t\tnode.DistanceToRightVerge = reader.ReadByte();\r\n\t\t\t\tnode.DistanceToRightVerge *= GameConfig.TerrainScale * vergeScale;\r\n\t\t\t\tnode.DistanceToLeftBarrier = reader.ReadByte();\r\n\t\t\t\tnode.DistanceToLeftBarrier *= GameConfig.TerrainScale * vergeScale;\r\n\t\t\t\tnode.DistanceToRightBarrier = reader.ReadByte();\r\n\t\t\t\tnode.DistanceToRightBarrier *= GameConfig.TerrainScale * vergeScale;\r\n\r\n\t\t\t\tnode.Flag1 = reader.ReadByte();\r\n\t\t\t\tnode.Flag2 = reader.ReadByte();\r\n\t\t\t\tnode.Flag3 = reader.ReadByte();\r\n\t\t\t\tnode.NodeProperty = reader.ReadByte();\r\n\r\n\t\t\t\t// unused trackNodes are filled with zeroes, so stop when we have a node with a zero position\r\n\t\t\t\tif (node.DistanceToLeftVerge == 0 && node.DistanceToRightVerge == 0\r\n\t\t\t\t\t&& node.DistanceToLeftBarrier == 0 && node.DistanceToRightBarrier == 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\t//Debug.WriteLine(\"{0},{1},{2},{3}\", node.b[0], node.b[1], node.b[2], node.b[3]);\r\n\r\n\t\t\t\tnode.Position = new Vector3(reader.ReadInt32(), reader.ReadInt32(), -reader.ReadInt32()) * GameConfig.TerrainScale;\r\n\r\n\r\n\t\t\t\t// Slope is stored as a 2's complement value.  Convert it back to signed value\r\n\t\t\t\tInt16 slope = reader.ReadInt16();\r\n\t\t\t\t//bool msbSet = (slope & (0x1 << 13)) != 0;\r\n\t\t\t\t//if (msbSet)\r\n\t\t\t\t//{\r\n\t\t\t\t//\tslope = (short)~slope;\r\n\t\t\t\t//\tslope++;\r\n\t\t\t\t//\tslope *= -1;\r\n\t\t\t\t//}\r\n\t\t\t\tif (slope > 0x2000)\r\n\t\t\t\t{\r\n\t\t\t\t\tslope -= 0x3FFF;\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tnode.Slope = slope;\r\n\r\n\t\t\t\tnode.Slant = reader.ReadInt16(); //weird slant-A\r\n\r\n\t\t\t\tfloat orientation = (float)reader.ReadInt16();\r\n\t\t\t\t//convert to signed degrees\r\n\t\t\t\t//0 = forwards, 0x1000 = right, 0x2000 = back, 0x3000 = left, 0x3FFF back to forwards\r\n\r\n\t\t\t\tif (orientation > 0x2000)\r\n\t\t\t\t{\r\n\t\t\t\t\torientation -= 0x3FFF;\r\n\t\t\t\t}\r\n\t\t\t\tnode.Orientation = ((orientation / 0x3FFF) * -360);\r\n\r\n\t\t\t\tnode.unk1 = reader.ReadBytes(2);\r\n\t\t\t\tnode.ZOrientation = reader.ReadInt16();\r\n\t\t\t\tnode.Slant = reader.ReadInt16(); // slant-B\r\n\t\t\t\tnode.XOrientation = reader.ReadInt16();\r\n\t\t\t\tnode.unk2 = reader.ReadBytes(2);\r\n\r\n\t\t\t\tif (prevNode != null)\r\n\t\t\t\t{\r\n\t\t\t\t\tprevNode.Next = node;\r\n\t\t\t\t\tnode.Prev = prevNode;\r\n\t\t\t\t}\r\n\t\t\t\tprevNode = node;\r\n\t\t\t\tNodes.Add(node);\r\n\t\t\t}\r\n\r\n\t\t\t// If this is a circuit track, hook the last node up to the first\r\n\t\t\tif (Vector3.Distance(Nodes[0].Position, Nodes[Nodes.Count - 1].Position) > 100)\r\n\t\t\t{\r\n\t\t\t\tIsOpenRoad = true;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tprevNode.Next = Nodes[0];\r\n\t\t\t\tNodes[0].Prev = prevNode;\r\n\t\t\t}\r\n\t\t\t\r\n\r\n\t\t\tfor (int i = 0; i < Nodes.Count - 1; i++)\r\n\t\t\t{\r\n\t\t\t\tvar node = Nodes[i];\r\n\t\t\t\tvar normal = Vector3.Cross(node.GetRightBoundary() - node.GetLeftBoundary(),\r\n\t\t\t\t\tnode.Next.Position - node.GetLeftBoundary());\r\n\t\t\t\tnode.Up = Vector3.Normalize(normal);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\tprivate void ParseSceneryObjectsBlock(BinaryReader reader)\r\n\t\t{\r\n\t\t\treader.BaseStream.Position = 0x15B0C;\r\n\r\n\t\t\treader.BaseStream.Position += 1800; //jump over unknown index\r\n\t\t\tint objectDescriptorCount = reader.ReadInt32();\r\n\t\t\tint objectCount = reader.ReadInt32();\r\n\t\t\treader.ReadChars(4); //'OBJS'\r\n\t\t\treader.BaseStream.Position += 8;\r\n\r\n\t\t\tfor (int i = 0; i < objectDescriptorCount; i++)\r\n\t\t\t{\r\n\t\t\t\tSceneryObjectDescriptor sd = new SceneryObjectDescriptor();\r\n\t\t\t\tbyte[] bytes = reader.ReadBytes(16);\r\n\r\n\t\t\t\tsd.Flags = (SceneryFlags)bytes[0];\r\n\t\t\t\tsd.Type = (SceneryType)bytes[1];\r\n\t\t\t\tsd.Width = BitConverter.ToInt16(bytes, 6);\r\n\t\t\t\tsd.Width *= GameConfig.TerrainScale * 65000;\r\n\t\t\t\tsd.Height = BitConverter.ToInt16(bytes, 14);\r\n\t\t\t\tsd.Height *= GameConfig.TerrainScale * 65000;\r\n\t\t\t\tsd.ResourceId = bytes[2];\r\n\r\n\t\t\t\tif (sd.Type == SceneryType.TwoSidedBitmap)\r\n\t\t\t\t{\r\n\t\t\t\t\tsd.Resource2Id = bytes[3];\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif ((sd.Flags & SceneryFlags.Animated) == SceneryFlags.Animated)\r\n\t\t\t\t{\r\n\t\t\t\t\tsd.AnimationFrameCount = bytes[8];\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tsd.Id = ObjectDescriptors.Count;\r\n\t\t\t\tObjectDescriptors.Add(sd);\r\n\t\t\t}\r\n\r\n\t\t\tlong currentPos = reader.BaseStream.Position;\r\n\r\n\t\t\tfor (int i = 0; i < objectCount; i++)\r\n\t\t\t{\r\n\t\t\t\tint referenceNode = reader.ReadInt32();\r\n\t\t\t\tif (referenceNode == -1)\r\n\t\t\t\t{\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tSceneryObject obj = new SceneryObject();\r\n\t\t\t\tobj.ReferenceNode = referenceNode % Nodes.Count;  //why do some tracks reference nodes above the node count?\r\n\t\t\t\tint descriptorRef = reader.ReadByte();\r\n\t\t\t\tobj.Descriptor = ObjectDescriptors[descriptorRef];\r\n\r\n\t\t\t\tobj.Orientation = (float)reader.ReadByte();\r\n\t\t\t\tbyte[] flags = reader.ReadBytes(4);\r\n\t\t\t\tobj.RelativePosition = new Vector3(reader.ReadInt16(), reader.ReadInt16(), -reader.ReadInt16());\r\n\t\t\t\tScenery.Add(obj);\r\n\t\t\t}\r\n\t\t\treader.BaseStream.Position = currentPos + 16000; //jump to end of billboards            \r\n\t\t\treader.BaseStream.Position += 492; //blank space\r\n\t\t}\r\n\r\n\t\tprivate void ParseTerrainBlock(BinaryReader reader)\r\n\t\t{\r\n\t\t\treader.BaseStream.Position = 0x1A4A8;\r\n\r\n\t\t\tlong fileSize = reader.BaseStream.Length;\r\n\t\t\tlong position = reader.BaseStream.Position;\r\n\r\n\t\t\tTerrainSegment last = null;\r\n\t\t\t\r\n\t\t\twhile (true)\r\n\t\t\t{\r\n\t\t\t\tchar[] trkd = reader.ReadChars(4);  //\"TRKD\"\r\n\t\t\t\tif (trkd[0] == 'C')  //Strange format un-used AL22.TRI\r\n\t\t\t\t{\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tint blockLength = reader.ReadInt32();\r\n\t\t\t\tint blockNumber = reader.ReadInt32();\r\n\t\t\t\tbyte unk1 = reader.ReadByte();\r\n\t\t\t\tbyte fenceType = reader.ReadByte();\r\n\r\n\t\t\t\tTerrainSegment terrainSegment = new TerrainSegment();\r\n\t\t\t\tterrainSegment.Number = Segments.Count;\r\n\t\t\t\tterrainSegment.TextureIds = reader.ReadBytes(10);\r\n\t\t\t\t\r\n\t\t\t\tterrainSegment.Rows[0] = ReadTerrainRow(reader);\r\n\t\t\t\tterrainSegment.Rows[1] = ReadTerrainRow(reader);\r\n\t\t\t\tterrainSegment.Rows[2] = ReadTerrainRow(reader);\r\n\t\t\t\tterrainSegment.Rows[3] = ReadTerrainRow(reader);\t\t\t\t\t\t\t\t\r\n\r\n\t\t\t\t//fenceType stores the sides of the road the fence lives, and the textureId to use for it.\r\n\t\t\t\t// If the top bit is set, fence on the left exists, if next bit is set, fence is on the right.  Both can also be set. \r\n\t\t\t\t//The other 6 bits seem to the texture number\r\n\t\t\t\tif (fenceType != 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tbool bit7 = (fenceType & (0x1 << 7)) != 0;\r\n\t\t\t\t\tbool bit6 = (fenceType & (0x1 << 6)) != 0;\r\n\r\n\t\t\t\t\tterrainSegment.HasLeftFence = bit7;\r\n\t\t\t\t\tterrainSegment.HasRightFence = bit6;\r\n\r\n\t\t\t\t\t// Ignore the top 2 bits to find the texture to use\r\n\t\t\t\t\tterrainSegment.FenceTextureId = fenceType & (0xff >> 2);\r\n\t\t\t\t\t//Debug.WriteLine(\"fence: \" + fenceType + \", texture: \" + terrainSegment.FenceTextureId + \", \" + terrainSegment.HasLeftFence + \", \" + terrainSegment.HasRightFence);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t//Debug.WriteLine(\"TRKD: \" + i + \", \" + terrainSegment.Rows[0].RightPoints[5] + \", fence: \" + terrainSegment.FenceTextureId + \" , \" + terrainSegment.HasLeftFence + \", \" + terrainSegment.HasRightFence);\r\n\r\n\t\t\t\tif (last != null)\r\n\t\t\t\t{\r\n\t\t\t\t\tlast.Next = terrainSegment;\r\n\t\t\t\t\tterrainSegment.Prev = last;\r\n\t\t\t\t}\r\n\t\t\t\tlast = terrainSegment;\r\n\t\t\t\tSegments.Add(terrainSegment);\r\n\r\n\t\t\t\t//skip to end of block (+12 to include block header)\r\n\t\t\t\tposition += blockLength + 12;\r\n\t\t\t\treader.BaseStream.Position = position;\r\n\t\t\t\t\r\n\t\t\t\tif (reader.BaseStream.Position >= fileSize)\r\n\t\t\t\t{\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// If this is a circuit track, hook the last segment up to the first\r\n\t\t\tif (!IsOpenRoad)\r\n\t\t\t{\r\n\t\t\t\tlast.Next = Segments[0];\r\n\t\t\t\tSegments[0].Prev = last;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate TerrainRow ReadTerrainRow(BinaryReader reader)\r\n\t\t{\r\n\t\t\tTerrainRow row = new TerrainRow();\r\n\t\t\trow.MiddlePoint = new Vector3(reader.ReadInt16(), reader.ReadInt16(), -reader.ReadInt16());\r\n\t\t\t\r\n\t\t\trow.RightPoints[0] = row.MiddlePoint;\r\n\t\t\trow.RightPoints[1] = new Vector3(reader.ReadInt16(), reader.ReadInt16(), -reader.ReadInt16());\r\n\t\t\trow.RightPoints[2] = new Vector3(reader.ReadInt16(), reader.ReadInt16(), -reader.ReadInt16());\r\n\t\t\trow.RightPoints[3] = new Vector3(reader.ReadInt16(), reader.ReadInt16(), -reader.ReadInt16());\r\n\t\t\trow.RightPoints[4] = new Vector3(reader.ReadInt16(), reader.ReadInt16(), -reader.ReadInt16());\r\n\t\t\trow.RightPoints[5] = new Vector3(reader.ReadInt16(), reader.ReadInt16(), -reader.ReadInt16());\r\n\r\n\t\t\trow.LeftPoints[0] = row.MiddlePoint;\r\n\t\t\trow.LeftPoints[1] = new Vector3(reader.ReadInt16(), reader.ReadInt16(), -reader.ReadInt16());\r\n\t\t\trow.LeftPoints[2] = new Vector3(reader.ReadInt16(), reader.ReadInt16(), -reader.ReadInt16());\r\n\t\t\trow.LeftPoints[3] = new Vector3(reader.ReadInt16(), reader.ReadInt16(), -reader.ReadInt16());\r\n\t\t\trow.LeftPoints[4] = new Vector3(reader.ReadInt16(), reader.ReadInt16(), -reader.ReadInt16());\r\n\t\t\trow.LeftPoints[5] = new Vector3(reader.ReadInt16(), reader.ReadInt16(), -reader.ReadInt16());\r\n\r\n\t\t\trow.MiddlePoint *= GameConfig.TerrainScale;\r\n\t\t\tfor (int i = 0; i < NbrTerrainPointsPerSide; i++)\r\n\t\t\t\trow.RightPoints[i] *= GameConfig.TerrainScale;\r\n\t\t\tfor (int i = 0; i < NbrTerrainPointsPerSide; i++)\r\n\t\t\t\trow.LeftPoints[i] *= GameConfig.TerrainScale;\r\n\r\n\t\t\t// Each point is relative to the previous point\r\n\t\t\tfor (int i = 1; i < NbrTerrainPointsPerSide; i++)\r\n\t\t\t{\r\n\t\t\t\trow.RightPoints[i] += row.RightPoints[i - 1];\r\n\t\t\t}\r\n\r\n\t\t\tfor (int i = 1; i < NbrTerrainPointsPerSide; i++)\r\n\t\t\t{\r\n\t\t\t\trow.LeftPoints[i] += row.LeftPoints[i - 1];\r\n\t\t\t}\r\n\r\n\t\t\treturn row;\r\n\t\t}\r\n\r\n\t\tprivate void ComputeAbsoluteTerrainPoints()\r\n\t\t{\r\n\t\t\tvar segmentIndex = 0;\r\n\t\t\tvar nodeIndex = 0;\r\n\t\t\tforeach (var segment in Segments)\r\n\t\t\t{\r\n\r\n\t\t\t\tif (segmentIndex == 48)\r\n\t\t\t\t{\r\n\r\n\t\t\t\t}\r\n\t\t\t\tfor (int i = 0; i < NbrRowsPerSegment; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar relativePoint = Nodes[nodeIndex].Position;\r\n\t\t\t\t\tsegment.Rows[i].RelativizeTo(relativePoint);\r\n\t\t\t\t\tnodeIndex++;\r\n\t\t\t\t}\r\n\t\t\t\tsegmentIndex++;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Physics/AutoGearbox.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework.Input;\r\n\r\nnamespace OpenNFS1.Physics\r\n{\r\n    class AutoGearbox : BaseGearbox\r\n    {\r\n        private const float ChangeUpPoint = 0.94f;\r\n        private const float ChangeDownPoint = 0.65f;\r\n\r\n        public AutoGearbox(List<float> ratios, float changeTime)\r\n            : base(ratios, changeTime)\r\n        {\r\n        }\r\n\r\n        public override void Update(float motorRpmPercent, GearboxAction action)\r\n        {\r\n\t\t\tif (_motor.Rpm < 2 && (_currentGear == GEAR_NEUTRAL || _currentGear == GEAR_1) && action == GearboxAction.GearDown)\r\n\t\t\t{\r\n\t\t\t\tGearDown();\r\n\t\t\t}\r\n\t\t\tif ((_currentGear == GEAR_REVERSE || _currentGear == GEAR_NEUTRAL) && action == GearboxAction.GearUp)\r\n\t\t\t{\r\n\t\t\t\tGearUp();\r\n\t\t\t}\r\n            \r\n            if (!_motor.WheelsSpinning)\r\n            {\r\n                \r\n                if (_currentGear == GEAR_REVERSE || _currentGear == GEAR_NEUTRAL)\r\n                {\r\n\r\n                }\r\n                else\r\n                {\r\n                    if (motorRpmPercent > ChangeUpPoint && _currentGear < Ratios.Count - 1)\r\n                        GearUp();\r\n                    if (motorRpmPercent < ChangeDownPoint && CurrentGear > 1 && GearEngaged && !_motor.IsAccelerating && _motor.CanChangeDown)\r\n                        GearDown();\r\n                }\r\n            }\r\n\r\n            base.Update(motorRpmPercent, action);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Physics/BaseGearbox.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing System.Diagnostics;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\n\r\nnamespace OpenNFS1.Physics\r\n{\r\n    class GearboxGearChange\r\n    {\r\n        public int Change;\r\n        public float TimeTillEngaged;\r\n    }\r\n\r\n\tenum GearboxAction\r\n\t{\r\n\t\tNone,\r\n\t\tGearUp,\r\n\t\tGearDown\r\n\t}\r\n\r\n    abstract class BaseGearbox\r\n    {\r\n        public const int GEAR_REVERSE = 0;\r\n        public const int GEAR_NEUTRAL = 1;\r\n        public const int GEAR_1 = 2;\r\n\r\n        public event EventHandler GearChangeStarted;\r\n        public event EventHandler GearChangeCompleted;\r\n\r\n        private List<float> _ratios;\r\n\r\n        protected float _changeTime;\r\n        protected int _currentGear;\r\n        protected GearboxGearChange _gearChange;\r\n        protected Motor _motor;\r\n        private float _clutch;\r\n\r\n        public float Clutch\r\n        {\r\n            get { return _clutch; }\r\n            set { _clutch = value; }\r\n        }\r\n\r\n        public List<float> Ratios\r\n        {\r\n            get { return _ratios; }\r\n        }\r\n\r\n        public int CurrentGear\r\n        {\r\n            get { return _currentGear - 1; }\r\n            set { _currentGear = value + 1; }\r\n        }\r\n\r\n        public float CurrentRatio\r\n        {\r\n            get { return _ratios[_currentGear]; }\r\n        }\r\n\r\n        public float NextRatio\r\n        {\r\n            get { return _ratios[_currentGear + _gearChange.Change]; }\r\n        }\r\n\r\n        public int NextGear\r\n        {\r\n            get { return CurrentGear + _gearChange.Change; }\r\n        }\r\n\r\n        public bool GearEngaged\r\n        {\r\n            get { return _gearChange == null; }\r\n        }\r\n\r\n        public Motor Motor\r\n        {\r\n            set { _motor = value; }\r\n        }\r\n\r\n        public BaseGearbox(List<float> ratios, float changeTime)\r\n        {\r\n            ratios.Insert(0, -ratios[0] * 1.5f); //insert reverse\r\n            ratios.Insert(1, 0); //insert neutral\r\n            \r\n            _ratios = ratios;\r\n            _changeTime = changeTime;\r\n\t\t\t_currentGear = GEAR_NEUTRAL;\r\n        }\r\n\r\n        public void GearUp()\r\n        {\r\n            if (_gearChange == null)\r\n            {\r\n                _gearChange = new GearboxGearChange();\r\n                _gearChange.Change = 1;\r\n                _gearChange.TimeTillEngaged = _changeTime;\r\n                _clutch = 0.0f;\r\n                GearChangeStarted(this, null);\r\n            }\r\n        }\r\n\r\n        public void GearDown()\r\n        {\r\n            if (_gearChange == null)\r\n            {\r\n                _gearChange = new GearboxGearChange();\r\n                _gearChange.Change = -1;\r\n                _gearChange.TimeTillEngaged = _changeTime;\r\n                _clutch = 0.0f;\r\n                GearChangeStarted(this, null);\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// \r\n        /// </summary>\r\n        /// <param name=\"motorRpmPercent\">0..1 where 1 is max rpms</param>\r\n        /// <param name=\"gameTime\"></param>\r\n        public virtual void Update(float motorRpmPercent, GearboxAction action)\r\n        {\r\n            if (_gearChange != null)\r\n            {\r\n\t\t\t\t_gearChange.TimeTillEngaged -= Engine.Instance.FrameTime;\r\n\r\n                if (_gearChange.TimeTillEngaged <= 0)\r\n                {\r\n                    if (_gearChange.Change > 0)\r\n                    {\r\n                        if (_currentGear < _ratios.Count - 1)\r\n                            _currentGear++;\r\n                    }\r\n                    else\r\n                    {\r\n                        if (_currentGear > -1)\r\n                            _currentGear--;\r\n                    }\r\n                    _clutch = 1.0f;\r\n                    _gearChange = null;\r\n                    if (GearChangeCompleted != null) GearChangeCompleted(this, null);\r\n                }\r\n                else\r\n                {\r\n                    _clutch = (_changeTime - _gearChange.TimeTillEngaged) / _changeTime;\r\n                }\r\n            }\r\n        }\r\n\r\n        public static BaseGearbox Create(bool manual, List<float> ratios, float changeTime)\r\n        {\r\n            if (manual)\r\n                return new ManualGearbox(ratios, changeTime);\r\n            else\r\n                return new AutoGearbox(ratios, changeTime);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "OpenNFS1/Physics/DrivableVehicle.cs",
    "content": "\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing GameEngine;\r\nusing OpenNFS1.Parsers.Track;\r\nusing OpenNFS1.Tracks;\r\nusing OpenNFS1.Vehicles;\r\n\r\n\r\nnamespace OpenNFS1.Physics\r\n{\r\n\r\n\tclass DrivableVehicle : Vehicle\r\n\t{\r\n\t\tconst float CarFrictionOnRoad = 14;\r\n\t\tconst float AirFrictionPerSpeed = 0.07f;\r\n\t\tconst float MaxAirFriction = AirFrictionPerSpeed * 100.0f;\r\n\t\t// for reflecting away from walls\r\n\t\tpublic float MaxRotationPerSec = 5f;\r\n\t\tpublic float RearSlipFactorSpeed = 1.6f;\r\n\t\tpublic float RearSlipFactorMax = 0.5f;\r\n\t\tpublic float FrontSlipMultiplier = 0.0000017f;\r\n\r\n\t\tpublic VehicleDescription Descriptor { get; private set; }\r\n\r\n\t\tpublic float BrakePower = 70;\r\n\t\tpublic float HandbrakePower = 40;\r\n\r\n\t\tpublic Motor Motor { get; private set; }\r\n\t\tpublic Vector3 RenderDirection;\r\n\t\tpublic Spring BodyPitch { get; private set; }\r\n\t\tpublic Spring BodyRoll { get; private set; }\r\n\r\n\t\tprivate float _rotateAfterCollision;\r\n\t\tpublic float RotateCarAfterCollision\r\n\t\t{\r\n\t\t\tset\r\n\t\t\t{\r\n\t\t\t\t_rotateAfterCollision = value;\r\n\t\t\t\t_rotationChange = 0;\r\n\t\t\t}\r\n\t\t}\r\n\t\tpublic VehicleWheel[] Wheels { get; private set; }\r\n\r\n\t\tfloat _frontSlipFactor, _rearSlipFactor;\t\t\r\n\t\tVector3 _force;\t\r\n\t\tVehicleAudioProvider _audioProvider;\r\n\t\tint _nbrWheelsOffRoad;\r\n\t\tfloat _traction = 520;\r\n\t\t\r\n\t\t// inputs\r\n\t\tpublic float ThrottlePedalInput, BrakePedalInput;\r\n\t\tpublic bool GearUpInput, GearDownInput, HandbrakeInput;\r\n\t\tpublic bool AutoDrift;  //used for ai racers\r\n\r\n\t\tpublic DrivableVehicle(VehicleDescription desc)\r\n\t\t\t: base(desc.ModelFile)\r\n\t\t{\r\n\t\t\tDescriptor = desc;\r\n\t\t\tfloat offset = VehicleWheel.Width / 2 - 0.1f;\r\n\t\t\tWheels = new VehicleWheel[4];\r\n\t\t\tWheels[0] = new VehicleWheel(this, _model.LeftFrontWheelPos, _model.FrontWheelSize, _model.WheelTexture, offset);\r\n\t\t\tWheels[1] = new VehicleWheel(this, _model.RightFrontWheelPos, _model.FrontWheelSize, _model.WheelTexture, -offset);\r\n\t\t\tWheels[2] = new VehicleWheel(this, _model.LeftRearWheelPos, _model.RearWheelSize, _model.WheelTexture, offset);\r\n\t\t\tWheels[3] = new VehicleWheel(this, _model.RightRearWheelPos, _model.RearWheelSize, _model.WheelTexture, -offset);\r\n\r\n\t\t\tList<float> power = new List<float>(new float[] { 0.2f, 0.3f, 0.4f, 0.7f, 0.8f, 1.0f, 0.8f, 0.8f, 0.8f, 0.3f });\r\n\t\t\tList<float> ratios = new List<float>(new float[] { 3.827f, 2.360f, 1.685f, 1.312f, 1.000f, 0.793f });\r\n\r\n\t\t\tBaseGearbox gearbox = BaseGearbox.Create(GameConfig.ManualGearbox, ratios, 0.2f);\r\n\t\t\tMotor = new Motor(power, Descriptor.Horsepower, Descriptor.Redline, gearbox);\r\n\t\t\tMotor.Gearbox.GearChangeStarted += new EventHandler(Gearbox_GearChanged);\r\n\t\t\t_traction = (Motor.GetPowerAtRpmForGear(Motor.RedlineRpm, 2) * 30) - 30;\r\n\r\n\t\t\tBodyPitch = new Spring(1200, 1.5f, 200, 0, 1.4f);\r\n\t\t\tBodyRoll = new Spring(1200, 1.5f, 180, 0, 3);\r\n\r\n\t\t\t_audioProvider = new VehicleAudioProvider(this);\r\n\t\t}\r\n\r\n\r\n\t\tbool _audioEnabled;\r\n\t\tpublic bool AudioEnabled\r\n\t\t{\r\n\t\t\tget { return _audioEnabled; }\r\n\t\t\tset\r\n\t\t\t{\r\n\t\t\t\tif (value)\r\n\t\t\t\t\t_audioProvider.Initialize();\r\n\t\t\t\telse\r\n\t\t\t\t\t_audioProvider.StopAll();\r\n\t\t\t\t_audioEnabled = value;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void UpdateWheels()\r\n\t\t{\r\n\t\t\t//front wheels\r\n\t\t\tfor (int i = 0; i < 2; i++)\r\n\t\t\t\tWheels[i].Rotation -= Speed * Engine.Instance.FrameTime;\r\n\r\n\t\t\t//back wheels\r\n\t\t\tif (!HandbrakeInput)\r\n\t\t\t{\r\n\t\t\t\tfor (int i = 2; i < 4; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tWheels[i].Rotation -= Engine.Instance.FrameTime * (Motor.WheelsSpinning ? 50 : Speed);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t\t\r\n\r\n\t\t\tWheels[0].Steer(_steeringWheel);\r\n\t\t\tWheels[1].Steer(_steeringWheel);\r\n\r\n\t\t\tif (_isOnGround && BrakePedalInput > 0.5f && Math.Abs(Speed) > 10)\r\n\t\t\t{\r\n\t\t\t\tif (Speed < 80)\r\n\t\t\t\t{\r\n\t\t\t\t\tWheels[0].IsSkidding = Wheels[1].IsSkidding = Wheels[2].IsSkidding = Wheels[3].IsSkidding = true;\r\n\t\t\t\t}\r\n\t\t\t\t_audioProvider.PlaySkid(true);\r\n\t\t\t}\r\n\t\t\telse if (_isOnGround && Math.Abs(_rearSlipFactor) > 0)\r\n\t\t\t{\r\n\t\t\t\tWheels[2].IsSkidding = Wheels[3].IsSkidding = true;\r\n\t\t\t\t_audioProvider.PlaySkid(true);\r\n\t\t\t}\r\n\t\t\telse if (_isOnGround && Math.Abs(_steeringWheel) > 0.25f && _frontSlipFactor > 0.43f)\r\n\t\t\t{\r\n\t\t\t\t_audioProvider.PlaySkid(true);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t_audioProvider.PlaySkid(false);\r\n\t\t\t}\r\n\r\n\t\t\tforeach (VehicleWheel wheel in Wheels)\r\n\t\t\t\twheel.Update();\r\n\t\t}\r\n\r\n\t\t\r\n\r\n\t\tprivate void UpdateDrag()\r\n\t\t{\r\n\t\t\tfloat elapsedSeconds = Engine.Instance.FrameTime;\r\n\r\n\t\t\tfloat airFriction = AirFrictionPerSpeed * Math.Abs(Speed);\r\n\t\t\tif (airFriction > MaxAirFriction)\r\n\t\t\t\tairFriction = MaxAirFriction;\r\n\t\t\t// Don't use ground friction if we are not on the ground.\r\n\t\t\tfloat groundFriction = CarFrictionOnRoad;\r\n\t\t\tif (_isOnGround == false)\r\n\t\t\t\tgroundFriction = 0;\r\n\r\n\t\t\t_force *= 1.0f - (groundFriction + airFriction) * 0.06f * elapsedSeconds;\r\n\t\t\tSpeed *= 1.0f - (groundFriction + airFriction) * 0.0015f * elapsedSeconds;\r\n\r\n\t\t\tif (_isOnGround)\r\n\t\t\t{\r\n\t\t\t\tfloat drag = BrakePower * BrakePedalInput;\r\n\t\t\t\tif (HandbrakeInput) drag += HandbrakePower;\r\n\t\t\t\t\r\n\t\t\t\t// as we're turning, add drag\r\n\t\t\t\tdrag += Math.Abs(_steeringWheel) * 10f;\r\n\t\t\t\tif (Math.Abs(Speed) > 30)\r\n\t\t\t\t{\r\n\t\t\t\t\tdrag += _nbrWheelsOffRoad * 5f;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tdrag += Motor.CurrentFriction;\r\n\r\n\t\t\t\tif (Math.Abs(Speed) < 1 || drag < 0)\r\n\t\t\t\t\tdrag = 0;\r\n\r\n\t\t\t\tif (Speed > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tSpeed -= drag * elapsedSeconds;\r\n\t\t\t\t\tif (Speed < 0) Speed = 0; //avoid braking so hard we go backwards\r\n\t\t\t\t}\r\n\t\t\t\telse if (Speed < 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tSpeed += drag * elapsedSeconds;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void UpdateEngineForce()\r\n\t\t{\r\n\t\t\t_previousSpeed = Speed;\r\n\r\n\t\t\tfloat newAccelerationForce = 0.0f;\r\n\r\n\t\t\tMotor.Throttle = ThrottlePedalInput;\r\n\t\t\tnewAccelerationForce += Motor.CurrentPowerOutput * 0.4f;\r\n\r\n\t\t\tif (Motor.Gearbox.GearEngaged && Motor.Gearbox.CurrentGear > 0)\r\n\t\t\t{\r\n\t\t\t\tfloat tractionFactor = Math.Min(1, (_traction + Speed) / newAccelerationForce);\r\n\t\t\t\tMotor.WheelsSpinning = tractionFactor < 1 || (Motor.Rpm > 0.7f && Speed < 10 && Motor.Throttle > 0);\r\n\t\t\t\tif (Motor.WheelsSpinning)\r\n\t\t\t\t{\r\n\t\t\t\t\t_audioProvider.PlaySkid(true);\r\n\t\t\t\t\tWheels[2].IsSkidding = Wheels[3].IsSkidding = true;\r\n\t\t\t\t}\r\n\t\t\t\telse if (!_isOnGround)\r\n\t\t\t\t{\r\n\t\t\t\t\tMotor.WheelsSpinning = true;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tGearboxAction action = GearboxAction.None;\r\n\t\t\tif (GearUpInput) action = GearboxAction.GearUp;\r\n\t\t\telse if (GearDownInput) action = GearboxAction.GearDown;\r\n\t\t\tMotor.Update(Speed, action);\r\n\r\n\t\t\tif (Motor.AtRedline && !Motor.WheelsSpinning)\r\n\t\t\t{\r\n\t\t\t\t_force *= 0.2f;\r\n\t\t\t}\r\n\r\n\t\t\tif (Motor.Throttle == 0 && Math.Abs(Speed) < 1)\r\n\t\t\t{\r\n\t\t\t\tSpeed = 0;\r\n\t\t\t}\r\n\r\n\t\t\tif (_isOnGround)\r\n\t\t\t\t_force += Direction * newAccelerationForce * Engine.Instance.FrameTime * 2.5f;\r\n\t\t}\r\n\r\n\t\tpublic override void Update()\r\n\t\t{\r\n\t\t\tif (CurrentNode.Next == null) return;\r\n\t\t\tfloat elapsedSeconds = Engine.Instance.FrameTime;\r\n\r\n\t\t\tUpdateRearSlip();\r\n\t\t\tUpdateEngineForce();\r\n\t\t\tUpdateDrag();\r\n\r\n\t\t\tVector3 speedChangeVector = _force / Descriptor.Mass;\r\n\t\t\tif (_isOnGround && speedChangeVector.Length() > 0)\r\n\t\t\t{\r\n\t\t\t\tfloat speedApplyFactor = Vector3.Dot(Vector3.Normalize(speedChangeVector), Direction);\r\n\t\t\t\tif (speedApplyFactor > 1)\r\n\t\t\t\t\tspeedApplyFactor = 1;\r\n\t\t\t\tSpeed += speedChangeVector.Length() * speedApplyFactor;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tUpdateWheels();\r\n\t\t\tCheckForCollisions();\r\n\r\n\t\t\t// Calculate pitch depending on the force\r\n\t\t\tfloat speedChange = Speed - _previousSpeed;\r\n\t\t\tBodyPitch.ChangePosition(speedChange * 0.6f);\r\n\t\t\tBodyRoll.ChangePosition(_steeringWheel * -0.05f * Math.Min(1, Math.Abs(Speed) / 30));\r\n\t\t\tBodyPitch.Simulate(Engine.Instance.FrameTime * 2.5f);\r\n\t\t\tBodyRoll.Simulate(Engine.Instance.FrameTime * 2.5f);\r\n\r\n\t\t\t_audioProvider.UpdateEngine();\r\n\r\n\t\t\tbase.Update();\r\n\t\t}\r\n\r\n\t\tpublic override void HandleExtraSteeringPhysics()\r\n\t\t{\r\n\t\t\tfloat maxRot = MaxRotationPerSec * Engine.Instance.FrameTime;\r\n\r\n\t\t\t// Handle car rotation after collision\r\n\t\t\tif (_rotateAfterCollision != 0)\r\n\t\t\t{\r\n\t\t\t\t_audioProvider.PlaySkid(true);\r\n\r\n\t\t\t\tif (_rotateAfterCollision > maxRot)\r\n\t\t\t\t{\r\n\t\t\t\t\t_rotationChange += maxRot;\r\n\t\t\t\t\t_rotateAfterCollision -= maxRot;\r\n\t\t\t\t}\r\n\t\t\t\telse if (_rotateAfterCollision < -maxRot)\r\n\t\t\t\t{\r\n\t\t\t\t\t_rotationChange -= maxRot;\r\n\t\t\t\t\t_rotateAfterCollision += maxRot;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t_rotationChange += _rotateAfterCollision;\r\n\t\t\t\t\tRotateCarAfterCollision = 0;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t_frontSlipFactor = 0;\r\n\t\t\t\tif (_rotationChange != 0)\r\n\t\t\t\t{\r\n\t\t\t\t\t_frontSlipFactor = Math.Min(0.91f, Descriptor.Mass * Math.Abs(Speed) * FrontSlipMultiplier);\r\n\t\t\t\t\t_rotationChange *= 1 - _frontSlipFactor;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\r\n\t\tvoid UpdateRearSlip()\r\n\t\t{\r\n\t\t\tif (_isOnGround)\r\n\t\t\t{\r\n\t\t\t\tif (((AutoDrift && Speed > 100) || HandbrakeInput || (SteeringInput != 0 && _rearSlipFactor != 0)) && Speed > 10)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (SteeringInput < 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfloat prevSlipFactor = _rearSlipFactor;\r\n\t\t\t\t\t\t_rearSlipFactor = Math.Min(RearSlipFactorMax, _rearSlipFactor + RearSlipFactorSpeed * Engine.Instance.FrameTime);\r\n\t\t\t\t\t\tif (prevSlipFactor < 0 && _rearSlipFactor > 0) _rearSlipFactor = 0;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (SteeringInput > 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfloat prevSlipFactor = _rearSlipFactor;\r\n\t\t\t\t\t\t_rearSlipFactor = Math.Max(-RearSlipFactorMax, _rearSlipFactor - RearSlipFactorSpeed * Engine.Instance.FrameTime);\r\n\t\t\t\t\t\tif (prevSlipFactor > 0 && _rearSlipFactor < 0) _rearSlipFactor = 0;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (SteeringInput == 0 || Speed < 10)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (Math.Abs(_rearSlipFactor) > 0.03f)\r\n\t\t\t\t\t\t_rearSlipFactor += 0.7f * (_rearSlipFactor > 0 ? -RearSlipFactorSpeed : RearSlipFactorSpeed) * Engine.Instance.FrameTime;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\t_rearSlipFactor = 0;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tRenderDirection = Vector3.TransformNormal(Direction, Matrix.CreateFromAxisAngle(Up, _rearSlipFactor));\r\n\t\t}\r\n\r\n\t\tvoid CheckForCollisions()\r\n\t\t{\r\n\t\t\t_nbrWheelsOffRoad = VehicleFenceCollision.GetWheelsOutsideRoadVerge(this);\r\n\r\n\t\t\tif (Speed > 3 && _nbrWheelsOffRoad > 0)\r\n\t\t\t{\r\n\t\t\t\t_audioProvider.PlayOffRoad(true);\r\n\t\t\t\tWheels[2].IsSkidding = Wheels[3].IsSkidding = true;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t_audioProvider.PlayOffRoad(false);\r\n\t\t\t}\r\n\t\t\tVehicleFenceCollision.Handle(this);\r\n\t\t}\r\n\r\n\t\tpublic void RenderShadow(bool isPlayer)\r\n\t\t{\r\n\t\t\t// Shadow\r\n\t\t\tVector3[] points = new Vector3[4];\r\n\t\t\tfloat y = -Wheels[0].Size / 2 + 0.4f;\r\n\t\t\tfloat xoffset = 0.1f;\r\n\t\t\tpoints[0] = Wheels[0].GetOffsetPosition(new Vector3(-xoffset, y, -2));\r\n\t\t\tpoints[1] = Wheels[1].GetOffsetPosition(new Vector3(xoffset, y, -2));\r\n\t\t\tpoints[2] = Wheels[2].GetOffsetPosition(new Vector3(-xoffset, y, 3.5f));\r\n\t\t\tpoints[3] = Wheels[3].GetOffsetPosition(new Vector3(xoffset, y, 3.5f));\r\n\r\n\t\t\tif (!_isOnGround)\r\n\t\t\t{\r\n\t\t\t\tpoints[0].Y = _currentHeightOfTrack;\r\n\t\t\t\tpoints[1].Y = _currentHeightOfTrack;\r\n\t\t\t\tpoints[2].Y = _currentHeightOfTrack;\r\n\t\t\t\tpoints[3].Y = _currentHeightOfTrack;\r\n\t\t\t}\r\n\t\t\tObjectShadow.Render(points, isPlayer);\r\n\t\t}\r\n\r\n\t\tpublic override void Render()\r\n\t\t{\r\n\t\t\t_effect.View = Engine.Instance.Camera.View;\r\n\t\t\t_effect.Projection = Engine.Instance.Camera.Projection;\r\n\t\t\t_effect.World = GetRenderMatrix();\r\n\t\t\t_effect.CurrentTechnique.Passes[0].Apply();\r\n\t\t\t_model.Render(_effect, BrakePedalInput > 0);\r\n\t\t\t\r\n\t\t\tWheelModel.BeginBatch();\r\n\t\t\tforeach (VehicleWheel wheel in Wheels)\r\n\t\t\t{\r\n\t\t\t\twheel.Render();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic override Matrix GetRenderMatrix()\r\n\t\t{\r\n\t\t\tMatrix orientation = Matrix.Identity;\r\n\t\t\torientation.Right = Vector3.Cross(RenderDirection, Up);\r\n\t\t\torientation.Up = Up;\r\n\t\t\torientation.Forward = RenderDirection;\r\n\r\n\t\t\treturn Matrix.CreateRotationX(BodyPitch.Position / 60) *\r\n\t\t\t\t\tMatrix.CreateRotationZ(-BodyRoll.Position * 0.21f) *\r\n\t\t\t\t\torientation *\r\n\t\t\t\t\tMatrix.CreateTranslation(Position);\r\n\t\t}\r\n\r\n\t\tprotected void Gearbox_GearChanged(object sender, EventArgs e)\r\n\t\t{\r\n\t\t\t_audioProvider.ChangeGear();\r\n\t\t}\r\n\r\n\t\tpublic override void OnGroundHit()\r\n\t\t{\r\n\t\t\t_audioProvider.HitGround();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Physics/ManualGearbox.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework.Input;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace OpenNFS1.Physics\r\n{\r\n    class ManualGearbox : BaseGearbox\r\n    {\r\n        public ManualGearbox(List<float> ratios, float changeTime)\r\n            : base(ratios, changeTime)\r\n        { }\r\n\r\n\r\n        public override void Update(float motorRpmPercent, GearboxAction action)\r\n        {\r\n            if (action == GearboxAction.GearUp && _currentGear < Ratios.Count - 1)\r\n                GearUp();\r\n            if (action == GearboxAction.GearDown && CurrentGear > -1 && _motor.CanChangeDown)\r\n                GearDown();\r\n\r\n            base.Update(motorRpmPercent, action);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Physics/Motor.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing System.Diagnostics;\r\nusing GameEngine;\r\n\r\nnamespace OpenNFS1.Physics\r\n{\r\n\tclass Motor\r\n\t{\r\n        private const float DRIVETRAIN_MULTIPLIER = 34;\r\n\r\n\t\tprivate List<float> _powerCurve;\r\n        private float _maxPower;\r\n        private float _redlineRpm;\r\n        private BaseGearbox _gearbox;\r\n        private float _rpm, _prevRpm, _prevEngagedRpm;\r\n        private float _throttle;\r\n        private float _currentPowerOutput;\r\n        private float _rpmLimiter;\r\n        private float _lastCarSpeed;\r\n\r\n        public float CurrentPowerOutput\r\n        {\r\n            get\r\n            {\r\n                if (_gearbox.GearEngaged)\r\n                    return _currentPowerOutput * _throttle * _gearbox.CurrentRatio;\r\n                else\r\n                    return 0;\r\n            }\r\n        }\r\n\r\n        public float CurrentFriction\r\n        {\r\n            get\r\n            {\r\n                if (_gearbox.GearEngaged && _throttle == 0)\r\n                    return Math.Abs(_gearbox.CurrentRatio) * 4;\r\n                else\r\n                    return 0;\r\n            }\r\n        }\r\n\r\n        public float MaxPower\r\n        {\r\n            get { return _maxPower; }\r\n            set { _maxPower = value; }\r\n        }\r\n\r\n        public bool AtRedline\r\n        {\r\n            get { return _rpm >= _redlineRpm; }\r\n        }\r\n\r\n        public float Throttle\r\n        {\r\n            get { return _throttle; }\r\n            set { _throttle = value; }\r\n        }\r\n\r\n\t\tpublic float Rpm\r\n\t\t{\r\n\t\t\tget { return _rpm; }\r\n\t\t}\r\n\r\n        public float RedlineRpm\r\n        {\r\n            get { return _redlineRpm; }\r\n        }\r\n\r\n        public bool IsAccelerating\r\n        {\r\n            get { return _rpm > _prevRpm; }\r\n        }\r\n\r\n        internal BaseGearbox Gearbox\r\n        {\r\n            get { return _gearbox; }\r\n        }\r\n\r\n\t\tpublic Motor(List<float> powerCurve, float maxPower, float redline, BaseGearbox gearbox)\r\n\t\t{\r\n\t\t\t_powerCurve = powerCurve;\r\n            _maxPower = maxPower;\r\n\t\t\t_redlineRpm = redline;\r\n\t\t\t_gearbox = gearbox;\r\n            _gearbox.Motor = this;\r\n\t\t}\r\n\r\n\r\n        public void Update(float carSpeed, GearboxAction action)\r\n        {\r\n            _prevRpm = _rpm;\r\n            _lastCarSpeed = carSpeed;\r\n\r\n            if (_rpm >= _redlineRpm)\r\n            {\r\n                _rpm = _redlineRpm;\r\n            }\r\n\r\n            if (_rpmLimiter > 0)\r\n            {\r\n                if (!WheelsSpinning) _currentPowerOutput = 0;\r\n                //_throttle = 0;\r\n\t\t\t\t_rpmLimiter -= Engine.Instance.FrameTime;\r\n            }\r\n            else\r\n                _currentPowerOutput = _maxPower * MathHelper.Lerp(_powerCurve[(int)_rpm], _powerCurve[(int)_rpm + 1], _rpm - (int)_rpm);\r\n\r\n            if (_gearbox.GearEngaged)\r\n            {\r\n                if (_gearbox.CurrentGear == 0 || WheelsSpinning)\r\n                {\r\n                    HandleRpmNoLoad();\r\n                }\r\n                else\r\n                {\r\n                    _rpm = carSpeed * _gearbox.CurrentRatio / DRIVETRAIN_MULTIPLIER;\r\n                    if (_rpm < 0.8f)\r\n                        _rpm = 0.8f;  //idle speed\r\n                }\r\n                _prevEngagedRpm = _rpm;\r\n            }\r\n            else\r\n            {\r\n                _rpm = MathHelper.Lerp(_prevEngagedRpm /*Math.Abs(carSpeed) * _gearbox.CurrentRatio / DRIVETRAIN_MULTIPLIER*/,\r\n                    carSpeed * _gearbox.NextRatio / DRIVETRAIN_MULTIPLIER, _gearbox.Clutch);   \r\n            }\r\n\r\n            if (_rpm < 0.8f)\r\n                _rpm = 0.8f;\r\n\r\n            if (_rpm >= _redlineRpm)\r\n            {\r\n                _rpmLimiter = 0.2f;\r\n\t\t\t\t_rpm = _redlineRpm;\r\n            }\r\n\r\n            _gearbox.Update(_rpm / _redlineRpm, action);\r\n        }\r\n\r\n        public float GetPowerAtRpmForGear(float rpm, int gear)\r\n        {\r\n            float power = _maxPower * MathHelper.Lerp(_powerCurve[(int)_redlineRpm], _powerCurve[(int)_redlineRpm + 1], _redlineRpm - (int)_redlineRpm);\r\n            power *= _gearbox.Ratios[gear];\r\n            return power;\r\n        }\r\n\r\n        public void Idle()\r\n        {\r\n            _rpm = 0.8f;\r\n            _gearbox.CurrentGear = 1;\r\n        }\r\n\r\n        public float GetRpmForGear(int gear)\r\n        {\r\n            return _lastCarSpeed * _gearbox.Ratios[gear] / DRIVETRAIN_MULTIPLIER;\r\n        }\r\n\r\n        private void HandleRpmNoLoad()\r\n        {\r\n            if (_throttle == 0.0f || _rpmLimiter > 0)\r\n            {\r\n                _rpm -= Engine.Instance.FrameTime * 4.4f;\r\n\r\n                if (_rpm < 0.8f)\r\n                    _rpm = 0.8f;\r\n            }\r\n            else\r\n            {\r\n\t\t\t\t_rpm += Engine.Instance.FrameTime *_throttle * 8f;\r\n            }\r\n        }\r\n\r\n        public bool WheelsSpinning { get; set; }\r\n\r\n        public bool CanChangeDown\r\n        {\r\n            get\r\n            {\r\n                return GetRpmForGear(_gearbox.CurrentGear) / RedlineRpm < 0.9f;\r\n            }\r\n        }\r\n\t}\r\n}"
  },
  {
    "path": "OpenNFS1/Physics/Spring.cs",
    "content": "using System;\r\nusing System.Collections;\r\nusing System.Text;\r\n\r\n\r\nnamespace OpenNFS1.Physics\r\n{\r\n    /// <summary>\r\n    /// Simple 1D spring\r\n    /// </summary>\r\n    class Spring\r\n    {\r\n        private float _mass;\r\n        private float _friction;\r\n        private float _springConstant;\r\n        private float _position = 0.0f;\r\n        private float _velocity = 0.0f;\r\n        private float _force = 0.0f;\r\n        private float _maxValue;\r\n\r\n        public float Position\r\n        {\r\n            get { return _position; }\r\n        }\r\n\r\n        /// <summary>\r\n        /// Create simple 1D spring with specified values.\r\n        /// </summary>\r\n        public Spring(float mass, float friction, float springConstant, float initialPosition, float maxValue)\r\n        {\r\n            _mass = mass;\r\n            _friction = friction;\r\n            _springConstant = springConstant;\r\n            _position = initialPosition;\r\n            _force = 0;\r\n            _velocity = 0;\r\n            _maxValue = maxValue;\r\n        }\r\n\r\n        \r\n        public void Simulate(float timeChange)\r\n        {\r\n            // Calculate force again\r\n            _force += -_position * _springConstant;\r\n            // Calculate velocity\r\n            _velocity = _force / _mass;\r\n            // And apply it to the current position\r\n            _position += timeChange * _velocity;\r\n            // Apply friction\r\n            _force *= 1.0f - (timeChange * _friction);\r\n        }\r\n        \r\n        public void ChangePosition(float change)\r\n        {\r\n            _position += change;\r\n            if (_position < 0 && _position < -_maxValue)\r\n                _position = -_maxValue;\r\n            else if (_position > 0 && _position > _maxValue)\r\n                _position = _maxValue;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Physics/Vector3Helper.cs",
    "content": "// Project: RacingGame, File: Vector3Helper.cs\r\n// Namespace: RacingGame.Helpers, Class: Vector3Helper\r\n// Path: C:\\code\\RacingGame\\Helpers, Author: Abi\r\n// Code lines: 62, Size of file: 2,14 KB\r\n// Creation date: 13.10.2006 23:14\r\n// Last modified: 20.10.2006 08:29\r\n// Generated with Commenter by abi.exDream.com\r\n\r\nusing Microsoft.Xna.Framework;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing System.IO;\r\n\r\n\r\nnamespace OpenNFS1.Physics\r\n{\r\n    /// <summary>\r\n    /// Vector 3 helper\r\n    /// </summary>\r\n    class Vector3Helper\r\n    {\r\n        #region Constructor\r\n        /// <summary>\r\n        /// Private constructor to prevent instantiation.\r\n        /// </summary>\r\n        private Vector3Helper()\r\n        {\r\n        } // Vector3Helper()\r\n        #endregion\r\n\r\n        #region GetAngleBetweenVectors\r\n        /// <summary>\r\n        /// Return angle between two vectors. Used for visbility testing and\r\n        /// for checking angles between vectors for the road sign generation.\r\n        /// </summary>\r\n        /// <param name=\"vec1\">Vector 1</param>\r\n        /// <param name=\"vec2\">Vector 2</param>\r\n        /// <returns>Float</returns>\r\n        public static float GetAngleBetweenVectors(Vector3 vec1, Vector3 vec2)\r\n        {\r\n            // See http://en.wikipedia.org/wiki/Vector_(spatial)\r\n            // for help and check out the Dot Product section ^^\r\n            // Both vectors are normalized so we can save deviding through the\r\n            // lengths.\r\n            return (float)Math.Acos(Vector3.Dot(vec1, vec2));\r\n        } // GetAngleBetweenVectors(vec1, vec2)\r\n        #endregion\r\n\r\n        #region DistanceToLine\r\n        /// <summary>\r\n        /// Distance from our point to the line described by linePos1 and linePos2.\r\n        /// </summary>\r\n        /// <param name=\"point\">Point</param>\r\n        /// <param name=\"linePos1\">Line position 1</param>\r\n        /// <param name=\"linePos2\">Line position 2</param>\r\n        /// <returns>Float</returns>\r\n        public static float DistanceToLine(Vector3 point, Vector3 linePos1, Vector3 linePos2)\r\n        {\r\n            // For help check out this article:\r\n            // http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html\r\n            Vector3 lineVec = linePos2 - linePos1;\r\n            Vector3 pointVec = linePos1 - point;\r\n            return Vector3.Cross(lineVec, pointVec).Length() / lineVec.Length();\r\n        }\r\n        #endregion\r\n\r\n        #region SignedDistanceToPlane\r\n        /// <summary>\r\n        /// Signed distance to plane\r\n        /// </summary>\r\n        /// <param name=\"point\">Point</param>\r\n        /// <param name=\"planePosition\">Plane position</param>\r\n        /// <param name=\"planeNormal\">Plane normal</param>\r\n        /// <returns>Float</returns>\r\n        public static float SignedDistanceToPlane(Vector3 point, Vector3 planePosition, Vector3 planeNormal)\r\n        {\r\n            Vector3 pointVec = planePosition - point;\r\n            return Vector3.Dot(Vector3.Normalize(planeNormal), pointVec);\r\n        }\r\n        #endregion\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Physics/VehicleFenceCollision.cs",
    "content": "using System;\r\nusing System.Diagnostics;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing OpenNFS1.Audio;\r\n\r\nnamespace OpenNFS1.Physics\r\n{\r\n    \r\n    class VehicleFenceCollision\r\n    {\r\n        public static void Handle(DrivableVehicle car)\r\n        {\r\n\t\t\tvar leftBound1 = car.CurrentNode.GetLeftBoundary();\r\n\t\t\tvar leftBound2 = car.CurrentNode.Next.GetLeftBoundary();\r\n\r\n\t\t\tvar rightBound1 = car.CurrentNode.GetRightBoundary();\r\n\t\t\tvar rightBound2 = car.CurrentNode.Next.GetRightBoundary();\r\n            \r\n\r\n\t\t\tfor (int wheelNumber = 0; wheelNumber < 4; wheelNumber++)\r\n\t\t\t{\r\n\t\t\t\tVehicleWheel wheel = car.Wheels[wheelNumber];\r\n\t\t\t\tVector3 fenceNormal = Vector3.Zero;\r\n\t\t\t\tbool collision = false;\r\n\t\t\t\tfloat collisionAngle = 0;\r\n\t\t\t\tfloat direction = 0;\r\n\t\t\t\tif (Utility.IsLeftOfLine(leftBound2, leftBound1, wheel.WorldPosition))\r\n\t\t\t\t{\r\n\t\t\t\t\tfenceNormal = Vector3.Cross(Vector3.Normalize(leftBound2 - leftBound1), car.CurrentNode.Up);\r\n\t\t\t\t\tcollisionAngle = Vector3Helper.GetAngleBetweenVectors(car.Right, fenceNormal);\r\n\t\t\t\t\tcollision = true;\r\n\t\t\t\t\tdirection = -1;\r\n\t\t\t\t}\r\n\t\t\t\telse if (!Utility.IsLeftOfLine(rightBound2, rightBound1, wheel.WorldPosition))\r\n\t\t\t\t{\r\n\t\t\t\t\tfenceNormal = Vector3.Cross(car.CurrentNode.Up, Vector3.Normalize(rightBound2 - rightBound1));\r\n\t\t\t\t\tcollisionAngle = Vector3Helper.GetAngleBetweenVectors(-car.Right, fenceNormal);\r\n\t\t\t\t\tcollision = true;\r\n\t\t\t\t\tdirection = 1;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (!collision) continue;\r\n\r\n\t\t\t\tVector3 collisionDir = Vector3.Reflect(car.Direction, fenceNormal);\r\n\t\t\t\t// Force car back on the road, for that calculate impulse and collision direction\r\n\t\t\t\t\r\n\t\t\t\t// Flip at 180 degrees (if driving in wrong direction)\r\n\t\t\t\tif (collisionAngle > MathHelper.Pi / 2)\r\n\t\t\t\t\tcollisionAngle -= MathHelper.Pi;\r\n\r\n\t\t\t\t// Just correct rotation if we hit the fence at a shallow angle\r\n\t\t\t\tif (Math.Abs(collisionAngle) < MathHelper.ToRadians(45))\r\n\t\t\t\t{\r\n\t\t\t\t\tSlideAlongFence(car, wheelNumber, collisionAngle, direction);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// If 90-45 degrees (in either direction), make frontal crash\r\n\t\t\t\t// + stop car + wobble camera\r\n\t\t\t\telse if (Math.Abs(collisionAngle) < MathHelper.Pi * 3.0f / 4.0f)\r\n\t\t\t\t{\r\n\t\t\t\t\tHandleHeadOnCrash(car, wheelNumber, collisionAngle);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// move away from the way slightly.  We should be a bit smarter and along more of a slide along the wall..\r\n\t\t\t\tcar.Position += fenceNormal * 1.5f;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n        }\r\n\r\n        public static int GetWheelsOutsideRoadVerge(DrivableVehicle car)\r\n        {\r\n\t\t\tvar leftVerge1 = car.CurrentNode.GetLeftVerge();\r\n\t\t\tvar leftVerge2 = car.CurrentNode.Next.GetLeftVerge();\r\n\r\n\t\t\tvar rightVerge1 = car.CurrentNode.GetRightVerge();\r\n\t\t\tvar rightVerge2 = car.CurrentNode.Next.GetRightVerge();\r\n\t\t\r\n            int wheelsOutsideVerge = 0;\r\n\r\n            for (int wheelNumber = 0; wheelNumber < 4; wheelNumber++)\r\n            {\r\n                VehicleWheel wheel = car.Wheels[wheelNumber];\r\n\r\n\t\t\t\tif (Utility.IsLeftOfLine(leftVerge2, leftVerge1, wheel.WorldPosition))\r\n\t\t\t\t{\r\n\t\t\t\t\twheelsOutsideVerge++;\r\n\t\t\t\t}\r\n\t\t\t\telse if (!Utility.IsLeftOfLine(rightVerge2, rightVerge1, wheel.WorldPosition))\r\n\t\t\t\t{\r\n\t\t\t\t\twheelsOutsideVerge++;\r\n\t\t\t\t}\r\n            }\r\n\r\n            return wheelsOutsideVerge;\r\n        }\r\n    \r\n\r\n        private static void SlideAlongFence(DrivableVehicle car, int wheel, float collisionAngle, float direction)\r\n        {\r\n\t\t\tif (car.AudioEnabled && Math.Abs(collisionAngle) > MathHelper.ToRadians(30))\r\n\t\t\t{\r\n\t\t\t\tEnvironmentAudioProvider.Instance.PlayVehicleFenceCollision();\r\n\t\t\t}\r\n\r\n            // if we hit the front wheels, change the direction to almost match the fence direction.  If we hit the back wheels this looks weird so\r\n\t\t\t// only rotate 50% of the way\r\n            if (wheel < 2)\r\n            {\r\n\t\t\t\tcar.RotateCarAfterCollision = direction * collisionAngle * 1.1f;\r\n                car.Speed *= 0.80f;\r\n            }\r\n            else\r\n            {\r\n                car.RotateCarAfterCollision = direction * collisionAngle *0.5f;\r\n                car.Speed *= 0.9f;\r\n            }\r\n        }\r\n\r\n        private static void HandleHeadOnCrash(DrivableVehicle car, int wheel, float collisionAngle)\r\n        {\r\n\t\t\tif (car.AudioEnabled)\r\n\t\t\t{\r\n\t\t\t\tEnvironmentAudioProvider.Instance.PlayVehicleFenceCollision();\r\n\t\t\t}\r\n\r\n\r\n            // Also rotate car if less than 60 degrees\r\n            if (Math.Abs(collisionAngle) < MathHelper.Pi / 3.0f)\r\n                car.RotateCarAfterCollision = +collisionAngle / 3.0f;\r\n\r\n\t\t\t//bounce back a little\r\n\t\t\tcar.Speed *= -0.15f;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Physics/VehicleWheel.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework.Graphics;\r\n\r\n\r\nnamespace OpenNFS1.Physics\r\n{\r\n\r\n\tclass VehicleWheel\r\n\t{\r\n\t\tpublic const float Width = 1.5f;\r\n\t\tVector3 _axlePoint;\r\n\t\tDrivableVehicle _car;\r\n\t\tfloat _steeringAngle;\r\n\t\tfloat _size;\r\n        float _rotation;\r\n\t\tTexture2D _texture;\r\n        ParticleEmitter _smokeEmitter;\r\n\t\tVector3 _renderOffset;  //we need to offset the wheel so that the front of the tire matches the original vertex position regardless of the tire width\r\n\t\tMatrix _wheelMatrix;\r\n\r\n        public float Rotation\r\n        {\r\n            get { return _rotation; }\r\n            set { _rotation = value; }\r\n        }\r\n\r\n        public bool IsSkidding { get; set; }\r\n\r\n\r\n\t\tpublic VehicleWheel(DrivableVehicle car, Vector3 axlePoint, float size, Texture2D texture, float renderXOffset)\r\n\t\t{\r\n\t\t\t_car = car;\r\n\t\t\t_axlePoint = axlePoint;\r\n\t\t\t_size = size;\r\n\t\t\t_texture = texture;\r\n\t\t\t_renderOffset = new Vector3(renderXOffset, 0, 0);\r\n\t\t\t_wheelMatrix = Matrix.CreateScale(new Vector3(_size, Width, _size)) * Matrix.CreateRotationZ(MathHelper.ToRadians(-90));  //cylinder geometry faces upwards\r\n\t\t\t_smokeEmitter = new ParticleEmitter(TyreSmokeParticleSystem.Instance, 20, WorldPosition);\r\n\t\t}\r\n\r\n\t\tpublic Vector3 WorldPosition\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\tvar m = Matrix.Identity;\r\n\t\t\t\tm.Right = Vector3.Cross(_car.RenderDirection, _car.Up);\r\n\t\t\t\tm.Up = _car.Up;\r\n\t\t\t\tm.Forward = _car.RenderDirection;\r\n\t\t\t\treturn Vector3.Transform(_axlePoint, m * Matrix.CreateTranslation(_car.Position));\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\tpublic Vector3 BottomPosition\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\tvar pos = WorldPosition;\r\n\t\t\t\tpos.Y -= Size / 2;\r\n\t\t\t\treturn pos;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic Vector3 GetOffsetPosition(Vector3 offset)\r\n\t\t{\r\n\t\t\tvar m = Matrix.Identity;\r\n\t\t\tm.Right = Vector3.Cross(_car.RenderDirection, _car.Up);\r\n\t\t\tm.Up = _car.Up;\r\n\t\t\tm.Forward = _car.RenderDirection;\r\n\t\t\tvar pos = _axlePoint + offset;\r\n\t\t\treturn Vector3.Transform(pos, m * Matrix.CreateTranslation(_car.Position));\r\n\t\t}\r\n\r\n\t\tpublic float Size\r\n\t\t{\r\n\t\t\tget { return _size; }\r\n\t\t}\r\n\r\n\r\n\t\tpublic void Steer(float angle)\r\n\t\t{\r\n\t\t\t_steeringAngle = -angle;\r\n\t\t}\r\n\r\n        public void Update()\r\n        {\r\n\t\t\t_smokeEmitter.Enabled = IsSkidding;\r\n            _smokeEmitter.Update(BottomPosition);\r\n            IsSkidding = false;\r\n        }\r\n\r\n\t\tpublic void Render()\r\n\t\t{\r\n\t\t\tMatrix carOrientation = Matrix.Identity;\r\n\t\t\tcarOrientation.Forward = _car.RenderDirection;\r\n\t\t\tcarOrientation.Up = _car.Up;\r\n\t\t\tcarOrientation.Right = Vector3.Cross(_car.RenderDirection, _car.Up);\r\n\t\t\tWheelModel.Render(\r\n\t\t\t\t_wheelMatrix *\r\n\t\t\t\tMatrix.CreateRotationX(_rotation / _size * 2f) *\r\n\t\t\t\tMatrix.CreateRotationY(_steeringAngle * 1.3f) *\r\n\t\t\t\tcarOrientation *\r\n\t\t\t\tMatrix.CreateTranslation(GetOffsetPosition(_renderOffset)),\r\n\t\t\t\t_texture);\r\n\t\t}\r\n\t}\r\n}"
  },
  {
    "path": "OpenNFS1/PlayerUI.cs",
    "content": "using System.Collections.Generic;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Input;\r\nusing GameEngine;\r\nusing OpenNFS1.Physics;\r\nusing OpenNFS1.Tracks;\r\nusing OpenNFS1.Vehicles;\r\nusing OpenNFS1.Views;\r\n\r\n\r\nnamespace OpenNFS1\r\n{\r\n\tclass PlayerUI\r\n\t{\r\n\t\tconst int DebugViewIndex = 0;\r\n\t\tDrivableVehicle _vehicle;\r\n\t\tList<IView> _views = new List<IView>();\r\n\t\tint _currentView = 1;\r\n\r\n\r\n\t\tpublic PlayerUI(DrivableVehicle vehicle)\r\n\t\t{\r\n\t\t\t_vehicle = vehicle;\r\n\r\n\t\t\t_views.Add(new DebugView(_vehicle));\r\n\t\t\t_views.Add(new ChaseView(_vehicle, 32, 14, 0));\r\n\t\t\t_views.Add(new DashboardView(_vehicle));\r\n\t\t\t_views.Add(new BumperView(_vehicle));\r\n\t\t\t_views.Add(new DropCameraView(_vehicle));\r\n\t\t\t_views[_currentView].Activate();\r\n\t\t}\r\n\r\n\t\tpublic bool ShouldRenderCar { get { return _views[_currentView].ShouldRenderPlayer; } }\r\n\r\n\t\tpublic TrackNode CurrentNode { get { return _vehicle.CurrentNode; } }\r\n\r\n\t\tpublic void Update(GameTime gameTime)\r\n\t\t{\r\n\t\t\tif (VehicleController.ChangeView)\r\n\t\t\t{\r\n\t\t\t\t_views[_currentView].Deactivate();\r\n\r\n\t\t\t\twhile (true)\r\n\t\t\t\t{\r\n\t\t\t\t\t_currentView++;\r\n\t\t\t\t\t_currentView %= _views.Count;\r\n\t\t\t\t\tif (_views[_currentView].Selectable)\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\t_views[_currentView].Activate();\r\n\t\t\t}\r\n\t\t\tif (Engine.Instance.Input.WasPressed(Keys.F1))  //toggle debug FPS view\r\n\t\t\t{\r\n\t\t\t\tif (_currentView == DebugViewIndex)\r\n\t\t\t\t\t_currentView = 1;\r\n\t\t\t\telse\r\n\t\t\t\t\t_currentView = DebugViewIndex;\r\n\t\t\t\t_views[_currentView].Activate();\r\n\t\t\t}\r\n\r\n\t\t\t_views[_currentView].Update(gameTime);\r\n\t\t}\r\n\t\t\r\n\r\n\t\tpublic void Render()\r\n\t\t{\r\n\t\t\t_views[_currentView].Render();\r\n\t\t}\r\n\t}\r\n}"
  },
  {
    "path": "OpenNFS1/Polygon.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace OpenNFS1.Parsers\r\n{\r\n    enum PolygonShape\r\n    {\r\n\t\tTriangle = 3,\r\n        Quad = 4,\r\n    }\r\n\r\n\r\n    class Polygon\r\n    {\r\n\t\tpublic PolygonShape Shape { get; set; }\r\n\t\tpublic string Label { get; set; }\r\n\t\tpublic string TextureName { get; set; }\r\n        public Vector3[] Vertices {get ;set; }\r\n        public Vector2[] TextureUVs {get; set;}\r\n        public Texture2D Texture {get; set;}\r\n\r\n        public int VertexCount\r\n        {\r\n            get\r\n            {\r\n                if (Shape == PolygonShape.Triangle)\r\n                    return 3;\r\n                else\r\n                    return 6;\r\n            }\r\n        }\r\n\r\n\t\tpublic int VertexBufferIndex { get; set; }\r\n\r\n\t\tbool _computeUvs;\r\n\r\n        public Polygon(PolygonShape type, bool computeUVs)\r\n        {\r\n            Shape = type;\r\n\t\t\t_computeUvs = computeUVs;\r\n\t\t\tVertices = new Vector3[VertexCount];\r\n\t\t\tTextureUVs = new Vector2[VertexCount];\r\n\r\n\t\t\tif (_computeUvs)\r\n\t\t\t{\r\n\t\t\t\tTextureUVs[0] = new Vector2(0, 0);\r\n\t\t\t\tTextureUVs[1] = new Vector2(1, 0);\r\n\t\t\t\tTextureUVs[2] = new Vector2(1, 1);\r\n\t\t\t\tTextureUVs[3] = new Vector2(0, 0);\r\n\t\t\t\tTextureUVs[4] = new Vector2(1, 1);\r\n\t\t\t\tTextureUVs[5] = new Vector2(0, 1);\r\n\t\t\t}\r\n        }\r\n\r\n        public void ResolveTexture(BitmapEntry bmpEntry)\r\n        {\r\n            if (bmpEntry == null)\r\n            {\r\n                return;\r\n            }\r\n\t\t\t\r\n            Texture = bmpEntry.Texture;\r\n            if (_computeUvs)  //don't need to scale our uvs based on the texture size\r\n                return;\r\n\r\n\t\t\t// otherwise, because the uvs are in the range 0,0,tex_width,tex_height we need to scale them to the 0,1 range\r\n            for (int i =0; i < VertexCount; i++)\r\n            {\r\n                Vector2 coord = TextureUVs[i];\r\n                coord.X /= bmpEntry.Texture.Width;\r\n                coord.Y /= bmpEntry.Texture.Height;\r\n                TextureUVs[i] = coord;\r\n            }\r\n        }\r\n\r\n\t\tpublic VertexPositionTexture[] GetVertices()\r\n\t\t{\r\n\t\t\tVertexPositionTexture[] verts = new VertexPositionTexture[VertexCount];\r\n\t\t\tfor (int i = 0; i < VertexCount; i++ )\r\n\t\t\t{\r\n\t\t\t\tverts[i] = new VertexPositionTexture(Vertices[i], TextureUVs[i]);\r\n\t\t\t}\r\n\t\t\treturn verts;\r\n\t\t}\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Program.cs",
    "content": "#region Using Statements\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\n#endregion\r\n\r\nnamespace OpenNFS1\r\n{\r\n    /// <summary>\r\n    /// The main class.\r\n    /// </summary>\r\n    public static class Program\r\n    {\r\n        /// <summary>\r\n        /// The main entry point for the application.\r\n        /// </summary>\r\n        [STAThread]\r\n        static void Main()\r\n        {\r\n\t\t\tAppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;\r\n            using (var game = new Game1())\r\n                game.Run();\r\n        }\r\n\r\n\t\tstatic void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)\r\n\t\t{\r\n\t\t\tFile.WriteAllText(\"exception.txt\", e.ExceptionObject.ToString());\r\n\t\t}\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\r\nusing System.Runtime.CompilerServices;\r\nusing System.Runtime.InteropServices;\r\n\r\n// General Information about an assembly is controlled through the following \r\n// set of attributes. Change these attribute values to modify the information\r\n// associated with an assembly.\r\n[assembly: AssemblyTitle(\"OpenNFS1\")]\r\n[assembly: AssemblyProduct(\"OpenNFS1\")]\r\n[assembly: AssemblyConfiguration(\"\")]\r\n[assembly: AssemblyDescription(\"\")]\r\n[assembly: AssemblyCompany(\"\")]\r\n[assembly: AssemblyCopyright(\"Copyright ©  2013\")]\r\n[assembly: AssemblyTrademark(\"\")]\r\n[assembly: AssemblyCulture(\"\")]\r\n\r\n// Setting ComVisible to false makes the types in this assembly not visible \r\n// to COM components.  If you need to access a type in this assembly from \r\n// COM, set the ComVisible attribute to true on that type.\r\n[assembly: ComVisible(false)]\r\n\r\n// The following GUID is for the ID of the typelib if this project is exposed to COM\r\n[assembly: Guid(\"169d9959-2864-49ca-acb8-d5adb00ff627\")]\r\n\r\n// Version information for an assembly consists of the following four values:\r\n//\r\n//      Major Version\r\n//      Minor Version \r\n//      Build Number\r\n//      Revision\r\n//\r\n// You can specify all the values or you can default the Build and Revision Numbers \r\n// by using the '*' as shown below:\r\n// [assembly: AssemblyVersion(\"1.0.*\")]\r\n[assembly: AssemblyVersion(\"1.2.0.0\")]\r\n[assembly: AssemblyFileVersion(\"1.2.0.0\")]\r\n"
  },
  {
    "path": "OpenNFS1/Race/PlayerRaceStats.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\n\r\nnamespace OpenNFS1\r\n{\r\n\tclass PlayerRaceStats\r\n\t{\r\n\t\tpublic int CurrentLap { get; private set; }\r\n\t\tDateTime _currentLapStartTime = DateTime.MinValue;\r\n\t\tpublic List<int> LapTimes = new List<int>();\r\n\t\tpublic bool HasPassedLapHalfwayPoint;\r\n\t\tpublic int Position { get; set; }\r\n\r\n\t\tpublic void OnLapStarted()\r\n\t\t{\r\n\t\t\tif (_currentLapStartTime != DateTime.MinValue)\r\n\t\t\t{\r\n\t\t\t\tLapTimes.Add((int)new TimeSpan(DateTime.Now.Ticks - _currentLapStartTime.Ticks).TotalSeconds);\r\n\t\t\t}\r\n\t\t\tCurrentLap++;\r\n\t\t\t_currentLapStartTime = DateTime.Now;\r\n\t\t\tHasPassedLapHalfwayPoint = false;\r\n\t\t}\r\n\r\n\t\tpublic TimeSpan CurrentLapTime\r\n\t\t{\r\n\t\t\tget { return new TimeSpan(DateTime.Now.Ticks - _currentLapStartTime.Ticks); }\r\n\t\t}\r\n\t}\r\n}"
  },
  {
    "path": "OpenNFS1/Race/Race.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing OpenNFS1.Physics;\r\nusing GameEngine;\r\nusing OpenNFS1.UI.Screens;\r\nusing OpenNFS1.Parsers.Track;\r\nusing OpenNFS1.Tracks;\r\nusing OpenNFS1.Vehicles.AI;\r\nusing OpenNFS1.Vehicles;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace OpenNFS1\r\n{\r\n\r\n\tclass Race\r\n\t{\r\n\t\tint _nbrLaps;\r\n\t\tDateTime _countdownStartTime, _raceStartTime;\r\n\t\tbool _started;\r\n\t\tpublic bool Finished { get; private set; }\r\n\t\tpublic PlayerDriver Player { get; private set; }\r\n\t\tTrafficController _trafficController;\r\n\t\tpublic List<IDriver> Drivers { get; private set; }\r\n\t\tpublic PlayerRaceStats PlayerStats { get; private set; }\r\n\t\tpublic Track Track { get; private set; }\r\n\r\n\t\tpublic Race(int nbrLaps, Track track, PlayerDriver player)\r\n\t\t{\r\n\t\t\t_nbrLaps = nbrLaps;\r\n\t\t\tPlayer = player;\r\n\t\t\tTrack = track;\r\n\t\t\tPlayerStats = new PlayerRaceStats();\r\n\t\t\tDrivers = new List<IDriver>();\r\n\t\t\tAddDriver(player);\r\n\t\t\tif (track.Description.IsOpenRoad)\r\n\t\t\t{\r\n\t\t\t\t_trafficController = new TrafficController(this);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\tpublic int NbrLaps\r\n\t\t{\r\n\t\t\tget { return _nbrLaps; }\r\n\t\t}\r\n\r\n\t\tpublic TimeSpan RaceTime\r\n\t\t{\r\n\t\t\tget { return new TimeSpan(DateTime.Now.Ticks - _raceStartTime.Ticks); }\r\n\t\t}\r\n\r\n\t\tpublic int SecondsTillStart\r\n\t\t{\r\n\t\t\tget { return 3 - (int)new TimeSpan(DateTime.Now.Ticks - _countdownStartTime.Ticks).TotalSeconds; }\r\n\t\t}\r\n\r\n\r\n\t\tpublic void StartCountdown()\r\n\t\t{\r\n\t\t\tVehicleController.ForceBrake = false;\r\n\t\t\t_countdownStartTime = DateTime.Now;\r\n\t\t}\r\n\r\n\t\tpublic void AddDriver(IDriver d)\r\n\t\t{\r\n\t\t\tAddDriver(d, Track.RoadNodes[1]);\r\n\t\t}\r\n\r\n\t\t// add a driver to the race, placing him in a starting grid\r\n\t\tpublic void AddDriver(IDriver d, TrackNode startNode)\r\n\t\t{\r\n\t\t\td.Vehicle.PlaceOnTrack(Track, startNode);\r\n\t\t\tif (d is TrafficDriver)\r\n\t\t\t{\r\n\t\t\t}\r\n\t\t\telse if (d is RacingAIDriver || d is PlayerDriver)\r\n\t\t\t{\r\n\t\t\t\t// place on starting grid\r\n\t\t\t\tint lane = (Drivers.Count % 2 == 0 ? AIDriver.MaxVirtualLanes - 1 : 1);\r\n\t\t\t\tif (d is RacingAIDriver)\r\n\t\t\t\t{\r\n\t\t\t\t\t((RacingAIDriver)d).VirtualLane = lane;\r\n\t\t\t\t}\r\n\t\t\t\tVector3 pos = d.Vehicle.CurrentNode.Position;\r\n\t\t\t\tpos.Z -= Drivers.Count * 30;\r\n\t\t\t\tpos.X = Vector3.Lerp(d.Vehicle.CurrentNode.GetLeftVerge2(), d.Vehicle.CurrentNode.GetRightVerge2(), (float)lane / (AIDriver.MaxVirtualLanes)).X;\r\n\t\t\t\td.Vehicle.Position = pos;\r\n\t\t\t}\r\n\t\t\tDrivers.Add(d);\r\n\t\t}\r\n\r\n\t\tpublic void Update()\r\n\t\t{\r\n\t\t\tforeach (var driver in Drivers)\r\n\t\t\t\tdriver.Update(Drivers);\r\n\r\n\t\t\tTrack.Update();\r\n\r\n\t\t\t/* so you cant reverse over the line  and get 0.1sec laps */\r\n\t\t\tvar node = Player.Vehicle.CurrentNode;\r\n\t\t\tif (node.Number == Track.CheckpointNode && PlayerStats.HasPassedLapHalfwayPoint)\r\n\t\t\t{\r\n\t\t\t\tPlayerStats.OnLapStarted();\r\n\t\t\t}\r\n\r\n\t\t\tif (node.Number == 100)  //just pick some arbitrary node thats farish away from the start\r\n\t\t\t{\r\n\t\t\t\tPlayerStats.HasPassedLapHalfwayPoint = true;\r\n\t\t\t}\r\n\r\n\t\t\tvar racingDrivers = new List<IDriver>(Drivers.Where(a => a is RacingAIDriver || a is PlayerDriver));\r\n\t\t\tracingDrivers.Sort((a1, a2) => a2.Vehicle.TrackPosition.CompareTo(a1.Vehicle.TrackPosition));\r\n\t\t\tPlayerStats.Position = racingDrivers.FindIndex(a => a == Player);\r\n\r\n\t\t\tif (SecondsTillStart <= 0 && !_started)\r\n\t\t\t{\r\n\t\t\t\tforeach (var d in racingDrivers)\r\n\t\t\t\t{\r\n\t\t\t\t\t((DrivableVehicle)d.Vehicle).Motor.Gearbox.CurrentGear = 1;\r\n\t\t\t\t}\r\n\t\t\t\t_raceStartTime = DateTime.Now;\r\n\t\t\t\tPlayerStats.OnLapStarted();\r\n\t\t\t\t_started = true;\r\n\t\t\t}\r\n\r\n\t\t\tif (PlayerStats.CurrentLap > _nbrLaps)\r\n\t\t\t{\r\n\t\t\t\tif (Player.Vehicle.Speed > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tVehicleController.ForceBrake = true;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tFinished = true;\r\n\t\t\t}\r\n\t\t\tif (_trafficController != null) _trafficController.Update();\r\n\t\t}\r\n\r\n\t\tpublic void Render(bool renderPlayerVehicle)\r\n\t\t{\r\n\t\t\tEngine.Instance.Device.BlendState = BlendState.Opaque;\r\n\t\t\tEngine.Instance.Device.DepthStencilState = DepthStencilState.Default;\r\n\t\t\tEngine.Instance.Device.SamplerStates[0] = GameConfig.WrapSampler;\r\n\r\n\t\t\tTrack.Render(Engine.Instance.Camera.Position, Player.Vehicle.CurrentNode);\r\n\r\n\t\t\tvar frustum = new BoundingFrustum(Engine.Instance.Camera.View * Engine.Instance.Camera.Projection);\r\n\t\t\t\r\n\t\t\tforeach (var driver in Drivers)\r\n\t\t\t{\r\n\t\t\t\tbool isPlayer = driver == Player;\r\n\t\t\t\tif (isPlayer && !renderPlayerVehicle)\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\tif (!frustum.Intersects(driver.Vehicle.BoundingSphere))\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\tif (driver.Vehicle is DrivableVehicle)\r\n\t\t\t\t{\r\n\t\t\t\t\t((DrivableVehicle)driver.Vehicle).RenderShadow(isPlayer);\r\n\t\t\t\t}\r\n                if (driver is AIDriver && ((AIDriver)driver).AtEndOfTrack)\r\n                    continue; \r\n\r\n\t\t\t\tdriver.Vehicle.Render();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Race/RaceUI.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing OpenNFS1.Vehicles.AI;\r\nusing OpenNFS1.Parsers;\r\nusing OpenNFS1.Physics;\r\n\r\nnamespace OpenNFS1.UI\r\n{\r\n    class RaceUI\r\n    {\r\n        Race _race;\r\n\r\n        Rectangle _backgroundRectangle;\r\n        Texture2D _backgroundTexture;\r\n        SpriteFont _font;\r\n\r\n        public RaceUI(Race race)\r\n        {\r\n            _race = race;\r\n\r\n\t\t\tint height = Engine.Instance.Device.Viewport.Height;\r\n\t\t\tint width = Engine.Instance.Device.Viewport.Width;\r\n\r\n            _backgroundRectangle = new Rectangle(0, 0, width, 30);\r\n\t\t\tColor[] pixel = new Color[1] { Color.Black };\r\n\t\t\t_backgroundTexture = new Texture2D(Engine.Instance.Device, 1, 1);\r\n\t\t\t_backgroundTexture.SetData<Color>(pixel);\r\n            _font = Engine.Instance.ContentManager.Load<SpriteFont>(\"Content\\\\ArialBlack-Italic\");\r\n        }\r\n\r\n        public void Render()\r\n        {\r\n            Engine.Instance.SpriteBatch.Begin();\r\n\r\n            int secondsTillStart = _race.SecondsTillStart;\r\n            if (secondsTillStart > 0)\r\n            {\r\n                Engine.Instance.SpriteBatch.DrawString(_font, _race.SecondsTillStart.ToString(), new Vector2(300, 50), Color.Yellow, 0, Vector2.Zero, 1.5f, SpriteEffects.None, 0);\r\n            }\r\n            else if (secondsTillStart == 0)\r\n            {\r\n                Engine.Instance.SpriteBatch.DrawString(_font, \"Go!\", new Vector2(300, 50), Color.Yellow, 0, Vector2.Zero, 1.5f, SpriteEffects.None, 0);\r\n            }\r\n\t\t\t\r\n            Engine.Instance.SpriteBatch.Draw(_backgroundTexture, _backgroundRectangle, Color.White);\r\n\t\t\t\r\n            string msg = String.Format(\"{0}:{1}\", _race.PlayerStats.CurrentLapTime.Minutes.ToString(\"00\"), _race.PlayerStats.CurrentLapTime.Seconds.ToString(\"00\"));\r\n            Engine.Instance.SpriteBatch.DrawString(_font, msg, new Vector2(15, 0), Color.GreenYellow, 0, Vector2.Zero, 0.6f, SpriteEffects.None, 0);\r\n\r\n            msg = String.Format(\"{0} kph\", Math.Abs((int)_race.Player.Vehicle.Speed));\r\n            Engine.Instance.SpriteBatch.DrawString(_font, msg, new Vector2(270, -5), Color.WhiteSmoke, 0, Vector2.Zero, 0.8f, SpriteEffects.None, 0);\r\n            Engine.Instance.SpriteBatch.DrawString(_font, msg, new Vector2(271, -4), Color.Red, 0, Vector2.Zero, 0.8f, SpriteEffects.None, 0);\r\n\r\n            msg = String.Format(\"G:{0}\", GearToString(((DrivableVehicle)_race.Player.Vehicle).Motor.Gearbox.CurrentGear));\r\n            Engine.Instance.SpriteBatch.DrawString(_font, msg, new Vector2(410, 0), Color.GreenYellow, 0, Vector2.Zero, 0.6f, SpriteEffects.None, 0);\r\n\r\n\t\t\tif (!GameConfig.SelectedTrackDescription.IsOpenRoad)\r\n            {\r\n\t\t\t\tmsg = String.Format(\"L:{0}/{1}\", Math.Min(_race.PlayerStats.CurrentLap, _race.NbrLaps), _race.NbrLaps);\r\n                Engine.Instance.SpriteBatch.DrawString(_font, msg, new Vector2(480, 0), Color.GreenYellow, 0, Vector2.Zero, 0.6f, SpriteEffects.None, 0);\r\n            }\r\n\r\n\t\t\tmsg = String.Format(\"P:{0}/{1}\", _race.PlayerStats.Position + 1, _race.Drivers.Count(a => a is RacingAIDriver || a is PlayerDriver));\r\n\t\t\tEngine.Instance.SpriteBatch.DrawString(_font, msg, new Vector2(550, 0), Color.GreenYellow, 0, Vector2.Zero, 0.6f, SpriteEffects.None, 0);\r\n            Engine.Instance.SpriteBatch.End();\r\n        }\r\n\r\n        private string GearToString(int gear)\r\n        {\r\n            if (gear == -1)\r\n                return \"R\";\r\n            else if (gear == 0)\r\n                return \"N\";\r\n            else\r\n                return gear.ToString();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Tracks/SceneryObject.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\nusing OpenNFS1.Loaders;\r\nusing GameEngine;\r\nusing OpenNFS1.Parsers;\r\nusing OpenNFS1;\r\n\r\nnamespace OpenNFS1.Parsers.Track\r\n{\r\n\r\n\tabstract class SceneryItem\r\n\t{\r\n\t\tpublic int SegmentRef;\r\n\t\tpublic Vector3 Position;\r\n\t\tpublic float Orientation;\r\n\t\tpublic Vector2 Size;\r\n\r\n\t\tpublic abstract void Initialize();\r\n\t\tpublic virtual void Update() { }\r\n\t\tpublic abstract void Render(AlphaTestEffect effect);\r\n\t}\r\n\r\n\tclass BillboardSceneryItem : SceneryItem\r\n\t{\r\n\t\tprotected Matrix _matrix;\r\n\t\tprotected Texture2D _texture;\r\n\r\n\t\tpublic BillboardSceneryItem() { }\r\n\t\tpublic BillboardSceneryItem(Texture2D texture)\r\n\t\t{\r\n\t\t\t_texture = texture;\r\n\t\t}\r\n\r\n\t\tpublic override void Initialize()\r\n\t\t{\r\n\t\t\tif (Size.X == 0)\r\n\t\t\t{\r\n\t\t\t\tfloat aspect = (float)_texture.Width / _texture.Height;\r\n\t\t\t\tSize.X = Size.Y * aspect * GameConfig.TerrainScale * 10000;\r\n\t\t\t}\r\n\r\n\t\t\t_matrix = Matrix.CreateScale(Size.X, Size.Y, 1) *\r\n\t\t\t\t\t\t\tMatrix.CreateRotationY(Orientation) *\r\n\t\t\t\t\t\t\tMatrix.CreateTranslation(Position);\r\n\t\t}\r\n\r\n\t\tpublic override void Render(AlphaTestEffect effect)\r\n\t\t{\r\n\t\t\teffect.World = _matrix;\r\n\t\t\teffect.Texture = _texture;\r\n\t\t\teffect.CurrentTechnique.Passes[0].Apply();\r\n\t\t\tEngine.Instance.Device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);\r\n\t\t}\r\n\t}\r\n\r\n\tclass AnimatedBillboardSceneryItem : BillboardSceneryItem\r\n\t{\r\n\t\tList<Texture2D> _textures;\r\n\t\tint _currentTexture;\r\n\t\tdouble _textureChangeTime;\r\n\r\n\t\tpublic AnimatedBillboardSceneryItem(List<Texture2D> textures)\r\n\t\t{\r\n\t\t\t_textures = textures;\r\n\t\t\t_textureChangeTime = Engine.Instance.Random.NextDouble();\r\n\t\t}\r\n\r\n\t\tpublic override void Initialize()\r\n\t\t{\r\n\t\t\tif (Size.X == 0)\r\n\t\t\t\tSize.X = _textures[0].Width * GameConfig.TerrainScale * 10000;\r\n\t\t\tif (Size.Y == 0)\r\n\t\t\t\tSize.Y = _textures[0].Height * GameConfig.TerrainScale * 10000;\r\n\r\n\t\t\t_matrix = Matrix.CreateScale(Size.X, Size.Y, 1) *\r\n\t\t\t\t\t\t\tMatrix.CreateRotationY(Orientation) *\r\n\t\t\t\t\t\t\tMatrix.CreateTranslation(Position);\r\n\t\t}\r\n\r\n\t\tpublic override void Update()\r\n\t\t{\r\n\t\t\t_textureChangeTime -= Engine.Instance.FrameTime;\r\n\t\t\tif (_textureChangeTime < 0.0f)\r\n\t\t\t{\r\n\t\t\t\t_currentTexture++;\r\n\t\t\t\t_currentTexture %= _textures.Count;\r\n\t\t\t\t_texture = _textures[_currentTexture];\r\n\t\t\t\t_textureChangeTime = 0.2f;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tclass TwoSidedBillboardSceneryItem : SceneryItem\r\n\t{\r\n\t\tTexture2D _texture1, _texture2;\r\n\t\tMatrix _matrix1, _matrix2;\r\n\r\n\t\tpublic TwoSidedBillboardSceneryItem(Texture2D texture1, Texture2D texture2)\r\n\t\t{\r\n\t\t\t_texture1 = texture1;\r\n\t\t\t_texture2 = texture2;\r\n\t\t}\r\n\r\n\t\tpublic override void Initialize()\r\n\t\t{\r\n\t\t\t_matrix1 = Matrix.CreateScale(Size.X, Size.Y, 1) *\r\n\t\t\t\t\tMatrix.CreateRotationY(Orientation) *\r\n\t\t\t\t\tMatrix.CreateTranslation(Position);\r\n\r\n\t\t\t_matrix2 = Matrix.CreateScale(Size.X, Size.Y, 1) *\r\n\t\t\t\t\tMatrix.CreateTranslation(Size.X / 2, 0, Size.X / 2) *\r\n\t\t\t\t\tMatrix.CreateRotationY(Orientation + MathHelper.ToRadians(90)) *\r\n\t\t\t\t\tMatrix.CreateTranslation(Position);\r\n\t\t}\r\n\r\n\t\tpublic override void Render(AlphaTestEffect effect)\r\n\t\t{\r\n\t\t\t//side 1\r\n\t\t\teffect.World = _matrix1;\r\n\t\t\teffect.Texture = _texture1;\r\n\t\t\teffect.CurrentTechnique.Passes[0].Apply();\r\n\t\t\tEngine.Instance.Device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);\r\n\r\n\t\t\t//side 2\r\n\t\t\teffect.World = _matrix2;\r\n\t\t\teffect.Texture = _texture2;\r\n\t\t\teffect.CurrentTechnique.Passes[0].Apply();\r\n\t\t\tEngine.Instance.Device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);\r\n\t\t}\r\n\t}\r\n\r\n\tclass ModelSceneryItem : SceneryItem\r\n\t{\r\n\t\tMesh _mesh;\r\n\t\tMatrix _matrix;\r\n\r\n\t\tpublic ModelSceneryItem(Mesh mesh)\r\n\t\t{\r\n\t\t\t_mesh = mesh;\r\n\t\t}\r\n\r\n\t\tpublic override void Initialize()\r\n\t\t{\r\n\t\t\t_matrix = Matrix.CreateScale(7.5f) *\r\n\t\t\t\t   Matrix.CreateRotationY(Orientation) *\r\n\t\t\t\t   Matrix.CreateTranslation(Position);\r\n\t\t}\r\n\r\n\t\tpublic override void Render(AlphaTestEffect effect)\r\n\t\t{\r\n\t\t\teffect.World = _matrix;\r\n\t\t\t_mesh.Render(effect);\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Tracks/TerrainRow.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing OpenNFS1.Parsers;\r\n\r\nnamespace OpenNFS1.Tracks\r\n{\r\n\t// A terrain row maps to a TrackNode.  TerrainRow defines the vertex positions for that bit of track, the TrackNode defines the physical properties\r\n\t// that the vehicles follow (slope, slant, width...)\r\n\tclass TerrainRow\r\n\t{\r\n\r\n\t\tconst int TerrainPositionScale = 500;\r\n\t\tpublic Vector3 MiddlePoint;\r\n\t\tpublic Vector3[] LeftPoints = new Vector3[TriFile.NbrTerrainPointsPerSide];\r\n\t\tpublic Vector3[] RightPoints = new Vector3[TriFile.NbrTerrainPointsPerSide];\r\n\r\n\t\tpublic Vector3 GetPoint(int pointIndex)\r\n\t\t{\r\n\t\t\tif (pointIndex >= TriFile.NbrTerrainPointsPerSide)\r\n\t\t\t\treturn LeftPoints[(pointIndex - TriFile.NbrTerrainPointsPerSide)];\r\n\t\t\telse\r\n\t\t\t\treturn RightPoints[pointIndex];\r\n\t\t}\r\n\r\n\t\tpublic void RelativizeTo(Vector3 position)\r\n\t\t{\r\n\t\t\tMiddlePoint = position + MiddlePoint * TerrainPositionScale;\r\n\t\t\tfor (int i = 0; i < LeftPoints.Length; i++)\r\n\t\t\t{\r\n\t\t\t\tLeftPoints[i] = position + LeftPoints[i] * TerrainPositionScale;\r\n\t\t\t}\r\n\r\n\t\t\tfor (int i = 0; i < RightPoints.Length; i++)\r\n\t\t\t{\r\n\t\t\t\tRightPoints[i] = position + RightPoints[i] * TerrainPositionScale;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Tracks/TerrainSegment.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing OpenNFS1.Parsers;\r\nusing OpenNFS1.Parsers.Track;\r\n\r\nnamespace OpenNFS1.Tracks\r\n{\r\n\t// A terrainSegment holds a group of 4 TerrainRow's and defines properties common to those rows (fence, textures etc)\r\n\tclass TerrainSegment\r\n\t{\r\n\t\tpublic int Number;\r\n\t\tpublic Texture2D[] Textures = new Texture2D[10];\r\n\t\tpublic bool HasLeftFence, HasRightFence;\r\n\t\tpublic int FenceTextureId;\r\n\t\tpublic Texture2D FenceTexture;\r\n\t\tpublic byte[] TextureIds;\r\n\t\tpublic TerrainRow[] Rows = new TerrainRow[4];\r\n\t\tpublic int FenceBufferIndex;\r\n\t\tpublic int TerrainBufferIndex;\r\n\t\tpublic TerrainSegment Next, Prev;\r\n\r\n\t\t// used for frustum culling calculation\r\n\t\tpublic BoundingBox BoundingBox;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Tracks/Track.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing System.IO;\r\nusing Microsoft.Xna.Framework;\r\nusing System.Diagnostics;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing OpenNFS1.Loaders;\r\nusing OpenNFS1.Tracks;\r\nusing Microsoft.Xna.Framework.Input;\r\nusing OpenNFS1.Vehicles;\r\nusing OpenNFS1.Vehicles.AI;\r\nusing OpenNFS1.Physics;\r\n\r\nnamespace OpenNFS1.Parsers.Track\r\n{\r\n\r\n\tclass Track\r\n\t{\r\n\t\tAlphaTestEffect _effect;\r\n\t\tTrackSkyBox _skybox;\r\n\t\tBasicEffect _physicalRoadEffect;\r\n\r\n\t\tpublic TrackDescription Description { get; set; }\r\n\t\tpublic List<SceneryItem> SceneryItems { get; set; }\r\n\t\tpublic List<TrackNode> RoadNodes { get; set; }\r\n\t\tpublic List<TerrainSegment> TerrainSegments { get; set; }\r\n\t\tpublic VertexBuffer TerrainVertexBuffer { get; set; }\r\n\t\tpublic VertexBuffer FenceVertexBuffer { get; set; }\r\n\t\tpublic TrackfamFile TrackFam { get; set; }\r\n\t\tpublic int CheckpointNode { get; set; }\r\n\t\tpublic bool IsOpenRoad { get; set; }\r\n\t\t\r\n\r\n\t\tpublic Track()\r\n\t\t{\r\n\t\t\t_effect = new AlphaTestEffect(Engine.Instance.Device);\r\n\t\t\t_effect.ReferenceAlpha = 100;\r\n\t\t\t_effect.AlphaFunction = CompareFunction.Greater;\r\n\t\t\t_effect.VertexColorEnabled = false;\r\n\t\t\t_effect.FogEnabled = false;\r\n\t\t\t_physicalRoadEffect = new BasicEffect(Engine.Instance.Device);\r\n\t\t}\r\n\r\n\t\tpublic void Initialize()\r\n\t\t{\r\n\t\t\t_skybox = new TrackSkyBox(TrackFam.HorizonTexture);\r\n\t\t}\r\n\r\n\t\tpublic void Update()\r\n\t\t{\r\n\t\t\t_skybox.Update();\r\n\t\t\tforeach (var item in SceneryItems)\r\n\t\t\t{\r\n\t\t\t\titem.Update();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void Render(Vector3 cameraPosition, TrackNode currentNode)\r\n\t\t{\r\n\t\t\t_skybox.Render();\r\n\t\t\t_effect.View = Engine.Instance.Camera.View;\r\n\t\t\t_effect.Projection = Engine.Instance.Camera.Projection;\r\n\t\t\t_effect.World = Matrix.Identity;\r\n\r\n\t\t\tint segmentIndex = currentNode.Number / 4;\r\n\t\t\tvar startSegment = TerrainSegments[segmentIndex];\r\n\r\n\t\t\tvar renderedSegments = new List<TerrainSegment>();\r\n\r\n\t\t\tEngine.Instance.Device.SetVertexBuffer(TerrainVertexBuffer);\r\n\t\t\t_effect.CurrentTechnique.Passes[0].Apply();\r\n\t\t\tEngine.Instance.Device.RasterizerState = RasterizerState.CullNone;\r\n\t\t\tEngine.Instance.Device.SamplerStates[0] = GameConfig.WrapSampler;\r\n\r\n\t\t\tvar frustum = new BoundingFrustum(Engine.Instance.Camera.View * Engine.Instance.Camera.Projection);\r\n\r\n\t\t\t// draw segments from the player vehicle forwards. Stop when a segment is out of view\r\n\t\t\tvar segment = startSegment;\r\n\t\t\tfor (int i = 0; i < GameConfig.MaxSegmentRenderCount; i++)\r\n\t\t\t{\r\n\t\t\t\tif (segment == null) break;\r\n\t\t\t\tif (frustum.Intersects(segment.BoundingBox))\r\n\t\t\t\t{\r\n\t\t\t\t\tRenderSegment(segment);\r\n\t\t\t\t\trenderedSegments.Add(segment);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tsegment = segment.Next;\r\n\t\t\t}\r\n\r\n\t\t\t// draw segments from the player vehicle backwards. Stop when a segment is out of view\r\n\t\t\tsegment = startSegment.Prev;\r\n\t\t\tfor (int i = 0; i < GameConfig.MaxSegmentRenderCount; i++)\r\n\t\t\t{\r\n\t\t\t\tif (segment == null) break;\r\n\t\t\t\tif (frustum.Intersects(segment.BoundingBox))\r\n\t\t\t\t{\r\n\t\t\t\t\tRenderSegment(segment);\r\n\t\t\t\t\trenderedSegments.Add(segment);\r\n\t\t\t\t}\r\n\t\t\t\tsegment = segment.Prev;\r\n\t\t\t}\r\n\r\n\t\t\tDrawScenery(renderedSegments);\r\n\r\n\t\t\tif (FenceVertexBuffer != null)\r\n\t\t\t{\r\n\t\t\t\tEngine.Instance.Device.SetVertexBuffer(FenceVertexBuffer);\r\n\t\t\t\t_effect.World = Matrix.Identity;\r\n\t\t\t\t_effect.CurrentTechnique.Passes[0].Apply();\r\n\t\t\t\tEngine.Instance.Device.SamplerStates[0] = GameConfig.WrapSampler;\r\n\t\t\t\tforeach (var renderedSegment in renderedSegments)\r\n\t\t\t\t{\r\n\t\t\t\t\tDrawFenceStrips(renderedSegment);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (GameConfig.DrawDebugInfo)\r\n\t\t\t{\r\n\t\t\t\tvar node = currentNode;\r\n\t\t\t\tfor (int i = 0; i < GameConfig.MaxSegmentRenderCount; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tEngine.Instance.GraphicsUtils.AddCube(Matrix.CreateTranslation(node.GetLeftBoundary()), Color.Red);\r\n\t\t\t\t\tEngine.Instance.GraphicsUtils.AddCube(Matrix.CreateTranslation(node.GetRightBoundary()), Color.Red);\r\n\t\t\t\t\tEngine.Instance.GraphicsUtils.AddCube(Matrix.CreateTranslation(node.GetLeftVerge()), Color.Blue);\r\n\t\t\t\t\tEngine.Instance.GraphicsUtils.AddCube(Matrix.CreateTranslation(node.GetRightVerge()), Color.Blue);\r\n\t\t\t\t\tEngine.Instance.GraphicsUtils.AddCube(Matrix.CreateTranslation(node.Position), Color.Yellow);\r\n\r\n\t\t\t\t\tif (node.Number % TriFile.NbrRowsPerSegment == 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tEngine.Instance.GraphicsUtils.AddLine(node.GetLeftBoundary(), node.GetRightBoundary(), Color.White);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tnode = node.Next;\r\n\t\t\t\t\tif (node == null) break;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tGameConsole.WriteLine(String.Format(\"Position node: {0}, segment: {1}\", currentNode.Number, (int)(currentNode.Number / TriFile.NbrRowsPerSegment)));\r\n\t\t\t\tGameConsole.WriteLine(String.Format(\"Node property: {0}, flags: {1}, {2}, {3}\", currentNode.NodeProperty, currentNode.Flag1, currentNode.Flag2, currentNode.Flag3));\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void RenderSegment(TerrainSegment segment)\r\n\t\t{\r\n\t\t\tint vertexIndex = segment.TerrainBufferIndex;\r\n\r\n\t\t\tDrawTerrainStrip(ref vertexIndex, 0, segment.Textures[0]);\r\n\t\t\tDrawTerrainStrip(ref vertexIndex, 1, segment.Textures[1]);\r\n\t\t\tDrawTerrainStrip(ref vertexIndex, 2, segment.Textures[2]);\r\n\t\t\tDrawTerrainStrip(ref vertexIndex, 3, segment.Textures[3]);\r\n\t\t\tDrawTerrainStrip(ref vertexIndex, 4, segment.Textures[4]);\r\n\t\t\tDrawTerrainStrip(ref vertexIndex, 5, segment.Textures[5]);\r\n\t\t\tDrawTerrainStrip(ref vertexIndex, 6, segment.Textures[6]);\r\n\t\t\tDrawTerrainStrip(ref vertexIndex, 7, segment.Textures[7]);\r\n\t\t\tDrawTerrainStrip(ref vertexIndex, 8, segment.Textures[8]);\r\n\t\t\tDrawTerrainStrip(ref vertexIndex, 9, segment.Textures[9]);\r\n\t\t}\r\n\r\n\t\tprivate void DrawTerrainStrip(ref int vertexIndex, int stripNumber, Texture2D texture)\r\n\t\t{\r\n\t\t\tif (texture != null)\r\n\t\t\t{\r\n\t\t\t\tEngine.Instance.Device.Textures[0] = texture;\r\n\t\t\t\tEngine.Instance.Device.DrawPrimitives(PrimitiveType.TriangleStrip, vertexIndex, TrackAssembler.NbrTrianglesPerTerrainStrip);\r\n\t\t\t}\r\n\t\t\tvertexIndex += TrackAssembler.NbrVerticesPerTerrainStrip;\r\n\t\t}\r\n\r\n\t\tprivate void DrawFenceStrips(TerrainSegment segment)\r\n\t\t{\r\n\t\t\tint offset = segment.FenceBufferIndex;\r\n\t\t\tif (segment.HasLeftFence)\r\n\t\t\t{\r\n\t\t\t\tEngine.Instance.Device.Textures[0] = segment.FenceTexture;\r\n\t\t\t\tEngine.Instance.Device.DrawPrimitives(PrimitiveType.TriangleStrip, offset, 4);\r\n\t\t\t\toffset += 6;\r\n\t\t\t}\r\n\t\t\tif (segment.HasRightFence)\r\n\t\t\t{\r\n\t\t\t\tEngine.Instance.Device.Textures[0] = segment.FenceTexture;\r\n\t\t\t\tEngine.Instance.Device.DrawPrimitives(PrimitiveType.TriangleStrip, offset, 4);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void DrawScenery(List<TerrainSegment> renderedSegments)\r\n\t\t{\r\n\t\t\tEngine.Instance.Device.RasterizerState = RasterizerState.CullNone;\r\n\t\t\tEngine.Instance.Device.SamplerStates[0] = SamplerState.PointClamp;\r\n\r\n\t\t\tif (GameConfig.Render2dScenery)\r\n\t\t\t{\r\n\t\t\t\tTrackBillboardModel.BeginBatch();\r\n\t\t\t\tforeach (SceneryItem scenery in SceneryItems)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!renderedSegments.Exists(a=> a.Number == scenery.SegmentRef))\r\n\t\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\t\tif (scenery is BillboardSceneryItem || scenery is TwoSidedBillboardSceneryItem)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tscenery.Render(_effect);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (GameConfig.Render3dScenery)\r\n\t\t\t{\r\n\t\t\t\tforeach (SceneryItem model in SceneryItems)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (model is ModelSceneryItem)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (!renderedSegments.Exists(a => a.Number == model.SegmentRef))\r\n\t\t\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\t\t\tmodel.Render(_effect);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic float GetHeightAtPoint(TrackNode node, Vector3 point)\r\n\t\t{\r\n\t\t\tVector3 a = node.GetLeftBoundary();\r\n\t\t\tVector3 b = node.GetRightBoundary();\r\n\t\t\tVector3 c = node.Next.GetRightBoundary();\r\n\t\t\tVector3 d = node.Next.GetLeftBoundary();\r\n\t\t\tVector3 hitLoc;\r\n\t\t\tfloat t;\r\n\t\t\t\r\n\t\t\tVector3 pos = point; pos.Y += 100;\r\n\t\t\tif (Utility.FindRayTriangleIntersection(ref pos, Vector3.Down, 1000f, ref a, ref b, ref c, out hitLoc, out t))\r\n\t\t\t{\r\n\t\t\t\treturn hitLoc.Y;\r\n\t\t\t}\r\n\t\t\telse if (Utility.FindRayTriangleIntersection(ref pos, Vector3.Down, 1000f, ref a, ref c, ref d, out hitLoc, out t))\r\n\t\t\t{\r\n\t\t\t\treturn hitLoc.Y;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\treturn -9999;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void Dispose()\r\n\t\t{\r\n\t\t\t_effect.Dispose();\r\n\t\t\tTerrainVertexBuffer.Dispose();\r\n\t\t\tif (FenceVertexBuffer != null) FenceVertexBuffer.Dispose();\r\n\t\t\tTrackFam.Dispose();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Tracks/TrackAssembler.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing OpenNFS1.Parsers.Track;\r\nusing System.IO;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\nusing System.Diagnostics;\r\nusing GameEngine;\r\nusing OpenNFS1.Parsers;\r\nusing OpenNFS1.Tracks;\r\nusing System.Linq;\r\n\r\nnamespace OpenNFS1.Loaders\r\n{\r\n\tclass TrackAssembler\r\n\t{\r\n\t\t//public static readonly int TRIANGLES_PER_ROW = 12;\r\n\t\tpublic static readonly int TRIANGLES_PER_SEGMENT = 2; //48;\r\n\t\tconst int OPEN_ROAD_CHECKPOINT_SCENERYITEM_ID = 124;\r\n\t\tpublic const int NbrTrianglesPerTerrainStrip = 8;\r\n\t\tpublic const int NbrVerticesPerTerrainStrip = 10;\r\n\t\tpublic const int NbrVerticesPerSegment = 10 * NbrVerticesPerTerrainStrip;\r\n\t\tpublic const int NbrNodesPerSegment = 4;\r\n\r\n\t\tprivate TrackfamFile _trackFam;\r\n\t\tprivate TriFile _tri;\r\n\r\n\t\tpublic string ProgressMessage;\r\n\r\n\t\tprivate void AddProgress(string progress)\r\n\t\t{\r\n\t\t\tProgressMessage += \"\\r\\n\" + progress;\r\n\t\t}\r\n\r\n\t\tpublic Track Assemble(TriFile tri)\r\n\t\t{\r\n\t\t\t_tri = tri;\r\n\t\t\tAddProgress(\"Reading track scenery file\");\r\n\t\t\t_trackFam = tri.IsOpenRoad ? new OpenRoadTrackfamFile(tri.FileName, GameConfig.AlternativeTimeOfDay) : new TrackfamFile(tri.FileName, GameConfig.AlternativeTimeOfDay);\r\n\t\t\tAssembleTrackSegments();\r\n\r\n\t\t\tTrack track = new Track();\r\n\t\t\ttrack.RoadNodes = _tri.Nodes;\r\n\t\t\ttrack.SceneryItems = AssembleSceneryItems();\r\n\t\t\ttrack.TerrainSegments = _tri.Segments;\r\n\t\t\ttrack.TerrainVertexBuffer = AssembleTerrainVertices();\r\n\t\t\ttrack.FenceVertexBuffer = AssembleFenceVertices();\r\n\t\t\ttrack.TrackFam = _trackFam;\r\n\t\t\ttrack.IsOpenRoad = _tri.IsOpenRoad;\r\n\r\n\t\t\t// Find checkpoint scenery object and use it to mark the end of the track.\r\n\t\t\t// Or ignore it and mark the end of the track as the last node to allow us to drive past the checkpoint!\r\n\t\t\tif (tri.IsOpenRoad && GameConfig.RespectOpenRoadCheckpoints)\r\n\t\t\t{\r\n\t\t\t\tforeach (var obj in _tri.Scenery)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (obj.Descriptor.ResourceId == OPEN_ROAD_CHECKPOINT_SCENERYITEM_ID)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttrack.CheckpointNode = obj.ReferenceNode;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\ttrack.CheckpointNode = _tri.Nodes.Count - 2;\r\n\t\t\t}\r\n\r\n\t\t\ttrack.Initialize();\r\n\t\t\treturn track;\r\n\t\t}\r\n\r\n\t\tprivate List<SceneryItem> AssembleSceneryItems()\r\n\t\t{\r\n\t\t\tAddProgress(\"Assembling scenery items\");\r\n\t\t\tList<SceneryItem> renderObjects = new List<SceneryItem>();\r\n\r\n\t\t\tforeach (var obj in _tri.Scenery)\r\n\t\t\t{\r\n\t\t\t\tSceneryItem renderObject;\r\n\t\t\t\tint bitmapId;\r\n\r\n\t\t\t\tswitch (obj.Descriptor.Type)\r\n\t\t\t\t{\r\n\t\t\t\t\tcase SceneryType.Bitmap:\r\n\t\t\t\t\t\tif ((obj.Descriptor.Flags & SceneryFlags.Animated) == SceneryFlags.Animated)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t// Successive bitmaps are referenced by incrementing the scenery descriptor\r\n\t\t\t\t\t\t\tList<Texture2D> textures = new List<Texture2D>();\r\n\t\t\t\t\t\t\tfor (int i = 0; i < obj.Descriptor.AnimationFrameCount; i++)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tvar nextBitmap = _tri.ObjectDescriptors[obj.Descriptor.Id + i];\r\n\t\t\t\t\t\t\t\tvar texture = _trackFam.GetSceneryTexture(nextBitmap.ResourceId);\r\n\t\t\t\t\t\t\t\ttextures.Add(texture);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\trenderObject = new AnimatedBillboardSceneryItem(textures);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tbitmapId = obj.Descriptor.ResourceId;\r\n\t\t\t\t\t\t\trenderObject = new BillboardSceneryItem(_trackFam.GetSceneryTexture(bitmapId));\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase SceneryType.TwoSidedBitmap:\r\n\r\n\t\t\t\t\t\tbitmapId = obj.Descriptor.ResourceId;\r\n\t\t\t\t\t\tint bitmap2Id = obj.Descriptor.Resource2Id;\r\n\t\t\t\t\t\trenderObject = new TwoSidedBillboardSceneryItem(_trackFam.GetSceneryTexture(bitmapId),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t_trackFam.GetSceneryTexture(bitmap2Id));\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase SceneryType.Model:\r\n\t\t\t\t\t\tint modelId = obj.Descriptor.ResourceId;\r\n\t\t\t\t\t\trenderObject = new ModelSceneryItem(_trackFam.GetMesh(modelId));\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tdefault:\r\n\t\t\t\t\t\tthrow new NotImplementedException();\r\n\t\t\t\t}\r\n\r\n\t\t\t\trenderObject.Size = new Vector2(obj.Descriptor.Width, obj.Descriptor.Height);\r\n\t\t\t\trenderObject.SegmentRef = obj.ReferenceNode / 4;\r\n\t\t\t\trenderObject.Position = _tri.Nodes[obj.ReferenceNode].Position + ((obj.RelativePosition * GameConfig.TerrainScale) * 240);\r\n\t\t\t\trenderObject.Orientation = MathHelper.ToRadians((_tri.Nodes[obj.ReferenceNode].Orientation) - ((obj.Orientation / 256) * 360));\r\n\t\t\t\trenderObject.Initialize();\r\n\t\t\t\trenderObjects.Add(renderObject);\r\n\r\n\t\t\t}\r\n\r\n\t\t\treturn renderObjects;\r\n\t\t}\r\n\r\n\r\n\t\tprivate VertexBuffer AssembleTerrainVertices()\r\n\t\t{\r\n\t\t\tAddProgress(\"Assembling terrain vertices\");\r\n\t\t\tList<VertexPositionTexture> vertices = new List<VertexPositionTexture>();\r\n\r\n\t\t\tfor (int segmentIndex = 0; segmentIndex < _tri.Segments.Count; segmentIndex++)\r\n\t\t\t{\r\n\t\t\t\tvar segment = _tri.Segments[segmentIndex];\r\n\t\t\t\tsegment.TerrainBufferIndex = vertices.Count;\r\n\r\n\t\t\t\t//Ground textures are rotated 90 degrees, so we swap u,v coordinates around\r\n\t\t\t\tfloat tU = 0.0f;\r\n\r\n\t\t\t\t//Right side of road\r\n\t\t\t\tfor (int j = 1; j < TriFile.NbrTerrainPointsPerSide; j++)\r\n\t\t\t\t{\r\n\t\t\t\t\ttU = 0.0f;\r\n\t\t\t\t\tint index1, index2;\r\n\t\t\t\t\tGetPointIndicesForRightSide(segment, j, out index1, out index2);\r\n\t\t\t\t\t\r\n\t\t\t\t\tfor (int row = 0; row < TriFile.NbrRowsPerSegment; row++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvertices.Add(new VertexPositionTexture(segment.Rows[row].GetPoint(index1), new Vector2(tU, 0.0f)));\r\n\t\t\t\t\t\tvertices.Add(new VertexPositionTexture(segment.Rows[row].GetPoint(index2), new Vector2(tU, 1.0f)));\r\n\t\t\t\t\t\ttU += 0.5f;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t//attach the end of this segment to the start of the next if its not the last\r\n\t\t\t\t\tif (segment.Next != null)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvertices.Add(new VertexPositionTexture(segment.Next.Rows[0].GetPoint(index1), new Vector2(tU, 0.0f)));\r\n\t\t\t\t\t\tvertices.Add(new VertexPositionTexture(segment.Next.Rows[0].GetPoint(index2), new Vector2(tU, 1.0f)));\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvertices.Add(new VertexPositionTexture(segment.Rows[TriFile.NbrRowsPerSegment - 1].GetPoint(index1), new Vector2(tU, 0.0f)));\r\n\t\t\t\t\t\tvertices.Add(new VertexPositionTexture(segment.Rows[TriFile.NbrRowsPerSegment - 1].GetPoint(index2), new Vector2(tU, 1.0f)));\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\t//Left side of road\r\n\t\t\t\tfor (int j = 1; j < TriFile.NbrTerrainPointsPerSide; j++)\r\n\t\t\t\t{\r\n\t\t\t\t\ttU = 0.0f;\r\n\t\t\t\t\tint index1, index2;\r\n\t\t\t\t\tGetPointIndicesForLeftSide(segment, j, out index1, out index2);\r\n\t\t\t\t\tfor (int row = 0; row < TriFile.NbrRowsPerSegment; row++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvertices.Add(new VertexPositionTexture(segment.Rows[row].GetPoint(index1), new Vector2(tU, 1.0f)));\r\n\t\t\t\t\t\tvertices.Add(new VertexPositionTexture(segment.Rows[row].GetPoint(index2), new Vector2(tU, 0.0f)));\r\n\t\t\t\t\t\ttU += 0.5f;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t//attach the end of this segment to the start of the next if its not the last\r\n\t\t\t\t\tif (segment.Next != null)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvertices.Add(new VertexPositionTexture(segment.Next.Rows[0].GetPoint(index1), new Vector2(tU, 1.0f)));\r\n\t\t\t\t\t\tvertices.Add(new VertexPositionTexture(segment.Next.Rows[0].GetPoint(index2), new Vector2(tU, 0.0f)));\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvertices.Add(new VertexPositionTexture(segment.Rows[TriFile.NbrRowsPerSegment - 1].GetPoint(index1), new Vector2(tU, 1.0f)));\r\n\t\t\t\t\t\tvertices.Add(new VertexPositionTexture(segment.Rows[TriFile.NbrRowsPerSegment - 1].GetPoint(index2), new Vector2(tU, 0.0f)));\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tvar vertexBuffer = new VertexBuffer(Engine.Instance.Device, typeof(VertexPositionTexture), vertices.Count, BufferUsage.WriteOnly);\r\n\t\t\tvertexBuffer.SetData<VertexPositionTexture>(vertices.ToArray());\r\n\t\t\treturn vertexBuffer;\r\n\t\t}\r\n\r\n\t\tvoid GetPointIndicesForRightSide(TerrainSegment segment, int terrainPointIndex, out int index1, out int index2)\r\n\t\t{\r\n\t\t\t// only care if this is the last terrain strip\r\n\t\t\tif (terrainPointIndex == TriFile.NbrTerrainPointsPerSide - 1)\r\n\t\t\t{\r\n\t\t\t\tvar node = _tri.Nodes[segment.Number * TriFile.NbrRowsPerSegment];\r\n\t\t\t\tif (node.NodeProperty == TrackNodeProperty.RIGHT_TUNNEL_A2_A9)\r\n\t\t\t\t{\r\n\t\t\t\t\tindex1 = 2;\r\n\t\t\t\t\tindex2 = 10;\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tindex1 = terrainPointIndex;\r\n\t\t\tindex2 = terrainPointIndex - 1;\r\n\t\t}\r\n\r\n\t\tvoid GetPointIndicesForLeftSide(TerrainSegment segment, int terrainPointIndex, out int index1, out int index2)\r\n\t\t{\r\n\t\t\t// only care if this is the last terrain strip\r\n\t\t\tif (terrainPointIndex == TriFile.NbrTerrainPointsPerSide - 1)\r\n\t\t\t{\r\n\t\t\t\tvar node = _tri.Nodes[segment.Number * TriFile.NbrRowsPerSegment];\r\n\t\t\t\tif (node.NodeProperty == TrackNodeProperty.LEFT_TUNNEL_A9_A4)\r\n\t\t\t\t{\r\n\t\t\t\t\tindex1 = 9;\r\n\t\t\t\t\tindex2 = 4;\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tif (node.NodeProperty == TrackNodeProperty.LEFT_TUNNEL_A9_A5)\r\n\t\t\t\t{\r\n\t\t\t\t\tindex1 = 9;\r\n\t\t\t\t\tindex2 = 5;\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tindex1 = TriFile.NbrTerrainPointsPerSide + terrainPointIndex - 1;\r\n\t\t\tindex2 = TriFile.NbrTerrainPointsPerSide + terrainPointIndex;\r\n\t\t}\r\n\r\n\t\t\r\n\r\n\t\t// Fences are defined by the TerrainSegment. If fence is enabled, we draw a fence from row0 to row2, then row2 to row4.\r\n\t\tVertexBuffer AssembleFenceVertices()\r\n\t\t{\r\n\t\t\tAddProgress(\"Assembling fence vertices\");\r\n\t\t\tVector3 fenceHeight = new Vector3(0, GameConfig.TerrainScale * 50000, 0);\r\n\r\n\t\t\tList<VertexPositionTexture> vertices = new List<VertexPositionTexture>();\r\n\r\n\t\t\tfor (int i = 0; i < _tri.Segments.Count; i++)\r\n\t\t\t{\r\n\t\t\t\tvar segment = _tri.Segments[i];\r\n\t\t\t\tif (!(segment.HasLeftFence | segment.HasRightFence)) continue;\r\n\r\n\t\t\t\tsegment.FenceBufferIndex = vertices.Count;\r\n\t\t\t\tsegment.FenceTexture = _trackFam.GetFenceTexture(segment.FenceTextureId);\r\n\t\t\t\tvar node0 = _tri.Nodes[i * NbrNodesPerSegment];\r\n\t\t\t\tvar node2 = node0.Next.Next;\r\n\t\t\t\tvar node4 = node2.Next.Next;\r\n\r\n\t\t\t\tif (segment.HasLeftFence)\r\n\t\t\t\t{\r\n\t\t\t\t\tvertices.Add(new VertexPositionTexture(node0.GetLeftBoundary(), new Vector2(0, 1)));\r\n\t\t\t\t\tvertices.Add(new VertexPositionTexture(node0.GetLeftBoundary() + fenceHeight, new Vector2(0, 0)));\r\n\t\t\t\t\tvertices.Add(new VertexPositionTexture(node2.GetLeftBoundary(), new Vector2(1.5f, 1)));\r\n\t\t\t\t\tvertices.Add(new VertexPositionTexture(node2.GetLeftBoundary() + fenceHeight, new Vector2(1.5f, 0)));\r\n\t\t\t\t\tvertices.Add(new VertexPositionTexture(node4.GetLeftBoundary(), new Vector2(3, 1)));\r\n\t\t\t\t\tvertices.Add(new VertexPositionTexture(node4.GetLeftBoundary() + fenceHeight, new Vector2(3, 0)));\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (segment.HasRightFence)\r\n\t\t\t\t{\r\n\t\t\t\t\tvertices.Add(new VertexPositionTexture(node0.GetRightBoundary(), new Vector2(0, 1)));\r\n\t\t\t\t\tvertices.Add(new VertexPositionTexture(node0.GetRightBoundary() + fenceHeight, new Vector2(0, 0)));\r\n\t\t\t\t\tvertices.Add(new VertexPositionTexture(node2.GetRightBoundary(), new Vector2(1.5f, 1)));\r\n\t\t\t\t\tvertices.Add(new VertexPositionTexture(node2.GetRightBoundary() + fenceHeight, new Vector2(1.5f, 0)));\r\n\t\t\t\t\tvertices.Add(new VertexPositionTexture(node4.GetRightBoundary(), new Vector2(3, 1)));\r\n\t\t\t\t\tvertices.Add(new VertexPositionTexture(node4.GetRightBoundary() + fenceHeight, new Vector2(3, 0)));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (vertices.Count == 0) return null;\r\n\t\t\tvar vertexBuffer = new VertexBuffer(Engine.Instance.Device, typeof(VertexPositionTexture), vertices.Count, BufferUsage.WriteOnly);\r\n\t\t\tvertexBuffer.SetData<VertexPositionTexture>(vertices.ToArray());\r\n\t\t\treturn vertexBuffer;\r\n\t\t}\r\n\r\n\t\tvoid AssembleTrackSegments()\r\n\t\t{\r\n\t\t\tAddProgress(\"Assembling track segments\");\r\n\r\n\t\t\tconst int textureCount = 10;\r\n\t\t\tforeach (var segment in _tri.Segments)\r\n\t\t\t{\r\n\t\t\t\tsegment.Textures = new Texture2D[textureCount];\r\n\t\t\t\tfor (int i = 0; i < textureCount; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tsegment.Textures[i] = _trackFam.GetGroundTexture(segment.TextureIds[i]);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// calculate bounding size for this segment\r\n\t\t\t\tvar firstRow = segment.Rows[0];\r\n\t\t\t\tvar lastRow = segment.Next != null ? segment.Next.Rows[0] : segment.Rows[TriFile.NbrRowsPerSegment - 1];\r\n\t\t\t\tVector3 min = new Vector3(float.MaxValue), max = new Vector3();\r\n\t\t\t\tList<Vector3> allPoints = new List<Vector3>();\r\n\t\t\t\tallPoints.AddRange(firstRow.LeftPoints);\r\n\t\t\t\tallPoints.AddRange(firstRow.RightPoints);\r\n\t\t\t\tallPoints.AddRange(lastRow.LeftPoints);\r\n\t\t\t\tallPoints.AddRange(lastRow.RightPoints);\r\n\r\n\t\t\t\tforeach (var pos in allPoints)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (pos.X < min.X) min.X = pos.X;\r\n\t\t\t\t\tif (pos.X > max.X) max.X = pos.X;\r\n\t\t\t\t\tif (pos.Y < min.Y) min.Y = pos.Y;\r\n\t\t\t\t\tif (pos.Y > max.Y) max.Y = pos.Y;\r\n\t\t\t\t\tif (pos.Z < min.Z) min.Z = pos.Z;\r\n\t\t\t\t\tif (pos.Z > max.Z) max.Z = pos.Z;\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tsegment.BoundingBox = new BoundingBox(min, max);\r\n\t\t\t}\r\n\t\t\r\n\t\t}\r\n\r\n\t\tpublic List<Triangle> GeneratePhysicalVertices(List<TrackNode> nodes)\r\n\t\t{\r\n\t\t\tList<VertexPositionColor> verts = new List<VertexPositionColor>();\r\n\t\t\tList<Triangle> roadVerts = new List<Triangle>();\r\n\t\t\tTrackNode nextNode;\r\n\t\t\t//Vector3 prevLeft = GetRoadOffsetPosition(nodes[nodes.Count - 1], -nodes[nodes.Count - 1].DistanceToLeftBarrier); // nodes[0].Position + Utility.RotatePoint(new Vector2(-nodes[0].DistanceToLeftBarrier, 0), nodes[0].Orientation);\r\n\t\t\t//Vector3 prevRight = GetRoadOffsetPosition(nodes[nodes.Count - 1], nodes[nodes.Count - 1].DistanceToRightBarrier); // nodes[0].Position + Utility.RotatePoint(new Vector2(nodes[0].DistanceToRightBarrier, 0), nodes[0].Orientation);\r\n\r\n\t\t\tfor (int i = 0; i < nodes.Count; i++)\r\n\t\t\t{\r\n\t\t\t\tTrackNode node = nodes[i];\r\n\r\n\t\t\t\tif (i + 1 < nodes.Count)\r\n\t\t\t\t\tnextNode = nodes[i + 1];\r\n\t\t\t\telse\r\n\t\t\t\t\tnextNode = nodes[0];  //join up to start line\r\n\r\n\r\n\t\t\t\tfloat zPos = node.Position.Z - nextNode.Position.Z;\r\n\t\t\t\t\r\n\t\t\t\tVector3 prevLeft = GetRoadOffsetPosition(node, -node.DistanceToLeftBarrier);\r\n\t\t\t\tVector3 prevRight = GetRoadOffsetPosition(node, node.DistanceToRightBarrier);\r\n\r\n\t\t\t\tVector3 currentLeft = nextNode.Position + Utility.RotatePoint(new Vector2(-node.DistanceToLeftBarrier, 0), -node.Orientation);\r\n\t\t\t\tVector3 currentRight = nextNode.Position + Utility.RotatePoint(new Vector2(node.DistanceToRightBarrier, 0), -node.Orientation);\r\n\r\n\t\t\t\tvar t = new Triangle(nextNode.GetLeftBoundary(), node.GetLeftBoundary(), node.GetRightBoundary());\r\n\t\t\t\tvar t2 = new Triangle(nextNode.GetLeftBoundary(), node.GetRightBoundary(), nextNode.GetRightBoundary());\r\n\t\t\t\troadVerts.Add(t);\r\n\t\t\t\troadVerts.Add(t2);\r\n\r\n\t\t\t\tvar normal = Vector3.Normalize(Vector3.Cross(t.V2 - t.V1, t.V3 - t.V1));\r\n\t\t\t\tvar normal2 = Vector3.Normalize(Vector3.Cross(t2.V2 - t2.V1, t2.V3 - t2.V1));\r\n\t\t\t\tif (Math.Round(normal.X, 2) != Math.Round(normal2.X, 2) ||\r\n\t\t\t\t\tMath.Round(normal.Y, 2) != Math.Round(normal2.Y, 2) ||\r\n\t\t\t\t\tMath.Round(normal.Z, 2) != Math.Round(normal2.Z, 2))\r\n\t\t\t\t{\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\t//roadVerts.Add(prevLeft);\r\n\t\t\t\t//roadVerts.Add(currentLeft);\r\n\t\t\t\t//roadVerts.Add(prevRight);\r\n\t\t\t\t//roadVerts.Add(currentLeft);\r\n\t\t\t\t//roadVerts.Add(prevRight);\r\n\t\t\t\t//roadVerts.Add(currentRight);\r\n\r\n\t\t\t\tprevLeft = currentLeft;\r\n\t\t\t\tprevRight = currentRight;\r\n\t\t\t}\r\n\r\n\t\t\t//_vertexBuffer = new VertexBuffer(Engine.Instance.Device, VertexPositionColor.SizeInBytes * verts.Count, BufferUsage.WriteOnly);\r\n\t\t\t//_vertexBuffer.SetData<VertexPositionColor>(verts.ToArray());\r\n\t\t\treturn roadVerts;\r\n\t\t}\r\n\r\n\t\tpublic static Vector3 GetRoadOffsetPosition(TrackNode roadNode, float offset)\r\n\t\t{\r\n\t\t\tVector3 position = roadNode.Position + Utility.RotatePoint(new Vector2(offset, 0), -roadNode.Orientation);\r\n\t\t\treturn position;\r\n\t\t}\r\n\r\n\t}\r\n}"
  },
  {
    "path": "OpenNFS1/Tracks/TrackBillboardModel.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\n\r\nnamespace OpenNFS1\r\n{\r\n\tstatic class TrackBillboardModel\r\n\t{\r\n\t\tstatic VertexPositionTexture[] _vertices;\r\n\t\tstatic VertexBuffer _vertexBuffer;\r\n\r\n\t\tstatic TrackBillboardModel()\r\n\t\t{\r\n\t\t\tCreateGeometry();\r\n\r\n\t\t\t//_renderEffect = new AlphaTestEffect(Engine.Instance.Device);\r\n\t\t\t//_renderEffect.AlphaFunction = CompareFunction.Greater;\r\n\t\t\t//_renderEffect.ReferenceAlpha = 5;\r\n\t\t}\r\n\r\n\t\tpublic static void BeginBatch()\r\n\t\t{\r\n\t\t\tEngine.Instance.Device.SetVertexBuffer(_vertexBuffer);\r\n\r\n\t\t\t//_renderEffect.View = Engine.Instance.Camera.View;\r\n\t\t\t//_renderEffect.Projection = Engine.Instance.Camera.Projection;\r\n\t\t\t//_renderEffect.CurrentTechnique.Passes[0].Apply();\r\n\t\t}\r\n\r\n\t\t//public static void Render(AlphaTestEffect effect, Texture2D texture)\r\n\t\t//{\r\n\t\t//\t_renderEffect.World = world;\r\n\t\t//\t_renderEffect.Texture = texture;\r\n\t\t//\t_renderEffect.CurrentTechnique.Passes[0].Apply();\r\n\r\n\t\t//\tEngine.Instance.Device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);\r\n\t\t//}\r\n\r\n\t\tprivate static void CreateGeometry()\r\n\t\t{\r\n\t\t\tVector3 topLeftFront = new Vector3(-0.5f, 1.0f, 0.5f);\r\n\t\t\tVector3 bottomLeftFront = new Vector3(-0.5f, 0.0f, 0.5f);\r\n\t\t\tVector3 topRightFront = new Vector3(0.5f, 1.0f, 0.5f);\r\n\t\t\tVector3 bottomRightFront = new Vector3(0.5f, 0.0f, 0.5f);\r\n\r\n\t\t\tVector2 textureTopLeft = new Vector2(0.0f, 0.0f);\r\n\t\t\tVector2 textureTopRight = new Vector2(1.0f, 0.0f);\r\n\t\t\tVector2 textureBottomLeft = new Vector2(0.0f, 1.0f);\r\n\t\t\tVector2 textureBottomRight = new Vector2(1.0f, 1.0f);\r\n\r\n\t\t\t_vertices = new VertexPositionTexture[4];\r\n\t\t\t_vertices[0] = new VertexPositionTexture(topLeftFront, textureTopLeft);\r\n\t\t\t_vertices[1] = new VertexPositionTexture(bottomLeftFront, textureBottomLeft);\r\n\t\t\t_vertices[2] = new VertexPositionTexture(topRightFront, textureTopRight);\r\n\t\t\t_vertices[3] = new VertexPositionTexture(bottomRightFront, textureBottomRight);\r\n\r\n\t\t\t_vertexBuffer = new VertexBuffer(Engine.Instance.Device,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t typeof(VertexPositionTexture), _vertices.Length,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t BufferUsage.WriteOnly);\r\n\r\n\t\t\t_vertexBuffer.SetData<VertexPositionTexture>(_vertices);\r\n\t\t}\r\n\r\n\t}\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Tracks/TrackDescription.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\n\r\nusing System.Text;\r\nusing System.IO;\r\n\r\nnamespace OpenNFS1\r\n{\r\n    class TrackDescription\r\n    {\r\n        static List<TrackDescription> _trackDescriptions;\r\n\r\n        public static List<TrackDescription> Descriptions\r\n        {\r\n            get { return TrackDescription._trackDescriptions; }\r\n        }\r\n\r\n        public string Name;\r\n        public string FileName;\r\n        public bool IsOpenRoad;\r\n        public string ImageFile;\r\n        public bool HideFromMenu;\r\n\t\tpublic string AlternativeTimeOfDay;\r\n\r\n\t\tstatic TrackDescription()\r\n\t\t{\r\n\t\t\t_trackDescriptions = new List<TrackDescription>();\r\n\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\al1.tri\",\r\n\t\t\t\tName = \"Alpine\",\r\n\t\t\t\tIsOpenRoad = true,\r\n\t\t\t\tImageFile = \"alpine.qfs\",\r\n\t\t\t\tAlternativeTimeOfDay = \"Morning\"\r\n\t\t\t});\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\cl1.tri\",\r\n\t\t\t\tName = \"Coastal\",\r\n\t\t\t\tIsOpenRoad = true,\r\n\t\t\t\tImageFile = \"coast.qfs\",\r\n\t\t\t\tAlternativeTimeOfDay = \"Morning\"\r\n\t\t\t});\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\cy1.tri\",\r\n\t\t\t\tName = \"City\",\r\n\t\t\t\tIsOpenRoad = true,\r\n\t\t\t\tImageFile = \"city.qfs\",\r\n\t\t\t\tAlternativeTimeOfDay = \"Morning\"\r\n\t\t\t});\r\n\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\tr1.tri\",\r\n\t\t\t\tName = \"Rusty Springs\",\r\n\t\t\t\tIsOpenRoad = false,\r\n\t\t\t\tImageFile = \"springs.qfs\",\r\n\t\t\t\tAlternativeTimeOfDay = \"Morning\"\r\n\t\t\t});\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\tr2.tri\",\r\n\t\t\t\tName = \"Autumn Valley\",\r\n\t\t\t\tIsOpenRoad = false,\r\n\t\t\t\tImageFile = \"avalley.qfs\",\r\n\t\t\t\tAlternativeTimeOfDay = \"Morning\"\r\n\t\t\t});\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\tr3.tri\",\r\n\t\t\t\tName = \"Vertigo Ridge\",\r\n\t\t\t\tIsOpenRoad = false,\r\n\t\t\t\tImageFile = \"vridge.qfs\",\r\n\t\t\t\tAlternativeTimeOfDay = \"Morning\"\r\n\t\t\t});\r\n\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\tr5.tri\",\r\n\t\t\t\tName = \"Oasis Springs (hidden track)\",\r\n\t\t\t\tIsOpenRoad = false,\r\n\t\t\t\tImageFile = \"springsr.qfs\",\r\n\t\t\t\tAlternativeTimeOfDay = \"Morning\"\r\n\t\t\t});\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\tr6.tri\",\r\n\t\t\t\tName = \"Burnt Sienna\",\r\n\t\t\t\tIsOpenRoad = false,\r\n\t\t\t\tImageFile = \"bsienna.qfs\",\r\n\t\t\t\tAlternativeTimeOfDay = \"Morning\"\r\n\t\t\t});\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\tr7.tri\",\r\n\t\t\t\tName = \"Transpolis\",\r\n\t\t\t\tIsOpenRoad = false,\r\n\t\t\t\tImageFile = \"tpolis.qfs\",\r\n\t\t\t\tAlternativeTimeOfDay = \"Morning\"\r\n\t\t\t});\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\tr4.tri\",\r\n\t\t\t\tName = \"Lost Vegas\",\r\n\t\t\t\tIsOpenRoad = false,\r\n\t\t\t\tImageFile = \"lvegas.qfs\",\r\n\t\t\t});\r\n\r\n\r\n\t\t\t//Open road stages\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\al2.tri\",\r\n\t\t\t\tName = \"Alpine 2\",\r\n\t\t\t\tIsOpenRoad = true,\r\n\t\t\t\tImageFile = \"alpine.qfs\",\r\n\t\t\t\tHideFromMenu = true\r\n\t\t\t});\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\al3.tri\",\r\n\t\t\t\tName = \"Alpine 3\",\r\n\t\t\t\tIsOpenRoad = true,\r\n\t\t\t\tImageFile = \"alpine.qfs\",\r\n\t\t\t\tHideFromMenu = true\r\n\t\t\t});\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\cy2.tri\",\r\n\t\t\t\tName = \"City 2\",\r\n\t\t\t\tIsOpenRoad = true,\r\n\t\t\t\tImageFile = \"city.qfs\",\r\n\t\t\t\tHideFromMenu = true\r\n\t\t\t});\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\cy3.tri\",\r\n\t\t\t\tName = \"City 3\",\r\n\t\t\t\tIsOpenRoad = true,\r\n\t\t\t\tImageFile = \"city.qfs\",\r\n\t\t\t\tHideFromMenu = true\r\n\t\t\t});\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\cl2.tri\",\r\n\t\t\t\tName = \"Coastal 2\",\r\n\t\t\t\tIsOpenRoad = true,\r\n\t\t\t\tImageFile = \"coast.qfs\",\r\n\t\t\t\tHideFromMenu = true\r\n\t\t\t});\r\n\t\t\t_trackDescriptions.Add(new TrackDescription()\r\n\t\t\t{\r\n\t\t\t\tFileName = \"SIMDATA\\\\MISC\\\\cl3.tri\",\r\n\t\t\t\tName = \"Coastal 3\",\r\n\t\t\t\tIsOpenRoad = true,\r\n\t\t\t\tImageFile = \"coast.qfs\",\r\n\t\t\t\tHideFromMenu = true\r\n\t\t\t});\r\n\t\t}\r\n\r\n        /// <summary>\r\n        /// Quick and very dirty way of getting the next stage in an open road race\r\n        /// </summary>\r\n        /// <param name=\"openRoadTrack\"></param>\r\n        /// <returns></returns>\r\n        public static TrackDescription GetNextOpenRoadStage(TrackDescription openRoadTrack)\r\n        {\r\n            string lookForNumber = Path.GetFileNameWithoutExtension(openRoadTrack.FileName).Substring(2);\r\n            lookForNumber = (int.Parse(lookForNumber)+1).ToString();\r\n\r\n            return _trackDescriptions.Find(delegate(TrackDescription track)\r\n            {\r\n                if (Path.GetFileName(track.FileName).StartsWith(Path.GetFileName(openRoadTrack.FileName).Substring(0,\r\n2)))\r\n                {\r\n                    if (Path.GetFileNameWithoutExtension(track.FileName).EndsWith(lookForNumber))\r\n                        return true;\r\n                }\r\n                return false;\r\n            });\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Tracks/TrackNode.cs",
    "content": "﻿using Microsoft.Xna.Framework;\r\nusing OpenNFS1;\r\nusing GameEngine;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\n\r\nnamespace OpenNFS1.Tracks\r\n{\r\n\tclass TrackNodeProperty\r\n\t{\r\n\t\t// Unimplemented:\r\n\t\tpublic const byte LANE_SPLIT = 0;  // Used in Alpine, Coastal tracks when the road widens. vertices should be moved to the right. \r\n\t\tpublic const byte LANE_REJOIN = 2;  //Used in Alpine, Coastal tracks when the road narrows. vertices should be moved to the right\r\n\t\tpublic const byte TUNNEL = 4;  //plays tunnel sound\r\n\t\tpublic const byte COBBLED_ROAD = 5;  //plays different road noise (Last Vegas)\r\n\t\tpublic const byte WATERFALL_AUDIO_LEFT_CHANNEL = 14;  //plays waterfall audio in left channel\r\n\t\tpublic const byte WATERFALL_AUDIO_RIGHT_CHANNEL = 15;  //plays waterfall audio in right channel\r\n\t\tpublic const byte WATER_AUDIO_LEFT_CHANNEL = 18;  //plays water sound\r\n\t\tpublic const byte WATER_AUDIO_RIGHT_CHANNEL = 18;  //plays water sound\r\n\r\n\t\t// Implemented:\r\n\t\t// The next 3 properties are interesting!  If set, either the last terrain strip on the right or left is detached and \r\n\t\t// placed between the specified points.  This is how the tunnels in Vertigo Ridge and Coastal (and Alpine 3) are constructed.\r\n\t\t// For the meaning of A2,A9 etc, refer to /NFSSpecs.txt\r\n\t\tpublic const byte RIGHT_TUNNEL_A2_A9 = 7;\r\n\t\tpublic const byte LEFT_TUNNEL_A9_A4 = 12;\r\n\t\tpublic const byte LEFT_TUNNEL_A9_A5 = 13;\r\n\t}\r\n\t\r\n\tclass TrackNode\r\n\t{\r\n\t\tpublic int Number;\r\n\t\tpublic float DistanceToLeftVerge, DistanceToLeftBarrier;\r\n\t\tpublic float DistanceToRightVerge, DistanceToRightBarrier;\r\n\t\tpublic byte Flag1;\r\n\t\tpublic byte Flag2;\r\n\t\tpublic byte Flag3;\r\n\t\tpublic byte NodeProperty;\r\n\t\tpublic Vector3 Position;\r\n\t\tpublic float Slope;\r\n\t\tpublic Int32 Slant, ZOrientation, XOrientation;\r\n\t\tpublic float Orientation;\r\n\t\tpublic Vector3 Up;\r\n\t\tpublic byte[] unk1, unk2;\r\n\t\tpublic TrackNode Next, Prev;\r\n\r\n\t\tconst float SlantMultiplier = 1.028f;\r\n\t\tconst float RoadPadding = 7;\r\n\r\n\t\tpublic Vector3 GetLeftBoundary()\r\n\t\t{\r\n\t\t\treturn GetLeftOffset(DistanceToLeftBarrier);\r\n\t\t}\r\n\t\tpublic Vector3 GetLeftVerge()\r\n\t\t{\r\n\t\t\treturn GetLeftOffset(DistanceToLeftVerge);\r\n\t\t}\r\n\t\tpublic Vector3 GetRightBoundary()\r\n\t\t{\r\n\t\t\treturn GetRightOffset(DistanceToRightBarrier);\r\n\t\t}\r\n\t\tpublic Vector3 GetRightVerge()\r\n\t\t{\r\n\t\t\treturn GetRightOffset(DistanceToRightVerge);\r\n\t\t}\r\n\r\n\t\tpublic Vector3 GetLeftVerge2()\r\n\t\t{\r\n\t\t\treturn GetLeftOffset(DistanceToLeftVerge - RoadPadding);\r\n\t\t}\r\n\r\n\t\tpublic Vector3 GetRightVerge2()\r\n\t\t{\r\n\t\t\treturn GetRightOffset(DistanceToRightVerge - RoadPadding);\r\n\t\t}\r\n\r\n\t\tpublic Vector3 GetMiddlePoint()\r\n\t\t{\r\n\t\t\treturn (GetLeftVerge() + GetRightVerge()) / 2;\r\n\t\t}\r\n\r\n\r\n\t\tprivate Vector3 GetLeftOffset(float offset)\r\n\t\t{\r\n\t\t\tVector3 position = Position + Utility.RotatePoint(new Vector2(-offset, 0), -Orientation);\r\n\r\n\t\t\tfloat slantAngle = (((float)Slant / short.MaxValue));\r\n\r\n\t\t\tvar pos3 = Vector3.Transform(new Vector3(-offset, 0, 0), Matrix.CreateRotationZ(slantAngle * SlantMultiplier) * Matrix.CreateRotationY(MathHelper.ToRadians(Orientation)));\r\n\t\t\treturn Position + pos3;\r\n\t\t}\r\n\r\n\t\tprivate Vector3 GetRightOffset(float offset)\r\n\t\t{\r\n\t\t\tfloat slantAngle = (((float)Slant / short.MaxValue)); // 32000f));\r\n\t\t\tvar pos3 = Vector3.Transform(new Vector3(offset, 0, 0), Matrix.CreateRotationZ(slantAngle * SlantMultiplier) * Matrix.CreateRotationY(MathHelper.ToRadians(Orientation)));\r\n\t\t\treturn Position + pos3;\r\n\t\t}\r\n\t}\r\n}"
  },
  {
    "path": "OpenNFS1/Tracks/TrackObjectDescriptor.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\n\r\nnamespace OpenNFS1.Tracks\r\n{\r\n\tclass TrackObjectDescriptor\r\n\t{\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Tracks/TrackSkybox.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Content;\r\nusing GameEngine;\r\nusing System.IO;\r\n\r\nnamespace OpenNFS1.Tracks\r\n{\r\n\tpublic class TrackSkyBox\r\n\t{\r\n\t\tTexture2D[] textures = new Texture2D[6];\r\n\r\n\t\tpublic Texture2D[] Textures\r\n\t\t{\r\n\t\t\tget { return textures; }\r\n\t\t\tset { textures = value; }\r\n\t\t}\r\n\r\n\t\tBasicEffect _effect;\r\n\r\n\t\tVertexBuffer vertices;\r\n\t\tIndexBuffer indices;\r\n\t\tbool _hasGeneratedTopBottomTextures;\r\n\r\n\t\tpublic float YOffset;\r\n\r\n\t\tpublic TrackSkyBox(Texture2D horizon)\r\n\t\t{\r\n\t\t\t// This is silly... for some reason after loading the track on a separate thread, we have to wait until \r\n\t\t\t// we get called in the update() loop to be able to pull the pixel data to generate the top & bottom textures\r\n\t\t\t_hasGeneratedTopBottomTextures = false;\r\n\t\t\t\r\n\t\t\tTextures[0] = horizon;\r\n\t\t\tTextures[1] = horizon;\r\n\t\t\t//Textures[2] = bottomTexture;\r\n\t\t\t//Textures[3] = topTexture;\r\n\t\t\tTextures[4] = horizon;\r\n\t\t\tTextures[5] = horizon;\r\n\t\t\tYOffset = 60;\r\n\r\n\t\t\t_effect = new BasicEffect(Engine.Instance.Device);\r\n\t\t\t_effect.TextureEnabled = true;\r\n\r\n\t\t\tvertices = new VertexBuffer(Engine.Instance.Device,\r\n\t\t\t\t\t\t\t\ttypeof(VertexPositionTexture),\r\n\t\t\t\t\t\t\t\t4 * 6,\r\n\t\t\t\t\t\t\t\tBufferUsage.WriteOnly);\r\n\r\n\t\t\tVertexPositionTexture[] data = new VertexPositionTexture[4 * 6];\r\n\r\n\t\t\tfloat y = 0.3f;\r\n\r\n\t\t\t#region Define Vertexes\r\n\t\t\tVector3 vExtents = new Vector3(1400, 450, 800);\r\n\t\t\t//back\r\n\t\t\tdata[0].Position = new Vector3(vExtents.X, -vExtents.Y * y, -vExtents.Z);\r\n\t\t\tdata[0].TextureCoordinate.X = 1f; data[0].TextureCoordinate.Y = 1.0f;\r\n\t\t\tdata[1].Position = new Vector3(vExtents.X, vExtents.Y, -vExtents.Z);\r\n\t\t\tdata[1].TextureCoordinate.X = 1f; data[1].TextureCoordinate.Y = 0.0f;\r\n\t\t\tdata[2].Position = new Vector3(-vExtents.X, vExtents.Y, -vExtents.Z);\r\n\t\t\tdata[2].TextureCoordinate.X = 0f; data[2].TextureCoordinate.Y = 0.0f;\r\n\t\t\tdata[3].Position = new Vector3(-vExtents.X, -vExtents.Y * y, -vExtents.Z);\r\n\t\t\tdata[3].TextureCoordinate.X = 0f; data[3].TextureCoordinate.Y = 1.0f;\r\n\r\n\t\t\t//front\r\n\t\t\tdata[4].Position = new Vector3(-vExtents.X, -vExtents.Y * y, vExtents.Z);\r\n\t\t\tdata[4].TextureCoordinate.X = 1f; data[4].TextureCoordinate.Y = 1.0f;\r\n\t\t\tdata[5].Position = new Vector3(-vExtents.X, vExtents.Y, vExtents.Z);\r\n\t\t\tdata[5].TextureCoordinate.X = 1f; data[5].TextureCoordinate.Y = 0.0f;\r\n\t\t\tdata[6].Position = new Vector3(vExtents.X, vExtents.Y, vExtents.Z);\r\n\t\t\tdata[6].TextureCoordinate.X = 0f; data[6].TextureCoordinate.Y = 0.0f;\r\n\t\t\tdata[7].Position = new Vector3(vExtents.X, -vExtents.Y * y, vExtents.Z);\r\n\t\t\tdata[7].TextureCoordinate.X = 0f; data[7].TextureCoordinate.Y = 1.0f;\r\n\r\n\t\t\t//bottom\r\n\t\t\tdata[8].Position = new Vector3(-vExtents.X, -vExtents.Y * y, -vExtents.Z);\r\n\t\t\tdata[8].TextureCoordinate.X = 1f; data[8].TextureCoordinate.Y = 0.0f;\r\n\t\t\tdata[9].Position = new Vector3(-vExtents.X, -vExtents.Y * y, vExtents.Z);\r\n\t\t\tdata[9].TextureCoordinate.X = 1f; data[9].TextureCoordinate.Y = 1.0f;\r\n\t\t\tdata[10].Position = new Vector3(vExtents.X, -vExtents.Y * y, vExtents.Z);\r\n\t\t\tdata[10].TextureCoordinate.X = 0f; data[10].TextureCoordinate.Y = 1.0f;\r\n\t\t\tdata[11].Position = new Vector3(vExtents.X, -vExtents.Y * y, -vExtents.Z);\r\n\t\t\tdata[11].TextureCoordinate.X = 0f; data[11].TextureCoordinate.Y = 0.0f;\r\n\r\n\t\t\t//top\r\n\t\t\tdata[12].Position = new Vector3(vExtents.X, vExtents.Y, -vExtents.Z);\r\n\t\t\tdata[12].TextureCoordinate.X = 0f; data[12].TextureCoordinate.Y = 0.0f;\r\n\t\t\tdata[13].Position = new Vector3(vExtents.X, vExtents.Y, vExtents.Z);\r\n\t\t\tdata[13].TextureCoordinate.X = 0f; data[13].TextureCoordinate.Y = 1.0f;\r\n\t\t\tdata[14].Position = new Vector3(-vExtents.X, vExtents.Y, vExtents.Z);\r\n\t\t\tdata[14].TextureCoordinate.X = 1f; data[14].TextureCoordinate.Y = 1.0f;\r\n\t\t\tdata[15].Position = new Vector3(-vExtents.X, vExtents.Y, -vExtents.Z);\r\n\t\t\tdata[15].TextureCoordinate.X = 1f; data[15].TextureCoordinate.Y = 0.0f;\r\n\r\n\r\n\t\t\t//left\r\n\t\t\tdata[16].Position = new Vector3(-vExtents.X, vExtents.Y, -vExtents.Z);\r\n\t\t\tdata[16].TextureCoordinate.X = 0f; data[16].TextureCoordinate.Y = 0.0f;\r\n\t\t\tdata[17].Position = new Vector3(-vExtents.X, vExtents.Y, vExtents.Z);\r\n\t\t\tdata[17].TextureCoordinate.X = 1f; data[17].TextureCoordinate.Y = 0.0f;\r\n\t\t\tdata[18].Position = new Vector3(-vExtents.X, -vExtents.Y * y, vExtents.Z);\r\n\t\t\tdata[18].TextureCoordinate.X = 1f; data[18].TextureCoordinate.Y = 1.0f;\r\n\t\t\tdata[19].Position = new Vector3(-vExtents.X, -vExtents.Y * y, -vExtents.Z);\r\n\t\t\tdata[19].TextureCoordinate.X = 0f; data[19].TextureCoordinate.Y = 1.0f;\r\n\r\n\t\t\t//right\r\n\t\t\tdata[20].Position = new Vector3(vExtents.X, -vExtents.Y * y, -vExtents.Z);\r\n\t\t\tdata[20].TextureCoordinate.X = 1f; data[20].TextureCoordinate.Y = 1.0f;\r\n\t\t\tdata[21].Position = new Vector3(vExtents.X, -vExtents.Y * y, vExtents.Z);\r\n\t\t\tdata[21].TextureCoordinate.X = 0f; data[21].TextureCoordinate.Y = 1.0f;\r\n\t\t\tdata[22].Position = new Vector3(vExtents.X, vExtents.Y, vExtents.Z);\r\n\t\t\tdata[22].TextureCoordinate.X = 0f; data[22].TextureCoordinate.Y = 0.0f;\r\n\t\t\tdata[23].Position = new Vector3(vExtents.X, vExtents.Y, -vExtents.Z);\r\n\t\t\tdata[23].TextureCoordinate.X = 1f; data[23].TextureCoordinate.Y = 0.0f;\r\n\r\n\t\t\tvertices.SetData<VertexPositionTexture>(data);\r\n\r\n\r\n\t\t\tindices = new IndexBuffer(Engine.Instance.Device,\r\n\t\t\t\t\t\t\t\ttypeof(short), 6 * 6,\r\n\t\t\t\t\t\t\t\tBufferUsage.WriteOnly);\r\n\r\n\t\t\tshort[] ib = new short[6 * 6];\r\n\r\n\t\t\tfor (int x = 0; x < 6; x++)\r\n\t\t\t{\r\n\t\t\t\tib[x * 6 + 0] = (short)(x * 4 + 0);\r\n\t\t\t\tib[x * 6 + 2] = (short)(x * 4 + 1);\r\n\t\t\t\tib[x * 6 + 1] = (short)(x * 4 + 2);\r\n\r\n\t\t\t\tib[x * 6 + 3] = (short)(x * 4 + 2);\r\n\t\t\t\tib[x * 6 + 5] = (short)(x * 4 + 3);\r\n\t\t\t\tib[x * 6 + 4] = (short)(x * 4 + 0);\r\n\t\t\t}\r\n\r\n\t\t\tindices.SetData<short>(ib);\r\n\t\t\t#endregion\r\n\r\n\t\t}\r\n\r\n\t\tpublic void Update()\r\n\t\t{\r\n\t\t\tvar pos = Engine.Instance.Camera.Position;\r\n\t\t\tpos.Y += YOffset;\r\n\t\t\t_effect.World = Matrix.CreateTranslation(pos);\r\n\t\t\t_effect.Projection = Engine.Instance.Camera.Projection;\r\n\t\t\t_effect.View = Engine.Instance.Camera.View;\r\n\r\n\r\n\t\t\t// Skybox only has the side textures, generate a single-color top and bottom texture\r\n\t\t\tif (!_hasGeneratedTopBottomTextures)\r\n\t\t\t{\r\n\t\t\t\tColor[] pixel = new Color[1];\r\n\t\t\t\ttextures[0].GetData<Color>(0, new Rectangle(0, 0, 1, 1), pixel, 0, 1); //top left pixel\r\n\t\t\t\tvar topTexture = new Texture2D(Engine.Instance.Device, 1, 1);\r\n\t\t\t\ttopTexture.SetData<Color>(pixel);\r\n\r\n\t\t\t\tvar bottomTexture = new Texture2D(Engine.Instance.Device, 1, 1);\r\n\t\t\t\ttextures[0].GetData<Color>(0, new Rectangle(0, textures[0].Height - 1, 1, 1), pixel, 0, 1); //bottom left pixel\r\n\t\t\t\tbottomTexture.SetData<Color>(pixel);\r\n\r\n\t\t\t\tColor[] px = new Color[textures[0].Width * textures[1].Height];\r\n\t\t\t\ttextures[0].GetData(px);\r\n\r\n\t\t\t\tTextures[2] = bottomTexture;\r\n\t\t\t\tTextures[3] = topTexture;\r\n\t\t\t\t_hasGeneratedTopBottomTextures = true;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\tpublic void Render()\r\n\t\t{\r\n\t\t\tif (vertices == null)\r\n\t\t\t\treturn;\r\n\r\n\t\t\tGraphicsDevice device = Engine.Instance.Device;\r\n\r\n\t\t\tdevice.DepthStencilState = DepthStencilState.None;\r\n\t\t\tdevice.SamplerStates[0] = SamplerState.LinearWrap;\r\n\t\t\tdevice.RasterizerState = RasterizerState.CullCounterClockwise;\r\n\t\t\tdevice.SetVertexBuffer(vertices);\r\n\t\t\tdevice.Indices = indices;\t\t\t\r\n\r\n\t\t\tfor (int x = 0; x < 6; x++)\r\n\t\t\t{\r\n\t\t\t\tif (textures[x] == null) continue;\r\n\r\n\t\t\t\t_effect.Texture = textures[x];\r\n\t\t\t\t_effect.CurrentTechnique.Passes[0].Apply();\r\n\t\t\t\tdevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,\r\n\t\t\t\t\t0, 0, vertices.VertexCount, x * 6, 2);\r\n\t\t\t}\r\n\r\n\t\t\tdevice.DepthStencilState = DepthStencilState.Default;\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/UI/Screens/BaseUIScreen.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Media;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\nusing OpenNFS1.Parsers;\r\nusing System.IO;\r\n\r\nnamespace OpenNFS1.UI.Screens\r\n{\r\n    class BaseUIScreen\r\n    {\r\n\t\tprotected SpriteFont Font { get; private set; }\r\n\t\tstatic Texture2D _background;\r\n\t\tint _currentLine;\r\n\t\tprotected float TextSize = 0.5f;\r\n\t\tprotected float TitleSize = 1;\r\n\t\tprotected float SectionSize = 0.8f;\r\n\t\tprotected Color TextColor = Color.Orange;\r\n\r\n        public BaseUIScreen()\r\n        {\r\n\t\t\tFont = Engine.Instance.ContentManager.Load<SpriteFont>(\"Content\\\\ArialBlack-Italic\");\r\n\t\t\t_background = Engine.Instance.ContentManager.Load<Texture2D>(\"Content\\\\splash-screen.jpg\");\r\n        }\r\n\r\n\t\tpublic virtual void Draw()\r\n\t\t{\r\n\t\t\tEngine.Instance.SpriteBatch.Begin();\r\n\t\t\tif (_background != null)\r\n\t\t\t{\r\n\t\t\t\tEngine.Instance.SpriteBatch.Draw(_background, Vector2.Zero, Color.FromNonPremultiplied(50, 50, 50, 255));\r\n\t\t\t}\r\n\t\t\t_currentLine = 5;\r\n\t\t}\r\n\r\n\t\tpublic void WriteLine(string text)\r\n\t\t{\r\n\t\t\tWriteLine(text, Color.White, _currentLine, 30, TextSize);\r\n\t\t}\r\n\r\n\t\tpublic void WriteLine(string text, Color c)\r\n\t\t{\r\n\t\t\tWriteLine(text, c, _currentLine, 30, TextSize);\r\n\t\t}\r\n\r\n\t\tpublic void WriteLine(string text, bool selected, int lineOffset, int column, float size)\r\n\t\t{\r\n\t\t\tWriteLine(text, selected ? Color.Yellow : Color.LightGray, lineOffset, column, selected ? size * 1.1f : size);\r\n\t\t}\r\n\r\n\t\tpublic void WriteLine(string text, Color c, int lineOffset, int column, float size)\r\n\t\t{\r\n\t\t\tif (String.IsNullOrEmpty(text)) return;\r\n\r\n\t\t\t_currentLine += lineOffset;\r\n\t\t\tstring[] lines = text.Split(new string[] { \"\\r\\n\" }, StringSplitOptions.None);\r\n\t\t\tfor (int i = 0; i <lines.Length; i++)\r\n\t\t\t{\r\n\t\t\t\tif (lines[i] != \"\")\r\n\t\t\t\t{\r\n\t\t\t\t\tEngine.Instance.SpriteBatch.DrawString(Font, lines[i], new Vector2(column + 2, _currentLine + 2), Color.FromNonPremultiplied(0, 0, 0, 150), 0, Vector2.Zero, size, SpriteEffects.None, 0);\r\n\t\t\t\t\tEngine.Instance.SpriteBatch.DrawString(Font, lines[i], new Vector2(column, _currentLine), c, 0, Vector2.Zero, size, SpriteEffects.None, 0);\r\n\t\t\t\t}\r\n\t\t\t\tif (i < lines.Length-1)\r\n\t\t\t\t\t_currentLine += 20;\r\n\t\t\t}\r\n\t\t}\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/UI/Screens/ChooseDataDownloadScreen.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework.Input;\r\nusing GameEngine;\r\n\r\nnamespace OpenNFS1.UI.Screens\r\n{\r\n\tclass ChooseDataDownloadScreen : BaseUIScreen, IGameScreen\r\n\t{\r\n\t\tint _selectedIndex = 0;\r\n\r\n\t\tpublic ChooseDataDownloadScreen() : base()\r\n\t\t{\r\n\r\n\t\t}\r\n\r\n\t\tpublic void Update(GameTime gameTime)\r\n\t\t{\r\n\t\t\tif (Engine.Instance.Input.WasPressed(Keys.Left))\r\n\t\t\t{\r\n\t\t\t\t_selectedIndex = 0;\r\n\t\t\t}\r\n\t\t\telse if (Engine.Instance.Input.WasPressed(Keys.Right))\r\n\t\t\t{\r\n\t\t\t\t_selectedIndex = 1;\r\n\t\t\t}\r\n\r\n\t\t\tif (Engine.Instance.Input.WasPressed(Keys.Enter))\r\n\t\t\t{\r\n\t\t\t\tif (_selectedIndex == 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tEngine.Instance.Screen = new DataDownloadScreen();\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tEngine.Instance.Game.Exit();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic override void Draw()\r\n\t\t{\r\n\t\t\tbase.Draw();\r\n\r\n\t\t\tWriteLine(\"No game data found!\", Color.Red, 0, 30, TextSize);\r\n\t\t\tWriteLine(\"Download Need for Speed 1 SE CD data package (15mb)?\", TextColor, 80, 30, TextSize);\r\n\t\t\t\t\t\t\r\n\t\t\tWriteLine(\"OK\", _selectedIndex == 0, 40, 30, TextSize);\r\n\t\t\tWriteLine(\"No thanks\", _selectedIndex == 1, 0, 80, TextSize);\r\n\r\n\t\t\tWriteLine(\r\n\t\t\t\t\"Need for Speed 1 was produced by Pioneer Productions\\r\\nand EA Seattle in 1995.\\r\\n\" +\r\n\t\t\t\t\"1amStudios and OpenNFS1 are not affiliated in any way\\r\\nwith Pioneer Productions or EA Seattle.\", Color.LightGray, 120, 30, TextSize);\r\n\r\n\t\t\tEngine.Instance.SpriteBatch.End();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/UI/Screens/DataDownloadScreen.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Net;\r\nusing System.Text;\r\nusing System.Threading;\r\nusing Ionic.Zip;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Input;\r\nusing GameEngine;\r\n\r\nnamespace OpenNFS1.UI.Screens\r\n{\r\n\tclass DataDownloadScreen : BaseUIScreen, IGameScreen\r\n\t{\r\n\t\tThread _downloadThread;\r\n\t\tlong _dataContentLength, _dataDownloaded;\r\n\t\tbool _downloadError;\r\n\t\tbool _unpacking;\r\n\t\tbool _completed;\r\n\r\n\t\tpublic DataDownloadScreen() : base()\r\n\t\t{\r\n\t\t\t_downloadThread = new Thread(DownloadDataThreadProc);\r\n\t\t\t_downloadThread.Priority = ThreadPriority.AboveNormal;\r\n\t\t\t_downloadThread.Start();\r\n\t\t}\r\n\r\n\t\tpublic void Update(Microsoft.Xna.Framework.GameTime gameTime)\r\n\t\t{\r\n\t\t\tif (Engine.Instance.Input.WasPressed(Keys.Enter))\r\n\t\t\t{\r\n\t\t\t\tif (_completed)\r\n\t\t\t\t\tEngine.Instance.Screen = new HomeScreen();\r\n\t\t\t\telse if (_downloadError)\r\n\t\t\t\t\tEngine.Instance.Game.Exit();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic override void Draw()\r\n\t\t{\r\n\t\t\tbase.Draw();\r\n\r\n\t\t\tWriteLine(\"Downloading CD data package\", Color.Red, 0, 30, TextSize);\r\n\r\n\t\t\tif (_dataDownloaded > 0)\r\n\t\t\t{\r\n\t\t\t\tint ratio = (int)(((double)_dataDownloaded / (double)_dataContentLength) * 50);\r\n\t\t\t\tstring progress = new string('=', ratio);\r\n\t\t\t\tWriteLine(progress, TextColor, 80, 30, TextSize);\r\n\r\n\t\t\t\tlong downloadedMb = _dataDownloaded / 1024 / 1024;\r\n\t\t\t\tlong contentLengthMb = _dataContentLength / 1024 / 1024;\r\n\t\t\t\tWriteLine(String.Format(\"Downloaded {0}mb / {1}mb\", downloadedMb, contentLengthMb), TextColor, 20, 30, TextSize);\r\n\t\t\t}\r\n\r\n\t\t\tif (_unpacking)\r\n\t\t\t{\r\n\t\t\t\tWriteLine(\"Unpacking into CD_Data folder...\", TextColor, 30, 30, TextSize);\r\n\t\t\t}\r\n\t\t\tif (_completed)\r\n\t\t\t{\r\n\t\t\t\tWriteLine(\"Unpacking complete! Hit enter to continue.\", TextColor, 30, 30, TextSize);\r\n\t\t\t}\r\n\r\n\t\t\tif (_downloadError)\r\n\t\t\t{\r\n\t\t\t\tWriteLine(\"An error occured while downloading - please check\\r\\nexception.txt for details.\\r\\n\\r\\nHit enter to quit.\", TextColor, 60, 30, TextSize);\r\n\t\t\t}\r\n\r\n\t\t\tEngine.Instance.SpriteBatch.End();\r\n\t\t}\r\n\r\n\t\tprivate void DownloadDataThreadProc()\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tstring url = \"http://www.1amstudios.com/download/NFSSE_cd_data.zip\";\r\n\r\n\t\t\t\tWebRequest request = WebRequest.Create(url);\r\n\t\t\t\tvar response = request.GetResponse();\r\n\t\t\t\t_dataContentLength = long.Parse(response.Headers[\"Content-Length\"]);\r\n\r\n\t\t\t\tbyte[] buffer = new byte[4096];\r\n\t\t\t\tstring tempFileName = Path.GetTempFileName();\r\n\t\t\t\tStream fileStream = File.Open(tempFileName, FileMode.Create);\r\n\t\t\t\tusing (Stream s = response.GetResponseStream())\r\n\t\t\t\t{\r\n\t\t\t\t\twhile (true)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tint read = s.Read(buffer, 0, 4096);\r\n\t\t\t\t\t\t_dataDownloaded += read;\r\n\t\t\t\t\t\tif (read == 0)\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tfileStream.Write(buffer, 0, read);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tfileStream.Close();\r\n\t\t\t\t_unpacking = true;\r\n\r\n\t\t\t\tvar zipFile = ZipFile.Read(tempFileName);\r\n\t\t\t\tDirectory.CreateDirectory(\"CD_Data\");\r\n\t\t\t\tzipFile.ExtractAll(\"CD_Data\");\r\n\t\t\t\tzipFile.Dispose();\r\n\t\t\t\tFile.Delete(tempFileName);\r\n\t\t\t\t_completed = true;\r\n\t\t\t}\r\n\t\t\tcatch (Exception ex)\r\n\t\t\t{\r\n\t\t\t\tFile.WriteAllText(\"exception.txt\", ex.ToString());\r\n\t\t\t\t_downloadError = true;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/UI/Screens/DoRaceScreen.cs",
    "content": "using Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework.Input;\r\nusing GameEngine;\r\nusing OpenNFS1.Parsers.Track;\r\nusing OpenNFS1.Physics;\r\nusing OpenNFS1.UI;\r\nusing OpenNFS1.UI.Screens;\r\nusing OpenNFS1.Vehicles;\r\nusing OpenNFS1.Vehicles.AI;\r\n\r\n\r\nnamespace OpenNFS1\r\n{\r\n\tclass DoRaceScreen : IGameScreen\r\n\t{\r\n\t\tTrack _track;\r\n\t\tDrivableVehicle _car;\r\n\t\tRace _race;\r\n\t\tRaceUI _raceUI;\r\n\t\tPlayerUI _playerUI;\r\n\t\tViewport _raceViewport, _uiViewport;\r\n\t\tPlayerDriver _playerDriver;\r\n\r\n\t\tpublic DoRaceScreen(Track track)\r\n\t\t{\r\n\t\t\t_track = track;\r\n\t\t\t_car = new DrivableVehicle(GameConfig.SelectedVehicle);\r\n\r\n\t\t\t_playerDriver = new PlayerDriver(_car);\r\n\t\t\t_car.AudioEnabled = true;\r\n\r\n\t\t\t_race = new Race(_track.IsOpenRoad ? 1 : 3, _track, _playerDriver);\r\n\t\t\tfor (int i = 0; i < 10; i++)\r\n\t\t\t{\r\n\t\t\t\tint j = Engine.Instance.Random.Next(VehicleDescription.Descriptions.Count);\r\n\t\t\t\t_race.AddDriver(new RacingAIDriver(VehicleDescription.Descriptions[j]));\r\n\t\t\t}\r\n\t\t\t\t//_race.AddDriver(new AIDriver(VehicleDescription.Descriptions.Find(a => a.Name == \"Viper\")));\r\n\t\t\t\t//_race.AddDriver(new AIDriver(VehicleDescription.Descriptions.Find(a => a.Name == \"Viper\")));\r\n\t\t\t\t//_race.AddDriver(new AIDriver(VehicleDescription.Descriptions.Find(a => a.Name == \"Viper\")));\r\n\t\t\t\t//_race.AddDriver(new AIDriver(VehicleDescription.Descriptions.Find(a => a.Name == \"Viper\")));\r\n\t\t\t\t_playerUI = new PlayerUI(_car);\r\n\t\t\t/*\r\n\t\t\td = new AIDriver(VehicleDescription.Descriptions.Find(a => a.Name == \"911\"));\r\n\t\t\t_aiDrivers.Add(d);\r\n\t\t\t_track.AddDriver(d);\r\n\t\t\td = new AIDriver(VehicleDescription.Descriptions.Find(a => a.Name == \"Viper\"));\r\n\t\t\t_aiDrivers.Add(d);\r\n\t\t\t_track.AddDriver(d);\r\n\t\t\td = new AIDriver(VehicleDescription.Descriptions.Find(a => a.Name == \"Diablo\"));\r\n\t\t\t_aiDrivers.Add(d);\r\n\t\t\t_track.AddDriver(d);\r\n\t\t\td = new AIDriver(VehicleDescription.Descriptions.Find(a => a.Name == \"F512\"));\r\n\t\t\t_aiDrivers.Add(d);\r\n\t\t\t_track.AddDriver(d);\r\n\t\t\td = new AIDriver(VehicleDescription.Descriptions.Find(a => a.Name == \"ZR1\"));\r\n\t\t\t_aiDrivers.Add(d);\r\n\t\t\t_track.AddDriver(d);\r\n\t\t\td = new AIDriver(VehicleDescription.Descriptions.Find(a => a.Name == \"NSX\"));\r\n\t\t\t_aiDrivers.Add(d);\r\n\t\t\t_track.AddDriver(d);\r\n\t\t\t*/\r\n\t\t\t\t\t\t\r\n\t\t\t_raceUI = new RaceUI(_race);\r\n\t\t\t_race.StartCountdown();\r\n\r\n\t\t\t_raceViewport = new Viewport(0, 0, 640, 400);\r\n\t\t\t_uiViewport = new Viewport(0, 0, 640, 480);\r\n\t\t}\r\n\r\n\t\t#region IDrawableObject Members\r\n\r\n\t\tpublic void Update(GameTime gameTime)\r\n\t\t{\r\n\t\t\tEngine.Instance.Device.Viewport = _raceViewport;\r\n\r\n\t\t\t_race.Update();\r\n\t\t\tTyreSmokeParticleSystem.Instance.Update();\r\n\t\t\t\r\n\t\t\t_playerUI.Update(gameTime);\r\n\r\n\t\t\tif (_race.Finished)\r\n\t\t\t{\r\n\t\t\t\t_car.AudioEnabled = false;\r\n\t\t\t\tEngine.Instance.Screen = new RaceFinishedScreen(_race, _track);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (UIController.Pause)\r\n\t\t\t{\r\n\t\t\t\tPause();\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (Engine.Instance.Input.WasPressed(Keys.R))\r\n\t\t\t{\r\n\t\t\t\t_car.Reset();\r\n\t\t\t}\r\n\r\n\t\t\tTyreSmokeParticleSystem.Instance.SetCamera(Engine.Instance.Camera);\r\n\t\t\tEngine.Instance.Camera.Update(gameTime);\r\n\t\t}\r\n\r\n\t\tpublic void Pause()\r\n\t\t{\r\n\t\t\t_car.AudioEnabled = false;\r\n\t\t\tEngine.Instance.Screen = new RacePausedScreen(this);\r\n\t\t}\r\n\r\n\t\tpublic void Resume()\r\n\t\t{\r\n\t\t\t_car.AudioEnabled = true;\r\n\t\t}\r\n\r\n\t\tpublic void Draw()\r\n\t\t{\r\n\t\t\tEngine.Instance.Device.Viewport = _raceViewport;\r\n\r\n\t\t\t_race.Render(_playerUI.ShouldRenderCar);\r\n\t\t\tTyreSmokeParticleSystem.Instance.Render();\r\n\r\n\t\t\tEngine.Instance.Device.Viewport = _uiViewport;\r\n\t\t\t_raceUI.Render();\r\n\t\t\t_playerUI.Render();\r\n\r\n\t\t\tEngine.Instance.Device.Viewport = _raceViewport;\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/UI/Screens/HomeScreen.cs",
    "content": "﻿using System.Collections.Generic;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Audio;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework.Input;\r\nusing GameEngine;\r\nusing OpenNFS1.Parsers;\r\nusing OpenNFS1.Parsers.Audio;\r\nusing OpenNFS1.Vehicles;\r\n\r\nnamespace OpenNFS1.UI.Screens\r\n{\r\n\tclass VehicleUIControl\r\n\t{\r\n\t\tpublic BitmapEntry Bitmap { get; private set; }\r\n\t\tpublic VehicleDescription Descriptor { get; private set; }\r\n\r\n\t\tpublic VehicleUIControl(VehicleDescription desc)\r\n\t\t{\r\n\t\t\tDescriptor = desc;\r\n\t\t\tQfsFile qfs = new QfsFile(@\"Frontend\\Art\\Control\\\" + desc.UIImageFile);\r\n\t\t\tBitmap = qfs.Content.Header.Bitmaps.Find(a => a.Id == \"0000\");\r\n\t\t}\r\n\t}\r\n\r\n\tclass TrackUIControl\r\n\t{\r\n\t\tpublic BitmapEntry Bitmap { get; private set; }\r\n\t\tpublic TrackDescription Descriptor { get; private set; }\r\n\r\n\t\tpublic TrackUIControl(TrackDescription desc)\r\n\t\t{\r\n\t\t\tDescriptor = desc;\r\n\t\t\tQfsFile qfs = new QfsFile(@\"Frontend\\Art\\Control\\\" + desc.ImageFile);\r\n\t\t\tBitmap = qfs.Content.Header.Bitmaps.Find(a => a.Id == \"0000\");\r\n\t\t}\r\n\t}\r\n\r\n\tenum SelectedControlType\r\n\t{\r\n\t\tVehicle,\r\n\t\tTrack\r\n\t}\r\n\r\n\tclass HomeScreen : IGameScreen\r\n\t{\r\n\t\tBitmapEntry _background, _vehicleSelection, _trackSelection, _raceButtonSelection;\r\n\r\n\t\tconst int VehicleSelected = 0;\r\n\t\tconst int TrackSelected = 1;\r\n\t\tconst int RaceButtonSelected = 2;\r\n\r\n\t\tList<VehicleUIControl> _vehicles = new List<VehicleUIControl>();\r\n\t\tList<TrackUIControl> _track = new List<TrackUIControl>();\r\n\t\tint _currentVehicle = 2;\r\n\t\tint _currentTrack = 4;\r\n\t\tint _selectedControl = RaceButtonSelected;\r\n\r\n\t\tpublic HomeScreen()\r\n\t\t\t: base()\r\n\t\t{\r\n\t\t\tQfsFile qfs = new QfsFile(@\"FRONTEND\\ART\\control\\central.qfs\");\r\n\t\t\t_background = qfs.Content.Header.FindByName(\"bgnd\");\r\n\t\t\t_vehicleSelection = qfs.Content.Header.FindByName(\"Tlb1\");\r\n\t\t\t_trackSelection = qfs.Content.Header.FindByName(\"Brb4\");\r\n\t\t\t_raceButtonSelection = qfs.Content.Header.FindByName(\"Ra1l\");\r\n\r\n\t\t\tforeach (var vehicle in VehicleDescription.Descriptions)\r\n\t\t\t{\r\n\t\t\t\t_vehicles.Add(new VehicleUIControl(vehicle));\r\n\t\t\t}\r\n\r\n\t\t\tforeach (var track in TrackDescription.Descriptions)\r\n\t\t\t{\r\n\t\t\t\tif (!track.HideFromMenu)\r\n\t\t\t\t{\r\n\t\t\t\t\t_track.Add(new TrackUIControl(track));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (GameConfig.SelectedTrackDescription != null)\r\n\t\t\t\t_currentTrack = _track.FindIndex(a => a.Descriptor == GameConfig.SelectedTrackDescription);\r\n\t\t\tif (GameConfig.SelectedVehicle != null)\r\n\t\t\t\t_currentVehicle = _vehicles.FindIndex(a => a.Descriptor == GameConfig.SelectedVehicle);\r\n\r\n\t\t\tif (_currentTrack == -1) _currentTrack = 0;\r\n\t\t}\r\n\r\n\t\tpublic void Update(GameTime gameTime)\r\n\t\t{\r\n\t\t\tswitch (_selectedControl)\r\n\t\t\t{\r\n\t\t\t\tcase VehicleSelected:\r\n\t\t\t\t\tif (Engine.Instance.Input.WasPressed(Keys.Left))\r\n\t\t\t\t\t\t_currentVehicle--; if (_currentVehicle < 0) _currentVehicle = _vehicles.Count-1;\r\n\t\t\t\t\telse if (Engine.Instance.Input.WasPressed(Keys.Right))\r\n\t\t\t\t\t\t_currentVehicle++; _currentVehicle %= _vehicles.Count;\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\tcase TrackSelected:\r\n\t\t\t\t\tif (Engine.Instance.Input.WasPressed(Keys.Left))\r\n\t\t\t\t\t\t_currentTrack--; if (_currentTrack < 0) _currentTrack = _track.Count - 1;\r\n\t\t\t\t\telse if (Engine.Instance.Input.WasPressed(Keys.Right))\r\n\t\t\t\t\t\t_currentTrack = (_currentTrack + 1) % _track.Count;\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tif (Engine.Instance.Input.WasPressed(Keys.Down))\r\n\t\t\t{\r\n\t\t\t\t_selectedControl++; _selectedControl %= 3;\r\n\t\t\t}\r\n\t\t\telse if (Engine.Instance.Input.WasPressed(Keys.Up))\r\n\t\t\t{\r\n\t\t\t\t_selectedControl--; if (_selectedControl < 0) _selectedControl = 2;\r\n\t\t\t}\r\n\t\t\telse if (Engine.Instance.Input.WasPressed(Keys.Enter) && _selectedControl == RaceButtonSelected)\r\n\t\t\t{\r\n\t\t\t\tGameConfig.SelectedVehicle = _vehicles[_currentVehicle].Descriptor;\r\n\t\t\t\tGameConfig.SelectedTrackDescription = _track[_currentTrack].Descriptor;\r\n\t\t\t\tEngine.Instance.Screen = new RaceOptionsScreen();\r\n\t\t\t}\r\n\t\t\telse if (Engine.Instance.Input.WasPressed(Keys.Escape))\r\n\t\t\t{\r\n\t\t\t\tEngine.Instance.Game.Exit();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void Draw()\r\n\t\t{\r\n\t\t\tEngine.Instance.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);\r\n\t\t\tEngine.Instance.SpriteBatch.Draw(_background.Texture, Vector2.Zero, Color.White);\r\n\t\t\tEngine.Instance.SpriteBatch.Draw(_vehicles[_currentVehicle].Bitmap.Texture, _vehicles[_currentVehicle].Bitmap.GetDisplayAt(), Color.White);\r\n\t\t\tEngine.Instance.SpriteBatch.Draw(_track[_currentTrack].Bitmap.Texture, _track[_currentTrack].Bitmap.GetDisplayAt(), Color.White);\r\n\r\n\t\t\tswitch (_selectedControl)\r\n\t\t\t{\r\n\t\t\t\tcase VehicleSelected:\r\n\t\t\t\t\tEngine.Instance.SpriteBatch.Draw(_vehicleSelection.Texture, _vehicleSelection.GetDisplayAt(), Color.White);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase TrackSelected:\r\n\t\t\t\t\tEngine.Instance.SpriteBatch.Draw(_trackSelection.Texture, _trackSelection.GetDisplayAt(), Color.White);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase RaceButtonSelected:\r\n\t\t\t\t\tEngine.Instance.SpriteBatch.Draw(_raceButtonSelection.Texture, _raceButtonSelection.GetDisplayAt(), Color.White);\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tEngine.Instance.SpriteBatch.End();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/UI/Screens/LoadRaceScreen.cs",
    "content": "﻿using System;\r\nusing System.Threading;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing GameEngine;\r\nusing OpenNFS1.Loaders;\r\nusing OpenNFS1.Parsers;\r\nusing OpenNFS1.Parsers.Track;\r\nusing OpenNFS1.UI.Screens;\r\n\r\n\r\nnamespace OpenNFS1\r\n{\r\n    class LoadRaceScreen : BaseUIScreen, IGameScreen\r\n    {\r\n\t\tfloat _loadingTime;\r\n\t\tint _nbrDots = 0;\r\n\t\tTriFile _tri;\r\n\t\tTrackAssembler _assembler;\r\n\r\n        public LoadRaceScreen() : base()\r\n        {\r\n\t\t\tif (GameConfig.CurrentTrack != null)\r\n\t\t\t{\r\n\t\t\t\tGameConfig.CurrentTrack.Dispose();\r\n\t\t\t\tGameConfig.CurrentTrack = null;\r\n\t\t\t}\r\n            new Thread(LoadTrack).Start();\r\n        }\r\n\r\n        #region IDrawableObject Members\r\n\r\n        public void Update(GameTime gameTime)\r\n        {\r\n\t\t\tif (GameConfig.CurrentTrack != null)\r\n            {\r\n\t\t\t\tEngine.Instance.Screen = new DoRaceScreen(GameConfig.CurrentTrack);\r\n            }\r\n\t\t\t_loadingTime += Engine.Instance.FrameTime;\r\n\t\t\tif (_loadingTime > 0.1f)\r\n\t\t\t{\r\n\t\t\t\t_nbrDots++;\r\n\t\t\t\t_loadingTime = 0;\r\n\t\t\t}\r\n        }\r\n\r\n\t\tpublic override void Draw()\r\n\t\t{\r\n\t\t\tbase.Draw();\r\n\t\t\tWriteLine(GameConfig.SelectedTrackDescription.Name, Color.White, 20, 30, TitleSize);\r\n\t\t\tWriteLine(String.Format(\"Loading {0}\", new string('.', _nbrDots)), TextColor, 50, 30, TextSize);\r\n\t\t\t\r\n\t\t\tWriteLine(\"Reading track file \" + GameConfig.SelectedTrackDescription.FileName, TextColor, 20, 30, TextSize);\r\n\t\t\tif (_assembler != null)\r\n\t\t\t\tWriteLine(_assembler.ProgressMessage, TextColor, 20, 30, TextSize);\r\n\t\t\t\r\n\t\t\tEngine.Instance.SpriteBatch.End();\r\n\t\t}\r\n\r\n        #endregion\r\n\r\n        private void LoadTrack()\r\n        {\r\n\t\t\t_tri = new TriFile(GameConfig.SelectedTrackDescription.FileName);\r\n\t\t\t_assembler = new TrackAssembler();\r\n\t\t\tGameConfig.CurrentTrack = _assembler.Assemble(_tri);\r\n\t\t\tGameConfig.CurrentTrack.Description = GameConfig.SelectedTrackDescription;\r\n            \r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/UI/Screens/OpenNFS1SplashScreen.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Reflection;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Input;\r\nusing GameEngine;\r\n\r\nnamespace OpenNFS1.UI.Screens\r\n{\r\n\tclass OpenNFS1SplashScreen : BaseUIScreen, IGameScreen\r\n\t{\r\n\t\tfloat _elapsedTime;\r\n\t\tpublic void Update(GameTime gameTime)\r\n\t\t{\r\n\t\t\t_elapsedTime += Engine.Instance.FrameTime;\r\n\t\t\tif (Engine.Instance.Input.WasPressed(Keys.Enter))\r\n\t\t\t{\r\n\t\t\t\tif (!Directory.Exists(GameConfig.CdDataPath) || Directory.GetDirectories(GameConfig.CdDataPath).Length == 0)\r\n\t\t\t\t\tEngine.Instance.Screen = new ChooseDataDownloadScreen();\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tEngine.Instance.Screen = new HomeScreen();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic override void Draw()\r\n\t\t{\r\n\t\t\tbase.Draw();\r\n\r\n\t\t\tvar version = Assembly.GetExecutingAssembly().GetName().Version;\r\n\r\n\t\t\tWriteLine(\"OpenNFS1 v\" + version.ToString(2), Color.Red, 0, 30, TextSize);\r\n\t\t\tWriteLine(\r\n\t\t\t\t\"OpenNFS1 is a remake of the original EA Need for Speed 1.\\r\\n\\r\\n\" + \r\n\t\t\t\t\"OpenNFS1 code was written from scratch without reverse\\r\\n\" + \r\n\t\t\t\t\"engineering the NFS executable, and it uses the original data\\r\\n\" + \r\n\t\t\t\t\"files that were on the CD back in 1995!\", TextColor, 50, 30, TextSize);\r\n\r\n\t\t\tWriteLine(\"By Jeff H - www.1amstudios.com\", TextColor, 40, 30, TextSize);\r\n\r\n\t\t\tWriteLine(\r\n\t\t\t\t\"Need for Speed 1 was produced by Pioneer Productions\\r\\nand EA Seattle in 1995.\\r\\n\" + \r\n\t\t\t\t\"1amStudios and OpenNFS1 are not affiliated in any way\\r\\nwith Pioneer Productions or EA Seattle.\", Color.LightGray, 190, 30, TextSize);\r\n\r\n\t\t\tEngine.Instance.SpriteBatch.End();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/UI/Screens/RaceFinishedScreen.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing OpenNFS1.Parsers.Track;\r\n\r\nnamespace OpenNFS1.UI.Screens\r\n{\r\n    class RaceFinishedScreen : BaseUIScreen, IGameScreen\r\n    {\r\n        Race _race;\r\n        Texture2D _background;\r\n        Track _raceTrack;\r\n\r\n        int _selectedOption = 0;\r\n\r\n        public RaceFinishedScreen(Race race, Track raceTrack)\r\n            : base()\r\n        {\r\n            _race = race;\r\n            _raceTrack = raceTrack;\r\n            _background = ScreenEffects.TakeScreenshot();\r\n            GC.Collect(); //force some memory freeness here.\r\n        }\r\n\r\n\r\n        #region IDrawableObject Members\r\n\r\n        public void Update(GameTime gameTime)\r\n        {\r\n            if (UIController.Back)\r\n            {\r\n                Engine.Instance.Screen = new HomeScreen();\r\n            }\r\n\r\n            if (UIController.Up && _selectedOption > 0)\r\n                _selectedOption--;\r\n            else if (UIController.Down && _selectedOption < 1)\r\n                _selectedOption++;\r\n\r\n            if (UIController.Ok)\r\n            {\r\n                if (_selectedOption == 1)\r\n                    Engine.Instance.Screen = new HomeScreen();\r\n                else\r\n                {\r\n\t\t\t\t\tif (!GameConfig.SelectedTrackDescription.IsOpenRoad)\r\n                        Engine.Instance.Screen = new DoRaceScreen(_raceTrack);\r\n                    else\r\n                    {\r\n                        GameConfig.SelectedTrackDescription = TrackDescription.GetNextOpenRoadStage(GameConfig.SelectedTrackDescription);\r\n                        if (GameConfig.SelectedTrackDescription == null)\r\n                            Engine.Instance.Screen = new HomeScreen();\r\n                        else\r\n                            Engine.Instance.Screen = new LoadRaceScreen();\r\n                    }\r\n                }\r\n            }\r\n        }\r\n\r\n\t\tpublic override void Draw()\r\n\t\t{\r\n\t\t\tbase.Draw();\r\n\r\n            Engine.Instance.SpriteBatch.Draw(_background, Vector2.Zero, new Color(255, 255, 255, 100));\r\n\r\n\t\t\tif (GameConfig.SelectedTrackDescription.IsOpenRoad)\r\n                DrawOpenRoadResult();\r\n            else\r\n                DrawCircuitResult();\r\n\r\n            Engine.Instance.SpriteBatch.End();\r\n        }\r\n\r\n        #endregion\r\n\r\n        private void DrawCircuitResult()\r\n        {\r\n\t\t\tWriteLine(GameConfig.SelectedTrackDescription.Name + \"- Race completed\", Color.Gray, 20, 30, TitleSize);\r\n            \r\n            int totalSeconds = 0;\r\n            for (int i = 0; i < _race.PlayerStats.LapTimes.Count; i++)\r\n            {\r\n\t\t\t\tWriteLine(\"Lap \" + (i + 1) + \": \" + TimeSpan.FromSeconds(_race.PlayerStats.LapTimes[i]).ToString());\r\n                totalSeconds += _race.PlayerStats.LapTimes[i];\r\n            }\r\n\r\n\t\t\tWriteLine(\"Total time: \" + TimeSpan.FromSeconds(totalSeconds));\r\n\t\t\t\r\n\t\t\tWriteLine(\" Race again\", _selectedOption == 0, 60, 30, SectionSize);\r\n\t\t\tWriteLine(\" Main menu\", _selectedOption == 1, 30, 30, SectionSize);\r\n        }\r\n\r\n        private void DrawOpenRoadResult()\r\n        {\r\n\t\t\tWriteLine(GameConfig.SelectedTrackDescription.Name + \" - Stage completed\", Color.Gray, 20, 30, TitleSize);\r\n            \r\n\t\t\tWriteLine(\"Time: \" + TimeSpan.FromSeconds(_race.PlayerStats.LapTimes[0]));\r\n\r\n            if (TrackDescription.GetNextOpenRoadStage(GameConfig.SelectedTrackDescription) != null)\r\n\t\t\t{\r\n\t\t\t\tWriteLine(\"Continue to next stage\", _selectedOption == 0, 60, 30, SectionSize);\r\n            }\r\n\r\n\t\t\tWriteLine(\" Main menu\", _selectedOption == 1, 30, 30, SectionSize);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/UI/Screens/RaceOptionsScreen.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework.Input;\r\nusing GameEngine;\r\nusing OpenNFS1.Parsers;\r\n\r\nnamespace OpenNFS1.UI.Screens\r\n{\r\n\tclass RaceOptionsScreen: BaseUIScreen, IGameScreen\r\n\t{\r\n\t\tint _selectedOption, _gearboxOption, _timeOfDayOption;\r\n\r\n\t\tpublic RaceOptionsScreen() : base()\r\n\t\t{\r\n\t\t\t_selectedOption = 2;\r\n\t\t\t_gearboxOption = GameConfig.ManualGearbox ? 1 : 0;\r\n\t\t\t_timeOfDayOption = GameConfig.AlternativeTimeOfDay ? 1 : 0;\r\n\t\t}\r\n\r\n\t\tpublic void Update(GameTime gameTime)\r\n\t\t{\r\n\t\t\tif (Engine.Instance.Input.WasPressed(Keys.Down))\r\n\t\t\t{\r\n\t\t\t\t_selectedOption = Math.Min(_selectedOption + 1, 2);\r\n\t\t\t}\r\n\t\t\telse if (Engine.Instance.Input.WasPressed(Keys.Up))\r\n\t\t\t{\r\n\t\t\t\t_selectedOption = Math.Max(_selectedOption - 1, 0);\r\n\t\t\t}\r\n\t\t\telse if (Engine.Instance.Input.WasPressed(Keys.Left))\r\n\t\t\t{\r\n\t\t\t\tif (_selectedOption == 0)\r\n\t\t\t\t\t_gearboxOption = 0;\r\n\t\t\t\telse\r\n\t\t\t\t\t_timeOfDayOption = 0;\r\n\t\t\t}\r\n\t\t\telse if (Engine.Instance.Input.WasPressed(Keys.Right))\r\n\t\t\t{\r\n\t\t\t\tif (_selectedOption == 0)\r\n\t\t\t\t\t_gearboxOption = 1;\r\n\t\t\t\telse\r\n\t\t\t\t\t_timeOfDayOption = 1;\r\n\t\t\t}\r\n\t\t\telse if (Engine.Instance.Input.WasPressed(Keys.Enter))\r\n\t\t\t{\r\n\t\t\t\tif (_selectedOption == 2)\r\n\t\t\t\t{\r\n\t\t\t\t\tGameConfig.ManualGearbox = _gearboxOption == 1;\r\n\t\t\t\t\tGameConfig.AlternativeTimeOfDay = _timeOfDayOption == 1;\r\n\t\t\t\t\tEngine.Instance.Screen = new LoadRaceScreen();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic override void Draw()\r\n\t\t{\r\n\t\t\tbase.Draw();\r\n\t\t\t\r\n\t\t\tWriteLine(\"Race settings\", Color.White, 20, 30, TitleSize);\r\n\r\n\t\t\tWriteLine(\"Gearbox\", _selectedOption == 0, 60, 30, SectionSize);\r\n\t\t\tWriteLine(\"Auto\", _gearboxOption == 0, 40, 40, TextSize);\r\n\t\t\tWriteLine(\"Manual\", _gearboxOption == 1, 0, 120, TextSize);\r\n\t\t\t\r\n\t\t\tWriteLine(\"Time of day\", _selectedOption == 1, 40, 30, SectionSize);\r\n\t\t\tWriteLine(\"Midday\", _timeOfDayOption == 0, 40, 40, TextSize);\r\n\t\t\tif (GameConfig.SelectedTrackDescription.AlternativeTimeOfDay != null)\r\n\t\t\t{\r\n\t\t\t\tWriteLine(GameConfig.SelectedTrackDescription.AlternativeTimeOfDay, _timeOfDayOption == 1, 0, 120, TextSize);\r\n\t\t\t}\r\n\r\n\t\t\tWriteLine(\"Go!\", _selectedOption == 2, 40, 30, SectionSize);\r\n\r\n\t\t\tEngine.Instance.SpriteBatch.End();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/UI/Screens/RacePausedScreen.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing OpenNFS1.Physics;\r\n\r\nnamespace OpenNFS1.UI.Screens\r\n{\r\n    class RacePausedScreen : BaseUIScreen, IGameScreen\r\n    {\r\n        DoRaceScreen _currentRace;\r\n        int _selectedOption = 0;\r\n\r\n        public RacePausedScreen(DoRaceScreen currentRace)\r\n            : base()\r\n        {\r\n            _currentRace = currentRace;\r\n        }\r\n\r\n        #region IDrawableObject Members\r\n\r\n        public void Update(GameTime gameTime)\r\n        {\r\n\t\t\t//Engine.Instance.Device.Viewport = FullViewport;\r\n\r\n            if (UIController.Up && _selectedOption > 0)\r\n                _selectedOption--;\r\n            else if (UIController.Down && _selectedOption < 1)\r\n                _selectedOption++;\r\n\r\n            if (UIController.Back)\r\n            {\r\n                Engine.Instance.Screen = _currentRace;\r\n                return;\r\n            }\r\n\r\n            if (UIController.Ok)\r\n            {\r\n                if (_selectedOption == 0)\r\n                {\r\n                    Engine.Instance.Screen = _currentRace;\r\n\t\t\t\t\t_currentRace.Resume();\r\n                }\r\n                else\r\n                {\r\n                    Engine.Instance.Screen = new HomeScreen();\r\n                }\r\n            }\r\n        }\r\n\r\n\t\tpublic override void Draw()\r\n\t\t{\r\n\t\t\tbase.Draw();\r\n\t\t\t\r\n\t\t\tWriteLine(\"Race paused\", Color.White, 20, 30, TitleSize);\r\n\t\t\tWriteLine(\"Continue\", _selectedOption == 0, 60, 30, SectionSize);\r\n\t\t\tWriteLine(\"Main menu\", _selectedOption == 1, 40, 30, SectionSize);\r\n\r\n            Engine.Instance.SpriteBatch.End();\r\n        }\r\n\r\n        #endregion\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/UIController.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework.Input;\r\n\r\nnamespace OpenNFS1\r\n{\r\n    static class UIController\r\n    {\r\n\r\n        public static bool Left\r\n        {\r\n            get\r\n            {\r\n                if (Engine.Instance.Input.WasPressed(Keys.Left))\r\n                    return true;\r\n                if (Engine.Instance.Input.WasPressed(Buttons.DPadLeft))\r\n                    return true;\r\n                return false;\r\n            }\r\n        }\r\n\r\n        public static bool Right\r\n        {\r\n            get\r\n            {\r\n                if (Engine.Instance.Input.WasPressed(Keys.Right))\r\n                    return true;\r\n                if (Engine.Instance.Input.WasPressed(Buttons.DPadRight))\r\n                    return true;\r\n                return false;\r\n            }\r\n        }\r\n\r\n        public static bool Up\r\n        {\r\n            get\r\n            {\r\n                if (Engine.Instance.Input.WasPressed(Keys.Up))\r\n                    return true;\r\n                if (Engine.Instance.Input.WasPressed(Buttons.DPadUp))\r\n                    return true;\r\n                return false;\r\n            }\r\n        }\r\n\r\n        public static bool Down\r\n        {\r\n            get\r\n            {\r\n                if (Engine.Instance.Input.WasPressed(Keys.Down))\r\n                    return true;\r\n                if (Engine.Instance.Input.WasPressed(Buttons.DPadDown))\r\n                    return true;\r\n                return false;\r\n            }\r\n        }\r\n\r\n        public static bool Back\r\n        {\r\n            get\r\n            {\r\n                if (Engine.Instance.Input.WasPressed(Keys.Escape))\r\n                    return true;\r\n                if (Engine.Instance.Input.WasPressed(Buttons.B))\r\n                    return true;\r\n                return false;\r\n            }\r\n        }\r\n\r\n        public static bool Ok\r\n        {\r\n            get\r\n            {\r\n                if (Engine.Instance.Input.WasPressed(Keys.Enter))\r\n                    return true;\r\n                if (Engine.Instance.Input.WasPressed(Buttons.A))\r\n                    return true;\r\n                return false;\r\n            }\r\n        }\r\n\r\n        public static bool Pause\r\n        {\r\n            get\r\n            {\r\n                if (Engine.Instance.Input.WasPressed(Keys.P) || Engine.Instance.Input.WasPressed(Keys.Escape))\r\n                    return true;\r\n                if (Engine.Instance.Input.WasPressed(Buttons.Start))\r\n                    return true;\r\n                return false;\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/VehicleController.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework.Input;\r\n\r\nnamespace OpenNFS1\r\n{\r\n    static class VehicleController\r\n    {\r\n\r\n        public static bool ForceBrake { get; set; }\r\n\r\n        public static float Acceleration\r\n        {\r\n            get\r\n            {\r\n                if (ForceBrake)\r\n                    return 0;\r\n\r\n                if (Engine.Instance.Input.IsKeyDown(Keys.Up))\r\n                    return 1.0f;\r\n\r\n                return Engine.Instance.Input.GamePadState.Triggers.Right;\r\n            }\r\n        }\r\n\r\n        public static float Brake\r\n        {\r\n            get\r\n            {\r\n                if (ForceBrake)\r\n                    return 1.0f;\r\n\r\n                if (Engine.Instance.Input.IsKeyDown(Keys.Down))\r\n                    return 1.0f;\r\n\r\n                return Engine.Instance.Input.GamePadState.Triggers.Left;\r\n            }\r\n        }\r\n\r\n        public static float Turn\r\n        {\r\n            get\r\n            {\r\n                if (Engine.Instance.Input.IsKeyDown(Keys.Left))\r\n                    return -1;\r\n                else if (Engine.Instance.Input.IsKeyDown(Keys.Right))\r\n                    return 1;\r\n\r\n                return Engine.Instance.Input.GamePadState.ThumbSticks.Left.X;\r\n            }\r\n        }\r\n\r\n        public static bool ChangeView\r\n        {\r\n            get\r\n            {\r\n                if (Engine.Instance.Input.WasPressed(Keys.C))\r\n                    return true;\r\n                if (Engine.Instance.Input.WasPressed(Buttons.RightShoulder))\r\n                    return true;\r\n                return false;\r\n            }\r\n        }\r\n\r\n        public static bool GearUp\r\n        {\r\n            get\r\n            {\r\n                if (Engine.Instance.Input.WasPressed(Keys.A))\r\n                    return true;\r\n                if (Engine.Instance.Input.WasPressed(Buttons.B))\r\n                    return true;\r\n                return false;\r\n            }\r\n        }\r\n\r\n        public static bool GearDown\r\n        {\r\n            get\r\n            {\r\n                if (Engine.Instance.Input.WasPressed(Keys.Z))\r\n                    return true;\r\n                if (Engine.Instance.Input.WasPressed(Buttons.X))\r\n                    return true;\r\n                return false;\r\n            }\r\n        }\r\n\r\n\t\tpublic static bool Handbrake\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\tif (Engine.Instance.Input.IsKeyDown(Keys.Space))\r\n\t\t\t\t\treturn true;\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Vehicles/AI/AIDriver.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing OpenNFS1.Parsers.Track;\r\nusing OpenNFS1.Physics;\r\nusing OpenNFS1.Tracks;\r\n\r\nnamespace OpenNFS1.Vehicles.AI\r\n{\r\n\r\n\tabstract class AIDriver : IDriver\r\n\t{\r\n\t\tpublic int VirtualLane { get; set; }\r\n\t\tpublic Vehicle Vehicle { get; protected set; }\r\n        public Boolean AtEndOfTrack { get; set; }\r\n\r\n\t\tpublic const int MaxVirtualLanes = 4;\r\n\r\n\t\tpublic virtual Vector3 GetNextTarget()\r\n\t\t{\r\n\t\t\treturn Vector3.Lerp(Vehicle.CurrentNode.Next.Next.GetLeftVerge2(), Vehicle.CurrentNode.Next.Next.GetRightVerge2(), (float)VirtualLane / (MaxVirtualLanes));\r\n\t\t}\r\n\t\t\t\t\r\n\t\tprotected virtual void FollowTrack()\r\n\t\t{\r\n\t\t\tvar target = GetNextTarget();\r\n\t\t\tif (GameConfig.DrawDebugInfo)\r\n\t\t\t{\r\n\t\t\t\tEngine.Instance.GraphicsUtils.AddCube(Matrix.CreateTranslation(target), Color.Yellow);\r\n\t\t\t}\r\n\t\t\tfloat angle = Utility.GetSignedAngleBetweenVectors(Vehicle.Direction, target - Vehicle.Position, true);\r\n\t\t\tif (angle < -0.1f)\r\n\t\t\t{\r\n\t\t\t\tVehicle.SteeringInput = 0.5f;\r\n\t\t\t}\r\n\t\t\telse if (angle > 0.1f)\r\n\t\t\t{\r\n\t\t\t\tVehicle.SteeringInput = -0.5f;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tVehicle.SteeringInput = 0;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic abstract void Update(List<IDriver> otherDrivers);\r\n\r\n\t}\r\n\r\n\tclass RacingAIDriver : AIDriver\r\n\t{\t\t\r\n\t\tprivate float _firstLaneChangeAllowed;  //avoid all racers changing lanes immediately\r\n\t\tprivate DrivableVehicle _vehicle;\r\n        public bool AtEndOfTrack;\r\n\r\n\t\tpublic RacingAIDriver(VehicleDescription vehicleDescriptor)\r\n\t\t{\r\n\t\t\t_vehicle = new DrivableVehicle(vehicleDescriptor);\r\n\t\t\t_vehicle.SteeringSpeed = 6;\r\n\t\t\t_vehicle.AutoDrift = false;\r\n\t\t\tVehicle = _vehicle;\r\n\t\t\t_firstLaneChangeAllowed = Engine.Instance.Random.Next(5, 40);\r\n\t\t}\r\n\r\n\t\tpublic override void Update(List<IDriver> otherDrivers)\r\n\t\t{\r\n\t\t\t_vehicle.ThrottlePedalInput = 0.9f;\r\n\t\t\t_vehicle.BrakePedalInput = 0;\r\n\r\n\t\t\tvar node = _vehicle.CurrentNode;\r\n\t\t\tvar pos = _vehicle.Position;\r\n\r\n\t\t\tif (node.Next == null || node.Next.Next == null)\r\n\t\t\t{\r\n\t\t\t\t_vehicle.ThrottlePedalInput = 0;\r\n\t\t\t\t_vehicle.BrakePedalInput = 1;\r\n                AtEndOfTrack = true;\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tFollowTrack();\r\n\t\t\t\r\n\t\t\tforeach (var otherDriver in otherDrivers)\r\n\t\t\t{\r\n\t\t\t\tif (otherDriver == this) continue;\r\n\t\t\t\t//if (!(otherDriver is AIDriver)) continue;\r\n\t\t\t\t// if I am not in the same lane, ignore\r\n\t\t\t\tif (otherDriver is AIDriver && ((AIDriver)otherDriver).VirtualLane != VirtualLane) continue;\r\n\t\t\t\tif (otherDriver is PlayerDriver) continue;  //ignore player for now\r\n\t\t\t\t\r\n\t\t\t\t// if I am going slower than the other driver, ignore\r\n\t\t\t\tif (Vehicle.Speed < otherDriver.Vehicle.Speed) continue;\r\n\r\n\t\t\t\tvar progressDist = otherDriver.Vehicle.TrackPosition - _vehicle.TrackPosition;\r\n\t\t\t\t// if we are only slightly behind another driver (less than 2 nodes back) then consider them a possible danger\r\n\t\t\t\tif (progressDist > 0 && progressDist < 2f)\r\n\t\t\t\t{\r\n\t\t\t\t\t// pick a new lane if we're far enough from the start of the race.\r\n\t\t\t\t\tif (Vehicle.CurrentNode.Number > _firstLaneChangeAllowed)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (Engine.Instance.Random.Next() % 2 == 0)\r\n\t\t\t\t\t\t\tVirtualLane = Math.Max(0, VirtualLane - 1);\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tVirtualLane = Math.Min(MaxVirtualLanes, VirtualLane + 1);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tVehicle.Speed = Math.Max(0, otherDriver.Vehicle.Speed * 0.8f);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t_vehicle.Update();\r\n\t\t}\t\t\r\n\r\n\t\tpublic void Render()\r\n\t\t{\r\n\t\t\t\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Vehicles/AI/IDriver.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing OpenNFS1.Physics;\r\n\r\nnamespace OpenNFS1.Vehicles.AI\r\n{\r\n\tinterface IDriver\r\n\t{\r\n\t\tVehicle Vehicle { get; }\r\n\t\tvoid Update(List<IDriver> otherDrivers);\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Vehicles/AI/TrafficDriver.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing OpenNFS1.Parsers.Track;\r\nusing OpenNFS1.Physics;\r\nusing OpenNFS1.Tracks;\r\n\r\nnamespace OpenNFS1.Vehicles.AI\r\n{\r\n\tenum TrafficDriverDirection \r\n\t{\r\n\t\tForward,\r\n\t\tBackward\r\n\t}\r\n\r\n\tclass TrafficDriver : AIDriver\r\n\t{\r\n\t\tTrafficDriverDirection _direction;\r\n\r\n\t\tpublic TrafficDriver(string cfmFile, TrafficDriverDirection direction)\r\n\t\t{\r\n\t\t\tVehicle = new Vehicle(cfmFile);\r\n\t\t\t//Vehicle.SteeringSpeed = 7;\r\n\t\t\t_direction = direction;\r\n\t\t\tif (direction == TrafficDriverDirection.Forward)\r\n\t\t\t{\r\n\t\t\t\tVirtualLane = MaxVirtualLanes - 1;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tVirtualLane = 1;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic override Vector3 GetNextTarget()\r\n\t\t{\r\n\t\t\tif (_direction == TrafficDriverDirection.Forward)\r\n\t\t\t\treturn Vector3.Lerp(Vehicle.CurrentNode.Next.Next.GetLeftVerge2(), Vehicle.CurrentNode.Next.Next.GetRightVerge2(), (float)VirtualLane / (MaxVirtualLanes));\r\n\t\t\telse\r\n\t\t\t\treturn Vector3.Lerp(Vehicle.CurrentNode.Prev.Prev.GetLeftVerge2(), Vehicle.CurrentNode.Prev.Prev.GetRightVerge2(), (float)VirtualLane / (MaxVirtualLanes));\r\n\t\t}\r\n\r\n\t\tprotected override void FollowTrack()\r\n\t\t{\r\n\t\t\tfloat angle = Utility.GetSignedAngleBetweenVectors(Vehicle.Direction, GetNextTarget() - Vehicle.Position, true);\r\n\r\n\t\t\t// if we're more than 90 degrees off course, set immediately. (where we've just placed a traffic car on the track)\r\n\t\t\tif (Math.Abs(angle) > MathHelper.PiOver2)\r\n\t\t\t{\r\n\t\t\t\tVector3 newDir = Vector3.Normalize(GetNextTarget() - Vehicle.Position);\r\n\t\t\t\tnewDir.Y = 0;\r\n\t\t\t\tVehicle.Direction = Vector3.Normalize(newDir);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tbase.FollowTrack();\r\n\t\t}\r\n\r\n\t\tpublic override void Update(List<IDriver> otherDrivers)\r\n\t\t{\r\n\t\t\tVehicle.Speed = 50;\r\n\r\n\t\t\tvar node = Vehicle.CurrentNode;\r\n\t\t\t\r\n\t\t\tif (node.Next == null || node.Next.Next == null || node.Prev == null || node.Prev.Prev == null)\r\n\t\t\t{\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (_direction == TrafficDriverDirection.Backward)\r\n\t\t\t{\r\n\r\n\t\t\t}\r\n\t\t\tFollowTrack();\r\n\r\n\t\t\tforeach (var otherDriver in otherDrivers)\r\n\t\t\t{\r\n\t\t\t\tif (otherDriver == this) continue;\r\n\t\t\t\t// if I am not in the same lane, ignore\r\n\t\t\t\tif (otherDriver is AIDriver && ((AIDriver)otherDriver).VirtualLane != VirtualLane) continue;\r\n\t\t\t\t\r\n\t\t\t\t// if we are going slower than the other driver, ignore\r\n\t\t\t\tif (Vehicle.Speed < otherDriver.Vehicle.Speed) continue;\r\n\t\t\t\t\r\n\t\t\t\tvar progressDist = otherDriver.Vehicle.TrackPosition - Vehicle.TrackPosition;\r\n\t\t\t\tif (_direction == TrafficDriverDirection.Backward) progressDist = -progressDist;\r\n\t\t\t\t// if we are only slightly behind another driver (less than 2 nodes back) then consider them a possible danger\r\n\t\t\t\tif (progressDist > 0 && progressDist < 2f)\r\n\t\t\t\t{\r\n\t\t\t\t\t// slow down immediately\r\n\t\t\t\t\tVehicle.Speed = Math.Max(0, otherDriver.Vehicle.Speed * 0.9f);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tVehicle.Update();\r\n\t\t}\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Vehicles/CarMesh.cs",
    "content": "﻿using Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing OpenNFS1.Parsers;\r\nusing GameEngine;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\n\r\nnamespace OpenNFS1.Vehicles\r\n{\r\n\tclass CarMesh : Mesh\r\n\t{\r\n\t\tPolygon _rightRearWheel, _leftRearWheel, _rightFrontWheel, _leftFrontWheel;\r\n\t\tPolygon _leftBrakeLight, _rightBrakeLight;\r\n\t\tTexture2D _wheelTexture, _brakeOnTexture, _brakeOffTexture;\r\n\t\t\r\n\t\tstatic Color BrakeOffColor = new Color(88, 10, 5); //color of brake-off light color...\r\n\t\tstatic Color BrakeOnColor = new Color(158, 110, 6); //color of brake-off light color...\r\n\r\n\t\tpublic CarMesh(MeshChunk meshChunk, BitmapChunk bmpChunk, Color brakeColor)\r\n\t\t\t: base(meshChunk, bmpChunk)\r\n\t\t{\r\n\t\t\tforeach (var poly in _polys)\r\n\t\t\t{\r\n\t\t\t\tswitch (poly.Label)\r\n\t\t\t\t{\r\n\t\t\t\t\tcase \"rt_rear\":\r\n\t\t\t\t\t\t_rightRearWheel = poly;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \"lt_rear\":\r\n\t\t\t\t\t\t_leftRearWheel = poly;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \"rt_frnt\":\r\n\t\t\t\t\t\t_rightFrontWheel = poly;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \"lt_frnt\":\r\n\t\t\t\t\t\t_leftFrontWheel = poly;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \"bkll\":\r\n\t\t\t\t\t\t_leftBrakeLight = poly;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \"bklr\":\r\n\t\t\t\t\t\t_rightBrakeLight = poly;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tvar tyreEntry = bmpChunk.FindByName(\"tyr1\");\r\n\t\t\tif (tyreEntry != null)\r\n\t\t\t\t_wheelTexture = tyreEntry.Texture;\r\n\r\n\t\t\t// This seems like it should be done in a shader but I couldn't get it to work well enough \r\n\t\t\t// (dealing with original paletted colors doesn't work so well in a texture stretched over a polygon)\r\n\t\t\t\r\n\t\t\tvar rsidPoly = _polys.FirstOrDefault(a => a.TextureName == \"rsid\");\r\n\t\t\tif (rsidPoly != null)\r\n\t\t\t{\r\n\t\t\t\t// Generate a new texture for brake lights on.  \r\n\t\t\t\tColor[] pixels = new Color[rsidPoly.Texture.Width * rsidPoly.Texture.Height];\r\n\t\t\t\trsidPoly.Texture.GetData<Color>(pixels);\r\n\t\t\t\tfor (int i = 0; i < pixels.Length; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (pixels[i] == brakeColor)\r\n\t\t\t\t\t\tpixels[i] = BrakeOnColor;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t_brakeOnTexture = new Texture2D(Engine.Instance.Device, rsidPoly.Texture.Width, rsidPoly.Texture.Height);\r\n\t\t\t\t_brakeOnTexture.SetData<Color>(pixels);\r\n\r\n\t\t\t\t// Generate a new texture for brake lights off.\r\n\t\t\t\tfor (int i = 0; i < pixels.Length; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (pixels[i] == BrakeOnColor)\r\n\t\t\t\t\t\tpixels[i] = BrakeOffColor;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t_brakeOffTexture = new Texture2D(Engine.Instance.Device, _leftBrakeLight.Texture.Width, _leftBrakeLight.Texture.Height);\r\n\t\t\t\t_brakeOffTexture.SetData<Color>(pixels);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic Vector3 LeftFrontWheelPos { get { return GetWheelAxlePoint(_leftFrontWheel); } }\r\n\t\tpublic Vector3 RightFrontWheelPos { get { return GetWheelAxlePoint(_rightFrontWheel); } }\r\n\t\tpublic Vector3 LeftRearWheelPos { get { return GetWheelAxlePoint(_leftRearWheel); } }\r\n\t\tpublic Vector3 RightRearWheelPos { get { return GetWheelAxlePoint(_rightRearWheel); } }\r\n\r\n\t\tprivate Vector3 GetWheelAxlePoint(Polygon wheelPoly)\r\n\t\t{\r\n\t\t\tfloat y = (wheelPoly.Vertices.Max(a => a.Y) + wheelPoly.Vertices.Min(a => a.Y)) / 2;\r\n\t\t\tfloat z = (wheelPoly.Vertices.Max(a => a.Z) + wheelPoly.Vertices.Min(a => a.Z)) / 2;\r\n\r\n\t\t\t// X value is always the same\r\n\t\t\treturn new Vector3(wheelPoly.Vertices[0].X, y, z);\r\n\t\t}\r\n\r\n\t\tpublic float RearWheelSize\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\treturn _leftRearWheel.Vertices.Max(a => a.Y) - _leftRearWheel.Vertices.Min(a => a.Y);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic float FrontWheelSize\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\treturn _leftFrontWheel.Vertices.Max(a => a.Y) - _leftFrontWheel.Vertices.Min(a => a.Y);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic Texture2D WheelTexture { get { return _wheelTexture; } }\r\n\r\n\t\tpublic void Render(Effect effect, bool enableBrakeLights)\r\n\t\t{\r\n\t\t\tEngine.Instance.Device.SetVertexBuffer(_vertexBuffer);\r\n\r\n\t\t\teffect.CurrentTechnique.Passes[0].Apply();\r\n\r\n\t\t\tforeach (Polygon poly in _polys)\r\n\t\t\t{\r\n\t\t\t\tif (poly == _rightFrontWheel || poly == _leftFrontWheel || poly == _leftRearWheel || poly == _rightRearWheel)\r\n\t\t\t\t{\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\telse if (_brakeOnTexture != null && poly.TextureName == \"rsid\")\r\n\t\t\t\t{\r\n\t\t\t\t\tEngine.Instance.Device.Textures[0] = enableBrakeLights ? _brakeOnTexture : _brakeOffTexture;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tEngine.Instance.Device.Textures[0] = poly.Texture;\r\n\t\t\t\t}\r\n\t\t\t\tEngine.Instance.Device.DrawPrimitives(PrimitiveType.TriangleList, poly.VertexBufferIndex, poly.VertexCount / 3);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Vehicles/CarModelCache.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing OpenNFS1.Parsers;\r\n\r\nnamespace OpenNFS1.Vehicles\r\n{\r\n    static class CarModelCache\r\n    {\r\n\t\tstatic Dictionary<string, CarMesh> _cache = new Dictionary<string, CarMesh>();\r\n\r\n\t\tpublic static CarMesh GetCfm(string filename)\r\n        {\r\n            if (!_cache.ContainsKey(filename))\r\n            {\r\n\t\t\t\tCfmFile cfm = new CfmFile(filename);\r\n                _cache.Add(filename, cfm.Mesh);\r\n                return cfm.Mesh;\r\n            }\r\n            else\r\n            {\r\n                return _cache[filename];\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Vehicles/PlayerDriver.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing OpenNFS1.Physics;\r\n\r\nnamespace OpenNFS1.Vehicles.AI\r\n{\r\n\tclass PlayerDriver : IDriver\r\n\t{\r\n\t\tDrivableVehicle _vehicle;\r\n\r\n\t\tpublic Vehicle Vehicle { get { return _vehicle; } }\r\n\t\tpublic PlayerDriver(DrivableVehicle vehicle)\r\n\t\t{\r\n\t\t\t_vehicle = vehicle;\r\n\t\t}\r\n\r\n\t\tpublic void Update(List<IDriver> otherDrivers)\r\n\t\t{\r\n\t\t\t_vehicle.ThrottlePedalInput = VehicleController.Acceleration;\r\n\t\t\t_vehicle.BrakePedalInput = VehicleController.Brake;\r\n\t\t\t_vehicle.SteeringInput= VehicleController.Turn;\r\n\t\t\t_vehicle.GearDownInput = VehicleController.GearDown;\r\n\t\t\t_vehicle.GearUpInput = VehicleController.GearUp;\r\n\t\t\t_vehicle.HandbrakeInput = VehicleController.Handbrake;\r\n\t\t\t_vehicle.Update();\r\n\t\t}\r\n\r\n\t\tpublic void Render()\r\n\t\t{\r\n\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Vehicles/Traffic/TrafficController.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing OpenNFS1.Parsers.Track;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing OpenNFS1.Physics;\r\nusing OpenNFS1.Tracks;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing OpenNFS1.Vehicles.AI;\r\n\r\nnamespace OpenNFS1.Vehicles\r\n{\r\n    class TrafficController\r\n    {\r\n        Race _race;\r\n\t\tList<TrafficDriver> _traffic = new List<TrafficDriver>();\r\n\t\tstatic string[] _trafficModels;\r\n\t\tstatic TrafficController()\r\n\t\t{\r\n\t\t\t_trafficModels = new string[] {\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\axxess.CFM\",\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\vandura.CFM\",\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\bmw.CFM\",\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\copmust.CFM\",\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\crx.CFM\",\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\jeep.CFM\",\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\jetta.CFM\",\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\lemans.CFM\",\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\pickup.CFM\",\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\probe.CFM\",\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\rodeo.CFM\",\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\sunbird.CFM\",\r\n\t\t\t\t@\"SIMDATA\\CARFAMS\\wagon.CFM\"\r\n\t\t\t};\r\n\t\t}\r\n\r\n        public TrafficController(Race race)\r\n        {\r\n            _race = race;\r\n        }\r\n\r\n\r\n        public void Update()\r\n        {\r\n\t\t\tvar player = _race.Player;\r\n            for (int i = _traffic.Count - 1; i >= 0; i--)\r\n            {\r\n\t\t\t\tvar driver = _traffic[i];\r\n\r\n\t\t\t\t// too far from player\r\n\t\t\t\tif (driver.Vehicle.CurrentNode.Number < player.Vehicle.CurrentNode.Number - 30 || driver.Vehicle.CurrentNode.Number > player.Vehicle.CurrentNode.Number + 140)\r\n\t\t\t\t{\r\n\t\t\t\t\t_race.Drivers.Remove(driver);\r\n\t\t\t\t\t_traffic.Remove(driver);\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// end of track, just stop\r\n\t\t\t\tif (driver.Vehicle.CurrentNode.Next == null || driver.Vehicle.CurrentNode.Next.Next == null)\r\n\t\t\t\t{\r\n\t\t\t\t\tdriver.Vehicle.Speed = 0;\r\n                    driver.AtEndOfTrack = true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// start of track just stop\r\n\t\t\t\tif (driver.Vehicle.CurrentNode.Prev == null || driver.Vehicle.CurrentNode.Prev.Prev == null)\r\n\t\t\t\t{\r\n\t\t\t\t\tdriver.Vehicle.Speed = 0;\r\n\t\t\t\t\t//_race.Drivers.Remove(driver);\r\n\t\t\t\t\t//_traffic.Remove(driver);\r\n\t\t\t\t\t//continue;\r\n\t\t\t\t}\r\n            }\r\n\r\n\t\t\t// if player is close to the start or end of track, don't spawn new traffic\r\n\t\t\tif (_race.Player.Vehicle.CurrentNode.Number < 20 || _race.Player.Vehicle.CurrentNode.Number > _race.Track.RoadNodes.Count - 20)\r\n\t\t\t\treturn;\r\n\r\n\t\t\twhile (_traffic.Count < 5)\r\n\t\t\t{\r\n\t\t\t\tint cfmIndex = Engine.Instance.Random.Next(_trafficModels.Length);\r\n\r\n\t\t\t\t// about 1/3rd of cars should go backwards\r\n\t\t\t\tvar direction = Engine.Instance.Random.Next() % 3 == 0 ? TrafficDriverDirection.Backward : TrafficDriverDirection.Forward;\r\n\t\t\t\tvar driver = new TrafficDriver(_trafficModels[cfmIndex], direction);\r\n\t\t\t\tint distanceFromPlayer;\r\n\t\t\t\t//if (direction == TrafficDriverDirection.Forward)\r\n\t\t\t\t\tdistanceFromPlayer = Engine.Instance.Random.Next(40, 200);\r\n\t\t\t\t//else\r\n\t\t\t\t//\tdistanceFromPlayer = Engine.Instance.Random.Next(-100, -30);\r\n\t\t\t\tint nodeIndex = (_race.Player.Vehicle.CurrentNode.Number + distanceFromPlayer) % _race.Track.RoadNodes.Count - 1;\r\n\t\t\t\tnodeIndex = Math.Max(1, nodeIndex);\r\n\t\t\t\t_race.AddDriver(driver, _race.Track.RoadNodes[nodeIndex]);\r\n\t\t\t\t_traffic.Add(driver);\r\n\t\t\t}\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Vehicles/TyreSmokeParticleSystem.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing OpenNFS1.Physics;\r\nusing GameEngine;\r\nusing Microsoft.Xna.Framework.Graphics;\r\n\r\nnamespace OpenNFS1\r\n{\r\n    class TyreSmokeParticleSystem : ParticleSystem\r\n    {\r\n        static TyreSmokeParticleSystem _instance;\r\n        public static TyreSmokeParticleSystem Instance\r\n        {\r\n            get\r\n            {\r\n\t\t\t\tif (_instance == null)\r\n\t\t\t\t{\r\n\t\t\t\t\t_instance = new TyreSmokeParticleSystem();\r\n\t\t\t\t\t_instance.InitializeSystem();\r\n\t\t\t\t}\r\n                return _instance;\r\n            }\r\n        }\r\n\r\n\r\n        protected override void InitializeSettings(ParticleSettings settings)\r\n        {\r\n            settings.Texture = Engine.Instance.ContentManager.Load<Texture2D>(\"Content/smoke\");\r\n\r\n            settings.MaxParticles = 800;\r\n            \r\n            settings.Duration = TimeSpan.FromSeconds(0.7f);\r\n\t\t\tsettings.DurationRandomness = 1f;\r\n\r\n            settings.MinHorizontalVelocity = 0;\r\n            settings.MaxHorizontalVelocity = 5;\r\n\r\n\t\t\tsettings.EmitterVelocitySensitivity = 0.6f;\r\n\r\n            settings.MinVerticalVelocity = 3;\r\n            settings.MaxVerticalVelocity = 7;\r\n\r\n            settings.Gravity = new Vector3(0, -2, 0);\r\n\r\n            settings.EndVelocity = 0.75f;\r\n\r\n            //settings.MinRotateSpeed = -1;\r\n            //settings.MaxRotateSpeed = 1;\r\n\r\n\t\t\tsettings.MinStartSize = 4;\r\n\t\t\tsettings.MaxStartSize = 4;\r\n\r\n\t\t\tsettings.MinEndSize = 10;\r\n\t\t\tsettings.MaxEndSize = 30;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Vehicles/Vehicle.cs",
    "content": "﻿using Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing OpenNFS1;\r\nusing OpenNFS1.Dashboards;\r\nusing OpenNFS1.Physics;\r\nusing OpenNFS1.Vehicles;\r\nusing GameEngine;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.IO;\r\nusing OpenNFS1.Parsers.Track;\r\nusing OpenNFS1.Tracks;\r\nusing System.Diagnostics;\r\n\r\nnamespace OpenNFS1.Vehicles\r\n{\r\n\tclass Vehicle\r\n\t{\r\n\t\tpublic const float MaxSteeringLock = 0.3f;\r\n\t\tconst float Gravity = 9.81f;\r\n\r\n\t\tpublic float SteeringSpeed = 2.1f;\r\n\t\tpublic Track Track { get; set; }\r\n\t\tpublic Vector3 Position { get; set; }\r\n\t\tpublic Vector3 Direction { get; set; }\r\n\t\tpublic Vector3 Up { get; set; }\r\n\t\tpublic float Speed { get; set; }\r\n\t\tpublic TrackNode CurrentNode { get; private set; }\r\n\t\tpublic float TrackPosition { get; set; }\r\n\t\t\r\n\r\n\t\tprotected CarMesh _model;\r\n\t\tprotected AlphaTestEffect _effect;\r\n\t\tpublic float _steeringWheel;\r\n\r\n\t\t// inputs\r\n\t\tpublic float SteeringInput;\r\n\t\t\r\n\r\n\t\tprotected float _previousSpeed;\r\n\t\tprotected float _currentHeightOfTrack;\r\n\t\tprotected float _rotationChange = 0.0f;\r\n\t\tprotected bool _isOnGround = true;\r\n\t\tfloat _upVelocity = 0;\r\n\t\tfloat _timeInAir = 0;\r\n\t\t\r\n\r\n\t\tpublic Vector3 Right\r\n\t\t{\r\n\t\t\tget { return Vector3.Cross(Direction, Up); }\r\n\t\t}\r\n\r\n\t\tpublic BoundingSphere BoundingSphere\r\n\t\t{\r\n\t\t\tget\r\n\t\t\t{\r\n\t\t\t\treturn new BoundingSphere(Position, _model.BoundingBox.Max.Z - _model.BoundingBox.Min.Z);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic Vehicle(string cfmFile)\r\n\t\t{\r\n\t\t\t_model = CarModelCache.GetCfm(cfmFile);\r\n\t\t\t_effect = new AlphaTestEffect(Engine.Instance.Device);\r\n\t\t\tDirection = Vector3.Forward;\r\n\t\t\tUp = Vector3.Up;\r\n\t\t}\r\n\r\n\t\tpublic void PlaceOnTrack(Track t, TrackNode startNode)\r\n\t\t{\r\n\t\t\tTrack = t;\r\n\t\t\tCurrentNode = startNode;\r\n\t\t\tPosition = Vector3.Lerp(startNode.GetLeftVerge(), startNode.GetRightVerge2(), 0.5f);  //center of node\r\n\t\t}\r\n\r\n\t\tpublic virtual void Update()\r\n\t\t{\r\n\t\t\tif (CurrentNode.Next == null || CurrentNode.Prev == null) return;\r\n\t\t\tfloat elapsedSeconds = Engine.Instance.FrameTime;\r\n\t\t\tDirection = Vector3.TransformNormal(Direction, Matrix.CreateFromAxisAngle(Up, _rotationChange));\t\t\t\r\n\t\t\tPosition += Speed * Direction * Engine.Instance.FrameTime * 2.5f;\r\n\r\n\t\t\tUpdateSteering();\r\n\t\t\tUpdateTrackNode();\r\n            if (CurrentNode.Next == null || CurrentNode.Prev == null) return;\r\n\t\t\tTrackNode node = null, nextNode = null;\r\n\t\t\t\r\n\t\t\tif (Distance2d(Position, CurrentNode.Next.Position) < Distance2d(Position, CurrentNode.Prev.Position))\r\n\t\t\t{\r\n\t\t\t\tnode = CurrentNode;\r\n\t\t\t\tnextNode = CurrentNode.Next;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tnode = CurrentNode.Prev;\r\n\t\t\t\tnextNode = CurrentNode;\r\n\t\t\t}\r\n\r\n\t\t\tFollowTrackOrientation(node, nextNode);\r\n\t\t\tApplyGravity();\r\n\t\t}\r\n\r\n\t\tprivate void UpdateTrackNode()\r\n\t\t{\r\n\t\t\tvar nextNode = CurrentNode.Next;\r\n\t\t\tvar prevNode = CurrentNode.Prev;\r\n\t\t\tif (!Utility.IsLeftOfLine(nextNode.GetLeftBoundary(), nextNode.GetRightBoundary(), Position))\r\n\t\t\t{\r\n\t\t\t\tCurrentNode = CurrentNode.Next;\r\n\t\t\t\t//Debug.WriteLine(\"passed node - new node \" + CurrentNode.Number);\r\n\t\t\t}\r\n\t\t\telse if (prevNode != null && Utility.IsLeftOfLine(prevNode.GetLeftBoundary(), prevNode.GetRightBoundary(), Position))\r\n\t\t\t{\r\n\t\t\t\tCurrentNode = prevNode;\r\n\t\t\t\t//Debug.WriteLine(\"passed node (back) - new node \" + CurrentNode.Number);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void FollowTrackOrientation(TrackNode node, TrackNode nextNode)\r\n\t\t{\r\n\t\t\tvar closestPoint1 = Utility.GetClosestPointOnLine(node.GetLeftBoundary(), node.GetRightBoundary(), Position);\r\n\t\t\tvar closestPoint2 = Utility.GetClosestPointOnLine(nextNode.GetLeftBoundary(), nextNode.GetRightBoundary(), Position);\r\n\r\n\t\t\tvar dist = Distance2d(closestPoint1, closestPoint2);\r\n\t\t\tvar carDist = Distance2d(closestPoint1, Position);\r\n\t\t\tfloat ratio = Math.Min(carDist / dist, 1.0f);\r\n\t\t\tTrackPosition = CurrentNode.Number + ratio;\r\n\r\n\t\t\t// if the road is sloping downwards and we have enough speed, unstick from ground\r\n\t\t\tif (node.Slope - nextNode.Slope > 50 && Speed > 100 && _isOnGround)\r\n\t\t\t{\r\n\t\t\t\t_isOnGround = false;\r\n\t\t\t\t_upVelocity = -0.4f;\r\n\t\t\t}\r\n\r\n\t\t\tif (_isOnGround)\r\n\t\t\t{\r\n\t\t\t\tUp = Vector3.Lerp(node.Up, nextNode.Up, ratio);\r\n\t\t\t\tUp = Vector3.Normalize(Up);\r\n\t\t\t\tDirection = Vector3.Cross(Up, Right);\r\n\t\t\t\tDirection.Normalize();\r\n\t\t\t}\r\n\r\n\t\t\t_currentHeightOfTrack = MathHelper.Lerp(closestPoint1.Y, closestPoint2.Y, ratio);\r\n\t\t\tif (_currentHeightOfTrack == -9999)\r\n\t\t\t{\r\n\t\t\t\tthrow new Exception();\r\n\t\t\t}\r\n\t\t\tif (_isOnGround)\r\n\t\t\t{\r\n\t\t\t\tvar newPosition = Position;\r\n\t\t\t\tnewPosition.Y = _currentHeightOfTrack;\r\n\t\t\t\tPosition = newPosition;\r\n\t\t\t}\r\n\t\t\t//GameConsole.WriteLine(\"height: \" + _position.Y, 0);\r\n\t\t\t//GameConsole.WriteLine(\"ratio: \" + ratio, 1);\r\n\t\t}\r\n\r\n\t\tprivate void UpdateSteering()\r\n\t\t{\r\n\t\t\tfloat elapsedSeconds = Engine.Instance.FrameTime;\r\n\r\n\t\t\tif (SteeringInput < 0)\r\n\t\t\t{\r\n\t\t\t\t_steeringWheel += SteeringSpeed * elapsedSeconds * SteeringInput;\r\n\t\t\t\t_steeringWheel = Math.Max(_steeringWheel, -MaxSteeringLock);\r\n\t\t\t}\r\n\t\t\telse if (SteeringInput > 0)\r\n\t\t\t{\r\n\t\t\t\t_steeringWheel += SteeringSpeed * elapsedSeconds * SteeringInput;\r\n\t\t\t\t_steeringWheel = Math.Min(_steeringWheel, MaxSteeringLock);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (_steeringWheel > 0.01f)\r\n\t\t\t\t\t_steeringWheel -= SteeringSpeed * elapsedSeconds * 0.9f;\r\n\t\t\t\telse if (_steeringWheel < -0.01f)\r\n\t\t\t\t\t_steeringWheel += SteeringSpeed * elapsedSeconds * 0.9f;\r\n\t\t\t\telse\r\n\t\t\t\t\t_steeringWheel = 0;\r\n\t\t\t}\r\n\t\t\tif (_isOnGround)\r\n\t\t\t{\r\n\t\t\t\t_rotationChange = _steeringWheel * 0.05f;\r\n\t\t\t\tif (Math.Abs(Speed) < 2)\r\n\t\t\t\t\t_rotationChange = 0;\r\n\t\t\t\t\r\n\t\t\t\tif (Speed > 0)\r\n\t\t\t\t\t_rotationChange *= -1;\r\n\t\t\t}\r\n\r\n\t\t\tHandleExtraSteeringPhysics();\r\n\t\t}\r\n\r\n\t\tpublic virtual void HandleExtraSteeringPhysics() { }\r\n\r\n\t\t\r\n\t\tprivate void ApplyGravity()\r\n\t\t{\r\n\t\t\tif (_isOnGround) return;\r\n\r\n\t\t\tbool wasOnGround = _isOnGround;\r\n\r\n\t\t\t_isOnGround = Position.Y < _currentHeightOfTrack;\r\n\r\n\t\t\tif (!_isOnGround)\r\n\t\t\t{\r\n\t\t\t\tvar newPosition = Position;\r\n\t\t\t\tnewPosition.Y -= Gravity * 10f * _timeInAir * Engine.Instance.FrameTime;\r\n\t\t\t\tPosition = newPosition;\r\n\t\t\t\t// slowly pitch the nose of the car downwards - helps to flatten out the jump and looks better\r\n\t\t\t\tif (_timeInAir > 0.3f && Direction.Y > -0.3f)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar newDirection = Direction;\r\n\t\t\t\t\tnewDirection.Y -= _timeInAir * 0.006f;\r\n\t\t\t\t\tDirection = Vector3.Normalize(newDirection);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (_isOnGround && !wasOnGround)\r\n\t\t\t{\r\n\t\t\t\tif (_timeInAir > 0.2f)\r\n\t\t\t\t{\r\n\t\t\t\t\tOnGroundHit();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (_isOnGround)\r\n\t\t\t\t_timeInAir = 0;\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t_timeInAir += Engine.Instance.FrameTime;\r\n\t\t\t\t_upVelocity -= Engine.Instance.FrameTime * 100;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic virtual void OnGroundHit() { }\r\n\r\n\t\tpublic void Reset()\r\n\t\t{\r\n\t\t\tPosition = CurrentNode.Position;\r\n\t\t\tDirection = Vector3.Transform(Vector3.Forward, Matrix.CreateRotationY(MathHelper.ToRadians(CurrentNode.Orientation)));\r\n\t\t\tSpeed = 0;\r\n\t\t}\r\n\r\n\t\tpublic virtual void Render()\r\n\t\t{\r\n\t\t\t_effect.View = Engine.Instance.Camera.View;\r\n\t\t\t_effect.Projection = Engine.Instance.Camera.Projection;\r\n\r\n\t\t\t_effect.World = GetRenderMatrix();\r\n\r\n\t\t\tEngine.Instance.Device.RasterizerState = RasterizerState.CullNone;\r\n\t\t\tEngine.Instance.Device.BlendState = BlendState.Opaque;\r\n\t\t\t_effect.CurrentTechnique.Passes[0].Apply();\r\n\t\t\t_model.Render(_effect);\r\n\t\t}\r\n\r\n\r\n\t\tpublic virtual Matrix GetRenderMatrix()\r\n\t\t{\r\n\t\t\tMatrix orientation = Matrix.Identity;\r\n\t\t\torientation.Right = Right;\r\n\t\t\torientation.Up = Up;\r\n\t\t\torientation.Forward = Direction;\r\n\t\t\treturn orientation * Matrix.CreateTranslation(Position);\r\n\t\t}\r\n\r\n\t\tpublic static float Distance2d(Vector3 pos1, Vector3 pos2)\r\n\t\t{\r\n\t\t\tpos1.Y = pos2.Y = 0;\r\n\t\t\treturn Vector3.Distance(pos1, pos2);\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Vehicles/VehicleDescription.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\n\r\nnamespace OpenNFS1.Vehicles\r\n{\r\n\tclass VehicleDescription\r\n\t{\r\n\t\tpublic string Name;\r\n\t\tpublic string UIImageFile;\r\n\t\tpublic string ModelFile;\r\n\t\tpublic string SoundBnkFile;\r\n\t\tpublic int Horsepower;\r\n\t\tpublic float Redline;\r\n\t\tpublic int Mass;\r\n\r\n\t\tpublic static List<VehicleDescription> Descriptions;\r\n\r\n\t\tstatic VehicleDescription()\r\n\t\t{\r\n\t\t\tDescriptions = new List<VehicleDescription>();\r\n\r\n\t\t\tDescriptions.Add(new VehicleDescription\r\n\t\t\t{\r\n\t\t\t\tName = \"RX7\",\r\n\t\t\t\tUIImageFile = \"rx71.qfs\",\r\n\t\t\t\tModelFile = @\"SIMDATA\\CARFAMS\\mrx7.cfm\",\r\n\t\t\t\tSoundBnkFile = \"RX7_SW.bnk\",\r\n\t\t\t\tHorsepower = 255, //4,\r\n\t\t\t\tMass = 1280,\r\n\t\t\t\tRedline = 8f\r\n\t\t\t});\r\n\r\n\t\t\tDescriptions.Add(new VehicleDescription\r\n\t\t\t{\r\n\t\t\t\tName = \"NSX\",\r\n\t\t\t\tUIImageFile = \"nsx1.qfs\",\r\n\t\t\t\tModelFile = @\"SIMDATA\\CARFAMS\\ansx.cfm\",\r\n\t\t\t\tSoundBnkFile = \"NSX_SW.bnk\",\r\n\t\t\t\tHorsepower = 270,\r\n\t\t\t\tMass = 1380,\r\n\t\t\t\tRedline = 7.5f\r\n\t\t\t});\r\n\r\n\t\t\tDescriptions.Add(new VehicleDescription\r\n\t\t\t{\r\n\t\t\t\tName = \"Supra\",\r\n\t\t\t\tUIImageFile = \"sup1.qfs\",\r\n\t\t\t\tModelFile = @\"SIMDATA\\CARFAMS\\tsupra.cfm\",\r\n\t\t\t\tSoundBnkFile = \"SUPRA_SW.bnk\",\r\n\t\t\t\tHorsepower = 320,  // was 6 (x46)\r\n\t\t\t\tMass = 1580,\r\n\t\t\t\tRedline = 7f\r\n\t\t\t});\r\n\r\n\t\t\tDescriptions.Add(new VehicleDescription\r\n\t\t\t{\r\n\t\t\t\tName = \"911\",\r\n\t\t\t\tUIImageFile = \"9111.qfs\",\r\n\t\t\t\tModelFile = @\"SIMDATA\\CARFAMS\\p911.cfm\",\r\n\t\t\t\tSoundBnkFile = \"911_SW.bnk\",\r\n\t\t\t\tHorsepower = 270,\r\n\t\t\t\tMass = 1380,\r\n\t\t\t\tRedline = 6.6f\r\n\t\t\t});\r\n\r\n\t\t\tDescriptions.Add(new VehicleDescription\r\n\t\t\t{\r\n\t\t\t\tName = \"ZR1\",\r\n\t\t\t\tUIImageFile = \"vet1.qfs\",\r\n\t\t\t\tModelFile = @\"SIMDATA\\CARFAMS\\czr1.cfm\",\r\n\t\t\t\tSoundBnkFile = \"ZR1_SW.bnk\",\r\n\t\t\t\tHorsepower = 405,\r\n\t\t\t\tMass = 1380,\r\n\t\t\t\tRedline = 6.5f\r\n\t\t\t});\r\n\r\n\t\t\tDescriptions.Add(new VehicleDescription\r\n\t\t\t{\r\n\t\t\t\tName = \"Viper\",\r\n\t\t\t\tUIImageFile = \"vip1.qfs\",\r\n\t\t\t\tModelFile = @\"SIMDATA\\CARFAMS\\dviper.cfm\",\r\n\t\t\t\tSoundBnkFile = \"VIPERSW.bnk\",\r\n\t\t\t\tHorsepower = 400,\r\n\t\t\t\tMass = 1380,\r\n\t\t\t\tRedline = 6f\r\n\t\t\t});\r\n\r\n\t\t\tDescriptions.Add(new VehicleDescription\r\n\t\t\t{\r\n\t\t\t\tName = \"F512\",\r\n\t\t\t\tUIImageFile = \"5121.qfs\",\r\n\t\t\t\tModelFile = @\"SIMDATA\\CARFAMS\\f512tr.cfm\",\r\n\t\t\t\tSoundBnkFile = \"TR512_SW.bnk\",\r\n\t\t\t\tHorsepower = 421,\r\n\t\t\t\tMass = 1380,\r\n\t\t\t\tRedline = 8f\r\n\t\t\t});\r\n\r\n\t\t\tDescriptions.Add(new VehicleDescription\r\n\t\t\t{\r\n\t\t\t\tName = \"Diablo\",\r\n\t\t\t\tUIImageFile = \"dia1.qfs\",\r\n\t\t\t\tModelFile = @\"SIMDATA\\CARFAMS\\ldiabl.cfm\",\r\n\t\t\t\tSoundBnkFile = \"DIABLOSW.bnk\",\r\n\t\t\t\tHorsepower = 490,\r\n\t\t\t\tMass = 1380,\r\n\t\t\t\tRedline = 7.5f\r\n\t\t\t});\r\n\r\n\t\t\tDescriptions.Add(new VehicleDescription\r\n\t\t\t{\r\n\t\t\t\tName = \"Warrior\",\r\n\t\t\t\tUIImageFile = \"war1.qfs\",\r\n\t\t\t\tModelFile = @\"SIMDATA\\CARFAMS\\traffc.cfm\",\r\n\t\t\t\tSoundBnkFile = \"TRAFFC.bnk\",\r\n\t\t\t\tHorsepower = 700,\r\n\t\t\t\tMass = 1380,\r\n\t\t\t\tRedline = 7f\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Vehicles/WheelModel.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing OpenNFS1.Parsers;\r\n\r\nnamespace OpenNFS1\r\n{\r\n    static class WheelModel\r\n    {\r\n        static VertexPositionTexture[] _cylinderVertices;\r\n        static VertexBuffer _cylinderVertexBuffer;\r\n        static AlphaTestEffect _effect;\r\n\t\tstatic Texture2D _rubberTexture;\r\n\r\n        static WheelModel()\r\n        {\r\n            CreateGeometry();\r\n\r\n            _effect = new AlphaTestEffect(Engine.Instance.Device);\r\n            _effect.VertexColorEnabled = false;\r\n\t\t\t_rubberTexture = new Texture2D(Engine.Instance.Device, 1, 1);\r\n\t\t\t_rubberTexture.SetData<Color>(new Color[] { new Color(0.1f, 0.1f, 0.1f) });\r\n        }\r\n\r\n        public static void BeginBatch()\r\n        {\r\n            Engine.Instance.Device.SetVertexBuffer(_cylinderVertexBuffer);\r\n\r\n            _effect.View = Engine.Instance.Camera.View;\r\n            _effect.Projection = Engine.Instance.Camera.Projection;\r\n        }\r\n\r\n\t\tpublic static void Render(Matrix world, Texture2D texture)\r\n\t\t{\r\n\t\t\t// render rubber tire part\r\n\t\t\t_effect.World = world;\r\n\t\t\t_effect.Texture = _rubberTexture;\r\n\t\t\t_effect.CurrentTechnique.Passes[0].Apply();\r\n\t\t\tEngine.Instance.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, 288 / 3);\r\n\r\n\t\t\t// render the sides (hubs)\r\n\t\t\t_effect.Texture = texture;\r\n\t\t\t_effect.VertexColorEnabled = false;\r\n\t\t\t_effect.CurrentTechnique.Passes[0].Apply();\r\n\t\t\tEngine.Instance.Device.DrawPrimitives(PrimitiveType.TriangleList, 288, 4);\r\n\r\n\t\t}\r\n\r\n        private static void CreateGeometry()\r\n        {\r\n            int numSides = 32;\r\n            Vector3 bottomCenter = new Vector3(0, -.5f, 0);\r\n            Vector3 topCenter = new Vector3(0, .5f, 0);\r\n            Vector3 currentVector;\r\n            Vector3 nextVector;\r\n            float xPos1, xPos2, zPos1, zPos2;\r\n            float radius = 0.51f;\r\n            _cylinderVertices = new VertexPositionTexture[numSides * 9 + 12];\r\n            float angleChange = (float)Math.PI / (numSides / 2);\r\n            float angle;\r\n\t\t\t\r\n\t\t\tColor c = new Color(0.1f, 0.1f, 0.1f); //rubber color\r\n\r\n            for (int k = 0; k < numSides; k++)\r\n            {\r\n                angle = k * angleChange;\r\n                xPos1 = (float)Math.Cos(angle);\r\n                xPos2 = (float)Math.Cos(angle + angleChange);\r\n                zPos1 = (float)Math.Sin(angle);\r\n                zPos2 = (float)Math.Sin(angle + angleChange);\r\n                currentVector = new Vector3(xPos1, 0, zPos1);\r\n                nextVector = new Vector3(xPos2, 0, zPos2);\r\n\r\n                _cylinderVertices[k * 9 + 0] = new VertexPositionTexture(topCenter + currentVector * radius, Vector2.Zero);\r\n\t\t\t\t_cylinderVertices[k * 9 + 1] = new VertexPositionTexture(bottomCenter + currentVector * radius, Vector2.Zero);\r\n\t\t\t\t_cylinderVertices[k * 9 + 2] = new VertexPositionTexture(bottomCenter + nextVector * radius, Vector2.Zero);\r\n\r\n\t\t\t\t_cylinderVertices[k * 9 + 3] = new VertexPositionTexture(bottomCenter + nextVector * radius, Vector2.Zero);\r\n\t\t\t\t_cylinderVertices[k * 9 + 5] = new VertexPositionTexture(topCenter + currentVector * radius, Vector2.Zero);\r\n\t\t\t\t_cylinderVertices[k * 9 + 4] = new VertexPositionTexture(topCenter + nextVector * radius, Vector2.Zero);\r\n            }\r\n\r\n\t\t\t// Add the sides of the wheel to the cylinder\r\n\r\n            float y = 0.505f;\r\n            Vector3 bottomLeftFront = new Vector3(-0.5f, y, 0.5f);\r\n            Vector3 bottomRightFront = new Vector3(0.5f, y, 0.5f);\r\n            Vector3 bottomLeftBack = new Vector3(-0.5f, y, -0.5f);\r\n            Vector3 bottomRightBack = new Vector3(0.5f, y, -0.5f);\r\n\r\n\t\t\tc = Color.White;\r\n\t\t\t_cylinderVertices[288] = new VertexPositionTexture(bottomLeftFront, new Vector2(0.0f, 0.0f));\r\n\t\t\t_cylinderVertices[289] = new VertexPositionTexture(bottomLeftBack, new Vector2(0.0f, 1.0f));\r\n\t\t\t_cylinderVertices[290] = new VertexPositionTexture(bottomRightBack, new Vector2(1.0f, 1.0f));\r\n\t\t\t_cylinderVertices[291] = new VertexPositionTexture(bottomLeftFront, new Vector2(0.0f, 0.0f));\r\n\t\t\t_cylinderVertices[292] = new VertexPositionTexture(bottomRightBack, new Vector2(1.0f, 1.0f));\r\n\t\t\t_cylinderVertices[293] = new VertexPositionTexture(bottomRightFront, new Vector2(1.0f, 0.0f));\r\n\r\n\t\t\t_cylinderVertices[294] = new VertexPositionTexture(new Vector3(-0.5f, -y, 0.5f), new Vector2(0.0f, 1.0f));\r\n\t\t\t_cylinderVertices[295] = new VertexPositionTexture(new Vector3(0.5f, -y, -0.5f), new Vector2(1.0f, 0.0f));\r\n\t\t\t_cylinderVertices[296] = new VertexPositionTexture(new Vector3(-0.5f, -y, -0.5f), new Vector2(0.0f, 0.0f));\r\n\t\t\t_cylinderVertices[297] = new VertexPositionTexture(new Vector3(-0.5f, -y, 0.5f), new Vector2(0.0f, 1.0f));\r\n\t\t\t_cylinderVertices[298] = new VertexPositionTexture(new Vector3(0.5f, -y, 0.5f), new Vector2(1.0f, 1.0f));\r\n\t\t\t_cylinderVertices[299] = new VertexPositionTexture(new Vector3(0.5f, -y, -0.5f), new Vector2(1.0f, 0.0f));\r\n\r\n            _cylinderVertexBuffer = new VertexBuffer(Engine.Instance.Device,\r\n                                                 typeof(VertexPositionTexture), _cylinderVertices.Length,\r\n                                                 BufferUsage.WriteOnly);\r\n\r\n            _cylinderVertexBuffer.SetData<VertexPositionTexture>(_cylinderVertices);\r\n        }\r\n\r\n    }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Views/BaseExternalView.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing GameEngine;\r\nusing OpenNFS1.Parsers;\r\nusing OpenNFS1.Physics;\r\n\r\nnamespace OpenNFS1.Views\r\n{\r\n\tclass BaseExternalView\r\n\t{\r\n\t\tstatic BitmapEntry _bottomBar, _bottomFill, _tacho, _map;\r\n\t\tstatic Texture2D _tachLineTexture;\r\n\t\tconst int _size = 160;\r\n\t\tconst float _needleLength = 2.5f;\r\n\t\tconst float _needleWidth = 3f;\r\n\r\n\t\tpublic BaseExternalView()\r\n\t\t{\r\n\t\t\tif (_bottomBar == null)\r\n\t\t\t{\r\n\t\t\t\tvar fsh = new FshFile(@\"Simdata\\Misc\\MaskHi.fsh\");\r\n\t\t\t\t_bottomBar = fsh.Header.Bitmaps.Find(a => a.Id == \"b00b\");\r\n\t\t\t\t_bottomFill = fsh.Header.Bitmaps.Find(a => a.Id == \"0002\");\r\n\t\t\t\t_tacho = fsh.Header.Bitmaps.Find(a => a.Id == \"tach\");\r\n\t\t\t\t_map = fsh.Header.Bitmaps.Find(a => a.Id == \"mpbd\");\r\n\r\n\t\t\t\t_tachLineTexture = new Texture2D(Engine.Instance.Device, (int)_needleWidth, 25);\r\n\t\t\t\tColor[] pixels = new Color[_tachLineTexture.Width * _tachLineTexture.Height];\r\n\t\t\t\tfor (int i = 0; i < pixels.Length; i++) pixels[i] = Color.Red;\r\n\t\t\t\t_tachLineTexture.SetData<Color>(pixels);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void RenderBackground(DrivableVehicle car)\r\n\t\t{\r\n\t\t\tfloat carRpm = car.Motor.Rpm / car.Motor.RedlineRpm;\r\n\t\t\tEngine.Instance.SpriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.None, RasterizerState.CullNone);\r\n\r\n\t\t\t// Draw tacho\r\n\t\t\tColor color = new Color(255, 255, 255, 200);\r\n\t\t\tEngine.Instance.SpriteBatch.Draw(_tacho.Texture, new Rectangle(_tacho.Misc[2], _tacho.Misc[3], _size, _size), Color.White);\r\n\r\n\t\t\tfloat rotation = (float)(carRpm * Math.PI * 1.4f) - 2.56f;\r\n\t\t\tEngine.Instance.SpriteBatch.Draw(_tachLineTexture, new Vector2(_tacho.Misc[2] + _size / 2, _tacho.Misc[3] + _size / 2), null, color, rotation, new Vector2(_needleWidth/2, 25), new Vector2(1f, _needleLength), SpriteEffects.None, 0);\r\n\r\n\t\t\t// mini-map overlay\r\n\t\t\t//Engine.Instance.SpriteBatch.Draw(_map.Texture, _map.GetDisplayAt(), Color.White);\r\n\r\n\t\t\t// Draw bottom fill\r\n\t\t\tconst int barHeight = 17;\r\n\t\t\tEngine.Instance.SpriteBatch.Draw(_bottomBar.Texture, new Vector2(0, 400), new Rectangle(0, 0, 640, barHeight), Color.White);\r\n\t\t\tEngine.Instance.SpriteBatch.Draw(_bottomFill.Texture, new Vector2(0, 400 + barHeight), new Rectangle(0, 0, 640, 60), Color.White);\t\t\r\n\r\n\t\t\tEngine.Instance.SpriteBatch.End();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Views/BumperView.cs",
    "content": "﻿using Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing OpenNFS1.Physics;\r\n\r\nnamespace OpenNFS1.Views\r\n{\r\n\tclass BumperView : BaseExternalView, IView\r\n\t{\r\n\t\tDrivableVehicle _car;\r\n        SimpleCamera _camera;\r\n\r\n\t\tpublic BumperView(DrivableVehicle car)\r\n        {\r\n            _car = car;\r\n\t\t\t_camera = new SimpleCamera();\r\n\t\t\t_camera.FieldOfView = GameConfig.FOV;\r\n\t\t\t_camera.FarPlaneDistance = GameConfig.DrawDistance;\r\n        }\r\n\r\n        #region IView Members\r\n\r\n        public bool Selectable\r\n        {\r\n            get { return true; }\r\n        }\r\n\r\n\t\tpublic bool ShouldRenderPlayer { get { return false; } }\r\n\r\n        public void Activate()\r\n        {\r\n            Engine.Instance.Camera = _camera;\r\n        }\r\n\r\n        public void Deactivate()\r\n        {\r\n        }\r\n\r\n        public void Update(GameTime gameTime)\r\n        {\r\n            _camera.Position = _car.Position + _car.Direction * 2.8f + new Vector3(0, 5, 0);\r\n            _camera.LookAt = _camera.Position + _car.Direction * 60f + new Vector3(0, _car.BodyPitch.Position, 0);\r\n            _camera.UpVector = _car.Up; // +new Vector3(_car.Roll.Position * 0.2f, 0, 0);\r\n            //float f = 1.8f; // _car.Roll.Position;\r\n            //_camera.Up = _car.Up + _car.Direction * new Vector3(f, f, f);\r\n            \r\n        }\r\n\r\n        public void Render()\r\n        {\r\n\t\t\tRenderBackground(_car);\r\n        }\r\n\r\n        #endregion\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Views/ChaseView.cs",
    "content": "using Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing OpenNFS1.Physics;\r\n\r\nnamespace OpenNFS1.Views\r\n{\r\n\t\r\n\r\n    class ChaseView : BaseExternalView, IView\r\n    {\r\n        DrivableVehicle _car;\r\n        FixedChaseCamera _camera;\r\n\t\t\r\n\t\tpublic bool ShouldRenderPlayer { get { return true; } }\r\n        \r\n        public ChaseView(DrivableVehicle car, int distance, int height, int offset)\r\n        {\r\n            _car = car;\r\n            _camera = new FixedChaseCamera();\r\n\t\t\t_camera.FieldOfView = GameConfig.FOV;\r\n\t\t\t_camera.FarPlaneDistance = GameConfig.DrawDistance;\r\n            _camera.ChaseDistance = distance;\r\n            _camera.ChaseHeight = height;\r\n            _camera.ChaseOffset = offset;\r\n        }\r\n\r\n        #region IView Members\r\n\r\n        public bool Selectable\r\n        {\r\n            get { return true; }\r\n        }\r\n\r\n        public void Activate()\r\n        {\r\n            Engine.Instance.Camera = _camera;\r\n            _camera.Position = _car.Position;\r\n            _camera.ChaseDirection = _car.Direction;\r\n        }\r\n\r\n        public void Deactivate()\r\n        {\r\n\t\t}\r\n\r\n        public void Update(GameTime gameTime)\r\n        {\r\n            _camera.Position = _car.Position;\r\n\t\t\t_camera.ChaseDirection = _car.Direction;\r\n\t\t\t_camera.UpVector = _car.Up;\r\n        }\r\n\r\n        public void Render()\r\n        {\r\n\t\t\tRenderBackground(_car);\r\n        }\r\n\r\n        #endregion\r\n\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Views/DashboardView.cs",
    "content": "using System.IO;\r\nusing Microsoft.Xna.Framework;\r\nusing Microsoft.Xna.Framework.Graphics;\r\nusing GameEngine;\r\nusing OpenNFS1.Dashboards;\r\nusing OpenNFS1.Physics;\r\nusing OpenNFS1.Vehicles;\r\nusing OpenNFS1.Views;\r\n\r\nnamespace OpenNFS1\r\n{\r\n    class DashboardView : IView\r\n    {\r\n\t\tDrivableVehicle _car;\r\n        SimpleCamera _camera;\r\n        private Dashboard _dashboard;\r\n\r\n\t\tpublic DashboardView(DrivableVehicle car)\r\n        {\r\n            _car = car;\r\n            _camera = new SimpleCamera();\r\n\t\t\t_camera.FieldOfView = GameConfig.FOV;\r\n\t\t\t_camera.FarPlaneDistance = GameConfig.DrawDistance;\r\n\r\n\t\t\tvar dashfile = Path.GetFileNameWithoutExtension(car.Descriptor.ModelFile) + \"dh.fsh\";\r\n\t\t\tvar dashDescription = DashboardDescription.Descriptions.Find(a => a.Filename == dashfile);\r\n\t\t\t_dashboard = new Dashboard(car, dashDescription);\r\n        }\r\n\r\n        #region IView Members\r\n\r\n        public bool Selectable\r\n        {\r\n            get { return true; }\r\n        }\r\n\r\n\t\tpublic bool ShouldRenderPlayer { get { return false; } }\r\n\r\n        public void Update(GameTime gameTime)\r\n        {\r\n            _camera.Position = _car.Position + new Vector3(0, 5, 0);\r\n\t\t\t_camera.LookAt = _camera.Position + _car.RenderDirection * 60f + new Vector3(0, _car.BodyPitch.Position, 0);\r\n\t\t\t_camera.UpVector = _car.Up; // +new Vector3(_car.Roll.Position * 0.2f, 0, 0);\r\n\r\n            _dashboard.Update(gameTime);\r\n        }\r\n\r\n        public void Render()\r\n        {\r\n\t\t\tEngine.Instance.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);\r\n\r\n            _dashboard.Render();\r\n\r\n            Engine.Instance.SpriteBatch.End();\r\n        }\r\n\r\n        public void Activate()\r\n        {\r\n            Engine.Instance.Camera = _camera;\r\n            _dashboard.IsVisible = true;\r\n        }\r\n\r\n        public void Deactivate()\r\n        {\r\n            _dashboard.IsVisible = false;\r\n        }\r\n\r\n        #endregion\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Views/DebugView.cs",
    "content": "﻿using Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing OneAmEngine;\r\nusing OpenNFS1.Physics;\r\nusing OpenNFS1.Views;\r\n\r\nnamespace OpenNFS1\r\n{\r\n\tclass DebugView : IView\r\n\t{\r\n\t\tDrivableVehicle _car;\r\n\t\tFPSCamera _camera;\r\n\r\n\t\tpublic DebugView(DrivableVehicle car)\r\n\t\t{\r\n\t\t\t_car = car;\r\n\t\t\t_camera = new FPSCamera();\r\n\t\t}\r\n\r\n\t\t#region IView Members\r\n\r\n\t\tpublic bool Selectable\r\n\t\t{\r\n\t\t\tget { return false; }\r\n\t\t}\r\n\r\n\t\tpublic bool ShouldRenderPlayer { get { return true; } }\r\n\r\n\t\tpublic void Update(GameTime gameTime)\r\n\t\t{\r\n\t\t\t_camera.Update(gameTime);\r\n\t\t}\r\n\r\n\t\tpublic void Render()\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tpublic void Activate()\r\n\t\t{\r\n\t\t\tEngine.Instance.Camera = _camera;\r\n\t\t\t_camera.Position = _car.Position;\r\n\t\t}\r\n\r\n\t\tpublic void Deactivate()\r\n\t\t{\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Views/DropCameraView.cs",
    "content": "﻿using System;\r\nusing Microsoft.Xna.Framework;\r\nusing GameEngine;\r\nusing OpenNFS1.Physics;\r\nusing OpenNFS1.Tracks;\r\n\r\nnamespace OpenNFS1.Views\r\n{\r\n\tclass DropCameraView : BaseExternalView, IView\r\n\t{\r\n\t\tconst int MaxCameraDistance = 15;\r\n\t\tDrivableVehicle _car;\r\n\t\tSimpleCamera _camera;\r\n\t\tTrackNode _cameraNode;\r\n\r\n\t\tpublic DropCameraView(DrivableVehicle car)\r\n\t\t{\r\n\t\t\t_car = car;\r\n\t\t\t_camera = new SimpleCamera();\r\n\t\t\t_camera.FieldOfView = GameConfig.FOV;\r\n\t\t\t_camera.FarPlaneDistance = GameConfig.DrawDistance;\r\n\t\t\tPositionCameraAtNode(car.Track.RoadNodes[10]);\r\n\t\t}\r\n\r\n\t\t#region IView Members\r\n\r\n\t\tpublic bool Selectable\r\n\t\t{\r\n\t\t\tget { return true; }\r\n\t\t}\r\n\r\n\t\tpublic bool ShouldRenderPlayer { get { return true; } }\r\n\r\n\t\tpublic void Activate()\r\n\t\t{\r\n\t\t\tEngine.Instance.Camera = _camera;\r\n\t\t}\r\n\r\n\t\tpublic void Deactivate()\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tpublic void Update(GameTime gameTime)\r\n\t\t{\r\n\t\t\tif (Math.Abs(_car.CurrentNode.Number - _cameraNode.Number) > MaxCameraDistance)\r\n\t\t\t{\r\n\t\t\t\tint nextNode = (_car.CurrentNode.Number + MaxCameraDistance) % _car.Track.RoadNodes.Count;\r\n\t\t\t\tPositionCameraAtNode(_car.Track.RoadNodes[nextNode]);\r\n\t\t\t}\r\n\t\t\t_camera.LookAt = _car.Position;\r\n\t\t}\r\n\r\n\t\tprivate void PositionCameraAtNode(TrackNode node)\r\n\t\t{\r\n\t\t\t_cameraNode = node;\r\n\t\t\t_camera.Position = Engine.Instance.Random.Next() % 2 == 0 ? _cameraNode.GetLeftBoundary() : _cameraNode.GetRightBoundary();\r\n\t\t\t_camera.Position = _camera.Position + new Vector3(0, Engine.Instance.Random.Next(15, 50), 0);\r\n\t\t}\r\n\r\n\t\tpublic void Render()\r\n\t\t{\r\n\t\t\tRenderBackground(_car);\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\t}\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/Views/IView.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing Microsoft.Xna.Framework;\r\n\r\nnamespace OpenNFS1.Views\r\n{\r\n    interface IView\r\n    {\r\n        bool Selectable { get; }\r\n\t\tbool ShouldRenderPlayer { get; }\r\n        void Update(GameTime gameTime);\r\n        void Render();\r\n        void Activate();\r\n        void Deactivate();\r\n\t\t\r\n    }\r\n}\r\n"
  },
  {
    "path": "OpenNFS1/gameconfig.json",
    "content": "﻿{\r\n\t\"fullScreen\": false,\r\n\t\"cdDataPath\": \"CD_Data\",\r\n\t\"drawDistance\": 5000,\r\n\t\"respectOpenRoadCheckpoints\": true,\r\n\t\"drawDebugInfo\": false\r\n}"
  },
  {
    "path": "OpenNFS1.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 2013\r\nVisualStudioVersion = 12.0.31101.0\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"OpenNFS1\", \"OpenNFS1\\OpenNFS1.csproj\", \"{2BBFF57F-1C9D-430E-8343-D2662437114D}\"\r\nEndProject\r\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"GameEngine\", \"Engine\\GameEngine.csproj\", \"{F66B2F9A-AF38-40F9-A094-522C823D04EE}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tAndroid|Any CPU = Android|Any CPU\r\n\t\tAndroid|Mixed Platforms = Android|Mixed Platforms\r\n\t\tAndroid|x86 = Android|x86\r\n\t\tDebug|Any CPU = Debug|Any CPU\r\n\t\tDebug|Mixed Platforms = Debug|Mixed Platforms\r\n\t\tDebug|x86 = Debug|x86\r\n\t\tiOS|Any CPU = iOS|Any CPU\r\n\t\tiOS|Mixed Platforms = iOS|Mixed Platforms\r\n\t\tiOS|x86 = iOS|x86\r\n\t\tLinux|Any CPU = Linux|Any CPU\r\n\t\tLinux|Mixed Platforms = Linux|Mixed Platforms\r\n\t\tLinux|x86 = Linux|x86\r\n\t\tOSX|Any CPU = OSX|Any CPU\r\n\t\tOSX|Mixed Platforms = OSX|Mixed Platforms\r\n\t\tOSX|x86 = OSX|x86\r\n\t\tPSM|Any CPU = PSM|Any CPU\r\n\t\tPSM|Mixed Platforms = PSM|Mixed Platforms\r\n\t\tPSM|x86 = PSM|x86\r\n\t\tRelease|Any CPU = Release|Any CPU\r\n\t\tRelease|Mixed Platforms = Release|Mixed Platforms\r\n\t\tRelease|x86 = Release|x86\r\n\t\tWindows|Any CPU = Windows|Any CPU\r\n\t\tWindows|Mixed Platforms = Windows|Mixed Platforms\r\n\t\tWindows|x86 = Windows|x86\r\n\t\tWindows8|Any CPU = Windows8|Any CPU\r\n\t\tWindows8|Mixed Platforms = Windows8|Mixed Platforms\r\n\t\tWindows8|x86 = Windows8|x86\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Android|Any CPU.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Android|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Android|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Android|x86.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Android|x86.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Debug|Any CPU.ActiveCfg = Debug|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Debug|Mixed Platforms.ActiveCfg = Debug|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Debug|Mixed Platforms.Build.0 = Debug|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Debug|x86.ActiveCfg = Debug|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Debug|x86.Build.0 = Debug|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.iOS|Any CPU.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.iOS|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.iOS|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.iOS|x86.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.iOS|x86.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Linux|Any CPU.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Linux|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Linux|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Linux|x86.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Linux|x86.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.OSX|Any CPU.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.OSX|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.OSX|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.OSX|x86.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.OSX|x86.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.PSM|Any CPU.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.PSM|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.PSM|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.PSM|x86.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.PSM|x86.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Release|Any CPU.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Release|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Release|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Release|x86.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Release|x86.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Windows|Any CPU.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Windows|Mixed Platforms.ActiveCfg = Debug|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Windows|Mixed Platforms.Build.0 = Debug|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Windows|x86.ActiveCfg = Debug|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Windows|x86.Build.0 = Debug|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Windows8|Any CPU.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Windows8|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Windows8|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Windows8|x86.ActiveCfg = Release|x86\r\n\t\t{2BBFF57F-1C9D-430E-8343-D2662437114D}.Windows8|x86.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Android|Any CPU.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Android|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Android|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Android|x86.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Android|x86.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Debug|Any CPU.ActiveCfg = Debug|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Debug|Mixed Platforms.ActiveCfg = Debug|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Debug|Mixed Platforms.Build.0 = Debug|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Debug|x86.ActiveCfg = Debug|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Debug|x86.Build.0 = Debug|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.iOS|Any CPU.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.iOS|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.iOS|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.iOS|x86.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.iOS|x86.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Linux|Any CPU.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Linux|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Linux|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Linux|x86.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Linux|x86.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.OSX|Any CPU.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.OSX|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.OSX|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.OSX|x86.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.OSX|x86.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.PSM|Any CPU.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.PSM|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.PSM|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.PSM|x86.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.PSM|x86.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Release|Any CPU.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Release|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Release|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Release|x86.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Release|x86.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Windows|Any CPU.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Windows|Mixed Platforms.ActiveCfg = Debug|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Windows|Mixed Platforms.Build.0 = Debug|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Windows|x86.ActiveCfg = Debug|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Windows|x86.Build.0 = Debug|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Windows8|Any CPU.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Windows8|Mixed Platforms.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Windows8|Mixed Platforms.Build.0 = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Windows8|x86.ActiveCfg = Release|x86\r\n\t\t{F66B2F9A-AF38-40F9-A094-522C823D04EE}.Windows8|x86.Build.0 = Release|x86\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "TnfsSeSpex.txt",
    "content": "\r\n============================================================================\r\nTHE UNOFFICIAL NEED FOR SPEED - SE FILE FORMAT SPECIFICATIONS - Version 0.2\r\n Copyright(c)1996, Ian Brown - 101735.527@compuserve.com\r\n Portions Copyright(c)1995-1996, Dennis Auroux (MXK) - auroux@clipper.ens.fr\r\n============================================================================\r\n\r\nThis file forms an addendum to the original NFS File Format specifications,\r\navailable from the following URL:\r\n\r\n\thttp://www.eleves.ens.fr:8080/home/auroux/nfsspecs.txt\r\n\r\nThe most recent version of this file is available from:\r\n\r\n\thttp://ourworld.compuserve.com/homepages/ianbrown/nfsse_sp.txt\r\n\r\n\r\nE - APPENDIX  -- NFS-SE changes\r\n    ===========================\r\n\r\nE.1 GAMEDATA\\CONFIG\\PATHS.DAT\r\n    -------------------------\r\n\r\nThis file is essentially the same as for TNFS except for one addition. The\r\nTrack FAM files in NFSSE now come in 2 varieties, a normal file in\r\n\\simdata\\ntrackfam and an internationalised file \\simdata\\etrackfam (English)\r\nor \\simdata\\gtrackfam (German). The pattern of entries, then, is now as follows:\r\n\r\n000 gamedata/config/              2D0 F:\\simdata\\misc\\\r\n050 gamedata/savegame/            320 F:\\simdata\\etrackfam\\\r\n0A0 F:\\frontend\\speech\\           370 F:\\simdata\\gtrackfam\\\r\n0F0 F:\\simdata\\soundbnk\\          3C0 F:\\simdata\\slides\\\r\n140 F:\\frontend\\music\\            410 F:\\simdata\\carfams\\\r\n190 F:\\frontend\\art\\              460 F:\\simdata\\soundbnk\\\r\n1E0 gamedata/modem/               4B0 F:\\simdata\\carspecs\\\r\n230 F:\\frontend\\movielow\\         500 F:\\simdata\\dash\\\r\n280 gamedata/replay/              550 F:\\frontend\\misc\\\r\n\t\t\t\t  5A0 F:\\frontend\\show\\\r\n\r\n\r\nE.2 SIMDATA\\MISC\\*.TRI\r\n    ------------------\r\n\r\nThese files describe the track itself, including the shape of the road\r\nand the position of the scenery items. The objects are referenced by numbers\r\nwhich correspond to entries in the corresponding files in SIMDATA\\ETRACKFAM,\r\nand SIMDATA\\NTRACKFAM.\r\n\r\n\r\nIn order to understand the structure of TRI files you have to know that a\r\ntrack is the superposition of three structures :\r\n\r\n- first, a 'virtual road' : this is a sequence of points in space,\r\n  which will be called 'nodes'. These points correspond to successive\r\n  positions along the track, and all the cars have to pass near each of\r\n  these positions. During the game, the virtual road is invisible, but\r\n  you have to stay close to it.\r\n\r\n- second, the scenery : this is a collection of points in space, which\r\n  will be called 'vertices', together with textures which are mapped onto\r\n  the polygons defined by consecutive vertices. These textures are used\r\n  to draw the road, the roadside and part of the landscape. Thus it is\r\n  of course preferable that the scenery remain close to the virtual road !\r\n\r\n- and last, the objects : points in space together with bitmaps, which\r\n  are used for road signs, buildings, trees, etc... Some of them are\r\n  plain 2D bitmaps, but others have a more sophisticated polygonal 3D\r\n  structure.\r\n\r\nThe 3D coordinates x,y,z will always be used with the following meaning :\r\n\r\n- x is an axis which is transversal to the starting line. Positive x values\r\n  correspond to points which are on the right of the starting position.\r\n  (i.e. if you start looking to the north, x points to the east)\r\n\r\n- y is an axis which is parallel to the starting line. Positive y values\r\n  correspond to points which are ahead of the starting position.\r\n  (i.e. y points to the north)\r\n\r\n- z is a vertical axis. Positive z values correspond to points higher than\r\n  the starting position.\r\n\r\na) TRI files begin with 98Ch bytes of headers and index tables. These are\r\nas follows :\r\n\r\noffset len data\r\n------ --- ----\r\n00      4   ?\r\n04      8   ?\r\n0C      4   x coordinate of the first node\r\n10      4   z coordinate of the first node\r\n14      4   y coordinate of the first node\r\n18      4   ?\r\n1C      4   ?\r\n20      4   ?\r\n24      4   length of the scenery data (in bytes : 120h per record)\r\n28      4   ?\r\n2C    960h  first index table\r\n\r\nThe first index table is a succession of 32-bit offsets. It follows an\r\narithmetic progression by 120h as a general rule. This means the first\r\nvalue is 0, followed by 548h, A90h, etc... On closed tracks, when the\r\nend is reached, the offsets start again with 0, 548h, A90h, etc...\r\n(so that the end of a lap is connected with the beginning of the following\r\none !). The table is filled to 960h bytes (600 entries) with zero values.\r\nThese offsets are added to the base address of the scenery data to get an\r\nabsolute offset into the file.\r\n\r\n\r\nb) The virtual road data follows, and is constituted of 36-byte records\r\n(one for each node). The first record is at offset 98Ch, and the last\r\nallowed record is at offset 15B0Ch (this leaves room for 2400 records, and\r\nsince a scenery block corresponds to four nodes, both capacities are equal).\r\nThe unused records (after the last node) are filled with zero values.\r\nThe structure of each record is the following :\r\n\r\noffset len data\r\n------ --- ----\r\n00      1  a0\r\n01      1  a1\r\n02      1  a2\r\n03      1  a3\r\n04      1  b0\r\n05      1  b1\r\n06      1  b2\r\n07      1  b3\r\n08      4  x coordinate\r\n0C      4  z coordinate\r\n10      4  y coordinate\r\n14      2  slope\r\n16      2  slant-A\r\n18      2  orientation\r\n1A      2  0\r\n1C      2  y-orientation\r\n1E      2  slant-B\r\n20      2  x-orientation\r\n22      2  0\r\n\r\n- a0,a1,a2,a3 are 8-bit values which specify the width of the main road,\r\n  and the width of the finging area.\r\n\r\n        a0 = distance from the virtual road to the left hand verge\r\n        a1 = distance from the virtual road to the right hand verge\r\n        a2 = distance from virtual road to the absolute left hand edge\r\n        a3 = distanec from virtual road to the absolute right hand edge  \r\n\r\n- b0,b1,b2,b3 are 8-bit values of unknown purpose.\r\n\r\n- x, z and y coordinates are signed long values.\r\n\r\n- slope is a value indicating the slope at the current node, i.e. the\r\n  difference between the z coordinates of two consecutive nodes.\r\n  A good approximation is : slope(i) = (z(i+1) - z(i))/152.\r\n  However, to complicate things, it is stored as a signed 14-bit value,\r\n  complemented to 4000h. This means -1 is stored as 3FFFh, -2 as 3FFEh, ...\r\n  So in fact you must perform a logical and with 3FFFh before storing !\r\n\r\n- slant-A is a value indicating how the road is slanted to the left or\r\n  to the right (as in the turns in Autumn Valley or Lost Vegas). It is\r\n  a signed 14-bit value, like slope. The value is positive if the road\r\n  is slanted to the right, negative if it is slanted to the left.\r\n\r\n- slant-B has the same purpose, but is a standard signed 16-bit value.\r\n  Its value is positive for the left, negative for the right.\r\n  The approximative relation between slant-A and slant-B is\r\n  slant-B = -12.3 slant-A (remember that slant-A is 14-bit, though)\r\n\r\n- orientation is a 14-bit value, and is equal to 0 for north (increasing y),\r\n  1000h for east (increasing x), 2000h for south (decreasing y), 3000h for\r\n  west (decreasing x), and back to 3FFFh for north.\r\n\r\n- y-orientation is a signed 16-bit value, which is proportional to the\r\n  y coordinate variation. Meanwhile, x-orientation is proportional to the\r\n  *opposite* of the x coordinate variation. This means that the couple\r\n  (-xorientation,yorientation) gives the orientation of the track.\r\n  The norm (square root of xorient^2+yorient^2) is usually around 32000\r\n  (a little less than 8000h, to avoid numeric overflows), but can fluctuate\r\n  with the only condition that (-xor,yor) gives the correct orientation.\r\n\r\nc) The objects data comes next. There are first several distinct zones,\r\nmany of which seem to be unused (?) :\r\n\r\noffset len  data\r\n------ ---  ----\r\n15B0C  708h  3-byte records (there are 600... as many as scenery blocks ?)\r\n16214   4    40h (?)\r\n16218   4    3E8h = 1000 (size of the main block in records)\r\n1621C   4    'SJOB'\r\n16220   4    428Ch (total length of the remaining blocks)\r\n16224  400h  16-byte records (unknown purpose)\r\n16624   4    ?\r\n16628 3E80h  object data : 1000 records of 16 bytes (one per object)\r\n\r\nThe object data itself consists of a 16-byte record per object. The record\r\nstructure is the following :\r\n\r\noffset len data\r\n------ --- ----\r\n00      4  reference node\r\n04      1  bitmap number\r\n05      1  flip\r\n06      4  flags (unknown purpose)\r\n0A      2  relative x coordinate\r\n0C      2  relative z coordinate\r\n0E      2  relative y coordinate\r\n\r\nEach object is related to a reference node in the virtual road. The x,z,y\r\ncoordinates are then expressed as signed 16-bit values relative to the\r\ncoordinates of the reference node. Beware that the axes are simply translated\r\nbut not rotated ! (i.e. the x and y axes are still pointing east and north)\r\nThe objects are sorted in the order of increasing reference nodes.\r\nA reference node value of -1 indicates that the record is unused (i.e. after\r\nthe end of the used records). Also note that the coordinates are not\r\nexpressed in the same unit as the 32-bit absolute coordinates seen above\r\n(the units are much larger, so that the value fits in 16 bits).\r\n\r\nThe 8-bit flip value is equal to 0 for an object that is perfectly perpendi-\r\ncular to the track (e.g. a road sign), larger values for objects that are\r\nslightly turned, until 64 for an object that is mapped along the track\r\n(e.g. an ad on the side of the road), then up to 128 which is the perfectly\r\nreversed position (since the objects have no \"back\", this is the common\r\nway of reversing a road sign for a turn in the other direction), then\r\nup to 192 which is again longitudinal mapping (the other way) and until\r\n255 which is back to the normal position.\r\n\r\nThe bitmap number corresponds to a texture in the corresponding .FAM file\r\n(see B.8). The relevant bitmaps are in the second chunk of the .FAM file.\r\nTwo cases can occur :\r\n\r\n- closed tracks : the second chunk is a 'wwww' structure containing a\r\nsingle subchunk which is in turn a SHPI directory where the entry corres-\r\nponding to bitmap #n is called \"nn00\" where nn is n written in decimal.\r\n(e.g. bitmap #18 is \"1800\"). Furthermore the object called \"!pal\" or \"!PAL\",\r\nwhen it exists, is the corresponding palette (256 3-byte entries) ; FFh is\r\ntransparent.\r\n\r\n- open roads : the second chunk contains a subchunk per bitmap, and each\r\nsubchunk is a SHPI containing at least the object \"0000\" (the bitmap), and\r\npossibly a palette (\"!pal\" or \"!PAL\"). Object #n is then the bitmap \"0000\"\r\nin the subchunk #n (the first subchunk is #0).\r\n\r\nOne must add to these 2D objects (plain bitmaps) the 3D objects described\r\nin the fourth chunk of the .FAM file. They usually correspond to numbers\r\nabove the last 2D object ; however it happens, in closed tracks, that some\r\nof the 3D objects are given numbers inside the range used by 2D objects.\r\nIn that case, the numbers describing 2D objects are shifted upwards.\r\n(i.e. the bitmap \"4400\" corresponds to object #45 or #46). This phenomenon\r\napparently does not occur for open roads, where the 3D objects always follow\r\nthe 2D objects.\r\n\r\nFurthermore, certain consecutive bitmaps represent successive states of an\r\nanimated object. In that case, the game will display successively the\r\nrelevant bitmaps. Note that if the second bitmap is given instead of the\r\nfirst, the animation does not occur.\r\n\r\nd) The scenery data starts at offset 1A4A8h. It is made up of records of\r\nsize 120h, each corresponding to four nodes in the virtual road. The\r\nrecords are consecutive and the last record ends the .TRI file.\r\nEach record has the following structure :\r\n\r\noffset len data\r\n------ --- ----\r\n000     4   'TRKD'\r\n004     4   114h = length of the record contents\r\n008     4   00000000h\r\n00C     2   ?\r\n00E    10   textures\r\n018    12   reference point\r\n024     6   point A0\r\n02A     6   point A1\r\n...    ..   ........\r\n05C     6   point A9\r\n060     6   point A10\r\n066     6   point B0\r\n06C     6   point B1\r\n...    ..   ........\r\n09E     6   point B9\r\n0A2     6   point B10\r\n0A8     6   point C0\r\n0AE     6   point C1\r\n...    ..   ........\r\n0DE     6   point C9\r\n0E4     6   point C10\r\n0EA     6   point D0\r\n0F0     6   point D1\r\n...    ..   ........\r\n120     6   point D9\r\n126     6   point D10\r\n12C     6   point E0\r\n130     6   point E1\r\n...    ..   ........\r\n162     6   point E9\r\n168     6   point E10\r\n\r\nEach point is given by three signed 16-bit relative coordinates (x,z,y as\r\nusual). The coordinates are in the same reference frame as in the virtual\r\nroad data. The coordinates are relative to the virtual track point as given\r\nin the virtual track data.\r\n\r\nThe points A0,...,A10 in record #n (starting with 0) correspond to the node\r\n#4n (starting with 0) in the virtual road data. B0,...,B10 correspond to\r\nnode #4n+1, C0...C10 to node #4n+2, D0...D10 to node #4n+3 and E0...E10 to\r\nnode #4n+4. Thus the points E0...E10 are identical to the points A0...A10\r\nof the following record.\r\n\r\nThe eleven point series (0 to 10) are arranged as follows : A0-E0 are near\r\nthe middle of the road, and thus close to the corresponding nodes.\r\nA1-E1 are a little to the right, A2-E2 further right, ... until A5-E5.\r\nA6-E6 are a little to the left, A7-E7 further left, ... until A10-E10.\r\n(In tunnels, the points A5-E5 and A10-E10 get back to the center, consti-\r\ntuting the ceiling).\r\n\r\nTo each record correspond ten textures (coded at the beginning), each given\r\nby a 8-bit value, T1,T2,...,T10. T1 is used between A0-E0 and A1-E1,\r\nT2 between A1-E1 and A2-E2, ..., T5 between A4-E4 and A5-E5 ; meanwhile,\r\nT6 is used between A0-E0 and A6-E6, T7 between A6-E6 and A7-E7, ...,\r\nT10 between A9-E9 and A10-E10. This is summarized on the following diagram :\r\n\r\nE10---E9---E8---E7---E6---E0---E1---E2---E3---E4---E5  node 4n+4\r\n |    |    |    |    |    ||    |    |    |    |    |\r\n |    |    |    |    |    ||    |    |    |    |    |\r\nD10   D9   D8   D7   D6   D0   D1   D2   D3   D4   D5  node 4n+3\r\n |  T |  T |  T |  T |  T || T  | T  | T  | T  | T  |\r\n |    |    |    |    |    ||    |    |    |    |    |\r\nC10   C9   C8   C7   C6   C0   C1   C2   C3   C4   C5  node 4n+2\r\n | 10 |  9 |  8 |  7 |  6 || 1  | 2  | 3  | 4  | 5  |\r\n |    |    |    |    |    ||    |    |    |    |    |\r\nB10   B9   B8   B7   B6   B0   B1   B2   B3   B4   B5  node 4n+1\r\n |    |    |    |    |    ||    |    |    |    |    |\r\n |    |    |    |    |    ||    |    |    |    |    |\r\nA10---A9---A8---A7---A6---A0---A1---A2---A3---A4---A5  node 4n\r\n                          ^\r\n       the nodes are here |\r\n\r\nThe texture numbers are converted to bitmaps in the first chunk of the\r\n.FAM file (see B.8). There are two different cases :\r\n\r\n- closed tracks : the first chunk is a 'wwww' structure which contains a \r\nsingle subchunk which is in turn a SHPI bitmap directory, possibly with a \r\npalette '!PAL' or '!pal'. There is also often a bitmap called 'ga00' or \r\n\r\n'GA00' (unknown interpretation). The names have the structure \"xxls\", where \r\nxx is a decimal value indicating the texture group, l is 'A', 'B' or 'C', \r\nand s indicates a scale ('0' is the largest, while '3'&'4' are very small). \r\nThe various scales are here to speed up the texture-mapping algorithm,\r\nanyway the only texture that is always present is with s=0.\r\nThe xx and l values correspond to a texture number n in the following way :\r\nn=3xx if l='A', 3xx+1 if l='B' and 3xx+2 if l='C'. Note that there are holes\r\nin the numbering : many numbers do not have a bitmap.\r\nExamples : bitmap \"03C0\" corresponds to texture #11 (3x3+2) at the largest\r\nscale; bitmap \"14A1\" corresponds to texture #42 (3x14) at the second scale\r\navailable.\r\n\r\n- open roads : the first chunk contains a subchunk per texture group (i.e.\r\nthe xx value is now the number of the subchunk, starting with 0). Each\r\nsubchunk is a SHPI directory containing potentially a palette, and bitmaps\r\nlabelled \"l00s\", where l is 'A','B' or 'C' and s is the scale.\r\nAs before, n=3xx if l='A', 3xx+1 if l='B', 3xx+2 if l='C', and there are\r\nholes in the numbering.\r\nExamples : texture #11 at scale '0' is now the bitmap \"C000\" in subchunk #3.\r\nTexture #42 at scale '1' is now the bitmap called \"A001\" in subchunk #14.\r\n\r\nF - APPENDIX  -- Compression Formats\r\n    ================================\r\n\r\nCompressed data files start with a 5 byte header.\r\n\r\noffset len  data\r\n------ ---  ----\r\n000     1   Pack Code Hi byte\r\n001     1   Pack Code Lo byte\t\t(== FBh or 32h)\r\n002     1   Expanded length Hi byte\r\n003     1   Expanded length Mid byte\r\n004     1   Expanded length Lo byte\r\n\r\nIf bit 0 of the pack code Hi byte (ie Offset 0 & 0x01) is set, then there\r\nare now 3 padding bytes to allow the data to begin on a 32 bit boundary.\r\nOtherwise, the data starts at offset 5. The interpretation of the data is\r\ndifferent for different pack codes. In the description that follows, it is\r\nassumed that bit zero of the pack code Hi byte has been cleared.\r\n\r\n\r\nF.1  Pack code == 10FBh, or 1032h\r\n     ----------------------------\r\n\r\nThis is the pack code used by the .QFS files, and it indicates LZ77\r\ncompression has been used.\r\n\r\nTo decode, we read and decode chunks as per the following C code:\r\n\r\n/*************************************************************************\r\n\r\n  Function:  ReadPackFile (CFile&)\r\n\r\n   Purpose:  Reads in and unpacks the specified file\r\n\r\n   Returns:  A pointer to the unpacked data if successful.\r\n\r\n  Comments:  A null pointer is returned on error\r\n\r\n*************************************************************************/\r\n\r\n#include \"stdafx.h\"\r\n#include \"pack.h\"\r\n\r\n// Note that we rely here on the characteristics of an\r\n// overlapping 'copy up' of bytes. Hence we cannot use\r\n// the memcpy library function.\r\nunsigned char* ReadPackFile(CFile& file)\r\n{\r\n\tint filesize = file.GetLength();\r\n\tunsigned char *pSourceData = (unsigned char *)malloc(filesize);\r\n\tif (NULL == pSourceData) return(NULL);\r\n\r\n\tint bytesread = file.Read(pSourceData, filesize);\r\n\tif (bytesread != filesize){\r\n\t\tfree(pSourceData);\r\n\t\treturn(NULL);\r\n\t}\r\n\r\n\tint PackCode = (pSourceData[0]&0xfe)*256 + pSourceData[1];\r\n\tif (PackCode != 0x10fb){\r\n\t\tfree(pSourceData);\r\n\t\treturn(NULL);\t\t\t// Invalid pack code\r\n\t}\r\n\tint ExpandedLength = (pSourceData[2] << 16) + (pSourceData[3] << 8) + pSourceData[4];\r\n\tint filepos = 5;\r\n\tint TargetOffset = 0;\r\n\tif (pSourceData[0] & 0x01) filepos = 8;\t\t// align if necessary.\r\n\r\n\tunsigned char *pExpandedData = (unsigned char *)malloc(ExpandedLength);\r\n\tif (pExpandedData){\r\n\t\twhile(filepos < filesize && pSourceData[filepos] < 0xfc){\r\n\t\t\tunsigned char pack_byte = pSourceData[filepos];\r\n\t\t\tint a = pSourceData[filepos+1];\r\n\t\t\tint b = pSourceData[filepos+2];\r\n\t\t\tif (!(pack_byte & 0x80)){\r\n\t\t\t\tint len = pack_byte&0x03;\r\n\t\t\t\tunsigned char *pDest = pExpandedData+TargetOffset;\r\n\t\t\t\tunsigned char *pSrc = pSourceData+filepos+2;\r\n\t\t\t\tTargetOffset += len;\r\n\t\t\t\tfilepos += (len+2);\r\n\t\t\t\twhile (len--) *pDest++ = *pSrc++;\r\n\t\t\t\tlen = ((pack_byte & 0x1c)>>2) + 3;\r\n\t\t\t\tint offset = (pack_byte >> 5) + a + 1;\r\n\t\t\t\tpDest = pExpandedData + TargetOffset;\r\n\t\t\t\tpSrc = pDest-offset;\r\n\t\t\t\tTargetOffset += len;\r\n\t\t\t\twhile (len--) *pDest++ = *pSrc++;\r\n\t\t\t}\r\n\t\t\telse if (!(pack_byte & 0x40)){\r\n\t\t\t\tint len = (a >> 6) &0x03; \r\n\t\t\t\tunsigned char *pDest = pExpandedData + TargetOffset;\r\n\t\t\t\tunsigned char *pSrc = pSourceData+filepos+3;\r\n\t\t\t\tfilepos += (len+3);\r\n\t\t\t\tTargetOffset += len;\r\n\t\t\t\twhile (len--) *pDest++ = *pSrc++;\r\n\t\t\t\tint offset = (a&0x3f)*256 + b + 1;\r\n\t\t\t\tpDest = pExpandedData + TargetOffset;\r\n\t\t\t\tpSrc = pDest-offset;\r\n\t\t\t\tlen = (pack_byte & 0x3f)+4;\r\n\t\t\t\tTargetOffset += len;\r\n\t\t\t\twhile (len--) *pDest++ = *pSrc++;\r\n\t\t\t}\r\n\t\t\telse if (!(pack_byte & 0x20)){\r\n\t\t\t\tint c = pSourceData[filepos+3];\r\n\t\t\t\tint len = (pack_byte & 0x03);\r\n\t\t\t\tunsigned char *pDest = pExpandedData + TargetOffset;\r\n\t\t\t\tunsigned char *pSrc = pSourceData+filepos+4;\r\n\t\t\t\tfilepos += (len+4);\r\n\t\t\t\tTargetOffset += len;\r\n\t\t\t\twhile (len--) *pDest++ = *pSrc++;\r\n\t\t\t\tint offset = ((pack_byte & 0x10)<<0x0c) + 256*a + b + 1;\r\n\t\t\t\tpDest = pExpandedData + TargetOffset;\r\n\t\t\t\tpSrc = pDest-offset;\r\n\t\t\t\tlen = ((pack_byte >> 2)&0x03)*256+c+5;\r\n\t\t\t\tTargetOffset += len;\r\n\t\t\t\twhile (len--) *pDest++ = *pSrc++;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tint len = (pack_byte&0x1f)*4+4;\r\n\t\t\t\tunsigned char *pDest = pExpandedData + TargetOffset;\r\n\t\t\t\tunsigned char *pSrc = pSourceData+filepos+1;\r\n\t\t\t\tfilepos += (len+1);\r\n\t\t\t\tTargetOffset += len;\r\n\t\t\t\twhile (len--) *pDest++ = *pSrc++;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (filepos < filesize && TargetOffset < ExpandedLength){\r\n\t\t\t\tunsigned char *pDest = pExpandedData + TargetOffset;\r\n\t\t\t\tunsigned char *pSrc = pSourceData+filepos+1;\r\n\t\t\t\tint len = pSourceData[filepos]&0x03;\r\n\t\t\t\twhile (len--) *pDest++ = *pSrc++;\r\n\t\t}\r\n\t}\r\n\tfree(pSourceData);\r\n\treturn(pExpandedData);\r\n}\r\n\r\n\r\n"
  },
  {
    "path": "readme.md",
    "content": "## OpenNFS1\r\nOpenNFS1 is a ground-up remake of the original EA Need for Speed 1. The code is all written from scratch without reverse engineering executables, and it uses the original data files that were on the CD back in 1995! The format of the various binary data files was worked out by Ian Brown, Denis Auroux and myself.\r\n\r\n## Main features\r\n  * Written in C# and XNA. (Now converted to Monogame)\r\n  * 16 tracks with scenery, animations, tunnels\r\n  * 9 drivable cars with animated dashboards\r\n  * Can drive tracks backwards (not allowed in the original)\r\n  * Can drive past the finish signs on open road stages to see the real end of the track (probably never seen by anyone except by the original developers!)\r\n\r\n## Requires\r\n  * OpenAL\r\n  * Monogame >= 3.2.0\r\n  * .NET 4\r\n\r\n### Build from source\r\n```\r\ngit clone https://github.com/jeff-1amstudios/OpenNFS1.git\r\n```\r\nOpen OpenNFS1.sln in Visual Studio\r\n\r\n### Installer\r\nYou can also install the last stable binary from the [Releases page](https://github.com/jeff-1amstudios/OpenNFS1/releases)\r\n\r\n## Legal:\r\nModels, textures, tracks, cars by Pioneer Productions / EA Seattle (C) 1995.\r\nOpenNFS1 is not affiated in any way with EA or Pioneer Productions\r\n"
  },
  {
    "path": "reverse_engineering.txt",
    "content": "2  left lane /\r\n1 nothing\r\n0 left lane \\\r\n3 nothing (default)\r\n\r\nLost Vegas:\r\nInside (ambient noise): 17,17,0,4\r\nNormal: 17,0,0,3\r\nCobbled road: 17,0,0,5\r\nTwisty section: 17,1,0,3\r\n\r\nAlpine:\r\nNormal: 17,17,34,3\r\nSome bits: 17,1,34,3\r\nLane change: 17,17,34,0\r\nWide lane: \t18,17,34,1 ... some 18,17,34,1\r\nUphill wide lane: 18,17,34,16\r\nIn tunnel: 17,0,0,4\r\n\r\nCoastal 1 node 288 - 18,17,2,1\r\n\r\nx,x,0,0 move A6-A8 verts 1 to the right if the associated terrainRow is #1 in its segment\r\nx,x,0,1 no effect\r\nx,x,0,2 move A6-A8 verts 1 to the right if the associated terrainRow is last in its segment\r\nx,x,0,3 no effect\r\nx,x,0,4 play tunnel sound effect\r\n\t  5 cobbled road sound effect (Lost Vegas)\r\n      6 no effect\r\n\t  7 tunnel take last right strip and put it between A2-A9\r\nx,x,0,8 .. left wall appeared to move across the track - snapped back as we got closer\r\nx,x,0,9 tunnel (vertigo track) take last left strip and put it between A7-A4\r\nx,x,0,10 no effect\r\nx,x,0,11 no effect\r\nx,x,0,12 tunnel (vertigo track) take last left strip and put it between A9-A4\r\nx,x,0,13 tunnel - take last left strip and put it between A9-A5\r\n\t  14 waterfall (autumn valley) left channel\r\n\t  15 waterfall (autumn valley) right channel\r\nx,x,0,16 no effect\r\n\t  17 water left channel\r\n      18 water right channel\r\nx,x,0,32 crash\r\n\r\n20,x,x,x - distance cars should drive from boundary?\r\n10 - cars going forwards all in center of road, no cars going backwards\r\n5 - same as 10\r\n\r\nTunnels:\r\n\r\nNot based on vertex locations\r\nNot based on textureId\r\n\r\nAppears to join the last / 2nd last left to second-last right A8 - A4"
  }
]