CSC8503_Advanced_Game_Technologies / CSC8503 / GameTech / RacecourseSingleGame.cpp
RacecourseSingleGame.cpp
Raw
#include "RacecourseSingleGame.h"
#include "../CSC8503Common/GameWorld.h"
#include "../../Plugins/OpenGLRendering/OGLMesh.h"
#include "../../Plugins/OpenGLRendering/OGLShader.h"
#include "../../Plugins/OpenGLRendering/OGLTexture.h"
#include "../../Common/TextureLoader.h"

#include "../CSC8503Common/PositionConstraint.h"
#include "../CSC8503Common/HingeConstraint.h"
#include "../CSC8503Common/FixedHeightConstraint.h"

#include <string>

using namespace NCL;
using namespace CSC8503;

RacecourseSingleGame::RacecourseSingleGame() {
	world = new GameWorld();
	renderer = new GameTechRenderer(*world);
	physics = new PhysicsSystem(*world);

	forceMagnitude = 100.0f;
	useGravity = true;
	inSelectionMode = false;

	Debug::SetRenderer(renderer);

	InitialiseAssets();
}

void RacecourseSingleGame::InitialiseAssets() {
	auto loadFunc = [](const string& name, OGLMesh** into) {
		*into = new OGLMesh(name);
		(*into)->SetPrimitiveType(GeometryPrimitive::Triangles);
		(*into)->UploadToGPU();
	};

	loadFunc("cube.msh", &cubeMesh);
	loadFunc("sphere.msh", &sphereMesh);
	loadFunc("Male1.msh", &charMeshA);
	loadFunc("courier.msh", &charMeshB);
	loadFunc("security.msh", &enemyMesh);
	loadFunc("coin.msh", &bonusMesh);
	loadFunc("capsule.msh", &capsuleMesh);

	basicTex = (OGLTexture*)TextureLoader::LoadAPITexture("checkerboard.png");
	basicShader = new OGLShader("GameTechVert.glsl", "GameTechFrag.glsl");

	InitCamera();
	InitWorld();
}

RacecourseSingleGame::~RacecourseSingleGame() {
	delete cubeMesh;
	delete sphereMesh;
	delete charMeshA;
	delete charMeshB;
	delete enemyMesh;
	delete bonusMesh;

	delete basicTex;
	delete basicShader;

	delete physics;
	delete renderer;
	delete world;
}

void RacecourseSingleGame::UpdateGame(float dt) {
	EndGame(dt);

	if (levelCompleted == false) {
		endGameTimer += dt;
		if (endGameTimer >= 1.0f) {
			endGameTimer = 0.0f;
			playerCharacter->SetScore(playerCharacter->GetScore() - 10);
		}

		levelTimer += dt;

		//Restart position of a falling player
		if (playerCharacter->GetTransform().GetPosition().y <= -10) {
			playerCharacter->GetTransform().SetPosition(Vector3(0, 1, 30));
		}

		if (!inSelectionMode) {
			world->GetMainCamera()->UpdateCamera(dt);
		}

		CameraMovement();

		UpdateKeys(dt);

		MovePlayerCharacter(dt);

		playerCharacter->UpdatePlayerAbility(dt);

		//for (std::vector<GameObject*>::iterator i = movablePlatforms.begin(); i != movablePlatforms.end(); i++) {
		//	if ((*i)->GetTransform().GetPosition().x >= 13) {
		//		(*i)->GetTransform().SetPosition(Vector3(
		//			(*i)->GetTransform().GetPosition().x - dt,
		//			(*i)->GetTransform().GetPosition().y,
		//			(*i)->GetTransform().GetPosition().z));
		//	}
		//	if ((*i)->GetTransform().GetPosition().x <= -13) {
		//		(*i)->GetTransform().SetPosition(Vector3(
		//			(*i)->GetTransform().GetPosition().x + dt,
		//			(*i)->GetTransform().GetPosition().y,
		//			(*i)->GetTransform().GetPosition().z));
		//	}

		//}


		//for (GameObject*& object : movablePlatforms) {

		//	if (object->GetTransform().GetPosition().x >= 15) {
		//		object->GetTransform().SetPosition(Vector3(
		//			object->GetTransform().GetPosition().x - dt,
		//			object->GetTransform().GetPosition().y,
		//			object->GetTransform().GetPosition().z));
		//			}
		//			if (object->GetTransform().GetPosition().x <= -15) {
		//				object->GetTransform().SetPosition(Vector3(
		//					object->GetTransform().GetPosition().x + dt,
		//					object->GetTransform().GetPosition().y,
		//					object->GetTransform().GetPosition().z));
		//			}

		//}

		if (snowObject) {
			snowObject->Update(dt);
		}

		SelectObject();
		//MoveSelectedObject();

	}
		physics->Update(dt);

		world->UpdateWorld(dt);
		renderer->Update(dt);

		renderer->Render();

		DrawDisplay(dt);
}

