diff --git a/resources/inter.ttf b/resources/inter.ttf new file mode 100644 index 0000000..4ab79e0 Binary files /dev/null and b/resources/inter.ttf differ diff --git a/src/core/assets/asset_db.cpp b/src/core/assets/asset_db.cpp index b77828c..328a9e2 100644 --- a/src/core/assets/asset_db.cpp +++ b/src/core/assets/asset_db.cpp @@ -8,12 +8,12 @@ AssetDB::AssetDB() { this->index_assets(); } -void AssetDB::clean() { +void AssetDB::clean(bool force) { for(size_t i{0}; i < this->loaded.size();) { std::string asset_name{this->loaded.at(i)}; std::shared_ptr asset{this->assets.at(asset_name)}; - // usecount=1 means the asset is unused (other than the local use and the use in this->assets) - if(asset.use_count() == 2) { + // usecount of 2 means the asset is unused (other than the local use and the use in this->assets) + if(force || asset.use_count() <= 2) { asset->unload(); this->loaded.erase(this->loaded.begin() + i); } else ++i; // don't iterate when the asset is unloaded diff --git a/src/core/assets/asset_db.hpp b/src/core/assets/asset_db.hpp index 199c808..5580a8b 100644 --- a/src/core/assets/asset_db.hpp +++ b/src/core/assets/asset_db.hpp @@ -15,7 +15,7 @@ public: AssetDB(); ~AssetDB() = default; template std::optional> get_asset(std::string const &name); - void clean(); + void clean(bool force); private: void index_assets(); void load(std::string asset_name); @@ -25,8 +25,10 @@ template std::optional> AssetDB::ge if(!this->assets.contains(name)) return std::nullopt; std::shared_ptr found{std::dynamic_pointer_cast(this->assets.at(name))}; if(found == nullptr) return std::nullopt; - if(!found->is_loaded()) + if(!found->is_loaded()) { found->load(); + this->loaded.push_back(name); + } return std::make_optional(found); } } diff --git a/src/core/assets/asset_wrapper.cpp b/src/core/assets/asset_wrapper.cpp index 2b9b972..278d40f 100644 --- a/src/core/assets/asset_wrapper.cpp +++ b/src/core/assets/asset_wrapper.cpp @@ -1,6 +1,7 @@ #include "asset_wrapper.h" #include "core/canvas_engine.hpp" #include +#include #include #include #include @@ -15,15 +16,18 @@ Texture::~Texture() { this->unload(); } -void Texture::unload() { - SDL_DestroyTexture(this->texture); - this->texture = nullptr; -} - void Texture::load() { this->texture = IMG_LoadTexture(CanvasEngine::get_singleton()->get_render(), this->path.c_str()); } +void Texture::unload() { + if(this->is_loaded()) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "unloading font %s", this->path.c_str()); + SDL_DestroyTexture(this->texture); + this->texture = nullptr; + } +} + bool Texture::is_loaded() const { return this->texture != nullptr; } @@ -39,15 +43,21 @@ Font::~Font() { } void Font::load() { - this->font = TTF_OpenFont(this->path.c_str(), 32); + this->font = TTF_OpenFont(this->path.c_str(), this->PT_SIZE); } void Font::unload() { - TTF_CloseFont(this->font); - this->font = nullptr; + if(this->is_loaded()) { + TTF_CloseFont(this->font); + this->font = nullptr; + } } bool Font::is_loaded() const { return this->font != nullptr; } + +TTF_Font *Font::get() { + return this->font; +} } diff --git a/src/core/assets/asset_wrapper.h b/src/core/assets/asset_wrapper.h index ac55cd3..130152f 100644 --- a/src/core/assets/asset_wrapper.h +++ b/src/core/assets/asset_wrapper.h @@ -32,6 +32,8 @@ public: class Font : public Asset { TTF_Font *font{nullptr}; +public: + static const int PT_SIZE{64}; public: Font(std::filesystem::path const &path); ~Font(); diff --git a/src/core/canvas_engine.cpp b/src/core/canvas_engine.cpp index aa9ead4..8fdd416 100644 --- a/src/core/canvas_engine.cpp +++ b/src/core/canvas_engine.cpp @@ -1,6 +1,8 @@ #include "canvas_engine.hpp" +#include "core/level.hpp" #include #include +#include #include #include #include @@ -22,18 +24,29 @@ CanvasEngine *CanvasEngine::singleton_instance{nullptr}; CanvasEngine::CanvasEngine() { if(SDL_Init(SDL_INIT_EVERYTHING) != 0) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to initialize SDL, SDL error: %s", SDL_GetError()); + this->deinit_handled = true; return; } if(IMG_Init(IMG_INIT_PNG | IMG_INIT_JXL) == 0) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to initialize SDL_image, error: %s", IMG_GetError()); SDL_Quit(); + this->deinit_handled = true; + return; + } + if(TTF_Init() == -1) { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to initialize SDL_ttf, error: %s", TTF_GetError()); + IMG_Quit(); + SDL_Quit(); + this->deinit_handled = true; return; } this->window = SDL_CreateWindow(PROJECTNAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1000, 800, SDL_WINDOW_RESIZABLE | SDL_WINDOW_FULLSCREEN_DESKTOP); if(this->window == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to create window, SDL error: %s", SDL_GetError()); IMG_Quit(); + TTF_Quit(); SDL_Quit(); + this->deinit_handled = true; return; } this->render = SDL_CreateRenderer(this->window, -1, SDL_RENDERER_ACCELERATED); @@ -41,7 +54,9 @@ CanvasEngine::CanvasEngine() { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to initialize renderer, SDL error: %s", SDL_GetError()); SDL_DestroyWindow(this->window); IMG_Quit(); + TTF_Quit(); SDL_Quit(); + this->deinit_handled = true; return; } this->render_target = SDL_CreateTexture(this->render, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080); @@ -50,12 +65,22 @@ CanvasEngine::CanvasEngine() { } CanvasEngine::~CanvasEngine() { - SDL_DestroyRenderer(this->render); - SDL_DestroyWindow(this->window); - SDL_Quit(); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "CANVAS: Shutting down"); + this->level.reset(); + this->assets.clean(true); + if(!this->deinit_handled) { + SDL_DestroyRenderer(this->render); + SDL_DestroyWindow(this->window); + IMG_Quit(); + TTF_Quit(); + SDL_Quit(); + } + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "CANVAS: Done shutting down"); } void CanvasEngine::run(std::unique_ptr &level) { + if(!stay_open) + return; assert(CanvasEngine::singleton_instance == nullptr && "Engine singleton instance already assigned, starting another instance is invalid"); // register as singleton CanvasEngine::singleton_instance = this; @@ -82,6 +107,10 @@ void CanvasEngine::run(std::unique_ptr &level) { } else { SDL_Delay(2); } + if(this->next_level) { + this->level = std::move(this->next_level); + this->level->instantiate(); + } } assert(CanvasEngine::singleton_instance == this && "Engine singleton instance changed while game was running"); CanvasEngine::singleton_instance = nullptr; @@ -95,6 +124,10 @@ void CanvasEngine::set_target_delta_time(double target) { this->target_delta_time = target; } +void CanvasEngine::change_level(std::unique_ptr &level) { + this->next_level = std::move(level); +} + AssetDB &CanvasEngine::get_assets() { return this->assets; } diff --git a/src/core/canvas_engine.hpp b/src/core/canvas_engine.hpp index a7481ec..2425dd6 100644 --- a/src/core/canvas_engine.hpp +++ b/src/core/canvas_engine.hpp @@ -25,11 +25,13 @@ private: CollisionWorld collision_world{}; InputMap input_map{}; //!< map of inputs to input callback objects std::unique_ptr level; + std::unique_ptr next_level; Uint64 last_frame_start_time{}; //!< time at start of last frame Uint64 frame_start_time{}; //!< time at start of this frame double delta_time{0.f}; //!< measured delta time double target_delta_time{}; //!< delta time target bool stay_open{false}; //!< application loop will continue so long as this is true + bool deinit_handled{false}; public: CanvasEngine(); ~CanvasEngine(); @@ -41,6 +43,7 @@ public: void run(std::unique_ptr &level); void request_close(); void set_target_delta_time(double target); + void change_level(std::unique_ptr &level); AssetDB &get_assets(); CollisionWorld &get_collision_world(); InputMap &get_input_map(); diff --git a/src/core/level.cpp b/src/core/level.cpp index 3077d82..a7995ca 100644 --- a/src/core/level.cpp +++ b/src/core/level.cpp @@ -5,14 +5,22 @@ #include namespace ce { +Level::~Level() { + this->deinstantiate(); +} void Level::instantiate() { std::unique_ptr constructed{this->construct()}; - constructed->set_is_inside_tree(true); this->root = std::move(constructed); + this->root->set_is_inside_tree(true); + this->root->set_level(this); this->root->propagate_added(); } + void Level::deinstantiate() { - this->root.reset(); + if(this->root) { + this->root->propagate_removed(); + this->root.reset(); + } } void Level::propagate_tick(double delta_time) { @@ -24,20 +32,17 @@ void Level::propagate_draw(SDL_Renderer *render) { int w, h; SDL_Window *window{SDL_RenderGetWindow(render)}; SDL_GetWindowSize(window, &w, &h); - ce::Transform const screen_transform{ - .position = ce::Vecf::ZERO, + Transform const screen_transform{ + .position = Vecf::ZERO, .rotation = 0.f, - .scale = ce::Vecf::ONE * float(w), + .scale = Vecf::ONE * float(w), }; float const ratio{float(h)/float(w)}; this->root->propagate_draw(render, Transform().translated({ 0.5f / this->view_transform.scale.x, 0.5f / this->view_transform.scale.y * ratio - }) * this->view_transform * screen_transform); - this->root->propagate_draw_ui(render, Transform().translated({ - 0.5f / this->view_transform.scale.x, - 0.5f / this->view_transform.scale.y * ratio - }) * screen_transform); + }) * view_transform * screen_transform); + this->root->propagate_draw_ui(render, screen_transform); } Node *Level::get_root() { diff --git a/src/core/level.hpp b/src/core/level.hpp index 75ef4fb..018a77e 100644 --- a/src/core/level.hpp +++ b/src/core/level.hpp @@ -14,7 +14,7 @@ protected: .scale = {1.f/10.f, 1.f/10.f}, }; public: - virtual ~Level() = default; + virtual ~Level(); void instantiate(); virtual Node::OwnedPtr construct() = 0; void deinstantiate(); diff --git a/src/core/node.cpp b/src/core/node.cpp index feebb69..183e952 100644 --- a/src/core/node.cpp +++ b/src/core/node.cpp @@ -19,6 +19,7 @@ void Node::add_child(Node::OwnedPtr &child) { this->children.push_back({child->get_name(), std::move(child)}); added->parent = this; added->set_is_inside_tree(this->inside_tree); + added->set_level(this->level); this->child_added.invoke(added); added->propagate_added(); } diff --git a/src/core/sprite.cpp b/src/core/sprite.cpp index 9156e94..30665b0 100644 --- a/src/core/sprite.cpp +++ b/src/core/sprite.cpp @@ -22,14 +22,12 @@ void Sprite::_draw(SDL_Renderer *render, ce::Transform const &view_transform) { } int w, h; SDL_QueryTexture(this->texture->get(), NULL, NULL, &w, &h); - Transform transform{this->get_global_transform() * view_transform}; - assert(transform.scale.x != 0 && transform.scale.y != 0); // !!! - ce::Vecf size{this->get_size()}; - size.scale(view_transform.scale); + Transform const transform{this->get_global_transform() * view_transform}; + ce::Vecf const size{this->get_size().scaled(view_transform.scale)}; assert(size.x != 0.f && size.y != 0.f); //float fw(w), fh(h); - SDL_Rect src{.x=0, .y=0, .w=w, .h=h}; - SDL_FRect dst{.x=transform.position.x - size.x/2.f, .y=transform.position.y - size.y/2.f, .w=size.x, .h=size.y}; + SDL_Rect const src{.x=0, .y=0, .w=w, .h=h}; + SDL_FRect const dst{.x=transform.position.x - size.x/2.f, .y=transform.position.y - size.y/2.f, .w=size.x, .h=size.y}; SDL_RenderCopyExF(render, this->texture->get(), &src, &dst, transform.rotation * 57.2958f,NULL, diff --git a/src/core/ui_text.cpp b/src/core/ui_text.cpp new file mode 100644 index 0000000..7023251 --- /dev/null +++ b/src/core/ui_text.cpp @@ -0,0 +1,69 @@ +#include "ui_text.hpp" +#include "core/assets/asset_db.hpp" +#include "core/canvas_engine.hpp" +#include +#include +#include + +namespace ce { +UiText::UiText(std::string const &name, std::string text, std::string font, SDL_Color color) +: Node2D(name) +, font{} +, text{text} +, color{color} { + std::optional> font_asset{CanvasEngine::get_singleton()->get_assets().get_asset(font)}; + if(font_asset.has_value()) + this->font = font_asset.value(); +} + +UiText::~UiText() { + if(this->cached) + SDL_DestroyTexture(this->cached); +} + +void UiText::_draw_ui(SDL_Renderer *render, Transform const &ui_transform) { + if(dirty) { + this->render(); + } + if(this->cached == nullptr) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "No texture assigned"); + this->set_visible(false); + return; + } + int w, h; SDL_QueryTexture(this->cached, NULL, NULL, &w, &h); + Transform const transform{this->get_global_transform() * ui_transform}; + Vecf const size{Vecf{float(w) / float(h), 1.f}.scaled(transform.scale)}; + assert(size.x != 0.f && size.y != 0.f); + SDL_Rect const src{.x=0, .y=0, .w=w, .h=h}; + SDL_FRect const dst{.x=transform.position.x, .y=transform.position.y, .w=size.x, .h=size.y}; + SDL_RenderCopyExF(render, this->cached, + &src, &dst, + transform.rotation * 57.2958f,NULL, + SDL_FLIP_NONE); +} + +void UiText::set_text(std::string text) { + this->text = text; + this->render(); +} + +std::string const &UiText::get_text() const { + return this->text; +} + +void UiText::render() { + if(this->cached != nullptr) + SDL_DestroyTexture(this->cached); + SDL_Renderer *render{CanvasEngine::get_singleton()->get_render()}; + if(render == nullptr) { + this->dirty = true; // can't render right now, defer for later + return; + } + SDL_Surface *surf{TTF_RenderText_Blended(this->font->get(), this->text.c_str(), this->color)}; + assert(surf != nullptr && "Failed to render text"); + this->cached = SDL_CreateTextureFromSurface(render, surf); + assert(this->cached != nullptr && "Failed to pass rendered text to the GPU"); + SDL_FreeSurface(surf); + this->dirty = false; +} +} diff --git a/src/core/ui_text.hpp b/src/core/ui_text.hpp new file mode 100644 index 0000000..c3f523c --- /dev/null +++ b/src/core/ui_text.hpp @@ -0,0 +1,27 @@ +#ifndef CORE_UI_TEXT_HPP +#define CORE_UI_TEXT_HPP + +#include "core/assets/asset_wrapper.h" +#include "core/math/transform.hpp" +#include "core/node2d.hpp" + +namespace ce { +class UiText : public Node2D { + std::shared_ptr font{nullptr}; + std::string text{}; + SDL_Color color{255, 255, 255, 255}; + SDL_Texture *cached{nullptr}; + bool dirty{true}; +public: + UiText(std::string const &name, std::string text, std::string font, SDL_Color color); + ~UiText(); + virtual void _draw_ui(SDL_Renderer *render, Transform const &view_transform) override; + + void set_text(std::string text); + std::string const &get_text() const; +protected: + void render(); +}; +} + +#endif // !CORE_UI_TEXT_HPP diff --git a/src/end_screen.cpp b/src/end_screen.cpp new file mode 100644 index 0000000..c391021 --- /dev/null +++ b/src/end_screen.cpp @@ -0,0 +1,15 @@ +#include "end_screen.hpp" +#include "core/math/transform.hpp" +#include "core/node2d.hpp" +#include "core/ui_text.hpp" +#include + +EndScreen::EndScreen(unsigned score) +: score{score} {} + +ce::Node::OwnedPtr EndScreen::construct() { + ce::Node::OwnedPtr root{new ce::Node2D("root")}; + root->create_child("score", std::format("Score: {}", this->score), "inter", SDL_Color{255, 255, 255, 255}) + ->set_global_transform(ce::Transform().translated({0.3f, 0.3f}).scaled({0.1f, 0.1f})); + return std::move(root); +} diff --git a/src/end_screen.hpp b/src/end_screen.hpp new file mode 100644 index 0000000..30607d4 --- /dev/null +++ b/src/end_screen.hpp @@ -0,0 +1,13 @@ +#ifndef END_SCREEN_HPP +#define END_SCREEN_HPP + +#include "core/level.hpp" + +class EndScreen : public ce::Level { + unsigned score; +public: + EndScreen(unsigned score); + virtual ce::Node::OwnedPtr construct() override; +}; + +#endif // !END_SCREEN_HPP diff --git a/src/level_1.cpp b/src/level_1.cpp index 1cd5455..27aadbf 100644 --- a/src/level_1.cpp +++ b/src/level_1.cpp @@ -1,9 +1,16 @@ #include "level_1.hpp" +#include "core/canvas_engine.hpp" +#include "core/level.hpp" #include "core/node.hpp" #include "core/node2d.hpp" +#include "core/ui_text.hpp" +#include "end_screen.hpp" +#include "life_display.hpp" #include "player.hpp" +#include "score_display.hpp" #include "scrolling_ground.hpp" -#include "truck.hpp" +#include "spawner.hpp" +#include ce::Node::OwnedPtr Level1::construct() { ce::Node::OwnedPtr root{new ce::Node2D("root")}; @@ -13,6 +20,23 @@ ce::Node::OwnedPtr Level1::construct() { .scale = ce::Vecf::ONE }); root->create_child(); - root->create_child(true); + root->create_child(); + root->create_child() + ->set_global_transform(ce::Transform().scaled({.05f, .05f}).translated({0.01f, 0.f})); + root->create_child() + ->set_global_transform(ce::Transform().scaled({.05f, .05f}).translated({.9f, 0.f})); return std::move(root); } + +void Level1::add_score() { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "add_score"); + this->score_added.invoke(++this->score); +} + +void Level1::lose_life() { + this->life_lost.invoke(--this->lives); + if(this->lives == 0) { + std::unique_ptr next{ce::Level::make(this->score)}; + ce::CanvasEngine::get_singleton()->change_level(next); + } +} diff --git a/src/level_1.hpp b/src/level_1.hpp index e3b5c96..57a4348 100644 --- a/src/level_1.hpp +++ b/src/level_1.hpp @@ -2,8 +2,17 @@ #define LEVEL_1_HPP #include "core/level.hpp" +#include "core/signal.hpp" class Level1 : public ce::Level { +public: + unsigned score{0}; + unsigned lives{3}; + ce::Signal score_added{}; + ce::Signal life_lost{}; +public: + void add_score(); + void lose_life(); virtual ce::Node::OwnedPtr construct() override; }; diff --git a/src/life_display.cpp b/src/life_display.cpp new file mode 100644 index 0000000..de99349 --- /dev/null +++ b/src/life_display.cpp @@ -0,0 +1,19 @@ +#include "life_display.hpp" +#include "core/ui_text.hpp" +#include "level_1.hpp" +#include + +LifeDisplay::LifeDisplay() +: ce::UiText("score_display", "0", "inter", {255, 255, 255, 255}) {} + +void LifeDisplay::_added() { + if(Level1 *level{dynamic_cast(this->get_level())}) { + level->life_lost.connect(ce::Callable::make(this, &LifeDisplay::_lives_changed)); + this->_lives_changed(level->lives); + } +} + +void LifeDisplay::_lives_changed(unsigned value) { + this->set_text(std::format("{}", value)); + this->render(); +} diff --git a/src/life_display.hpp b/src/life_display.hpp new file mode 100644 index 0000000..0435e26 --- /dev/null +++ b/src/life_display.hpp @@ -0,0 +1,13 @@ +#ifndef LIFE_DISPLAY_HPP +#define LIFE_DISPLAY_HPP + +#include "core/ui_text.hpp" + +class LifeDisplay : public ce::UiText { +public: + LifeDisplay(); + virtual void _added() override; + void _lives_changed(unsigned new_value); +}; + +#endif // !LIFE_DISPLAY_HPP diff --git a/src/main.cpp b/src/main.cpp index f3ccfa2..17ab307 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ #include "core/canvas_engine.hpp" +#include "core/level.hpp" #include "level_1.hpp" #include @@ -6,6 +7,6 @@ ce::CanvasEngine engine{}; int main(int argc [[maybe_unused]], char* argv [[maybe_unused]][]) { SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); - std::unique_ptr level{std::make_unique()}; + std::unique_ptr level{ce::Level::make()}; engine.run(level); } diff --git a/src/player.cpp b/src/player.cpp index 1b95a64..93be496 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1,4 +1,5 @@ #include "player.hpp" +#include "level_1.hpp" #include "truck.hpp" #include "core/callable.hpp" #include "core/canvas_engine.hpp" @@ -72,6 +73,10 @@ void Player::_input_vertical_movement(ce::InputValue value) { void Player::_on_overlap_enter(ce::CollisionShape *, ce::CollidableNode *other, ce::CollisionShape *shape) { if(this->invincibility > 0.f) return; - if(Truck *truck{dynamic_cast(other)}) + if(Truck *truck{dynamic_cast(other)}) { this->invincibility = 2.f; + if(Level1 *level{dynamic_cast(this->get_level())}) { + level->lose_life(); + } + } } diff --git a/src/score_display.cpp b/src/score_display.cpp new file mode 100644 index 0000000..4348dc6 --- /dev/null +++ b/src/score_display.cpp @@ -0,0 +1,18 @@ +#include "score_display.hpp" +#include "core/ui_text.hpp" +#include "level_1.hpp" +#include + +ScoreDisplay::ScoreDisplay() +: ce::UiText("score_display", "0", "inter", {255, 255, 255, 255}) {} + +void ScoreDisplay::_added() { + if(Level1 *level{dynamic_cast(this->get_level())}) { + level->score_added.connect(ce::Callable::make(this, &ScoreDisplay::_score_changed)); + } +} + +void ScoreDisplay::_score_changed(unsigned value) { + this->set_text(std::format("{}", value)); + this->render(); +} diff --git a/src/score_display.hpp b/src/score_display.hpp new file mode 100644 index 0000000..7c56431 --- /dev/null +++ b/src/score_display.hpp @@ -0,0 +1,13 @@ +#ifndef SCORE_DISPLAY_HPP +#define SCORE_DISPLAY_HPP + +#include "core/ui_text.hpp" + +class ScoreDisplay : public ce::UiText { +public: + ScoreDisplay(); + virtual void _added() override; + void _score_changed(unsigned new_value); +}; + +#endif // !SCORE_DISPLAY_HPP diff --git a/src/truck.cpp b/src/truck.cpp index 89466eb..a578e32 100644 --- a/src/truck.cpp +++ b/src/truck.cpp @@ -2,6 +2,7 @@ #include "core/math/transform.hpp" #include "core/sprite.hpp" #include "core/collision_shape.hpp" +#include "level_1.hpp" #include #include #include @@ -29,6 +30,11 @@ void Truck::_tick(double delta) { trans.position.x = std::clamp(trans.position.x, -LIMITS.x, LIMITS.x); trans.position.y = std::max(trans.position.y, -LIMITS.y); this->set_global_transform(trans); - if(transform.position.y > 4.5f) + if(transform.position.y > 4.5f) { this->flag_for_deletion(); + if(Level1 *level{dynamic_cast(this->get_level())}) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "adding score"); + level->add_score(); + } + } }