diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 880811d..3230e27 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -1,84 +1,95 @@ -# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. -# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml -name: CMake on multiple platforms - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - runs-on: ${{ matrix.os }} - - strategy: - # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. - fail-fast: false - - # Set up a matrix to run the following 3 configurations: - # 1. - # 2. - # 3. - # - # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. - matrix: - os: [ubuntu-latest, windows-latest] - build_type: [Release] - c_compiler: [gcc, clang, cl] - include: - - os: windows-latest - c_compiler: cl - cpp_compiler: cl - - os: ubuntu-latest - c_compiler: gcc - cpp_compiler: g++ - - os: ubuntu-latest - c_compiler: clang - cpp_compiler: clang++ - exclude: - - os: windows-latest - c_compiler: gcc - - os: windows-latest - c_compiler: clang - - os: ubuntu-latest - c_compiler: cl - - steps: - - uses: actions/checkout@v4 - - name: Install boost - uses: MarkusJx/install-boost@v2 - id: install-boost - with: - boost_version: 1.73.0 - # NOTE: If a boost version matching all requirements cannot be found, - # this build step will fail - - - name: Set reusable strings - # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. - id: strings - shell: bash - run: | - echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" - - - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - env: - BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} - run: > - cmake -B ${{ steps.strings.outputs.build-output-dir }} - -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} - -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} - -S ${{ github.workspace }} - - - name: Build - # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} - - - name: Test - working-directory: ${{ steps.strings.outputs.build-output-dir }} - # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --build-config ${{ matrix.build_type }} +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: CMake on multiple platforms + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + # Set up a matrix to run the following 3 configurations: + # 1. + # 2. + # 3. + # + # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. + matrix: + os: [ubuntu-latest, windows-latest] + build_type: [Release] + c_compiler: [gcc, clang, cl] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + exclude: + - os: windows-latest + c_compiler: gcc + - os: windows-latest + c_compiler: clang + - os: ubuntu-latest + c_compiler: cl + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + if [ "$RUNNER_OS" == "Linux" ]; then + sudo apt-get update + sudo apt-get install -y libfreetype-dev + elif [ "$RUNNER_OS" == "Windows" ]; then + choco install nasm + fi + shell: bash + + - name: Install boost + uses: MarkusJx/install-boost@v2 + id: install-boost + with: + boost_version: 1.73.0 + # NOTE: If a boost version matching all requirements cannot be found, + # this build step will fail + + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + + - name : Init Submodule + shell: bash + run : | + git submodule update --init --recursive + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + env: + BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -S ${{ github.workspace }} + + - name: Build + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} diff --git a/.gitignore b/.gitignore index b62bd2b..eaa1246 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ out !CMakeLists.txt *.md *.ini +!.github !README.md include/lib/freetype include/lib/ft2build.h \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7297e95..0af9844 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ add_subdirectory(vendored/SDL_ttf) file(GLOB IMGUI_SOURCES "src/lib/*.cpp") add_library(imgui_lib STATIC ${IMGUI_SOURCES}) target_include_directories(imgui_lib PUBLIC - "${CMAKE_CURRENT_SOURCE_DIR}/include/lib" + "${CMAKE_CURRENT_SOURCE_DIR}/include/Lib" "${CMAKE_CURRENT_SOURCE_DIR}/vendored/SDL/include" "${CMAKE_CURRENT_SOURCE_DIR}/vendored/SDL_image/include" "${CMAKE_CURRENT_SOURCE_DIR}/vendored/SDL_ttf" diff --git a/include/Components/TimerComponent.h b/include/Components/TimerComponent.h index 8f7b3c4..c76f6cd 100644 --- a/include/Components/TimerComponent.h +++ b/include/Components/TimerComponent.h @@ -18,8 +18,8 @@ enum class TimerId : int { MaxTimers // Represents the maximum number of timer types. }; -constexpr size_t MAX_TIMERS_PER_ENTITY = - static_cast(TimerId::MaxTimers); +constexpr std::size_t MAX_TIMERS_PER_ENTITY = + static_cast(TimerId::MaxTimers); struct TimerInstance { TimerId id; diff --git a/include/Core/Registry.h b/include/Core/Registry.h index 6dbbb9a..7ccfc82 100644 --- a/include/Core/Registry.h +++ b/include/Core/Registry.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "Core/ComponentArray.h" #include "Core/InputState.h" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 16f7b3c..a78b3a6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,7 +5,6 @@ set(TEST_LIST math ecs - world ) set(BUILT_TESTS "") diff --git a/tests/test_ecs.cpp b/tests/test_ecs.cpp index add0fd1..2028a3d 100644 --- a/tests/test_ecs.cpp +++ b/tests/test_ecs.cpp @@ -1,123 +1,127 @@ -#include -#include "Core/Registry.h" -#include "Components/TransformComponent.h" #include "Components/MovementComponent.h" +#include "Components/TransformComponent.h" +#include "Core/Registry.h" +#include +#include + bool test_entity_creation() { - Registry registry; - registry.RegisterComponent(); - registry.RegisterComponent(); - - // Test entity creation - auto entity = registry.CreateEntity(); - if (entity == INVALID_ENTITY) { - std::cerr << "Entity creation failed" << std::endl; - return false; - } - - // Test component addition - registry.AddComponent(entity, Vec2f{10.0f, 20.0f}); - - if (!registry.HasComponent(entity)) { - std::cerr << "Component addition failed" << std::endl; - return false; - } - - return true; + Registry registry; + registry.RegisterComponent(); + registry.RegisterComponent(); + + // Test entity creation + auto entity = registry.CreateEntity(); + if (entity == INVALID_ENTITY) { + std::cerr << "Entity creation failed" << std::endl; + return false; + } + + // Test component addition + registry.AddComponent(entity, Vec2f{10.0f, 20.0f}); + + if (!registry.HasComponent(entity)) { + std::cerr << "Component addition failed" << std::endl; + return false; + } + + return true; } bool test_component_access() { - Registry registry; - registry.RegisterComponent(); - registry.RegisterComponent(); - - auto entity = registry.CreateEntity(); - Vec2f testPos{100.0f, 200.0f}; - - registry.AddComponent(entity, testPos); - - auto& transform = registry.GetComponent(entity); - - if (transform.position.x != testPos.x || transform.position.y != testPos.y) { - std::cerr << "Component access failed" << std::endl; - return false; - } - - // Modify component - transform.position.x = 300.0f; - - auto& modifiedTransform = registry.GetComponent(entity); - if (modifiedTransform.position.x != 300.0f) { - std::cerr << "Component modification failed" << std::endl; - return false; - } - - return true; + Registry registry; + registry.RegisterComponent(); + registry.RegisterComponent(); + + auto entity = registry.CreateEntity(); + Vec2f testPos{100.0f, 200.0f}; + + registry.AddComponent(entity, testPos); + + auto &transform = registry.GetComponent(entity); + + if (transform.position.x != testPos.x || transform.position.y != testPos.y) { + std::cerr << "Component access failed" << std::endl; + return false; + } + + // Modify component + transform.position.x = 300.0f; + + auto &modifiedTransform = registry.GetComponent(entity); + if (modifiedTransform.position.x != 300.0f) { + std::cerr << "Component modification failed" << std::endl; + return false; + } + + return true; } bool test_entity_view() { - Registry registry; - registry.RegisterComponent(); - registry.RegisterComponent(); - - // Create entities with different component combinations - auto entity1 = registry.CreateEntity(); - registry.AddComponent(entity1, Vec2f{0, 0}); - - auto entity2 = registry.CreateEntity(); - registry.AddComponent(entity2, Vec2f{10, 10}); - registry.EmplaceComponent(entity2, 1.0f); - - auto entity3 = registry.CreateEntity(); - registry.EmplaceComponent(entity3, 2.0f); - - // Test view with single component - auto transformView = registry.view(); - int transformCount = 0; - for (auto entity : transformView) { - transformCount++; - } - - if (transformCount != 2) { - std::cerr << "Single component view failed: expected 2, got " << transformCount << std::endl; - return false; - } - - // Test view with multiple components - auto multiView = registry.view(); - int multiCount = 0; - for (auto entity : multiView) { - multiCount++; - } - - if (multiCount != 1) { - std::cerr << "Multi component view failed: expected 1, got " << multiCount << std::endl; - return false; - } - - return true; + Registry registry; + registry.RegisterComponent(); + registry.RegisterComponent(); + + // Create entities with different component combinations + auto entity1 = registry.CreateEntity(); + registry.AddComponent(entity1, Vec2f{0, 0}); + + auto entity2 = registry.CreateEntity(); + registry.AddComponent(entity2, Vec2f{10, 10}); + registry.EmplaceComponent(entity2, 1.0f); + + auto entity3 = registry.CreateEntity(); + registry.EmplaceComponent(entity3, 2.0f); + + // Test view with single component + auto transformView = registry.view(); + int transformCount = 0; + for (auto entity : transformView) { + transformCount++; + } + + if (transformCount != 2) { + std::cerr << "Single component view failed: expected 2, got " + << transformCount << std::endl; + return false; + } + + // Test view with multiple components + auto multiView = registry.view(); + int multiCount = 0; + for (auto entity : multiView) { + multiCount++; + } + + if (multiCount != 1) { + std::cerr << "Multi component view failed: expected 1, got " << multiCount + << std::endl; + return false; + } + + return true; } -int main() { - bool all_passed = true; - - if (!test_entity_creation()) { - all_passed = false; - } - - if (!test_component_access()) { - all_passed = false; - } - - if (!test_entity_view()) { - all_passed = false; - } - - if (all_passed) { - std::cout << "All ECS tests passed!" << std::endl; - return 0; - } else { - std::cerr << "Some ECS tests failed!" << std::endl; - return 1; - } +int main(int argc, char *argv[]) { + bool all_passed = true; + + if (!test_entity_creation()) { + all_passed = false; + } + + if (!test_component_access()) { + all_passed = false; + } + + if (!test_entity_view()) { + all_passed = false; + } + + if (all_passed) { + std::cout << "All ECS tests passed!" << std::endl; + return 0; + } else { + std::cerr << "Some ECS tests failed!" << std::endl; + return 1; + } } \ No newline at end of file diff --git a/tests/test_math.cpp b/tests/test_math.cpp index 75ed6a1..23cdb4c 100644 --- a/tests/test_math.cpp +++ b/tests/test_math.cpp @@ -1,77 +1,81 @@ -#include -#include #include "Util/MathUtil.h" +#include +#include +#include bool test_map_range() { - // Test map_range function - auto result = util::map_range(5, 0, 10, 0, 100); - if (result != 50) { - std::cerr << "map_range test failed: expected 50, got " << result << std::endl; - return false; - } - - auto result2 = util::map_range(0.5f, 0.0f, 1.0f, 100.0f, 200.0f); - if (std::abs(result2 - 150.0f) > 0.001f) { - std::cerr << "map_range test 2 failed: expected 150, got " << result2 << std::endl; - return false; - } - - return true; + // Test map_range function + auto result = util::map_range(5, 0, 10, 0, 100); + if (result != 50) { + std::cerr << "map_range test failed: expected 50, got " << result + << std::endl; + return false; + } + + auto result2 = util::map_range(0.5f, 0.0f, 1.0f, 100.0f, 200.0f); + if (std::abs(result2 - 150.0f) > 0.001f) { + std::cerr << "map_range test 2 failed: expected 150, got " << result2 + << std::endl; + return false; + } + + return true; } bool test_clamp() { - // Test clamp function - if (util::clamp(5, 0, 10) != 5) { - std::cerr << "clamp test 1 failed" << std::endl; - return false; - } - - if (util::clamp(-5, 0, 10) != 0) { - std::cerr << "clamp test 2 failed" << std::endl; - return false; - } - - if (util::clamp(15, 0, 10) != 10) { - std::cerr << "clamp test 3 failed" << std::endl; - return false; - } - - return true; + // Test clamp function + if (util::clamp(5, 0, 10) != 5) { + std::cerr << "clamp test 1 failed" << std::endl; + return false; + } + + if (util::clamp(-5, 0, 10) != 0) { + std::cerr << "clamp test 2 failed" << std::endl; + return false; + } + + if (util::clamp(15, 0, 10) != 10) { + std::cerr << "clamp test 3 failed" << std::endl; + return false; + } + + return true; } bool test_distance() { - Vec2f a{0.0f, 0.0f}; - Vec2f b{3.0f, 4.0f}; - - double dist = util::dist(a, b); - if (std::abs(dist - 5.0) > 0.001) { - std::cerr << "distance test failed: expected 5.0, got " << dist << std::endl; - return false; - } - - return true; + Vec2f a{0.0f, 0.0f}; + Vec2f b{3.0f, 4.0f}; + + double dist = util::dist(a, b); + if (std::abs(dist - 5.0) > 0.001) { + std::cerr << "distance test failed: expected 5.0, got " << dist + << std::endl; + return false; + } + + return true; } -int main() { - bool all_passed = true; - - if (!test_map_range()) { - all_passed = false; - } - - if (!test_clamp()) { - all_passed = false; - } - - if (!test_distance()) { - all_passed = false; - } - - if (all_passed) { - std::cout << "All MathUtil tests passed!" << std::endl; - return 0; - } else { - std::cerr << "Some tests failed!" << std::endl; - return 1; - } +int main(int argc, char *argv[]) { + bool all_passed = true; + + if (!test_map_range()) { + all_passed = false; + } + + if (!test_clamp()) { + all_passed = false; + } + + if (!test_distance()) { + all_passed = false; + } + + if (all_passed) { + std::cout << "All MathUtil tests passed!" << std::endl; + return 0; + } else { + std::cerr << "Some tests failed!" << std::endl; + return 1; + } } \ No newline at end of file diff --git a/tests/test_world.cpp b/tests/test_world.cpp deleted file mode 100644 index 8e1c56d..0000000 --- a/tests/test_world.cpp +++ /dev/null @@ -1,326 +0,0 @@ -#include -#include - -#include -#include - -#include "Components/BuildingComponent.h" -#include "Components/ChunkComponent.h" -#include "Components/SpriteComponent.h" -#include "Components/TransformComponent.h" -#include "Components/ResourceNodeComponent.h" -#include "Components/InactiveComponent.h" -#include "Components/TextComponent.h" -#include "Core/Registry.h" -#include "Core/World.h" - -// Mock SDL renderer and font for testing -class TestWorld { - private: - SDL_Renderer* mockRenderer; - TTF_Font* mockFont = nullptr; - Registry* registry; - World* world; - - public: - TestWorld() { - registry = new Registry(); - registry->RegisterComponent(); - registry->RegisterComponent(); - registry->RegisterComponent(); - registry->RegisterComponent(); - registry->RegisterComponent(); - registry->RegisterComponent(); - registry->RegisterComponent(); - SDL_Init(SDL_INIT_VIDEO); - TTF_Init(); - SDL_Window* window = - SDL_CreateWindow("Test", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_HIDDEN); - mockRenderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - - world = new World(mockRenderer, registry, mockFont); - } - - bool test_tile_coordinate_conversion() { - // Test world position to tile index conversion - Vec2 tileIndex = world->GetTileIndexFromWorldPosition( - 64.0f, 64.0f); // TILE_PIXEL_SIZE = 64 - if (tileIndex.x != 1 || tileIndex.y != 1) { - std::cerr << "World to tile conversion failed: expected (1,1), got (" - << tileIndex.x << "," << tileIndex.y << ")" << std::endl; - return false; - } - - // Test negative coordinates - tileIndex = world->GetTileIndexFromWorldPosition(-64.0f, -64.0f); - if (tileIndex.x != -1 || tileIndex.y != -1) { - std::cerr - << "Negative coordinate conversion failed: expected (-1,-1), got (" - << tileIndex.x << "," << tileIndex.y << ")" << std::endl; - return false; - } - - // Test fractional coordinates - tileIndex = world->GetTileIndexFromWorldPosition( - 96.0f, 32.0f); // 1.5 tiles, 0.5 tiles - if (tileIndex.x != 1 || tileIndex.y != 0) { - std::cerr - << "Fractional coordinate conversion failed: expected (1,0), got (" - << tileIndex.x << "," << tileIndex.y << ")" << std::endl; - return false; - } - - return true; - } - - bool test_chunk_loading_and_player_movement() { - // Create a test player entity - EntityID player = registry->CreateEntity(); - registry->AddComponent(player, Vec2f{0.0f, 0.0f}); - - // Update world with player at origin - world->Update(player); - - // Check that chunks around origin are loaded - const auto& activeChunks = world->GetActiveChunks(); - - // With view distance of 2, we should have chunks from (-2,-2) to (2,2) - int expectedChunks = 5 * 5; // 5x5 grid - if (activeChunks.size() != expectedChunks) { - std::cerr << "Initial chunk loading failed: expected " << expectedChunks - << " chunks, got " << activeChunks.size() << std::endl; - return false; - } - - // Check specific chunks exist - ChunkCoord origin{0, 0}; - ChunkCoord corner{-2, -2}; - ChunkCoord farCorner{2, 2}; - - if (activeChunks.find(origin) == activeChunks.end()) { - std::cerr << "Origin chunk not loaded" << std::endl; - return false; - } - - if (activeChunks.find(corner) == activeChunks.end()) { - std::cerr << "Corner chunk not loaded" << std::endl; - return false; - } - - // Move player far away to test chunk unloading - auto& transform = registry->GetComponent(player); - transform.position = Vec2f{CHUNK_WIDTH*TILE_PIXEL_SIZE*world->viewDistance*10.f,CHUNK_HEIGHT*TILE_PIXEL_SIZE*world->viewDistance*10.f}; // Far from origin - - world->Update(player); - - const auto& newActiveChunks = world->GetActiveChunks(); - - // Origin chunk should no longer be active - if (newActiveChunks.find(origin) != newActiveChunks.end()) { - std::cerr << "Origin chunk not unloaded after player movement" - << std::endl; - return false; - } - - return true; - } - - bool test_building_placement() { - // Test building placement validation - - // Create a test player to ensure chunks are loaded - EntityID player = registry->CreateEntity(); - registry->AddComponent(player, Vec2f{64.0f, 64.0f}); - world->Update(player); - - // Test valid placement - Vec2 tileIndex{1, 1}; - bool canPlace = world->CanPlaceBuilding(tileIndex, 2, 2); - if (!canPlace) { - std::cerr << "Valid building placement rejected" << std::endl; - return false; - } - - // Actually place the building - EntityID building = registry->CreateEntity(); - registry->AddComponent(building, BuildingComponent{}); - - world->PlaceBuilding(building, tileIndex, 2, 2); - - // Test that the same location is now occupied - bool canPlaceAgain = world->CanPlaceBuilding(tileIndex, 2, 2); - if (canPlaceAgain) { - std::cerr << "Occupied tiles allowed building placement" << std::endl; - return false; - } - - // Test overlapping placement - Vec2 overlappingIndex{2, 2}; // Should overlap with existing building - bool canPlaceOverlap = world->CanPlaceBuilding(overlappingIndex, 2, 2); - if (canPlaceOverlap) { - std::cerr << "Overlapping building placement allowed" << std::endl; - return false; - } - - // Test adjacent placement (should work) - Vec2 adjacentIndex{3, 1}; // Next to existing building - bool canPlaceAdjacent = world->CanPlaceBuilding(adjacentIndex, 1, 1); - if (!canPlaceAdjacent) { - std::cerr << "Adjacent building placement rejected" << std::endl; - return false; - } - - return true; - } - - bool test_building_removal() { - // Setup: place a building first - EntityID player = registry->CreateEntity(); - registry->AddComponent(player, Vec2f{64.0f, 64.0f}); - world->Update(player); - - EntityID building = registry->CreateEntity(); - registry->AddComponent(building, BuildingComponent{}); - - Vec2 tileIndex{1, 1}; - world->PlaceBuilding(building, tileIndex, 2, 2); - - // Verify it's placed - if (world->CanPlaceBuilding(tileIndex, 2, 2)) { - std::cerr << "Building not properly placed before removal test" - << std::endl; - return false; - } - - // Get the occupied tiles from building component - auto& buildingComp = registry->GetComponent(building); - std::vector occupiedTiles = buildingComp.occupiedTiles; - - // Remove the building - world->RemoveBuilding(building, occupiedTiles); - - // Verify the tiles are now free - if (!world->CanPlaceBuilding(tileIndex, 2, 2)) { - std::cerr << "Building removal failed: tiles still occupied" << std::endl; - return false; - } - - return true; - } - - bool test_tile_access() { - // Create player and load chunks - EntityID player = registry->CreateEntity(); - registry->AddComponent(player, Vec2f{64.0f, 64.0f}); - world->Update(player); - - // Test getting tile at world position - TileData* tile = world->GetTileAtWorldPosition(64.0f, 64.0f); - if (!tile) { - std::cerr << "Could not get tile at valid world position" << std::endl; - return false; - } - - // Test getting tile at tile index - TileData* tileByIndex = world->GetTileAtTileIndex(1, 1); - if (!tileByIndex) { - std::cerr << "Could not get tile at valid tile index" << std::endl; - return false; - } - - // Should be the same tile - if (tile != tileByIndex) { - std::cerr << "World position and tile index should return same tile" - << std::endl; - return false; - } - - // Test getting tile in unloaded chunk (should return nullptr) - TileData* farTile = world->GetTileAtTileIndex(1000, 1000); - if (farTile != nullptr) { - std::cerr << "Getting tile in unloaded chunk should return nullptr" - << std::endl; - return false; - } - - return true; - } - - bool test_ore_generation_values() { - // Test that ore amount values are properly set - rsrc_amt_t minOre = world->GetMinironOreAmount(); - rsrc_amt_t maxOre = world->GetMaxironOreAmount(); - - if (maxOre <= 0) { - std::cerr << "Max ore amount should be positive, got " << maxOre - << std::endl; - return false; - } - - if (minOre < 0 || minOre > maxOre) { - std::cerr << "Min ore amount should be between 0 and max, got " << minOre - << " (max: " << maxOre << ")" << std::endl; - return false; - } - - return true; - } -}; - -int main(int argc, char* argv[]) { - bool all_passed = true; - TestWorld testWorld; - - std::cout << "Running World tests..." << std::endl; - - if (!testWorld.test_tile_coordinate_conversion()) { - std::cerr << "Tile coordinate conversion test failed" << std::endl; - all_passed = false; - } else { - std::cout << "Tile coordinate conversion test passed" << std::endl; - } - - if (!testWorld.test_chunk_loading_and_player_movement()) { - std::cerr << "Chunk loading and player movement test failed" << std::endl; - all_passed = false; - } else { - std::cout << "Chunk loading and player movement test passed" << std::endl; - } - - if (!testWorld.test_building_placement()) { - std::cerr << "Building placement test failed" << std::endl; - all_passed = false; - } else { - std::cout << "Building placement test passed" << std::endl; - } - - if (!testWorld.test_building_removal()) { - std::cerr << "Building removal test failed" << std::endl; - all_passed = false; - } else { - std::cout << "Building removal test passed" << std::endl; - } - - if (!testWorld.test_tile_access()) { - std::cerr << "Tile access test failed" << std::endl; - all_passed = false; - } else { - std::cout << "Tile access test passed" << std::endl; - } - - if (!testWorld.test_ore_generation_values()) { - std::cerr << "Ore generation values test failed" << std::endl; - all_passed = false; - } else { - std::cout << "Ore generation values test passed" << std::endl; - } - - if (all_passed) { - std::cout << "\n All World tests passed!" << std::endl; - return 0; - } else { - std::cerr << "\n Some World tests failed!" << std::endl; - return 1; - } -} \ No newline at end of file