void RacecourseSingleGame::UpdateKeys(float dt) {
	if (Window::GetKeyboard()->KeyPressed(KeyboardKeys::F1)) {
		InitWorld(); //We can reset the simulation at any time with F1
		selectionObject = nullptr;
		lockedObject = nullptr;
		levelTimer = 0.0f;
	}

	if (Window::GetKeyboard()->KeyPressed(KeyboardKeys::F2)) {
		InitCamera(); //F2 will reset the camera to a specific default place
	}

	if (Window::GetKeyboard()->KeyPressed(KeyboardKeys::G)) {
		useGravity = !useGravity; //Toggle gravity!
		physics->UseGravity(useGravity);
	}
	//Running certain physics updates in a consistent order might cause some
	//bias in the calculations - the same objects might keep 'winning' the constraint
	//allowing the other one to stretch too much etc. Shuffling the order so that it
	//is random every frame can help reduce such bias.
	if (Window::GetKeyboard()->KeyPressed(KeyboardKeys::F9)) {
		world->ShuffleConstraints(true);
	}
	if (Window::GetKeyboard()->KeyPressed(KeyboardKeys::F10)) {
		world->ShuffleConstraints(false);
	}

	if (Window::GetKeyboard()->KeyPressed(KeyboardKeys::F7)) {
		world->ShuffleObjects(true);
	}
	if (Window::GetKeyboard()->KeyPressed(KeyboardKeys::F8)) {
		world->ShuffleObjects(false);
	}

	//if (lockedObject) {
	//	LockedObjectMovement();
	//}
	//else {
	//	DebugObjectMovement();
	//}
}

void  RacecourseSingleGame::CameraMovement() {
	Vector3 objPos = playerCharacter->GetTransform().GetPosition();
	Vector3 camPos = objPos + playerCharacter->GetTransform().GetOrientation() * lockedOffset;

	Matrix4 temp = Matrix4::BuildViewMatrix(camPos, objPos + Vector3(0, 5, 0), Vector3(0, 1, 0));

	Matrix4 modelMat = temp.Inverse();

	Quaternion q(modelMat);
	Vector3 angles = q.ToEuler();
	 
	world->GetMainCamera()->SetPosition(camPos);
	world->GetMainCamera()->SetPitch(angles.x);
	world->GetMainCamera()->SetYaw(angles.y);
}

void RacecourseSingleGame::MovePlayerCharacter(float dt)
{
	float rotationSpeed = 60.0f;
	Vector3 pyr = playerCharacter->GetTransform().GetOrientation().ToEuler();

	if (Window::GetKeyboard()->KeyDown(KeyboardKeys::W)) {
		playerCharacter->SetSleep(false);
		playerCharacter->GetPhysicsObject()->AddForce(playerCharacter->GetTransform().GetOrientation() * Vector3(0, 0, -1) * forceMagnitude * playerCharacter->GetSpeedMultiplier());
	}
	if (Window::GetKeyboard()->KeyDown(KeyboardKeys::A)) {
		playerCharacter->SetSleep(false);
		pyr.y += rotationSpeed * dt;
		pyr.y = pyr.y >= 0.0f ? pyr.y <= 360.0f ? pyr.y : pyr.y - 360.0f : pyr.y + 360.0f;
		playerCharacter->GetTransform().SetOrientation(Quaternion::EulerAnglesToQuaternion(pyr.x, pyr.y, pyr.z));
	}
	if (Window::GetKeyboard()->KeyDown(KeyboardKeys::S)) {
		playerCharacter->SetSleep(false);
		playerCharacter->GetPhysicsObject()->AddForce(playerCharacter->GetTransform().GetOrientation() * Vector3(0, 0, 1) * forceMagnitude * playerCharacter->GetSpeedMultiplier());
	}
	if (Window::GetKeyboard()->KeyDown(KeyboardKeys::D)) {
		playerCharacter->SetSleep(false);
		pyr.y -= rotationSpeed * dt;
		pyr.y = pyr.y >= 0.0f ? pyr.y <= 360.0f ? pyr.y : pyr.y - 360.0f : pyr.y + 360.0f;
		playerCharacter->GetTransform().SetOrientation(Quaternion::EulerAnglesToQuaternion(pyr.x, pyr.y, pyr.z));
	}
	if (Window::GetKeyboard()->KeyDown(KeyboardKeys::SPACE)) {
		playerCharacter->SetSleep(false);
		//if (jumpTimer <= 0.0) {
		playerCharacter->GetPhysicsObject()->AddForce(Vector3(0, 2, 0) * forceMagnitude);
		jumpTimer += 2.0f;
		//	}
	}
	if (Window::GetKeyboard()->KeyPressed(NCL::KeyboardKeys::F))
	{
		if (playerCharacter->GetPlayerAttackCD() <= 0)
		{
			playerCharacter->GetPlayerAttackCD() = 5.0f;
			PlayerAbillity();
		}
	}
}

void RacecourseSingleGame::PlayerAbillity() {
	for (GameObject*& object : movables) {
			Vector3 direction = object->GetTransform().GetPosition() - playerCharacter->GetTransform().GetPosition();
			if (direction.Length() < playerCharacter->GetPlayerkRange()) {
					direction.Normalise();
			Ray* ray = new Ray(playerCharacter->GetTransform().GetPosition(), direction);
			RayCollision collision;
			if (world->Raycast(*ray, collision, true))	{
				object->GetPhysicsObject()->AddForceAtPosition(direction * playerCharacter->GetPlayerForce(), collision.collidedAt);
			}
			delete ray;
		}
	}
}

void RacecourseSingleGame::InitCamera()
{
	world->GetMainCamera()->SetNearPlane(0.5f);
	world->GetMainCamera()->SetFarPlane(2000.0f);
	world->GetMainCamera()->SetPitch(-15.0f);
	world->GetMainCamera()->SetYaw(315.0f);
	world->GetMainCamera()->SetPosition(Vector3(-60, 40, 60));
	lockedObject = nullptr;
}

