#include "ShaysWorldState.h" #include "../ShaysWorld/main.h" #include "theArchanist/theArchanist.h" std::shared_ptr<State> ShaysWorldState::m_thisState = nullptr; Camera_ ShaysWorldState::temp{}; ShaysWorldStateSounds ShaysWorldState::m_sounds{}; ShaysWorldStateToggles ShaysWorldState::m_toggles{}; ShaysWorldTransition ShaysWorldState::m_transition{}; unsigned int ShaysWorldState::newExitTexture; std::shared_ptr<State> ShaysWorldState::init(Resources& engineResources) { if (m_thisState == nullptr) { m_thisState = std::make_shared<State>(draw, handleEvents, destroy, pause, update, engineResources); // load the new exit texture in separately as it requires bmp to be // rotated newExitTexture = m_thisState->resources.textureManager .loadBMPTexture("thanks.bmp", 180); setCamera(); glPushMatrix(); glPushAttrib(GL_ALL_ATTRIB_BITS); ShaysWorld::myinit(m_thisState->resources.settings.width, m_thisState->resources.settings.height); glPopAttrib(); glPopMatrix(); // Create sky bridge object and its bounding boxes/planes. createSkyBridge(); // Create tavern object and its bounding boxes/planes createTav(); // Initiate shays bounding boxes after we add ours. ShaysWorld::cam.InitiateBoundingBoxes(); m_thisState->audioManager.playSound(m_sounds.ambience); glClearColor(97.f / 255.f, 140.f / 255.f, 185.f / 255.f, 1.f); createLightObjects(); m_thisState->skybox.setModel( m_thisState->resources.modelManager.getModel("as1/shaySkybox.obj")); } return m_thisState; } void ShaysWorldState::destroy() { m_toggles = ShaysWorldStateToggles{}; resetTransition(); m_thisState->stop = false; m_thisState->audioManager.stopAllSounds(); } void ShaysWorldState::pause(bool pause) { if (pause) { glEnable(GL_CULL_FACE); m_thisState->audioManager.pauseSounds(true); Light::disableLighting(); } else { Light::disableLighting(); // set background (sky colour) glClearColor(97.f / 255.f, 140.f / 255.f, 185.f / 255.f, 1.f); // Set camera perspective m_thisState->camera .setPerspective(45.f, (float)m_thisState->resources.settings.width / (float)m_thisState->resources.settings.height, 3.f, 50000.f); m_thisState->camera.lockMouse(true); m_thisState->audioManager.pauseSounds(false); glDisable(GL_CULL_FACE); } } void ShaysWorldState::handleEvents(SDL_Event& event) { m_thisState->newState = nullptr; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: exit(0); case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_ESCAPE: // Esc key ShaysWorld::keys(27, 0, 0); m_toggles.endScreen = !m_toggles.endScreen; // Unlock the mouse m_thisState->camera.lockMouse(!m_toggles.endScreen); break; case SDLK_SPACE: ShaysWorld::keys(' ', 0, 0); m_toggles.endScreen = false; break; case SDLK_m: ShaysWorld::keys('m', 0, 0); break; case SDLK_l: m_toggles.displayLights = !m_toggles.displayLights; break; case SDLK_p: ShaysWorld::keys('p', 0, 0); break; case SDLK_0: m_thisState ->newState = theArchanist::init(m_thisState ->resources, 0, 3, 1); return; case SDLK_x: std::cout << m_thisState->camera.position.x << ' ' << m_thisState->camera.position.y << ' ' << m_thisState->camera.position.z << '\n'; break; case SDLK_MINUS: case SDLK_UNDERSCORE: m_thisState->audioManager.setMasterVolume( m_thisState->audioManager.getMasterVolume() - 10); break; case SDLK_EQUALS: case SDLK_PLUS: m_thisState->audioManager.setMasterVolume( m_thisState->audioManager.getMasterVolume() + 10); break; // TODO: Map keys better, demo for now case SDLK_SEMICOLON: m_thisState->audioManager.toggleMute(); break; default: break; } default: break; } if (event.button.button == SDL_BUTTON_LEFT && m_toggles.endScreen) { checkExitClick(); } } // For input that needs very fast updates SDL_PumpEvents(); const Uint8* keyStates = SDL_GetKeyboardState(nullptr); // walk if ((keyStates[SDL_SCANCODE_W] || keyStates[SDL_SCANCODE_A] || keyStates[SDL_SCANCODE_S] || keyStates[SDL_SCANCODE_D]) && !(keyStates[SDL_SCANCODE_LSHIFT])) { if (m_sounds.soundDelta <= 0) { m_thisState->audioManager.playSound(m_sounds.walk); m_sounds.soundDelta = 2.5f; } else { m_sounds.soundDelta -= 0.1f; } } // run if ((keyStates[SDL_SCANCODE_W] || keyStates[SDL_SCANCODE_A] || keyStates[SDL_SCANCODE_S] || keyStates[SDL_SCANCODE_D]) && keyStates[SDL_SCANCODE_LSHIFT]) { if (m_sounds.soundDelta < 0) { m_thisState->audioManager.playSound(m_sounds.walk); m_sounds.soundDelta = 1.2f; } else { m_sounds.soundDelta -= 0.1f; } } if (keyStates[SDL_SCANCODE_W]) { m_thisState->camera.move.forward = true; } else { m_thisState->camera.move.forward = false; } if (keyStates[SDL_SCANCODE_A]) { m_thisState->camera.move.left = true; } else { m_thisState->camera.move.left = false; } if (keyStates[SDL_SCANCODE_S]) { m_thisState->camera.move.backward = true; } else { m_thisState->camera.move.backward = false; } if (keyStates[SDL_SCANCODE_D]) { m_thisState->camera.move.right = true; } else { m_thisState->camera.move.right = false; } if (keyStates[SDL_SCANCODE_LSHIFT]) { m_thisState->camera.speed = 5000; } else { m_thisState->camera.speed = 1000; } } void ShaysWorldState::update(float deltaTime) { updateCamera(deltaTime); checkLevelTransition(); m_thisState->audioManager.setListener(m_thisState->camera.position, m_thisState->camera.direction); } void ShaysWorldState::draw() { // Draw first so that it is behind everything. m_thisState->skybox.draw(m_thisState->camera.direction, m_thisState->camera.up); // Safety matrix, who knows what's going on in there! glPushMatrix(); glPushAttrib(GL_ALL_ATTRIB_BITS); ShaysWorld::Display(); glPopAttrib(); glPopMatrix(); if (m_toggles.endScreen) { // now calling end screen for here and not shays world ShaysWorld::cam.DisplayWelcomeScreen(ShaysWorld::width, ShaysWorld::height, 0, newExitTexture); } // Cull our objects glEnable(GL_CULL_FACE); for (auto& obj : m_thisState->objects) { if (m_toggles.displayLights) { obj.second.draw(); } else { if (obj.second.name != "light") { obj.second.draw(); } } } glDisable(GL_CULL_FACE); } void ShaysWorldState::setCamera() { // Set camera perspective m_thisState->camera .setPerspective(45.f, (float)m_thisState->resources.settings.width / (float)m_thisState->resources.settings.height, 3.f, 50000.f); // Lock the mouse to the screen area m_thisState->camera.lockMouse(true); // Shays world starting position m_thisState->camera.position.x = 32720.0f; m_thisState->camera.position.y = 9536.0f; m_thisState->camera.position.z = 4800.0f; // Set look direction m_thisState->camera.yaw = 180; // Enable FPS style camera m_thisState->camera.move.freeCam = false; // Set speed (high because of Shays worlds scale) m_thisState->camera.speed = 1000; } void ShaysWorldState::updateCamera(float deltaTime) { temp = m_thisState->camera; temp.update(deltaTime); // If no collision update our camera if (!ShaysWorld::cam.m_colDetect.Collide(temp.position.x, temp.position.y, temp.position.z)) { // Store our movement delta for SetPlanes float moveX = temp.position.x - m_thisState->camera.position.x; float moveZ = temp.position.z - m_thisState->camera.position.z; // Since no collision set the state camera to be same as temp m_thisState->camera = temp; // Update Shay camera for his calculations and updates ShaysWorld::cam.Position(m_thisState->camera.position.x, m_thisState->camera.position.y, m_thisState->camera.position.z, 0); // Update our y position ShaysWorld::cam.SetPlanes(moveX, moveZ); m_thisState->camera.position.y = (float)ShaysWorld::cam.GetUD(); } } void ShaysWorldState::createSkyBridge() { const int FLAT_PLAIN = 0; std::string objectName("skyBridge"); auto model = m_thisState->resources.modelManager.getModel( "as1/skybridge.obj"); glm::vec3 location61(1300.f, 10000.f, 50650.f); glm::vec3 rotation61(0, 180, 0); glm::vec3 scale61(150, 150, 150); Object newObject(objectName, model, location61, rotation61, scale61); m_thisState->objects.insert({objectName, newObject}); objectName = "movingWall"; model = m_thisState->resources.modelManager.getModel("as1/movingWall.obj"); glm::vec3 location1(6300.f, 9200, 59530.f); glm::vec3 rotation1(0, 180, 0); glm::vec3 scale1(150, 150, 150); newObject = Object(objectName, model, location1, rotation1, scale1); m_thisState->objects.insert({objectName, newObject}); // Stair bottom floor ShaysWorld::cam .SetPlanes(FLAT_PLAIN, 0.0, 6000.0, 9800, 9800, 59233, 61000); // Corridor extension floor ShaysWorld::cam .SetPlanes(FLAT_PLAIN, 0.0, 5000.0, 10450.0, 10450.0, 50000.0, 57870.0); // Add AABB // Left side ShaysWorld::cam.AddBoundingBox(); ShaysWorld::cam.SetAABBMaxX(17, 16000.0); ShaysWorld::cam.SetAABBMinX(17, 4607.0); ShaysWorld::cam.SetAABBMaxZ(17, 59080.0); ShaysWorld::cam.SetAABBMinZ(17, 45594.0); // Stair middle ShaysWorld::cam.AddBoundingBox(); ShaysWorld::cam.SetAABBMaxX(18, 3800.0); ShaysWorld::cam.SetAABBMinX(18, 3587.0); ShaysWorld::cam.SetAABBMaxZ(18, 59242.0); ShaysWorld::cam.SetAABBMinZ(18, 57548.0); // Far wall ShaysWorld::cam.AddBoundingBox(); ShaysWorld::cam.SetAABBMaxX(19, 16000.0); ShaysWorld::cam.SetAABBMinX(19, 0.0); ShaysWorld::cam.SetAABBMaxZ(19, 60300.0); ShaysWorld::cam.SetAABBMinZ(19, 60082.0); // Dead end wall ShaysWorld::cam.AddBoundingBox(); ShaysWorld::cam.SetAABBMaxX(20, 16466.0); ShaysWorld::cam.SetAABBMinX(20, 16460.0); ShaysWorld::cam.SetAABBMaxZ(20, 60100.0); ShaysWorld::cam.SetAABBMinZ(20, 57000.0); // Canteen side ShaysWorld::cam.AddBoundingBox(); ShaysWorld::cam.SetAABBMaxX(21, 2608.0); ShaysWorld::cam.SetAABBMinX(21, 0.0); ShaysWorld::cam.SetAABBMaxZ(21, 61000.0); ShaysWorld::cam.SetAABBMinZ(21, 49046.0); // Stair far ShaysWorld::cam.AddBoundingBox(); ShaysWorld::cam.SetAABBMaxX(22, 16000.0); ShaysWorld::cam.SetAABBMinX(22, 3662.0); ShaysWorld::cam.SetAABBMaxZ(22, 59245.0); ShaysWorld::cam.SetAABBMinZ(22, 59065.0); // Create downward steps GLdouble step = 10385.0; GLdouble stepLength = 57870.0; for (std::size_t i = 0; i < 9; ++i) { ShaysWorld::cam.SetPlanes(FLAT_PLAIN, 0.0, 3584.0, step, step, stepLength, stepLength + 166); // Step height step -= 65.0; // Step length stepLength += 166; } // Create upward steps step = 10515.0; stepLength = 57870.0; for (std::size_t i = 0; i < 8; ++i) { ShaysWorld::cam.SetPlanes(FLAT_PLAIN, 3820.0, 5000.0, step, step, stepLength, stepLength + 166); // Step height step += 65.0; // Step length stepLength += 166; } } void ShaysWorldState::createTav() { std::string objectName("tav"); auto model = m_thisState->resources.modelManager.getModel("as1/tav.obj"); glm::vec3 location61(8900.f, 10200, -1500.f); glm::vec3 rotation61(0, 90, 0); glm::vec3 scale61(2000, 2000, 2000); Object newObject = Object(objectName, model, location61, rotation61, scale61); m_thisState->objects.insert({objectName, newObject}); objectName = "sign"; model = m_thisState->resources.modelManager.getModel("as1/sign.obj"); newObject = Object(objectName, model, location61, rotation61, scale61); m_thisState->objects.insert({objectName, newObject}); // Grass slope ShaysWorld::cam .SetPlanes(2, 4938.87, 26340, 9500, 10444.5, -9363.39, 9959.19); ShaysWorld::cam.SetPlanes(2, 26340, 31434.4, 9500, 10450, 4675.06, 10000); // Paved slope ShaysWorld::cam .SetPlanes(2, 2641.9, 4763.86, 9600, 10100, -208.623, 8938.74); // lower steps float stepMinHeight = 9459.48f; float stepMaxHeight = 9600.f; float stepMinX = 2609.f; float stepMaxX = 4762.53f; float stepMinZ = -1274.04f; float stepMaxZ = -202.86f; float stepNum = 12.f; float stepHeight = (stepMaxHeight - stepMinHeight) / stepNum; float stepZIncrement = (stepMaxZ - stepMinZ) / stepNum; for (float i = 0; i < stepNum; ++i) { ShaysWorld::cam.SetPlanes(0, stepMinX, stepMaxX, stepMinHeight + i * stepHeight, stepMinHeight + i * stepHeight, stepMinZ + (i - 1) * stepZIncrement, stepMinZ + i * stepZIncrement); } // upper steps stepMinHeight = 10093.3f; stepMaxHeight = 10450.0f; stepMinX = 2617.63f; stepMaxX = 4708.19f; stepMinZ = 8973.21f; stepMaxZ = 9978.03f; stepNum = 18.f; stepHeight = (stepMaxHeight - stepMinHeight) / stepNum; stepZIncrement = (stepMaxZ - stepMinZ) / stepNum; for (float i = 0; i < stepNum; ++i) { ShaysWorld::cam.SetPlanes(0, stepMinX, stepMaxX, stepMinHeight + i * stepHeight, stepMinHeight + i * stepHeight, stepMinZ + (i - 1) * stepZIncrement, stepMinZ + i * stepZIncrement); } // Wall for non-grass area // ShaysWorld::cam.AddBoundingBox(); // ShaysWorld::cam.SetAABBMaxX(23, 31443.4); // ShaysWorld::cam.SetAABBMinX(23, 15255.3); // ShaysWorld::cam.SetAABBMaxZ(23, 9999.06); // ShaysWorld::cam.SetAABBMinZ(23, -10505.8); // Wall below tav ShaysWorld::cam.AddBoundingBox(); ShaysWorld::cam.SetAABBMaxX(23, 31527); ShaysWorld::cam.SetAABBMinX(23, 2347.45); ShaysWorld::cam.SetAABBMaxZ(23, -12600.48); ShaysWorld::cam.SetAABBMinZ(23, -13000.1); // tav ShaysWorld::cam.AddBoundingBox(); ShaysWorld::cam.SetAABBMaxX(24, 11141.6); ShaysWorld::cam.SetAABBMinX(24, 4770.91); ShaysWorld::cam.SetAABBMaxZ(24, 6484.78); ShaysWorld::cam.SetAABBMinZ(24, -8113.03); // stair wall ShaysWorld::cam.AddBoundingBox(); ShaysWorld::cam.SetAABBMaxX(25, 4922.27); ShaysWorld::cam.SetAABBMinX(25, 4710.92); ShaysWorld::cam.SetAABBMaxZ(25, 10048); ShaysWorld::cam.SetAABBMinZ(25, 8866.42); // edge near entrance ShaysWorld::cam.AddBoundingBox(); ShaysWorld::cam.SetAABBMaxX(26, 31536.4); ShaysWorld::cam.SetAABBMinX(26, 31436.4); ShaysWorld::cam.SetAABBMaxZ(26, 4708.78); ShaysWorld::cam.SetAABBMinZ(26, -9560.46); } void ShaysWorldState::createLightObjects() { GLfloat beamStepZ = 0.f; GLfloat beamStepX = 0.f; for (std::size_t i = 0; i < 4; ++i) { std::string objectName("light"); auto model = m_thisState->resources.modelManager.getModel( "as1/light.obj"); glm::vec3 location(32905.f, 11200.f, 11134.f + beamStepZ); glm::vec3 rotation(0, 0, 0); glm::vec3 scale(80, 80, 80); Object newObject = Object(objectName, model, location, rotation, scale); m_thisState->objects.insert({objectName, newObject}); objectName = "light"; model = m_thisState->resources.modelManager.getModel("as1/light.obj"); glm::vec3 location1(3493.f, 11200.f, 11134.f + beamStepZ); glm::vec3 rotation1(0, 0, 0); glm::vec3 scale1(80, 80, 80); newObject = Object(objectName, model, location1, rotation1, scale1); m_thisState->objects.insert({objectName, newObject}); objectName = "light"; model = m_thisState->resources.modelManager.getModel("as1/light.obj"); glm::vec3 location2(30054.f - beamStepX, 11200.f, 42195.f); glm::vec3 rotation2(0, 90, 0); glm::vec3 scale2(80, 80, 80); newObject = Object(objectName, model, location2, rotation2, scale2); m_thisState->objects.insert({objectName, newObject}); beamStepZ += 8492.f; beamStepX += 5835.f; } std::string objectName("light"); auto model = m_thisState->resources.modelManager.getModel("as1/light.obj"); glm::vec3 location2(30054.f - beamStepX, 11200.f, 42195.f); glm::vec3 rotation2(0, 90, 0); glm::vec3 scale2(80, 80, 80); Object newObject = Object(objectName, model, location2, rotation2, scale2); m_thisState->objects.insert({objectName, newObject}); } void ShaysWorldState::checkExitClick() { int x, y; SDL_GetMouseState(&x, &y); if ((x <= m_thisState->resources.settings.width / 2.0 + 256.0) && (x >= m_thisState->resources.settings.width / 2.0 - 256.0) && (y <= m_thisState->resources.settings.height / 2.0 + 256.0) && (y >= m_thisState->resources.settings.height / 2.0 - 256.0)) { m_thisState->stop = true; } } void ShaysWorldState::checkLevelTransition() { if (m_thisState->camera.position.y == 9800 && m_thisState->camera.position.x > 5100 && m_thisState->camera.position.z > 58000) { transition(); } else if (m_transition.transitioning) { resetTransition(); } } void ShaysWorldState::transition() { if (!m_transition.transitioning) { m_thisState->audioManager.stopAllSounds(); m_thisState->audioManager.playSound(m_sounds.transition); m_transition.transitioning = true; } auto wallKey(m_thisState->objects.find("movingWall")); if (wallKey != m_thisState->objects.end()) { Object& wall = wallKey->second; const float wallMax{16000}; const float originalWallLocation{6200.f}; if (wall.location.x <= wallMax && wall.location.x >= originalWallLocation) { float playerDelta = m_thisState->camera.position.x - m_transition.lastX; if (playerDelta > 0) { m_transition.wallToCameraOffset += 10; } else if (playerDelta < 0) { m_transition.wallToCameraOffset -= 10; } wall.location.x = m_thisState->camera.position.x + m_transition.wallToCameraOffset; m_transition.lastX = m_thisState->camera.position.x; } if (wall.location.x > wallMax) { wall.location.x = wallMax; } if (m_transition.flicker <= 0) { if (m_transition.lighting) { Light::enableLighting(); m_transition.lighting = !m_transition.lighting; m_transition.flicker = (int)m_transition.distribution( m_transition.gen); } else { Light::disableLighting(); m_transition.lighting = !m_transition.lighting; m_transition.flicker = (int)m_transition.distribution( m_transition.gen); } } --m_transition.flicker; // Swallow the player into the next scene if (wall.location.x - m_thisState->camera.position.x <= 0) { // Set camera for after we return to shays world. m_thisState->camera.position.x = 3302.0f; m_thisState->camera.position.y = 10450.0f; m_thisState->camera.position.z = 55823.0f; m_thisState->camera.yaw = 0; resetTransition(); // Init next state m_thisState->newState = theArchanist::init(m_thisState->resources, 0, 3, 1); } } } void ShaysWorldState::resetTransition() { const float originalWallLocation{6200.f}; m_thisState->audioManager.stopAllSounds(); m_thisState->audioManager.playSound(m_sounds.ambience); m_transition.transitioning = false; Light::disableLighting(); m_transition.wallToCameraOffset = 1200; auto wallPair(m_thisState->objects.find("movingWall")); if (wallPair != m_thisState->objects.end()) { wallPair->second.location.x = originalWallLocation; } }