diff --git a/The Great Machine.zip b/OLCCodeJam2020-The-Great-Machine.zip similarity index 61% rename from The Great Machine.zip rename to OLCCodeJam2020-The-Great-Machine.zip index c7e00c9..99f768b 100644 Binary files a/The Great Machine.zip and b/OLCCodeJam2020-The-Great-Machine.zip differ diff --git a/TODO b/TODO index f3a4697..8375f5a 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,6 @@ -[ ] Dungeon generation -[ ] Tile movement / playable character +[x] Dungeon generation +[x] Tile movement / playable character +[-] Tile collisions [ ] Lighting [ ] Gameplay [ ] Actually figure out Gameplay diff --git a/The Great Machine.exe b/The Great Machine.exe index f5c3272..1e5915a 100644 Binary files a/The Great Machine.exe and b/The Great Machine.exe differ diff --git a/The Great Machine/Camera.hpp b/The Great Machine/Camera.hpp index 3925f20..5c53f35 100644 --- a/The Great Machine/Camera.hpp +++ b/The Great Machine/Camera.hpp @@ -7,23 +7,23 @@ class Entity; class Camera { -public: + public: olc::vf2d Coords; olc::vi2d ViewPort; - + void Update(float fTime); - + void Input(olc::PixelGameEngine* engine); - + void TrackEntity(Entity* e); - -private: - + + private: + Entity* _Track = nullptr; - + olc::vi2d _DesiredCoords; - float _SmoothSpeed = 0.0125f; - + float _SmoothSpeed = 0.01f; + }; #endif diff --git a/The Great Machine/Collisions.hpp b/The Great Machine/Collisions.hpp index 0c8cd85..86f5697 100644 --- a/The Great Machine/Collisions.hpp +++ b/The Great Machine/Collisions.hpp @@ -1,6 +1,25 @@ #ifndef GREATMACHINE_COLLISIONS_H_ #define GREATMACHINE_COLLISIONS_H_ +class Entity; +// class + +class Collider +{ + public: + int x, y, w, h; +}; + +class CollisionInfo +{ +public: + +}; + + + +// bool EntityCollide + #endif diff --git a/The Great Machine/Dungeon.cpp b/The Great Machine/Dungeon.cpp index 1878748..14f83c2 100644 --- a/The Great Machine/Dungeon.cpp +++ b/The Great Machine/Dungeon.cpp @@ -1,23 +1,26 @@ #include "Dungeon.hpp" #include +#include +#include "Collisions.hpp" #include "Things.hpp" #include "Camera.hpp" #include "Logger.hpp" Dungeon::Dungeon() - : _Logger(Logger::getInstance()) +: _Logger(Logger::getInstance()) { ActiveCamera = new Camera(); ActiveCamera->Coords = { 0, 0 }; ActiveCamera->ViewPort = { 1280, 720 }; - + Player = new Playable(); - + Player->Coords = { 0, 0 }; Player->Type = EEntity::Type::Player; - + Player->HitBox = new Collider{ 0, 0, static_cast((static_cast(TileSize) / 3.0f) * 2.0f), static_cast((static_cast(TileSize) / 3.0f) * 2.0f) } ; + ActiveCamera->TrackEntity(Player); ActiveCamera->Update(0.0f); } @@ -25,17 +28,17 @@ Dungeon::Dungeon() void Dungeon::Generate() { srand(time(NULL)); - + TileSetDictionary = new TileDictionary(); TileSetDictionary->Register(); - + TileSet = new olc::Renderable(); TileSet->Load("res/dungeon_tileset.png"); _Logger.Debug("Texture Loaded: ", TileSet, " ", TileSet->Sprite()->width, " ", TileSet->Sprite()->height); - + DungeonWidth = 0; DungeonHeight = 0; - + // Generate a dungeon // Loosely follows the algorithm in section 3.3 "Agent Based Growing" // http://pcgbook.com/wp-content/uploads/chapter03.pdf @@ -44,39 +47,39 @@ void Dungeon::Generate() { int x, y, w, h; }; - + auto randomRoom = [](int x, int y) - { return new Room{ x, y, (rand() % 7) + 3, (rand() % 7) + 3 }; }; - + { return new Room{ x, y, (rand() % 7) + 3, (rand() % 7) + 3 }; }; + std::vector tiles; - + // Starting at 0,0 - + int directionChance = 5; int roomChance = 5; int dungeonMinSize = 5000; - + struct Agent { // 0 up 1 right 2 down 3 left int x, y, direction; }; - + Agent* agent = new Agent(); agent->x = 0; agent->y = 0; agent->direction = rand() % 4; - + _Logger.Debug("Agent ", agent->x, " ", agent->y, " ", agent->direction); - + auto addTile = [&](int x, int y) { for (auto i : tiles) if (i.x == x && i.y == y) - return; + return; tiles.push_back({ x, y }); }; - + addTile(0, 0); - + while (tiles.size() < dungeonMinSize) { switch (agent->direction) @@ -97,12 +100,12 @@ void Dungeon::Generate() { directionChance += 5; } - + if (rand() % 300 < roomChance) { Room* room = randomRoom(agent->x, agent->y); for (int x = room->x; x < room->w + room->x; x++) - for (int y = room->y; y < room->h + room->y; y++) + for (int y = room->y; y < room->h + room->y; y++) addTile(x, y); delete room; roomChance = 0; @@ -112,24 +115,26 @@ void Dungeon::Generate() roomChance += 5; } } - + for (auto& tile : tiles) { if (DungeonWidth <= tile.x) DungeonWidth = tile.x; if (DungeonHeight <= tile.y) DungeonHeight = tile.y; - + ETile::Type tileType = rand() % 100 > 1 ? ETile::Type::Floor : ETile::Type::FloorV1; - + auto DoesTileExist = [&](olc::vi2d tile) { return std::find(tiles.begin(), tiles.end(), tile) != tiles.end(); }; - + bool tileBelow = DoesTileExist({ tile.x, tile.y + 1 }); bool tileAbove = DoesTileExist({ tile.x, tile.y - 1 }); bool tileLeft = DoesTileExist({ tile.x - 1, tile.y }); bool tileRight = DoesTileExist({ tile.x + 1, tile.y }); - + + // Order here is very important! stop fucking it up + if (!tileAbove) tileType = ETile::Type::WallU; if (!tileRight) @@ -138,12 +143,12 @@ void Dungeon::Generate() tileType = ETile::Type::WallD; if (!tileLeft) tileType = ETile::Type::WallL; - + if (!tileAbove && !tileBelow) tileType = ETile::Type::PathAcross; if (!tileRight && !tileLeft) tileType = ETile::Type::PathUp; - + if (!tileAbove && !tileLeft) tileType = ETile::Type::WallTLCorner; if (!tileAbove && !tileRight) @@ -152,7 +157,7 @@ void Dungeon::Generate() tileType = ETile::Type::WallBLCorner; if (!tileBelow && !tileRight) tileType = ETile::Type::WallBRCorner; - + if (!tileAbove && !tileBelow && !tileRight) tileType = ETile::Type::PathRight; if (!tileAbove && !tileBelow && !tileLeft) @@ -161,68 +166,119 @@ void Dungeon::Generate() tileType = ETile::Type::PathTop; if (!tileRight && !tileLeft && !tileBelow) tileType = ETile::Type::PathBottom; - + // Is the tile below free? if so, make it 3D if (!tileBelow) { Tile* psuedo3DTile = new Tile({ tile.x, tile.y + 1 }, - ETile::Type::ThreeDStandard, ETile::State::Default); + ETile::Type::ThreeDStandard, ETile::State::Default); DungeonTiles[psuedo3DTile->Coords] = psuedo3DTile; } - + Tile* t = new Tile(tile, tileType, ETile::State::Default); DungeonTiles[t->Coords] = t; } - - //DungeonRenderTarget = new olc::Renderable(); - //DungeonRenderTarget->Create(DungeonWidth * TileSize, DungeonHeight * TileSize); - //DungeonRenderTarget->Create(200, 200); } void Dungeon::SpawnEntity(Entity* entity) { - Entities[entity->Coords] = entity; + Entities[entity->Coords] = entity; } void Dungeon::Input(olc::PixelGameEngine* engine, float fTime) { if (engine->GetKey(olc::W).bHeld) - Player->Coords.y -= static_cast(TileSize) * (fTime * 10.0f); + Player->Coords.y -= static_cast(TileSize) * (fTime * Player->Speed); if (engine->GetKey(olc::A).bHeld) - Player->Coords.x -= static_cast(TileSize) * (fTime * 10.0f); + Player->Coords.x -= static_cast(TileSize) * (fTime * Player->Speed); if (engine->GetKey(olc::S).bHeld) - Player->Coords.y += static_cast(TileSize) * (fTime * 10.0f); + Player->Coords.y += static_cast(TileSize) * (fTime * Player->Speed); if (engine->GetKey(olc::D).bHeld) - Player->Coords.x += static_cast(TileSize) * (fTime * 10.0f); + Player->Coords.x += static_cast(TileSize) * (fTime * Player->Speed); + + if (engine->GetKey(olc::W).bHeld && engine->GetKey(olc::A).bHeld) + { + Player->Coords.y += static_cast(TileSize) * (fTime * (Player->Speed / 3.0f)); + Player->Coords.x += static_cast(TileSize) * (fTime * (Player->Speed / 3.0f)); + } + if (engine->GetKey(olc::W).bHeld && engine->GetKey(olc::D).bHeld) + { + Player->Coords.y += static_cast(TileSize) * (fTime * (Player->Speed / 3.0f)); + Player->Coords.x -= static_cast(TileSize) * (fTime * (Player->Speed / 3.0f)); + } + if (engine->GetKey(olc::S).bHeld && engine->GetKey(olc::D).bHeld) + { + Player->Coords.y -= static_cast(TileSize) * (fTime * (Player->Speed / 3.0f)); + Player->Coords.x -= static_cast(TileSize) * (fTime * (Player->Speed / 3.0f)); + } + if (engine->GetKey(olc::S).bHeld && engine->GetKey(olc::A).bHeld) + { + Player->Coords.y -= static_cast(TileSize) * (fTime * (Player->Speed / 3.0f)); + Player->Coords.x += static_cast(TileSize) * (fTime * (Player->Speed / 3.0f)); + } } void Dungeon::Update(float fTime) { - - - + + // Map collisions + + // generate a 3x3 bool map around where the player is + std::vector colliderMap; + colliderMap.reserve(9); + + olc::vi2d currentTile = { static_cast(Player->Coords.x / TileSize), static_cast(Player->Coords.y / TileSize) }; + + static olc::vi2d lastTile; + + auto IsVoid = [&] (int x, int y) { + std::unordered_map::const_iterator found = DungeonTiles.find({x, y}); + if (found == DungeonTiles.end()) return true; + if (DungeonTiles[{x, y}]->Type == ETile::Type::ThreeDStandard) return true; + if (DungeonTiles[{x, y}]->Type == ETile::Type::Void) return true; + return false; + }; + + if (lastTile != currentTile) + _Logger.Debug("Player Tile: ", currentTile.x, " ", currentTile.y, " ISVOID: ", IsVoid(currentTile.x, currentTile.y)); + + + + lastTile = currentTile; ActiveCamera->Update(fTime); } void Dungeon::Draw(olc::PixelGameEngine* engine) { - //int - //engine->SetDrawTarget(DungeonRenderTarget->Sprite()); + // Maps not gonna be big enough for me to care about optimistaion + // maybe i should care? i don't :^) + + //engine->SetDrawTarget(4); + //engine->Clear({38, 36, 40}); + + engine->SetDrawTarget(1); + engine->Clear({38, 36, 40}); + + engine->SetPixelMode(olc::Pixel::ALPHA); for (std::pair tile : DungeonTiles) { - // TODO: Perform culling - + // if (tile.second->Type == ETile::Type::Void) continue; + engine->DrawPartialDecal({ static_cast((tile.first.x * TileSize) - ActiveCamera->Coords.x), static_cast((tile.first.y * TileSize) - ActiveCamera->Coords.y) }, - { static_cast(TileSize), static_cast(TileSize) }, TileSet->Decal(), TileSetDictionary->Dictionary[tile.second->Type], { 16, 16 }); + { static_cast(TileSize), static_cast(TileSize) }, TileSet->Decal(), TileSetDictionary->Dictionary[tile.second->Type], { 16, 16 }); } - - //engine->SetDrawTarget(1); - //engine->DrawSprite({ 0, 0 }, DungeonRenderTarget->Sprite()); - + + // engine->SetPixelMode(olc::Pixel::NORMAL); + + engine->SetDrawTarget(static_cast(0)); + engine->Clear(olc::BLANK); + + engine->FillRect({0, 0}, {TileSize, TileSize}, olc::RED); + // Draw character engine->DrawPartialDecal({ static_cast(Player->Coords.x - ActiveCamera->Coords.x), static_cast(Player->Coords.y - ActiveCamera->Coords.y) }, - { (static_cast(TileSize) / 3.0f) * 2.0f, (static_cast(TileSize) / 3.0f) * 2.0f }, TileSet->Decal(), { 143, 130 }, { 16, 16 }); - + { (static_cast(TileSize) / 3.0f) * 2.0f, (static_cast(TileSize) / 3.0f) * 2.0f }, TileSet->Decal(), { 143, 130 }, { 16, 16 }); + } Dungeon::~Dungeon() diff --git a/The Great Machine/Dungeon.hpp b/The Great Machine/Dungeon.hpp index c222c96..164f2a9 100644 --- a/The Great Machine/Dungeon.hpp +++ b/The Great Machine/Dungeon.hpp @@ -18,35 +18,35 @@ class TileDictionary; // Single level dungeon, no need for fancy levels class Dungeon { -public: + public: Dungeon(); - + void Generate(); void SpawnEntity(Entity* entity); - + void Input(olc::PixelGameEngine* engine, float fTime); void Update(float fTime); void Draw(olc::PixelGameEngine* engine); - + Playable* Player; Camera* ActiveCamera; - - int TileSize = 64; - + + int TileSize = 32; + int DungeonWidth; int DungeonHeight; std::unordered_map DungeonTiles; - std::unordered_map Entities; // key here could be room? + std::unordered_map Entities; std::unordered_map FixedItems; - + TileDictionary* TileSetDictionary; olc::Renderable* TileSet; - + olc::Renderable* DungeonRenderTarget; - + ~Dungeon(); - -private: + + private: Logger& _Logger; }; diff --git a/The Great Machine/Logger.hpp b/The Great Machine/Logger.hpp index 9f15cdd..2d4ddb6 100644 --- a/The Great Machine/Logger.hpp +++ b/The Great Machine/Logger.hpp @@ -74,7 +74,7 @@ namespace EConsoleColour class Colour { -public: + public: static inline void ResetColour() { #if defined(_WINDOWS) @@ -86,9 +86,9 @@ public: << "\033[" << EConsoleColour::BG_DEFAULT << "m"; #endif } - + template - static inline void ConsoleColour(T colour) + static inline void ConsoleColour(T colour) { #if defined(_WINDOWS) SetConsoleTextAttribute(h, colour); @@ -100,7 +100,7 @@ public: class LogManager { - + }; namespace ELogType @@ -125,21 +125,21 @@ struct LogEntity // Config must be loaded first class Logger { -public: - + public: + Logger(); - + static Logger& getInstance() { static Logger instance; return instance; } - + void InitializeFileLogging(std::filesystem::path path); void InitializeLoggingThread(); - + template - void LogElement(T t) + void LogElement(T t) { if constexpr (std::is_same::value) { @@ -148,12 +148,13 @@ public: } std::cout << t; } - + // Basic log doesn't use the logthread so // can be used before the thread is setup // unless it is already ready + // Does not support the logging of vectors template - void BasicLog(Args... args) + void BasicLog(Args... args) { while (_IsLogging) {} _IsLogging = true; @@ -161,7 +162,7 @@ public: LogElement('\n'); _IsLogging = false; } - + void FastLog(std::string args) { if (!_IsRunning) @@ -172,12 +173,12 @@ public: _QueueLock.lock(); _LogQueue.push(new LogEntity{ args, ELogType::NONE }); _QueueLock.unlock(); - + _TaskEnqueued.notify_one(); } - + template - void Log(Args... args) + void Log(Args... args) { if (!_IsRunning) { @@ -187,16 +188,16 @@ public: std::stringstream s; _FillStringStream(s, args...); LogEntity* e = new LogEntity{ s.str(), ELogType::NONE }; - + _QueueLock.lock(); _LogQueue.push(e); _QueueLock.unlock(); - + _TaskEnqueued.notify_one(); } - + template - void Info(Args... args) + void Info(Args... args) { if (!_IsRunning) { @@ -205,9 +206,9 @@ public: } _Log(ELogType::INFO, args...); } - + template - void Debug(Args... args) + void Debug(Args... args) { if (!_IsRunning) { @@ -216,9 +217,9 @@ public: } _Log(ELogType::DEBUG, args...); } - + template - void Warn(Args... args) + void Warn(Args... args) { if (!_IsRunning) { @@ -227,9 +228,9 @@ public: } _Log(ELogType::WARN, args...); } - + template - void Error(Args... args) + void Error(Args... args) { if (!_IsRunning) { @@ -238,9 +239,9 @@ public: } _Log(ELogType::ERR, args...); } - + template - void Panic(Args... args) + void Panic(Args... args) { if (!_IsRunning) { @@ -250,60 +251,60 @@ public: } _Log(ELogType::PANIC, args...); } - + ~Logger(); - -public: - + + public: + std::atomic _IsRunning = false; - + std::condition_variable _TaskEnqueued; std::queue _LogQueue; std::mutex _QueueLock; std::atomic _IsLogging = false; - + std::atomic _HasFileHandle = false; std::ofstream _FileOutput; - -private: - + + private: + template - void _FillStringStream(std::stringstream& s, T head, A... a) + void _FillStringStream(std::stringstream& s, T head, A... a) { - s << head;// << ' '; + s << head; if constexpr (sizeof...(a)) { _FillStringStream(s, a...); } } - + template - void _Log(ELogType::Type type, Args... args) + void _Log(ELogType::Type type, Args... args) { std::stringstream s; _FillStringStream(s, args...); LogEntity* e = new LogEntity{ s.str(), type }; - + _QueueLock.lock(); _LogQueue.push(e); _QueueLock.unlock(); - + _TaskEnqueued.notify_one(); } - + std::thread* _OutputWorker; - + }; class ScopedLogger { - + }; // times how long the scope took etc class ProfileLogger : public ScopedLogger { - + }; #endif diff --git a/The Great Machine/The Great Machine.vcxproj b/The Great Machine/The Great Machine.vcxproj index 6902a57..1db844f 100644 --- a/The Great Machine/The Great Machine.vcxproj +++ b/The Great Machine/The Great Machine.vcxproj @@ -147,6 +147,7 @@ + @@ -155,6 +156,7 @@ + diff --git a/The Great Machine/The Great Machine.vcxproj.filters b/The Great Machine/The Great Machine.vcxproj.filters index 7593cb7..798274f 100644 --- a/The Great Machine/The Great Machine.vcxproj.filters +++ b/The Great Machine/The Great Machine.vcxproj.filters @@ -33,6 +33,9 @@ Source Files + + Source Files + @@ -53,5 +56,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/The Great Machine/Things.cpp b/The Great Machine/Things.cpp index 8ca32e9..acfcab4 100644 --- a/The Great Machine/Things.cpp +++ b/The Great Machine/Things.cpp @@ -4,11 +4,12 @@ void Tile::Update(float fTime) { - + } void TileDictionary::Register() { + Dictionary[ETile::Type::Void] = { 217, 15 }; Dictionary[ETile::Type::Floor] = { 27, 37 }; Dictionary[ETile::Type::FloorV1] = { 44, 16 }; Dictionary[ETile::Type::WallL] = { 0, 16 }; diff --git a/The Great Machine/Things.hpp b/The Great Machine/Things.hpp index ebe1679..b27e7e9 100644 --- a/The Great Machine/Things.hpp +++ b/The Great Machine/Things.hpp @@ -22,6 +22,7 @@ namespace ETile enum Type { None, + Void, Floor, FloorV1, WallL, @@ -49,13 +50,13 @@ namespace ETile DoorOpenTop, DoorClosedTop, DoorPortcullisTop, - + ThreeDStandard, ThreeDSolid, ThreeDSpike, ThreeDSpikeBottom }; - + enum State { Default, @@ -73,7 +74,7 @@ namespace EEntity Enemy, Boss }; - + namespace EItem { enum Item @@ -81,7 +82,7 @@ namespace EEntity None, Sword }; - + // Fixed items can have states #door open / closed enum FixedItem { @@ -91,58 +92,67 @@ namespace EEntity } } -class HitBox; +class Collider; + class Entity { -public: + public: olc::vf2d Coords; EEntity::Type Type; - HitBox* AABBHitBox; + Collider* HitBox; olc::vf2d SpriteTextureMask; olc::Renderable* SpriteMap; }; class Item : public Entity { -public: + public: EEntity::EItem::Item Item; }; class FixedItem : public Entity { -public: + public: EEntity::EItem::FixedItem Item; }; class Playable : public Entity { -public: + public: + float Speed = 10.0f; + int SelectedInventoryItem = 0; std::array Inventory; }; class TileDictionary { -public: + public: void Register(); std::map Dictionary; }; class Tile { -public: + public: Tile(olc::vi2d coords, ETile::Type type, ETile::State state) : Coords(coords) , Type(type) , State(state) - { } - + { + if (Type == ETile::Type::None || Type == ETile::Type::Void || Type == ETile::Type::ThreeDStandard) + IsSolid = true; + else + IsSolid = false; + } + olc::vi2d Coords; ETile::Type Type; ETile::State State; - - HitBox* AABBHitBox; + bool IsSolid; + + Collider* HitBox; virtual void Update(float fTime); }; diff --git a/The Great Machine/UI.cpp b/The Great Machine/UI.cpp new file mode 100644 index 0000000..e69de29 diff --git a/The Great Machine/UI.hpp b/The Great Machine/UI.hpp new file mode 100644 index 0000000..e69de29 diff --git a/The Great Machine/main.cpp b/The Great Machine/main.cpp index 36c8ffc..898723b 100644 --- a/The Great Machine/main.cpp +++ b/The Great Machine/main.cpp @@ -7,26 +7,29 @@ class Game : public olc::PixelGameEngine { -private: - + private: + Logger& _Logger; Dungeon* _Dungeon; - -public: + + public: Game() : _Logger(Logger::getInstance()) { sAppName = "The Great Machine"; } - + bool OnUserCreate() override { _Dungeon = new Dungeon(); _Dungeon->Generate(); - + + for (int i = 0; i < 5; i++) + CreateLayer(); + return true; } - + void DisplayTitle(float fTime) { if (m_TimeAccumilator > 2.0f) @@ -38,36 +41,38 @@ public: DrawString(5, ScreenHeight() - 15, "Powered by the OLC Pixel Game Engine", olc::Pixel(255, 255, 255, static_cast(m_DeltaFade))); DrawString(ScreenWidth() - (8 * (std::string("Copyright Benjamin Kyd 2020").length())) - 5, ScreenHeight() - 15, "Copyright Benjamin Kyd 2020", olc::Pixel(255, 255, 255, static_cast(m_DeltaFade))); } - + bool OnUserUpdate(float fTime) override { - SetPixelMode(olc::Pixel::ALPHA); m_TimeAccumilator += fTime; - - Clear({ 38, 36, 40 }); - // _Logger.Debug(m_TimeAccumilator); - - //if (m_TimeAccumilator < 4.0f) - //{ - // DisplayTitle(fTime); - // return true; - //} - - _Dungeon->Input(this, fTime); - + Clear({38, 36, 40}); + + // _Logger.Debug(m_TimeAccumilator); + + //if (m_TimeAccumilator < 4.0f) + //{ + //DisplayTitle(fTime); + //return true; + //} + + _Dungeon->Input(this, fTime); + _Dungeon->Update(fTime); - + _Dungeon->Draw(this); - + + for (int i = 0; i < 5; i++) + EnableLayer(i, true); + return true; } - -private: + + private: long float m_TimeAccumilator = 0; - + float m_DeltaFade = 255.0f; - + }; @@ -76,7 +81,7 @@ int main() Logger& _Logger = Logger::getInstance(); _Logger.InitializeFileLogging("./logs.log"); _Logger.InitializeLoggingThread(); - + Game _Game; _Game.Construct(1280, 720, 1, 1, false, false); _Logger.Info("Game Constructed");