443 lines
11 KiB
C++
443 lines
11 KiB
C++
#include "GameObject.h"
|
|
#include "Level.h"
|
|
#include "Physics2D.h"
|
|
#include "LightEffectSystem.h"
|
|
#include "LevelLoader.h"
|
|
#include "LevelData.h"
|
|
|
|
#include <cassert>
|
|
#include <iostream>
|
|
|
|
using namespace LightGame;
|
|
|
|
namespace
|
|
{
|
|
void TestGameObjectDefaults()
|
|
{
|
|
GameObject obj;
|
|
assert(obj.id == 0);
|
|
assert(obj.type == GameObjectType::Player);
|
|
assert(obj.position.x == 0);
|
|
assert(obj.position.y == 0);
|
|
assert(obj.velocity.x == 0);
|
|
assert(obj.velocity.y == 0);
|
|
assert(obj.sprite == nullptr);
|
|
assert(obj.active == true);
|
|
assert(obj.solid == true);
|
|
assert(obj.flip_h == false);
|
|
}
|
|
|
|
void TestGameObjectWorldCollider()
|
|
{
|
|
GameObject obj;
|
|
obj.position = Math::Vector2Int(100, 200);
|
|
obj.collider = RenderData::BoundingBox2D(
|
|
Math::Vector2Int(0, 0),
|
|
Math::Vector2Int(32, 32));
|
|
|
|
RenderData::BoundingBox2D world = obj.get_world_collider();
|
|
assert(world.min.x == 100);
|
|
assert(world.min.y == 200);
|
|
assert(world.max.x == 132);
|
|
assert(world.max.y == 232);
|
|
}
|
|
|
|
void TestLevelAddAndGet()
|
|
{
|
|
Level level;
|
|
assert(level.object_count() == 0);
|
|
|
|
GameObject platform;
|
|
platform.type = GameObjectType::Collectible;
|
|
platform.position = Math::Vector2Int(0, 400);
|
|
uint32_t id1 = level.add_object(platform);
|
|
assert(id1 == 1);
|
|
assert(level.object_count() == 1);
|
|
|
|
GameObject player;
|
|
player.type = GameObjectType::Player;
|
|
player.position = Math::Vector2Int(50, 350);
|
|
uint32_t id2 = level.add_object(player);
|
|
assert(id2 == 2);
|
|
assert(level.object_count() == 2);
|
|
|
|
const GameObject* found = level.get_object(id1);
|
|
assert(found != nullptr);
|
|
assert(found->type == GameObjectType::Collectible);
|
|
assert(found->id == 1);
|
|
|
|
const GameObject* not_found = level.get_object(999);
|
|
assert(not_found == nullptr);
|
|
}
|
|
|
|
void TestLevelRemove()
|
|
{
|
|
Level level;
|
|
GameObject obj;
|
|
obj.type = GameObjectType::Collectible;
|
|
uint32_t id = level.add_object(obj);
|
|
assert(level.object_count() == 1);
|
|
|
|
level.remove_object(id);
|
|
assert(level.object_count() == 0);
|
|
assert(level.get_object(id) == nullptr);
|
|
}
|
|
|
|
void TestLevelQueryByType()
|
|
{
|
|
Level level;
|
|
|
|
GameObject platform;
|
|
platform.type = GameObjectType::Collectible;
|
|
level.add_object(platform);
|
|
|
|
GameObject light_plat;
|
|
light_plat.type = GameObjectType::LightPlatform;
|
|
level.add_object(light_plat);
|
|
|
|
GameObject player;
|
|
player.type = GameObjectType::Player;
|
|
level.add_object(player);
|
|
|
|
auto platforms = level.get_objects_by_type(GameObjectType::Collectible);
|
|
assert(platforms.size() == 1);
|
|
assert(platforms[0]->type == GameObjectType::Collectible);
|
|
|
|
auto players = level.get_objects_by_type(GameObjectType::Player);
|
|
assert(players.size() == 1);
|
|
|
|
auto doors = level.get_objects_by_type(GameObjectType::Door);
|
|
assert(doors.size() == 0);
|
|
}
|
|
|
|
void TestLevelQueryRegion()
|
|
{
|
|
Level level;
|
|
|
|
GameObject obj1;
|
|
obj1.type = GameObjectType::Collectible;
|
|
obj1.position = Math::Vector2Int(100, 100);
|
|
obj1.collider = RenderData::BoundingBox2D(
|
|
Math::Vector2Int(0, 0), Math::Vector2Int(32, 32));
|
|
level.add_object(obj1);
|
|
|
|
GameObject obj2;
|
|
obj2.type = GameObjectType::Collectible;
|
|
obj2.position = Math::Vector2Int(500, 500);
|
|
obj2.collider = RenderData::BoundingBox2D(
|
|
Math::Vector2Int(0, 0), Math::Vector2Int(32, 32));
|
|
level.add_object(obj2);
|
|
|
|
RenderData::BoundingBox2D query(
|
|
Math::Vector2Int(80, 80), Math::Vector2Int(200, 200));
|
|
auto results = level.query_region(query);
|
|
assert(results.size() == 1);
|
|
assert(results[0]->position.x == 100);
|
|
}
|
|
|
|
void TestLevelInactiveObjectsExcluded()
|
|
{
|
|
Level level;
|
|
|
|
GameObject obj;
|
|
obj.type = GameObjectType::Collectible;
|
|
obj.active = true;
|
|
uint32_t id = level.add_object(obj);
|
|
|
|
auto active = level.get_objects_by_type(GameObjectType::Collectible);
|
|
assert(active.size() == 1);
|
|
|
|
GameObject* found = level.get_object(id);
|
|
found->active = false;
|
|
|
|
auto active2 = level.get_objects_by_type(GameObjectType::Collectible);
|
|
assert(active2.size() == 0);
|
|
}
|
|
|
|
void TestLevelCheckpoints()
|
|
{
|
|
Level level;
|
|
level.set_spawn_point(Math::Vector2Int(50, 300));
|
|
|
|
Checkpoint cp1(200, 300);
|
|
Checkpoint cp2(400, 200);
|
|
level.add_checkpoint(cp1);
|
|
level.add_checkpoint(cp2);
|
|
|
|
Math::Vector2Int respawn = level.get_active_checkpoint();
|
|
assert(respawn.x == 50);
|
|
assert(respawn.y == 300);
|
|
|
|
level.get_all_objects();
|
|
auto& cps = level.get_checkpoints();
|
|
const_cast<Checkpoint&>(cps[0]).activated = true;
|
|
|
|
respawn = level.get_active_checkpoint();
|
|
assert(respawn.x == 200);
|
|
assert(respawn.y == 300);
|
|
|
|
const_cast<Checkpoint&>(cps[1]).activated = true;
|
|
respawn = level.get_active_checkpoint();
|
|
assert(respawn.x == 400);
|
|
assert(respawn.y == 200);
|
|
}
|
|
|
|
void TestAabbOverlap()
|
|
{
|
|
RenderData::BoundingBox2D a(Math::Vector2Int(0, 0), Math::Vector2Int(10, 10));
|
|
RenderData::BoundingBox2D b(Math::Vector2Int(5, 5), Math::Vector2Int(15, 15));
|
|
assert(aabb_overlap(a, b) == true);
|
|
|
|
RenderData::BoundingBox2D c(Math::Vector2Int(20, 20), Math::Vector2Int(30, 30));
|
|
assert(aabb_overlap(a, c) == false);
|
|
|
|
RenderData::BoundingBox2D d(Math::Vector2Int(10, 0), Math::Vector2Int(20, 10));
|
|
assert(aabb_overlap(a, d) == false);
|
|
|
|
RenderData::BoundingBox2D e(Math::Vector2Int(9, 0), Math::Vector2Int(19, 10));
|
|
assert(aabb_overlap(a, e) == true);
|
|
}
|
|
|
|
void TestGravityAccumulation()
|
|
{
|
|
Physics2D physics;
|
|
PhysicsConfig config;
|
|
config.gravity = 1000;
|
|
config.max_fall_speed = 500;
|
|
physics.set_config(config);
|
|
|
|
GameObject obj;
|
|
obj.velocity = Math::Vector2Int(0, 0);
|
|
|
|
physics.apply_gravity(obj, 16);
|
|
assert(obj.velocity.y > 0);
|
|
assert(obj.velocity.y <= config.max_fall_speed);
|
|
|
|
for (int i = 0; i < 100; ++i)
|
|
{
|
|
physics.apply_gravity(obj, 16);
|
|
}
|
|
assert(obj.velocity.y == config.max_fall_speed);
|
|
}
|
|
|
|
void TestGroundDetection()
|
|
{
|
|
Level level;
|
|
level.set_bounds(LevelBounds(0, 0, 800, 600));
|
|
|
|
GameObject ground;
|
|
ground.type = GameObjectType::Collectible;
|
|
ground.position = Math::Vector2Int(0, 400);
|
|
ground.collider = RenderData::BoundingBox2D(
|
|
Math::Vector2Int(0, 0), Math::Vector2Int(800, 32));
|
|
ground.solid = true;
|
|
level.add_object(ground);
|
|
|
|
GameObject player;
|
|
player.type = GameObjectType::Player;
|
|
player.position = Math::Vector2Int(100, 368);
|
|
player.collider = RenderData::BoundingBox2D(
|
|
Math::Vector2Int(0, 0), Math::Vector2Int(16, 32));
|
|
|
|
Physics2D physics;
|
|
assert(physics.is_grounded(player, level) == true);
|
|
|
|
player.position = Math::Vector2Int(100, 300);
|
|
assert(physics.is_grounded(player, level) == false);
|
|
}
|
|
|
|
void TestOneWayPlatform()
|
|
{
|
|
Level level;
|
|
LevelBounds bounds(0, 0, 800, 600);
|
|
level.set_bounds(bounds);
|
|
|
|
GameObject platform;
|
|
platform.type = GameObjectType::LightPlatform;
|
|
platform.position = Math::Vector2Int(100, 300);
|
|
platform.collider = RenderData::BoundingBox2D(
|
|
Math::Vector2Int(0, 0), Math::Vector2Int(64, 16));
|
|
platform.solid = true;
|
|
level.add_object(platform);
|
|
|
|
GameObject player;
|
|
player.type = GameObjectType::Player;
|
|
player.position = Math::Vector2Int(110, 320);
|
|
player.collider = RenderData::BoundingBox2D(
|
|
Math::Vector2Int(0, 0), Math::Vector2Int(16, 32));
|
|
player.velocity = Math::Vector2Int(0, -100);
|
|
|
|
Physics2D physics;
|
|
physics.move_and_collide(player, level, 16);
|
|
|
|
assert(player.position.y < 320);
|
|
}
|
|
|
|
void TestLightPlatformActivation()
|
|
{
|
|
LightEffectSystem system;
|
|
|
|
GameObject light_plat;
|
|
light_plat.type = GameObjectType::LightPlatform;
|
|
light_plat.light_threshold.min_level = 2000;
|
|
light_plat.solid = false;
|
|
|
|
std::vector<GameObject> objects;
|
|
objects.push_back(light_plat);
|
|
|
|
system.update(objects, 1000);
|
|
assert(objects[0].solid == false);
|
|
|
|
system.update(objects, 2500);
|
|
assert(objects[0].solid == true);
|
|
|
|
system.update(objects, 2000);
|
|
assert(objects[0].solid == true);
|
|
}
|
|
|
|
void TestShadowPlatformActivation()
|
|
{
|
|
LightEffectSystem system;
|
|
|
|
GameObject shadow_plat;
|
|
shadow_plat.type = GameObjectType::ShadowPlatform;
|
|
shadow_plat.light_threshold.max_level = 1000;
|
|
shadow_plat.solid = true;
|
|
|
|
std::vector<GameObject> objects;
|
|
objects.push_back(shadow_plat);
|
|
|
|
system.update(objects, 2000);
|
|
assert(objects[0].solid == false);
|
|
|
|
system.update(objects, 500);
|
|
assert(objects[0].solid == true);
|
|
|
|
system.update(objects, 1000);
|
|
assert(objects[0].solid == true);
|
|
}
|
|
|
|
void TestLightDoorOpen()
|
|
{
|
|
LightEffectSystem system;
|
|
|
|
GameObject door;
|
|
door.type = GameObjectType::Door;
|
|
door.light_threshold.min_level = 1500;
|
|
door.light_threshold.max_level = 2500;
|
|
door.solid = true;
|
|
|
|
std::vector<GameObject> objects;
|
|
objects.push_back(door);
|
|
|
|
system.update(objects, 1000);
|
|
assert(objects[0].solid == true);
|
|
|
|
system.update(objects, 2000);
|
|
assert(objects[0].solid == false);
|
|
|
|
system.update(objects, 3000);
|
|
assert(objects[0].solid == true);
|
|
|
|
system.update(objects, 1500);
|
|
assert(objects[0].solid == false);
|
|
|
|
system.update(objects, 2500);
|
|
assert(objects[0].solid == false);
|
|
}
|
|
|
|
void TestLightEffectIgnoresOtherTypes()
|
|
{
|
|
LightEffectSystem system;
|
|
|
|
GameObject collectible;
|
|
collectible.type = GameObjectType::Collectible;
|
|
collectible.solid = true;
|
|
|
|
GameObject trigger;
|
|
trigger.type = GameObjectType::Trigger;
|
|
trigger.solid = true;
|
|
|
|
std::vector<GameObject> objects;
|
|
objects.push_back(collectible);
|
|
objects.push_back(trigger);
|
|
|
|
system.update(objects, 0);
|
|
assert(objects[0].solid == true);
|
|
assert(objects[1].solid == true);
|
|
|
|
system.update(objects, 4095);
|
|
assert(objects[0].solid == true);
|
|
assert(objects[1].solid == true);
|
|
}
|
|
|
|
void TestLevelLoaderBasic()
|
|
{
|
|
const uint16_t tiles[] = {
|
|
0, 0, 0,
|
|
0, 0, 0,
|
|
1, 1, 1,
|
|
};
|
|
|
|
const ObjectSpawn spawns[] = {
|
|
{ GameObjectType::Player, 1, 1, 0, 0, TriggerAction::None, 0 },
|
|
{ GameObjectType::Collectible, 0, 2, 0, 0, TriggerAction::None, 0 },
|
|
};
|
|
|
|
const LevelData data = {
|
|
tiles,
|
|
3,
|
|
3,
|
|
16,
|
|
nullptr,
|
|
0,
|
|
spawns,
|
|
2,
|
|
nullptr,
|
|
0,
|
|
Math::Vector2Int(16, 16),
|
|
0,
|
|
0,
|
|
48,
|
|
48
|
|
};
|
|
|
|
Level level;
|
|
LevelLoader::load(level, data);
|
|
|
|
assert(level.object_count() == 2);
|
|
assert(level.get_bounds().max_x == 48);
|
|
assert(level.get_bounds().max_y == 48);
|
|
assert(level.get_spawn_point().x == 16);
|
|
assert(level.get_spawn_point().y == 16);
|
|
|
|
const RenderData::Tilemap& tm = level.get_tilemap();
|
|
assert(tm.width == 3);
|
|
assert(tm.height == 3);
|
|
assert(tm.tiles == tiles);
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
TestGameObjectDefaults();
|
|
TestGameObjectWorldCollider();
|
|
TestLevelAddAndGet();
|
|
TestLevelRemove();
|
|
TestLevelQueryByType();
|
|
TestLevelQueryRegion();
|
|
TestLevelInactiveObjectsExcluded();
|
|
TestLevelCheckpoints();
|
|
TestAabbOverlap();
|
|
TestGravityAccumulation();
|
|
TestGroundDetection();
|
|
TestOneWayPlatform();
|
|
TestLightPlatformActivation();
|
|
TestShadowPlatformActivation();
|
|
TestLightDoorOpen();
|
|
TestLightEffectIgnoresOtherTypes();
|
|
TestLevelLoaderBasic();
|
|
std::cout << "game_engine_tests: PASS\n";
|
|
return 0;
|
|
}
|