void RacecourseSingleGame::InitWorld() {
	world->ClearAndErase();
	physics->Clear();
	forceMagnitude = 50.0f;
	obstacles.clear();
	movables.clear();
	movableDoors.clear();
	obstacleDoors.clear();
	obstacleMaze.clear();
	bonus.clear();

	//Setup
	Vector3 floorPosition(0, -2, 0);
	Vector3 floorSize(30, 2, 37.5);
	Vector3 wallSize(2, 50, 500);
	Vector4 floorColour(1, 1, 1, 1);
	Vector4 terrainColour(0, 0, 1, 1);


	//Player Starting Position
	playerCharacter = AddPlayerToWorld(Vector3(0,1,30));
	//Player First save point
	//playerCharacter = AddPlayerToWorld(Vector3(0, 9, -257));
	//Player Second save point
	//playerCharacter = AddPlayerToWorld(Vector3(0, 9, -315));

	//playerCharacter = AddPlayerToWorld(Vector3(0, 9, -430));

	//playerCharacter = AddPlayerToWorld(Vector3(0, 9, -630));

	playerCharacter->AddScore(1000);

	//Wall Right
	obstacles.push_back(AddWallToWorld(Vector3(floorSize.x + 1, floorSize.y + 12, (-wallSize.z/2 - 110)), Vector3(1,14,400), Vector4(0.5,0.5,0.5, 1)));
	// Wall Left
	obstacles.push_back(AddWallToWorld(Vector3(-floorSize.x - 1, floorSize.y + 12, (-wallSize.z / 2 - 110)), Vector3(1, 14, 400), Vector4(0.5, 0.5, 0.5, 1)));
	//Add starting location
	AddFloorToWorld(floorPosition, floorSize , floorColour, CollisionResolution::Impulse);
	//OBB with Sphere collision
	movables.push_back(AddSphereToWorld(Vector3(0, 2, 20), 1, 0.5f));
	AddRampToWorld(Vector3(-15, 3, -37.5), Vector3(8,0.5,2), Vector3(15, 50, 0), 0);
	movables.push_back(AddSphereToWorld(Vector3(-12, 5, -38), 1, 0.5f));
	AddRampToWorld(Vector3(0, 3, -37.5), Vector3(15, 0.5, 2), Vector3(0, 90, 5), 0);
	movables.push_back(AddSphereToWorld(Vector3(0, 6, -38), 1, 0.5f));
	//Add starting Constraint obstacle
	AddGateToWorld(Vector3(-8, 2, 10),  Vector3(0, 0, 0));
	//Add Stairs
	AddFloorToWorld(Vector3(0,0,-47.5), Vector3(30,2,10), floorColour, CollisionResolution::Impulse);
	AddFloorToWorld(Vector3(0,2,-67), Vector3(30,2,10), floorColour, CollisionResolution::Impulse);
	AddFloorToWorld(Vector3(0,4,-87), Vector3(30,2,10), floorColour, CollisionResolution::Impulse);
	AddFloorToWorld(Vector3(0,6,-132), Vector3(30,2,35), floorColour, CollisionResolution::Impulse);
	//Add Sphere Obstacles on Stairs
	movables.push_back(AddSphereToWorld(Vector3(14, 15, -93), 1, 0.5f));
	movables.push_back(AddSphereToWorld(Vector3(0, 15, -93), 1, 0.5f));
	movables.push_back(AddSphereToWorld(Vector3(-14, 15, -93), 1, 0.5f));
	//Add Jelly
	AddJellyToWorld(Vector3(0, 7, -155.5));
	AddCubeToWorld(Vector3(0, 10, -155.5), Vector3(2, 2, 2), 0.5f);
	//Water (with OBB platforms)
	Vector3 waterPosition(0, 6, -220);
	Vector3 waterSize(30, 2, 35);
	AddFloorToWorld(waterPosition, waterSize, terrainColour, CollisionResolution::Spring);
	AddFloorToWorld(waterPosition, waterSize - Vector3(0, 2, 0), terrainColour, CollisionResolution::Impulse);
	AddRampToWorld(waterPosition + Vector3(0, 5, 20), waterSize / 4, Vector3(15, 50, 0), 0);
	AddRampToWorld(waterPosition + Vector3(0, 5, -20), waterSize / 4, Vector3(-15, -50, 0), 0);
	//Add Floor with Doors (movable and non movable cubes)
	//Middle door
	AddFloorToWorld(Vector3(0, 6, -290), Vector3(30, 2, 35), floorColour, CollisionResolution::Impulse);
	movableDoors.push_back(AddCubeToWorld(Vector3(0, 7, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(0, 11, -290), Vector3(2, 2, 2), 0.5f,	Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(0, 15, -290), Vector3(2, 2, 2), 0.5f,	Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(-4, 7, -290), Vector3(2, 2, 2), 0.5f,	Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(-4, 11, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(-4, 15, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(4, 7, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(4, 11, -290), Vector3(2, 2, 2), 0.5f,	Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(4, 15, -290), Vector3(2, 2, 2), 0.5f,	Vector4(0.7, 0, 0.7, 1)));
	obstacleDoors.push_back(AddCubeToWorld(Vector3(-8, 14, -290), Vector3(2, 6, 2), 0.0f, Vector4(0, 1, 1, 1)));
	obstacleDoors.push_back(AddCubeToWorld(Vector3(0, 21, -290), Vector3(10, 1, 2), 0.0f, Vector4(0, 1, 1, 1)));
	obstacleDoors.push_back(AddCubeToWorld(Vector3(8, 14, -290), Vector3(2, 6, 2), 0.0f, Vector4(0, 1, 1, 1)));
	//Left door
	movableDoors.push_back(AddCubeToWorld(Vector3(-24, 7, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(-24, 11, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(-24, 15, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(-20, 7, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(-20, 11, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(-20, 15, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(-16, 7, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(-16, 11, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(-16, 15, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	obstacleDoors.push_back(AddCubeToWorld(Vector3(-28, 14, -290), Vector3(2, 6, 2), 0.0f, Vector4(0, 0.2, 0.6, 1)));
	obstacleDoors.push_back(AddCubeToWorld(Vector3(-20, 21, -290), Vector3(10, 1, 2), 0.0f, Vector4(0, 0.2, 0.6, 1)));
	obstacleDoors.push_back(AddCubeToWorld(Vector3(-12, 14, -290), Vector3(2, 6, 2), 0.0f, Vector4(0, 0.2, 0.6, 1)));
	//Right door	
	movableDoors.push_back(AddCubeToWorld(Vector3(24, 7, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(24, 11, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(24, 15, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(20, 7, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(20, 11, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(20, 15, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(16, 7, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(16, 11, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	movableDoors.push_back(AddCubeToWorld(Vector3(16, 15, -290), Vector3(2, 2, 2), 0.5f, Vector4(0.7, 0, 0.7, 1)));
	obstacleDoors.push_back(AddCubeToWorld(Vector3(28, 14, -290), Vector3(2, 6, 2), 0.0f, Vector4(0, 0.2, 0.6, 1)));
	obstacleDoors.push_back(AddCubeToWorld(Vector3(20, 21, -290), Vector3(10, 1, 2), 0.0f, Vector4(0, 0.2, 0.6, 1)));
	obstacleDoors.push_back(AddCubeToWorld(Vector3(12, 14, -290), Vector3(2, 6, 2), 0.0f, Vector4(0, 0.2, 0.6, 1)));
	//Add Floor with non-movable obstacles (Maze)
	AddFloorToWorld(Vector3(0, 6, -395), Vector3(30, 2, 70), floorColour, CollisionResolution::Impulse);
	obstacleMaze.push_back(AddCubeToWorld(Vector3(-17.5, 14, -360), Vector3(12.5, 6, 2), 0.0f));
	obstacleMaze.push_back(AddCubeToWorld(Vector3(17.5, 14, -360), Vector3(12.5, 6, 2), 0.0f));
	//Right Maze Wall
	obstacleMaze.push_back(AddCubeToWorld(Vector3(7, 14, -387), Vector3(2, 6, 25), 0.0f, Vector4(1, 1, 1, 0.5)));
	//Left Maze Wall
	obstacleMaze.push_back(AddCubeToWorld(Vector3(-7, 14, -387), Vector3(2, 6, 25), 0.0f, Vector4(1, 1, 1, 0.5)));
	//obstacleMaze.push_back(AddCubeToWorld(Vector3(-7, 14, -382), Vector3(2, 6, 20), 0.0f, Vector4(1, 1, 1, 0.5)));
	//
	obstacleMaze.push_back(AddCubeToWorld(Vector3(17.5, 14, -414), Vector3(12.5, 6, 2), 0.0f, Vector4(1, 1, 1, 0.5)));
	obstacleMaze.push_back(AddCubeToWorld(Vector3(-17.5, 14, -414), Vector3(12.5, 6, 2), 0.0f, Vector4(1, 1, 1, 0.5)));
	//Add Snow Floor
	snowObject = AddSnowFloorToWorld(Vector3(0, 6, -540), Vector3(30, 1.5, 80), Vector4(0, 1, 1, 1), CollisionResolution::Snow);
	movables.push_back(AddSphereToWorld(Vector3(-25, 8.5, -500), 1, 0.5f));
	movables.push_back(AddCubeToWorld(Vector3(0, 8.5, -540), Vector3(2, 2, 2), 0.5f, Vector4(1, 1, 1, 1)));
	movables.push_back(AddSphereToWorld(Vector3(25, 8.5, -580), 1, 0.5f));
	//Platoforms move between in x-axis -20 to 20
	movablePlatforms.push_back(AddFloorToWorld(Vector3(15, 12, -470), Vector3(5, 0.5, 5), floorColour, CollisionResolution::Impulse));
	movablePlatforms.push_back(AddFloorToWorld(Vector3(5, 14, -490), Vector3(5, 0.5, 5), floorColour, CollisionResolution::Impulse));
	movablePlatforms.push_back(AddFloorToWorld(Vector3(-5, 16, -510), Vector3(5, 0.5, 5), floorColour, CollisionResolution::Impulse));
	movablePlatforms.push_back(AddFloorToWorld(Vector3(-15, 18, -530), Vector3(5, 0.5, 5), floorColour, CollisionResolution::Impulse));
	movablePlatforms.push_back(AddFloorToWorld(Vector3(-15, 18, -550), Vector3(5, 0.5, 5), floorColour, CollisionResolution::Impulse));
	movablePlatforms.push_back(AddFloorToWorld(Vector3(5, 18, -550), Vector3(5, 0.5, 5), floorColour, CollisionResolution::Impulse));
	movablePlatforms.push_back(AddFloorToWorld(Vector3(-5, 16, -570), Vector3(5, 0.5, 5), floorColour, CollisionResolution::Impulse));
	movablePlatforms.push_back(AddFloorToWorld(Vector3(5, 14, -590), Vector3(5, 0.5, 5), floorColour, CollisionResolution::Impulse));
	movablePlatforms.push_back(AddFloorToWorld(Vector3(15, 12, -610), Vector3(5, 0.5, 5), floorColour, CollisionResolution::Impulse));
	//Add Finish Floor (Finish is at z-axis -690)
	AddFloorToWorld(Vector3(0, 6, -690), Vector3(30, 2, 70), floorColour, CollisionResolution::Impulse);
	//Non passable object with Contraints (Position, Hinge and Height) 
	AddFinishGateToWorld(Vector3(-10, 17, -688), Vector3(0, 0, 0));
	//Bonuses
	bonus.push_back(AddBonusToWorld(Vector3(0, 10, -105)));
	bonus.push_back(AddBonusToWorld(Vector3(0, 12, -165)));
	bonus.push_back(AddBonusToWorld(Vector3(0, 10, -265)));
	bonus.push_back(AddBonusToWorld(Vector3(0, 10, -440)));
	bonus.push_back(AddBonusToWorld(Vector3(0, 10, -630)));
	bonus.push_back(AddBonusToWorld(Vector3(5, 20, -550), Vector4(1.0f, 1.0f, 0.0f, 1.0f)));
}

void RacecourseSingleGame::EndGame(float dt) {

	if (playerCharacter->GetScore() <= 0) {
		DrawDisplay(dt);

		renderer->DrawString("You have lost", Vector2(45, 50));

		renderer->DrawString("Press Enter/Return to go back to main menu", Vector2(20, 60));

		levelCompleted = true;

		playerCharacter->MakeInactive();

		if (Window::GetKeyboard()->KeyDown(KeyboardKeys::RETURN)) {

			gameFinished = true;
		}
	}

	if (playerCharacter->GetTransform().GetPosition().z <= -690) {
		DrawDisplay(dt);

		renderer->DrawString("You have won" , Vector2(45, 50));

		renderer->DrawString("Press Enter/Return to go back to menu", Vector2(20, 60));

		levelCompleted = true;

		playerCharacter->MakeInactive();

		if (Window::GetKeyboard()->KeyDown(KeyboardKeys::RETURN)) {

			gameFinished = true;
		}
	}
}

GameObject* RacecourseSingleGame::AddFloorToWorld(const Vector3& position, const Vector3& scale, const Vector4& colour, const int collisionResolution) {
	GameObject* floor = new GameObject();

	AABBVolume* volume = new AABBVolume(scale);
	floor->SetBoundingVolume((CollisionVolume*)volume);

	floor->GetTransform()
		.SetScale(scale * 2)
		.SetPosition(position);

	floor->SetRenderObject(new RenderObject(&floor->GetTransform(), cubeMesh, basicTex, basicShader));
	floor->SetPhysicsObject(new PhysicsObject(&floor->GetTransform(), floor->GetBoundingVolume()));
	floor->GetPhysicsObject()->SetElasticity(0.0f);
	floor->GetPhysicsObject()->SetBuoyancy(0.0f);
	floor->GetPhysicsObject()->SetGravityAffinity(false);
	floor->GetPhysicsObject()->SetCollisionResolution(collisionResolution);

	floor->GetPhysicsObject()->SetInverseMass(0);
	floor->GetPhysicsObject()->InitCubeInertia();

	floor->GetRenderObject()->SetColour(colour);

	world->AddGameObject(floor);

	return floor;
}

PlayerObject* RacecourseSingleGame::AddPlayerToWorld(const Vector3& position) {
	float radius = 1.0f;
	float inverseMass = 0.25f;

	PlayerObject* player = new PlayerObject("Player1");

	Vector3 sphereSize = Vector3(radius, radius, radius);
	SphereVolume* volume = new SphereVolume(radius);
	player->SetBoundingVolume((CollisionVolume*)volume);

	player->GetTransform()
		.SetScale(sphereSize)
		.SetPosition(position);
	//player->GetTransform().SetOrientation(Quaternion::EulerAnglesToQuaternion(0,180,0));

	player->SetRenderObject(new RenderObject(&player->GetTransform(), charMeshB, basicTex, basicShader));
	player->SetPhysicsObject(new PhysicsObject(&player->GetTransform(), player->GetBoundingVolume()));

	player->GetPhysicsObject()->SetInverseMass(inverseMass);
	player->GetPhysicsObject()->InitSphereInertia();
	player->GetPhysicsObject()->SetBuoyancy(140);
	player->GetPhysicsObject()->SetElasticity(0.7f);
	player->GetPhysicsObject()->SetCollisionResolution(CollisionResolution::Impulse | CollisionResolution::Spring | CollisionResolution::Jelly | CollisionResolution::Snow | CollisionResolution::Collect);

	player->GetRenderObject()->SetColour(Vector4(255, 255, 0, 1));
	playerOriginalColour = Vector4(255, 255, 0, 1);
	
	world->AddGameObject(player);

	return player;
}

GameObject* RacecourseSingleGame::AddCubeToWorld(const Vector3& position, Vector3 dimensions, float inverseMass, const Vector4& colour) {
	GameObject* cube = new GameObject();

	AABBVolume* volume = new AABBVolume(dimensions);

	cube->SetBoundingVolume((CollisionVolume*)volume);

	cube->GetTransform()
		.SetScale(dimensions * 2)
		.SetPosition(position);

	cube->SetRenderObject(new RenderObject(&cube->GetTransform(), cubeMesh, basicTex, basicShader));
	cube->SetPhysicsObject(new PhysicsObject(&cube->GetTransform(), cube->GetBoundingVolume()));
	cube->GetPhysicsObject()->SetElasticity((rand() % 100 + 1) / 100.f);
	cube->GetPhysicsObject()->SetBuoyancy(rand() % 351 + 50);
	cube->GetPhysicsObject()->SetCollisionResolution(CollisionResolution::Impulse | CollisionResolution::Spring | CollisionResolution::Jelly | CollisionResolution::Snow);

	cube->GetPhysicsObject()->SetInverseMass(inverseMass);
	cube->GetPhysicsObject()->InitCubeInertia();

	//cube->SetSleep(true); // 

	cube->GetRenderObject()->SetColour(colour);

	world->AddGameObject(cube);

	return cube;
}

GameObject* RacecourseSingleGame::AddSphereToWorld(const Vector3& position, float radius, float inverseMass, const Vector4& colour) {
	string sphereName = std::to_string(sphereNr);
	
	GameObject* sphere = new GameObject();

	//++cubeNumber;

	Vector3 sphereSize = Vector3(radius, radius, radius);
	SphereVolume* volume = new SphereVolume(radius);
	sphere->SetBoundingVolume((CollisionVolume*)volume);

	sphere->GetTransform()
		.SetScale(sphereSize)
		.SetPosition(position);

	sphere->SetRenderObject(new RenderObject(&sphere->GetTransform(), sphereMesh, basicTex, basicShader));
	sphere->SetPhysicsObject(new PhysicsObject(&sphere->GetTransform(), sphere->GetBoundingVolume()));

	sphere->GetPhysicsObject()->SetElasticity((rand() % 100 + 1) / 100.f);
	sphere->GetPhysicsObject()->SetBuoyancy(rand() % 351 + 50);
	//sphere->GetPhysicsObject()->SetElasticity(0.9f);
	//sphere->GetPhysicsObject()->SetBuoyancy(100.0f);
	//sphere->GetPhysicsObject()->SetGravityAffinity(false);
	sphere->GetPhysicsObject()->SetCollisionResolution(CollisionResolution::Impulse | CollisionResolution::Spring | CollisionResolution::Jelly | CollisionResolution::Snow);

	sphere->GetPhysicsObject()->SetInverseMass(inverseMass);
	sphere->GetPhysicsObject()->InitSphereInertia();

	//sphere->GetPhysicsObject()->SetGravityAffinity(true);
	//sphere->GetPhysicsObject()->AddForce(Vector3(100,30,35) * forceMagnitude );

	sphere->GetPhysicsObject()->AddForce(Vector3(0, 0, 1) );

	sphere->GetRenderObject()->SetColour(colour);

	world->AddGameObject(sphere);

	return sphere;
}

GameObject* RacecourseSingleGame::AddWallToWorld(const Vector3& position, const Vector3& scale, const Vector4& colour)
{
	GameObject* wall = new GameObject();

	AABBVolume* volume = new AABBVolume(scale);
	wall->SetBoundingVolume((CollisionVolume*)volume);

	wall->GetTransform()
		.SetScale(scale * 2)
		.SetPosition(position);

	wall->SetRenderObject(new RenderObject(&wall->GetTransform(), cubeMesh, basicTex, basicShader));
	wall->SetPhysicsObject(new PhysicsObject(&wall->GetTransform(), wall->GetBoundingVolume()));
	wall->GetPhysicsObject()->SetElasticity(0.0f);
	wall->GetPhysicsObject()->SetBuoyancy(0.0f);
	wall->GetPhysicsObject()->SetGravityAffinity(false);
	wall->GetPhysicsObject()->SetCollisionResolution(CollisionResolution::Impulse);

	wall->GetPhysicsObject()->SetInverseMass(0);
	wall->GetPhysicsObject()->InitCubeInertia();

	wall->GetRenderObject()->SetColour(colour);

	world->AddGameObject(wall);

	return wall;
}

void RacecourseSingleGame::AddGateToWorld(const Vector3& position, const Vector3& rotation)
{
	GameObject* fencePillar1 = AddRampToWorld(position, Vector3(0.2, 2, 0.3), rotation, 0);
	GameObject* gate = AddRampToWorld(position + Quaternion::EulerAnglesToQuaternion(rotation.x, rotation.y, rotation.z) * Vector3(12, 0, 0), Vector3(11.5, 1.8, 0.2), rotation, 0.5);
	GameObject* fencePillar2 = AddRampToWorld(position + Quaternion::EulerAnglesToQuaternion(rotation.x, rotation.y, rotation.z) * Vector3(24, 0, 0), Vector3(0.2, 2, 0.3), rotation, 0);

	PositionConstraint* constraint = new PositionConstraint(fencePillar1, gate, 12);
	HingeConstraint* hinge = new HingeConstraint(fencePillar1, gate);
	FixedHeightConstraint* height = new FixedHeightConstraint(gate, gate->GetTransform().GetPosition().y);

	fencePillar1->SetSleep(true);
	gate->SetSleep(true);
	fencePillar2->SetSleep(true);

	world->AddConstraint(constraint);
	world->AddConstraint(hinge);
	world->AddConstraint(height);
}


void RacecourseSingleGame::AddFinishGateToWorld(const Vector3& position, const Vector3& rotation)
{
	GameObject* fencePillar1 = AddRampToWorld(position, Vector3(0.4, 8, 0.6), rotation, 0);
	GameObject* gate = AddRampToWorld(position + Quaternion::EulerAnglesToQuaternion(rotation.x, rotation.y, rotation.z) * Vector3(12, 2, 0), Vector3(23, 1.8, 0.2), rotation, 0.0);
	GameObject* fencePillar2 = AddRampToWorld(position + Quaternion::EulerAnglesToQuaternion(rotation.x, rotation.y, rotation.z) * Vector3(24, 0, 0), Vector3(0.4, 8, 0.6), rotation, 0);

	PositionConstraint* constraint = new PositionConstraint(fencePillar1, gate, 12);
	HingeConstraint* hinge = new HingeConstraint(fencePillar1, gate);
	HingeConstraint* hinge2 = new HingeConstraint(fencePillar2, gate);
	FixedHeightConstraint* height = new FixedHeightConstraint(gate, gate->GetTransform().GetPosition().y);

	fencePillar1->SetSleep(true);
	gate->SetSleep(true);
	fencePillar2->SetSleep(true);

	world->AddConstraint(constraint);
	world->AddConstraint(hinge);
	world->AddConstraint(hinge2);
	world->AddConstraint(height);
}

GameObject* RacecourseSingleGame::AddRampToWorld(const Vector3& position, const Vector3& scale, const Vector3& rotation, const float inverseMass)
{
	GameObject* ramp = new GameObject();

	OBBVolume* volume = new OBBVolume(scale);
	ramp->SetBoundingVolume((CollisionVolume*)volume);

	ramp->GetTransform()
		.SetScale(scale * 2)
		.SetPosition(position);

	ramp->GetTransform().SetOrientation(Quaternion::EulerAnglesToQuaternion(rotation.x, rotation.y, rotation.z));

	ramp->SetRenderObject(new RenderObject(&ramp->GetTransform(), cubeMesh, basicTex, basicShader));
	ramp->SetPhysicsObject(new PhysicsObject(&ramp->GetTransform(), ramp->GetBoundingVolume()));
	ramp->GetPhysicsObject()->SetElasticity(0.0f);
	ramp->GetPhysicsObject()->SetBuoyancy(0.0f);
	ramp->GetPhysicsObject()->SetGravityAffinity(false);
	ramp->GetPhysicsObject()->SetCollisionResolution(CollisionResolution::Impulse);

	ramp->GetPhysicsObject()->SetInverseMass(inverseMass);
	ramp->GetPhysicsObject()->InitCubeInertia();

	ramp->GetRenderObject()->SetColour(Vector4(1, 1, 1, 1));

	world->AddGameObject(ramp);

	return ramp;
}

void RacecourseSingleGame::AddJellyToWorld(const Vector3& position)
{
	AddFloorToWorld(position, Vector3(25, 1.5, 25), Vector4(1, 0, 0, 1), CollisionResolution::Jelly);
	obstacles.push_back(AddWallToWorld(position + Vector3(27.5, 1, 0), Vector3(4.7, 2.5, 22.5))); //Right
	obstacles.push_back(AddWallToWorld(position - Vector3(27.5, -1, 0), Vector3(4.7, 2.5, 22.5))); //Left
	obstacles.push_back(AddWallToWorld(position + Vector3(0, 1, 27.5), Vector3(30, 2.5, 4.7))); //Bottom
	obstacles.push_back(AddWallToWorld(position - Vector3(0, -1, 27.5), Vector3(30, 2.5, 4.7))); //Up
}

void RacecourseSingleGame::AddSnowToWorld(const Vector3& position)
{
	AddFloorToWorld(position, Vector3(30, 1.5, 80), Vector4(0, 1, 1, 1), CollisionResolution::Snow);
}

SnowObject* RacecourseSingleGame::AddSnowFloorToWorld(const Vector3& position, const Vector3& scale, const Vector4& colour, const int collisionResolution) {
	SnowObject* snowFloor = new SnowObject();

	AABBVolume* volume = new AABBVolume(scale);
	snowFloor->SetBoundingVolume((CollisionVolume*)volume);

	snowFloor->GetTransform()
		.SetScale(scale * 2)
		.SetPosition(position);

	snowFloor->SetRenderObject(new RenderObject(&snowFloor->GetTransform(), cubeMesh, basicTex, basicShader));
	snowFloor->SetPhysicsObject(new PhysicsObject(&snowFloor->GetTransform(), snowFloor->GetBoundingVolume()));
	snowFloor->GetPhysicsObject()->SetElasticity(0.0f);
	snowFloor->GetPhysicsObject()->SetBuoyancy(0.0f);
	snowFloor->GetPhysicsObject()->SetGravityAffinity(false);
	snowFloor->GetPhysicsObject()->SetCollisionResolution(collisionResolution);

	snowFloor->GetPhysicsObject()->SetInverseMass(0);
	snowFloor->GetPhysicsObject()->InitCubeInertia();

	snowFloor->GetRenderObject()->SetColour(colour);

	snowFloor->SetPlayer(playerCharacter);
	snowFloor->SetOriginalPlayerColour(playerCharacter->GetRenderObject()->GetColour());

	world->AddGameObject(snowFloor);

	return snowFloor;
}

GameObject* RacecourseSingleGame::AddBonusToWorld(const Vector3& position, const Vector4& colour) {
	BonusObject* bonus = new BonusObject(25);

	SphereVolume* volume = new SphereVolume(0.25f);
	bonus->SetBoundingVolume((CollisionVolume*)volume);
	bonus->GetTransform()
		.SetScale(Vector3(0.25, 0.25, 0.25))
		.SetPosition(position);

	bonus->SetRenderObject(new RenderObject(&bonus->GetTransform(), bonusMesh, nullptr, basicShader));
	bonus->SetPhysicsObject(new PhysicsObject(&bonus->GetTransform(), bonus->GetBoundingVolume()));

	bonus->GetPhysicsObject()->SetInverseMass(1.0f);
	bonus->GetPhysicsObject()->InitSphereInertia();

	bonus->GetRenderObject()->SetColour(colour);

	world->AddGameObject(bonus);

	return bonus;
}

bool RacecourseSingleGame::SelectObject() {
	if (Window::GetKeyboard()->KeyPressed(KeyboardKeys::Q)) {
		inSelectionMode = !inSelectionMode;
		if (inSelectionMode) {
			Window::GetWindow()->ShowOSPointer(true);
			Window::GetWindow()->LockMouseToWindow(false);
		}
		else {
			Window::GetWindow()->ShowOSPointer(false);
			Window::GetWindow()->LockMouseToWindow(true);
		}
	}
	if (inSelectionMode) {
		renderer->DrawString("Press Q to change to camera mode!", Vector2(5, 95));

		if (Window::GetMouse()->ButtonDown(NCL::MouseButtons::LEFT)) {
			if (selectionObject) {	//set colour to deselected;
				selectionObject->GetRenderObject()->SetColour(Vector4(1, 1, 1, 1));
				selectionObject = nullptr;
				lockedObject = nullptr;

			}

			Ray ray = CollisionDetection::BuildRayFromMouse(*world->GetMainCamera());

			RayCollision closestCollision;
			if (world->Raycast(ray, closestCollision, true)) {
				selectionObject = (GameObject*)closestCollision.node;
				selectionObject->GetRenderObject()->SetColour(Vector4(0, 1, 0, 1));

				renderer->DrawString("Pos x: " + std::to_string(selectionObject->GetRenderObject()->GetTransform()->GetPosition().x), Vector2(55, 5));
				renderer->DrawString("Pos y: " + std::to_string(selectionObject->GetRenderObject()->GetTransform()->GetPosition().y), Vector2(55, 10));
				renderer->DrawString("Pos z: " + std::to_string(selectionObject->GetRenderObject()->GetTransform()->GetPosition().z), Vector2(55, 15));
				renderer->DrawString("Object name: " + selectionObject->GetName(), Vector2(55, 20));
				return true;
			}
			else {
				return false;
			}
		}

	}
	else {
		renderer->DrawString("Press Q to change to select mode!", Vector2(5, 95));
	}
	return false;
}

void RacecourseSingleGame::MoveSelectedObject() {
	renderer->DrawString("Click Force:" + std::to_string(forceMagnitude),
		Vector2(5, 90));
	forceMagnitude += Window::GetMouse()->GetWheelMovement() * 10.0f;

	if (!selectionObject) {
		return; //we haven't selected anything!
	}
	//Push the selected object!
	if (Window::GetMouse()->ButtonPressed(NCL::MouseButtons::RIGHT)) {
		if (selectionObject != playerCharacter) {
			Ray ray = CollisionDetection::BuildRayFromMouse(
				*world->GetMainCamera());
			RayCollision closestCollision;
			if (world->Raycast(ray, closestCollision, true)) {
				if (closestCollision.node == selectionObject) {
					selectionObject->GetPhysicsObject()->
						//AddForce(ray.GetDirection() * forceMagnitude);
						AddForceAtPosition(ray.GetDirection() * forceMagnitude,
							closestCollision.collidedAt);
				}
			}
		}
	}
}

void RacecourseSingleGame::DrawDisplay(float dt) {
	renderer->DrawString("Time taken: " + std::to_string(levelTimer), Vector2(5, 10), Vector4(0, 0, 0, 1));
	renderer->DrawString("Score: " + std::to_string(playerCharacter->GetScore()), Vector2(5, 15), Vector4(0, 0, 0, 1));
	renderer->DrawString("Points collected: " + std::to_string(playerCharacter->GetPointsCollected()), Vector2(5, 20), Vector4(0, 0, 0, 1));
	renderer->DrawString("Total items: " + std::to_string(bonus.size()), Vector2(5, 25), Vector4(0, 0, 0, 1));
	renderer->DrawString("Collected items: " + std::to_string(playerCharacter->GetItems()), Vector2(5, 30), Vector4(0, 0, 0, 1));
	renderer->DrawString("Remaining items: " + std::to_string(bonus.size() - playerCharacter->GetItems()), Vector2(5, 35), Vector4(0,0,0,1));

	if (playerCharacter->GetPlayerAttackCD() > 0.0f && (levelCompleted == false))
	{
		renderer->DrawString("Attack Cooldown: " + std::to_string(playerCharacter->GetPlayerAttackCD()), Vector2(5, 90));
	}
}