diff --git a/.gitignore b/.gitignore index 1669b9bb..b4d0a0ae 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ build out CMakeSettings.json compile_commands.json -.cache/ \ No newline at end of file +.cache/ + diff --git a/assets/textures/snowball.png b/assets/textures/snowball.png new file mode 100644 index 00000000..bb24393a Binary files /dev/null and b/assets/textures/snowball.png differ diff --git a/src/battle_game/core/bullets/bullets.h b/src/battle_game/core/bullets/bullets.h index d1447761..22c84015 100644 --- a/src/battle_game/core/bullets/bullets.h +++ b/src/battle_game/core/bullets/bullets.h @@ -9,6 +9,7 @@ #include "battle_game/core/bullets/rebounding_ball.h" #include "battle_game/core/bullets/rocket.h" #include "battle_game/core/bullets/smoke_bomb.h" +#include "battle_game/core/bullets/snowball.h" #include "battle_game/core/bullets/sweaty_soybean.h" #include "battle_game/core/bullets/udongein_directional_bullet.h" #include "battle_game/core/bullets/warning_line.h" diff --git a/src/battle_game/core/bullets/snowball.cpp b/src/battle_game/core/bullets/snowball.cpp new file mode 100644 index 00000000..663d6d65 --- /dev/null +++ b/src/battle_game/core/bullets/snowball.cpp @@ -0,0 +1,59 @@ +#include "battle_game/core/bullets/snowball.h" + +#include "battle_game/core/game_core.h" +#include "battle_game/core/particles/particles.h" + +namespace battle_game::bullet { +SnowBall::SnowBall(GameCore *core, + uint32_t id, + uint32_t unit_id, + uint32_t player_id, + glm::vec2 position, + float rotation, + float damage_scale, + glm::vec2 velocity) + : Bullet(core, id, unit_id, player_id, position, rotation, damage_scale), + velocity_(velocity) { +} + +void SnowBall::Render() { + SetTransformation(position_, rotation_, glm::vec2{0.4f}); + SetColor(game_core_->GetPlayerColor(player_id_)); + SetTexture("../../textures/snowball.png"); + DrawModel(0); +} + +void SnowBall::Update() { + position_ += velocity_ * kSecondPerTick; + bool should_die = false; + if (game_core_->IsBlockedByObstacles(position_)) { + should_die = true; + } + + auto &units = game_core_->GetUnits(); + for (auto &unit : units) { + if (unit.first == unit_id_) { + continue; + } + if (unit.second->IsHit(position_)) { + game_core_->PushEventDealDamage(unit.first, id_, damage_scale_ * 5.0f); + auto distance_per_tick = velocity_ * (kSecondPerTick * 0.22f / + (0.1f + unit.second->GetWeight())); + game_core_->PushEventMoveRUForTicks(unit.first, distance_per_tick, 50); + should_die = true; + } + } + + if (should_die) { + game_core_->PushEventRemoveBullet(id_); + } +} + +SnowBall::~SnowBall() { + for (int i = 0; i < 5; i++) { + game_core_->PushEventGenerateParticle( + position_, rotation_, game_core_->RandomInCircle() * 2.0f, 0.2f, + glm::vec4{0.0f, 0.0f, 0.0f, 1.0f}, 3.0f); + } +} +} // namespace battle_game::bullet \ No newline at end of file diff --git a/src/battle_game/core/bullets/snowball.h b/src/battle_game/core/bullets/snowball.h new file mode 100644 index 00000000..99ea57bd --- /dev/null +++ b/src/battle_game/core/bullets/snowball.h @@ -0,0 +1,22 @@ +#pragma once +#include "battle_game/core/bullet.h" + +namespace battle_game::bullet { +class SnowBall : public Bullet { + public: + SnowBall(GameCore *core, + uint32_t id, + uint32_t unit_id, + uint32_t player_id, + glm::vec2 position, + float rotation, + float damage_scale, + glm::vec2 velocity); + ~SnowBall() override; + void Render() override; + void Update() override; + + private: + glm::vec2 velocity_{}; +}; +} // namespace battle_game::bullet \ No newline at end of file diff --git a/src/battle_game/core/game_core.cpp b/src/battle_game/core/game_core.cpp index f8845ba7..fb8d28b4 100644 --- a/src/battle_game/core/game_core.cpp +++ b/src/battle_game/core/game_core.cpp @@ -45,6 +45,10 @@ void GameCore::Update() { particle.second->Update(); } ProcessEventQueue(); + ProcessAwaitingQueue(); + for (auto &units : units_) { + units.second->EndTick(); + } } /* @@ -135,6 +139,13 @@ void GameCore::ProcessEventQueue() { } } +void GameCore::ProcessAwaitingQueue() { + while (!awaiting_queue_.empty()) { + awaiting_queue_.front()(); + awaiting_queue_.pop(); + } +} + bool GameCore::IsBlockedByObstacles(glm::vec2 p) const { if (IsOutOfRange(p)) { return true; @@ -161,20 +172,56 @@ void GameCore::PushEventMoveUnit(uint32_t unit_id, glm::vec2 new_position) { event_queue_.emplace([this, unit_id, new_position]() { auto unit = GetUnit(unit_id); if (unit) { - unit->SetPosition(new_position); + unit->position_change_ += new_position - unit->GetPosition(); } }); } +void GameCore::PushEventMoveRelativeUnit(uint32_t unit_id, + glm::vec2 relative_position) { + event_queue_.emplace([this, unit_id, relative_position]() { + auto unit = GetUnit(unit_id); + if (unit) { + unit->position_change_ += relative_position; + } + }); +} // this function is inspired by XieRujian, completely abandon the queue + +void GameCore::PushEventMoveRUForTicks(uint32_t unit_id, + glm::vec2 r_p, + uint32_t remaining_ticks) { + if (remaining_ticks > 0) { + event_queue_.emplace([this, unit_id, r_p, remaining_ticks]() { + auto unit = GetUnit(unit_id); + if (unit) { + unit->position_change_ += r_p; + awaiting_queue_.emplace([=]() { + PushEventMoveRUForTicks(unit_id, r_p, remaining_ticks - 1); + }); + } + }); + } +} // this function is an easy simulation for "effects" + void GameCore::PushEventRotateUnit(uint32_t unit_id, float new_rotation) { event_queue_.emplace([this, unit_id, new_rotation]() { auto unit = GetUnit(unit_id); if (unit) { - unit->SetRotation(new_rotation); + unit->rotation_change_ += new_rotation - unit->GetRotation(); } }); } +void GameCore::PushEventRotateRelativeUnit(uint32_t unit_id, + float relative_rotation) { + event_queue_.emplace([this, unit_id, relative_rotation]() { + auto unit = GetUnit(unit_id); + if (unit) { + unit->rotation_change_ += relative_rotation; + } + }); +} // change another btw, although I dont actually use + Unit *GameCore::GetUnit(uint32_t unit_id) const { if (!units_.count(unit_id)) { return nullptr; diff --git a/src/battle_game/core/game_core.h b/src/battle_game/core/game_core.h index 0e81d8e4..2b4354c7 100644 --- a/src/battle_game/core/game_core.h +++ b/src/battle_game/core/game_core.h @@ -123,7 +123,12 @@ class GameCore { [[nodiscard]] Obstacle *GetBlockedObstacle(glm::vec2 p) const; void PushEventMoveUnit(uint32_t unit_id, glm::vec2 new_position); + void PushEventMoveRelativeUnit(uint32_t unit_id, glm::vec2 relative_position); + void PushEventMoveRUForTicks(uint32_t unit_id, + glm::vec2 r_p, + uint32_t remaining_ticks); void PushEventRotateUnit(uint32_t unit_id, float new_rotation); + void PushEventRotateRelativeUnit(uint32_t unit_id, float new_rotation); void PushEventDealDamage(uint32_t dst_unit_id, uint32_t src_unit_id, float damage); @@ -163,6 +168,7 @@ class GameCore { } void ProcessEventQueue(); + void ProcessAwaitingQueue(); void SetCamera(glm::vec2 position, float rotation = 0.0f); [[nodiscard]] glm::vec2 GetCameraPosition() const { @@ -186,6 +192,8 @@ class GameCore { private: std::queue> event_queue_; + std::queue> awaiting_queue_; + // this one is for "buff" type std::map> units_; uint32_t unit_index_{1}; diff --git a/src/battle_game/core/selectable_units.cpp b/src/battle_game/core/selectable_units.cpp index fa7b1d01..80fe7d8d 100644 --- a/src/battle_game/core/selectable_units.cpp +++ b/src/battle_game/core/selectable_units.cpp @@ -29,6 +29,7 @@ void GameCore::GeneratePrimaryUnitList() { /* * TODO: Add Your Unit Here! * */ + ADD_SELECTABLE_UNIT(unit::Snowball_Sample_Sledge); ADD_SELECTABLE_UNIT(unit::InfernoTank); ADD_SELECTABLE_UNIT(unit::Tank); ADD_SELECTABLE_UNIT(unit::DoubleScatterTank); @@ -51,7 +52,6 @@ void GameCore::GeneratePrimaryUnitList() { ADD_SELECTABLE_UNIT(unit::CritTank); ADD_SELECTABLE_UNIT(unit::Railgun); ADD_SELECTABLE_UNIT(unit::Udongein); - unit.reset(); } } // namespace battle_game diff --git a/src/battle_game/core/unit.cpp b/src/battle_game/core/unit.cpp index 1c64960c..e186b528 100644 --- a/src/battle_game/core/unit.cpp +++ b/src/battle_game/core/unit.cpp @@ -118,6 +118,15 @@ void Unit::RenderLifeBar() { void Unit::RenderHelper() { } +void Unit::EndTick() { + if (!game_core_->IsBlockedByObstacles(position_ + position_change_) && + !game_core_->IsOutOfRange(position_ + position_change_)) + position_ += position_change_; + rotation_ += rotation_change_; + position_change_ = {.0f, .0f}; + rotation_change_ = {.0f}; +} + const char *Unit::UnitName() const { return "Unknown Unit"; } diff --git a/src/battle_game/core/unit.h b/src/battle_game/core/unit.h index 45d335b8..d307d5ba 100644 --- a/src/battle_game/core/unit.h +++ b/src/battle_game/core/unit.h @@ -36,6 +36,10 @@ class Unit : public Object { return health_; } + [[nodiscard]] float GetWeight() const { + return weight_; + } + /* * The value of new_health will be clamped to [0, 1] * */ @@ -43,6 +47,13 @@ class Unit : public Object { health_ = std::clamp(new_health, 0.0f, 1.0f); } + /* + * The value of new_weight will be a float from 0 to \infty + * */ + void SetWeight(float new_weight) { + weight_ = new_weight; + } + void SetLifeBarLength(float new_length); void SetLifeBarOffset(glm::vec2 new_offset); void SetLifeBarFrontColor(glm::vec4 new_color); @@ -84,9 +95,15 @@ class Unit : public Object { return skills_; } + void EndTick(); // inspired by XieRujian + + glm::vec2 position_change_{glm::vec2(.0f, .0f)}; + float rotation_change_{.0f}; + protected: uint32_t player_id_{}; float health_{1.0f}; + float weight_{1.0f}; std::vector skills_; bool lifebar_display_{true}; glm::vec2 lifebar_offset_{}; @@ -100,5 +117,3 @@ class Unit : public Object { }; } // namespace battle_game - -// add something to pull \ No newline at end of file diff --git a/src/battle_game/core/units/round_UFO.cpp b/src/battle_game/core/units/round_UFO.cpp index 91109768..9c088ccd 100644 --- a/src/battle_game/core/units/round_UFO.cpp +++ b/src/battle_game/core/units/round_UFO.cpp @@ -78,10 +78,8 @@ void RoundUFO::Teleport_() { auto player = game_core_->GetPlayer(player_id_); if (player) { auto &input_data = player->GetInputData(); - auto new_position = input_data.mouse_cursor_position; - if (!game_core_->IsBlockedByObstacles(new_position)) { - game_core_->PushEventMoveUnit(id_, new_position); - } + game_core_->PushEventMoveRelativeUnit(id_, + input_data.mouse_cursor_position); } } @@ -113,13 +111,10 @@ void RoundUFO::UFOMove(float move_speed) { } 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); - } + auto relative_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}}; + game_core_->PushEventMoveRelativeUnit(id_, relative_position); auto diff = input_data.mouse_cursor_position - position_; float new_rotation; diff --git a/src/battle_game/core/units/snowball_sample_sledge.cpp b/src/battle_game/core/units/snowball_sample_sledge.cpp new file mode 100644 index 00000000..41136677 --- /dev/null +++ b/src/battle_game/core/units/snowball_sample_sledge.cpp @@ -0,0 +1,135 @@ +#include "snowball_sample_sledge.h" + +#include "battle_game/core/bullets/bullets.h" +#include "battle_game/core/game_core.h" +#include "battle_game/core/units/tiny_tank.h" +#include "battle_game/graphics/graphics.h" + +namespace battle_game::unit { + +namespace { +uint32_t sledge_body_model_index = 0xffffffffu; +uint32_t sledge_turret_model_index = 0xffffffffu; +} // namespace + +Snowball_Sample_Sledge::Snowball_Sample_Sledge(GameCore *game_core, + uint32_t id, + uint32_t player_id) + : Tank(game_core, id, player_id) { + Skill temp; + temp.name = "Antigravity mode"; + temp.description = "-50% weight, less damage in all directions"; + temp.time_remain = 0; + temp.time_total = 360; + temp.type = E; + temp.function = SKILL_ADD_FUNCTION(Snowball_Sample_Sledge::AntigravityClick); + skills_.push_back(temp); + id_ = id; +}; + +void Snowball_Sample_Sledge::Render() { + Tank::Render(); +} + +void Snowball_Sample_Sledge::AntigravityClick() { + hidden_count_down_ = 3 * kTickPerSecond; + antigravity_count_down_ = 6 * kTickPerSecond; +} + +void Snowball_Sample_Sledge::Antigravity() { + skills_[0].time_remain = antigravity_count_down_; + if (antigravity_count_down_) { + antigravity_count_down_--; + } else { + auto player = game_core_->GetPlayer(player_id_); + if (player) { + auto &input_data = player->GetInputData(); + if (input_data.key_down[GLFW_KEY_E]) { + AntigravityClick(); + } + } + } +} + +void Snowball_Sample_Sledge::Antigravity_() { + if (hidden_count_down_ == 3 * kTickPerSecond) { + auto &units = game_core_->GetUnits(); + for (auto &unit : units) { + if (unit.first == id_) { + continue; + } + unit.second->SetWeight(0.5f * unit.second->GetWeight()); + } + speed_ *= 2; + } + if (hidden_count_down_ > 0 && hidden_count_down_ < 3 * kTickPerSecond + 1) { + Antigravity_fire(); + hidden_count_down_--; + } + if (hidden_count_down_ == 0) { + auto &units = game_core_->GetUnits(); + for (auto &unit : units) { + if (unit.first == id_) { + continue; + } + unit.second->SetWeight(2.0f * unit.second->GetWeight()); + } + speed_ /= 2; + hidden_count_down_ = 181; + } +} + +void Snowball_Sample_Sledge::Antigravity_fire() { + if (shoot_count_down_) { + shoot_count_down_--; + } else { + auto player = game_core_->GetPlayer(player_id_); + if (player) { + auto &input_data = player->GetInputData(); + auto velocity = Rotate(glm::vec2{0.0f, 10.0f}, turret_rotation_); + for (int i = -19; i < 19; ++i) { + auto offset = glm::radians(10.0f * i); + velocity = Rotate(glm::vec2{0.0f, 10.0f}, turret_rotation_ + offset); + GenerateBullet( + position_ + Rotate({0.0f, 1.2f}, turret_rotation_ + offset), + turret_rotation_ + offset, 0, velocity); + } + } + shoot_count_down_ = 30; + } +} + +void Snowball_Sample_Sledge::Update() { + Tank::TankMove(speed_, glm::radians(180.0f)); + Tank::TurretRotate(); + Antigravity(); + Antigravity_(); + Fire(); +} + +void Snowball_Sample_Sledge::Fire() { + if (fire_count_down_) { + fire_count_down_--; + } else { + 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 velocity = Rotate(glm::vec2{0.0f, 20.0f}, turret_rotation_); + GenerateBullet( + position_ + Rotate({0.0f, 1.2f}, turret_rotation_), + turret_rotation_, GetDamageScale(), velocity); + fire_count_down_ = kTickPerSecond; // Fire interval 1 second. + } + } + } +} + +const char *Snowball_Sample_Sledge::UnitName() const { + return "sledge_lyc"; +} + +const char *Snowball_Sample_Sledge::Author() const { + return "cylic14790382"; +} +} // namespace battle_game::unit \ No newline at end of file diff --git a/src/battle_game/core/units/snowball_sample_sledge.h b/src/battle_game/core/units/snowball_sample_sledge.h new file mode 100644 index 00000000..0fe26f00 --- /dev/null +++ b/src/battle_game/core/units/snowball_sample_sledge.h @@ -0,0 +1,29 @@ +#pragma once +#include "battle_game/core/unit.h" +#include "battle_game/core/units/tiny_tank.h" + +namespace battle_game::unit { +class Snowball_Sample_Sledge : public Tank { + private: + uint32_t id_{0}; + + public: + Snowball_Sample_Sledge(GameCore *game_core, uint32_t id, uint32_t player_id); + void Render() override; + void Update() override; + + protected: + void AntigravityClick(); + void Antigravity(); + void Antigravity_(); + void Antigravity_fire(); + void Fire(); + [[nodiscard]] const char *UnitName() const override; + [[nodiscard]] const char *Author() const override; + uint32_t fire_count_down_{0}; + uint32_t antigravity_count_down_{360}; + uint32_t shoot_count_down_{0}; + uint32_t hidden_count_down_{181}; + float speed_{5.0f}; +}; +} // namespace battle_game::unit \ No newline at end of file diff --git a/src/battle_game/core/units/tiny_tank.cpp b/src/battle_game/core/units/tiny_tank.cpp index 99b61f59..fe40270f 100644 --- a/src/battle_game/core/units/tiny_tank.cpp +++ b/src/battle_game/core/units/tiny_tank.cpp @@ -98,13 +98,12 @@ void Tank::TankMove(float move_speed, float rotate_angular_speed) { } 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); - } + + auto relative_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}}; + game_core_->PushEventMoveRelativeUnit(id_, relative_position); + float rotation_offset = 0.0f; if (input_data.key_down[GLFW_KEY_A]) { rotation_offset += 1.0f; @@ -113,7 +112,7 @@ void Tank::TankMove(float move_speed, float rotate_angular_speed) { rotation_offset -= 1.0f; } rotation_offset *= kSecondPerTick * rotate_angular_speed * GetSpeedScale(); - game_core_->PushEventRotateUnit(id_, rotation_ + rotation_offset); + game_core_->PushEventRotateRelativeUnit(id_, rotation_offset); } } diff --git a/src/battle_game/core/units/units.h b/src/battle_game/core/units/units.h index ebaa91e6..f6a56903 100644 --- a/src/battle_game/core/units/units.h +++ b/src/battle_game/core/units/units.h @@ -1,5 +1,4 @@ #pragma once - #include "battle_game/core/units/Whaooooo_tank.h" #include "battle_game/core/units/crit_tank.h" #include "battle_game/core/units/dark_fury.h" @@ -13,6 +12,7 @@ #include "battle_game/core/units/rebounding_sample_tank.h" #include "battle_game/core/units/round_UFO.h" #include "battle_game/core/units/smoke_bomb_tank.h" +#include "battle_game/core/units/snowball_sample_sledge.h" #include "battle_game/core/units/sparky.h" #include "battle_game/core/units/spell_caster.h" #include "battle_game/core/units/square_tank.h"