diff --git a/.github/workflows/about.md b/.github/workflows/about.md new file mode 100644 index 00000000..382e4dc5 --- /dev/null +++ b/.github/workflows/about.md @@ -0,0 +1,5 @@ +1.compile-games.yml +This is the automatic compilation pipeline of GitHub Actions. When someone requests a PR to the main branch, it automatically finds the modified games in games/. Compile these games across platforms only (Win x86/Linux x86/Linux ARM), then backfill the executable files to compiled-games/< game name >/ in the repository, and finally attach the "compiled" tag to the PR. + +2.validate-games.yml +Check and verify the config.txt file of the game in the PR submission. If it does not meet the requirements, the merge is not allowed. \ No newline at end of file diff --git a/advanced-game-design-team/about.md b/advanced-game-design-team/about.md new file mode 100644 index 00000000..c2484925 --- /dev/null +++ b/advanced-game-design-team/about.md @@ -0,0 +1 @@ +Some guidance and cases on developing games using the splashkit framework are provided. We hope you can quickly get started with the framework. There are many excellent teaching cases and guidance \ No newline at end of file diff --git a/games/BelowTheSurface/SDL2.dll b/games/BelowTheSurface/SDL2.dll new file mode 100644 index 00000000..24246ebe Binary files /dev/null and b/games/BelowTheSurface/SDL2.dll differ diff --git a/games/BelowTheSurface/SDL2_image.dll b/games/BelowTheSurface/SDL2_image.dll new file mode 100644 index 00000000..50549744 Binary files /dev/null and b/games/BelowTheSurface/SDL2_image.dll differ diff --git a/games/BelowTheSurface/SDL2_mixer.dll b/games/BelowTheSurface/SDL2_mixer.dll new file mode 100644 index 00000000..ab5e32ab Binary files /dev/null and b/games/BelowTheSurface/SDL2_mixer.dll differ diff --git a/games/BelowTheSurface/SDL2_net.dll b/games/BelowTheSurface/SDL2_net.dll new file mode 100644 index 00000000..45f50499 Binary files /dev/null and b/games/BelowTheSurface/SDL2_net.dll differ diff --git a/games/BelowTheSurface/SDL2_ttf.dll b/games/BelowTheSurface/SDL2_ttf.dll new file mode 100644 index 00000000..be578778 Binary files /dev/null and b/games/BelowTheSurface/SDL2_ttf.dll differ diff --git a/games/BelowTheSurface/SplashKit.dll b/games/BelowTheSurface/SplashKit.dll new file mode 100644 index 00000000..5e3559a4 --- /dev/null +++ b/games/BelowTheSurface/SplashKit.dll @@ -0,0 +1 @@ +libSplashKit.dylib \ No newline at end of file diff --git a/games/BelowTheSurface/rav1e.dll b/games/BelowTheSurface/rav1e.dll new file mode 100644 index 00000000..1f24f342 Binary files /dev/null and b/games/BelowTheSurface/rav1e.dll differ diff --git a/games/BelowTheSurface/zlib1.dll b/games/BelowTheSurface/zlib1.dll new file mode 100644 index 00000000..93c94c53 Binary files /dev/null and b/games/BelowTheSurface/zlib1.dll differ diff --git a/games/TowerDefense/Manager.h b/games/TowerDefense/Manager.h new file mode 100644 index 00000000..260ccf9b --- /dev/null +++ b/games/TowerDefense/Manager.h @@ -0,0 +1,39 @@ +#ifndef _MANAGER_H_ +#define _MANAGER_H_ + +template +class Manager +{ +public: + static T* instance()//外界取得钥匙的方法,从instance员工这里来拿 + { + if (!manager) + manager = new T();//如果之前还没有钥匙,创建钥匙 + + return manager;//刚创建好钥匙/已经有钥匙,直接返回钥匙 + + } + +private: + static T* manager;//钥匙实例 + +protected: + Manager() = default;//创建钥匙方法 + ~Manager() = default; + Manager(const Manager&) = delete; + Manager& operator=(const Manager&) = delete; + +}; +template //占位模板,调用时编译器根据你输入的T把你模板里写的T全换成你输入的那个 +T* Manager::manager = nullptr; +#endif // !_MANAGER_H_ + +/* +关于理解可继承单例模板:是一份天生该只有一份并被全局共享的实例,也就是说他是特定的,后面所有想使用它的地方都直接从这个地方拿特殊的就行了,比如说你想拿公司总账对账那个,不管在哪个地方拿公司账本他应该都是 +同一份,在这里就是一个实例,而不是两份账本也就是两个实例,这样就会产生错误,所以当你以后需要公司账本时,你直接来可继承单例模板这个部门直接拿就行了,不需要你传入参数等等,只需要直接拿,方便快捷而且不易出错 +优点还有很多,这里不再赘述*/ + +/* +关于实现可继承单例模板:instance方法作为manager类的内部员工,它可以搞到内部manager的方法和信息,所以如果要做可继承单例模板,我们就让其他想要钥匙的人都来找他,他就可以把做好的同一把特殊钥匙分发给其他人, +而其他人不能直接通过类来造钥匙而只能来找这个员工拿,这个钥匙就是我们想要实现的功能,所以我们每个人从可继承单例模板拿到的都是同样特殊的东西 +*/ diff --git a/games/TowerDefense/SDL2.dll b/games/TowerDefense/SDL2.dll new file mode 100644 index 00000000..45fafe52 Binary files /dev/null and b/games/TowerDefense/SDL2.dll differ diff --git a/games/TowerDefense/SDL2_image.dll b/games/TowerDefense/SDL2_image.dll new file mode 100644 index 00000000..d90de2a3 Binary files /dev/null and b/games/TowerDefense/SDL2_image.dll differ diff --git a/games/TowerDefense/SDL2_mixer.dll b/games/TowerDefense/SDL2_mixer.dll new file mode 100644 index 00000000..5e4fef32 Binary files /dev/null and b/games/TowerDefense/SDL2_mixer.dll differ diff --git a/games/TowerDefense/SDL2_ttf.dll b/games/TowerDefense/SDL2_ttf.dll new file mode 100644 index 00000000..dbbf3851 Binary files /dev/null and b/games/TowerDefense/SDL2_ttf.dll differ diff --git a/games/TowerDefense/TowerDefense.png b/games/TowerDefense/TowerDefense.png new file mode 100644 index 00000000..d5c94cd3 Binary files /dev/null and b/games/TowerDefense/TowerDefense.png differ diff --git a/games/TowerDefense/animation.h b/games/TowerDefense/animation.h new file mode 100644 index 00000000..5d934476 --- /dev/null +++ b/games/TowerDefense/animation.h @@ -0,0 +1,105 @@ +#ifndef _ANIMATION_H_ +#define _ANIMATION_H_ + +#include "timer.h" + +#include +#include +#include + +class Animation +{ +public: + typedef std::function PlayCallback; + +public: + Animation() + { + timer.set_one_shot(false); + timer.set_on_timeout( + [&]() + { + idx_frame++; + if (idx_frame >= rect_src_list.size()) + { + idx_frame = is_loop ? 0 : rect_src_list.size() - 1; + if (!is_loop && on_finished) + on_finished(); + } + } + ); + } + + ~Animation() = default; + + void reset() + { + timer.restart(); + + idx_frame = 0; + } + + void set_frame_data(SDL_Texture* texture, int num_h, int num_v, const std::vector& idx_list) + { + int width_tex, height_tex; + + this->texture = texture; + SDL_QueryTexture(texture, nullptr, nullptr, &width_tex, &height_tex); + width_frame = width_tex / num_h, height_frame = height_tex / num_v; + + rect_src_list.resize(idx_list.size()); + for (size_t i = 0; i < idx_list.size(); i++) + { + int idx = idx_list[i]; + SDL_Rect& rect_src = rect_src_list[i]; + + rect_src.x = (idx % num_h) * width_frame; + rect_src.y = (idx / num_h) * height_frame; + rect_src.w = width_frame, rect_src.h = height_frame; + } + } + + void set_loop(bool is_loop) + { + this->is_loop = is_loop; + } + + void set_interval(double interval) + { + timer.set_wait_time(interval); + } + + void set_on_finished(PlayCallback on_finished) + { + this->on_finished = on_finished; + } + + void on_update(double delta) + { + timer.on_update(delta); + } + + void on_render(SDL_Renderer* renderer, const SDL_Point& pos_dst, double angle = 0) const + { + static SDL_Rect rect_dst; + + rect_dst.x = pos_dst.x, rect_dst.y = pos_dst.y; + rect_dst.w = width_frame, rect_dst.h = height_frame; + + SDL_RenderCopyEx(renderer, texture, &rect_src_list[idx_frame], &rect_dst, angle, nullptr, SDL_RendererFlip::SDL_FLIP_NONE); + } + +private: + Timer timer; + bool is_loop = true; + size_t idx_frame = 0; + PlayCallback on_finished; + SDL_Texture* texture = nullptr; + std::vector rect_src_list; + int width_frame = 0, height_frame = 0; + +}; + + +#endif // !_ANIMATION_H_ + diff --git a/games/TowerDefense/archer_tower.h b/games/TowerDefense/archer_tower.h new file mode 100644 index 00000000..c1acfd6d --- /dev/null +++ b/games/TowerDefense/archer_tower.h @@ -0,0 +1,50 @@ +#ifndef _ARCHER_TOWER_H_ +#define _ARCHER_TOWER_H_ + +#include "tower.h" +#include "resources_manager.h" + + +class ArcherTower : public Tower +{ +public: + ArcherTower() + { + static SDL_Texture* tex_archer = ResourcesManager::instance() + ->get_texture_pool().find(ResID::Tex_Archer)->second; + + static const std::vector idx_list_idle_up = { 3, 4 }; + static const std::vector idx_list_idle_down = { 0, 1 }; + static const std::vector idx_list_idle_left = { 6, 7 }; + static const std::vector idx_list_idle_right = { 9, 10 }; + static const std::vector idx_list_fire_up = { 15, 16, 17 }; + static const std::vector idx_list_fire_down = { 12, 13, 14 }; + static const std::vector idx_list_fire_left = { 18, 19, 20 }; + static const std::vector idx_list_fire_right = { 21, 22, 23 }; + + anim_idle_up.set_frame_data(tex_archer, 3, 8, idx_list_idle_up); + anim_idle_down.set_frame_data(tex_archer, 3, 8, idx_list_idle_down); + anim_idle_left.set_frame_data(tex_archer, 3, 8, idx_list_idle_left); + anim_idle_right.set_frame_data(tex_archer, 3, 8, idx_list_idle_right); + anim_fire_up.set_frame_data(tex_archer, 3, 8, idx_list_fire_up); + anim_fire_down.set_frame_data(tex_archer, 3, 8, idx_list_fire_down); + anim_fire_left.set_frame_data(tex_archer, 3, 8, idx_list_fire_left); + anim_fire_right.set_frame_data(tex_archer, 3, 8, idx_list_fire_right); + + size.x = 48, size.y = 48; + + tower_type = TowerType::Archer; + + fire_speed = 6; + bullet_type = BulletType::Arrow; + } + + ~ArcherTower() = default; + + + +}; + + +#endif // !_ARCHER_TOWER_H_ + diff --git a/games/TowerDefense/arrow_bullet.h b/games/TowerDefense/arrow_bullet.h new file mode 100644 index 00000000..b36af2ed --- /dev/null +++ b/games/TowerDefense/arrow_bullet.h @@ -0,0 +1,54 @@ +#ifndef _ARROW_BULLET_H_ +#define _ARROW_BULLET_H_ + +#include"bullet.h" +#include "resources_manager.h" + + +class ArrowBullet : public Bullet +{ +public: + ArrowBullet() + { + static SDL_Texture* tex_arrow = ResourcesManager::instance() + ->get_texture_pool().find(ResID::Tex_BulletArrow)->second; + static const std::vector idx_list = { 0,1 }; + + animation.set_loop(true); + animation.set_interval(0.1); + animation.set_frame_data(tex_arrow, 2, 1, idx_list); + + can_rotated = true; + size.x = 48, size.y = 48; + } + + ~ArrowBullet() = default; + + void on_collide(Enemy* enemy) override + { + static const ResourcesManager::SoundPool& sound_pool + = ResourcesManager::instance()->get_sound_pool(); + + switch(rand()%3) + { + case 0: + Mix_PlayChannel(-1,sound_pool.find(ResID::Sound_ArrowHit_1)->second,0); + break; + case 1: + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_ArrowHit_2)->second, 0); + break; + case 2: + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_ArrowHit_3)->second, 0); + break; + default: + break; + } + Bullet::on_collide(enemy); + } + +private: + +}; + +#endif // !_ARROW_BULLET_H_ + diff --git a/games/TowerDefense/axe_bullet.h b/games/TowerDefense/axe_bullet.h new file mode 100644 index 00000000..b34431eb --- /dev/null +++ b/games/TowerDefense/axe_bullet.h @@ -0,0 +1,60 @@ +#ifndef _AXE_BULLET_H_ +#define _AXE_BULLET_H_ + +#include "bullet.h" +#include "resources_manager.h" + + + +class AxeBullet : public Bullet +{ +public: + AxeBullet() + { + static SDL_Texture* tex_axe = ResourcesManager::instance() + ->get_texture_pool().find(ResID::Tex_BulletAxe)->second; + static const std::vector idx_list = { 0,1,2,3,4,5,6,7,8}; + + animation.set_loop(true); + animation.set_interval(0.1); + animation.set_frame_data(tex_axe, 4, 2, idx_list); + + can_rotated = false; + size.x = 48, size.y = 48; + } + + ~AxeBullet() = default; + void on_collide(Enemy* enemy) override + { + static const ResourcesManager::SoundPool& sound_pool + = ResourcesManager::instance()->get_sound_pool(); + + switch (rand() % 3) + { + case 0: + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_AxeHit_1)->second, 0); + break; + case 1: + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_AxeHit_2)->second, 0); + break; + case 2: + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_AxeHit_3)->second, 0); + break; + default: + break; + } + + enemy->slow_down(); + + Bullet::on_collide(enemy); + } +private: + +}; + + + + + +#endif // !_AXE_BULLET_H_ + diff --git a/games/TowerDefense/axeman_tower.h b/games/TowerDefense/axeman_tower.h new file mode 100644 index 00000000..483ef84d --- /dev/null +++ b/games/TowerDefense/axeman_tower.h @@ -0,0 +1,48 @@ +#ifndef _AXEMAN_TOWER_H_ +#define _AXEMAN_TOWER_H_ + +#include "tower.h" +#include "resources_manager.h" + +class AxemanTower : public Tower +{ +public: + AxemanTower() + { + static SDL_Texture* tex_axeman = ResourcesManager::instance() + ->get_texture_pool().find(ResID::Tex_Axeman)->second; + + static const std::vector idx_list_idle_up = { 3, 4 }; + static const std::vector idx_list_idle_down = { 0, 1 }; + static const std::vector idx_list_idle_left = { 9, 10 }; + static const std::vector idx_list_idle_right = { 6, 7 }; + static const std::vector idx_list_fire_up = { 15, 16, 17 }; + static const std::vector idx_list_fire_down = { 12, 13, 14 }; + static const std::vector idx_list_fire_left = { 21, 22, 23 }; + static const std::vector idx_list_fire_right = { 18, 19, 20 }; + + anim_idle_up.set_frame_data(tex_axeman, 3, 8, idx_list_idle_up); + anim_idle_down.set_frame_data(tex_axeman, 3, 8, idx_list_idle_down); + anim_idle_left.set_frame_data(tex_axeman, 3, 8, idx_list_idle_left); + anim_idle_right.set_frame_data(tex_axeman, 3, 8, idx_list_idle_right); + anim_fire_up.set_frame_data(tex_axeman, 3, 8, idx_list_fire_up); + anim_fire_down.set_frame_data(tex_axeman, 3, 8, idx_list_fire_down); + anim_fire_left.set_frame_data(tex_axeman, 3, 8, idx_list_fire_left); + anim_fire_right.set_frame_data(tex_axeman, 3, 8, idx_list_fire_right); + + size.x = 48, size.y = 48; + + tower_type = TowerType::Axeman; + + fire_speed = 5; + bullet_type = BulletType::Axe; + } + + ~AxemanTower() = default; + +private: + +}; + +#endif // !_AXEMAN_TOWER_H_ + diff --git a/games/TowerDefense/banner.h b/games/TowerDefense/banner.h new file mode 100644 index 00000000..612badfc --- /dev/null +++ b/games/TowerDefense/banner.h @@ -0,0 +1,81 @@ +#ifndef _BANNER_H_ +#define _BANNER_H_ + +#include "timer.h" +#include "vector2.h" +#include "config_manager.h" +#include "resources_manager.h" + +#include + +class Banner +{ +public: + Banner() + { + size_foreground = { 646, 215 }; + size_background = { 1282, 209 }; + + timer_display.set_one_shot(true); + timer_display.set_wait_time(5); + timer_display.set_on_timeout( + [&]() + { + is_end_display = true; + }); + } + + ~Banner() = default; + + void set_center_position(const Vector2& pos) + { + pos_center = pos; + } + + void on_update(double delta) + { + timer_display.on_update(delta); + + const ResourcesManager::TexturePool& tex_pool + = ResourcesManager::instance()->get_texture_pool(); + const ConfigManager* instance = ConfigManager::instance(); + + tex_foreground = tex_pool.find(instance->is_game_win ? ResID::Tex_UIWinText : ResID::Tex_UILossText)->second; + tex_background = tex_pool.find(ResID::Tex_UIGameOverBar)->second; + } + + void on_render(SDL_Renderer* renderer) + { + static SDL_Rect rect_dst; + + rect_dst.x = (int)(pos_center.x - size_background.x / 2); + rect_dst.y = (int)(pos_center.y - size_background.y / 2); + rect_dst.w = (int)size_background.x, rect_dst.h = (int)size_background.y; + SDL_RenderCopy(renderer, tex_background, nullptr, &rect_dst); + + rect_dst.x = (int)(pos_center.x - size_foreground.x / 2); + rect_dst.y = (int)(pos_center.y - size_foreground.y / 2); + rect_dst.w = (int)size_foreground.x, rect_dst.h = (int)size_foreground.y; + SDL_RenderCopy(renderer, tex_foreground, nullptr, &rect_dst); + } + + bool check_end_dispaly() + { + return is_end_display; + } + +private: + Vector2 pos_center; + + Vector2 size_foreground; + Vector2 size_background; + + SDL_Texture* tex_foreground = nullptr; + SDL_Texture* tex_background = nullptr; + + Timer timer_display; + bool is_end_display = false; + +}; + +#endif // !_BANNER_H_ diff --git a/games/TowerDefense/builds/SDL2.dll b/games/TowerDefense/builds/SDL2.dll new file mode 100644 index 00000000..53dddbf7 Binary files /dev/null and b/games/TowerDefense/builds/SDL2.dll differ diff --git a/games/TowerDefense/builds/SDL2_image.dll b/games/TowerDefense/builds/SDL2_image.dll new file mode 100644 index 00000000..d90de2a3 Binary files /dev/null and b/games/TowerDefense/builds/SDL2_image.dll differ diff --git a/games/TowerDefense/builds/SDL2_mixer.dll b/games/TowerDefense/builds/SDL2_mixer.dll new file mode 100644 index 00000000..5e4fef32 Binary files /dev/null and b/games/TowerDefense/builds/SDL2_mixer.dll differ diff --git a/games/TowerDefense/builds/SDL2_ttf.dll b/games/TowerDefense/builds/SDL2_ttf.dll new file mode 100644 index 00000000..dbbf3851 Binary files /dev/null and b/games/TowerDefense/builds/SDL2_ttf.dll differ diff --git a/games/TowerDefense/builds/config.json b/games/TowerDefense/builds/config.json new file mode 100644 index 00000000..c645b22e --- /dev/null +++ b/games/TowerDefense/builds/config.json @@ -0,0 +1,96 @@ +{ + "basic": + { + "window_title": "鏉戝簞淇濆崼鎴橈紒", + "window_width": 1280, + "window_height": 720 + }, + "player": + { + "speed": 5, + "normal_attack_interval": 0.5, + "normal_attack_damage": 10, + "skill_interval": 10, + "skill_damage": 5 + }, + "tower": + { + "archer": + { + "interval": [1, 1, 1, 1, 1, 1, 1, 1, 1, 0.1], + "damage": [5, 15, 20, 30, 40, 50, 60, 70, 80, 90], + "view_range": [3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + "cost": [10, 20, 30, 40, 50, 60, 60, 70, 80, 0], + "upgrade_cost": [10, 10, 10, 10, 10, 10, 10, 10, 10] + }, + "axeman": + { + "interval": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + "damage": [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], + "view_range": [4, 4, 4, 4, 4, 4, 4, 4, 4, 4], + "cost": [20, 20, 20, 20, 20, 20, 20, 20, 20, 20], + "upgrade_cost": [10, 10, 10, 10, 10, 10, 10, 10, 10] + }, + "gunner": + { + "interval": [3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + "damage": [20, 10, 10, 10, 10, 10, 10, 10, 10, 10], + "view_range": [5, 5, 5, 5, 5, 5, 5, 5, 5, 5], + "cost": [30, 30, 30, 30, 30, 30, 30, 30, 30, 30], + "upgrade_cost": [10, 10, 10, 10, 10, 10, 10, 10, 10] + } + }, + "enemy": + { + "slim": + { + "hp": 20, + "speed": 1, + "damage": 1, + "reward_ratio": 0.8, + "recover_interval": 100, + "recover_range": -1, + "recover_intensity": 10 + }, + "king_slim": + { + "hp": 75, + "speed": 0.75, + "damage": 1, + "reward_ratio": 1, + "recover_interval": 100, + "recover_range": -1, + "recover_intensity": 10 + }, + "skeleton": + { + "hp": 40, + "speed": 1.5, + "damage": 1, + "reward_ratio": 1, + "recover_interval": 100, + "recover_range": -1, + "recover_intensity": 10 + }, + "goblin": + { + "hp": 50, + "speed": 1.5, + "damage": 1, + "reward_ratio": 1, + "recover_interval": 100, + "recover_range": -1, + "recover_intensity": 10 + }, + "goblin_priest": + { + "hp": 100, + "speed": 0.75, + "damage": 1, + "reward_ratio": 1, + "recover_interval": 10, + "recover_range": 5, + "recover_intensity": 10 + } + } +} \ No newline at end of file diff --git a/games/TowerDefense/builds/demo.exp b/games/TowerDefense/builds/demo.exp new file mode 100644 index 00000000..0e6902eb Binary files /dev/null and b/games/TowerDefense/builds/demo.exp differ diff --git a/games/TowerDefense/builds/demo.lib b/games/TowerDefense/builds/demo.lib new file mode 100644 index 00000000..2a927ba4 Binary files /dev/null and b/games/TowerDefense/builds/demo.lib differ diff --git a/games/TowerDefense/builds/demo.pdb b/games/TowerDefense/builds/demo.pdb new file mode 100644 index 00000000..9e373737 Binary files /dev/null and b/games/TowerDefense/builds/demo.pdb differ diff --git a/games/TowerDefense/builds/level.json b/games/TowerDefense/builds/level.json new file mode 100644 index 00000000..fe2be1e1 --- /dev/null +++ b/games/TowerDefense/builds/level.json @@ -0,0 +1,35 @@ +[{ + "interval": 1, + "rewards": 100, + "spawn_list": [{ + "interval": 1, + "point": 1, + "enemy": "Slim" + }, { + "interval": 1, + "point": 2, + "enemy": "KingSlim" + }, { + "interval": 1, + "point": 1, + "enemy": "Goblin" + }, { + "interval": 1, + "point": 2, + "enemy": "GoblinPriest" + }, { + "interval": 1, + "point": 1, + "enemy": "Skeleton" + }] + }, { + "interval": 3, + "rewards": 100, + "spawn_list": [{ + "interval": 3, + "point": 2, + "enemy": "KingSlim" + }] + }] + + \ No newline at end of file diff --git a/games/TowerDefense/builds/map.csv b/games/TowerDefense/builds/map.csv new file mode 100644 index 00000000..c65bf570 --- /dev/null +++ b/games/TowerDefense/builds/map.csv @@ -0,0 +1,15 @@ +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,3\-1\4\1,3\-1\4\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\4\-1,3\-1\4\-1,3\-1\4\-1,3\-1\4\-1,3\-1\4\-1,3\-1\4\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,4\-1\2\-1,3\-1\3\-1,3\-1\3\-1,4\-1\3\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\54\0\-1,0\54\0\-1,0\29\0\-1,4\-1\2\-1,0\29\0\-1,0\54\0\-1,0\54\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\54\0\-1,4\128\0\-1,5\-1\0\-1,3\-1\2\-1,5\-1\0\-1,4\128\0\-1,0\54\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\53\0\-1,3\-1\0\-1,3\-1\0\-1,5\-1\0\0,3\-1\0\-1,3\-1\0\-1,0\53\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\53\0\-1,0\53\0\-1,5\-1\0\-1,4\-1\1\-1,5\-1\0\-1,0\53\0\-1,0\53\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\285\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,3\-1\3\-1,3\-1\3\-1,3\-1\3\-1,3\-1\3\-1,3\-1\3\-1,4\-1\3\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\4\-1,3\-1\4\-1,3\-1\4\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,3\-1\3\-1,3\-1\3\2,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 \ No newline at end of file diff --git a/games/TowerDefense/builds/resources/bullet_arrow.png b/games/TowerDefense/builds/resources/bullet_arrow.png new file mode 100644 index 00000000..68f8d755 Binary files /dev/null and b/games/TowerDefense/builds/resources/bullet_arrow.png differ diff --git a/games/TowerDefense/builds/resources/bullet_axe.png b/games/TowerDefense/builds/resources/bullet_axe.png new file mode 100644 index 00000000..5e8b5cc8 Binary files /dev/null and b/games/TowerDefense/builds/resources/bullet_axe.png differ diff --git a/games/TowerDefense/builds/resources/bullet_shell.png b/games/TowerDefense/builds/resources/bullet_shell.png new file mode 100644 index 00000000..c4445dec Binary files /dev/null and b/games/TowerDefense/builds/resources/bullet_shell.png differ diff --git a/games/TowerDefense/builds/resources/coin.png b/games/TowerDefense/builds/resources/coin.png new file mode 100644 index 00000000..c8e0ddb8 Binary files /dev/null and b/games/TowerDefense/builds/resources/coin.png differ diff --git a/games/TowerDefense/builds/resources/effect_explode.png b/games/TowerDefense/builds/resources/effect_explode.png new file mode 100644 index 00000000..616b1178 Binary files /dev/null and b/games/TowerDefense/builds/resources/effect_explode.png differ diff --git a/games/TowerDefense/builds/resources/effect_flash_down.png b/games/TowerDefense/builds/resources/effect_flash_down.png new file mode 100644 index 00000000..a1b293b1 Binary files /dev/null and b/games/TowerDefense/builds/resources/effect_flash_down.png differ diff --git a/games/TowerDefense/builds/resources/effect_flash_left.png b/games/TowerDefense/builds/resources/effect_flash_left.png new file mode 100644 index 00000000..1b7258aa Binary files /dev/null and b/games/TowerDefense/builds/resources/effect_flash_left.png differ diff --git a/games/TowerDefense/builds/resources/effect_flash_right.png b/games/TowerDefense/builds/resources/effect_flash_right.png new file mode 100644 index 00000000..14ea59af Binary files /dev/null and b/games/TowerDefense/builds/resources/effect_flash_right.png differ diff --git a/games/TowerDefense/builds/resources/effect_flash_up.png b/games/TowerDefense/builds/resources/effect_flash_up.png new file mode 100644 index 00000000..6fc82af0 Binary files /dev/null and b/games/TowerDefense/builds/resources/effect_flash_up.png differ diff --git a/games/TowerDefense/builds/resources/effect_impact_down.png b/games/TowerDefense/builds/resources/effect_impact_down.png new file mode 100644 index 00000000..091b4fee Binary files /dev/null and b/games/TowerDefense/builds/resources/effect_impact_down.png differ diff --git a/games/TowerDefense/builds/resources/effect_impact_left.png b/games/TowerDefense/builds/resources/effect_impact_left.png new file mode 100644 index 00000000..bd543f6e Binary files /dev/null and b/games/TowerDefense/builds/resources/effect_impact_left.png differ diff --git a/games/TowerDefense/builds/resources/effect_impact_right.png b/games/TowerDefense/builds/resources/effect_impact_right.png new file mode 100644 index 00000000..c09a99cf Binary files /dev/null and b/games/TowerDefense/builds/resources/effect_impact_right.png differ diff --git a/games/TowerDefense/builds/resources/effect_impact_up.png b/games/TowerDefense/builds/resources/effect_impact_up.png new file mode 100644 index 00000000..adeb4d4e Binary files /dev/null and b/games/TowerDefense/builds/resources/effect_impact_up.png differ diff --git a/games/TowerDefense/builds/resources/enemy_goblin.png b/games/TowerDefense/builds/resources/enemy_goblin.png new file mode 100644 index 00000000..8e8117d4 Binary files /dev/null and b/games/TowerDefense/builds/resources/enemy_goblin.png differ diff --git a/games/TowerDefense/builds/resources/enemy_goblin_priest.png b/games/TowerDefense/builds/resources/enemy_goblin_priest.png new file mode 100644 index 00000000..f7fed8d9 Binary files /dev/null and b/games/TowerDefense/builds/resources/enemy_goblin_priest.png differ diff --git a/games/TowerDefense/builds/resources/enemy_goblin_priest_sketch.png b/games/TowerDefense/builds/resources/enemy_goblin_priest_sketch.png new file mode 100644 index 00000000..5b607435 Binary files /dev/null and b/games/TowerDefense/builds/resources/enemy_goblin_priest_sketch.png differ diff --git a/games/TowerDefense/builds/resources/enemy_goblin_sketch.png b/games/TowerDefense/builds/resources/enemy_goblin_sketch.png new file mode 100644 index 00000000..48fa9bab Binary files /dev/null and b/games/TowerDefense/builds/resources/enemy_goblin_sketch.png differ diff --git a/games/TowerDefense/builds/resources/enemy_king_slime.png b/games/TowerDefense/builds/resources/enemy_king_slime.png new file mode 100644 index 00000000..91c4a075 Binary files /dev/null and b/games/TowerDefense/builds/resources/enemy_king_slime.png differ diff --git a/games/TowerDefense/builds/resources/enemy_king_slime_sketch.png b/games/TowerDefense/builds/resources/enemy_king_slime_sketch.png new file mode 100644 index 00000000..966f833b Binary files /dev/null and b/games/TowerDefense/builds/resources/enemy_king_slime_sketch.png differ diff --git a/games/TowerDefense/builds/resources/enemy_skeleton.png b/games/TowerDefense/builds/resources/enemy_skeleton.png new file mode 100644 index 00000000..765c1bbe Binary files /dev/null and b/games/TowerDefense/builds/resources/enemy_skeleton.png differ diff --git a/games/TowerDefense/builds/resources/enemy_skeleton_sketch.png b/games/TowerDefense/builds/resources/enemy_skeleton_sketch.png new file mode 100644 index 00000000..c814dcda Binary files /dev/null and b/games/TowerDefense/builds/resources/enemy_skeleton_sketch.png differ diff --git a/games/TowerDefense/builds/resources/enemy_slime.png b/games/TowerDefense/builds/resources/enemy_slime.png new file mode 100644 index 00000000..d48b5833 Binary files /dev/null and b/games/TowerDefense/builds/resources/enemy_slime.png differ diff --git a/games/TowerDefense/builds/resources/enemy_slime_sketch.png b/games/TowerDefense/builds/resources/enemy_slime_sketch.png new file mode 100644 index 00000000..f7f3d8c5 Binary files /dev/null and b/games/TowerDefense/builds/resources/enemy_slime_sketch.png differ diff --git a/games/TowerDefense/builds/resources/home.png b/games/TowerDefense/builds/resources/home.png new file mode 100644 index 00000000..330e8bbd Binary files /dev/null and b/games/TowerDefense/builds/resources/home.png differ diff --git a/games/TowerDefense/builds/resources/ipix.ttf b/games/TowerDefense/builds/resources/ipix.ttf new file mode 100644 index 00000000..9a484ca9 Binary files /dev/null and b/games/TowerDefense/builds/resources/ipix.ttf differ diff --git a/games/TowerDefense/builds/resources/music_bgm.mp3 b/games/TowerDefense/builds/resources/music_bgm.mp3 new file mode 100644 index 00000000..9d2a8865 Binary files /dev/null and b/games/TowerDefense/builds/resources/music_bgm.mp3 differ diff --git a/games/TowerDefense/builds/resources/player.png b/games/TowerDefense/builds/resources/player.png new file mode 100644 index 00000000..46794a04 Binary files /dev/null and b/games/TowerDefense/builds/resources/player.png differ diff --git a/games/TowerDefense/builds/resources/sound_arrow_fire_1.mp3 b/games/TowerDefense/builds/resources/sound_arrow_fire_1.mp3 new file mode 100644 index 00000000..557a88ff Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_arrow_fire_1.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_arrow_fire_2.mp3 b/games/TowerDefense/builds/resources/sound_arrow_fire_2.mp3 new file mode 100644 index 00000000..d3c4b0cb Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_arrow_fire_2.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_arrow_hit_1.mp3 b/games/TowerDefense/builds/resources/sound_arrow_hit_1.mp3 new file mode 100644 index 00000000..eff116fa Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_arrow_hit_1.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_arrow_hit_2.mp3 b/games/TowerDefense/builds/resources/sound_arrow_hit_2.mp3 new file mode 100644 index 00000000..849a013f Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_arrow_hit_2.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_arrow_hit_3.mp3 b/games/TowerDefense/builds/resources/sound_arrow_hit_3.mp3 new file mode 100644 index 00000000..0c5b9917 Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_arrow_hit_3.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_axe_fire.wav b/games/TowerDefense/builds/resources/sound_axe_fire.wav new file mode 100644 index 00000000..ba263008 Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_axe_fire.wav differ diff --git a/games/TowerDefense/builds/resources/sound_axe_hit_1.mp3 b/games/TowerDefense/builds/resources/sound_axe_hit_1.mp3 new file mode 100644 index 00000000..eff116fa Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_axe_hit_1.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_axe_hit_2.mp3 b/games/TowerDefense/builds/resources/sound_axe_hit_2.mp3 new file mode 100644 index 00000000..849a013f Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_axe_hit_2.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_axe_hit_3.mp3 b/games/TowerDefense/builds/resources/sound_axe_hit_3.mp3 new file mode 100644 index 00000000..0c5b9917 Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_axe_hit_3.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_coin.mp3 b/games/TowerDefense/builds/resources/sound_coin.mp3 new file mode 100644 index 00000000..64401825 Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_coin.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_flash.wav b/games/TowerDefense/builds/resources/sound_flash.wav new file mode 100644 index 00000000..6525ad03 Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_flash.wav differ diff --git a/games/TowerDefense/builds/resources/sound_home_hurt.wav b/games/TowerDefense/builds/resources/sound_home_hurt.wav new file mode 100644 index 00000000..b094056e Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_home_hurt.wav differ diff --git a/games/TowerDefense/builds/resources/sound_impact.wav b/games/TowerDefense/builds/resources/sound_impact.wav new file mode 100644 index 00000000..14cd0b01 Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_impact.wav differ diff --git a/games/TowerDefense/builds/resources/sound_loss.mp3 b/games/TowerDefense/builds/resources/sound_loss.mp3 new file mode 100644 index 00000000..b1ab9df8 Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_loss.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_place_tower.mp3 b/games/TowerDefense/builds/resources/sound_place_tower.mp3 new file mode 100644 index 00000000..f90ccde6 Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_place_tower.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_shell_fire.wav b/games/TowerDefense/builds/resources/sound_shell_fire.wav new file mode 100644 index 00000000..7e804504 Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_shell_fire.wav differ diff --git a/games/TowerDefense/builds/resources/sound_shell_hit.mp3 b/games/TowerDefense/builds/resources/sound_shell_hit.mp3 new file mode 100644 index 00000000..23765a1b Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_shell_hit.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_tower_level_up.mp3 b/games/TowerDefense/builds/resources/sound_tower_level_up.mp3 new file mode 100644 index 00000000..5cf1c30b Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_tower_level_up.mp3 differ diff --git a/games/TowerDefense/builds/resources/sound_win.wav b/games/TowerDefense/builds/resources/sound_win.wav new file mode 100644 index 00000000..6fada6b3 Binary files /dev/null and b/games/TowerDefense/builds/resources/sound_win.wav differ diff --git a/games/TowerDefense/builds/resources/tileset.png b/games/TowerDefense/builds/resources/tileset.png new file mode 100644 index 00000000..b6366a8b Binary files /dev/null and b/games/TowerDefense/builds/resources/tileset.png differ diff --git a/games/TowerDefense/builds/resources/tower_archer.png b/games/TowerDefense/builds/resources/tower_archer.png new file mode 100644 index 00000000..d7bb5db3 Binary files /dev/null and b/games/TowerDefense/builds/resources/tower_archer.png differ diff --git a/games/TowerDefense/builds/resources/tower_axeman.png b/games/TowerDefense/builds/resources/tower_axeman.png new file mode 100644 index 00000000..49ffd2cb Binary files /dev/null and b/games/TowerDefense/builds/resources/tower_axeman.png differ diff --git a/games/TowerDefense/builds/resources/tower_gunner.png b/games/TowerDefense/builds/resources/tower_gunner.png new file mode 100644 index 00000000..0e6589d7 Binary files /dev/null and b/games/TowerDefense/builds/resources/tower_gunner.png differ diff --git a/games/TowerDefense/builds/resources/ui_coin.png b/games/TowerDefense/builds/resources/ui_coin.png new file mode 100644 index 00000000..fa386d11 Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_coin.png differ diff --git a/games/TowerDefense/builds/resources/ui_game_over_bar.png b/games/TowerDefense/builds/resources/ui_game_over_bar.png new file mode 100644 index 00000000..9699da4c Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_game_over_bar.png differ diff --git a/games/TowerDefense/builds/resources/ui_heart.png b/games/TowerDefense/builds/resources/ui_heart.png new file mode 100644 index 00000000..59be99e9 Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_heart.png differ diff --git a/games/TowerDefense/builds/resources/ui_home_avatar.png b/games/TowerDefense/builds/resources/ui_home_avatar.png new file mode 100644 index 00000000..61196531 Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_home_avatar.png differ diff --git a/games/TowerDefense/builds/resources/ui_loss_text.png b/games/TowerDefense/builds/resources/ui_loss_text.png new file mode 100644 index 00000000..628b9de3 Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_loss_text.png differ diff --git a/games/TowerDefense/builds/resources/ui_place_hovered_left.png b/games/TowerDefense/builds/resources/ui_place_hovered_left.png new file mode 100644 index 00000000..c803fa11 Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_place_hovered_left.png differ diff --git a/games/TowerDefense/builds/resources/ui_place_hovered_right.png b/games/TowerDefense/builds/resources/ui_place_hovered_right.png new file mode 100644 index 00000000..9e0dd1b1 Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_place_hovered_right.png differ diff --git a/games/TowerDefense/builds/resources/ui_place_hovered_top.png b/games/TowerDefense/builds/resources/ui_place_hovered_top.png new file mode 100644 index 00000000..410b6886 Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_place_hovered_top.png differ diff --git a/games/TowerDefense/builds/resources/ui_place_idle.png b/games/TowerDefense/builds/resources/ui_place_idle.png new file mode 100644 index 00000000..d83b6bdd Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_place_idle.png differ diff --git a/games/TowerDefense/builds/resources/ui_player_avatar.png b/games/TowerDefense/builds/resources/ui_player_avatar.png new file mode 100644 index 00000000..51b268b5 Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_player_avatar.png differ diff --git a/games/TowerDefense/builds/resources/ui_select_cursor.png b/games/TowerDefense/builds/resources/ui_select_cursor.png new file mode 100644 index 00000000..11a9886d Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_select_cursor.png differ diff --git a/games/TowerDefense/builds/resources/ui_upgrade_hovered_left.png b/games/TowerDefense/builds/resources/ui_upgrade_hovered_left.png new file mode 100644 index 00000000..6efad515 Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_upgrade_hovered_left.png differ diff --git a/games/TowerDefense/builds/resources/ui_upgrade_hovered_right.png b/games/TowerDefense/builds/resources/ui_upgrade_hovered_right.png new file mode 100644 index 00000000..1419f46d Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_upgrade_hovered_right.png differ diff --git a/games/TowerDefense/builds/resources/ui_upgrade_hovered_top.png b/games/TowerDefense/builds/resources/ui_upgrade_hovered_top.png new file mode 100644 index 00000000..2b95f668 Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_upgrade_hovered_top.png differ diff --git a/games/TowerDefense/builds/resources/ui_upgrade_idle.png b/games/TowerDefense/builds/resources/ui_upgrade_idle.png new file mode 100644 index 00000000..2317fe01 Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_upgrade_idle.png differ diff --git a/games/TowerDefense/builds/resources/ui_win_text.png b/games/TowerDefense/builds/resources/ui_win_text.png new file mode 100644 index 00000000..a7d8134b Binary files /dev/null and b/games/TowerDefense/builds/resources/ui_win_text.png differ diff --git a/games/TowerDefense/builds/the first game of seventhback.exp b/games/TowerDefense/builds/the first game of seventhback.exp new file mode 100644 index 00000000..e69d2189 Binary files /dev/null and b/games/TowerDefense/builds/the first game of seventhback.exp differ diff --git a/games/TowerDefense/builds/the first game of seventhback.lib b/games/TowerDefense/builds/the first game of seventhback.lib new file mode 100644 index 00000000..202c46b3 Binary files /dev/null and b/games/TowerDefense/builds/the first game of seventhback.lib differ diff --git a/games/TowerDefense/builds/the first game of seventhback.pdb b/games/TowerDefense/builds/the first game of seventhback.pdb new file mode 100644 index 00000000..7536de75 Binary files /dev/null and b/games/TowerDefense/builds/the first game of seventhback.pdb differ diff --git a/games/TowerDefense/bullet.h b/games/TowerDefense/bullet.h new file mode 100644 index 00000000..668756cc --- /dev/null +++ b/games/TowerDefense/bullet.h @@ -0,0 +1,129 @@ +#ifndef _BULLET_H_ +#define _BULLET_H_ + +#include "vector2.h" +#include "enemy.h" +#include "animation.h" +#include "config_manager.h" + +class Bullet +{ +public: + Bullet() = default; + ~Bullet() = default; + + void set_velocity(const Vector2& velocity) + { + this->velocity = velocity; + + if (can_rotated) + { + double randian = std::atan2(velocity.y, velocity.x); + angle_anim_rotated = randian * 180 / 3.14159265; + } + } + + void set_position(const Vector2& position) + { + this->position = position; + } + + void set_damage(double damage) + { + this->damage = damage; + } + + const Vector2& get_size() const + { + return size; + } + + const Vector2& get_position() const + { + return position; + } + + double get_damage() const + { + return damage; + } + + double get_damage_range() const + { + return damage_range; + } + + void disable_collide() + { + is_collisional = false; + } + + bool can_collide() const + { + return is_collisional; + } + + void make_invalid() + { + is_valid = false; + is_collisional = false; + } + + bool can_remove() const + { + return !is_valid; + } + + virtual void on_update(double delta) + { + animation.on_update(delta); + position += velocity * delta; + + static const SDL_Rect& rect_map + = ConfigManager::instance()->rect_tile_map; + + if (position.x - size.x / 2 <= rect_map.x + || position.x + size.x / 2 >= rect_map.x + rect_map.w + || position.y - size.y / 2 <= rect_map.y + || position.y + size.y / 2 >= rect_map.y + rect_map.h) + { + is_valid = false; + } + } + + virtual void on_render(SDL_Renderer* renderer) + { + static SDL_Point point; + + point.x = (int)(position.x - size.x / 2); + point.y = (int)(position.y - size.y / 2); + + animation.on_render(renderer, point, angle_anim_rotated); + } + + virtual void on_collide(Enemy* enemy) + { + is_valid = false; + is_collisional = false; + } + +protected: + Vector2 size; + Vector2 velocity; + Vector2 position; + + Animation animation; + bool can_rotated = false; + + double damage = 0; + double damage_range = -1; + +private: + bool is_valid = true; + bool is_collisional = true; + double angle_anim_rotated = 0; + +}; + +#endif // !_BULLET_H_ + diff --git a/games/TowerDefense/bullet_manager.h b/games/TowerDefense/bullet_manager.h new file mode 100644 index 00000000..8be37c44 --- /dev/null +++ b/games/TowerDefense/bullet_manager.h @@ -0,0 +1,87 @@ +#ifndef _BULLET_MANAGER_H_ +#define _BULLET_MANAGER_H_ + +#include "bullet.h" +#include "manager.h" +#include "bullet_type.h" +#include "shell_bullet.h" +#include "arrow_bullet.h" +#include "axe_bullet.h" +#include + +class BulletManager : public Manager +{ + friend class Manager; + +public: + typedef std::vector BulletList; + +public: + void on_update(double delta) + { + for (Bullet* bullet : bullet_list) + bullet->on_update(delta); + + bullet_list.erase(std::remove_if( + bullet_list.begin(), bullet_list.end(), + [](const Bullet* bullet) + { + bool deletable = bullet->can_remove(); + if (deletable) delete bullet; + return deletable; + }), bullet_list.end()); + } + + void on_render(SDL_Renderer* renderer) + { + for (Bullet* bullet : bullet_list) + bullet->on_render(renderer); + } + + BulletList& get_bullet_list() + { + return bullet_list; + } + + void fire_bullet(BulletType type, const Vector2& position, const Vector2& velocity, double damage) + { + Bullet* bullet = nullptr; + + switch (type) + { + case Arrow: + bullet = new ArrowBullet(); + break; + case Axe: + bullet = new AxeBullet(); + break; + case Shell: + bullet = new ShellBullet(); + break; + default: + bullet = new ArrowBullet(); + break; + } + + bullet->set_position(position); + bullet->set_velocity(velocity); + bullet->set_damage(damage); + + bullet_list.push_back(bullet); + } + +protected: + BulletManager() = default; + + ~BulletManager() + { + for (Bullet* bullet : bullet_list) + delete bullet; + } + +private: + BulletList bullet_list; + +}; + +#endif // !_BULLET_MANAGER_H_ diff --git a/games/TowerDefense/bullet_type.h b/games/TowerDefense/bullet_type.h new file mode 100644 index 00000000..6244c6bc --- /dev/null +++ b/games/TowerDefense/bullet_type.h @@ -0,0 +1,13 @@ +#ifndef _BULLET_TYPE_H_ +#define _BULLET_TYPE_H_ + +enum BulletType +{ + Arrow, + Axe, + Shell +}; + + +#endif // !_BULLET_TYPE_H_ + diff --git a/games/TowerDefense/coin_manager.h b/games/TowerDefense/coin_manager.h new file mode 100644 index 00000000..f14324bd --- /dev/null +++ b/games/TowerDefense/coin_manager.h @@ -0,0 +1,89 @@ +#ifndef _COIN_MANAGER_H_ +#define _COIN_MANAGER_H_ + +#include "coin_prop.h" +#include "manager.h" +#include "config_manager.h" + +#include + +class CoinManager : public Manager +{ + friend class Manager; + +public: + typedef std::vector CoinPropList; + +public: + void increase_coin(double val) + { + num_coin += val; + } + + void decrease_coin(double val) + { + num_coin -= val; + + if (num_coin < 0) + num_coin = 0; + } + + void on_update(double delta) + { + for (CoinProp* coin_prop : coin_prop_list) + coin_prop->on_update(delta); + + coin_prop_list.erase(std::remove_if(coin_prop_list.begin(), coin_prop_list.end(), + [](CoinProp* coin_prop) + { + bool deletable = coin_prop->can_remove(); + if (deletable) delete coin_prop; + return deletable; + }), coin_prop_list.end()); + } + + void on_render(SDL_Renderer* renderer) + { + for (CoinProp* coin_prop : coin_prop_list) + coin_prop->on_render(renderer); + } + + double get_current_coin_num() + { + return num_coin; + } + + CoinPropList& get_coin_prop_list() + { + return coin_prop_list; + } + + void spawn_coin_prop(const Vector2& position) + { + CoinProp* coin_prop = new CoinProp(); + coin_prop->set_position(position); + + coin_prop_list.push_back(coin_prop); + } + +protected: + CoinManager() + { + num_coin = ConfigManager::instance()->num_initial_coin; + } + + ~CoinManager() + { + for (CoinProp* coin_prop : coin_prop_list) + delete coin_prop; + } + +private: + double num_coin = 0; + + CoinPropList coin_prop_list; + +}; + + +#endif // !_COIN_MANAGER_H_ diff --git a/games/TowerDefense/coin_prop.h b/games/TowerDefense/coin_prop.h new file mode 100644 index 00000000..59493e79 --- /dev/null +++ b/games/TowerDefense/coin_prop.h @@ -0,0 +1,110 @@ +#ifndef _COIN_PROP_H_ +#define _COIN_PROP_H_ +#include "tile.h" +#include"timer.h" +#include "vector2.h" +#include "resources_manager.h" + +#include + + + +class CoinProp +{ +public: + CoinProp() + { + timer_jump.set_one_shot(true); + timer_jump.set_wait_time(interval_jump); + timer_jump.set_on_timeout( + [&]() + { + is_jumping = false; + }); + + timer_disappear.set_one_shot(true); + timer_disappear.set_wait_time(interval_disappear); + timer_disappear.set_on_timeout( + [&]() + { + is_valid = false; + }); + velocity.x = (rand() % 2 ? 1 : -1) * 2 * SIZE_TILE;//随机向左向右的方向 + velocity.y = -3 * SIZE_TILE; + } + + ~CoinProp() = default; + + void set_position(const Vector2& position) + { + this->position = position; + } + + const Vector2& get_position() const + { + return position; + } + + const Vector2& get_size() const + { + return size; + } + + void make_invalid() + { + is_valid = false; + } + + bool can_remove() + { + return !is_valid; + } + + void on_update(double delta) + { + timer_jump.on_update(delta); + timer_disappear.on_update(delta); + + if (is_jumping) + { + velocity.y += gravity * delta; + } + else + { + velocity.x = 0; + velocity.y = sin(SDL_GetTicks64() / 1000.0 * 4) * 30; + } + + position += velocity * delta; + } + + void on_render(SDL_Renderer* renderer) + { + static SDL_Rect rect = { 0, 0, (int)size.x, (int)size.y }; + static SDL_Texture* tex_coin = ResourcesManager::instance() + ->get_texture_pool().find(ResID::Tex_Coin)->second; + + rect.x = (int)(position.x - size.x / 2); + rect.y = (int)(position.y - size.y / 2); + + SDL_RenderCopy(renderer, tex_coin, nullptr, &rect); + } +private: + Vector2 position; + Vector2 velocity; + + Timer timer_jump; + Timer timer_disappear; + + bool is_valid = true; + bool is_jumping = true; + + double gravity = 490; + double interval_jump = 0.75; + Vector2 size = { 16, 16 }; + double interval_disappear = 10; + +}; + +#endif // !_COIN_PROP_H_ + diff --git a/games/TowerDefense/config.json b/games/TowerDefense/config.json new file mode 100644 index 00000000..c645b22e --- /dev/null +++ b/games/TowerDefense/config.json @@ -0,0 +1,96 @@ +{ + "basic": + { + "window_title": "鏉戝簞淇濆崼鎴橈紒", + "window_width": 1280, + "window_height": 720 + }, + "player": + { + "speed": 5, + "normal_attack_interval": 0.5, + "normal_attack_damage": 10, + "skill_interval": 10, + "skill_damage": 5 + }, + "tower": + { + "archer": + { + "interval": [1, 1, 1, 1, 1, 1, 1, 1, 1, 0.1], + "damage": [5, 15, 20, 30, 40, 50, 60, 70, 80, 90], + "view_range": [3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + "cost": [10, 20, 30, 40, 50, 60, 60, 70, 80, 0], + "upgrade_cost": [10, 10, 10, 10, 10, 10, 10, 10, 10] + }, + "axeman": + { + "interval": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + "damage": [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], + "view_range": [4, 4, 4, 4, 4, 4, 4, 4, 4, 4], + "cost": [20, 20, 20, 20, 20, 20, 20, 20, 20, 20], + "upgrade_cost": [10, 10, 10, 10, 10, 10, 10, 10, 10] + }, + "gunner": + { + "interval": [3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + "damage": [20, 10, 10, 10, 10, 10, 10, 10, 10, 10], + "view_range": [5, 5, 5, 5, 5, 5, 5, 5, 5, 5], + "cost": [30, 30, 30, 30, 30, 30, 30, 30, 30, 30], + "upgrade_cost": [10, 10, 10, 10, 10, 10, 10, 10, 10] + } + }, + "enemy": + { + "slim": + { + "hp": 20, + "speed": 1, + "damage": 1, + "reward_ratio": 0.8, + "recover_interval": 100, + "recover_range": -1, + "recover_intensity": 10 + }, + "king_slim": + { + "hp": 75, + "speed": 0.75, + "damage": 1, + "reward_ratio": 1, + "recover_interval": 100, + "recover_range": -1, + "recover_intensity": 10 + }, + "skeleton": + { + "hp": 40, + "speed": 1.5, + "damage": 1, + "reward_ratio": 1, + "recover_interval": 100, + "recover_range": -1, + "recover_intensity": 10 + }, + "goblin": + { + "hp": 50, + "speed": 1.5, + "damage": 1, + "reward_ratio": 1, + "recover_interval": 100, + "recover_range": -1, + "recover_intensity": 10 + }, + "goblin_priest": + { + "hp": 100, + "speed": 0.75, + "damage": 1, + "reward_ratio": 1, + "recover_interval": 10, + "recover_range": 5, + "recover_intensity": 10 + } + } +} \ No newline at end of file diff --git a/games/TowerDefense/config.txt b/games/TowerDefense/config.txt new file mode 100644 index 00000000..e927d866 --- /dev/null +++ b/games/TowerDefense/config.txt @@ -0,0 +1,50 @@ +#-- ARCADE MACHINE GAME CONFIG FILE --# +#-- For THOTH TECH - Splashkit Crew - Applications Team +#-------------------------------------------------------------------- +#-- File: config.txt +#-- Description: Configuration data for Arcade Machine games +#-------------------------------------------------------------------- +#-- Maintenance Log -- +#-------------------------------------------------------------------- +# Task | Who | Date | Comments +#-----------+---------+----------+----------------------------------- +# Sprint 1 | +#-----------+---------+----------+----------------------------------- +# Sprint 2 | +#-----------+---------+----------+----------------------------------- +# Sprint 3 | +#-----------+---------+----------+----------------------------------- + +#-------------------------------------------------------------------- +# PLEASE ONLY EDIT CONTENT AFTER THE "=" SIGN FOR EACH CONFIGURATION SETTING +#-------------------------------------------------------------------- + +# The title of your game (eg. Super Mario) +title=the first game of seventhback + +# The name of the author (eg. Jane Doe) +author=Haoyu Liu + +# The genre of your game (eg. Platformer) +genre=TowerDefense + +# A Description of your game +description=The greatest game you have ever seen! + +# A classification rating to advise the nature of the content (eg. MA 15+) +rating=PG + +# Programming language the game is written in (eg. C++) +language=C++ + +# Path to your game thumbnail image (eg. images/icon.png) +image=TowerDefense.png + +# Location of git repo (eg. https://github.com/thoth-tech/arcade-games) +repository=https://github.com/seventhback777/arcade-games + +# [Optional] Uncomment to include path to the game executable (game.exe) +#executable=the first game of seventhback.exe + +# [Optional] Uncomment to include specific compile commands - if you have a specific compile command (eg. 'make', 'skm g++ *.cpp -lstd++') +#compile-command=skm g++ *.cpp \ No newline at end of file diff --git a/games/TowerDefense/config_manager.h b/games/TowerDefense/config_manager.h new file mode 100644 index 00000000..02ed616b --- /dev/null +++ b/games/TowerDefense/config_manager.h @@ -0,0 +1,320 @@ +#ifndef _CONFIG_MANAGER_H_ +#define _CONFIG_MANAGER_H_ + +#include "map.h" +#include "wave.h" +#include "manager.h" + +#include +#include +#include +#include +#include +#include + +class ConfigManager : public Manager +{ + friend class Manager; + +public: + struct BasicTemplate + { + std::string window_title = u8"??????????"; + int window_width = 1280; + int window_height = 720; + }; + + struct PlayerTemplate + { + double speed = 3; + double normal_attack_interval = 0.5; + double normal_attack_damage = 0; + double skill_interval = 10; + double skill_damage = 1; + }; + + struct TowerTemplate + { + double interval[10] = { 1 }; + double damage[10] = { 25 }; + double view_range[10] = { 5 }; + double cost[10] = { 50 }; + double upgrade_cost[9] = { 75 }; + }; + + struct EnemyTemplate + { + double hp = 100; + double speed = 1; + double damage = 1; + double reward_ratio = 0.5; + double recover_interval = 10; + double recover_range = 0; + double recover_intensity = 25; + }; + +public: + Map map; + std::vector wave_list; + + int level_archer = 0; + int level_axeman = 0; + int level_gunner = 0; + + bool is_game_win = true; + bool is_game_over = false; + SDL_Rect rect_tile_map = { 0 }; + + BasicTemplate basic_template; + + PlayerTemplate player_template; + + TowerTemplate archer_template; + TowerTemplate axeman_template; + TowerTemplate gunner_template; + + EnemyTemplate slim_template; + EnemyTemplate king_slim_template; + EnemyTemplate skeleton_template; + EnemyTemplate goblin_template; + EnemyTemplate goblin_priest_template; + + const double num_initial_hp = 1; + const double num_initial_coin = 100; + const double num_coin_per_prop = 10; + +public: + bool load_level_config(const std::string& path) + { + std::ifstream file(path); + + if (!file.good()) return false; + + std::stringstream str_stream; + str_stream << file.rdbuf(); file.close(); + + cJSON* json_root = cJSON_Parse(str_stream.str().c_str()); + if (!json_root) return false; + + if (json_root->type != cJSON_Array) + { + cJSON_Delete(json_root); + return false; + } + + cJSON* json_wave = nullptr; + cJSON_ArrayForEach(json_wave, json_root) + { + if (json_wave->type != cJSON_Object) + continue; + + wave_list.emplace_back(); + Wave& wave = wave_list.back(); + + cJSON* json_wave_rewards = cJSON_GetObjectItem(json_wave, "rewards"); + if (json_wave_rewards && json_wave_rewards->type == cJSON_Number) + wave.rewards = json_wave_rewards->valuedouble; + cJSON* json_wave_interval = cJSON_GetObjectItem(json_wave, "interval"); + if (json_wave_interval && json_wave_interval->type == cJSON_Number) + wave.interval = json_wave_interval->valuedouble; + cJSON* json_wave_spawn_list = cJSON_GetObjectItem(json_wave, "spawn_list"); + if (json_wave_spawn_list && json_wave_spawn_list->type == cJSON_Array) + { + cJSON* json_spawn_event = nullptr; + cJSON_ArrayForEach(json_spawn_event, json_wave_spawn_list) + { + if (json_spawn_event->type != cJSON_Object) + continue; + + wave.spawn_event_list.emplace_back(); + Wave::SpawnEvent& spawn_event = wave.spawn_event_list.back(); + + cJSON* json_spawn_event_interval = cJSON_GetObjectItem(json_spawn_event, "interval"); + if (json_spawn_event_interval && json_spawn_event_interval->type == cJSON_Number) + spawn_event.interval = json_spawn_event_interval->valuedouble; + cJSON* json_spawn_event_spawn_point = cJSON_GetObjectItem(json_spawn_event, "point"); + if (json_spawn_event_spawn_point && json_spawn_event_spawn_point->type == cJSON_Number) + spawn_event.spawn_point = json_spawn_event_spawn_point->valueint; + cJSON* json_spawn_event_enemy_type = cJSON_GetObjectItem(json_spawn_event, "enemy"); + if (json_spawn_event_enemy_type && json_spawn_event_enemy_type->type == cJSON_String) + { + const std::string str_enemy_type = json_spawn_event_enemy_type->valuestring; + if (str_enemy_type == "Slim") + spawn_event.enemy_type = EnemyType::Slim; + else if (str_enemy_type == "KingSlim") + spawn_event.enemy_type = EnemyType::KingSlim; + else if (str_enemy_type == "Skeleton") + spawn_event.enemy_type = EnemyType::Skeleton; + else if (str_enemy_type == "Goblin") + spawn_event.enemy_type = EnemyType::Goblin; + else if (str_enemy_type == "GoblinPriest") + spawn_event.enemy_type = EnemyType::GoblinPriest; + } + } + + if (wave.spawn_event_list.empty()) + wave_list.pop_back(); + } + } + + cJSON_Delete(json_root); + + if (wave_list.empty()) + return false; + + return true; + } + + bool load_game_config(const std::string& path) + { + std::ifstream file(path); + if (!file.good()) return false; + + std::stringstream str_stream; + str_stream << file.rdbuf(); file.close(); + + cJSON* json_root = cJSON_Parse(str_stream.str().c_str()); + if (!json_root || json_root->type != cJSON_Object) return false; + + cJSON* json_basic = cJSON_GetObjectItem(json_root, "basic"); + cJSON* json_player = cJSON_GetObjectItem(json_root, "player"); + cJSON* json_tower = cJSON_GetObjectItem(json_root, "tower"); + cJSON* json_enemy = cJSON_GetObjectItem(json_root, "enemy"); + + if (!json_basic || !json_player || !json_tower || !json_enemy + || json_basic->type != cJSON_Object + || json_player->type != cJSON_Object + || json_tower->type != cJSON_Object + || json_enemy->type != cJSON_Object) + { + cJSON_Delete(json_root); + return false; + } + + parse_basic_template(basic_template, json_basic); + + parse_player_template(player_template, json_player); + + parse_tower_template(archer_template, cJSON_GetObjectItem(json_tower, "archer")); + parse_tower_template(axeman_template, cJSON_GetObjectItem(json_tower, "axeman")); + parse_tower_template(gunner_template, cJSON_GetObjectItem(json_tower, "gunner")); + + parse_enemy_template(slim_template, cJSON_GetObjectItem(json_enemy, "slim")); + parse_enemy_template(king_slim_template, cJSON_GetObjectItem(json_enemy, "king_slim")); + parse_enemy_template(skeleton_template, cJSON_GetObjectItem(json_enemy, "skeleton")); + parse_enemy_template(goblin_template, cJSON_GetObjectItem(json_enemy, "goblin")); + parse_enemy_template(goblin_priest_template, cJSON_GetObjectItem(json_enemy, "goblin_priest")); + + cJSON_Delete(json_root); + return true; + } + +protected: + ConfigManager() = default; + ~ConfigManager() = default; + +private: + void parse_basic_template(BasicTemplate& tpl, cJSON* json_root) + { + if (!json_root || json_root->type != cJSON_Object) return; + + cJSON* json_window_title = cJSON_GetObjectItem(json_root, "window_title"); + cJSON* json_window_width = cJSON_GetObjectItem(json_root, "window_width"); + cJSON* json_window_height = cJSON_GetObjectItem(json_root, "window_height"); + + if (json_window_title && json_window_title->type == cJSON_String) + tpl.window_title = json_window_title->valuestring; + if (json_window_width && json_window_width->type == cJSON_Number) + tpl.window_width = json_window_width->valueint; + if (json_window_height && json_window_height->type == cJSON_Number) + tpl.window_height = json_window_height->valueint; + } + + void parse_player_template(PlayerTemplate& tpl, cJSON* json_root) + { + if (!json_root || json_root->type != cJSON_Object) return; + + cJSON* json_speed = cJSON_GetObjectItem(json_root, "speed"); + cJSON* json_normal_attack_interval = cJSON_GetObjectItem(json_root, "normal_attack_interval"); + cJSON* json_normal_attack_damage = cJSON_GetObjectItem(json_root, "normal_attack_damage"); + cJSON* json_skill_interval = cJSON_GetObjectItem(json_root, "skill_interval"); + cJSON* json_skill_damage = cJSON_GetObjectItem(json_root, "skill_damage"); + + if (json_speed && json_speed->type == cJSON_Number) + tpl.speed = json_speed->valuedouble; + if (json_normal_attack_interval && json_normal_attack_interval->type == cJSON_Number) + tpl.normal_attack_interval = json_normal_attack_interval->valuedouble; + if (json_normal_attack_damage && json_normal_attack_damage->type == cJSON_Number) + tpl.normal_attack_damage = json_normal_attack_damage->valuedouble; + if (json_skill_interval && json_skill_interval->type == cJSON_Number) + tpl.skill_interval = json_skill_interval->valuedouble; + if (json_skill_damage && json_skill_damage->type == cJSON_Number) + tpl.skill_damage = json_skill_damage->valuedouble; + } + + void parse_number_array(double* ary, int max_len, cJSON* json_root) + { + if (!json_root || json_root->type != cJSON_Array) + return; + + int idx = -1; + cJSON* json_element = nullptr; + cJSON_ArrayForEach(json_element, json_root) + { + idx++; + if (json_element->type != cJSON_Number || idx >= max_len) + continue; + + ary[idx] = json_element->valuedouble; + } + } + + void parse_tower_template(TowerTemplate& tpl, cJSON* json_root) + { + if (!json_root || json_root->type != cJSON_Object) return; + + cJSON* json_interval = cJSON_GetObjectItem(json_root, "interval"); + cJSON* json_damage = cJSON_GetObjectItem(json_root, "damage"); + cJSON* json_view_range = cJSON_GetObjectItem(json_root, "view_range"); + cJSON* json_cost = cJSON_GetObjectItem(json_root, "cost"); + cJSON* json_upgrade_cost = cJSON_GetObjectItem(json_root, "upgrade_cost"); + + parse_number_array(tpl.interval, 10, json_interval); + parse_number_array(tpl.damage, 10, json_damage); + parse_number_array(tpl.view_range, 10, json_view_range); + parse_number_array(tpl.cost, 10, json_cost); + parse_number_array(tpl.upgrade_cost, 9, json_upgrade_cost); + } + + void parse_enemy_template(EnemyTemplate& tpl, cJSON* json_root) + { + if (!json_root || json_root->type != cJSON_Object) return; + + cJSON* json_hp = cJSON_GetObjectItem(json_root, "hp"); + cJSON* json_speed = cJSON_GetObjectItem(json_root, "speed"); + cJSON* json_damage = cJSON_GetObjectItem(json_root, "damage"); + cJSON* json_reward_ratio = cJSON_GetObjectItem(json_root, "reward_ratio"); + cJSON* json_recover_interval = cJSON_GetObjectItem(json_root, "recover_interval"); + cJSON* json_recover_range = cJSON_GetObjectItem(json_root, "recover_range"); + cJSON* json_recover_intensity = cJSON_GetObjectItem(json_root, "recover_intensity"); + + if (json_hp && json_hp->type == cJSON_Number) + tpl.hp = json_hp->valuedouble; + if (json_speed && json_speed->type == cJSON_Number) + tpl.speed = json_speed->valuedouble; + if (json_damage && json_damage->type == cJSON_Number) + tpl.damage = json_damage->valuedouble; + if (json_reward_ratio && json_reward_ratio->type == cJSON_Number) + tpl.reward_ratio = json_reward_ratio->valuedouble; + if (json_recover_interval && json_recover_interval->type == cJSON_Number) + tpl.recover_interval = json_recover_interval->valuedouble; + if (json_recover_range && json_recover_range->type == cJSON_Number) + tpl.recover_range = json_recover_range->valuedouble; + if (json_recover_intensity && json_recover_intensity->type == cJSON_Number) + tpl.recover_intensity = json_recover_intensity->valuedouble; + } + +}; + + +#endif // !_CONFIG_MANAGER_H_ \ No newline at end of file diff --git a/games/TowerDefense/enemy.h b/games/TowerDefense/enemy.h new file mode 100644 index 00000000..1e76898c --- /dev/null +++ b/games/TowerDefense/enemy.h @@ -0,0 +1,269 @@ +#ifndef _ENEMY_H_ +#define _ENEMY_H_ + +#include "timer.h" +#include "route.h" +#include "vector2.h" +#include "animation.h" +#include "config_manager.h" + +#include + +class Enemy +{ +public: + typedef std::function SkillCallback; + +public: + Enemy() + { + timer_skill.set_one_shot(false); + timer_skill.set_on_timeout([&]() { on_skill_released(this); }); + + timer_sketch.set_one_shot(true); + timer_sketch.set_wait_time(0.075); + timer_sketch.set_on_timeout([&]() { is_show_sketch = false; }); + + timer_restore_speed.set_one_shot(true); + timer_restore_speed.set_on_timeout([&]() { speed = max_speed; }); + } + + ~Enemy() = default; + + void on_update(double delta) + { + timer_skill.on_update(delta); + timer_sketch.on_update(delta); + timer_restore_speed.on_update(delta); + + Vector2 move_distance = velocity * delta; + Vector2 target_distance = position_target - position; + position += move_distance < target_distance ? move_distance : target_distance; + + if (target_distance.approx_zero()) + { + idx_target++; + refresh_position_target(); + + direction = (position_target - position).normalize(); + } + + velocity.x = direction.x * speed * SIZE_TILE; + velocity.y = direction.y * speed * SIZE_TILE; + + bool is_show_x_amin = abs(velocity.x) >= abs(velocity.y); + + if (is_show_sketch) + { + if (is_show_x_amin) + anim_current = velocity.x > 0 ? &anim_right_sketch : &anim_left_sketch; + else + anim_current = velocity.y > 0 ? &anim_down_sketch : &anim_up_sketch; + } + else + { + if (is_show_x_amin) + anim_current = velocity.x > 0 ? &anim_right : &anim_left; + else + anim_current = velocity.y > 0 ? &anim_down : &anim_up; + } + + anim_current->on_update(delta); + } + + void on_render(SDL_Renderer* renderer) + { + static SDL_Rect rect; + static SDL_Point point; + static const int offset_y = 2; + static const Vector2 size_hp_bar = { 40, 8 }; + static const SDL_Color color_border = { 116, 185, 124, 255 }; + static const SDL_Color color_content = { 226, 255, 194, 255 }; + + point.x = (int)(position.x - size.x / 2); + point.y = (int)(position.y - size.y / 2); + + anim_current->on_render(renderer, point); + + if (hp < max_hp) + { + rect.x = (int)(position.x - size_hp_bar.x / 2); + rect.y = (int)(position.y - size.y / 2 - size_hp_bar.y - offset_y); + rect.w = (int)(size_hp_bar.x * (hp / max_hp)); + rect.h = (int)size_hp_bar.y; + SDL_SetRenderDrawColor(renderer, color_content.r, color_content.g, color_content.b, color_content.a); + SDL_RenderFillRect(renderer, &rect); + + rect.w = (int)size_hp_bar.x; + SDL_SetRenderDrawColor(renderer, color_border.r, color_border.g, color_border.b, color_border.a); + SDL_RenderDrawRect(renderer, &rect); + } + } + + void set_on_skill_released(SkillCallback on_skill_released) + { + this->on_skill_released = on_skill_released; + } + + void increase_hp(double val) + { + hp += val; + + if (hp > max_hp) + hp = max_hp; + } + + void decrease_hp(double val) + { + hp -= val; + + if (hp <= 0) + { + hp = 0; + is_valid = false; + } + + is_show_sketch = true; + timer_sketch.restart(); + } + + void slow_down() + { + speed = max_speed - 0.5; + timer_restore_speed.set_wait_time(1); + timer_restore_speed.restart(); + } + + void set_position(const Vector2& position) + { + this->position = position; + } + + void set_route(const Route* route) + { + this->route = route; + + refresh_position_target(); + } + + void make_invalid() + { + is_valid = false; + } + + double get_hp() const + { + return hp; + } + + const Vector2& get_size() const + { + return size; + } + + const Vector2& get_position() const + { + return position; + } + + const Vector2& get_velocity() const + { + return velocity; + } + + double get_damage() const + { + return damage; + } + + double get_reward_ratio() const + { + return reward_ratio; + } + + double get_recover_radius() const + { + return SIZE_TILE * recover_range; + } + + double get_recover_intensity() const + { + return recover_intensity; + } + + bool can_remove() const + { + return !is_valid; + } + + double get_route_process() const + { + if (route->get_idx_list().size() == 1) + return 1; + + return (double)idx_target / (route->get_idx_list().size() - 1); + } + +protected: + Vector2 size; + + Timer timer_skill; + + Animation anim_up; + Animation anim_down; + Animation anim_left; + Animation anim_right; + Animation anim_up_sketch; + Animation anim_down_sketch; + Animation anim_left_sketch; + Animation anim_right_sketch; + + double hp = 0; + double max_hp = 0; + double speed = 0; + double max_speed = 0; + double damage = 0; + double reward_ratio = 0; + double recover_interval = 0; + double recover_range = 0; + double recover_intensity = 0; + +private: + Vector2 position; + Vector2 velocity; + Vector2 direction; + + bool is_valid = true; + + Timer timer_sketch; + bool is_show_sketch = false; + + Animation* anim_current = nullptr; + + SkillCallback on_skill_released; + + Timer timer_restore_speed; + + const Route* route = nullptr; + int idx_target = 0; + Vector2 position_target; + +private: + void refresh_position_target() + { + const Route::IdxList& idx_list = route->get_idx_list(); + + if (idx_target < idx_list.size()) + { + const SDL_Point& point = idx_list[idx_target]; + static const SDL_Rect& rect_tile_map = ConfigManager::instance()->rect_tile_map; + + position_target.x = rect_tile_map.x + point.x * SIZE_TILE + SIZE_TILE / 2; + position_target.y = rect_tile_map.y + point.y * SIZE_TILE + SIZE_TILE / 2; + } + } + +}; + +#endif // !_ENEMY_H_ + diff --git a/games/TowerDefense/enemy_manager.h b/games/TowerDefense/enemy_manager.h new file mode 100644 index 00000000..1f5cf39b --- /dev/null +++ b/games/TowerDefense/enemy_manager.h @@ -0,0 +1,228 @@ +#ifndef _ENEMY_MANAGER_H_ +#define _ENEMY_MANAGER_H_ + +#include "enemy.h" +#include "manager.h" +#include "config_manager.h" +#include "home_manager.h" +#include "slim_enemy.h" +#include "king_slim_enemy.h" +#include "skeleton_enemy.h" +#include "goblin_enemy.h" +#include "goblin_priest_enemy.h" +#include "bullet_manager.h" +#include "coin_manager.h" + +#include +#include + +class EnemyManager : public Manager +{ + friend class Manager; + +public: + typedef std::vector EnemyList; + +public: + void on_update(double delta) + { + for (Enemy* enemy : enemy_list) + enemy->on_update(delta); + + process_home_collision(); + process_bullet_collision(); + + remove_invalid_enemy(); + } + + void on_render(SDL_Renderer* renderer) + { + for (Enemy* enemy : enemy_list) + enemy->on_render(renderer); + } + + void spawn_enemy(EnemyType type, int idx_spawn_point) + { + static Vector2 position; + static const SDL_Rect& rect_tile_map = ConfigManager::instance()->rect_tile_map; + static const Map::SpawnerRoutePool& spawner_route_pool + = ConfigManager::instance()->map.get_idx_spawner_pool(); + + const auto& itor = spawner_route_pool.find(idx_spawn_point); + if (itor == spawner_route_pool.end()) + return; + + Enemy* enemy = nullptr; + + switch (type) + { + case EnemyType::Slim: + enemy = new SlimEnemy(); + break; + case EnemyType::KingSlim: + enemy = new KingSlimeEnemy(); + break; + case EnemyType::Skeleton: + enemy = new SkeletonEnemy(); + break; + case EnemyType::Goblin: + enemy = new GoblinEnemy(); + break; + case EnemyType::GoblinPriest: + enemy = new GoblinPriestEnemy(); + break; + default: + enemy = new SlimEnemy(); + break; + } + + enemy->set_on_skill_released( + [&](Enemy* enemy_src) + { + double recover_raduis = enemy_src->get_recover_radius(); + if (recover_raduis < 0) return; + + const Vector2 pos_src = enemy_src->get_position(); + for (Enemy* enemy_dst : enemy_list) + { + const Vector2& pos_dst = enemy_dst->get_position(); + double distance = (pos_dst - pos_src).length(); + if (distance <= recover_raduis) + enemy_dst->increase_hp(enemy_src->get_recover_intensity()); + } + }); + + const Route::IdxList& idx_list = itor->second.get_idx_list(); + position.x = rect_tile_map.x + idx_list[0].x * SIZE_TILE + SIZE_TILE / 2; + position.y = rect_tile_map.y + idx_list[0].y * SIZE_TILE + SIZE_TILE / 2; + + enemy->set_position(position); + enemy->set_route(&itor->second); + + enemy_list.push_back(enemy); + } + + bool check_cleared() + { + return enemy_list.empty(); + } + + EnemyManager::EnemyList& get_enemy_list() + { + return enemy_list; + } + +protected: + EnemyManager() = default; + + ~EnemyManager() + { + for (Enemy* enemy : enemy_list) + delete enemy; + } + +private: + EnemyList enemy_list; + +private: + void process_home_collision() + { + static const SDL_Point& idx_home = ConfigManager::instance()->map.get_idx_home(); + static const SDL_Rect& rect_tile_map = ConfigManager::instance()->rect_tile_map; + static const Vector2 position_home_tile = + { + (double)rect_tile_map.x + idx_home.x * SIZE_TILE, + (double)rect_tile_map.y + idx_home.y * SIZE_TILE + }; + + for (Enemy* enemy : enemy_list) + { + if (enemy->can_remove()) continue; + + const Vector2& position = enemy->get_position(); + + if (position.x >= position_home_tile.x + && position.y >= position_home_tile.y + && position.x <= position_home_tile.x + SIZE_TILE + && position.y <= position_home_tile.y + SIZE_TILE) + { + enemy->make_invalid(); + + HomeManager::instance()->decrease_hp(enemy->get_damage()); + } + } + } + + void process_bullet_collision() + { + static BulletManager::BulletList& bullet_list + = BulletManager::instance()->get_bullet_list(); + + for (Enemy* enemy : enemy_list) + { + if (enemy->can_remove()) continue; + + const Vector2& size_enemy = enemy->get_size(); + const Vector2& pos_enemy = enemy->get_position(); + + for (Bullet* bullet : bullet_list) + { + if (!bullet->can_collide()) continue; + + const Vector2& pos_bullet = bullet->get_position(); + + if (pos_bullet.x >= pos_enemy.x - size_enemy.x / 2 + && pos_bullet.y >= pos_enemy.y - size_enemy.y / 2 + && pos_bullet.x <= pos_enemy.x + size_enemy.x / 2 + && pos_bullet.y <= pos_enemy.y + size_enemy.y / 2) + { + double damage = bullet->get_damage(); + double damage_range = bullet->get_damage_range(); + if (damage_range < 0) + { + enemy->decrease_hp(damage); + if (enemy->can_remove()) + try_spawn_coin_prop(pos_enemy, enemy->get_reward_ratio()); + } + else + { + for (Enemy* target_enemy : enemy_list) + { + const Vector2& pos_target_enemy = target_enemy->get_position(); + if ((pos_target_enemy - pos_bullet).length() <= damage_range) + { + target_enemy->decrease_hp(damage); + if (target_enemy->can_remove()) + try_spawn_coin_prop(pos_target_enemy, enemy->get_reward_ratio()); + } + } + } + + bullet->on_collide(enemy); + } + } + } + } + + void remove_invalid_enemy() + { + enemy_list.erase(std::remove_if(enemy_list.begin(), enemy_list.end(), + [](const Enemy* enemy) + { + bool deletable = enemy->can_remove(); + if (deletable) delete enemy; + return deletable; + }), enemy_list.end()); + } + + void try_spawn_coin_prop(const Vector2& position, double ratio) + { + static CoinManager* instance = CoinManager::instance(); + + if ((double)(rand() % 100) / 100 <= ratio) + instance->spawn_coin_prop(position); + } + +}; + +#endif // !_ENEMY_MANAGER_H_ diff --git a/games/TowerDefense/enemy_type.h b/games/TowerDefense/enemy_type.h new file mode 100644 index 00000000..fbdbbf94 --- /dev/null +++ b/games/TowerDefense/enemy_type.h @@ -0,0 +1,17 @@ +#ifndef _ENEMY_TYPE_H_ +#define _ENEMY_TYPE_H_ + +enum class EnemyType +{ + Slim, + KingSlim, + Skeleton, + Goblin, + GoblinPriest +}; + + + + +#endif // !_ENEMY_H_ + diff --git a/games/TowerDefense/facing.h b/games/TowerDefense/facing.h new file mode 100644 index 00000000..123ac5e0 --- /dev/null +++ b/games/TowerDefense/facing.h @@ -0,0 +1,14 @@ +#ifndef _FACE_H_ +#define _FACE_H_ + + +enum Facing + +{ + Up, + Down, + Left, + Right +}; +#endif // !_FACE_H_ + diff --git a/games/TowerDefense/game_manager.h b/games/TowerDefense/game_manager.h new file mode 100644 index 00000000..d605d722 --- /dev/null +++ b/games/TowerDefense/game_manager.h @@ -0,0 +1,359 @@ +#ifndef _GAME_MANAGER_H_ +#define _GAME_MANAGER_H_ + +#include "banner.h" +#include "manager.h" +#include "config_manager.h" +#include "enemy_manager.h" +#include "wave_manager.h" +#include "resources_manager.h" +#include "tower_manager.h" +#include "bullet_manager.h" +#include "status_bar.h" +#include "panel.h" +#include "place_panel.h" +#include "upgrade_panel.h" +#include "player_manager.h" + +#include +#include +#include +#include + +class GameManager : public Manager +{ + friend class Manager; + +public: + int run(int argc, char** argv) + { + Mix_FadeInMusic(ResourcesManager::instance()->get_music_pool().find(ResID::Music_BGM)->second, -1, 1500); + + Uint64 last_counter = SDL_GetPerformanceCounter(); + const Uint64 counter_freq = SDL_GetPerformanceFrequency(); + + while (!is_quit) + { + while (SDL_PollEvent(&event)) + on_input(); + + Uint64 current_counter = SDL_GetPerformanceCounter(); + double delta = (double)(current_counter - last_counter) / counter_freq; + last_counter = current_counter; + if (delta * 1000 < 1000.0 / 60) + SDL_Delay((Uint32)(1000.0 / 60 - delta * 1000)); + + on_update(delta); + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + on_render(); + + SDL_RenderPresent(renderer); + } + + return 0; + } + +protected: + GameManager() + { + init_assert(!SDL_Init(SDL_INIT_EVERYTHING), u8"SDL2 ?????????"); + init_assert(IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG), u8"SDL_imgae ?????????"); + init_assert(Mix_Init(MIX_INIT_MP3), u8"SDL_mixer ?????????"); + init_assert(!TTF_Init(), u8"SDL_ttf ?????????"); + + Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048); + + SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); + + ConfigManager* config = ConfigManager::instance(); + + init_assert(config->map.load("map.csv"), u8"??????????????"); + init_assert(config->load_level_config("level.json"), u8"??????????????"); + init_assert(config->load_game_config("config.json"), u8"???????????????"); + + window = SDL_CreateWindow(config->basic_template.window_title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + config->basic_template.window_width, config->basic_template.window_height, SDL_WINDOW_SHOWN); + init_assert(window, u8"???????????????"); + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE); + init_assert(renderer, u8"?????????????"); + + init_assert(ResourcesManager::instance()->load_from_file(renderer), u8"??????????????"); + + init_assert(generate_tile_map_texture(), u8"??????????????????"); + + status_bar.set_position(15, 15); + + banner = new Banner(); + place_panel = new PlacePanel(); + upgrade_panel = new UpgradePanel(); + } + + ~GameManager() + { + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + + TTF_Quit(); + Mix_Quit(); + IMG_Quit(); + SDL_Quit(); + } + +private: + SDL_Event event; + bool is_quit = false; + + StatusBar status_bar; + + SDL_Window* window = nullptr; + SDL_Renderer* renderer = nullptr; + + SDL_Texture* tex_tile_map = nullptr; + + Panel* place_panel = nullptr; + Panel* upgrade_panel = nullptr; + Banner* banner = nullptr; + +private: + void init_assert(bool flag, const char* err_msg) + { + if (flag) return; + + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, u8"??????????", err_msg, window); + exit(-1); + } + + void on_input() + { + static SDL_Point pos_center; + static SDL_Point idx_tile_selected; + static ConfigManager* instance = ConfigManager::instance(); + + switch (event.type) + { + case SDL_QUIT: + is_quit = true; + break; + case SDL_MOUSEBUTTONDOWN: + if (instance->is_game_over) + break; + if (get_cursor_idx_tile(idx_tile_selected, event.motion.x, event.motion.y)) + { + get_selected_tile_center_pos(pos_center, idx_tile_selected); + + if (check_home(idx_tile_selected)) + { + upgrade_panel->set_idx_tile(idx_tile_selected); + upgrade_panel->set_center_pos(pos_center); + upgrade_panel->show(); + } + else if (can_place_tower(idx_tile_selected)) + { + place_panel->set_idx_tile(idx_tile_selected); + place_panel->set_center_pos(pos_center); + place_panel->show(); + } + } + break; + default: + break; + } + + if (!instance->is_game_over) + { + place_panel->on_input(event); + upgrade_panel->on_input(event); + PlayerManager::instance()->on_input(event); + } + } + + void on_update(double delta) + { + static bool is_game_over_last_tick = false; + static ConfigManager* instance = ConfigManager::instance(); + + if (HomeManager::instance()->get_current_hp_num() <= 0) + { + instance->is_game_win = false; + instance->is_game_over = true; + } + + if (!instance->is_game_over) + { + status_bar.on_update(renderer); + place_panel->on_update(renderer); + upgrade_panel->on_update(renderer); + WaveManager::instance()->on_update(delta); + EnemyManager::instance()->on_update(delta); + CoinManager::instance()->on_update(delta); + BulletManager::instance()->on_update(delta); + TowerManager::instance()->on_update(delta); + PlayerManager::instance()->on_update(delta); + + return; + } + + if (!is_game_over_last_tick && instance->is_game_over) + { + static const ResourcesManager::SoundPool& sound_pool + = ResourcesManager::instance()->get_sound_pool(); + + Mix_FadeOutMusic(1500); + Mix_PlayChannel(-1, sound_pool.find(instance->is_game_win ? ResID::Sound_Win : ResID::Sound_Loss)->second, 0); + } + + is_game_over_last_tick = instance->is_game_over; + + banner->on_update(delta); + if (banner->check_end_dispaly()) + is_quit = true; + } + + void on_render() + { + static ConfigManager* instance = ConfigManager::instance(); + static SDL_Rect& rect_dst = instance->rect_tile_map; + SDL_RenderCopy(renderer, tex_tile_map, nullptr, &rect_dst); + + EnemyManager::instance()->on_render(renderer); + CoinManager::instance()->on_render(renderer); + BulletManager::instance()->on_render(renderer); + TowerManager::instance()->on_render(renderer); + PlayerManager::instance()->on_render(renderer); + + if (!instance->is_game_over) + { + place_panel->on_render(renderer); + upgrade_panel->on_render(renderer); + status_bar.on_render(renderer); + + return; + } + + int width_screen, height_screen; + SDL_GetWindowSizeInPixels(window, &width_screen, &height_screen); + banner->set_center_position({ (double)width_screen / 2, (double)height_screen / 2 }); + banner->on_render(renderer); + } + + bool generate_tile_map_texture() + { + const Map& map = ConfigManager::instance()->map; + const TileMap& tile_map = map.get_tile_map(); + SDL_Rect& rect_tile_map = ConfigManager::instance()->rect_tile_map; + SDL_Texture* tex_tile_set = ResourcesManager::instance()->get_texture_pool().find(ResID::Tex_Tileset)->second; + + int width_tex_tile_set, height_tex_tile_set; + SDL_QueryTexture(tex_tile_set, nullptr, nullptr, &width_tex_tile_set, &height_tex_tile_set); + int num_tile_single_line = (int)std::ceil((double)width_tex_tile_set / SIZE_TILE); + + int width_tex_tile_map, height_tex_tile_map; + width_tex_tile_map = (int)map.get_width() * SIZE_TILE; + height_tex_tile_map = (int)map.get_height() * SIZE_TILE; + tex_tile_map = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_TARGET, width_tex_tile_map, height_tex_tile_map); + if (!tex_tile_map) return false; + + ConfigManager* config = ConfigManager::instance(); + rect_tile_map.x = (config->basic_template.window_width - width_tex_tile_map) / 2; + rect_tile_map.y = (config->basic_template.window_height - height_tex_tile_map) / 2; + rect_tile_map.w = width_tex_tile_map; + rect_tile_map.h = height_tex_tile_map; + + SDL_SetTextureBlendMode(tex_tile_map, SDL_BLENDMODE_BLEND); + SDL_SetRenderTarget(renderer, tex_tile_map); + + for (int y = 0; y < map.get_height(); y++) + { + for (int x = 0; x < map.get_width(); x++) + { + SDL_Rect rect_src; + const Tile& tile = tile_map[y][x]; + + const SDL_Rect& rect_dst = + { + x * SIZE_TILE, y * SIZE_TILE, + SIZE_TILE, SIZE_TILE + }; + + rect_src = + { + (tile.terrian % num_tile_single_line) * SIZE_TILE, + (tile.terrian / num_tile_single_line) * SIZE_TILE, + SIZE_TILE, SIZE_TILE + }; + SDL_RenderCopy(renderer, tex_tile_set, &rect_src, &rect_dst); + + if (tile.decoration >= 0) + { + rect_src = + { + (tile.decoration % num_tile_single_line) * SIZE_TILE, + (tile.decoration / num_tile_single_line) * SIZE_TILE, + SIZE_TILE, SIZE_TILE + }; + SDL_RenderCopy(renderer, tex_tile_set, &rect_src, &rect_dst); + } + } + } + + const SDL_Point& idx_home = map.get_idx_home(); + const SDL_Rect rect_dst = + { + idx_home.x * SIZE_TILE, idx_home.y * SIZE_TILE, + SIZE_TILE, SIZE_TILE + }; + SDL_RenderCopy(renderer, ResourcesManager::instance()->get_texture_pool().find(ResID::Tex_Home)->second, nullptr, &rect_dst); + + SDL_SetRenderTarget(renderer, nullptr); + + return true; + } + + bool check_home(const SDL_Point& idx_tile_selected) + { + static const Map& map = ConfigManager::instance()->map; + static const SDL_Point& idx_home = map.get_idx_home(); + + return (idx_home.x == idx_tile_selected.x && idx_home.y == idx_tile_selected.y); + } + + bool get_cursor_idx_tile(SDL_Point& idx_tile_selected, int screen_x, int screen_y) const + { + static const Map& map = ConfigManager::instance()->map; + static const SDL_Rect& rect_tile_map = ConfigManager::instance()->rect_tile_map; + + if (screen_x < rect_tile_map.x || screen_x > rect_tile_map.x + rect_tile_map.w + || screen_y < rect_tile_map.y || screen_y > rect_tile_map.x + rect_tile_map.h) + return false; + + idx_tile_selected.x = std::min((screen_x - rect_tile_map.x) / SIZE_TILE, (int)map.get_width() - 1); + idx_tile_selected.y = std::min((screen_y - rect_tile_map.y) / SIZE_TILE, (int)map.get_height() - 1); + + return true; + } + + bool can_place_tower(const SDL_Point& idx_tile_selected) const + { + static const Map& map = ConfigManager::instance()->map; + const Tile& tile = map.get_tile_map()[idx_tile_selected.y][idx_tile_selected.x]; + + return (tile.decoration < 0 && tile.direction == Tile::Direction::None && !tile.has_tower); + } + + void get_selected_tile_center_pos(SDL_Point& pos, const SDL_Point& idx_tile_selected) const + { + static const SDL_Rect& rect_tile_map = ConfigManager::instance()->rect_tile_map; + + pos.x = rect_tile_map.x + idx_tile_selected.x * SIZE_TILE + SIZE_TILE / 2; + pos.y = rect_tile_map.y + idx_tile_selected.y * SIZE_TILE + SIZE_TILE / 2; + } + +}; + + +#endif // !_GAME_MANAGER_H_ \ No newline at end of file diff --git a/games/TowerDefense/goblin_enemy.h b/games/TowerDefense/goblin_enemy.h new file mode 100644 index 00000000..f02e6858 --- /dev/null +++ b/games/TowerDefense/goblin_enemy.h @@ -0,0 +1,58 @@ +#ifndef _GOBLIN_ENEMY_H_ +#define _GOBLIN_ENEMY_H_ + +#include "enemy.h" +#include "config_manager.h" +#include "resources_manager.h" + +class GoblinEnemy : public Enemy +{ +public: + GoblinEnemy() + { + static const ResourcesManager::TexturePool& texture_pool + = ResourcesManager::instance()->get_texture_pool(); + static SDL_Texture* tex_goblin = texture_pool.find(ResID::Tex_Goblin)->second; + static SDL_Texture* tex_goblin_sketch = texture_pool.find(ResID::Tex_GoblinSketch)->second; + static ConfigManager::EnemyTemplate& goblin_template = ConfigManager::instance()->goblin_template; + + static const std::vector idx_list_up = { 5, 6, 7, 8, 9 }; + static const std::vector idx_list_down = { 0, 1, 2, 3, 4 }; + static const std::vector idx_list_left = { 15, 16, 17, 18, 19 }; + static const std::vector idx_list_right = { 10, 11, 12, 13, 14 }; + + anim_up.set_loop(true); anim_up.set_interval(0.15); + anim_up.set_frame_data(tex_goblin, 5, 4, idx_list_up); + anim_down.set_loop(true); anim_down.set_interval(0.15); + anim_down.set_frame_data(tex_goblin, 5, 4, idx_list_down); + anim_left.set_loop(true); anim_left.set_interval(0.15); + anim_left.set_frame_data(tex_goblin, 5, 4, idx_list_left); + anim_right.set_loop(true); anim_right.set_interval(0.15); + anim_right.set_frame_data(tex_goblin, 5, 4, idx_list_right); + + anim_up_sketch.set_loop(true); anim_up_sketch.set_interval(0.15); + anim_up_sketch.set_frame_data(tex_goblin_sketch, 5, 4, idx_list_up); + anim_down_sketch.set_loop(true); anim_down_sketch.set_interval(0.15); + anim_down_sketch.set_frame_data(tex_goblin_sketch, 5, 4, idx_list_down); + anim_left_sketch.set_loop(true); anim_left_sketch.set_interval(0.15); + anim_left_sketch.set_frame_data(tex_goblin_sketch, 5, 4, idx_list_left); + anim_right_sketch.set_loop(true); anim_right_sketch.set_interval(0.15); + anim_right_sketch.set_frame_data(tex_goblin_sketch, 5, 4, idx_list_right); + + max_hp = goblin_template.hp; + max_speed = goblin_template.speed; + damage = goblin_template.damage; + reward_ratio = goblin_template.reward_ratio; + recover_interval = goblin_template.recover_interval; + recover_range = goblin_template.recover_range; + recover_intensity = goblin_template.recover_intensity; + + size.x = 48, size.y = 48; + hp = max_hp, speed = max_speed; + } + + ~GoblinEnemy() = default; + +}; + +#endif // !_GOBLIN_ENEMY_H_ diff --git a/games/TowerDefense/goblin_priest_enemy.h b/games/TowerDefense/goblin_priest_enemy.h new file mode 100644 index 00000000..c8f30fb0 --- /dev/null +++ b/games/TowerDefense/goblin_priest_enemy.h @@ -0,0 +1,61 @@ +#ifndef _GOBLIN_PRIEST_ENEMY_H_ +#define _GOBLIN_PRIEST_ENEMY_H_ + +#include "enemy.h" +#include "config_manager.h" +#include "resources_manager.h" + +class GoblinPriestEnemy : public Enemy +{ +public: + GoblinPriestEnemy() + { + static const ResourcesManager::TexturePool& texture_pool + = ResourcesManager::instance()->get_texture_pool(); + static SDL_Texture* tex_goblin_priest = texture_pool.find(ResID::Tex_GoblinPriest)->second; + static SDL_Texture* tex_goblin_priest_sketch = texture_pool.find(ResID::Tex_GoblinPriestSketch)->second; + static ConfigManager::EnemyTemplate& goblin_priest_template = ConfigManager::instance()->goblin_priest_template; + + static const std::vector idx_list_up = { 5, 6, 7, 8, 9 }; + static const std::vector idx_list_down = { 0, 1, 2, 3, 4 }; + static const std::vector idx_list_left = { 15, 16, 17, 18, 19 }; + static const std::vector idx_list_right = { 10, 11, 12, 13, 14 }; + + anim_up.set_loop(true); anim_up.set_interval(0.15); + anim_up.set_frame_data(tex_goblin_priest, 5, 4, idx_list_up); + anim_down.set_loop(true); anim_down.set_interval(0.15); + anim_down.set_frame_data(tex_goblin_priest, 5, 4, idx_list_down); + anim_left.set_loop(true); anim_left.set_interval(0.15); + anim_left.set_frame_data(tex_goblin_priest, 5, 4, idx_list_left); + anim_right.set_loop(true); anim_right.set_interval(0.15); + anim_right.set_frame_data(tex_goblin_priest, 5, 4, idx_list_right); + + anim_up_sketch.set_loop(true); anim_up_sketch.set_interval(0.15); + anim_up_sketch.set_frame_data(tex_goblin_priest_sketch, 5, 4, idx_list_up); + anim_down_sketch.set_loop(true); anim_down_sketch.set_interval(0.15); + anim_down_sketch.set_frame_data(tex_goblin_priest_sketch, 5, 4, idx_list_down); + anim_left_sketch.set_loop(true); anim_left_sketch.set_interval(0.15); + anim_left_sketch.set_frame_data(tex_goblin_priest_sketch, 5, 4, idx_list_left); + anim_right_sketch.set_loop(true); anim_right_sketch.set_interval(0.15); + anim_right_sketch.set_frame_data(tex_goblin_priest_sketch, 5, 4, idx_list_right); + + max_hp = goblin_priest_template.hp; + max_speed = goblin_priest_template.speed; + damage = goblin_priest_template.damage; + reward_ratio = goblin_priest_template.reward_ratio; + recover_interval = goblin_priest_template.recover_interval; + recover_range = goblin_priest_template.recover_range; + recover_intensity = goblin_priest_template.recover_intensity; + + size.x = 48, size.y = 48; + hp = max_hp, speed = max_speed; + + timer_skill.set_wait_time(recover_interval); + } + + ~GoblinPriestEnemy() = default; + +}; + +#endif // !_GOBLIN_PRIEST_ENEMY_H_ + diff --git a/games/TowerDefense/gunner_tower.h b/games/TowerDefense/gunner_tower.h new file mode 100644 index 00000000..b81dadab --- /dev/null +++ b/games/TowerDefense/gunner_tower.h @@ -0,0 +1,47 @@ +#ifndef _GUNNER_TOWER_H_ +#define _GUNNER_TOWER_H_ + +#include "tower.h" +#include "resources_manager.h" + +class GunnerTower : public Tower +{ +public: + GunnerTower() + { + static SDL_Texture* tex_gunner = ResourcesManager::instance() + ->get_texture_pool().find(ResID::Tex_Gunner)->second; + + static const std::vector idx_list_idle_up = { 4, 5 }; + static const std::vector idx_list_idle_down = { 0, 1 }; + static const std::vector idx_list_idle_left = { 12, 13 }; + static const std::vector idx_list_idle_right = { 8, 9 }; + static const std::vector idx_list_fire_up = { 20, 21, 22, 23 }; + static const std::vector idx_list_fire_down = { 16, 17, 18, 19 }; + static const std::vector idx_list_fire_left = { 28, 29, 30, 31 }; + static const std::vector idx_list_fire_right = { 24, 25, 26, 27 }; + + anim_idle_up.set_frame_data(tex_gunner, 4, 8, idx_list_idle_up); + anim_idle_down.set_frame_data(tex_gunner, 4, 8, idx_list_idle_down); + anim_idle_left.set_frame_data(tex_gunner, 4, 8, idx_list_idle_left); + anim_idle_right.set_frame_data(tex_gunner, 4, 8, idx_list_idle_right); + anim_fire_up.set_frame_data(tex_gunner, 4, 8, idx_list_fire_up); + anim_fire_down.set_frame_data(tex_gunner, 4, 8, idx_list_fire_down); + anim_fire_left.set_frame_data(tex_gunner, 4, 8, idx_list_fire_left); + anim_fire_right.set_frame_data(tex_gunner, 4, 8, idx_list_fire_right); + + size.x = 48, size.y = 48; + + tower_type = TowerType::Gunner; + + fire_speed = 6; + bullet_type = BulletType::Shell; + } + + ~GunnerTower() = default; + +}; + + +#endif // !_GUNNER_TOWER_H_ + diff --git a/games/TowerDefense/home_manager.h b/games/TowerDefense/home_manager.h new file mode 100644 index 00000000..7b29df33 --- /dev/null +++ b/games/TowerDefense/home_manager.h @@ -0,0 +1,57 @@ +#ifndef _HOME_MANAGER_H_ +#define _HOME_MANAGER_H_ + +#include "Manager.h" +#include "config_manager.h" +#include "resources_manager.h" + +class HomeManager : public Manager +{ + friend class Manager; + + + +public: + double get_current_hp_num() + { + return num_hp; + } + + void decrease_hp(double val) + { + num_hp -= val; + + if (num_hp < 0) + num_hp = 0; + + static const::ResourcesManager::SoundPool& sound_pool + = ResourcesManager::instance()->get_sound_pool(); + + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_HomeHurt)->second, 0); + } + +protected: + HomeManager() + { + num_hp = ConfigManager::instance()->num_initial_hp; + } + + ~HomeManager() = default; + + +private: + double num_hp = 0; +}; + + + + + + + + + + + +#endif // !_HOME_MANAGER_H_ + diff --git a/games/TowerDefense/king_slim_enemy.h b/games/TowerDefense/king_slim_enemy.h new file mode 100644 index 00000000..6c246bc5 --- /dev/null +++ b/games/TowerDefense/king_slim_enemy.h @@ -0,0 +1,59 @@ +#ifndef _KINNG_SLIM_ENEMY_H_ +#define _KINNG_SLIM_ENEMY_H_ + +#include "enemy.h" +#include "config_manager.h" +#include "resources_manager.h" + +class KingSlimeEnemy : public Enemy +{ +public: + KingSlimeEnemy() + { + static const ResourcesManager::TexturePool& texture_pool + = ResourcesManager::instance()->get_texture_pool(); + static SDL_Texture* tex_king_slime = texture_pool.find(ResID::Tex_KingSlime)->second; + static SDL_Texture* tex_king_slime_sketch = texture_pool.find(ResID::Tex_KingSlimeSketch)->second; + static ConfigManager::EnemyTemplate& king_slim_template = ConfigManager::instance()->king_slim_template; + + static const std::vector idx_list_up = { 18, 19, 20, 21, 22, 23 }; + static const std::vector idx_list_down = { 0, 1, 2, 3, 4, 5 }; + static const std::vector idx_list_left = { 6, 7, 8, 9, 10, 11 }; + static const std::vector idx_list_right = { 12, 13, 14, 15, 16, 17 }; + + anim_up.set_loop(true); anim_up.set_interval(0.1); + anim_up.set_frame_data(tex_king_slime, 6, 4, idx_list_up); + anim_down.set_loop(true); anim_down.set_interval(0.1); + anim_down.set_frame_data(tex_king_slime, 6, 4, idx_list_down); + anim_left.set_loop(true); anim_left.set_interval(0.1); + anim_left.set_frame_data(tex_king_slime, 6, 4, idx_list_left); + anim_right.set_loop(true); anim_right.set_interval(0.1); + anim_right.set_frame_data(tex_king_slime, 6, 4, idx_list_right); + + anim_up_sketch.set_loop(true); anim_up_sketch.set_interval(0.1); + anim_up_sketch.set_frame_data(tex_king_slime_sketch, 6, 4, idx_list_up); + anim_down_sketch.set_loop(true); anim_down_sketch.set_interval(0.1); + anim_down_sketch.set_frame_data(tex_king_slime_sketch, 6, 4, idx_list_down); + anim_left_sketch.set_loop(true); anim_left_sketch.set_interval(0.1); + anim_left_sketch.set_frame_data(tex_king_slime_sketch, 6, 4, idx_list_left); + anim_right_sketch.set_loop(true); anim_right_sketch.set_interval(0.1); + anim_right_sketch.set_frame_data(tex_king_slime_sketch, 6, 4, idx_list_right); + + max_hp = king_slim_template.hp; + max_speed = king_slim_template.speed; + damage = king_slim_template.damage; + reward_ratio = king_slim_template.reward_ratio; + recover_interval = king_slim_template.recover_interval; + recover_range = king_slim_template.recover_range; + recover_intensity = king_slim_template.recover_intensity; + + size.x = 48, size.y = 48; + hp = max_hp, speed = max_speed; + } + + ~KingSlimeEnemy() = default; + +}; + + +#endif // !_KINNG_SLIM_ENEMY_H_ \ No newline at end of file diff --git a/games/TowerDefense/level.json b/games/TowerDefense/level.json new file mode 100644 index 00000000..fe2be1e1 --- /dev/null +++ b/games/TowerDefense/level.json @@ -0,0 +1,35 @@ +[{ + "interval": 1, + "rewards": 100, + "spawn_list": [{ + "interval": 1, + "point": 1, + "enemy": "Slim" + }, { + "interval": 1, + "point": 2, + "enemy": "KingSlim" + }, { + "interval": 1, + "point": 1, + "enemy": "Goblin" + }, { + "interval": 1, + "point": 2, + "enemy": "GoblinPriest" + }, { + "interval": 1, + "point": 1, + "enemy": "Skeleton" + }] + }, { + "interval": 3, + "rewards": 100, + "spawn_list": [{ + "interval": 3, + "point": 2, + "enemy": "KingSlim" + }] + }] + + \ No newline at end of file diff --git a/games/TowerDefense/main.cpp b/games/TowerDefense/main.cpp new file mode 100644 index 00000000..4a43eb50 --- /dev/null +++ b/games/TowerDefense/main.cpp @@ -0,0 +1,8 @@ +//#define SDL_MAIN_HANDLED + +#include "game_manager.h" + + +int main(int argc, char** argv) { + return GameManager::instance()->run(argc,argv); +} \ No newline at end of file diff --git a/games/TowerDefense/map.csv b/games/TowerDefense/map.csv new file mode 100644 index 00000000..c65bf570 --- /dev/null +++ b/games/TowerDefense/map.csv @@ -0,0 +1,15 @@ +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,3\-1\4\1,3\-1\4\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\4\-1,3\-1\4\-1,3\-1\4\-1,3\-1\4\-1,3\-1\4\-1,3\-1\4\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,4\-1\2\-1,3\-1\3\-1,3\-1\3\-1,4\-1\3\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\54\0\-1,0\54\0\-1,0\29\0\-1,4\-1\2\-1,0\29\0\-1,0\54\0\-1,0\54\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\54\0\-1,4\128\0\-1,5\-1\0\-1,3\-1\2\-1,5\-1\0\-1,4\128\0\-1,0\54\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\53\0\-1,3\-1\0\-1,3\-1\0\-1,5\-1\0\0,3\-1\0\-1,3\-1\0\-1,0\53\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\53\0\-1,0\53\0\-1,5\-1\0\-1,4\-1\1\-1,5\-1\0\-1,0\53\0\-1,0\53\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\285\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,3\-1\3\-1,3\-1\3\-1,3\-1\3\-1,3\-1\3\-1,3\-1\3\-1,4\-1\3\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\2\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,26\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\4\-1,3\-1\4\-1,3\-1\4\-1,4\-1\1\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,27\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,4\-1\1\-1,3\-1\3\-1,3\-1\3\2,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 +0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1,0\-1\0\-1 \ No newline at end of file diff --git a/games/TowerDefense/map.h b/games/TowerDefense/map.h new file mode 100644 index 00000000..1fa2334e --- /dev/null +++ b/games/TowerDefense/map.h @@ -0,0 +1,171 @@ +#ifndef _Map_H_ +#define _Map_H_ + +#include "tile.h" +#include"route.h" + +#include +#include +#include +#include +#include + +class Map +{ +public: typedef std::unordered_map SpawnerRoutePool; + +public: + Map() = default; + + ~Map() = default; + + bool load(const std::string& path)//定义加载文件函数 + { + + std::ifstream file(path);//读取文件 + if (!file.good()) return false;//如果文件打不开,直接返回终止 + + TileMap tile_map_tep;//定义二维数组tep版 + + int idx_x = -1, idy_y = -1;//定义并初始化二维数组的索引 + + std::string str_line;//定义读取csv文件行承载对象 + while (std::getline(file, str_line)) + { + str_line = trim_str(str_line);//标准化行对象,使得没有制表符和空格 + if (str_line.empty()) + continue; + + idx_x = -1, idy_y++;//都从第一列开始读起,行数根据循环数自动累加,实现一行一行往下读的效果 + tile_map_tep.emplace_back();//往二维数组里面追加新一行 + + std::string str_tile;//定义单元格承载 + std::stringstream str_stream(str_line);//字符串化行承载 + while (std::getline(str_stream, str_tile, ','))//进行getline操作,分隔符是, + { + idx_x++;//随着循环增加读取列数 + tile_map_tep[idy_y].emplace_back();//追加单元格承载 + Tile& tile = tile_map_tep[idy_y].back();//获得单元格承载 + load_tile_from_string(tile, str_tile); + } + } + file.close(); + + if (tile_map_tep.empty() || tile_map_tep[0].empty()) + return false; + + tilemap = tile_map_tep;//地图文件没有问题,将tep输入给正式版 + + generate_map_cache(); + + return true; + } + + size_t get_width() const//获取地图数据宽度 + { + if (tilemap.empty()) + return false; + + return tilemap[0].size(); + } + + size_t get_height() const//获取地图数据高度 + { + return tilemap.size(); + } + + const TileMap& get_tile_map() const//对外暴露接口 + { + return tilemap; + } + + const SDL_Point& get_idx_home() const//对外暴露接口 + { + return idx_home; + } + + const SpawnerRoutePool& get_idx_spawner_pool() const//对外暴露接口 + { + return spawner_route_pool; + } + + void place_tower(const SDL_Point& idx_title)//对外暴露接口 + { + tilemap[idx_title.y][idx_title.x].has_tower = true; + } +private: + TileMap tilemap; + SDL_Point idx_home = { 0 }; + SpawnerRoutePool spawner_route_pool; +private: + std::string trim_str(const std::string& str)//标准格式化字符串的方法 + { + size_t begin_idx = str.find_first_not_of(" \t"); + if (begin_idx == std::string::npos) + return ""; + size_t end_idx = str.find_last_not_of(" \t"); + size_t idx_range = end_idx - begin_idx + 1; + return str.substr(begin_idx, idx_range); + } + + void load_tile_from_string(Tile& tile, const std::string& str) + { + std::string str_tidy = trim_str(str); + std::string str_value; + std::vector values; + std::stringstream stringstream(str_tidy); + while (std::getline(stringstream, str_value, '\\')) + { + int value; + try { + value = std::stoi(str_value); + } + catch (const std::invalid_argument) + { + value = -1; + } + values.push_back(value); + }//value接收整数化后的字符串values,接收完一个以后往values推,直到推完。 + tile.terrian = (values.size() < 1 || values[0] < 0) ? 0 : values[0]; + tile.decoration = (values.size() < 2) ? -1 : values[1]; + tile.direction = (Tile::Direction)((values.size() < 3 || values[2] < 0) ? 0 : values[2]); + tile.special_flag = (values.size() <= 3) ? -1 : values[3]; + //对输入值的异常处理 + } + + void generate_map_cache() + { + for (int y = 0; y < get_height(); y++) + { + + for(int x = 0;x < get_width();x++) + { + const Tile& tile = tilemap[y][x]; + if (tile.special_flag < 0) + continue; + //非特殊地点继续 + if (tile.special_flag == 0) + { + idx_home.x = x; + idx_home.y = y; + } + //房屋守卫点记录 + else + { + spawner_route_pool[tile.special_flag] = Route(tilemap,{ x,y }); + } + } + + + } + + } + +}; + + + + + +#endif // !_Map_H_ + diff --git a/games/TowerDefense/panel.h b/games/TowerDefense/panel.h new file mode 100644 index 00000000..a6d76462 --- /dev/null +++ b/games/TowerDefense/panel.h @@ -0,0 +1,232 @@ +#ifndef _PANEL_H_ +#define _PANEL_H_ + +#include "tile.h" +#include "resources_manager.h" + +#include +#include + +class Panel +{ +public: + Panel() + { + tex_select_cursor = ResourcesManager::instance()->get_texture_pool().find(ResID::Tex_UISelectCursor)->second; + } + + ~Panel() + { + SDL_DestroyTexture(tex_text_background); + SDL_DestroyTexture(tex_text_foreground); + } + + void show() + { + visible = true; + } + + void set_idx_tile(const SDL_Point& idx) + { + idx_tile_selected = idx; + } + + void set_center_pos(const SDL_Point& pos) + { + center_pos = pos; + } + + void on_input(const SDL_Event& event) + { + if (!visible) return; + + switch (event.type) + { + case SDL_MOUSEMOTION: + { + SDL_Point pos_cursor = { event.motion.x, event.motion.y }; + SDL_Rect rect_target = { 0, 0, size_button, size_button }; + + rect_target.x = center_pos.x - width / 2 + offset_top.x; + rect_target.y = center_pos.y - width / 2 + offset_top.y; + if (SDL_PointInRect(&pos_cursor, &rect_target)) + { + hovered_target = HoveredTarget::Top; + return; + } + + rect_target.x = center_pos.x - width / 2 + offset_left.x; + rect_target.y = center_pos.y - width / 2 + offset_left.y; + if (SDL_PointInRect(&pos_cursor, &rect_target)) + { + hovered_target = HoveredTarget::Left; + return; + } + + rect_target.x = center_pos.x - width / 2 + offset_right.x; + rect_target.y = center_pos.y - width / 2 + offset_right.y; + if (SDL_PointInRect(&pos_cursor, &rect_target)) + { + hovered_target = HoveredTarget::Right; + return; + } + + hovered_target = HoveredTarget::None; + } + break; + case SDL_MOUSEBUTTONUP: + { + switch (hovered_target) + { + case Panel::HoveredTarget::Top: + on_click_top_area(); + break; + case Panel::HoveredTarget::Left: + on_click_left_area(); + break; + case Panel::HoveredTarget::Right: + on_click_right_area(); + break; + } + + visible = false; + } + break; + default: + break; + } + } + + virtual void on_update(SDL_Renderer* renderer) + { + static TTF_Font* font = ResourcesManager::instance()->get_font_pool().find(ResID::Font_Main)->second; + + if (hovered_target == HoveredTarget::None) + return; + + int val = 0; + switch (hovered_target) + { + case Panel::HoveredTarget::Top: + val = val_top; + break; + case Panel::HoveredTarget::Left: + val = val_left; + break; + case Panel::HoveredTarget::Right: + val = val_right; + break; + } + + SDL_DestroyTexture(tex_text_background); + tex_text_background = nullptr; + SDL_DestroyTexture(tex_text_foreground); + tex_text_foreground = nullptr; + + std::string str_val = val < 0 ? "MAX" : std::to_string(val); + SDL_Surface* suf_text_background = TTF_RenderText_Blended(font, str_val.c_str(), color_text_background); + SDL_Surface* suf_text_foreground = TTF_RenderText_Blended(font, str_val.c_str(), color_text_foreground); + + width_text = suf_text_background->w, height_text = suf_text_background->h; + tex_text_background = SDL_CreateTextureFromSurface(renderer, suf_text_background); + tex_text_foreground = SDL_CreateTextureFromSurface(renderer, suf_text_foreground); + + SDL_FreeSurface(suf_text_background); + SDL_FreeSurface(suf_text_foreground); + } + + virtual void on_render(SDL_Renderer* renderer) + { + if (!visible) return; + + SDL_Rect rect_dst_cursor = + { + center_pos.x - SIZE_TILE / 2, + center_pos.y - SIZE_TILE / 2, + SIZE_TILE, SIZE_TILE + }; + SDL_RenderCopy(renderer, tex_select_cursor, nullptr, &rect_dst_cursor); + + SDL_Rect rect_dst_panel = + { + center_pos.x - width / 2, + center_pos.y - height / 2, + width, height + }; + + SDL_Texture* tex_panel = nullptr; + switch (hovered_target) + { + case Panel::HoveredTarget::None: + tex_panel = tex_idle; + break; + case Panel::HoveredTarget::Top: + tex_panel = tex_hovered_top; + break; + case Panel::HoveredTarget::Left: + tex_panel = tex_hovered_left; + break; + case Panel::HoveredTarget::Right: + tex_panel = tex_hovered_right; + break; + } + + SDL_RenderCopy(renderer, tex_panel, nullptr, &rect_dst_panel); + + if (hovered_target == HoveredTarget::None) + return; + + SDL_Rect rect_dst_text; + + rect_dst_text.x = center_pos.x - width_text / 2 + offset_shadow.x; + rect_dst_text.y = center_pos.y + height / 2 + offset_shadow.y; + rect_dst_text.w = width_text, rect_dst_text.h = height_text; + SDL_RenderCopy(renderer, tex_text_background, nullptr, &rect_dst_text); + + rect_dst_text.x -= offset_shadow.x; + rect_dst_text.y -= offset_shadow.y; + SDL_RenderCopy(renderer, tex_text_foreground, nullptr, &rect_dst_text); + } + +protected: + enum class HoveredTarget + { + None, + Top, + Left, + Right + }; + +protected: + bool visible = false; + SDL_Point idx_tile_selected; + SDL_Point center_pos = { 0 }; + SDL_Texture* tex_idle = nullptr; + SDL_Texture* tex_hovered_top = nullptr; + SDL_Texture* tex_hovered_left = nullptr; + SDL_Texture* tex_hovered_right = nullptr; + SDL_Texture* tex_select_cursor = nullptr; + int val_top = 0, val_left = 0, val_right = 0; + HoveredTarget hovered_target = HoveredTarget::None; + +protected: + virtual void on_click_top_area() = 0; + virtual void on_click_left_area() = 0; + virtual void on_click_right_area() = 0; + +private: + const int size_button = 48; + const int width = 144, height = 144; + const SDL_Point offset_top = { 48, 6 }; + const SDL_Point offset_left = { 8, 80 }; + const SDL_Point offset_right = { 90, 80 }; + const SDL_Point offset_shadow = { 3, 3 }; + const SDL_Color color_text_background = { 175, 175, 175, 255 }; + const SDL_Color color_text_foreground = { 255, 255, 255, 255 }; + + int width_text = 0, height_text = 0; + SDL_Texture* tex_text_background = nullptr; + SDL_Texture* tex_text_foreground = nullptr; +}; + +#endif // !_PANEL_H_ \ No newline at end of file diff --git a/games/TowerDefense/place_panel.h b/games/TowerDefense/place_panel.h new file mode 100644 index 00000000..77847a80 --- /dev/null +++ b/games/TowerDefense/place_panel.h @@ -0,0 +1,114 @@ +#ifndef _PLACE_PANEL_H_ +#define _PLACE_PANEL_H_ + +#include "panel.h" +#include "coin_manager.h" +#include "tower_manager.h" +#include "resources_manager.h" + +#include + +class PlacePanel : public Panel +{ +public: + PlacePanel() + { + const ResourcesManager::TexturePool& texture_pool + = ResourcesManager::instance()->get_texture_pool(); + + tex_idle = texture_pool.find(ResID::Tex_UIPlaceIdle)->second; + tex_hovered_top = texture_pool.find(ResID::Tex_UIPlaceHoveredTop)->second; + tex_hovered_left = texture_pool.find(ResID::Tex_UIPlaceHoveredLeft)->second; + tex_hovered_right = texture_pool.find(ResID::Tex_UIPlaceHoveredRight)->second; + } + + ~PlacePanel() = default; + + void on_update(SDL_Renderer* renderer) override + { + static TowerManager* instance = TowerManager::instance(); + + val_top = (int)instance->get_place_cost(TowerType::Axeman); + val_left = (int)instance->get_place_cost(TowerType::Archer); + val_right = (int)instance->get_place_cost(TowerType::Gunner); + + reg_top = (int)instance->get_damage_range(TowerType::Axeman) * SIZE_TILE; + reg_left = (int)instance->get_damage_range(TowerType::Archer) * SIZE_TILE; + reg_right = (int)instance->get_damage_range(TowerType::Gunner) * SIZE_TILE; + + Panel::on_update(renderer); + } + + void on_render(SDL_Renderer* renderer) override + { + if (!visible) return; + + int reg = 0; + switch (hovered_target) + { + case Panel::HoveredTarget::Top: + reg = reg_top; + break; + case Panel::HoveredTarget::Left: + reg = reg_left; + break; + case Panel::HoveredTarget::Right: + reg = reg_right; + break; + } + + if (reg > 0) + { + filledCircleRGBA(renderer, center_pos.x, center_pos.y, reg, + color_region_content.r, color_region_content.g, color_region_content.b, color_region_content.a); + aacircleRGBA(renderer, center_pos.x, center_pos.y, reg, + color_region_frame.r, color_region_frame.g, color_region_frame.b, color_region_frame.a); + } + + Panel::on_render(renderer); + } + +protected: + void on_click_top_area() override + { + CoinManager* instance = CoinManager::instance(); + + if (val_top <= instance->get_current_coin_num()) + { + TowerManager::instance()->place_tower(TowerType::Axeman, idx_tile_selected); + instance->decrease_coin(val_top); + } + } + + void on_click_left_area() override + { + CoinManager* instance = CoinManager::instance(); + + if (val_left <= instance->get_current_coin_num()) + { + TowerManager::instance()->place_tower(TowerType::Archer, idx_tile_selected); + instance->decrease_coin(val_left); + } + } + + void on_click_right_area() override + { + CoinManager* instance = CoinManager::instance(); + + if (val_right <= instance->get_current_coin_num()) + { + TowerManager::instance()->place_tower(TowerType::Gunner, idx_tile_selected); + instance->decrease_coin(val_right); + } + } + +private: + const SDL_Color color_region_frame = { 30, 80, 162, 175 }; + const SDL_Color color_region_content = { 0, 149, 217, 75 }; + +private: + int reg_top = 0, reg_left = 0, reg_right = 0; + +}; + +#endif // !_PLACE_PANEL_H_ diff --git a/games/TowerDefense/player_manager.h b/games/TowerDefense/player_manager.h new file mode 100644 index 00000000..0ced5f3f --- /dev/null +++ b/games/TowerDefense/player_manager.h @@ -0,0 +1,449 @@ +#ifndef _PLAYER_MANAGER_H_ +#define _PLAYER_MANAGER_H_ + +#include "tile.h" +#include "facing.h" +#include "vector2.h" +#include "manager.h" +#include "animation.h" +#include "coin_manager.h" +#include "enemy_manager.h" +#include "resources_manager.h" + +#include + +class PlayerManager : public Manager +{ + friend class Manager; + +public: + void on_input(const SDL_Event& event) + { + switch (event.type) + { + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case SDLK_a: + is_move_left = true; + break; + case SDLK_d: + is_move_right = true; + break; + case SDLK_w: + is_move_up = true; + break; + case SDLK_s: + is_move_down = true; + break; + case SDLK_j: + on_release_flash(); + break; + case SDLK_k: + on_release_impact(); + break; + default: + break; + } + break; + case SDL_KEYUP: + switch (event.key.keysym.sym) + { + case SDLK_a: + is_move_left = false; + break; + case SDLK_d: + is_move_right = false; + break; + case SDLK_w: + is_move_up = false; + break; + case SDLK_s: + is_move_down = false; + break; + default: + break; + } + break; + default: + break; + } + } + + void on_update(double delta) + { + timer_auto_increase_mp.on_update(delta); + timer_release_flash_cd.on_update(delta); + + Vector2 direction = + Vector2(is_move_right - is_move_left, + is_move_down - is_move_up).normalize(); + velocity = direction * speed * SIZE_TILE; + + if (!is_releasing_flash && !is_releasing_impact) + { + position += velocity * delta; + + const SDL_Rect& rect_map = ConfigManager::instance()->rect_tile_map; + if (position.x < rect_map.x) position.x = rect_map.x; + if (position.x > rect_map.x + rect_map.w) position.x = rect_map.x + rect_map.w; + if (position.y < rect_map.y) position.y = rect_map.y; + if (position.y > rect_map.y + rect_map.h) position.y = rect_map.y + rect_map.h; + + if (velocity.y > 0) facing = Facing::Down; + if (velocity.y < 0) facing = Facing::Up; + if (velocity.x > 0) facing = Facing::Right; + if (velocity.x < 0) facing = Facing::Left; + + switch (facing) + { + case Facing::Left: + anim_current = &anim_idle_left; + break; + case Facing::Right: + anim_current = &anim_idle_right; + break; + case Facing::Up: + anim_current = &anim_idle_up; + break; + case Facing::Down: + anim_current = &anim_idle_down; + break; + } + } + else + { + switch (facing) + { + case Facing::Left: + anim_current = &anim_attack_left; + break; + case Facing::Right: + anim_current = &anim_attack_right; + break; + case Facing::Up: + anim_current = &anim_attack_up; + break; + case Facing::Down: + anim_current = &anim_attack_down; + break; + } + } + + anim_current->on_update(delta); + + if (is_releasing_flash) + { + anim_effect_flash_current->on_update(delta); + + EnemyManager::EnemyList& enemy_list + = EnemyManager::instance()->get_enemy_list(); + for (Enemy* enemy : enemy_list) + { + if (enemy->can_remove()) + continue; + + const Vector2& position = enemy->get_position(); + if (position.x >= rect_hitbox_flash.x + && position.x <= rect_hitbox_flash.x + rect_hitbox_flash.w + && position.y >= rect_hitbox_flash.y + && position.y <= rect_hitbox_flash.y + rect_hitbox_flash.h) + { + enemy->decrease_hp(ConfigManager::instance()->player_template.normal_attack_damage * delta); + } + } + } + + if (is_releasing_impact) + { + anim_effect_impact_current->on_update(delta); + + EnemyManager::EnemyList& enemy_list + = EnemyManager::instance()->get_enemy_list(); + for (Enemy* enemy : enemy_list) + { + if (enemy->can_remove()) + continue; + + const Vector2& size = enemy->get_size(); + const Vector2& position = enemy->get_position(); + if (position.x >= rect_hitbox_impact.x + && position.x <= rect_hitbox_impact.x + rect_hitbox_impact.w + && position.y >= rect_hitbox_impact.y + && position.y <= rect_hitbox_impact.y + rect_hitbox_impact.h) + { + enemy->decrease_hp(ConfigManager::instance()->player_template.skill_damage * delta); + enemy->slow_down(); + } + } + } + + CoinManager::CoinPropList& coin_prop_list = CoinManager::instance()->get_coin_prop_list(); + static const ResourcesManager::SoundPool& sound_pool = ResourcesManager::instance()->get_sound_pool(); + + for (CoinProp* coin_prop : coin_prop_list) + { + if (coin_prop->can_remove()) + continue; + + const Vector2& pos_coin_prop = coin_prop->get_position(); + if (pos_coin_prop.x >= position.x - size.x / 2 + && pos_coin_prop.x <= position.x + size.x / 2 + && pos_coin_prop.y >= position.y - size.y / 2 + && pos_coin_prop.y <= position.y + size.y / 2) + { + coin_prop->make_invalid(); + CoinManager::instance()->increase_coin(10); + + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_Coin)->second, 0); + } + } + } + + void on_render(SDL_Renderer* renderer) + { + static SDL_Point point; + + point.x = (int)(position.x - size.x / 2); + point.y = (int)(position.y - size.y / 2); + anim_current->on_render(renderer, point); + + if (is_releasing_flash) + { + point.x = rect_hitbox_flash.x; + point.y = rect_hitbox_flash.y; + anim_effect_flash_current->on_render(renderer, point); + } + + if (is_releasing_impact) + { + point.x = rect_hitbox_impact.x; + point.y = rect_hitbox_impact.y; + anim_effect_impact_current->on_render(renderer, point); + } + } + + double get_current_mp() const + { + return mp; + } + +protected: + PlayerManager() + { + timer_auto_increase_mp.set_one_shot(false); + timer_auto_increase_mp.set_wait_time(0.1); + timer_auto_increase_mp.set_on_timeout( + [&]() + { + double interval = ConfigManager::instance()->player_template.skill_interval; + mp = std::min(mp + 100 / (interval / 0.1), 100.0); + }); + + timer_release_flash_cd.set_one_shot(true); + timer_release_flash_cd.set_wait_time( + ConfigManager::instance()->player_template.skill_interval); + timer_release_flash_cd.set_on_timeout( + [&]() + { + can_release_flash = true; + }); + + const ResourcesManager::TexturePool& tex_pool + = ResourcesManager::instance()->get_texture_pool(); + + SDL_Texture* tex_player = tex_pool.find(ResID::Tex_Player)->second; + + anim_idle_up.set_loop(true); anim_idle_up.set_interval(0.1); + anim_idle_up.set_frame_data(tex_player, 4, 8, { 4, 5, 6, 7 }); + anim_idle_down.set_loop(true); anim_idle_down.set_interval(0.1); + anim_idle_down.set_frame_data(tex_player, 4, 8, { 0, 1, 2, 3 }); + anim_idle_left.set_loop(true); anim_idle_left.set_interval(0.1); + anim_idle_left.set_frame_data(tex_player, 4, 8, { 8, 9, 10, 11 }); + anim_idle_right.set_loop(true); anim_idle_right.set_interval(0.1); + anim_idle_right.set_frame_data(tex_player, 4, 8, { 12, 13, 14, 15 }); + + anim_attack_up.set_loop(true); anim_attack_up.set_interval(0.1); + anim_attack_up.set_frame_data(tex_player, 4, 8, { 20, 21 }); + anim_attack_down.set_loop(true); anim_attack_down.set_interval(0.1); + anim_attack_down.set_frame_data(tex_player, 4, 8, { 16, 17 }); + anim_attack_left.set_loop(true); anim_attack_left.set_interval(0.1); + anim_attack_left.set_frame_data(tex_player, 4, 8, { 24, 25 }); + anim_attack_right.set_loop(true); anim_attack_right.set_interval(0.1); + anim_attack_right.set_frame_data(tex_player, 4, 8, { 28, 29 }); + + anim_effect_flash_up.set_loop(false); anim_effect_flash_up.set_interval(0.1); + anim_effect_flash_up.set_frame_data(tex_pool.find(ResID::Tex_EffectFlash_Up)->second, 5, 1, { 0, 1, 2, 3, 4 }); + anim_effect_flash_up.set_on_finished([&]() { is_releasing_flash = false; }); + anim_effect_flash_down.set_loop(false); anim_effect_flash_down.set_interval(0.1); + anim_effect_flash_down.set_frame_data(tex_pool.find(ResID::Tex_EffectFlash_Down)->second, 5, 1, { 4, 3, 2, 1, 0 }); + anim_effect_flash_down.set_on_finished([&]() { is_releasing_flash = false; }); + anim_effect_flash_left.set_loop(false); anim_effect_flash_left.set_interval(0.1); + anim_effect_flash_left.set_frame_data(tex_pool.find(ResID::Tex_EffectFlash_Left)->second, 1, 5, { 4, 3, 2, 1, 0 }); + anim_effect_flash_left.set_on_finished([&]() { is_releasing_flash = false; }); + anim_effect_flash_right.set_loop(false); anim_effect_flash_right.set_interval(0.1); + anim_effect_flash_right.set_frame_data(tex_pool.find(ResID::Tex_EffectFlash_Right)->second, 1, 5, { 0, 1, 2, 3, 4 }); + anim_effect_flash_right.set_on_finished([&]() { is_releasing_flash = false; }); + + anim_effect_impact_up.set_loop(false); anim_effect_impact_up.set_interval(0.1); + anim_effect_impact_up.set_frame_data(tex_pool.find(ResID::Tex_EffectImpact_Up)->second, 5, 1, { 0, 1, 2, 3, 4 }); + anim_effect_impact_up.set_on_finished([&]() { is_releasing_impact = false; }); + anim_effect_impact_down.set_loop(false); anim_effect_impact_down.set_interval(0.1); + anim_effect_impact_down.set_frame_data(tex_pool.find(ResID::Tex_EffectImpact_Down)->second, 5, 1, { 4, 3, 2, 1, 0 }); + anim_effect_impact_down.set_on_finished([&]() { is_releasing_impact = false; }); + anim_effect_impact_left.set_loop(false); anim_effect_impact_left.set_interval(0.1); + anim_effect_impact_left.set_frame_data(tex_pool.find(ResID::Tex_EffectImpact_Left)->second, 1, 5, { 4, 3, 2, 1, 0 }); + anim_effect_impact_left.set_on_finished([&]() { is_releasing_impact = false; }); + anim_effect_impact_right.set_loop(false); anim_effect_impact_right.set_interval(0.1); + anim_effect_impact_right.set_frame_data(tex_pool.find(ResID::Tex_EffectImpact_Right)->second, 1, 5, { 0, 1, 2, 3, 4 }); + anim_effect_impact_right.set_on_finished([&]() { is_releasing_impact = false; }); + + const SDL_Rect& rect_map = ConfigManager::instance()->rect_tile_map; + position.x = rect_map.x + rect_map.w / 2; + position.y = rect_map.y + rect_map.h / 2; + + speed = ConfigManager::instance()->player_template.speed; + + size.x = 96, size.y = 96; + } + + ~PlayerManager() = default; + +private: + Vector2 size; + Vector2 position; + Vector2 velocity; + + SDL_Rect rect_hitbox_flash = { 0 }; + SDL_Rect rect_hitbox_impact = { 0 }; + + double mp = 100; + + double speed = 0; + + bool can_release_flash = true; + bool is_releasing_flash = false; + bool is_releasing_impact = false; + + bool is_move_up = false; + bool is_move_down = false; + bool is_move_left = false; + bool is_move_right = false; + + Animation anim_idle_up; + Animation anim_idle_down; + Animation anim_idle_left; + Animation anim_idle_right; + Animation anim_attack_up; + Animation anim_attack_down; + Animation anim_attack_left; + Animation anim_attack_right; + Animation* anim_current = &anim_idle_right; + + Animation anim_effect_flash_up; + Animation anim_effect_flash_down; + Animation anim_effect_flash_left; + Animation anim_effect_flash_right; + Animation* anim_effect_flash_current = nullptr; + + Animation anim_effect_impact_up; + Animation anim_effect_impact_down; + Animation anim_effect_impact_left; + Animation anim_effect_impact_right; + Animation* anim_effect_impact_current = nullptr; + + Timer timer_release_flash_cd; + Timer timer_auto_increase_mp; + + Facing facing = Facing::Left; + +private: + void on_release_flash() + { + if (!can_release_flash || is_releasing_flash) + return; + + switch (facing) + { + case Facing::Left: + anim_effect_flash_current = &anim_effect_flash_left; + rect_hitbox_flash.x = (int)(position.x - size.x / 2 - 300); + rect_hitbox_flash.y = (int)(position.y - 68 / 2); + rect_hitbox_flash.w = 300, rect_hitbox_flash.h = 68; + break; + case Facing::Right: + anim_effect_flash_current = &anim_effect_flash_right; + rect_hitbox_flash.x = (int)(position.x + size.x / 2); + rect_hitbox_flash.y = (int)(position.y - 68 / 2); + rect_hitbox_flash.w = 300, rect_hitbox_flash.h = 68; + break; + case Facing::Up: + anim_effect_flash_current = &anim_effect_flash_up; + rect_hitbox_flash.x = (int)(position.x - 68 / 2); + rect_hitbox_flash.y = (int)(position.y - size.x / 2 - 300); + rect_hitbox_flash.w = 68, rect_hitbox_flash.h = 300; + break; + case Facing::Down: + anim_effect_flash_current = &anim_effect_flash_down; + rect_hitbox_flash.x = (int)(position.x - 68 / 2); + rect_hitbox_flash.y = (int)(position.y + size.x / 2); + rect_hitbox_flash.w = 68, rect_hitbox_flash.h = 300; + break; + } + + is_releasing_flash = true; + anim_effect_flash_current->reset(); + timer_release_flash_cd.restart(); + + static const ResourcesManager::SoundPool& sound_pool + = ResourcesManager::instance()->get_sound_pool(); + + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_Flash)->second, 0); + } + + void on_release_impact() + { + if (mp < 100 || is_releasing_impact) + return; + + switch (facing) + { + case Facing::Left: + anim_effect_impact_current = &anim_effect_impact_left; + rect_hitbox_impact.x = (int)(position.x - size.x / 2 - 60); + rect_hitbox_impact.y = (int)(position.y - 140 / 2); + rect_hitbox_impact.w = 60, rect_hitbox_impact.h = 140; + break; + case Facing::Right: + anim_effect_impact_current = &anim_effect_impact_right; + rect_hitbox_impact.x = (int)(position.x + size.x / 2); + rect_hitbox_impact.y = (int)(position.y - 140 / 2); + rect_hitbox_impact.w = 60, rect_hitbox_impact.h = 140; + break; + case Facing::Up: + anim_effect_impact_current = &anim_effect_impact_up; + rect_hitbox_impact.x = (int)(position.x - 140 / 2); + rect_hitbox_impact.y = (int)(position.y - size.x / 2 - 60); + rect_hitbox_impact.w = 140, rect_hitbox_impact.h = 60; + break; + case Facing::Down: + anim_effect_impact_current = &anim_effect_impact_down; + rect_hitbox_impact.x = (int)(position.x - 140 / 2); + rect_hitbox_impact.y = (int)(position.y + size.x / 2); + rect_hitbox_impact.w = 140, rect_hitbox_impact.h = 60; + break; + } + + mp = 0; + is_releasing_impact = true; + anim_effect_impact_current->reset(); + + static const ResourcesManager::SoundPool& sound_pool + = ResourcesManager::instance()->get_sound_pool(); + + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_Impact)->second, 0); + } + +}; + +#endif // !_PLAYER_MANAGER_H_ diff --git a/games/TowerDefense/resources/bullet_arrow.png b/games/TowerDefense/resources/bullet_arrow.png new file mode 100644 index 00000000..68f8d755 Binary files /dev/null and b/games/TowerDefense/resources/bullet_arrow.png differ diff --git a/games/TowerDefense/resources/bullet_axe.png b/games/TowerDefense/resources/bullet_axe.png new file mode 100644 index 00000000..5e8b5cc8 Binary files /dev/null and b/games/TowerDefense/resources/bullet_axe.png differ diff --git a/games/TowerDefense/resources/bullet_shell.png b/games/TowerDefense/resources/bullet_shell.png new file mode 100644 index 00000000..c4445dec Binary files /dev/null and b/games/TowerDefense/resources/bullet_shell.png differ diff --git a/games/TowerDefense/resources/coin.png b/games/TowerDefense/resources/coin.png new file mode 100644 index 00000000..c8e0ddb8 Binary files /dev/null and b/games/TowerDefense/resources/coin.png differ diff --git a/games/TowerDefense/resources/effect_explode.png b/games/TowerDefense/resources/effect_explode.png new file mode 100644 index 00000000..616b1178 Binary files /dev/null and b/games/TowerDefense/resources/effect_explode.png differ diff --git a/games/TowerDefense/resources/effect_flash_down.png b/games/TowerDefense/resources/effect_flash_down.png new file mode 100644 index 00000000..a1b293b1 Binary files /dev/null and b/games/TowerDefense/resources/effect_flash_down.png differ diff --git a/games/TowerDefense/resources/effect_flash_left.png b/games/TowerDefense/resources/effect_flash_left.png new file mode 100644 index 00000000..1b7258aa Binary files /dev/null and b/games/TowerDefense/resources/effect_flash_left.png differ diff --git a/games/TowerDefense/resources/effect_flash_right.png b/games/TowerDefense/resources/effect_flash_right.png new file mode 100644 index 00000000..14ea59af Binary files /dev/null and b/games/TowerDefense/resources/effect_flash_right.png differ diff --git a/games/TowerDefense/resources/effect_flash_up.png b/games/TowerDefense/resources/effect_flash_up.png new file mode 100644 index 00000000..6fc82af0 Binary files /dev/null and b/games/TowerDefense/resources/effect_flash_up.png differ diff --git a/games/TowerDefense/resources/effect_impact_down.png b/games/TowerDefense/resources/effect_impact_down.png new file mode 100644 index 00000000..091b4fee Binary files /dev/null and b/games/TowerDefense/resources/effect_impact_down.png differ diff --git a/games/TowerDefense/resources/effect_impact_left.png b/games/TowerDefense/resources/effect_impact_left.png new file mode 100644 index 00000000..bd543f6e Binary files /dev/null and b/games/TowerDefense/resources/effect_impact_left.png differ diff --git a/games/TowerDefense/resources/effect_impact_right.png b/games/TowerDefense/resources/effect_impact_right.png new file mode 100644 index 00000000..c09a99cf Binary files /dev/null and b/games/TowerDefense/resources/effect_impact_right.png differ diff --git a/games/TowerDefense/resources/effect_impact_up.png b/games/TowerDefense/resources/effect_impact_up.png new file mode 100644 index 00000000..adeb4d4e Binary files /dev/null and b/games/TowerDefense/resources/effect_impact_up.png differ diff --git a/games/TowerDefense/resources/enemy_goblin.png b/games/TowerDefense/resources/enemy_goblin.png new file mode 100644 index 00000000..8e8117d4 Binary files /dev/null and b/games/TowerDefense/resources/enemy_goblin.png differ diff --git a/games/TowerDefense/resources/enemy_goblin_priest.png b/games/TowerDefense/resources/enemy_goblin_priest.png new file mode 100644 index 00000000..f7fed8d9 Binary files /dev/null and b/games/TowerDefense/resources/enemy_goblin_priest.png differ diff --git a/games/TowerDefense/resources/enemy_goblin_priest_sketch.png b/games/TowerDefense/resources/enemy_goblin_priest_sketch.png new file mode 100644 index 00000000..5b607435 Binary files /dev/null and b/games/TowerDefense/resources/enemy_goblin_priest_sketch.png differ diff --git a/games/TowerDefense/resources/enemy_goblin_sketch.png b/games/TowerDefense/resources/enemy_goblin_sketch.png new file mode 100644 index 00000000..48fa9bab Binary files /dev/null and b/games/TowerDefense/resources/enemy_goblin_sketch.png differ diff --git a/games/TowerDefense/resources/enemy_king_slime.png b/games/TowerDefense/resources/enemy_king_slime.png new file mode 100644 index 00000000..91c4a075 Binary files /dev/null and b/games/TowerDefense/resources/enemy_king_slime.png differ diff --git a/games/TowerDefense/resources/enemy_king_slime_sketch.png b/games/TowerDefense/resources/enemy_king_slime_sketch.png new file mode 100644 index 00000000..966f833b Binary files /dev/null and b/games/TowerDefense/resources/enemy_king_slime_sketch.png differ diff --git a/games/TowerDefense/resources/enemy_skeleton.png b/games/TowerDefense/resources/enemy_skeleton.png new file mode 100644 index 00000000..765c1bbe Binary files /dev/null and b/games/TowerDefense/resources/enemy_skeleton.png differ diff --git a/games/TowerDefense/resources/enemy_skeleton_sketch.png b/games/TowerDefense/resources/enemy_skeleton_sketch.png new file mode 100644 index 00000000..c814dcda Binary files /dev/null and b/games/TowerDefense/resources/enemy_skeleton_sketch.png differ diff --git a/games/TowerDefense/resources/enemy_slime.png b/games/TowerDefense/resources/enemy_slime.png new file mode 100644 index 00000000..d48b5833 Binary files /dev/null and b/games/TowerDefense/resources/enemy_slime.png differ diff --git a/games/TowerDefense/resources/enemy_slime_sketch.png b/games/TowerDefense/resources/enemy_slime_sketch.png new file mode 100644 index 00000000..f7f3d8c5 Binary files /dev/null and b/games/TowerDefense/resources/enemy_slime_sketch.png differ diff --git a/games/TowerDefense/resources/home.png b/games/TowerDefense/resources/home.png new file mode 100644 index 00000000..330e8bbd Binary files /dev/null and b/games/TowerDefense/resources/home.png differ diff --git a/games/TowerDefense/resources/ipix.ttf b/games/TowerDefense/resources/ipix.ttf new file mode 100644 index 00000000..9a484ca9 Binary files /dev/null and b/games/TowerDefense/resources/ipix.ttf differ diff --git a/games/TowerDefense/resources/music_bgm.mp3 b/games/TowerDefense/resources/music_bgm.mp3 new file mode 100644 index 00000000..9d2a8865 Binary files /dev/null and b/games/TowerDefense/resources/music_bgm.mp3 differ diff --git a/games/TowerDefense/resources/player.png b/games/TowerDefense/resources/player.png new file mode 100644 index 00000000..46794a04 Binary files /dev/null and b/games/TowerDefense/resources/player.png differ diff --git a/games/TowerDefense/resources/sound_arrow_fire_1.mp3 b/games/TowerDefense/resources/sound_arrow_fire_1.mp3 new file mode 100644 index 00000000..557a88ff Binary files /dev/null and b/games/TowerDefense/resources/sound_arrow_fire_1.mp3 differ diff --git a/games/TowerDefense/resources/sound_arrow_fire_2.mp3 b/games/TowerDefense/resources/sound_arrow_fire_2.mp3 new file mode 100644 index 00000000..d3c4b0cb Binary files /dev/null and b/games/TowerDefense/resources/sound_arrow_fire_2.mp3 differ diff --git a/games/TowerDefense/resources/sound_arrow_hit_1.mp3 b/games/TowerDefense/resources/sound_arrow_hit_1.mp3 new file mode 100644 index 00000000..eff116fa Binary files /dev/null and b/games/TowerDefense/resources/sound_arrow_hit_1.mp3 differ diff --git a/games/TowerDefense/resources/sound_arrow_hit_2.mp3 b/games/TowerDefense/resources/sound_arrow_hit_2.mp3 new file mode 100644 index 00000000..849a013f Binary files /dev/null and b/games/TowerDefense/resources/sound_arrow_hit_2.mp3 differ diff --git a/games/TowerDefense/resources/sound_arrow_hit_3.mp3 b/games/TowerDefense/resources/sound_arrow_hit_3.mp3 new file mode 100644 index 00000000..0c5b9917 Binary files /dev/null and b/games/TowerDefense/resources/sound_arrow_hit_3.mp3 differ diff --git a/games/TowerDefense/resources/sound_axe_fire.wav b/games/TowerDefense/resources/sound_axe_fire.wav new file mode 100644 index 00000000..ba263008 Binary files /dev/null and b/games/TowerDefense/resources/sound_axe_fire.wav differ diff --git a/games/TowerDefense/resources/sound_axe_hit_1.mp3 b/games/TowerDefense/resources/sound_axe_hit_1.mp3 new file mode 100644 index 00000000..eff116fa Binary files /dev/null and b/games/TowerDefense/resources/sound_axe_hit_1.mp3 differ diff --git a/games/TowerDefense/resources/sound_axe_hit_2.mp3 b/games/TowerDefense/resources/sound_axe_hit_2.mp3 new file mode 100644 index 00000000..849a013f Binary files /dev/null and b/games/TowerDefense/resources/sound_axe_hit_2.mp3 differ diff --git a/games/TowerDefense/resources/sound_axe_hit_3.mp3 b/games/TowerDefense/resources/sound_axe_hit_3.mp3 new file mode 100644 index 00000000..0c5b9917 Binary files /dev/null and b/games/TowerDefense/resources/sound_axe_hit_3.mp3 differ diff --git a/games/TowerDefense/resources/sound_coin.mp3 b/games/TowerDefense/resources/sound_coin.mp3 new file mode 100644 index 00000000..64401825 Binary files /dev/null and b/games/TowerDefense/resources/sound_coin.mp3 differ diff --git a/games/TowerDefense/resources/sound_flash.wav b/games/TowerDefense/resources/sound_flash.wav new file mode 100644 index 00000000..6525ad03 Binary files /dev/null and b/games/TowerDefense/resources/sound_flash.wav differ diff --git a/games/TowerDefense/resources/sound_home_hurt.wav b/games/TowerDefense/resources/sound_home_hurt.wav new file mode 100644 index 00000000..b094056e Binary files /dev/null and b/games/TowerDefense/resources/sound_home_hurt.wav differ diff --git a/games/TowerDefense/resources/sound_impact.wav b/games/TowerDefense/resources/sound_impact.wav new file mode 100644 index 00000000..14cd0b01 Binary files /dev/null and b/games/TowerDefense/resources/sound_impact.wav differ diff --git a/games/TowerDefense/resources/sound_loss.mp3 b/games/TowerDefense/resources/sound_loss.mp3 new file mode 100644 index 00000000..b1ab9df8 Binary files /dev/null and b/games/TowerDefense/resources/sound_loss.mp3 differ diff --git a/games/TowerDefense/resources/sound_place_tower.mp3 b/games/TowerDefense/resources/sound_place_tower.mp3 new file mode 100644 index 00000000..f90ccde6 Binary files /dev/null and b/games/TowerDefense/resources/sound_place_tower.mp3 differ diff --git a/games/TowerDefense/resources/sound_shell_fire.wav b/games/TowerDefense/resources/sound_shell_fire.wav new file mode 100644 index 00000000..7e804504 Binary files /dev/null and b/games/TowerDefense/resources/sound_shell_fire.wav differ diff --git a/games/TowerDefense/resources/sound_shell_hit.mp3 b/games/TowerDefense/resources/sound_shell_hit.mp3 new file mode 100644 index 00000000..23765a1b Binary files /dev/null and b/games/TowerDefense/resources/sound_shell_hit.mp3 differ diff --git a/games/TowerDefense/resources/sound_tower_level_up.mp3 b/games/TowerDefense/resources/sound_tower_level_up.mp3 new file mode 100644 index 00000000..5cf1c30b Binary files /dev/null and b/games/TowerDefense/resources/sound_tower_level_up.mp3 differ diff --git a/games/TowerDefense/resources/sound_win.wav b/games/TowerDefense/resources/sound_win.wav new file mode 100644 index 00000000..6fada6b3 Binary files /dev/null and b/games/TowerDefense/resources/sound_win.wav differ diff --git a/games/TowerDefense/resources/tileset.png b/games/TowerDefense/resources/tileset.png new file mode 100644 index 00000000..b6366a8b Binary files /dev/null and b/games/TowerDefense/resources/tileset.png differ diff --git a/games/TowerDefense/resources/tower_archer.png b/games/TowerDefense/resources/tower_archer.png new file mode 100644 index 00000000..d7bb5db3 Binary files /dev/null and b/games/TowerDefense/resources/tower_archer.png differ diff --git a/games/TowerDefense/resources/tower_axeman.png b/games/TowerDefense/resources/tower_axeman.png new file mode 100644 index 00000000..49ffd2cb Binary files /dev/null and b/games/TowerDefense/resources/tower_axeman.png differ diff --git a/games/TowerDefense/resources/tower_gunner.png b/games/TowerDefense/resources/tower_gunner.png new file mode 100644 index 00000000..0e6589d7 Binary files /dev/null and b/games/TowerDefense/resources/tower_gunner.png differ diff --git a/games/TowerDefense/resources/ui_coin.png b/games/TowerDefense/resources/ui_coin.png new file mode 100644 index 00000000..fa386d11 Binary files /dev/null and b/games/TowerDefense/resources/ui_coin.png differ diff --git a/games/TowerDefense/resources/ui_game_over_bar.png b/games/TowerDefense/resources/ui_game_over_bar.png new file mode 100644 index 00000000..9699da4c Binary files /dev/null and b/games/TowerDefense/resources/ui_game_over_bar.png differ diff --git a/games/TowerDefense/resources/ui_heart.png b/games/TowerDefense/resources/ui_heart.png new file mode 100644 index 00000000..59be99e9 Binary files /dev/null and b/games/TowerDefense/resources/ui_heart.png differ diff --git a/games/TowerDefense/resources/ui_home_avatar.png b/games/TowerDefense/resources/ui_home_avatar.png new file mode 100644 index 00000000..61196531 Binary files /dev/null and b/games/TowerDefense/resources/ui_home_avatar.png differ diff --git a/games/TowerDefense/resources/ui_loss_text.png b/games/TowerDefense/resources/ui_loss_text.png new file mode 100644 index 00000000..628b9de3 Binary files /dev/null and b/games/TowerDefense/resources/ui_loss_text.png differ diff --git a/games/TowerDefense/resources/ui_place_hovered_left.png b/games/TowerDefense/resources/ui_place_hovered_left.png new file mode 100644 index 00000000..c803fa11 Binary files /dev/null and b/games/TowerDefense/resources/ui_place_hovered_left.png differ diff --git a/games/TowerDefense/resources/ui_place_hovered_right.png b/games/TowerDefense/resources/ui_place_hovered_right.png new file mode 100644 index 00000000..9e0dd1b1 Binary files /dev/null and b/games/TowerDefense/resources/ui_place_hovered_right.png differ diff --git a/games/TowerDefense/resources/ui_place_hovered_top.png b/games/TowerDefense/resources/ui_place_hovered_top.png new file mode 100644 index 00000000..410b6886 Binary files /dev/null and b/games/TowerDefense/resources/ui_place_hovered_top.png differ diff --git a/games/TowerDefense/resources/ui_place_idle.png b/games/TowerDefense/resources/ui_place_idle.png new file mode 100644 index 00000000..d83b6bdd Binary files /dev/null and b/games/TowerDefense/resources/ui_place_idle.png differ diff --git a/games/TowerDefense/resources/ui_player_avatar.png b/games/TowerDefense/resources/ui_player_avatar.png new file mode 100644 index 00000000..51b268b5 Binary files /dev/null and b/games/TowerDefense/resources/ui_player_avatar.png differ diff --git a/games/TowerDefense/resources/ui_select_cursor.png b/games/TowerDefense/resources/ui_select_cursor.png new file mode 100644 index 00000000..11a9886d Binary files /dev/null and b/games/TowerDefense/resources/ui_select_cursor.png differ diff --git a/games/TowerDefense/resources/ui_upgrade_hovered_left.png b/games/TowerDefense/resources/ui_upgrade_hovered_left.png new file mode 100644 index 00000000..6efad515 Binary files /dev/null and b/games/TowerDefense/resources/ui_upgrade_hovered_left.png differ diff --git a/games/TowerDefense/resources/ui_upgrade_hovered_right.png b/games/TowerDefense/resources/ui_upgrade_hovered_right.png new file mode 100644 index 00000000..1419f46d Binary files /dev/null and b/games/TowerDefense/resources/ui_upgrade_hovered_right.png differ diff --git a/games/TowerDefense/resources/ui_upgrade_hovered_top.png b/games/TowerDefense/resources/ui_upgrade_hovered_top.png new file mode 100644 index 00000000..2b95f668 Binary files /dev/null and b/games/TowerDefense/resources/ui_upgrade_hovered_top.png differ diff --git a/games/TowerDefense/resources/ui_upgrade_idle.png b/games/TowerDefense/resources/ui_upgrade_idle.png new file mode 100644 index 00000000..2317fe01 Binary files /dev/null and b/games/TowerDefense/resources/ui_upgrade_idle.png differ diff --git a/games/TowerDefense/resources/ui_win_text.png b/games/TowerDefense/resources/ui_win_text.png new file mode 100644 index 00000000..a7d8134b Binary files /dev/null and b/games/TowerDefense/resources/ui_win_text.png differ diff --git a/games/TowerDefense/resources_manager.h b/games/TowerDefense/resources_manager.h new file mode 100644 index 00000000..7a2f7bf6 --- /dev/null +++ b/games/TowerDefense/resources_manager.h @@ -0,0 +1,233 @@ +#ifndef _RESOURCES_MANAGER_H_ +#define _RESOURCES_MANAGER_H_ + +#include "manager.h" + +#include +#include +#include +#include + +enum class ResID +{ + Tex_Tileset, + + Tex_Player, + Tex_Archer, + Tex_Axeman, + Tex_Gunner, + + Tex_Slime, + Tex_KingSlime, + Tex_Skeleton, + Tex_Goblin, + Tex_GoblinPriest, + Tex_SlimeSketch, + Tex_KingSlimeSketch, + Tex_SkeletonSketch, + Tex_GoblinSketch, + Tex_GoblinPriestSketch, + + Tex_BulletArrow, + Tex_BulletAxe, + Tex_BulletShell, + + Tex_Coin, + Tex_Home, + + Tex_EffectFlash_Up, + Tex_EffectFlash_Down, + Tex_EffectFlash_Left, + Tex_EffectFlash_Right, + Tex_EffectImpact_Up, + Tex_EffectImpact_Down, + Tex_EffectImpact_Left, + Tex_EffectImpact_Right, + Tex_EffectExplode, + + Tex_UISelectCursor, + Tex_UIPlaceIdle, + Tex_UIPlaceHoveredTop, + Tex_UIPlaceHoveredLeft, + Tex_UIPlaceHoveredRight, + Tex_UIUpgradeIdle, + Tex_UIUpgradeHoveredTop, + Tex_UIUpgradeHoveredLeft, + Tex_UIUpgradeHoveredRight, + Tex_UIHomeAvatar, + Tex_UIPlayerAvatar, + Tex_UIHeart, + Tex_UICoin, + Tex_UIGameOverBar, + Tex_UIWinText, + Tex_UILossText, + + Sound_ArrowFire_1, + Sound_ArrowFire_2, + Sound_AxeFire, + Sound_ShellFire, + Sound_ArrowHit_1, + Sound_ArrowHit_2, + Sound_ArrowHit_3, + Sound_AxeHit_1, + Sound_AxeHit_2, + Sound_AxeHit_3, + Sound_ShellHit, + + Sound_Flash, + Sound_Impact, + + Sound_Coin, + Sound_HomeHurt, + Sound_PlaceTower, + Sound_TowerLevelUp, + + Sound_Win, + Sound_Loss, + + Music_BGM, + + Font_Main +}; + +class ResourcesManager : public Manager +{ + friend class Manager; + +public: + typedef std::unordered_map FontPool; + typedef std::unordered_map SoundPool; + typedef std::unordered_map MusicPool; + typedef std::unordered_map TexturePool; + +public: + bool load_from_file(SDL_Renderer* renderer) + { + texture_pool[ResID::Tex_Tileset] = IMG_LoadTexture(renderer, "resources/tileset.png"); + + texture_pool[ResID::Tex_Player] = IMG_LoadTexture(renderer, "resources/player.png"); + texture_pool[ResID::Tex_Archer] = IMG_LoadTexture(renderer, "resources/tower_archer.png"); + texture_pool[ResID::Tex_Axeman] = IMG_LoadTexture(renderer, "resources/tower_axeman.png"); + texture_pool[ResID::Tex_Gunner] = IMG_LoadTexture(renderer, "resources/tower_gunner.png"); + + texture_pool[ResID::Tex_Slime] = IMG_LoadTexture(renderer, "resources/enemy_slime.png"); + texture_pool[ResID::Tex_KingSlime] = IMG_LoadTexture(renderer, "resources/enemy_king_slime.png"); + texture_pool[ResID::Tex_Skeleton] = IMG_LoadTexture(renderer, "resources/enemy_skeleton.png"); + texture_pool[ResID::Tex_Goblin] = IMG_LoadTexture(renderer, "resources/enemy_goblin.png"); + texture_pool[ResID::Tex_GoblinPriest] = IMG_LoadTexture(renderer, "resources/enemy_goblin_priest.png"); + texture_pool[ResID::Tex_SlimeSketch] = IMG_LoadTexture(renderer, "resources/enemy_slime_sketch.png"); + texture_pool[ResID::Tex_KingSlimeSketch] = IMG_LoadTexture(renderer, "resources/enemy_king_slime_sketch.png"); + texture_pool[ResID::Tex_SkeletonSketch] = IMG_LoadTexture(renderer, "resources/enemy_skeleton_sketch.png"); + texture_pool[ResID::Tex_GoblinSketch] = IMG_LoadTexture(renderer, "resources/enemy_goblin_sketch.png"); + texture_pool[ResID::Tex_GoblinPriestSketch] = IMG_LoadTexture(renderer, "resources/enemy_goblin_priest_sketch.png"); + + texture_pool[ResID::Tex_BulletArrow] = IMG_LoadTexture(renderer, "resources/bullet_arrow.png"); + texture_pool[ResID::Tex_BulletAxe] = IMG_LoadTexture(renderer, "resources/bullet_axe.png"); + texture_pool[ResID::Tex_BulletShell] = IMG_LoadTexture(renderer, "resources/bullet_shell.png"); + + texture_pool[ResID::Tex_Coin] = IMG_LoadTexture(renderer, "resources/coin.png"); + texture_pool[ResID::Tex_Home] = IMG_LoadTexture(renderer, "resources/home.png"); + + texture_pool[ResID::Tex_EffectFlash_Up] = IMG_LoadTexture(renderer, "resources/effect_flash_up.png"); + texture_pool[ResID::Tex_EffectFlash_Down] = IMG_LoadTexture(renderer, "resources/effect_flash_down.png"); + texture_pool[ResID::Tex_EffectFlash_Left] = IMG_LoadTexture(renderer, "resources/effect_flash_left.png"); + texture_pool[ResID::Tex_EffectFlash_Right] = IMG_LoadTexture(renderer, "resources/effect_flash_right.png"); + texture_pool[ResID::Tex_EffectImpact_Up] = IMG_LoadTexture(renderer, "resources/effect_impact_up.png"); + texture_pool[ResID::Tex_EffectImpact_Down] = IMG_LoadTexture(renderer, "resources/effect_impact_down.png"); + texture_pool[ResID::Tex_EffectImpact_Left] = IMG_LoadTexture(renderer, "resources/effect_impact_left.png"); + texture_pool[ResID::Tex_EffectImpact_Right] = IMG_LoadTexture(renderer, "resources/effect_impact_right.png"); + texture_pool[ResID::Tex_EffectExplode] = IMG_LoadTexture(renderer, "resources/effect_explode.png"); + + texture_pool[ResID::Tex_UISelectCursor] = IMG_LoadTexture(renderer, "resources/ui_select_cursor.png"); + texture_pool[ResID::Tex_UIPlaceIdle] = IMG_LoadTexture(renderer, "resources/ui_place_idle.png"); + texture_pool[ResID::Tex_UIPlaceHoveredTop] = IMG_LoadTexture(renderer, "resources/ui_place_hovered_top.png"); + texture_pool[ResID::Tex_UIPlaceHoveredLeft] = IMG_LoadTexture(renderer, "resources/ui_place_hovered_left.png"); + texture_pool[ResID::Tex_UIPlaceHoveredRight] = IMG_LoadTexture(renderer, "resources/ui_place_hovered_right.png"); + texture_pool[ResID::Tex_UIUpgradeIdle] = IMG_LoadTexture(renderer, "resources/ui_upgrade_idle.png"); + texture_pool[ResID::Tex_UIUpgradeHoveredTop] = IMG_LoadTexture(renderer, "resources/ui_upgrade_hovered_top.png"); + texture_pool[ResID::Tex_UIUpgradeHoveredLeft] = IMG_LoadTexture(renderer, "resources/ui_upgrade_hovered_left.png"); + texture_pool[ResID::Tex_UIUpgradeHoveredRight] = IMG_LoadTexture(renderer, "resources/ui_upgrade_hovered_right.png"); + texture_pool[ResID::Tex_UIHomeAvatar] = IMG_LoadTexture(renderer, "resources/ui_home_avatar.png"); + texture_pool[ResID::Tex_UIPlayerAvatar] = IMG_LoadTexture(renderer, "resources/ui_player_avatar.png"); + texture_pool[ResID::Tex_UIHeart] = IMG_LoadTexture(renderer, "resources/ui_heart.png"); + texture_pool[ResID::Tex_UICoin] = IMG_LoadTexture(renderer, "resources/ui_coin.png"); + texture_pool[ResID::Tex_UIGameOverBar] = IMG_LoadTexture(renderer, "resources/ui_game_over_bar.png"); + texture_pool[ResID::Tex_UIWinText] = IMG_LoadTexture(renderer, "resources/ui_win_text.png"); + texture_pool[ResID::Tex_UILossText] = IMG_LoadTexture(renderer, "resources/ui_loss_text.png"); + + for (const auto& pair : texture_pool) + if (!pair.second) return false; + + sound_pool[ResID::Sound_ArrowFire_1] = Mix_LoadWAV("resources/sound_arrow_fire_1.mp3"); + sound_pool[ResID::Sound_ArrowFire_2] = Mix_LoadWAV("resources/sound_arrow_fire_2.mp3"); + sound_pool[ResID::Sound_AxeFire] = Mix_LoadWAV("resources/sound_axe_fire.wav"); + sound_pool[ResID::Sound_ShellFire] = Mix_LoadWAV("resources/sound_shell_fire.wav"); + sound_pool[ResID::Sound_ArrowHit_1] = Mix_LoadWAV("resources/sound_arrow_hit_1.mp3"); + sound_pool[ResID::Sound_ArrowHit_2] = Mix_LoadWAV("resources/sound_arrow_hit_2.mp3"); + sound_pool[ResID::Sound_ArrowHit_3] = Mix_LoadWAV("resources/sound_arrow_hit_3.mp3"); + sound_pool[ResID::Sound_AxeHit_1] = Mix_LoadWAV("resources/sound_axe_hit_1.mp3"); + sound_pool[ResID::Sound_AxeHit_2] = Mix_LoadWAV("resources/sound_axe_hit_2.mp3"); + sound_pool[ResID::Sound_AxeHit_3] = Mix_LoadWAV("resources/sound_axe_hit_3.mp3"); + sound_pool[ResID::Sound_ShellHit] = Mix_LoadWAV("resources/sound_shell_hit.mp3"); + + sound_pool[ResID::Sound_Flash] = Mix_LoadWAV("resources/sound_flash.wav"); + sound_pool[ResID::Sound_Impact] = Mix_LoadWAV("resources/sound_impact.wav"); + + sound_pool[ResID::Sound_Coin] = Mix_LoadWAV("resources/sound_coin.mp3"); + sound_pool[ResID::Sound_HomeHurt] = Mix_LoadWAV("resources/sound_home_hurt.wav"); + sound_pool[ResID::Sound_PlaceTower] = Mix_LoadWAV("resources/sound_place_tower.mp3"); + sound_pool[ResID::Sound_TowerLevelUp] = Mix_LoadWAV("resources/sound_tower_level_up.mp3"); + + sound_pool[ResID::Sound_Win] = Mix_LoadWAV("resources/sound_win.wav"); + sound_pool[ResID::Sound_Loss] = Mix_LoadWAV("resources/sound_loss.mp3"); + + for (const auto& pair : sound_pool) + if (!pair.second) return false; + + music_pool[ResID::Music_BGM] = Mix_LoadMUS("resources/music_bgm.mp3"); + + for (const auto& pair : music_pool) + if (!pair.second) return false; + + font_pool[ResID::Font_Main] = TTF_OpenFont("resources/ipix.ttf", 25); + + for (const auto& pair : font_pool) + if (!pair.second) return false; + + return true; + } + + const FontPool& get_font_pool() + { + return font_pool; + } + + const SoundPool& get_sound_pool() + { + return sound_pool; + } + + const MusicPool& get_music_pool() + { + return music_pool; + } + + const TexturePool& get_texture_pool() + { + return texture_pool; + } + +protected: + ResourcesManager() = default; + ~ResourcesManager() = default; + +private: + FontPool font_pool; + SoundPool sound_pool; + MusicPool music_pool; + TexturePool texture_pool; + +}; + + +#endif // !_RESOURCES_MANAGER_H_ diff --git a/games/TowerDefense/route.h b/games/TowerDefense/route.h new file mode 100644 index 00000000..06a09693 --- /dev/null +++ b/games/TowerDefense/route.h @@ -0,0 +1,104 @@ +#ifndef _ROUTE_H_ +#define _ROUTE_H_ + +#include "tile.h" + +#include +#include + +class Route +{ +public: typedef std::vector IdxList; + + +public: + Route() = default; + + Route(const TileMap& map,const SDL_Point& idx_origin) + { + size_t width_map = map[0].size(); + size_t height_map = map.size(); + SDL_Point idx_next = idx_origin; + + while(true) + { + + if (idx_next.x >= width_map || idx_next.y >= height_map) + break; + + if (check_doublicate_idx(idx_next)) + break; + else + idx_list.push_back(idx_next); + + bool is_next_dir_exist = true; + const Tile& tile = map[idx_next.y][idx_next.x]; + if (tile.special_flag == 0) + break; + switch(tile.direction) + { + case Tile::Direction::Up: + idx_next.y--; + break; + case Tile::Direction::Down: + idx_next.y++; + break; + case Tile::Direction::Left: + idx_next.x--; + break; + case Tile::Direction::Right: + idx_next.x++; + break; + default: + is_next_dir_exist = false; + break; + } + if (!is_next_dir_exist) + break; + } + + + + + + + + } + + + ~Route() = default; + + const IdxList& get_idx_list() const + { + return idx_list; + } +private: + IdxList idx_list; + + +private: + bool check_doublicate_idx(const SDL_Point& target_idx) + { + for (const SDL_Point& idx : idx_list) + { + if (idx.x == target_idx.x && idx.y == target_idx.y) + return true; + } + return false; + + } + + + +}; + + + + + + + + + +#endif // !_ROUTE_H_ + diff --git a/games/TowerDefense/shell_bullet.h b/games/TowerDefense/shell_bullet.h new file mode 100644 index 00000000..47ce01d5 --- /dev/null +++ b/games/TowerDefense/shell_bullet.h @@ -0,0 +1,82 @@ +#ifndef _SHELL_BULLET_H_ +#define _SHELL_BULLET_H_ + +#include "bullet.h" +#include "resources_manager.h" + +class ShellBullet : public Bullet +{ +public: + ShellBullet() + { + static SDL_Texture* tex_shell = ResourcesManager::instance() + ->get_texture_pool().find(ResID::Tex_BulletShell)->second; + static SDL_Texture* tex_explode = ResourcesManager::instance() + ->get_texture_pool().find(ResID::Tex_EffectExplode)->second; + + static const std::vector idx_list = { 0, 1 }; + static const std::vector idx_explode_list = { 0, 1, 2, 3, 4 }; + + animation.set_loop(true); + animation.set_interval(0.1); + animation.set_frame_data(tex_shell, 2, 1, idx_list); + + animation_explode.set_loop(false); + animation_explode.set_interval(0.1); + animation_explode.set_frame_data(tex_explode, 5, 1, idx_explode_list); + animation_explode.set_on_finished( + [&]() + { + make_invalid(); + }); + + damage_range = 96; + size.x = 48, size.y = 48; + } + + ~ShellBullet() = default; + + void on_update(double delta) override + { + if (can_collide()) + { + Bullet::on_update(delta); + return; + } + + animation_explode.on_update(delta); + } + + void on_render(SDL_Renderer* renderer) override + { + if (can_collide()) + { + Bullet::on_render(renderer); + return; + } + + static SDL_Point point; + + point.x = (int)(position.x - 96 / 2); + point.y = (int)(position.y - 96 / 2); + + animation_explode.on_render(renderer, point); + } + + void on_collide(Enemy* enemy) override + { + static const ResourcesManager::SoundPool& sound_pool + = ResourcesManager::instance()->get_sound_pool(); + + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_ShellHit)->second, 0); + + disable_collide(); + } + +private: + Animation animation_explode; + +}; + + +#endif // !_SHELL_BULLET_H_ \ No newline at end of file diff --git a/games/TowerDefense/skeleton_enemy.h b/games/TowerDefense/skeleton_enemy.h new file mode 100644 index 00000000..858ee4d3 --- /dev/null +++ b/games/TowerDefense/skeleton_enemy.h @@ -0,0 +1,59 @@ +#ifndef _SKELETON_ENEMY_H_ +#define _SKELETON_ENEMY_H_ + +#include "enemy.h" +#include "config_manager.h" +#include "resources_manager.h" + +class SkeletonEnemy : public Enemy +{ +public: + SkeletonEnemy() + { + static const ResourcesManager::TexturePool& texture_pool + = ResourcesManager::instance()->get_texture_pool(); + static SDL_Texture* tex_skeleton = texture_pool.find(ResID::Tex_Skeleton)->second; + static SDL_Texture* tex_skeleton_sketch = texture_pool.find(ResID::Tex_SkeletonSketch)->second; + static ConfigManager::EnemyTemplate& skeleton_template = ConfigManager::instance()->skeleton_template; + + static const std::vector idx_list_up = { 5, 6, 7, 8, 9 }; + static const std::vector idx_list_down = { 0, 1, 2, 3, 4 }; + static const std::vector idx_list_left = { 15, 16, 17, 18, 19 }; + static const std::vector idx_list_right = { 10, 11, 12, 13, 14 }; + + anim_up.set_loop(true); anim_up.set_interval(0.15); + anim_up.set_frame_data(tex_skeleton, 5, 4, idx_list_up); + anim_down.set_loop(true); anim_down.set_interval(0.15); + anim_down.set_frame_data(tex_skeleton, 5, 4, idx_list_down); + anim_left.set_loop(true); anim_left.set_interval(0.15); + anim_left.set_frame_data(tex_skeleton, 5, 4, idx_list_left); + anim_right.set_loop(true); anim_right.set_interval(0.15); + anim_right.set_frame_data(tex_skeleton, 5, 4, idx_list_right); + + anim_up_sketch.set_loop(true); anim_up_sketch.set_interval(0.15); + anim_up_sketch.set_frame_data(tex_skeleton_sketch, 5, 4, idx_list_up); + anim_down_sketch.set_loop(true); anim_down_sketch.set_interval(0.15); + anim_down_sketch.set_frame_data(tex_skeleton_sketch, 5, 4, idx_list_down); + anim_left_sketch.set_loop(true); anim_left_sketch.set_interval(0.15); + anim_left_sketch.set_frame_data(tex_skeleton_sketch, 5, 4, idx_list_left); + anim_right_sketch.set_loop(true); anim_right_sketch.set_interval(0.15); + anim_right_sketch.set_frame_data(tex_skeleton_sketch, 5, 4, idx_list_right); + + max_hp = skeleton_template.hp; + max_speed = skeleton_template.speed; + damage = skeleton_template.damage; + reward_ratio = skeleton_template.reward_ratio; + recover_interval = skeleton_template.recover_interval; + recover_range = skeleton_template.recover_range; + recover_intensity = skeleton_template.recover_intensity; + + size.x = 48, size.y = 48; + hp = max_hp, speed = max_speed; + } + + ~SkeletonEnemy() = default; + +}; + +#endif // !_SKELETON_ENEMY_H_ + diff --git a/games/TowerDefense/slim_enemy.h b/games/TowerDefense/slim_enemy.h new file mode 100644 index 00000000..3a447915 --- /dev/null +++ b/games/TowerDefense/slim_enemy.h @@ -0,0 +1,70 @@ +#ifndef _SLIM_ENEMY_H_ +#define _SLIM_ENEMY_H_ + +#include "enemy.h" +#include "config_manager.h" +#include "resources_manager.h" + +class SlimEnemy : public Enemy +{ +public: + SlimEnemy() + { + + static const ResourcesManager::TexturePool& texture_pool + = ResourcesManager::instance()->get_texture_pool(); + static SDL_Texture* tex_slime = texture_pool.find(ResID::Tex_Slime)->second; + static SDL_Texture* tex_slime_sketch = texture_pool.find(ResID::Tex_SlimeSketch)->second; + static ConfigManager::EnemyTemplate& slim_template = ConfigManager::instance()->slim_template; + + static const std::vector idx_list_up = { 6,7,8,9,10,11 }; + static const std::vector idx_list_down = { 0,1,2,3,4,5 }; + static const std::vector idx_list_left = { 18,19,20,21,22,23 }; + static const std::vector idx_list_right = { 12,13,14,15,16,17 }; + + anim_up.set_loop(true); anim_up.set_interval(0.1); + anim_up.set_frame_data(tex_slime, 6, 4, idx_list_up); + anim_down.set_loop(true); anim_down.set_interval(0.1); + anim_down.set_frame_data(tex_slime, 6, 4, idx_list_down); + anim_left.set_loop(true); anim_left.set_interval(0.1); + anim_left.set_frame_data(tex_slime, 6, 4, idx_list_left); + anim_right.set_loop(true); anim_right.set_interval(0.1); + anim_right.set_frame_data(tex_slime, 6, 4, idx_list_right); + + anim_up_sketch.set_loop(true); anim_up_sketch.set_interval(0.1); + anim_up_sketch.set_frame_data(tex_slime_sketch, 6, 4, idx_list_up); + anim_down_sketch.set_loop(true); anim_down_sketch.set_interval(0.1); + anim_down_sketch.set_frame_data(tex_slime_sketch, 6, 4, idx_list_down); + anim_left_sketch.set_loop(true); anim_left_sketch.set_interval(0.1); + anim_left_sketch.set_frame_data(tex_slime_sketch, 6, 4, idx_list_left); + anim_right_sketch.set_loop(true); anim_right_sketch.set_interval(0.1); + anim_right_sketch.set_frame_data(tex_slime_sketch, 6, 4, idx_list_right); + + max_hp = slim_template.hp; + max_speed = slim_template.speed; + damage = slim_template.damage; + reward_ratio = slim_template.reward_ratio; + recover_interval = slim_template.recover_interval; + recover_intensity = slim_template.recover_intensity; + recover_range = slim_template.recover_range; + + size.x = 48, size.y = 48; + hp = max_hp, speed = max_speed; + + + + + + } + + ~SlimEnemy() = default; + +private: + +}; + + + + + +#endif // !_SLIM_ENEMY_H_ diff --git a/games/TowerDefense/status_bar.h b/games/TowerDefense/status_bar.h new file mode 100644 index 00000000..d5c37705 --- /dev/null +++ b/games/TowerDefense/status_bar.h @@ -0,0 +1,119 @@ +#ifndef _STATUS_BAR_H_ +#define _STATUS_BAR_H_ + +#include "coin_manager.h" +#include "home_manager.h" +#include "resources_manager.h" +#include "player_manager.h" + +#include +#include +#include + +class StatusBar +{ +public: + StatusBar() = default; + ~StatusBar() = default; + + void set_position(int x, int y) + { + position.x = x, position.y = y; + } + + void on_update(SDL_Renderer* renderer) + { + static TTF_Font* font = ResourcesManager::instance()->get_font_pool().find(ResID::Font_Main)->second; + + SDL_DestroyTexture(tex_text_background); + tex_text_background = nullptr; + SDL_DestroyTexture(tex_text_foreground); + tex_text_foreground = nullptr; + + std::string str_val = std::to_string((int)CoinManager::instance()->get_current_coin_num()); + SDL_Surface* suf_text_background = TTF_RenderText_Blended(font, str_val.c_str(), color_text_background); + SDL_Surface* suf_text_foreground = TTF_RenderText_Blended(font, str_val.c_str(), color_text_foreground); + + width_text = suf_text_background->w, height_text = suf_text_background->h; + + tex_text_background = SDL_CreateTextureFromSurface(renderer, suf_text_background); + tex_text_foreground = SDL_CreateTextureFromSurface(renderer, suf_text_foreground); + + SDL_FreeSurface(suf_text_background); + SDL_FreeSurface(suf_text_foreground); + } + + void on_render(SDL_Renderer* renderer) + { + static SDL_Rect rect_dst; + static const ResourcesManager::TexturePool& tex_pool = ResourcesManager::instance()->get_texture_pool(); + static SDL_Texture* tex_coin = tex_pool.find(ResID::Tex_UICoin)->second; + static SDL_Texture* tex_heart = tex_pool.find(ResID::Tex_UIHeart)->second; + static SDL_Texture* tex_home_avatar = tex_pool.find(ResID::Tex_UIHomeAvatar)->second; + static SDL_Texture* tex_player_avatar = tex_pool.find(ResID::Tex_UIPlayerAvatar)->second; + + rect_dst.x = position.x, rect_dst.y = position.y; + rect_dst.w = 78, rect_dst.h = 78; + SDL_RenderCopy(renderer, tex_home_avatar, nullptr, &rect_dst); + + for (int i = 0; i < (int)HomeManager::instance()->get_current_hp_num(); i++) + { + rect_dst.x = position.x + 78 + 15 + i * (32 + 2); + rect_dst.y = position.y; + rect_dst.w = 32, rect_dst.h = 32; + SDL_RenderCopy(renderer, tex_heart, nullptr, &rect_dst); + } + + rect_dst.x = position.x + 78 + 15; + rect_dst.y = position.y + 78 - 32; + rect_dst.w = 32, rect_dst.h = 32; + SDL_RenderCopy(renderer, tex_coin, nullptr, &rect_dst); + + rect_dst.x += 32 + 10 + offset_shadow.x; + rect_dst.y = rect_dst.y + (32 - height_text) / 2 + offset_shadow.y; + rect_dst.w = width_text, rect_dst.h = height_text; + SDL_RenderCopy(renderer, tex_text_background, nullptr, &rect_dst); + + rect_dst.x -= offset_shadow.x; + rect_dst.y -= offset_shadow.y; + SDL_RenderCopy(renderer, tex_text_foreground, nullptr, &rect_dst); + + rect_dst.x = position.x + (78 - 65) / 2; + rect_dst.y = position.y + 78 + 5; + rect_dst.w = 65, rect_dst.h = 65; + SDL_RenderCopy(renderer, tex_player_avatar, nullptr, &rect_dst); + + rect_dst.x = position.x + 78 + 15; + rect_dst.y += 10; + roundedBoxRGBA(renderer, rect_dst.x, rect_dst.y, rect_dst.x + width_mp_bar, rect_dst.y + height_mp_bar, 4, + color_mp_bar_background.r, color_mp_bar_background.g, color_mp_bar_background.b, color_mp_bar_background.a); + + rect_dst.x += width_border_mp_bar; + rect_dst.y += width_border_mp_bar; + rect_dst.w = width_mp_bar - 2 * width_border_mp_bar; + rect_dst.h = height_mp_bar - 2 * width_border_mp_bar; + double process = PlayerManager::instance()->get_current_mp() / 100; + roundedBoxRGBA(renderer, rect_dst.x, rect_dst.y, rect_dst.x + (int)(rect_dst.w * process), rect_dst.y + rect_dst.h, 2, + color_mp_bar_foredground.r, color_mp_bar_foredground.g, color_mp_bar_foredground.b, color_mp_bar_foredground.a); + } + +private: + const int size_heart = 32; + const int width_mp_bar = 200; + const int height_mp_bar = 20; + const int width_border_mp_bar = 4; + const SDL_Point offset_shadow = { 2, 2 }; + const SDL_Color color_text_background = { 175, 175, 175, 255 }; + const SDL_Color color_text_foreground = { 255, 255, 255, 255 }; + const SDL_Color color_mp_bar_background = { 48, 40, 51, 255 }; + const SDL_Color color_mp_bar_foredground = { 144, 121, 173, 255 }; + +private: + SDL_Point position = { 0 }; + int width_text = 0, height_text = 0; + SDL_Texture* tex_text_background = nullptr; + SDL_Texture* tex_text_foreground = nullptr; + +}; + +#endif // !_STATUS_BAR_H_ \ No newline at end of file diff --git a/games/TowerDefense/the first game of seventhback.vcxproj b/games/TowerDefense/the first game of seventhback.vcxproj new file mode 100644 index 00000000..11e18c03 --- /dev/null +++ b/games/TowerDefense/the first game of seventhback.vcxproj @@ -0,0 +1,180 @@ +锘 + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {d6418ee3-4ffb-4131-b4b6-47f07ee3a759} + thefirstgameofseventhback + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + ..\third party\SDL2\include;..\third party\SDL2_gfx\include;..\third party\SDL2_image\include;..\third party\SDL2_mixer\include;..\third party\SDL2_ttf\include;..\third party\cJSON\include;%(AdditionalIncludeDirectories) + + + Windows + true + ..\third party\SDL2\lib\x64;..\third party\SDL2_mixer\lib\x64;..\third party\SDL2_ttf\lib\x64;..\third party\SDL2_image\lib\x64;..\third party\SDL2_gfx\lib\x64;%(AdditionalLibraryDirectories) + SDL2.lib;SDL2main.lib;SDL2_gfx.lib;SDL2_mixer.lib;SDL2_image.lib;SDL2_ttf.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/games/TowerDefense/the first game of seventhback.vcxproj.filters b/games/TowerDefense/the first game of seventhback.vcxproj.filters new file mode 100644 index 00000000..a21d88b5 --- /dev/null +++ b/games/TowerDefense/the first game of seventhback.vcxproj.filters @@ -0,0 +1,174 @@ +锘 + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {8ff1caa8-148a-45e6-ac45-ee01d257dfe7} + + + {f69c3c66-3ff4-4670-9e09-1507632a130b} + + + {35f6fb4c-91b9-4237-97b8-8236e8ab3dba} + + + {8b9f0ae9-1ee9-457a-829a-9fc7885dcaff} + + + {07f170f5-40d9-4584-b11c-cb40deaffdcc} + + + {016a9870-841d-4ddf-a352-6f95ed7b8673} + + + {78e6277b-2c76-45e8-8ddd-4101a1ec7549} + + + + + 婧愭枃浠 + + + 澶存枃浠禱manager + + + 婧愭枃浠禱cJSON + + + 澶存枃浠禱manager + + + 澶存枃浠禱manager + + + + + 澶存枃浠 + + + 澶存枃浠 + + + 澶存枃浠 + + + 澶存枃浠禱manager + + + 澶存枃浠 + + + 澶存枃浠禱enemy + + + 澶存枃浠禱manager + + + 澶存枃浠禱manager + + + 澶存枃浠 + + + 澶存枃浠 + + + 澶存枃浠 + + + 澶存枃浠禱enemy + + + 澶存枃浠禱enemy + + + 澶存枃浠禱enemy + + + 澶存枃浠禱enemy + + + 澶存枃浠禱enemy + + + 澶存枃浠禱enemy + + + 澶存枃浠禱manager + + + 澶存枃浠禱manager + + + 澶存枃浠禱manager + + + 澶存枃浠禱bullet + + + 澶存枃浠禱bullet + + + 澶存枃浠禱bullet + + + 澶存枃浠禱bullet + + + 澶存枃浠禱bullet + + + 澶存枃浠禱tower + + + 澶存枃浠禱tower + + + 澶存枃浠 + + + 澶存枃浠禱tower + + + 澶存枃浠禱tower + + + 澶存枃浠禱tower + + + 澶存枃浠禱manager + + + 澶存枃浠 + + + 澶存枃浠禱ui + + + 澶存枃浠禱ui\panel + + + 澶存枃浠禱ui\panel + + + 澶存枃浠禱ui\panel + + + 澶存枃浠禱manager + + + 澶存枃浠禱ui + + + \ No newline at end of file diff --git a/games/TowerDefense/the first game of seventhback.vcxproj.user b/games/TowerDefense/the first game of seventhback.vcxproj.user new file mode 100644 index 00000000..88a55094 --- /dev/null +++ b/games/TowerDefense/the first game of seventhback.vcxproj.user @@ -0,0 +1,4 @@ +锘 + + + \ No newline at end of file diff --git a/games/TowerDefense/tile.h b/games/TowerDefense/tile.h new file mode 100644 index 00000000..f2e90fdc --- /dev/null +++ b/games/TowerDefense/tile.h @@ -0,0 +1,34 @@ +#ifndef _TILE_H_ +#define _TILE_H_ + +#include + +#define SIZE_TILE 48//定义瓦片大小 +struct Tile +{ + enum Direction//前进方向 + { + None = 0, + Up, + Down, + Left, + Right + }; + + + + int terrian = 0;//选择瓦片样式 + int decoration = -1;//选择装饰物 + int special_flag = -1;//特殊标志,出生点,守护点 + bool has_tower = false;//标志有无防御塔 + Direction direction = Direction::None; +}; + +typedef std::vector> TileMap;//构建可拓展的二维数组 + + + + +#endif // !_TILE_H_ + + diff --git a/games/TowerDefense/timer.h b/games/TowerDefense/timer.h new file mode 100644 index 00000000..f86a2684 --- /dev/null +++ b/games/TowerDefense/timer.h @@ -0,0 +1,77 @@ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include + + +class Timer +{ +public: + Timer() = default; + + ~Timer() = default; + + void restart() + { + pass_time = 0; + shotted = false; + } + + void set_wait_time(double val) + { + wait_time = val; + } + + void set_one_shot(bool flg) + { + one_shot = false; + } + + void set_on_timeout(std::function on_timeout) + { + this->on_timeout = on_timeout; + } + + void pause() + { + paused = true; + } + + void resume() + { + paused = false; + } + + void on_update(double delta) + { + if (paused) return; + + pass_time += delta; + if (pass_time >= wait_time) + { + bool can_shot = (!one_shot || (one_shot && !shotted)); + shotted = true; + if (can_shot && on_timeout) + on_timeout(); + + pass_time -= wait_time; + } + + } + + +private: + double pass_time; + double wait_time; + bool paused = false; + bool shotted = false; + bool one_shot = false; + std::function on_timeout; + +}; + + + + +#endif // !_TIMER_H_ + diff --git a/games/TowerDefense/tower.h b/games/TowerDefense/tower.h new file mode 100644 index 00000000..9d00647c --- /dev/null +++ b/games/TowerDefense/tower.h @@ -0,0 +1,260 @@ +#ifndef _TOWER_H_ +#define _TOWER_H_ + +#include "facing.h" +#include "vector2.h" +#include "animation.h" +#include "tower_type.h" +#include "enemy_manager.h" +#include "bullet_manager.h" + +class Tower +{ +public: + Tower() + { + timer_fire.set_one_shot(true); + timer_fire.set_on_timeout( + [&]() + { + can_fire = true; + } + ); + + anim_idle_up.set_loop(true); + anim_idle_up.set_interval(0.2); + anim_idle_down.set_loop(true); + anim_idle_down.set_interval(0.2); + anim_idle_left.set_loop(true); + anim_idle_left.set_interval(0.2); + anim_idle_right.set_loop(true); + anim_idle_right.set_interval(0.2); + + anim_fire_up.set_loop(false); + anim_fire_up.set_interval(0.2); + anim_fire_up.set_on_finished( + [&]() + { + update_idle_animation(); + }); + + anim_fire_down.set_loop(false); + anim_fire_down.set_interval(0.2); + anim_fire_down.set_on_finished( + [&]() + { + update_idle_animation(); + }); + + anim_fire_left.set_loop(false); + anim_fire_left.set_interval(0.2); + anim_fire_left.set_on_finished( + [&]() + { + update_idle_animation(); + }); + + anim_fire_right.set_loop(false); + anim_fire_right.set_interval(0.2); + anim_fire_right.set_on_finished( + [&]() + { + update_idle_animation(); + }); + } + + ~Tower() = default; + + void set_position(const Vector2& position) + { + this->position = position; + } + + const Vector2& get_size() const + { + return size; + } + + const Vector2& get_position() const + { + return size; + } + + void on_update(double delta) + { + timer_fire.on_update(delta); + anim_current->on_update(delta); + + if (can_fire) on_fire(); + } + + void on_render(SDL_Renderer* renderer) + { + static SDL_Point point; + + point.x = (int)(position.x - size.x / 2); + point.y = (int)(position.y - size.y / 2); + + anim_current->on_render(renderer, point); + } + +protected: + Vector2 size; + + Animation anim_idle_up; + Animation anim_idle_down; + Animation anim_idle_left; + Animation anim_idle_right; + Animation anim_fire_up; + Animation anim_fire_down; + Animation anim_fire_left; + Animation anim_fire_right; + + TowerType tower_type = TowerType::Archer; + + double fire_speed = 0; + BulletType bullet_type = BulletType::Arrow; + +private: + Timer timer_fire; + Vector2 position; + bool can_fire = true; + Facing facing = Facing::Right; + Animation* anim_current = &anim_idle_right; + +private: + void update_idle_animation() + { + switch (facing) + { + case Left: + anim_current = &anim_idle_left; + break; + case Right: + anim_current = &anim_idle_right; + break; + case Up: + anim_current = &anim_idle_up; + break; + case Down: + anim_current = &anim_idle_down; + break; + } + } + + void update_fire_animation() + { + switch (facing) + { + case Left: + anim_current = &anim_fire_left; + break; + case Right: + anim_current = &anim_fire_right; + break; + case Up: + anim_current = &anim_fire_up; + break; + case Down: + anim_current = &anim_fire_down; + break; + } + } + + Enemy* find_target_enemy() + { + double process = -1; + double view_range = 0; + Enemy* enemy_target = nullptr; + + static ConfigManager* instance = ConfigManager::instance(); + + switch (tower_type) + { + case Archer: + view_range = instance->archer_template.view_range[instance->level_archer]; + break; + case Axeman: + view_range = instance->axeman_template.view_range[instance->level_axeman]; + break; + case Gunner: + view_range = instance->gunner_template.view_range[instance->level_gunner]; + break; + } + + EnemyManager::EnemyList& enemy_list = EnemyManager::instance()->get_enemy_list(); + + for (Enemy* enemy : enemy_list) + { + if ((enemy->get_position() - position).length() <= view_range * SIZE_TILE) + { + double new_process = enemy->get_route_process(); + if (new_process > process) + { + enemy_target = enemy; + process = new_process; + } + } + } + + return enemy_target; + } + + void on_fire() + { + Enemy* target_enemy = find_target_enemy(); + + if (!target_enemy) return; + + can_fire = false; + static ConfigManager* instance = ConfigManager::instance(); + static const ResourcesManager::SoundPool& sound_pool = ResourcesManager::instance()->get_sound_pool(); + + double interval = 0, damage = 0; + switch (tower_type) + { + case Archer: + interval = instance->archer_template.interval[instance->level_archer]; + damage = instance->archer_template.damage[instance->level_archer]; + switch (rand() % 2) + { + case 0: + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_ArrowFire_1)->second, 0); + break; + case 1: + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_ArrowFire_2)->second, 0); + break; + } + break; + case Axeman: + interval = instance->axeman_template.interval[instance->level_axeman]; + damage = instance->axeman_template.damage[instance->level_axeman]; + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_AxeFire)->second, 0); + break; + case Gunner: + interval = instance->gunner_template.interval[instance->level_gunner]; + damage = instance->gunner_template.damage[instance->level_gunner]; + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_ShellFire)->second, 0); + break; + } + timer_fire.set_wait_time(interval); + timer_fire.restart(); + + Vector2 direction = target_enemy->get_position() - position; + BulletManager::instance()->fire_bullet(bullet_type, position, direction.normalize() * fire_speed * SIZE_TILE, damage); + + bool is_show_x_anim = abs(direction.x) >= abs(direction.y); + if (is_show_x_anim) + facing = direction.x > 0 ? Facing::Right : Facing::Left; + else + facing = direction.y > 0 ? Facing::Down : Facing::Up; + + update_fire_animation(); + anim_current->reset(); + } + +}; + + +#endif // !_TOWER_H_ + diff --git a/games/TowerDefense/tower_manager.h b/games/TowerDefense/tower_manager.h new file mode 100644 index 00000000..f4c9a300 --- /dev/null +++ b/games/TowerDefense/tower_manager.h @@ -0,0 +1,182 @@ +#ifndef _TOWER_MANAGER_H_ +#define _TOWER_MANAGER_H_ + +#include // <<< DEBUG: 只插入 + +#include "tower.h" +#include "tower_type.h" +#include "manager.h" +#include "archer_tower.h" +#include "axeman_tower.h" +#include "gunner_tower.h" +#include "config_manager.h" +#include "resources_manager.h" + +#include + +class TowerManager : public Manager +{ + friend class Manager; + +public: + void on_update(double delta) + { + for (Tower* tower : tower_list) + tower->on_update(delta); + } + + void on_render(SDL_Renderer* renderer) + { + for (Tower* tower : tower_list) + tower->on_render(renderer); + } + + double get_place_cost(TowerType type) + { + static ConfigManager* instance = ConfigManager::instance(); + + switch (type) + { + case Archer: + return instance->archer_template.cost[instance->level_archer]; + break; + case Axeman: + return instance->axeman_template.cost[instance->level_axeman]; + break; + case Gunner: + return instance->gunner_template.cost[instance->level_gunner]; + break; + } + + return 0; + } + + double get_upgrade_cost(TowerType type) + { + static ConfigManager* instance = ConfigManager::instance(); + + switch (type) + { + case Archer: + return instance->level_archer == 9 ? -1 : + instance->archer_template.upgrade_cost[instance->level_archer]; + break; + case Axeman: + return instance->level_axeman == 9 ? -1 : + instance->axeman_template.upgrade_cost[instance->level_axeman]; + break; + case Gunner: + return instance->level_gunner == 9 ? -1 : + instance->gunner_template.upgrade_cost[instance->level_gunner]; + break; + } + + return 0; + } + + double get_damage_range(TowerType type) + { + static ConfigManager* instance = ConfigManager::instance(); + + switch (type) + { + case Archer: + return instance->archer_template.view_range[instance->level_archer]; + break; + case Axeman: + return instance->axeman_template.view_range[instance->level_axeman]; + break; + case Gunner: + return instance->gunner_template.view_range[instance->level_gunner]; + break; + } + + return 0; + } + + void place_tower(TowerType type, const SDL_Point& idx) + { + Tower* tower = nullptr; + + switch (type) + { + case Archer: + tower = new ArcherTower(); + break; + case Axeman: + tower = new AxemanTower(); + break; + case Gunner: + tower = new GunnerTower(); + break; + default: + tower = new ArcherTower(); + break; + } + + static Vector2 position; + static const SDL_Rect& rect = ConfigManager::instance()->rect_tile_map; + + position.x = rect.x + idx.x * SIZE_TILE + SIZE_TILE / 2; + position.y = rect.y + idx.y * SIZE_TILE + SIZE_TILE / 2; + tower->set_position(position); + tower_list.push_back(tower); + + ConfigManager::instance()->map.place_tower(idx); + + static const ResourcesManager::SoundPool& sound_pool + = ResourcesManager::instance()->get_sound_pool(); + + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_PlaceTower)->second, 0); + } + + void upgrade_tower(TowerType type) + { + // DEBUG >> + std::printf("[TOWER_MANAGER] upgrade_tower called, type=%d\n", (int)type); + std::fflush(stdout); + // << DEBUG + + static ConfigManager* instance = ConfigManager::instance(); + + int beforeA = instance->level_archer; + int beforeX = instance->level_axeman; + int beforeG = instance->level_gunner; + + switch (type) + { + case Archer: + instance->level_archer = instance->level_archer >= 9 ? 9 : instance->level_archer + 1; + break; + case Axeman: + instance->level_axeman = instance->level_axeman >= 9 ? 9 : instance->level_axeman + 1; + break; + case Gunner: + instance->level_gunner = instance->level_gunner >= 9 ? 9 : instance->level_gunner + 1; + break; + } + + // DEBUG >> + std::printf("[TOWER_MANAGER] levels before(A:%d X:%d G:%d) after(A:%d X:%d G:%d)\n", + beforeA, beforeX, beforeG, + instance->level_archer, instance->level_axeman, instance->level_gunner); + std::fflush(stdout); + // << DEBUG + + static const ResourcesManager::SoundPool& sound_pool + = ResourcesManager::instance()->get_sound_pool(); + Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_TowerLevelUp)->second, 0); + } + + +protected: + TowerManager() = default; + ~TowerManager() = default; + +private: + std::vector tower_list; + +}; + +#endif // !_TOWER_MANAGER_H_ + diff --git a/games/TowerDefense/tower_type.h b/games/TowerDefense/tower_type.h new file mode 100644 index 00000000..c1ebba9b --- /dev/null +++ b/games/TowerDefense/tower_type.h @@ -0,0 +1,14 @@ +#ifndef _TOWER_TYPE_H_ +#define _TOWER_TYPE_H_ + + + +enum TowerType +{ + Archer, + Axeman, + Gunner +}; + +#endif // !_TOWER_TYPE_H_ + diff --git a/games/TowerDefense/upgrade_panel.h b/games/TowerDefense/upgrade_panel.h new file mode 100644 index 00000000..45becf12 --- /dev/null +++ b/games/TowerDefense/upgrade_panel.h @@ -0,0 +1,144 @@ +#ifndef _UPGRADE_PANEL_H_ +#define _UPGRADE_PANEL_H_ + +#include "panel.h" +#include "tower_manager.h" +#include "resources_manager.h" +#include "coin_manager.h" // 需要拿到当前金币 +#include "config_manager.h" // 只为打印当前等级时读取 level_* + +class UpgradePanel : public Panel +{ +public: + UpgradePanel() + { + const ResourcesManager::TexturePool& texture_pool + = ResourcesManager::instance()->get_texture_pool(); + + tex_idle = texture_pool.find(ResID::Tex_UIUpgradeIdle)->second; + tex_hovered_top = texture_pool.find(ResID::Tex_UIUpgradeHoveredTop)->second; + tex_hovered_left = texture_pool.find(ResID::Tex_UIUpgradeHoveredLeft)->second; + tex_hovered_right = texture_pool.find(ResID::Tex_UIUpgradeHoveredRight)->second; + } + + ~UpgradePanel() = default; + + void on_update(SDL_Renderer* renderer) + { + // 读取当前花费 + static TowerManager* tm = TowerManager::instance(); + const int new_top = (int)tm->get_upgrade_cost(TowerType::Axeman); + const int new_left = (int)tm->get_upgrade_cost(TowerType::Archer); + const int new_right = (int)tm->get_upgrade_cost(TowerType::Gunner); + + // 读取当前金币 + const int new_coin = CoinManager::instance()->get_current_coin_num(); + + // 读取当前等级(仅用于打印,方便观察是否随升级变化) + ConfigManager* cfg = ConfigManager::instance(); + const int lvlA = cfg->level_archer; + const int lvlX = cfg->level_axeman; + const int lvlG = cfg->level_gunner; + + // —— 节流打印:仅当数值变化 且 距上次打印超过最小间隔 才打印 —— // + const Uint32 now = SDL_GetTicks(); + const Uint32 MIN_LOG_INTERVAL_MS = 250; // 最小打印间隔 250ms(可按需调整) + + const bool changed = + (new_top != prev_top) || + (new_left != prev_left) || + (new_right != prev_right) || + (new_coin != prev_coin) || + (lvlA != prev_lvlA) || (lvlX != prev_lvlX) || (lvlG != prev_lvlG); + + if (changed && (now - last_log_ms >= MIN_LOG_INTERVAL_MS)) + { + SDL_Log("[UPGRADE_PANEL] cost Axeman(top)=%d Archer(left)=%d Gunner(right)=%d coin=%d levels(A:%d X:%d G:%d)", + new_top, new_left, new_right, new_coin, lvlA, lvlX, lvlG); + + prev_top = new_top; + prev_left = new_left; + prev_right = new_right; + prev_coin = new_coin; + prev_lvlA = lvlA; + prev_lvlX = lvlX; + prev_lvlG = lvlG; + last_log_ms = now; + } + + // 继续走你原来的 UI 更新逻辑 + val_top = new_top; + val_left = new_left; + val_right = new_right; + + Panel::on_update(renderer); + } + +protected: + void on_click_top_area() override + { + CoinManager* cm = CoinManager::instance(); + const int coin = cm->get_current_coin_num(); + + SDL_Log("[UPGRADE_PANEL] click TOP (Axeman): need=%d coin=%d", val_top, coin); + + if (val_top > 0 && val_top <= coin) + { + TowerManager::instance()->upgrade_tower(TowerType::Axeman); + cm->decrease_coin(val_top); + } + else + { + SDL_Log("[UPGRADE_PANEL] click TOP ignored: not enough coins or invalid cost."); + } + } + + void on_click_left_area() override + { + CoinManager* cm = CoinManager::instance(); + const int coin = cm->get_current_coin_num(); + + SDL_Log("[UPGRADE_PANEL] click LEFT (Archer): need=%d coin=%d", val_left, coin); + + if (val_left > 0 && val_left <= coin) + { + TowerManager::instance()->upgrade_tower(TowerType::Archer); + cm->decrease_coin(val_left); + } + else + { + SDL_Log("[UPGRADE_PANEL] click LEFT ignored: not enough coins or invalid cost."); + } + } + + void on_click_right_area() override + { + CoinManager* cm = CoinManager::instance(); + const int coin = cm->get_current_coin_num(); + + SDL_Log("[UPGRADE_PANEL] click RIGHT (Gunner): need=%d coin=%d", val_right, coin); + + if (val_right > 0 && val_right <= coin) + { + TowerManager::instance()->upgrade_tower(TowerType::Gunner); + cm->decrease_coin(val_right); + } + else + { + SDL_Log("[UPGRADE_PANEL] click RIGHT ignored: not enough coins or invalid cost."); + } + } + +private: + // —— 仅用于“是否变化”的轻量缓存,避免刷屏 —— // + int prev_top = INT_MIN; + int prev_left = INT_MIN; + int prev_right = INT_MIN; + int prev_coin = INT_MIN; + int prev_lvlA = INT_MIN; + int prev_lvlX = INT_MIN; + int prev_lvlG = INT_MIN; + Uint32 last_log_ms = 0; +}; + +#endif // !_UPGRADE_PANEL_H_ diff --git a/games/TowerDefense/vector2.h b/games/TowerDefense/vector2.h new file mode 100644 index 00000000..99c03a51 --- /dev/null +++ b/games/TowerDefense/vector2.h @@ -0,0 +1,101 @@ +#ifndef _VECTOR2_H_ +#define _VECTOR2_H_ + +#include + +class Vector2 +{ +public: + double x = 0; + double y = 0; + + + +public: + Vector2() = default; + ~Vector2() = default; + + Vector2(double x,double y) + : x(x),y(y) { } + + Vector2 operator+(const Vector2& vec) const + { + return Vector2(x + vec.x, y + vec.y); + } + + void operator+=(const Vector2& vec) + { + x += vec.x, y += vec.y; + } + + Vector2 operator-(const Vector2& vec) const + { + return Vector2(x - vec.x, y - vec.y); + } + + void operator-=(const Vector2& vec) + { + x -= vec.x, y -= vec.y; + } + + double operator*(const Vector2& vec) const + { + return x * vec.x + y * vec.y; + } + + Vector2 operator*(double val) const + { + return Vector2(x * val, y * val); + } + + void operator*=(double val) + { + x *= val, y *= val; + } + + bool operator==(const Vector2& vec) const + { + return x == vec.x && y == vec.y; + } + + bool operator>(const Vector2& vec) const + { + return length() > vec.length(); + } + + bool operator<(const Vector2& vec) const + { + return length() < vec.length(); + } + + double length() const + { + return sqrt(x * x + y * y); + } + + Vector2 normalize() const + { + double len = length(); + + if (len == 0) + return Vector2(0, 0); + + return Vector2(x / len, y / len); + } + + bool approx_zero() const + { + return length() < 0.0001; + } +private: + +}; + + + + + + + +#endif // !_VECTOR2_H_ + diff --git a/games/TowerDefense/wave.h b/games/TowerDefense/wave.h new file mode 100644 index 00000000..2ead05c5 --- /dev/null +++ b/games/TowerDefense/wave.h @@ -0,0 +1,27 @@ +#ifndef _WAVE_H_ +#define _WAVE_H_ + +#include "enemy_type.h" + +#include + +struct Wave +{ + struct SpawnEvent + { + double interval = 0; + int spawn_point = 1; + EnemyType enemy_type = EnemyType::Slim; + + }; + + double rewards = 0; + double interval = 0; + + std::vector spawn_event_list; +}; + + + + +#endif // !_WAVE_H_ diff --git a/games/TowerDefense/wave_manager.h b/games/TowerDefense/wave_manager.h new file mode 100644 index 00000000..9d495c23 --- /dev/null +++ b/games/TowerDefense/wave_manager.h @@ -0,0 +1,115 @@ +#ifndef _WAVE_MANAGER_H_ +#define _WAVE_MANAGER_H_ + +#include"Manager.h" +#include"timer.h" +#include"config_manager.h" +#include"enemy_manager.h" +#include"coin_manager.h" + + +class WaveManager : public Manager +{ + friend class Manager; +public: + void on_update(double delta) + { + static ConfigManager* instance = ConfigManager::instance(); + + + if (instance->is_game_over) + return; + + if (!is_wave_started) + timer_start_wave.on_update(delta); + else + timer_spawn_enemy.on_update(delta); + + + if (is_spawned_last_enemy && EnemyManager::instance()->check_cleared()) + { + CoinManager::instance()->increase_coin(instance->wave_list[idx_wave].rewards); + + idx_wave++; + + if (idx_wave >= instance->wave_list.size()) + { + instance->is_game_win = true; + instance->is_game_over = true; + } + + else + { + idx_spawn_event = 0; + is_wave_started = false; + is_spawned_last_enemy = false; + + const Wave& wave = instance->wave_list[idx_wave]; + timer_start_wave.set_wait_time(wave.interval); + timer_start_wave.restart(); + + } + } + } + + + + +protected: + WaveManager() + { + + static const std::vector& wave_list = ConfigManager::instance()->wave_list; + + timer_start_wave.set_one_shot(true); + timer_start_wave.set_wait_time(wave_list[0].interval); + timer_start_wave.set_on_timeout( + [&]() + { + is_wave_started = true; + timer_spawn_enemy.set_wait_time(wave_list[idx_wave].spawn_event_list[0].interval);//距离上次生成事件的间隔 + timer_spawn_enemy.restart(); + } + ); + timer_spawn_enemy.set_one_shot(true); + timer_spawn_enemy.set_on_timeout( + [&]() + { + const std::vector& spawn_event_list = wave_list[idx_wave].spawn_event_list; + const Wave::SpawnEvent& spawn_event = spawn_event_list[idx_spawn_event]; + + EnemyManager::instance()->spawn_enemy(spawn_event.enemy_type, spawn_event.spawn_point); + + idx_spawn_event++; + + if(idx_spawn_event >= spawn_event_list.size()) + { + is_spawned_last_enemy = true; + return; + } + + timer_spawn_enemy.set_wait_time(spawn_event_list[idx_spawn_event].interval); + timer_spawn_enemy.restart(); + + } + ); + } + + ~WaveManager() + { + } + +private: + int idx_wave = 0; + int idx_spawn_event = 0; + Timer timer_start_wave; + Timer timer_spawn_enemy; + bool is_wave_started = false; + bool is_spawned_last_enemy = false; +}; + + + + + +#endif // !_WAVE_MANAGER_H_ diff --git a/games/TowerDefense/x64/Release/bullet_manager.obj b/games/TowerDefense/x64/Release/bullet_manager.obj new file mode 100644 index 00000000..d5296fa0 Binary files /dev/null and b/games/TowerDefense/x64/Release/bullet_manager.obj differ diff --git a/games/TowerDefense/x64/Release/cJSON.obj b/games/TowerDefense/x64/Release/cJSON.obj new file mode 100644 index 00000000..38764c1d Binary files /dev/null and b/games/TowerDefense/x64/Release/cJSON.obj differ diff --git a/games/TowerDefense/x64/Release/enemy_manager.obj b/games/TowerDefense/x64/Release/enemy_manager.obj new file mode 100644 index 00000000..c79033d7 Binary files /dev/null and b/games/TowerDefense/x64/Release/enemy_manager.obj differ diff --git a/games/TowerDefense/x64/Release/main.obj b/games/TowerDefense/x64/Release/main.obj new file mode 100644 index 00000000..f939d863 Binary files /dev/null and b/games/TowerDefense/x64/Release/main.obj differ diff --git a/games/TowerDefense/x64/Release/resources_manager.obj b/games/TowerDefense/x64/Release/resources_manager.obj new file mode 100644 index 00000000..03f91e3a Binary files /dev/null and b/games/TowerDefense/x64/Release/resources_manager.obj differ diff --git a/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/CL.command.1.tlog b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/CL.command.1.tlog new file mode 100644 index 00000000..1a01217a Binary files /dev/null and b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/CL.command.1.tlog differ diff --git a/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/CL.read.1.tlog b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/CL.read.1.tlog new file mode 100644 index 00000000..eae479e2 Binary files /dev/null and b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/CL.read.1.tlog differ diff --git a/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/CL.write.1.tlog b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/CL.write.1.tlog new file mode 100644 index 00000000..97b1bc2c Binary files /dev/null and b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/CL.write.1.tlog differ diff --git a/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/Cl.items.tlog b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/Cl.items.tlog new file mode 100644 index 00000000..89f77f89 --- /dev/null +++ b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/Cl.items.tlog @@ -0,0 +1,5 @@ +E:\vs2022椤圭洰\the first game of seventhback\third party\cJSON\cJSON.c;E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\x64\Release\cJSON.obj +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\bullet_manager.h;E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\x64\Release\bullet_manager.obj +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\enemy_manager.h;E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\x64\Release\enemy_manager.obj +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\main.cpp;E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\x64\Release\main.obj +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\resources_manager.h;E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\x64\Release\resources_manager.obj diff --git a/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/link.command.1.tlog b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/link.command.1.tlog new file mode 100644 index 00000000..18588ea0 Binary files /dev/null and b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/link.command.1.tlog differ diff --git a/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/link.read.1.tlog b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/link.read.1.tlog new file mode 100644 index 00000000..153062b4 Binary files /dev/null and b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/link.read.1.tlog differ diff --git a/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/link.secondary.1.tlog b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/link.secondary.1.tlog new file mode 100644 index 00000000..df10f085 --- /dev/null +++ b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/link.secondary.1.tlog @@ -0,0 +1,5 @@ +^E:\VS2022椤圭洰\THE FIRST GAME OF SEVENTHBACK\THE FIRST GAME OF SEVENTHBACK\X64\RELEASE\BULLET_MANAGER.OBJ|E:\VS2022椤圭洰\THE FIRST GAME OF SEVENTHBACK\THE FIRST GAME OF SEVENTHBACK\X64\RELEASE\CJSON.OBJ|E:\VS2022椤圭洰\THE FIRST GAME OF SEVENTHBACK\THE FIRST GAME OF SEVENTHBACK\X64\RELEASE\ENEMY_MANAGER.OBJ|E:\VS2022椤圭洰\THE FIRST GAME OF SEVENTHBACK\THE FIRST GAME OF SEVENTHBACK\X64\RELEASE\MAIN.OBJ|E:\VS2022椤圭洰\THE FIRST GAME OF SEVENTHBACK\THE FIRST GAME OF SEVENTHBACK\X64\RELEASE\RESOURCES_MANAGER.OBJ +E:\vs2022椤圭洰\the first game of seventhback\x64\Release\the first game of seventhback.lib +E:\vs2022椤圭洰\the first game of seventhback\x64\Release\the first game of seventhback.EXP +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\x64\Release\the first game of seventhback.IPDB +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\x64\Release\the first game of seventhback.iobj diff --git a/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/link.write.1.tlog b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/link.write.1.tlog new file mode 100644 index 00000000..c6820865 Binary files /dev/null and b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/link.write.1.tlog differ diff --git a/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/the first game of seventhback.lastbuildstate b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/the first game of seventhback.lastbuildstate new file mode 100644 index 00000000..3fa80c26 --- /dev/null +++ b/games/TowerDefense/x64/Release/the firs.d6418ee3.tlog/the first game of seventhback.lastbuildstate @@ -0,0 +1,2 @@ +PlatformToolSet=v143:VCToolArchitecture=Native64Bit:VCToolsVersion=14.44.35207:TargetPlatformVersion=10.0.26100.0: +Release|x64|E:\vs2022椤圭洰\the first game of seventhback\| diff --git a/games/TowerDefense/x64/Release/the first game of seventhback.exe.recipe b/games/TowerDefense/x64/Release/the first game of seventhback.exe.recipe new file mode 100644 index 00000000..17afda60 --- /dev/null +++ b/games/TowerDefense/x64/Release/the first game of seventhback.exe.recipe @@ -0,0 +1,11 @@ +锘 + + + + E:\vs2022椤圭洰\the first game of seventhback\x64\Release\the first game of seventhback.exe + + + + + + \ No newline at end of file diff --git a/games/TowerDefense/x64/Release/the first game of seventhback.iobj b/games/TowerDefense/x64/Release/the first game of seventhback.iobj new file mode 100644 index 00000000..7f3cac33 Binary files /dev/null and b/games/TowerDefense/x64/Release/the first game of seventhback.iobj differ diff --git a/games/TowerDefense/x64/Release/the first game of seventhback.ipdb b/games/TowerDefense/x64/Release/the first game of seventhback.ipdb new file mode 100644 index 00000000..ee2f1441 Binary files /dev/null and b/games/TowerDefense/x64/Release/the first game of seventhback.ipdb differ diff --git a/games/TowerDefense/x64/Release/the first game of seventhback.log b/games/TowerDefense/x64/Release/the first game of seventhback.log new file mode 100644 index 00000000..fd486c44 --- /dev/null +++ b/games/TowerDefense/x64/Release/the first game of seventhback.log @@ -0,0 +1,34 @@ +锘 bullet_manager.h + enemy_manager.h + main.cpp +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\upgrade_panel.h(35,75): warning C4244: 鈥滃垵濮嬪寲鈥: 浠庘渄ouble鈥濊浆鎹㈠埌鈥渋nt鈥濓紝鍙兘涓㈠け鏁版嵁 + (缂栬瘧婧愭枃浠垛渕ain.cpp鈥) + +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\upgrade_panel.h(35,28): warning C4244: 鈥滃垵濮嬪寲鈥: 浠庘渄ouble鈥濊浆鎹㈠埌鈥渃onst int鈥濓紝鍙兘涓㈠け鏁版嵁 + (缂栬瘧婧愭枃浠垛渕ain.cpp鈥) + +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\upgrade_panel.h(81,50): warning C4244: 鈥滃垵濮嬪寲鈥: 浠庘渄ouble鈥濊浆鎹㈠埌鈥渋nt鈥濓紝鍙兘涓㈠け鏁版嵁 + (缂栬瘧婧愭枃浠垛渕ain.cpp鈥) + +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\upgrade_panel.h(81,24): warning C4244: 鈥滃垵濮嬪寲鈥: 浠庘渄ouble鈥濊浆鎹㈠埌鈥渃onst int鈥濓紝鍙兘涓㈠け鏁版嵁 + (缂栬瘧婧愭枃浠垛渕ain.cpp鈥) + +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\upgrade_panel.h(99,50): warning C4244: 鈥滃垵濮嬪寲鈥: 浠庘渄ouble鈥濊浆鎹㈠埌鈥渋nt鈥濓紝鍙兘涓㈠け鏁版嵁 + (缂栬瘧婧愭枃浠垛渕ain.cpp鈥) + +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\upgrade_panel.h(99,24): warning C4244: 鈥滃垵濮嬪寲鈥: 浠庘渄ouble鈥濊浆鎹㈠埌鈥渃onst int鈥濓紝鍙兘涓㈠け鏁版嵁 + (缂栬瘧婧愭枃浠垛渕ain.cpp鈥) + +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\upgrade_panel.h(117,50): warning C4244: 鈥滃垵濮嬪寲鈥: 浠庘渄ouble鈥濊浆鎹㈠埌鈥渋nt鈥濓紝鍙兘涓㈠け鏁版嵁 + (缂栬瘧婧愭枃浠垛渕ain.cpp鈥) + +E:\vs2022椤圭洰\the first game of seventhback\the first game of seventhback\upgrade_panel.h(117,24): warning C4244: 鈥滃垵濮嬪寲鈥: 浠庘渄ouble鈥濊浆鎹㈠埌鈥渃onst int鈥濓紝鍙兘涓㈠け鏁版嵁 + (缂栬瘧婧愭枃浠垛渕ain.cpp鈥) + + 姝e湪鍒涘缓搴 E:\vs2022椤圭洰\the first game of seventhback\x64\Release\the first game of seventhback.lib 鍜屽璞 E:\vs2022椤圭洰\the first game of seventhback\x64\Release\the first game of seventhback.exp + 姝e湪鐢熸垚浠g爜 + 1 of 2739 functions (<0.1%) were compiled, the rest were copied from previous compilation. + 0 functions were new in current compilation + 3 functions had inline decision re-evaluated but remain unchanged + 宸插畬鎴愪唬鐮佺殑鐢熸垚 + the first game of seventhback.vcxproj -> E:\vs2022椤圭洰\the first game of seventhback\x64\Release\the first game of seventhback.exe diff --git a/games/TowerDefense/x64/Release/vc143.pdb b/games/TowerDefense/x64/Release/vc143.pdb new file mode 100644 index 00000000..860d676b Binary files /dev/null and b/games/TowerDefense/x64/Release/vc143.pdb differ diff --git a/scripts/about.md b/scripts/about.md new file mode 100644 index 00000000..2511d33c --- /dev/null +++ b/scripts/about.md @@ -0,0 +1 @@ +A more confusing script, it specifically to CI compiled as the output of the arcade games - warehouses - games finished down, into/home/deakin/games/LaunchScripts /, All the runnable files of compile-games are placed in this directory. That is to say, the latest updates are kept here, but there is no information here indicating exactly what this path does. Is this the developer's own test script for specifying the path on their own host? It seems to have no universality in this project? This has nothing to do with the automatic update that machine aims to achieve either? In fact, games already has an excellent CI to implement automatic updates, but unfortunately, the pull logic of arcade machine doesn't match it. \ No newline at end of file