diff --git a/resources/levels/01.txt b/resources/levels/01.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a53f3f6c3645ee5cf43ddf60bb9ef0d46174ed71
--- /dev/null
+++ b/resources/levels/01.txt
@@ -0,0 +1,10 @@
+LEVEL:
+    start
+NEXT:
+    level_01
+ENEMIES:
+    120 240
+    240 240
+    360 240
+    480 240
+    600 240
\ No newline at end of file
diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt
index a23c6dbcd0a0dfbac900e7db99dec8e7551aa323..bd1b739cb642f979209b3e27ca833c3d31a6e2f4 100644
--- a/src/game/CMakeLists.txt
+++ b/src/game/CMakeLists.txt
@@ -1,5 +1,4 @@
 add_subdirectory(state)
-add_subdirectory(levels)
 add_subdirectory(input)
 add_subdirectory(renderer)
 
diff --git a/src/game/game.cpp b/src/game/game.cpp
index e557ca81552b6477bbb46e0aef0a0b31e8211578..4d7fcbcbe1a7ef74cff40c30ef4f604ffb5dd711 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -5,6 +5,7 @@
 #include "renderer/sdl_renderer.h"
 #include "single_shot.h"
 #include "multi_shot.h"
+#include "state/level.h"
 
 #include <variant>
 #include <chrono>
@@ -28,6 +29,14 @@ game::game(uint32_t fps):current_state(),
     shoot_control = new single_shot();
     spdlog::get("game")->debug("Fps set to: {} and delta_time set to: {}", fps, delta_time);
     spdlog::get("game")->info("Finished game component initialization.");
