ICT290 / src / scene / ShaysWorldState.cpp
ShaysWorldState.cpp
Raw
#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;
    }
}