diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..bcc65884 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.a !text !filter !merge !diff \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2df06fd5..a939eb0f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,11 @@ out CMakeSettings.json compile_commands.json .cache/ +vcpkg_installed/ +CMakeFiles/ +CMakeCache.txt +Makefile +cmake_install.cmake +vcpkg-bootstrap.log +vcpkg-manifest-install.log +.DS_Store \ No newline at end of file diff --git a/src/battle_game/CMakeLists.txt b/src/battle_game/CMakeLists.txt index 8f88d09b..af8fb58b 100644 --- a/src/battle_game/CMakeLists.txt +++ b/src/battle_game/CMakeLists.txt @@ -1,7 +1,7 @@ file(GLOB files *) foreach(file ${files}) - if (IS_DIRECTORY ${file}) + if (IS_DIRECTORY ${file} AND NOT ${file} STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}/CMakeFiles") file(RELATIVE_PATH short_name ${CMAKE_CURRENT_SOURCE_DIR} ${file}) set(COMPONENT_NAME ${short_name}) add_subdirectory(${file}) diff --git a/src/battle_game/app/built_in_shaders.inl b/src/battle_game/app/built_in_shaders.inl new file mode 100644 index 00000000..ee719c25 --- /dev/null +++ b/src/battle_game/app/built_in_shaders.inl @@ -0,0 +1,12 @@ +// This file is generated by CMake +#include "shaders/render.frag.h" +#include "shaders/render.vert.h" + +std::map> shader_list = { + {"shaders/render.frag", {reinterpret_cast(shaders_render_frag), shaders_render_frag_len}}, + {"shaders/render.vert", {reinterpret_cast(shaders_render_vert), shaders_render_vert_len}}, +}; + +std::string GetShaderCode(const std::string& file_name) { + return std::string(shader_list[file_name].first, shader_list[file_name].second); +} diff --git a/src/battle_game/core/selectable_units.cpp b/src/battle_game/core/selectable_units.cpp index d3c63f15..f3aaa88b 100644 --- a/src/battle_game/core/selectable_units.cpp +++ b/src/battle_game/core/selectable_units.cpp @@ -22,6 +22,7 @@ void GameCore::GeneratePrimaryUnitList() { * TODO: Add Your Unit Here! * */ ADD_SELECTABLE_UNIT(unit::Tank); + ADD_SELECTABLE_UNIT(unit::DoubleShrinker); unit.reset(); } diff --git a/src/battle_game/core/units/double_shrinker.cpp b/src/battle_game/core/units/double_shrinker.cpp new file mode 100644 index 00000000..c6b05773 --- /dev/null +++ b/src/battle_game/core/units/double_shrinker.cpp @@ -0,0 +1,202 @@ +#include "double_shrinker.h" + +#include "battle_game/core/bullets/bullets.h" +#include "battle_game/core/game_core.h" +#include "battle_game/graphics/graphics.h" + +namespace battle_game::unit { + +namespace { +uint32_t tank_body_model_index = 0xffffffffu; +uint32_t tank_turret_model_index = 0xffffffffu; +} // namespace + +DoubleShrinker::DoubleShrinker(GameCore *game_core, uint32_t id, uint32_t player_id) + : Unit(game_core, id, player_id) { + if (!~tank_body_model_index) { + auto mgr = AssetsManager::GetInstance(); + { + /* Tank Body */ + tank_body_model_index = mgr->RegisterModel( + { + {{-0.8f, 0.8f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, + {{-0.8f, -1.0f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, + {{0.8f, 0.8f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, + {{0.8f, -1.0f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, + // distinguish front and back + {{0.6f, 1.0f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, + {{-0.6f, 1.0f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, + }, + {0, 1, 2, 1, 2, 3, 0, 2, 5, 2, 4, 5}); + } + + { + /* Tank Turret */ + std::vector turret_vertices; + std::vector turret_indices; + const int precision = 60; + const float inv_precision = 1.0f / float(precision); + for (int i = 0; i < precision; i++) { + auto theta = (float(i) + 0.5f) * inv_precision; + theta *= glm::pi() * 2.0f; + auto sin_theta = std::sin(theta); + auto cos_theta = std::cos(theta); + turret_vertices.push_back({{sin_theta * 0.5f, cos_theta * 0.5f}, + {0.0f, 0.0f}, + {0.7f, 0.7f, 0.7f, 1.0f}}); + turret_indices.push_back(i); + turret_indices.push_back((i + 1) % precision); + turret_indices.push_back(precision); + } + turret_vertices.push_back( + {{0.0f, 0.0f}, {0.0f, 0.0f}, {0.7f, 0.7f, 0.7f, 1.0f}}); + turret_vertices.push_back( + {{-0.1f, 0.0f}, {0.0f, 0.0f}, {0.7f, 0.7f, 0.7f, 1.0f}}); + turret_vertices.push_back( + {{0.1f, 0.0f}, {0.0f, 0.0f}, {0.7f, 0.7f, 0.7f, 1.0f}}); + turret_vertices.push_back( + {{-0.1f, 1.2f}, {0.0f, 0.0f}, {0.7f, 0.7f, 0.7f, 1.0f}}); + turret_vertices.push_back( + {{0.1f, 1.2f}, {0.0f, 0.0f}, {0.7f, 0.7f, 0.7f, 1.0f}}); + turret_indices.push_back(precision + 1 + 0); + turret_indices.push_back(precision + 1 + 1); + turret_indices.push_back(precision + 1 + 2); + turret_indices.push_back(precision + 1 + 1); + turret_indices.push_back(precision + 1 + 2); + turret_indices.push_back(precision + 1 + 3); + tank_turret_model_index = + mgr->RegisterModel(turret_vertices, turret_indices); + } + } +} + +void DoubleShrinker::Render() { + glm::vec2 scale = glm::vec2(1.0f); + if (shrink_timer_ > 0) { + scale = glm::vec2(0.5f); + } + battle_game::SetTransformation(position_, rotation_, scale); + battle_game::SetTexture(0); + battle_game::SetColor(game_core_->GetPlayerColor(player_id_)); + battle_game::DrawModel(tank_body_model_index); + battle_game::SetRotation(turret_rotation_); + battle_game::DrawModel(tank_turret_model_index); + battle_game::SetRotation(turret_rotation_ + glm::radians(180.0f)); + battle_game::DrawModel(tank_turret_model_index); +} + +void DoubleShrinker::Update() { + TankMove(3.0f, glm::radians(180.0f)); + TurretRotate(); + FireGuns(); + Shrink(); + if (shrink_timer_ > 0) { + shrink_timer_--; + } + if (shrink_count_down_ > 0) { + shrink_count_down_--; + } +} + +void DoubleShrinker::TankMove(float move_speed, float rotate_angular_speed) { + auto player = game_core_->GetPlayer(player_id_); + if (player) { + auto &input_data = player->GetInputData(); + glm::vec2 offset{0.0f}; + if (input_data.key_down[GLFW_KEY_W]) { + offset.y += 1.0f; + } + if (input_data.key_down[GLFW_KEY_S]) { + offset.y -= 1.0f; + } + float speed = move_speed * GetSpeedScale(); + offset *= kSecondPerTick * speed; + auto new_position = + position_ + glm::vec2{glm::rotate(glm::mat4{1.0f}, rotation_, + glm::vec3{0.0f, 0.0f, 1.0f}) * + glm::vec4{offset, 0.0f, 0.0f}}; + if (!game_core_->IsBlockedByObstacles(new_position)) { + game_core_->PushEventMoveUnit(id_, new_position); + } + float rotation_offset = 0.0f; + if (input_data.key_down[GLFW_KEY_A]) { + rotation_offset += 1.0f; + } + if (input_data.key_down[GLFW_KEY_D]) { + rotation_offset -= 1.0f; + } + rotation_offset *= kSecondPerTick * rotate_angular_speed * GetSpeedScale(); + game_core_->PushEventRotateUnit(id_, rotation_ + rotation_offset); + } +} + +void DoubleShrinker::TurretRotate() { + auto player = game_core_->GetPlayer(player_id_); + if (player) { + auto &input_data = player->GetInputData(); + auto diff = input_data.mouse_cursor_position - position_; + if (glm::length(diff) < 1e-4) { + turret_rotation_ = rotation_; + } else { + turret_rotation_ = std::atan2(diff.y, diff.x) - glm::radians(90.0f); + } + } +} + +void DoubleShrinker::FireGuns() { + if (fire_count_down_ == 0) { + auto player = game_core_->GetPlayer(player_id_); + if (player) { + auto &input_data = player->GetInputData(); + if (input_data.mouse_button_down[GLFW_MOUSE_BUTTON_LEFT]) { + auto velocity1 = Rotate(glm::vec2{0.0f, 20.0f}, turret_rotation_); + auto velocity2 = Rotate(glm::vec2{0.0f, -20.0f}, turret_rotation_); + GenerateBullet( + position_ + Rotate({0.0f, 1.2f}, turret_rotation_), + turret_rotation_, GetDamageScale(), velocity1); + GenerateBullet( + position_ + Rotate({0.0f, -1.2f}, turret_rotation_), + turret_rotation_ + glm::radians(180.0f), GetDamageScale(), velocity2); + fire_count_down_ = kTickPerSecond; // Fire interval 1 second. + } + } + } + if (fire_count_down_) { + fire_count_down_--; + } +} + +void DoubleShrinker::Shrink() { + auto player = game_core_->GetPlayer(player_id_); + if (player) { + auto &input_data = player->GetInputData(); + if (input_data.key_down[GLFW_KEY_R] && shrink_count_down_ == 0) { + shrink_timer_ = 3 * kTickPerSecond; + shrink_count_down_ = 6 * kTickPerSecond; + } + } +} + +bool DoubleShrinker::IsHit(glm::vec2 position) const { + position = WorldToLocal(position); + if (shrink_timer_ > 0) { + return position.x > -0.4f && position.x < 0.4f && position.y > -0.5f && + position.y < 0.5f && position.x + position.y < 0.8f && + position.y - position.x < 0.8f; + } + else{ + return position.x > -0.8f && position.x < 0.8f && position.y > -1.0f && + position.y < 1.0f && position.x + position.y < 1.6f && + position.y - position.x < 1.6f; + } +} + +const char *DoubleShrinker::UnitName() const { + return "Double Shrinker"; +} + +const char *DoubleShrinker::Author() const { + return "JerryLin828"; +} + +} // namespace battle_game::unit \ No newline at end of file diff --git a/src/battle_game/core/units/double_shrinker.h b/src/battle_game/core/units/double_shrinker.h new file mode 100644 index 00000000..67295691 --- /dev/null +++ b/src/battle_game/core/units/double_shrinker.h @@ -0,0 +1,25 @@ +#pragma once +#include "battle_game/core/unit.h" + +namespace battle_game::unit { +class DoubleShrinker : public Unit { + public: + DoubleShrinker(GameCore *game_core, uint32_t id, uint32_t player_id); + void Render() override; + void Update() override; + [[nodiscard]] bool IsHit(glm::vec2 position) const override; + + protected: + void TankMove(float move_speed, float rotate_angular_speed); + void TurretRotate(); + void FireGuns(); + void Shrink(); + [[nodiscard]] const char *UnitName() const override; + [[nodiscard]] const char *Author() const override; + + float turret_rotation_{0.0f}; + uint32_t fire_count_down_{0}; + uint32_t shrink_timer_{0}; + uint32_t shrink_count_down_{0}; +}; +} // namespace battle_game::unit \ No newline at end of file diff --git a/src/battle_game/core/units/units.h b/src/battle_game/core/units/units.h new file mode 100644 index 00000000..dc2ddedf --- /dev/null +++ b/src/battle_game/core/units/units.h @@ -0,0 +1,5 @@ +// This is a generated file +#pragma once +#include "battle_game/core/units/double_shrinker.h" +#include "battle_game/core/units/tiny_tank.h" +#include "battle_game/core/units/units.h"