+
+    spdlog::get("game")->info("Loading levels");
+    level::create().from_file("levels/01.txt");
+    level::create().with_enemy_at({320, 120}).with_enemy_at({160, 240}).with_enemy_at({480, 360}).save_as("level_01");
+
+    level first_level = std::move(level::get("start"));
+    current_state.enemies = first_level.enemies;
+    current_state.next_level_id = first_level.next_level_id;
 }
 
 game::~game() {
@@ -50,6 +59,8 @@ void game::run_loop() {
         process_events();
         update_state();
         render_current_state();
+        if (try_to_switch_level())
+            break;
         auto end = std::chrono::system_clock::now();
         std::chrono::duration<double> diff = end - start;
         SDL_Delay(static_cast<uint32_t>((delta_time - diff.count())*1000));
@@ -57,6 +68,20 @@ void game::run_loop() {
     spdlog::get("game")->info("Game loop ended");
 }
 
+bool game::try_to_switch_level() {
+    if (current_state.enemies.size() == 0 && current_state.projectiles.size() == 0) {
+        if (current_state.next_level_id != "") {
+            level next_level = std::move(level::get(current_state.next_level_id));
+            current_state.enemies = next_level.enemies;
+            current_state.next_level_id = next_level.next_level_id;
+        } else {
+            renderer->show_win_dialog();
+            return true;
+        }
+    }
+    return false;
+}
+
 void game::process_events() {
     input_processor->poll_events();
     while(input_processor->has_events()) {
@@ -90,7 +115,7 @@ void game::process_events() {
                 current_state = std::move(state::pop());
         });
         do_event<shoot_event>(e, [&](auto&) {shoot = true;});
-        do_event<switch_controller>(e, [&](auto& sc) {
+        do_event<switch_controller>(e, [&](const auto& sc) {
             if (shoot_control->can_switch()) {
                 delete shoot_control;
                 switch(sc.mode) {
diff --git a/src/game/game.h b/src/game/game.h
index ac0cf98cf51ac02309facd3bf61773874658472b..11d0a96fd3e1990213c4b1a2d9a45618a6a06580 100644
--- a/src/game/game.h
+++ b/src/game/game.h
@@ -30,6 +30,8 @@ protected:
     /// Helper function that processes event of defined type using provided funtion (lambda) 
     template<typename T>
     void do_event(const event& event, const std::function<void(const T&)>& trigger);
+    /// Tries to load next level. Returns true if can't load any new levels.
+    bool try_to_switch_level();
 
     /// Current game state
     state current_state;
diff --git a/src/game/input/abstract_input.h b/src/game/input/abstract_input.h
index 433b6a000480cae3427c0a83f361fc1f86d3cb24..d86a83a4444b99dbd6327f1402ff4f345b3ef416 100644
--- a/src/game/input/abstract_input.h
+++ b/src/game/input/abstract_input.h
@@ -4,17 +4,17 @@
 #include <vector>
 #include "events.hpp"
 
-/**
- * Abstract class that defines common interface for all input processors
- */
+/// Abstract class that defines common interface for all input processors
 class abstract_input {
 public:
+    /// Virtual destructor
     virtual ~abstract_input() = default;
 
     /// Check if there are any events to be proccessed
     bool has_events() const;
     /// Tryies to get next evet. Can throw exception if there is no event to be pulled
     event& get_next_event();
+    /// virtual function to poll events from underlying api
     virtual void poll_events() = 0;
 protected:
     size_t index; ///< Current index of polled event
diff --git a/src/game/input/events.hpp b/src/game/input/events.hpp
index 5cd573eda19deb460e03eeb40c9fff2186f10ddf..85946cdcfa1a89f32a8b39e3e5ec6c476fca2e41 100644
--- a/src/game/input/events.hpp
+++ b/src/game/input/events.hpp
@@ -4,6 +4,11 @@
 #include <variant>
 #include <cstdint>
 
+template<typename T>
+struct change {
+    T data;
+};
+
 struct change_move_vector {
     constexpr static int32_t DEFAULT = 5;
     int32_t delta;
diff --git a/src/game/input/sdl_input.h b/src/game/input/sdl_input.h
index 2d290f03cc205c38144d6d16330196bad18ec3a6..ed5be6e6e7bac6dfc5430ba7815a2c077aa220d3 100644
--- a/src/game/input/sdl_input.h
+++ b/src/game/input/sdl_input.h
@@ -4,9 +4,7 @@
 #include "abstract_input.h"
 #include <SDL2/SDL.h>
 
-/**
- * Processes user inputs using SDL's own system.
- */
+/// Processes user inputs using SDL's own system.
 class sdl_input: public abstract_input {
 public:
     /// Initializes SDL input system
diff --git a/src/game/renderer/abstract_renderer.h b/src/game/renderer/abstract_renderer.h
index 222771a9c4545cf9ec293203affc40b0a0100efd..800eba20669960a40d49bd125d95c161353111a5 100644
--- a/src/game/renderer/abstract_renderer.h
+++ b/src/game/renderer/abstract_renderer.h
@@ -7,9 +7,11 @@
 class abstract_renderer{
 public:
     /// Virtual destructor to prevent memory leaks
-    virtual ~abstract_renderer() {}
+    virtual ~abstract_renderer() = default;
     /// Virtual draw method that is called by the game controller.
     virtual void render_state(const state& current_state, bool debug_ui) = 0;
+    /// Shows game win dialog
+    virtual void show_win_dialog() = 0;
 };
 
 #endif//ABSTRACT_RENDERER_H
\ No newline at end of file
diff --git a/src/game/renderer/sdl_renderer.cpp b/src/game/renderer/sdl_renderer.cpp
index 973d1f7c9a45ae9d758e487fd7cd818f865a35e7..b903e5cab26554a3272db49d22c679745aa604a9 100644
--- a/src/game/renderer/sdl_renderer.cpp
+++ b/src/game/renderer/sdl_renderer.cpp
@@ -49,7 +49,7 @@ void sdl_renderer::render_state(const state& cs, bool debug_ui) {
                        cs.player.position.get() - static_cast<int>(16*cs.player.force.get()*sin(cs.player.angle.get())));
     SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
 
-    // render_enemies(cs.enemies);
+    render_enemies(cs.enemies);
     render_projectiles(cs.projectiles);
     render_ui(cs, debug_ui);
     SDL_RenderPresent(renderer);
@@ -82,9 +82,24 @@ void sdl_renderer::render_text(const std::string& text, const vector2& pos) {
     SDL_FreeSurface(text_surface);
 }
 
-// void sdl_renderer::render_enemies(const std::vector<enemy>& enemies) {
-
-// }
+void sdl_renderer::render_enemies(const std::vector<enemy>& enemies) {
+    static vector2 enemy_pic_size{32, 32};
+    for (auto& en: enemies) {
+        vector2 pos{en.position.x - static_cast<int>(en.radius),
+                    en.position.y - static_cast<int>(en.radius)};
+        switch(en.variant) {
+            case 0:
+                render_texture(resources->enemy1, pos, enemy_pic_size);
+            break;
+            case 1:
+                render_texture(resources->enemy2, pos, enemy_pic_size);
+            break;
+            default:
+                render_texture(resources->enemy3, pos, enemy_pic_size);
+            break;
+        }
+    }
+}
 
 void sdl_renderer::render_projectiles(const std::vector<projectile>& projectiles) {
     static vector2 projectile_size{30, 30};
@@ -108,4 +123,11 @@ void sdl_renderer::render_ui(const state& cs, bool debug) {
     }
     oss << "SCORE: " << cs.score;
     render_text(oss.str(), {2, 0});
+}
+
+void sdl_renderer::show_win_dialog() {
+    if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Victory",
+        "Congratulation you have WON the game.", window) != 0) {
+        throw std::runtime_error(SDL_GetError());
+    }
 }
\ No newline at end of file
diff --git a/src/game/renderer/sdl_renderer.h b/src/game/renderer/sdl_renderer.h
index 69396b59b0ae047c4cc93705abbafd03e858c125..21b4474c918f83de4901fad07692a3077e4184dc 100644
--- a/src/game/renderer/sdl_renderer.h
+++ b/src/game/renderer/sdl_renderer.h
@@ -16,6 +16,8 @@ public:
 
     /// Renders passed state to created window
     void render_state(const state& current_state, bool debug_ui) override;
+    /// Renders win dialog uisng SDL api
+    void show_win_dialog() override;
 protected:
     /// Helper function that renders all enemies to screen.
     void render_enemies(const std::vector<enemy>& enemies);
diff --git a/src/game/state/CMakeLists.txt b/src/game/state/CMakeLists.txt
index 42f8be012cc2b1c65f5fd1b3333fc80cdb766614..debce46533faead015c58d62e870ebbf62700ee2 100644
--- a/src/game/state/CMakeLists.txt
+++ b/src/game/state/CMakeLists.txt
@@ -1,7 +1,8 @@
 add_library(state
             state.h state.cpp
             shooter.h shooter.cpp
-            entities.h entities.cpp)
+            entities.h entities.cpp
+            level.h level.cpp)
 
 target_include_directories(state PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 
diff --git a/src/game/state/level.cpp b/src/game/state/level.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..40912b701dda7aea9d78d8aed6ffe4325ff2ace6
--- /dev/null
+++ b/src/game/state/level.cpp
@@ -0,0 +1,85 @@
+#include "level.h"
+
+#include <stdexcept>
+#include <memory>
+#include <fstream>
+
+std::map<std::string, level> level::levels;
+
+level::level(): enemies(), next_level_id() { }
+
+level::level(const level& other): enemies(other.enemies),
+                                  next_level_id(other.next_level_id) { }
+
+level& level::operator=(const level& other) {
+    enemies = other.enemies;
+    next_level_id = other.next_level_id;
+    return *this;
+}
+
+level_creator level::create() {
+    return level_creator();
+}
+
+level level::get(const std::string& level_id) {
+    return level::levels.at(level_id);
+}
+
+level_creator::level_creator(): to_be_created() { }
+
+level_creator& level_creator::with_enemy_at(const vector2& position) {
+    to_be_created.enemies.emplace_back(position, 16);
+    return *this;
+}
+
+level_creator& level_creator::set_next_level(const std::string& level_id) {
+    to_be_created.next_level_id = level_id;
+    return *this;
+}
+
+void level_creator::save_as(const std::string& level_id) {
+    level::levels[level_id] = to_be_created;
+}
+
+void level_creator::from_file(const std::string& path_to_file) {
+    std::ifstream ifs(path_to_file);
+    if (!ifs.good()) {
+        throw std::runtime_error("Can't find level file at: " + path_to_file);
+    }
+    std::string tmp, id;
+    ifs >> tmp;
+    if (tmp != "LEVEL:" || !ifs.good()) {
+        throw std::runtime_error("Can't load level info. LEVEL id must be first information");
+    }
+    ifs >> id;
+    if (id.size() == 0 || !ifs.good()) {
+        throw std::runtime_error("Can't load level info. No id data");
+    }
+    ifs >> tmp;
+    if (tmp != "NEXT:" || !ifs.good()) {
+        throw std::runtime_error("Can't load level info. No id data or second entry isn't next level id.");
+    }
+    ifs >> to_be_created.next_level_id;
+    if (to_be_created.next_level_id.size() == 0 || !ifs.good()) {
+        throw std::runtime_error("Can't load level info. No next level id data found.");
+    }
+    ifs >> tmp;
+    if (tmp != "ENEMIES:" || !ifs.good()) {
+        throw std::runtime_error("Can't load level info. No next level id data found or missing ENEMIES tag.");
+    }
+    while (true) {
+        vector2 position;
+        ifs >> position.x >> position.y;
+        if (!ifs.good())
+            break;
+        to_be_created.enemies.emplace_back(position, 16);
+    }
+    if (to_be_created.enemies.size() == 0) {
+        throw std::runtime_error("Can't load level info. No enemies data were suplied.");
+    }
+    level::levels[id] = to_be_created;
+}
+
+level level_creator::get() {
+    return to_be_created;
+}
\ No newline at end of file
diff --git a/src/game/state/level.h b/src/game/state/level.h
new file mode 100644
index 0000000000000000000000000000000000000000..4247deff323ab965832aedb83f94d58f76d0c3f8
--- /dev/null
+++ b/src/game/state/level.h
@@ -0,0 +1,67 @@
+#ifndef LEVEL_H
+#define LEVEL_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "entities.h"
+
+class level_creator;
+
+/// Level data structure. Contains list of enemies to spawn and next level id.
+class level {
+public:
+    /// Default contructor - creates empty level with empty next level id.
+    level();
+    /// Copy constructor - to be able to pass level from funtions
+    level(const level& other);
+    /// Copy operator
+    level& operator=(const level& other);
+
+    /// List of all enemies that are going to be spawned in this level.
+    std::vector<enemy> enemies;
+    /// Next level id. If empty than games should end.
+    std::string next_level_id;
+
+    /// Static map of all levels in level_id -> level relation.
+    static std::map<std::string, level> levels;
+
+    /// Creates level_creator class that can create/load level from scratch
+    static level_creator create();
+    /// Returns level by defined level id. Throws out of range error if level with defined id doesn't exist.
+    static level get(const std::string& level_id);
+};
+
+/// Class that is able to create/load level data.
+class level_creator {
+public:
+    /// Creates basic level_creator
+    level_creator();
+    /// Copy contructor
+    level_creator(const level_creator& other) = default;
+
+    /// Adds enemy at defined position
+    [[nodiscard]] level_creator& with_enemy_at(const vector2& position);
+    /// Set next level id to current level
+    [[nodiscard]] level_creator& set_next_level(const std::string& level_id);
+    /// Saves level with defined id.
+    void save_as(const std::string& level_id);
+     /// Loads level from file.
+     /// If file doesn't exists it throws an error.
+     /// File must be in this format:
+     /// LEVEL:
+     ///     {level_id}
+     /// NEXT:
+     ///     {next_level_id}
+     /// ENEMIES:
+     ///     {list of positions as pairs of number seperated by white spaces}
+    void from_file(const std::string& path_to_file);
+    /// Returns currently created level
+    [[nodiscard]] level get();
+protected:
+    /// Level that is being created
+    level to_be_created;
+};
+
+#endif//LEVEL_H
\ No newline at end of file
diff --git a/src/game/state/state.cpp b/src/game/state/state.cpp
index fc09964da961f7143d6e3abe3f9a3e7428221966..9676aa9bf6aee57618690a966174515a97686eb7 100644
--- a/src/game/state/state.cpp
+++ b/src/game/state/state.cpp
@@ -24,6 +24,8 @@ state& state::operator=(const state& other) {
     player = other.player;
     enemies = other.enemies;
     projectiles = other.projectiles;
+    score = other.score;
+    next_level_id = other.next_level_id;
     return *this;
 }
 
diff --git a/src/game/state/state.h b/src/game/state/state.h
index 2e863bfcadb8d36f9bbb39655965240c4ee94629..5ca28e4e103f69df313b9a947c62fb67a786d8ea 100644
--- a/src/game/state/state.h
+++ b/src/game/state/state.h
@@ -3,6 +3,7 @@
 
 #include <list>
 #include <vector>
+#include <string>
 
 #include "entities.h"
 #include "shooter.h"
@@ -35,6 +36,8 @@ public:
     std::vector<projectile> projectiles;
     /// Current game score
     uint32_t score;
+    /// Id of next level to load to this state
+    std::string next_level_id;
     /// Check if there are enough stored states
     static bool can_pop();
 protected: