File MainInterface.cpp
File List > Interfaces > MainInterface.cpp
Go to the documentation of this file
#include <map>
#include "MainInterface.hpp"
namespace i_2D {
sf::Clock timer; // To drawTimer function
float elapsedTime = 0.0f; // Sets elapsed time = 0.0
MainInterface::MainInterface(size_t id, const std::string &name) : InterfaceBase(id, name),
mWindow(sf::VideoMode({1000, 800}),
"Maze Window") {
if (!mFont.loadFromFile("../../assets/font/ArialNarrow7.ttf")) {
std::cout << "Error loading font file: ../assets/font/ArialNarrow7.ttf" << std::endl;
}
mMessageBoard = std::make_unique<MessageBoard>(mFont);
mTextBox = std::make_unique<TextBox>(mFont);
auto a = mWindow.getSize().x;
auto b = mWindow.getSize().y;
mMenu.Initialize(mFont, sf::Vector2f{static_cast<float>(a), static_cast<float>(b)});
ChooseTexture();
}
std::vector<std::string> MainInterface::CreateVectorMaze(
const WorldGrid &grid, const type_options_t &type_options,
const item_map_t &item_map, const agent_map_t &agent_map) {
std::vector<std::string> symbol_grid(grid.GetHeight());
mGridHeight = grid.GetHeight();
mGridWidth = grid.GetWidth();
// Load the world into the symbol_grid;
for (size_t y = 0; y < grid.GetHeight(); ++y) {
symbol_grid[y].resize(grid.GetWidth());
for (size_t x = 0; x < grid.GetWidth(); ++x) {
symbol_grid[y][x] = type_options[grid.At(x, y)].symbol;
}
}
// Add in the entities
for (const auto &[id, item_ptr]: item_map) {
GridPosition pos = item_ptr->GetPosition();
char c = '+';
if (item_ptr->HasProperty("symbol")) {
c = item_ptr->GetProperty<char>("symbol");
}
if(pos.CellX() >= 0 && pos.CellY() >= 0 &&
pos.CellX() < grid.GetWidth() && pos.CellY() < grid.GetHeight() &&
!item_ptr->IsOwned()){
symbol_grid[pos.CellY()][pos.CellX()] = c;
}
}
// Add in the agents
for (const auto &[id, agent_ptr]: agent_map) {
GridPosition pos = agent_ptr->GetPosition();
char c = '*';
if (agent_ptr->HasProperty("symbol")) {
c = agent_ptr->GetProperty<char>("symbol");
}
if (!agent_ptr->HasProperty("deleted")){
symbol_grid[pos.CellY()][pos.CellX()] = c;
}
}
return symbol_grid;
}
sf::Vector2f MainInterface::CalculateCellSize(const WorldGrid &grid) {
float cellSizeWide, cellSizeTall;
if (mGridSizeLarge) {
cellSizeWide = mWindow.getSize().x / COL;
cellSizeTall = mWindow.getSize().y / ROW;
} else {
cellSizeWide = mWindow.getSize().x / grid.GetWidth();
cellSizeTall = mWindow.getSize().y / grid.GetHeight();
}
float cellSize = std::min(cellSizeWide, cellSizeTall);
return sf::Vector2f(cellSize, cellSize);
}
void MainInterface::DrawGrid(const WorldGrid &grid, const type_options_t &type_options, const item_map_t &item_map,
const agent_map_t &agent_map) {
// Clear old drawing
mWindow.clear(sf::Color::White);
// Check player's position
mPlayerPosition = sf::Vector2i(this->position.GetX(), this->position.GetY());
// Create grid of symbols representing the world
std::vector<std::string> symbol_grid;
std::vector<std::string> default_grid = CreateVectorMaze(grid, type_options, item_map, agent_map);
// Determine cell size
sf::Vector2f cellSize = CalculateCellSize(grid);
float drawSpaceWidth, drawSpaceHeight, drawCenterX, drawCenterY;
CalculateDrawSpace(grid, cellSize.x, drawSpaceWidth,
drawSpaceHeight, drawCenterX, drawCenterY);
if (mGridSizeLarge) {
symbol_grid = LargeDisplayGrid(default_grid);
} else {
symbol_grid = default_grid;
}
//CheckLargerGrid();
// Create a render texture to draw the grid
sf::RenderTexture renderTexture;
[[maybe_unused]] bool success =
renderTexture.create({static_cast<unsigned int>(drawSpaceWidth), static_cast<unsigned int>(drawSpaceHeight)});
renderTexture.clear(sf::Color::White);
for (size_t iterY = 0; iterY < symbol_grid.size(); ++iterY) {
for (size_t iterX = 0; iterX < symbol_grid[0].size(); ++iterX) {
float cellPosX = static_cast<float>(iterX) * cellSize.x;
float cellPosY = static_cast<float>(iterY) * cellSize.y;
char symbol = symbol_grid[iterY][iterX];
sf::RectangleShape cellRect(sf::Vector2f(cellSize.x, cellSize.y));
cellRect.setPosition(sf::Vector2f(cellPosX, cellPosY));
sf::RectangleShape cell(sf::Vector2f(cellSize.x, cellSize.y));
cell.setPosition(sf::Vector2f(cellPosX, cellPosY));
SwitchCellSelect(renderTexture, cellRect, cell, symbol);
}
}
renderTexture.display();
DrawTimer();
DrawHealthInfo();
// Draw the texture to the window
sf::Sprite sprite(renderTexture.getTexture());
sprite.setPosition({drawCenterX, drawCenterY});
mWindow.draw(sprite);
// Display everything
mTextBox->DrawTo(mWindow);
mMessageBoard->DrawTo(mWindow);
mMenu.DrawTo(mWindow);
mWindow.display();
}
void MainInterface::DrawTimer() {
// Get elapsed time in seconds
elapsedTime = timer.getElapsedTime().asSeconds();
// Set up font and location
sf::Text timerText(mFont);
timerText.setCharacterSize(24);
timerText.setPosition({750.0f, 75.0f}); // Adjust position as needed
// Format the elapsed time with 2 decimal points
std::ostringstream stream;
stream << "Time: " << std::fixed << std::setprecision(2) << elapsedTime << " S";
std::string formattedTime = stream.str();
// Create text for current value
timerText.setString(formattedTime);
timerText.setFillColor(sf::Color::Blue);
mWindow.draw(timerText);
}
void MainInterface::DrawHealthInfo() {
if(!HasProperty("Health")) return;
int health = property_map.at("Health")->ToInt();
// Set text properties and draw
sf::Text healthText(mFont);
healthText.setCharacterSize(24);
healthText.setPosition({20.0f, 75.0f});
healthText.setFillColor(sf::Color::Green);
healthText.setString("Hp: " + std::to_string(health));
mWindow.draw(healthText);
}
std::vector<std::string> MainInterface::LargeDisplayGrid(const std::vector<std::string> &symbol_grid) {
// Determine the top-left corner of the 9x23 window
int topLeftX = std::max(0,
std::min(mPlayerPosition.x - COL / 2, static_cast<int>(symbol_grid[0].size()) - COL));
int topLeftY = std::max(0, std::min(mPlayerPosition.y - ROW / 2, static_cast<int>(symbol_grid.size()) - ROW));
// Create a new symbol grid for the 9x23 display window
std::vector<std::string> display_grid;
for (int iterY = 0; iterY < ROW; ++iterY) {
std::string row;
for (int iterX = 0; iterX < COL; ++iterX) {
int posX = topLeftX + iterX;
int posY = topLeftY + iterY;
// Copy the symbol from symbol_grid to display_grid
row.push_back(symbol_grid[posY][posX]);
}
display_grid.push_back(row);
}
return display_grid;
}
void MainInterface::CalculateDrawSpace(const WorldGrid &grid, float cellSize, float &drawSpaceWidth,
float &drawSpaceHeight, float &drawCenterX, float &drawCenterY) {
if (mGridSizeLarge) {
drawSpaceWidth = static_cast<float>(COL) * cellSize;
drawSpaceHeight = static_cast<float>(ROW) * cellSize;
} else {
drawSpaceWidth = static_cast<float>(grid.GetWidth()) * cellSize;
drawSpaceHeight = static_cast<float>(grid.GetHeight()) * cellSize;
}
drawCenterX = (mWindow.getSize().x - drawSpaceWidth) / 2.0f;
drawCenterY = (mWindow.getSize().y - drawSpaceHeight) / 2.0f;
}
size_t MainInterface::SelectAction(const WorldGrid &grid,
const type_options_t &type_options,
const item_map_t &item_map,
const agent_map_t &agent_map) {
// Initialize action_id and timer
size_t action_id = 0;
timer.restart();
// While the timer is going
while (mWindow.isOpen() && timer.getElapsedTime().asSeconds() < mInputWaitTime) {
sf::Event event;
// Check through all events generated in this frame
while (mWindow.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
mWindow.close();
exitCleanup();
} else if (event.type == sf::Event::TextEntered) {
if (mTextBox->IsSelected()) {
mTextBox->TypedOn(event);
}
} else if (event.type == sf::Event::KeyPressed) {
action_id = HandleKeyEvent(event);
} else if (event.type == sf::Event::Resized) {
HandleResize(event, grid);
} else if (event.type == sf::Event::MouseMoved) {
auto textureName = mMenu.HandleMouseMove(mWindow);
if(textureName!="null"){
auto texture = mTextureHolder.GetTexture(textureName);
mMenu.SetInventoryItemDisplay(texture);
}
} else if (event.type == sf::Event::MouseButtonPressed) {
MouseClickEvent(event, GetID(), item_map);
}
}
// Check if a valid action was taken and return that if so
if (action_id != 0) {
return action_id;
}
// Otherwise update the screen drawing again...
DrawGrid(grid, type_options, item_map, agent_map);
}
// The timer has ended or the window has been closed
return 0;
}
size_t MainInterface::HandleKeyEvent(const sf::Event &event) {
size_t action_id = 0;
if (mTextBox->IsSelected()) {
// TextBox is selected, handle specific cases
switch (event.key.code) {
case sf::Keyboard::Enter:
mMessageBoard->Send(mTextBox->GetText());
mTextBox->SetString("");
mTextBox->SetSelected(false);
break;
case sf::Keyboard::Escape:
mTextBox->SetString("");
mTextBox->SetSelected(false);
break;
default:
break;
}
} else {
// TextBox is not selected, handle movement keys
switch (event.key.code) {
case sf::Keyboard::W:
if (mTextBox->IsSelected())break;
action_id = GetActionID("up");
break;
case sf::Keyboard::A:
if (mTextBox->IsSelected())break;
action_id = GetActionID("left");
break;
case sf::Keyboard::S:
if (mTextBox->IsSelected())break;
action_id = GetActionID("down");
break;
case sf::Keyboard::D:
if (mTextBox->IsSelected())break;
action_id = GetActionID("right");
break;
case sf::Keyboard::Up:
action_id = GetActionID("up");
break;
case sf::Keyboard::Left:
action_id = GetActionID("left");
break;
case sf::Keyboard::Down:
action_id = GetActionID("down");
break;
case sf::Keyboard::Right:
action_id = GetActionID("right");
break;
case sf::Keyboard::H:
action_id = GetActionID("heal");
break;
case sf::Keyboard::T:
if (GetName() == "Interface") action_id = GetActionID("drop");
else if (GetName() == "Interface3") action_id = GetActionID("stats");
break;
case sf::Keyboard::C:
action_id = GetActionID("use_axe");
break;
case sf::Keyboard::V:
action_id = GetActionID("use_boat");
break;
case sf::Keyboard::F:
action_id = GetActionID("attack");
break;
case sf::Keyboard::G:
action_id = GetActionID("special");
break;
case sf::Keyboard::B:
action_id = GetActionID("buff");
break;
case sf::Keyboard::R:
action_id = GetActionID("run");
break;
case sf::Keyboard::Y:
action_id = GetActionID("help");
break;
default:
break; // The user pressed an unknown key.
}
}
// If we waited for input, but don't understand it, notify the user.
if (action_id == 0 && !mTextBox->IsSelected()) {
std::cout << "Unknown key." << std::endl;
}
// No action performed
return action_id;
}
void MainInterface::HandleResize(const sf::Event &event, const WorldGrid &grid) {
// Check size limits of window
float widthWindow = event.size.width;
float heightWindow = event.size.height;
float widthMin, heightMin;
if (mGridSizeLarge) {
widthMin = COL * MIN_SIZE_CELL;
heightMin = ROW * MIN_SIZE_CELL;
} else {
widthMin = grid.GetWidth() * MIN_SIZE_CELL;
heightMin = grid.GetHeight() * MIN_SIZE_CELL;
}
widthWindow = std::max(widthWindow, widthMin);
heightWindow = std::max(heightWindow, heightMin);
mMenu.SetWorldSize(sf::Vector2f(widthWindow, heightWindow));
if (mMenu.IsInventoryOpen()) {
mMenu.DeconstructInventory();
mMenu.ConstructInventory(mAgentInventory);
}
// Restrict window size if necessary
if (widthWindow <= widthMin || heightWindow <= heightMin) {
mWindow.setSize(sf::Vector2u(widthWindow, heightWindow));
}
// Resize the view to match window size to prevent deformation
sf::FloatRect viewArea(sf::Vector2f(0, 0), sf::Vector2f(widthWindow, heightWindow));
mWindow.setView(sf::View(viewArea));
// Update TextBox position for resizing
if (mTextBox) {
// Define the percentage values for the position
float xPosPercentage = 0.01f; // 5% of the window width
float yPosPercentage = 0.9f; // 80% of the window height
// Calculate the actual position based on percentages
float xPos = (widthWindow * xPosPercentage);
float yPos = (heightWindow * yPosPercentage);
// Set the position of your textbox
mTextBox->SetPosition({xPos, yPos});
}
}
/*
* This function chooses the world to load the texture for it's images
* and sets the current texture map for drawing
*/
void MainInterface::ChooseTexture() {
if (GetName() == "Interface1") {
mTexturesDefault = mTextureHolder.MazeTexture();
mTexturesCurrent = mTexturesDefault;
} else if (GetName() == "Interface") {
mTexturesSecondWorld = mTextureHolder.SecondWorldTexture();
mTexturesCurrent = mTexturesSecondWorld;
} else if (GetName() == "Interface3") {
mTexturesManualWorld = mTextureHolder.ManualWorldTexture();
mTexturesCurrent = mTexturesManualWorld;
} else if (GetName() == "Interface2") {
mTexturesGenerativeWorld = mTextureHolder.GenerativeWorldTexture();
mTexturesCurrent = mTexturesGenerativeWorld;
}
}
void MainInterface::MouseClickEvent(const sf::Event &event, const size_t /*entity_id*/, const item_map_t &item_map) {
if (event.mouseButton.button == sf::Mouse::Left) {
sf::Vector2f mousePos(static_cast<float>(event.mouseButton.x), static_cast<float>(event.mouseButton.y));
// Check if the mouse click is inside the TextBox bounds
if (mTextBox->Contains(mousePos)) {
// Toggle the selected state of the TextBox
mTextBox->SetSelected(!mTextBox->IsSelected());
} else {
// If the click is outside the TextBox, deselect it
mTextBox->SetSelected(false);
}
// Check if the mouse is over specific menu items
if (mMenu.GetMenu()[4]->IsMouseOver(mWindow) ) {
SetLargeGrid(true);
} else if (mMenu.GetMenu()[3]->IsMouseOver(mWindow)) {
SetLargeGrid(false);
} else if (mMenu.GetMenu()[2]->IsMouseOver(mWindow)) {
exitCleanup();
} else {
// Handle mouse button press for the general menu
mAgentInventory.clear();
for(const auto &[x,y]:item_map){
if(GetID() == y->GetOwnerID()){
mAgentInventory.push_back(y->GetName());
}
}
mMenu.HandleMouseButtonPressed(mWindow, mAgentInventory);
}
}
}
void MainInterface::SwitchCellSelect(sf::RenderTexture &renderTexture, sf::RectangleShape &cellRect,
sf::RectangleShape &cell, char symbol) {
switch (symbol) {
case '#':
DrawWall(renderTexture, cellRect, mTexturesCurrent[symbol]);
break;
default:
DrawAgentCell(renderTexture, cellRect, cell, mTexturesCurrent[symbol]);
break;
}
}
void MainInterface::DrawAgentCell(sf::RenderTexture &renderTexture, sf::RectangleShape &cellRect,
sf::RectangleShape &cell, sf::Texture &agent) {
cellRect.setTexture(&agent);
cell.setTexture(&mTexturesCurrent[' ']);
if (&agent == &mTexturesCurrent['+'] and GetName() == "Interface") {
cellRect.setFillColor(sf::Color::Green);
}
renderTexture.draw(cell);
renderTexture.draw(cellRect);
}
void MainInterface::DrawWall(sf::RenderTexture &renderTexture,
sf::RectangleShape &cellRect, sf::Texture &wallTexture) {
cellRect.setTexture(&wallTexture);
renderTexture.draw(cellRect);
}
void MainInterface::setMInputWaitTime(double waitTime) {
MainInterface::mInputWaitTime = waitTime;
}
void MainInterface::CheckLargerGrid()
{
if (mGridWidth == mGridHeight)
{
mGridSizeLarge = true;
}
}
}