diff --git a/include/game/components.h b/include/game/components.h index fc7e81c8..f529c55d 100644 --- a/include/game/components.h +++ b/include/game/components.h @@ -8,7 +8,7 @@ #include "components/battle/transition_data.h" #include "components/battle/ui_object.h" -#include "components/audio/audio.h" +#include "components/audio/audio_component.h" #include "components/bullethell/booming.h" #include "components/bullethell/bullet.h" #include "components/bullethell/bullet_clearer.h" @@ -40,6 +40,7 @@ #include "components/rhythm/combo.h" #include "components/rhythm/holdconnect.h" #include "components/rhythm/holdstart.h" +#include "components/rhythm/judgement_line.h" #include "components/rhythm/judgetext.h" #include "components/rhythm/keyinput.h" #include "components/rhythm/lane.h" @@ -47,6 +48,5 @@ #include "components/rhythm/notestatus.h" #include "components/rhythm/notetype.h" #include "components/rhythm/timing.h" -#include "components/rhythm/judgement_line.h" #include "system/ecs.h" diff --git a/include/game/components/audio/audio.h b/include/game/components/audio/audio_component.h similarity index 97% rename from include/game/components/audio/audio.h rename to include/game/components/audio/audio_component.h index 3286bdfb..8e30825d 100644 --- a/include/game/components/audio/audio.h +++ b/include/game/components/audio/audio_component.h @@ -1,7 +1,6 @@ #pragma once +#include #include - -#include "audio.h" #include "system/asset_manager.h" namespace Game::Audio diff --git a/include/game/components/battle/bullet_data.h b/include/game/components/battle/bullet_data.h index 209b3b7e..ba2f5471 100644 --- a/include/game/components/battle/bullet_data.h +++ b/include/game/components/battle/bullet_data.h @@ -1,8 +1,10 @@ #pragma once #include -#include "../audio/audio.h" +#include "../audio/audio_component.h" +#include "game/components/bullethell/pattern.h" #include "game/components/physics/base_collider.h" +#include "maths/point.h" namespace Game::Battle { @@ -97,26 +99,65 @@ namespace Game::Battle {} }; + struct BulletMovementData + { + float posX, posY; + float vel, rot; + float acc, wvel; + + BulletMovementData(const float posX = 0, + const float posY = 0, + const float vel = 0, + const float rot = 0, + const float acc = 0, + const float wvel = 0): + posX(posX), posY(posY), vel(vel), rot(rot), acc(acc), wvel(wvel){} + }; + + struct BulletTimingData + { + int delay_frame; + int lifetime; + + BulletTimingData(const int delay_frame = 0, const int lifetime = 5000): delay_frame(delay_frame), lifetime(lifetime){} + }; + struct BulletGraphicMap { ColliderData collider_data; GraphicData graphic_data; - SpecialBulletData special_bullet_data; - float damage_mul; - int pierce; - BulletGraphicMap() : damage_mul(1), pierce(1) + BulletGraphicMap() {} explicit BulletGraphicMap( const ColliderData &collider_data, - const GraphicData &graphic_data = {}, + const GraphicData &graphic_data = {}) : + collider_data(collider_data), + graphic_data(graphic_data) + {} + }; + + struct StageBulletData + { + int graphicID; + SpecialBulletData special_bullet_data; + BulletTimingData bullet_timing_data; + Math::Point size; + float damage_mul; + int pierce; + + explicit StageBulletData( + const int graphicID = 0, const SpecialBulletData &special_bullet_data = {}, + const BulletTimingData &bullet_timing_data = {}, + const Math::Point size = Math::Point(1, 1), const float damage_mul = 1, const int pierce = 1) : - collider_data(collider_data), - graphic_data(graphic_data), special_bullet_data(special_bullet_data), + graphicID(graphicID), + bullet_timing_data(bullet_timing_data), + size(size), damage_mul(damage_mul), pierce(pierce) {} @@ -125,101 +166,40 @@ namespace Game::Battle struct BulletRegistry { std::vector bulletGraphicMaps; + std::vector bulletStageMaps; BulletRegistry() {}; explicit BulletRegistry(std::vector& bulletGraphicMaps) : bulletGraphicMaps(std::move(bulletGraphicMaps)) {} + explicit BulletRegistry(std::vector& bulletGraphicMaps, std::vector& bulletStageMaps) : + bulletGraphicMaps(std::move(bulletGraphicMaps)), bulletStageMaps(std::move(bulletStageMaps)) + {} }; struct BulletData { - float posX, posY; - float vel, rot; - float acc, wvel; - uint16_t patternID; - int delay_frame; - int lifetime; - int graphicID; - BulletData( - const float posX, - const float posY, - const int delay_frame, - const int lifetime, - const int graphicID) : - posX(posX), - posY(posY), - vel(0), - rot(0), - acc(0), - wvel(0), - patternID(0), - delay_frame(delay_frame), - lifetime(lifetime), - graphicID(graphicID) - {} - BulletData( - const float posX, - const float posY, - const float vel, - const float rot, - const int delay_frame, - const int lifetime, - const int graphicID) : - posX(posX), - posY(posY), - vel(vel), - rot(rot), - acc(0), - wvel(0), - patternID(0), - delay_frame(delay_frame), - lifetime(lifetime), - graphicID(graphicID) - {} - BulletData( - const float posX, - const float posY, - const float vel, - const float rot, - const uint16_t patternID, - const int delay_frame, - const int lifetime, - const int graphicID) : - posX(posX), posY(posY), vel(vel), rot(rot), acc(0), wvel(0), patternID(patternID), delay_frame(delay_frame), lifetime(lifetime), graphicID(graphicID) - {} - BulletData( - const float posX, - const float posY, - const float vel, - const float rot, - const float acc, - const float wvel, - const int delay_frame, - const int lifetime, - const int graphicID) : - posX(posX), - posY(posY), - vel(vel), - rot(rot), - acc(acc), - wvel(wvel), - patternID(0), - delay_frame(delay_frame), - lifetime(lifetime), - graphicID(graphicID) - {} + BulletMovementData movement_data; + BulletHell::Pattern pattern; + int bullet_id; + + BulletData() = default; BulletData( - const float posX, - const float posY, - const float vel, - const float rot, - const float acc, - const float wvel, - const uint16_t patternID, - const int delay_frame, - const int lifetime, - const int graphicID) : - posX(posX), posY(posY), vel(vel), rot(rot), acc(acc), wvel(wvel), patternID(patternID), delay_frame(delay_frame), lifetime(lifetime), graphicID(graphicID) + const int bullet_id, + const float posX = 0, + const float posY = 0, + const float speed = 0, + const float rot = 0, + const float acc = 0, + const float vwel = 0, + const uint16_t pattern_id = 0, + const float p0 = 0, + const float p1 = 0, + const float p2 = 0, + const float p3 = 0 + ) : + movement_data({posX,posY, speed, rot,acc,vwel}), + pattern({pattern_id,p0,p1,p2,p3}), + bullet_id(bullet_id) {} }; @@ -232,6 +212,7 @@ namespace Game::Battle struct BulletLoader { // this should be global. + bool initialized = false; int current_frame; int pointer; diff --git a/include/game/components/battle/pattern_container.h b/include/game/components/battle/pattern_container.h index 0432347b..5faa45ea 100644 --- a/include/game/components/battle/pattern_container.h +++ b/include/game/components/battle/pattern_container.h @@ -1,11 +1,14 @@ #pragma once +#include namespace Game::Battle { enum PatternOp : uint8_t { OP_SET, - OP_ADD + OP_ADD, + OP_MULT, + OP_PARAM }; constexpr size_t MAX_PATTERNS = 128; constexpr size_t MAX_PATTERN_SEQUENCE = 16; @@ -45,6 +48,7 @@ namespace Game::Battle std::vector pattern_sequences; std::vector pattern_steps; + PatternContainer() = default; PatternContainer( std::vector steps, std::vector seqs) diff --git a/include/game/components/bullethell/pattern.h b/include/game/components/bullethell/pattern.h index 5f0b2474..d9507e09 100644 --- a/include/game/components/bullethell/pattern.h +++ b/include/game/components/bullethell/pattern.h @@ -10,12 +10,19 @@ namespace Game::BulletHell int sequenceIdx = -1; // Would not be good but require for init int delay = 0; - Pattern() : sequenceID(0) + float params[4]; + + Pattern() : sequenceID(0), params{} {} - explicit Pattern( - const uint16_t sequenceID) : - sequenceID(sequenceID) + Pattern(const uint16_t sequenceID, + const float p0 = 0, + const float p1 = 0, + const float p2 = 0, + const float p3 = 0) : sequenceID(sequenceID), params{p0,p1,p2,p3} {} + + + //TODO : Constructor with params }; } // namespace Game::BulletHell diff --git a/include/game/systems/bullethell/bullet_load_system.h b/include/game/systems/bullethell/bullet_load_system.h index 92117d4a..4c0beea0 100644 --- a/include/game/systems/bullethell/bullet_load_system.h +++ b/include/game/systems/bullethell/bullet_load_system.h @@ -1,26 +1,31 @@ #pragma once -#include "../../components/audio/audio.h" +#include "../../components/audio/audio_component.h" #include "game/components.h" -// TODO : Render stuffs namespace Game::BulletHell { + // Refactor later template void spawn_bullet(T &syscall, const Battle::BulletData& bullet_data, const Battle::BulletRegistry& bullet_registry, const Battle::BulletHellState bhs, const Audio::SoundRegistry sound_registry) { - if (bullet_data.graphicID >= bullet_registry.bulletGraphicMaps.size()) return; + if (bullet_data.bullet_id >= bullet_registry.bulletStageMaps.size()) return; - const System::ECS::pid bullet = syscall.create_entity(Render::Transform(bullet_data.posX,bullet_data.posY), Delay(bullet_data.delay_frame)); + const auto &bullet_stage_data = bullet_registry.bulletStageMaps[bullet_data.bullet_id]; + const auto &bullet_graphic_data = bullet_registry.bulletGraphicMaps[bullet_stage_data.graphicID]; - syscall.add_components(bullet, Rotation(bullet_data.rot,true), Velocity(bullet_data.vel), Acceleration(bullet_data.acc), AngularVelocity(bullet_data.wvel), Pattern(bullet_data.patternID)); + auto &bullet_movement = bullet_data.movement_data; + auto &bullet_timing = bullet_stage_data.bullet_timing_data; - const Battle::BulletGraphicMap bullet_info = bullet_registry.bulletGraphicMaps[bullet_data.graphicID]; - syscall.add_components(bullet, Bullet(static_cast(bullet_info.damage_mul * static_cast(bhs.damage)), bullet_info.pierce), Particle(bullet_data.lifetime)); + const System::ECS::pid bullet = syscall.create_entity(Render::Transform(bullet_movement.posX,bullet_movement.posY,0,0,0,bullet_stage_data.size.x,bullet_stage_data.size.y,1), Delay(bullet_timing.delay_frame)); - if (bullet_info.special_bullet_data.type == Battle::Booming) syscall.add_components(bullet, Booming(bullet_info.special_bullet_data.size, bullet_info.special_bullet_data.frame)); - else if (bullet_info.special_bullet_data.type == Battle::Laser) syscall.add_components(bullet, Laser(bullet_data.posX, bullet_data.posY, bullet_info.special_bullet_data.size, bullet_info.special_bullet_data.frame)); + syscall.add_components(bullet, Rotation(bullet_movement.rot,true), Velocity(bullet_movement.vel), Acceleration(bullet_movement.acc), AngularVelocity(bullet_movement.wvel), Pattern(bullet_data.pattern)); - const Battle::GraphicData bullet_graphic = bullet_info.graphic_data; + syscall.add_components(bullet, Bullet(static_cast(bullet_stage_data.damage_mul * static_cast(bhs.damage)), bullet_stage_data.pierce), Particle(bullet_timing.lifetime)); + + if (bullet_stage_data.special_bullet_data.type == Battle::Booming) syscall.add_components(bullet, Booming(bullet_stage_data.special_bullet_data.size, bullet_stage_data.special_bullet_data.frame)); + else if (bullet_stage_data.special_bullet_data.type == Battle::Laser) syscall.add_components(bullet, Laser(bullet_movement.posX, bullet_movement.posY, bullet_stage_data.special_bullet_data.size, bullet_stage_data.special_bullet_data.frame)); + + const Battle::GraphicData &bullet_graphic = bullet_graphic_data.graphic_data; syscall.add_components(bullet, Render::Sprite{ .sp = get_assets_record_ptr(get_assets_id("bullet_sprite")), .pos = {{bullet_graphic.dest_rect[0], bullet_graphic.dest_rect[3], 0}, {bullet_graphic.dest_rect[2], bullet_graphic.dest_rect[3], 0}, {bullet_graphic.dest_rect[2], bullet_graphic.dest_rect[1], 0}, {bullet_graphic.dest_rect[0], bullet_graphic.dest_rect[1], 0}}, @@ -30,21 +35,21 @@ namespace Game::BulletHell .u1 = static_cast(bullet_graphic.src_rect[2])/512, .v1 = static_cast(bullet_graphic.src_rect[3])/512} , Render::Material(get_assets_record_ptr(get_assets_id("sprite_vs")),get_assets_record_ptr(get_assets_id("sprite_ps")), {bullet_graphic.r, bullet_graphic.g, bullet_graphic.b, bullet_graphic.a})); - if (const Battle::ColliderData bullet_collider = bullet_info.collider_data; + if (const Battle::ColliderData &bullet_collider = bullet_graphic_data.collider_data; bullet_collider.type == Physics::RECTANGLE) syscall.add_components(bullet, Physics::RectangularCollider(bullet_collider.offsetX,bullet_collider.offsetY, bullet_collider.colX, bullet_collider.colY)); else if (bullet_collider.type == Physics::CIRCLE) syscall.add_components(bullet, Physics::CircularCollider(bullet_collider.offsetX,bullet_collider.offsetY, bullet_collider.colX, bullet_collider.colY)); - auto sounds = sound_registry.audios; - switch (bullet_info.graphic_data.bullet_spawn_sound) + auto &sounds = sound_registry.audios; + switch (bullet_graphic_data.graphic_data.bullet_spawn_sound) { case 1 : - // Audio::audio_play(sounds["sound_bullet_spawn_0"]); + // Audio::audio_play(sounds.at("sound_bullet_spawn_0")); break; case 2 : - // Audio::audio_play(sounds["sound_bullet_spawn_1"]); + // Audio::audio_play(sounds.at("sound_bullet_spawn_1")); break; case 3 : - // Audio::audio_play(sounds["sound_bullet_spawn_2"]); + Audio::audio_play(sounds.at("sound_bullet_spawn_2")); break; default:; } @@ -63,6 +68,18 @@ namespace Game::BulletHell auto &pointer = bullet_loader.pointer; auto &batches = bullet_loader.batches; const auto ¤t_frame = query3.front().get().clock_time / 1000; + + // Sort bullet spawn frame + if (!bullet_loader.initialized) + { + std::sort(batches.begin(), batches.end(), + [](const Battle::BulletBatch & a, const Battle::BulletBatch& b) + { + return a.frame < b.frame; + }); + bullet_loader.initialized = true; + } + while ((pointer < static_cast(batches.size())) && (batches[pointer].frame <= current_frame)) { for (auto& b : batches[pointer].bullets) diff --git a/include/game/systems/bullethell/pattern_system.h b/include/game/systems/bullethell/pattern_system.h index f008fa66..383ebce1 100644 --- a/include/game/systems/bullethell/pattern_system.h +++ b/include/game/systems/bullethell/pattern_system.h @@ -50,10 +50,7 @@ namespace Game::BulletHell } if (patt_c.delay > 0) - { - LOG_INFO("%d",patt_c.delay); patt_c.delay -= static_cast(get_delta_time()); - } else { diff --git a/include/game/utils.h b/include/game/utils.h index 4f538795..cff472f4 100644 --- a/include/game/utils.h +++ b/include/game/utils.h @@ -1,8 +1,7 @@ #pragma once #include "utils/audio_util.h" -#include "utils/physics_util.h" -#include "utils/bullet_data_reader.h" #include "utils/constant.h" +#include "utils/physics_util.h" -#include "utils/Bullethell_DSL/interpreter.h" +#include "utils/Bullethell_DSL/bullet_script.h" diff --git a/include/game/utils/Bullethell_DSL/bullet_data_reader.h b/include/game/utils/Bullethell_DSL/bullet_data_reader.h new file mode 100644 index 00000000..a9f6b72e --- /dev/null +++ b/include/game/utils/Bullethell_DSL/bullet_data_reader.h @@ -0,0 +1,15 @@ +#pragma once + +#include "game/components/battle/bullet_data.h" +#include "game/components/battle/pattern_container.h" + +namespace Game::BulletHell +{ + Battle::BulletRegistry read_bullet_graphic_data_from_file(const char *filepath); + + void read_bullet_stage_data(const std::string& content, Battle::BulletRegistry &bullet_registry); + + std::vector parse_pattern_sequences(const std::string& source); + + std::vector parse_pattern_step(const std::string& source); +} diff --git a/include/game/utils/Bullethell_DSL/bullet_script.h b/include/game/utils/Bullethell_DSL/bullet_script.h new file mode 100644 index 00000000..bb892e71 --- /dev/null +++ b/include/game/utils/Bullethell_DSL/bullet_script.h @@ -0,0 +1,31 @@ +#pragma once +#include "game/components/battle/bullet_data.h" +#include "game/components/battle/pattern_container.h" +#include "interpreter.h" + +namespace Game::BulletHell +{ + class BulletScript + { + public: + Battle::BulletRegistry bullet_registry; + Battle::PatternContainer pattern_container; + Battle::BulletLoader bullet_loader; + explicit BulletScript(const char *graphic_path) + { + init_bullet_graphic(graphic_path); + } + explicit BulletScript(const char *graphic_path, const char *filepath) + { + init_bullet_graphic(graphic_path); + read_dsl_from_file(filepath); + } + void init_bullet_graphic(const char *filepath); + void read_dsl_from_file(const char *filepath); + private: + const char *graphic_path = nullptr; + const char *filepath = nullptr; + DSL::BulletHellCompiler complier; + }; + +} diff --git a/include/game/utils/Bullethell_DSL/interpreter.h b/include/game/utils/Bullethell_DSL/interpreter.h index 81ef8484..0f60748b 100644 --- a/include/game/utils/Bullethell_DSL/interpreter.h +++ b/include/game/utils/Bullethell_DSL/interpreter.h @@ -1,3 +1,105 @@ #pragma once +#include +#include "game/components/battle/battlestate.h" +#include "game/components/battle/bullet_data.h" #include "parser.h" -#include "tokenizer.h" + +namespace DSL +{ + using Value = std::variant; + + struct BuiltinInfo + { + int min_args; + int max_args; + }; + + inline const std::unordered_set BUILTIN_CONFIG = { + "@BPM" + }; + + inline const std::unordered_set BUILTIN_CONSTANTS = { + "BOX_LEFT", "BOX_RIGHT", "BOX_UP", "BOX_DOWN", "HALF_WIDTH", "HALF_HEIGHT", "BEAT" + }; + + inline const std::unordered_map BUILTIN_FUNCTIONS = { + {"rand", {0,2}}, + {"sin", {1,1}}, + {"cos", {1,1}}, + {"tan", {1,1}}, + {"sqrt", {1,1}}, + {"abs", {1,1}}, + {"round", {1,1}}, + {"ceil", {1,1}}, + {"floor", {1,1}}, + }; + struct Scope + { + Scope* parent = nullptr; + std::unordered_map variables; + Value* find(const std::string &name) + { + const auto it = variables.find(name); + + if (it != variables.end()) + return &it->second; + + if (parent) + return parent->find(name); + + return nullptr; + } + const Value* find(const std::string& name) const{ + const auto it = variables.find(name); + + if (it != variables.end()) + return &it->second; + + if (parent) + return parent->find(name); + + return nullptr; + } + }; + class Validator + { + public: + void validate(const AST& ast); + Scope scope; + + private: + void validate_stmt(const Statement& stmt, Scope &scope); + void validate_expr(const Expr& expr, Scope &scope, const size_t &line); + + static void validate_variable(const VariableExpr &var, Scope &scope, const size_t &line); + void validate_assign(const AssignStatement& stmt,Scope &scope, const size_t &line); + void validate_ascent(const AscentStatement& stmt, Scope &scope, const size_t &line); + void validate_function(const FunctionExpr& func, Scope &scope, const size_t &line); + }; + + class BulletHellCompiler + { + public: + Scope global; + Game::Battle::BulletLoader extract_bullets(const std::string &content); + + + private: + // Cache + std::vector tokens; + AST ast; + Game::Battle::BulletLoader bullet_reg; + size_t pos = 0; + float bpm = -1; + void compile_statement(const Statement& stmt, Scope& scope); + void compile_if(const IfStatement& stmt, Scope& scope, const size_t &line); + void compile_ascent(const AscentStatement &stmt, Scope &scope, const size_t &line); + void compile_spawn(const SpawnStatement &stmt, Scope &scope, const size_t &line); + void compile_assign(const AssignStatement& stmt, Scope& scope, const size_t &line); + Value eval_expr(const Expr& expr, Scope& scope, const size_t &line); + Value eval_function(const FunctionExpr& func, Scope& scope, const size_t &line); + Value eval_binary(const BinaryExpr& expr, Scope& scope, const size_t &line); + }; +} + + diff --git a/include/game/utils/Bullethell_DSL/parser.h b/include/game/utils/Bullethell_DSL/parser.h index 2bc9e476..f235c0db 100644 --- a/include/game/utils/Bullethell_DSL/parser.h +++ b/include/game/utils/Bullethell_DSL/parser.h @@ -1,119 +1,130 @@ #pragma once -#include +#include +#include + #include "tokenizer.h" -enum class Op { - SET, - ADD, - SELECT, - FIRE, - SLEEP, - LOOP_START, - LOOP_END, - INIT, - PUSH, - CLEAR, - IF, - ELSE, - END_IF -}; - -struct Instruction { - Op op; - std::vector args; - int jump = -1; -}; - -struct Parser +namespace DSL { - std::vector &tokens; - size_t pos = 0; - size_t line = 1; + struct Expr; - Token ¤t() + struct NumberExpr { - if (pos >= tokens.size()) - return tokens.back(); - return tokens[pos]; - } - Token &advance() + float value; + }; + + struct VariableExpr { - return tokens[pos++]; - } + std::string name; + }; - std::string expect_ident(bool optional = false) { - Token &t = current(); - if (t.type != Token_Type::T_IDENT) - { - if (optional) return ""; - throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected identifier"); - } - return advance().text; - } + struct BooleanExpr + { + bool value; + }; + struct UnaryExpr + { + Op_Type op; + std::unique_ptr value; + }; - std::string expect_expression(bool optional = false) { - Token &t = current(); + struct BinaryExpr + { + Op_Type op; + std::unique_ptr left; + std::unique_ptr right; + }; - if (t.type == Token_Type::T_EXP) - return advance().text; - if (optional) return ""; + struct FunctionExpr + { + std::string name; + std::vector> args; + }; - throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected expression"); - } + using ExprVariant = std::variant; - std::string expect_value(bool optional = false) { - Token &t = current(); + struct Expr + { + ExprVariant expr; + template + explicit Expr(T v) : expr(std::move(v)) {} + }; - if (t.type == Token_Type::T_NUM || - t.type == Token_Type::T_IDENT || - t.type == Token_Type::T_EXP) - return advance().text; - if (optional) return ""; + struct Statement; - throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected value"); - } + struct SpawnStatement + { + std::vector> args; + }; - Instruction parse_line() { - Token cmd = advance(); + struct AssignStatement + { + std::string name; + Op_Type op; + std::unique_ptr value; + }; - if (cmd.type != Token_Type::T_IDENT) - throw std::runtime_error("DSL line " + std::to_string(line) + " error : Shouldn't you start with Operation?"); + struct AscentStatement + { + std::string iterator; + std::unique_ptr start; + std::unique_ptr end; + std::vector> body; + }; - if (cmd.text == "SET") return {Op::SET, {expect_ident(), expect_value()}}; - if (cmd.text == "ADD") return {Op::ADD, {expect_ident(), expect_value()}}; - if (cmd.text == "SELECT") return {Op::SELECT, {expect_ident(), expect_value(true), expect_value(true)}}; - if (cmd.text == "LOOP_START") return {Op::LOOP_START, {}}; - if (cmd.text == "LOOP_END") return {Op::LOOP_END, {expect_ident(), expect_value()}}; - if (cmd.text == "SLEEP") return {Op::SLEEP, {expect_value()}}; - if (cmd.text == "FIRE") return {Op::FIRE, {expect_value(true), expect_value(true)}}; - if (cmd.text == "CLEAR") return {Op::CLEAR, {expect_ident(), expect_value(true), expect_value(true)}}; - if (cmd.text == "IF") return {Op::IF, {expect_expression()}}; - if (cmd.text == "ELSE") return {Op::ELSE, {}}; - if (cmd.text == "END_IF") return {Op::END_IF, {}}; - if (cmd.text == "INIT") return {Op::INIT, {expect_ident(), expect_value(true), expect_value(true)}}; - if (cmd.text == "PUSH") return {Op::PUSH, {expect_ident()}}; + struct BlockStatement + { + std::vector> body; + }; + struct IfBranch + { + std::unique_ptr condition; + std::vector> body; + }; + struct IfStatement + { + std::vector branches; + std::vector> else_body; + }; + + using StmtVariant = std::variant< + SpawnStatement, + AssignStatement, + AscentStatement, + BlockStatement, + IfStatement + >; + + struct Statement + { + size_t line; + StmtVariant stmt; + template + explicit Statement(T v, size_t line) : stmt(std::move(v)), line(line) {} + }; - throw std::runtime_error("DSL line " + std::to_string(line) + " error : What is that operation?"); - } + struct AST + { + std::vector> statements; + }; - std::vector parse() { - std::vector program; + std::unique_ptr extract_binary_expr(const std::vector& tokens, size_t& pos, size_t& line, int min_order = 0); + std::unique_ptr extract_function_expr(const std::vector& tokens, size_t& pos, size_t& line); - while (current().type != Token_Type::T_END) { - while (current().type == Token_Type::T_NEWLINE) { - advance(); - line++; - } + std::unique_ptr extract_primary_expr(const std::vector& tokens, size_t& pos, size_t& line); - if (current().type == Token_Type::T_END) break; + std::unique_ptr parse_block(const std::vector &tokens, size_t &pos, size_t &line); + std::unique_ptr parse_if(const std::vector &tokens, size_t &pos, size_t &line); + std::unique_ptr parse_ascent(const std::vector &tokens, size_t &pos, size_t &line); - program.push_back(parse_line()); - } + std::unique_ptr parse_spawn(const std::vector &tokens, size_t &pos, size_t &line); + std::unique_ptr parse_assign(const std::vector &tokens, size_t &pos,size_t &line); + std::unique_ptr parse_bpm(const std::vector &tokens, size_t &pos,size_t &line); - return program; - } + std::unique_ptr parse_line(const std::vector &tokens, size_t &pos, size_t &line); -}; + AST parser(const std::vector &tokens); +} diff --git a/include/game/utils/Bullethell_DSL/tokenizer.h b/include/game/utils/Bullethell_DSL/tokenizer.h index cc46f26d..1975ad1b 100644 --- a/include/game/utils/Bullethell_DSL/tokenizer.h +++ b/include/game/utils/Bullethell_DSL/tokenizer.h @@ -1,111 +1,80 @@ #pragma once -#include - -extern "C"{ - typedef unsigned long DWORD; - DWORD file_read(_Out_ FileContent **content, const char *name); - void file_free(FileContent **file); +#include +#include +#include + +namespace DSL +{ + enum class Token_Type : uint8_t { + T_END, + T_ID, + T_NUM, + T_OP, + T_SYMBOL, + T_KEYWORD, + T_NEWLINE, + T_COMMENT, + T_UNKNOWN + }; + + enum class Op_Type : uint8_t { + O_ASSIGN, + O_ADD_ASSIGN, + O_SUB_ASSIGN, + O_MUL_ASSIGN, + O_DIV_ASSIGN, + O_MOD_ASSIGN, + + O_ADD, + O_SUB, + O_MUL, + O_DIV, + O_MOD, + + O_EQ, + O_NEQ, + O_LT, + O_GT, + O_LTE, + O_GTE, + O_RANGE, + O_DOT, + O_AND, + O_OR, + O_NOT, + O_UNKNOWN + }; + + enum class Symbol_Type : uint8_t + { + S_LPAREN, + S_RPAREN, + S_LBRACE, + S_RBRACE, + S_COMMA, + S_AMPERSAND, + S_UNKNOWN + }; + + enum class Keyword_Type : uint8_t + { + K_IF, + K_ELSE, + K_ASCENT, + K_TRUE, + K_FALSE, + K_UNKNOWN + }; + + struct Token + { + Token_Type type; + std::string text; + + Op_Type op_type = Op_Type::O_UNKNOWN; + Symbol_Type symbol_type = Symbol_Type::S_UNKNOWN; + Keyword_Type keyword_type = Keyword_Type::K_UNKNOWN; + }; + + std::vector tokenize(const std::string &src); } - -enum class Token_Type : uint8_t { - T_END, - T_IDENT, - T_NUM, - T_EXP, - T_NEWLINE, - T_COMMENT, -}; - -struct Token { - Token_Type type; - std::string text; -}; - -inline std::vector tokenize(const char * filepath) { - std::vector tokens; - size_t i = 0; - size_t line = 1; - - FileContent* file_content; - file_read(&file_content, filepath); - - const std::string src(file_content->data, file_content->size); - - while (i < src.size()) { - // skip whitespace - while (i < src.size() && (src[i] == ' ' || src[i] == '\t' || src[i] == '\r')) - i++; - - if (i >= src.size()) - break; - - const char c = src[i]; - - // newline - if (c == '\n') { - tokens.push_back({Token_Type::T_NEWLINE, "\\n"}); - i++; - line++; - continue; - } - - // comment - if (c == '/' && i + 1 < src.size() && src[i + 1] == '/') { - i += 2; - while (i < src.size() && src[i] != '\n') i++; - continue; - } - - // (expression) - if (c == '(') { - int depth = 1; - const size_t start = ++i; - - while (i < src.size() && depth > 0) { - if (src[i] == '(') depth++; - else if (src[i] == ')') depth--; - i++; - } - - if (depth > 0) throw std::runtime_error("DSL line" + std::to_string(line) + " error : Wrong bracket stuffy"); - - std::string expr = src.substr(start, i - start - 1); - tokens.push_back({Token_Type::T_EXP, expr}); - continue; - } - - // number - if (isdigit(c) || (c == '-' && i + 1 < src.size() && isdigit(src[i + 1]))) { - size_t start = i++; - - while (i < src.size() && (isdigit(src[i]) || src[i] == '.')) - i++; - - tokens.push_back({ - Token_Type::T_NUM, - src.substr(start, i - start) - }); - continue; - } - - // --- identifier --- - if (isalpha(c) || c == '_') { - size_t start = i; - - while (i < src.size() && (isalnum(src[i]) || src[i] == '_')) - i++; - - tokens.push_back({ - Token_Type::T_IDENT, - src.substr(start, i - start) - }); - continue; - } - - // --- unknown character --- - throw std::runtime_error("DSL line" + std::to_string(line) + " error : What did you do?"); - } - - tokens.push_back({Token_Type::T_END, ""}); - return tokens; -} \ No newline at end of file diff --git a/include/game/utils/bullet_data_reader.h b/include/game/utils/bullet_data_reader.h deleted file mode 100644 index d0a428b4..00000000 --- a/include/game/utils/bullet_data_reader.h +++ /dev/null @@ -1,196 +0,0 @@ -#pragma once -#include -#include - -// Currently unused -> Don't know how to read file here. -// TODO : Migrate to source - -extern "C"{ - typedef unsigned long DWORD; - DWORD file_read(_Out_ FileContent **content, const char *name); - void file_free(FileContent **file); -} - -inline void CalculateCollider(const int *rect, int &p1) -{ - int width = rect[2] - rect[0]; - int height = rect[3] - rect[1]; - int size = width < height ? width : height; - int hitbox = size/2; - p1 = hitbox; -} - -inline Game::Battle::BulletRegistry read_bullet_data_from_file(const char *filepath) -{ - Game::Battle::BulletRegistry bullet_registry; - FileContent* file_content; - file_read(&file_content, filepath); - - std::string content(file_content->data, file_content->size); - std::istringstream f(content); - - std::string line; - - Game::Battle::BulletGraphicMap current; - bool inBlock = false; - - while (std::getline(f, line)) - { - line = std::regex_replace(line, std::regex("\\s+"), ""); - if (line.find("BulletData{") != std::string::npos) - { - current = Game::Battle::BulletGraphicMap{}; - inBlock = true; - continue; - } - if (line.find("}") != std::string::npos) - { - bullet_registry.bulletGraphicMaps.push_back(current); - inBlock = false; - continue; - } - if (!inBlock) - continue; - - // Necessary Info - if (line.starts_with("rect")) - { - std::string new_line = line.substr(6, line.size() - 6 + 1); - size_t p1 = new_line.find(','); - size_t p2 = new_line.find(',', p1 + 1); - size_t p3 = new_line.find(',', p2 + 1); - current.graphic_data = Game::Battle::GraphicData( - std::stoi(new_line.substr(0, p1)), - std::stoi(new_line.substr(p1 + 1, p2 - p1 - 1)), - std::stoi(new_line.substr(p2 + 1, p3 - p2 - 1)), - std::stoi(new_line.substr(p3 + 1))); - - int size; - CalculateCollider(current.graphic_data.src_rect, size); - current.collider_data = Game::Battle::ColliderData(Game::Physics::CIRCLE, static_cast(size)); - } - - else if (line.starts_with("color")) - { - auto &gd = current.graphic_data; - std::string new_line = line.substr(7, line.size() - 7 + 1); - size_t p1 = new_line.find(','); - size_t p2 = new_line.find(',', p1 + 1); - size_t p3 = new_line.find(',', p2 + 1); - gd.r = std::stof(new_line.substr(0, p1)); - gd.g = std::stof(new_line.substr(p1 + 1, p2 - p1 - 1)); - gd.b = std::stof(new_line.substr(p2 + 1, p3 - p2 - 1)); - gd.a = std::stof(new_line.substr(p3 + 1)); - } - - else if (line.starts_with("sound")) - { - auto &gd = current.graphic_data; - std::string inside = line.substr(7, line.size() - 7 + 1); - gd.bullet_spawn_sound = std::stoi(inside.substr(0)); - } - - - else if (line.starts_with("bullet_type")) - { - std::string new_line = line.substr(12, line.size() - 12 + 1); - size_t p1 = new_line.find(','); - size_t p2 = new_line.find(',', p1 + 1); - if (line.find("Booming") != std::string::npos) - { - current.special_bullet_data = Game::Battle::SpecialBulletData( - Game::Battle::Booming, - std::stof(new_line.substr(p1 + 1, p2 - p1 - 1)), - std::stoi(new_line.substr(p2 + 1))); - current.pierce = 999; - } - - if (line.find("Laser") != std::string::npos) - { - current.special_bullet_data = Game::Battle::SpecialBulletData( - Game::Battle::Laser, - std::stof(new_line.substr(p1 + 1, p2 - p1 - 1)), - std::stoi(new_line.substr(p2 + 1))); - current.pierce = 999; - } - } - - else if (line.starts_with("collider")) - { - Game::Physics::ColliderType c; - int p1, p2 = -1, p3 = -1, p4 = -1; - size_t openB; - int split = static_cast(std::count(line.begin(), line.end(), ',')); - auto rect = current.graphic_data.src_rect; - if (line.find("[") != std::string::npos) - { - c = Game::Physics::RECTANGLE; - openB = line.find("[") + 1; - } - - else if (line.find("(") != std::string::npos) - { - c = Game::Physics::CIRCLE; - openB = line.find("(") + 1; - } - else - { - continue; - } - - std::string new_line = line.substr(openB, line.size() - openB + 1); - if (new_line.size() <= 2) - CalculateCollider(rect, p1); - else - { - if (split == 0) - p1 = std::stoi(new_line); - else if (split == 1) - { - size_t pp1 = new_line.find(','); - p1 = std::stoi(new_line.substr(0, pp1)); - p2 = std::stoi(new_line.substr(pp1 + 1)); - } - else if (split == 3) - { - size_t pp1 = new_line.find(','); - size_t pp2 = new_line.find(',', pp1 + 1); - size_t pp3 = new_line.find(',', pp2 + 1); - p1 = std::stoi(new_line.substr(0, pp1)); - p2 = std::stoi(new_line.substr(pp1 + 1, pp2 - pp1 - 1)); - p3 = std::stoi(new_line.substr(pp2 + 1, pp3 - pp2 - 1)); - p4 = std::stoi(new_line.substr(pp3 + 1)); - } - else - { - p1 = 0; - LOG_ERROR("Da fuck???"); - } - } - if (p2 == -1) - current.collider_data = Game::Battle::ColliderData(c, static_cast(p1)); - else if (p3 == -1) - current.collider_data = Game::Battle::ColliderData(c, static_cast(p1), static_cast(p2)); - else - current.collider_data = Game::Battle::ColliderData( - c, - static_cast(p1), - static_cast(p2), - static_cast(p3), - static_cast(p4)); - } - - else if (line.starts_with("damage")) - { - current.damage_mul = std::stof(line.substr(7, line.size() - 7 + 1)); - } - else if (line.starts_with("pierce")) - { - current.pierce = std::stoi(line.substr(7, line.size() - 7 + 1)); - } - file_free(&file_content); - } - - return bullet_registry; -} - diff --git a/include/scene/demo_game.h b/include/scene/demo_game.h index 39641f75..5215dc7c 100644 --- a/include/scene/demo_game.h +++ b/include/scene/demo_game.h @@ -136,9 +136,6 @@ namespace Scene >; - static Game::Battle::BulletLoader create_bullet_test(); - static Game::Battle::ChartData create_note_test(); - static void load_chart( std::shared_ptr &tm, Game::Battle::ChartData &chart, diff --git a/include/scene/scene_manager.h b/include/scene/scene_manager.h index d8b301f3..5ae0680f 100644 --- a/include/scene/scene_manager.h +++ b/include/scene/scene_manager.h @@ -16,9 +16,9 @@ namespace Scene::Config { // starting scene - using StartingScene = DemoMenu; + using StartingScene = DemoGame; - using SceneTuple = std::tuple; + using SceneTuple = std::tuple; using SceneVariant = Utils::make_scene_variant_t; using TaskManagerVariant = Utils::make_task_manager_variant_t; diff --git a/src/dsl_reader/CMakeLists.txt b/src/dsl_reader/CMakeLists.txt new file mode 100644 index 00000000..6af179b7 --- /dev/null +++ b/src/dsl_reader/CMakeLists.txt @@ -0,0 +1,9 @@ +file(GLOB + DSL_READER_FILES + "./*.cpp" +) + +add_library(dsl_reader + STATIC + ${DSL_READER_FILES} +) diff --git a/src/dsl_reader/bullet_data_reader.cpp b/src/dsl_reader/bullet_data_reader.cpp new file mode 100644 index 00000000..397b8ef7 --- /dev/null +++ b/src/dsl_reader/bullet_data_reader.cpp @@ -0,0 +1,323 @@ +#include +#include +#include "game/utils/Bullethell_DSL/bullet_data_reader.h" + +extern "C"{ + typedef unsigned long DWORD; + DWORD file_read(_Out_ FileContent **content, const char *name); + void file_free(FileContent **file); +} + +using namespace Game::Battle; +using namespace Game::BulletHell; + +void CalculateCollider(const int *rect, int &p1) +{ + int width = rect[2] - rect[0]; + int height = rect[3] - rect[1]; + int size = width < height ? width : height; + int hitbox = size / 2; + p1 = hitbox; +} + +std::string trim(const std::string& s) +{ + return std::regex_replace(s, std::regex("\\s+"), ""); +} + +std::vector split(const std::string& s, const char delim) +{ + std::vector out; + + std::stringstream ss(s); + std::string item; + + while (std::getline(ss, item, delim)) + { + out.push_back(trim(item)); + } + + return out; +} + + +BulletRegistry Game::BulletHell::read_bullet_graphic_data_from_file(const char *filepath) +{ + BulletRegistry bullet_registry; + FileContent *file_content; + file_read(&file_content, filepath); + + std::string content(file_content->data, file_content->size); + std::istringstream f(content); + + std::string line; + int linecount = 0; + + BulletGraphicMap current; + bool inBlock = false; + + while (std::getline(f, line)) + { + line = trim(line); + linecount++; + if (line.find("BulletData{") != std::string::npos) + { + current = BulletGraphicMap{}; + inBlock = true; + continue; + } + if (line.find('}') != std::string::npos) + { + bullet_registry.bulletGraphicMaps.push_back(current); + inBlock = false; + continue; + } + if (!inBlock) + continue; + + // Necessary Info + if (line.starts_with("rect")) + { + std::string new_line = line.substr(6, line.size() - 6 + 1); + std::vector params = split(new_line, ','); + if (params.empty() || params.size() != 4) + throw std::runtime_error("DSL line " + std::to_string(linecount) + " error : Rect size should be 4, but it's " + std::to_string(params.size()) + "."); + + current.graphic_data = GraphicData( + std::stoi(params[0]), + std::stoi(params[1]), + std::stoi(params[2]), + std::stoi(params[3])); + + int size; + CalculateCollider(current.graphic_data.src_rect, size); + current.collider_data = ColliderData(Game::Physics::CIRCLE, static_cast(size)); + } + + else if (line.starts_with("color")) + { + auto &gd = current.graphic_data; + std::string new_line = line.substr(7, line.size() - 7 + 1); + std::vector params = split(new_line, ','); + if (params.empty() || params.size() < 3 || params.size() > 4) + throw std::runtime_error("DSL line " + std::to_string(linecount) + " error : Color size should be between 3-4, but it's " + std::to_string(params.size()) + "."); + gd.r = std::stof(params[0]); + gd.g = std::stof(params[1]); + gd.b = std::stof(params[2]); + gd.a = (params.size() == 4)? std::stof(params[3]):1; + } + + else if (line.starts_with("sound")) + { + auto &gd = current.graphic_data; + std::string inside = line.substr(7, line.size() - 7 + 1); + gd.bullet_spawn_sound = std::stoi(inside.substr(0)); + } + + else if (line.starts_with("collider")) + { + Game::Physics::ColliderType c; + + if (line.find('[') != std::string::npos) + c = Game::Physics::RECTANGLE; + + else if (line.find('(') != std::string::npos) + c = Game::Physics::CIRCLE; + else + continue; + + std::string new_line = line.substr(10, line.size() - 10 + 1); + + if (new_line.size() <= 2) + current.collider_data.type = c; + else + { + std::vector params = split(new_line, ','); + if (params.size()==1) + current.collider_data = ColliderData(c, std::stof(params[0])); + else if (params.size() == 2) + current.collider_data = ColliderData(c, std::stof(params[0]), std::stof(params[1])); + else if (params.size() == 4) + current.collider_data = ColliderData(c, std::stof(params[0]), std::stof(params[1]), std::stof(params[2]), std::stof(params[3])); + else + throw std::runtime_error("DSL line " + std::to_string(linecount) + " error : Collider size should be 1,2 or 4, but it's " + std::to_string(params.size()) + "."); + } + } + file_free(&file_content); + } + + return bullet_registry; +} + +void Game::BulletHell::read_bullet_stage_data(const std::string& content, BulletRegistry &bullet_registry) +{ + std::istringstream f(content); + + std::string line; + int linecount = 0; + + StageBulletData current; + bool inBlock = false; + + bullet_registry.bulletStageMaps.clear(); + + while (std::getline(f, line)) + { + line = trim(line); + linecount++; + if (line.find("BulletData{") != std::string::npos) + { + current = StageBulletData{}; + inBlock = true; + continue; + } + if (line.find('}') != std::string::npos) + { + bullet_registry.bulletStageMaps.push_back(current); + inBlock = false; + continue; + } + if (!inBlock) + continue; + + + if (line.starts_with("bullet_type")) + { + std::string new_line = line.substr(12, line.size() - 12 + 1); + std::vector params = split(new_line, ','); + if (params.empty() || params.size() != 3) + throw std::runtime_error("DSL line " + std::to_string(linecount) + " error : Bullet-type size should be 3, but it's " + std::to_string(params.size()) + "."); + + if (line.find("Booming") != std::string::npos) + { + current.special_bullet_data = SpecialBulletData( + Booming, + std::stof(params[1]), + std::stoi(params[2]) + ); + current.pierce = 999; + } + + if (line.find("Laser") != std::string::npos) + { + current.special_bullet_data = SpecialBulletData( + Laser, + std::stof(params[1]), + std::stoi(params[2])); + current.pierce = 999; + } + } + + else if (line.starts_with("size")) + { + std::string new_line = line.substr(6, line.size() - 6 + 1); + std::vector params = split(new_line, ','); + if (params.empty() || params.size() != 2) + throw std::runtime_error("DSL line " + std::to_string(linecount) + " error : Size size should be 2, but it's " + std::to_string(params.size()) + "."); + + current.size = Math::Point( + std::stof(params[0]), + std::stof(params[1]), 1); + } + + else if (line.starts_with("graphic")) + { + current.graphicID = std::stoi(line.substr(8, line.size() - 8 + 1)); + } + + else if (line.starts_with("damage")) + { + current.damage_mul = std::stof(line.substr(7, line.size() - 7 + 1)); + } + else if (line.starts_with("pierce")) + { + current.pierce = std::stoi(line.substr(7, line.size() - 7 + 1)); + } + else if (line.starts_with("lifetime")) + { + current.bullet_timing_data.lifetime = std::stoi(line.substr(9, line.size() - 9 + 1)); + } + else if (line.starts_with("delay")) + { + current.bullet_timing_data.delay_frame = std::stoi(line.substr(6, line.size() - 6 + 1)); + } + } +} + +std::vector Game::BulletHell::parse_pattern_sequences(const std::string& source) +{ + std::vector result; + + std::stringstream src(source); + std::string line; + + while (std::getline(src, line)) + { + line = trim(line); + + if (line.empty() || line.front() != '(' || line.back() != ')') + continue; + + line = line.substr(1, line.size() - 2); + + std::vector params = split(line, ','); + + if (params.empty()) + continue; + + bool loop = (params[0] == "true"); + + PatternSequence seq; + seq.isLoop = loop; + + for (size_t i = 1; i < params.size(); ++i) + { + seq.steps.push_back(static_cast(std::stoi(params[i]))); + } + + result.push_back(seq); + } + + return result; +} + +std::vector Game::BulletHell::parse_pattern_step(const std::string &source) +{ + std::vector result; + + std::stringstream src(source); + std::string line; + + while (std::getline(src, line)) + { + line = trim(line); + + if (line.empty() || line.front() != '(' || line.back() != ')') + continue; + + line = line.substr(1, line.size() - 2); + + std::vector params = split(line, ','); + + if (params.size() < 3) + continue; + + int delay = std::stoi(params[0]); + PatternOp op; + if (params[1]=="SET") op = OP_SET; + else if (params[1]=="ADD") op = OP_ADD; + else if (params[1]=="MULT") op = OP_MULT; + else if (params[1]=="PARAM") op = OP_PARAM; + else continue; + auto mask = static_cast(std::stoi(params[2], nullptr, 2)); + + float p1 = params.size() > 3 ? std::stof(params[3]) : 0.f; + float p2 = params.size() > 4 ? std::stof(params[4]) : 0.f; + float p3 = params.size() > 5 ? std::stof(params[5]) : 0.f; + float p4 = params.size() > 6 ? std::stof(params[6]) : 0.f; + + result.emplace_back(delay,op,mask,p1,p2,p3,p4); + } + + return result; +} diff --git a/src/dsl_reader/bullet_script.cpp b/src/dsl_reader/bullet_script.cpp new file mode 100644 index 00000000..72f146af --- /dev/null +++ b/src/dsl_reader/bullet_script.cpp @@ -0,0 +1,105 @@ +#include +#include + +#include "game/utils/Bullethell_DSL/bullet_script.h" + +#include "game/utils/Bullethell_DSL/bullet_data_reader.h" +#include "system/asset_manager.h" + +extern "C"{ + typedef unsigned long DWORD; + DWORD file_read(_Out_ FileContent **content, const char *name); + void file_free(FileContent **file); +} + +using namespace Game::BulletHell; + +bool check_header(const std::string& header) +{ + if (header == "Pattern") return true; + if (header == "PatternSeq") return true; + if (header == "BulletData") return true; + if (header == "Stage") return true; + return false; +} + +void trim_r(std::string &s) +{ + const char* ws = " \t\r"; + + size_t b = 0; + while (b < s.size() && strchr(ws, s[b])) ++b; + + size_t e = s.size(); + while (e > b && strchr(ws, s[e - 1])) --e; + + s.erase(e); + s.erase(0, b); +} + +std::unordered_map split_section(const std::string &src) +{ + std::unordered_map dsl_sections; + std::stringstream ss(src); + std::string line; + + std::string current_section; + + while (std::getline(ss, line)) + { + trim_r(line); + if (!line.empty() && line[0] == '#') + { + current_section = line.substr(1); + if (!check_header(current_section)) + throw std::runtime_error("error : Wrong header name -> " + current_section); + dsl_sections[current_section]; + continue; + } + + if (current_section.empty()) + continue; + + dsl_sections[current_section] += line + '\n'; + } + + return dsl_sections; +} + +void BulletScript::init_bullet_graphic(const char *filepath) +{ + if (graphic_path == filepath && !bullet_registry.bulletGraphicMaps.empty()) + return; + + graphic_path = filepath; + bullet_registry = read_bullet_graphic_data_from_file(graphic_path); +} + +void BulletScript::read_dsl_from_file(const char *filepath) +{ + if (this->filepath == filepath) return; + this->filepath = filepath; + + FileContent* file_content; + file_read(&file_content, filepath); + + const std::string src(file_content->data, file_content->size); + + std::unordered_map dsl_sections = split_section(src); + + pattern_container = { + parse_pattern_step(dsl_sections["Pattern"]), + parse_pattern_sequences(dsl_sections["PatternSeq"]) + }; + + read_bullet_stage_data(dsl_sections["BulletData"],bullet_registry); + + // Check for cache + + // ---- + + bullet_loader = complier.extract_bullets(dsl_sections["Stage"]); + + file_free(&file_content); +} + diff --git a/src/dsl_reader/interpreter.cpp b/src/dsl_reader/interpreter.cpp new file mode 100644 index 00000000..55a3a8ab --- /dev/null +++ b/src/dsl_reader/interpreter.cpp @@ -0,0 +1,453 @@ +#include + +#include "game/utils/Bullethell_DSL/interpreter.h" + +#include "game/utils/constant.h" +#include "game/utils/physics_util.h" +#include "utils/print_debug.h" + +using namespace DSL; + +void Validator::validate_function(const FunctionExpr& func, Scope &local, const size_t &line) +{ + const auto it = BUILTIN_FUNCTIONS.find(func.name); + + if (it == BUILTIN_FUNCTIONS.end()) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : No built-in function for " + func.name); + + const auto &[min_args, max_args] = it->second; + + if (const int count = static_cast(func.args.size()); count < min_args || count > max_args) + throw std::runtime_error( + "DSL line " + std::to_string(line) + " error : This function accept " + std::to_string(max_args) + + " arguments not " + std::to_string(count)); + + for (const auto &arg: func.args) + validate_expr(*arg, local, line); +} + +void Validator::validate_variable(const VariableExpr &var, Scope &local, const size_t &line) +{ + if (!(BUILTIN_CONSTANTS.contains(var.name) || local.find(var.name))) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Unknown variable " + var.name); +} + +void Validator::validate_assign(const AssignStatement &stmt, Scope &local, const size_t &line) +{ + if (BUILTIN_FUNCTIONS.contains(stmt.name) || BUILTIN_CONSTANTS.contains(stmt.name)) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Trying to assign constant " + stmt.name); + + if (stmt.op != Op_Type::O_ASSIGN && !local.find(stmt.name)) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Variable " + stmt.name + " not initialized"); + + validate_expr(*stmt.value, local, line); + + if (stmt.op == Op_Type::O_ASSIGN) + local.variables[stmt.name] = 0.f; +} + +void Validator::validate_ascent(const AscentStatement &stmt, Scope &parent, const size_t &line) +{ + Scope local; + local.parent = &parent; + + local.variables[stmt.iterator] = 0.f; + + validate_expr(*stmt.start, local, line); + validate_expr(*stmt.end, local, line); + + for (const auto &s: stmt.body) + validate_stmt(*s, local); +} + +void Validator::validate_expr(const Expr &expr, Scope &local, const size_t &line) +{ + std::visit([&](const auto &e) + { + using T = std::decay_t; + + if constexpr (std::is_same_v) + validate_variable(e, local, line); + else if constexpr (std::is_same_v) + validate_expr(*e.value, local, line); + else if constexpr (std::is_same_v) + { + validate_expr(*e.left, local, line); + validate_expr(*e.right, local, line); + } + else if constexpr (std::is_same_v) + validate_function(e, local, line); + }, + expr.expr); +} + +void Validator::validate_stmt(const Statement& stmt, Scope& local) +{ + std::visit([&](const auto& s) + { + using T = std::decay_t; + + if constexpr (std::is_same_v) + validate_assign(s, local, stmt.line); + + else if constexpr (std::is_same_v) + { + for (const auto& arg : s.args) + validate_expr(*arg, local, stmt.line); + } + + else if constexpr (std::is_same_v) + { + Scope local2; + local2.parent = &local; + + for (const auto& sub_stmt : s.body) + validate_stmt(*sub_stmt, local2); + } + + else if constexpr (std::is_same_v) + validate_ascent(s, local, stmt.line); + + else if constexpr (std::is_same_v) + { + for (const auto& branch : s.branches) + { + Scope local2; + local2.parent = &local; + + validate_expr(*branch.condition, local2, stmt.line); + + for (const auto& sub_stmt : branch.body) + validate_stmt(*sub_stmt, local2); + } + + Scope local2; + local2.parent = &local; + + for (const auto& sub_stmt : s.else_body) + validate_stmt(*sub_stmt, local2); + } + + }, stmt.stmt); +} + +void Validator::validate(const AST &ast) +{ + Scope global; + + for (const auto &stmt: ast.statements) + validate_stmt(*stmt, global); +} + +float as_number(const Value& v) +{ + if (std::holds_alternative(v)) return std::get(v); + if (std::holds_alternative(v)) return std::get(v) ? 1.f : 0.f; + return 0.f; +} + +bool as_bool(const Value& v) +{ + if (std::holds_alternative(v)) return std::get(v)!=0; + if (std::holds_alternative(v)) return std::get(v); + return false; +} + +Value BulletHellCompiler::eval_function(const FunctionExpr& func, Scope& scope, const size_t &line) +{ + std::vector args; + + for (const auto& arg : func.args) + args.push_back(eval_expr(*arg, scope, line)); + + if (func.name == "sin") return std::sin(as_number(args[0])); + if (func.name == "cos") return std::cos(as_number(args[0])); + if (func.name == "sqrt") + { + if (as_number(args[0]) < 0) throw std::runtime_error("DSL line " + std::to_string(line) + " error : square root negative number"); + return std::sqrt(as_number(args[0])); + } + if (func.name == "abs") return std::abs(as_number(args[0])); + if (func.name == "rand") + { + if (args.empty()) return Game::Physics::rand_float(0.f,1.f); + + if (args.size() == 2) + { + if (as_number(args[0]) > as_number(args[1])) + throw std::runtime_error("DSL line " + std::to_string(line) + " rand min > rand max??"); + return Game::Physics::rand_float(as_number(args[0]), as_number(args[1])); + } + } + if (func.name == "round") return std::round(as_number(args[0])); + if (func.name == "ceil") return std::ceil(as_number(args[0])); + if (func.name == "floor") return std::floor(as_number(args[0])); + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Unsupported function"); +} + +Value BulletHellCompiler::eval_binary(const BinaryExpr &expr, Scope& scope, const size_t &line) +{ + const Value left = eval_expr(*expr.left, scope, line); + + if (expr.op == Op_Type::O_AND) if (!as_bool(left)) return false; + if (expr.op == Op_Type::O_OR) + if (as_bool(left)) + return true; + const Value right = eval_expr(*expr.right, scope, line); + + if (expr.op == Op_Type::O_ADD) + return as_number(left) + as_number(right); + if (expr.op == Op_Type::O_SUB) + return as_number(left) - as_number(right); + if (expr.op == Op_Type::O_MUL) + return as_number(left) * as_number(right); + if (expr.op == Op_Type::O_DIV) + { + if (as_number(right) == 0) throw std::runtime_error("DSL line " + std::to_string(line) + " error : bro divide by zero"); + return as_number(left) / as_number(right); + } + if (expr.op == Op_Type::O_MOD) + { + if (as_number(right) == 0) throw std::runtime_error("DSL line " + std::to_string(line) + " error : bro modulo by zero"); + return static_cast(static_cast(as_number(left)) % static_cast(as_number(right))); + } + + if (expr.op == Op_Type::O_EQ) + return as_number(left) == as_number(right); + if (expr.op == Op_Type::O_LT) + return as_number(left) < as_number(right); + if (expr.op == Op_Type::O_GT) + return as_number(left) > as_number(right); + if (expr.op == Op_Type::O_LTE) + return as_number(left) <= as_number(right); + if (expr.op == Op_Type::O_GTE) + return as_number(left) >= as_number(right); + if (expr.op == Op_Type::O_AND) + return as_bool(left) && as_bool(right); + if (expr.op == Op_Type::O_OR) + return as_bool(left) || as_bool(right); + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Unknown operation"); +} + +Value BulletHellCompiler::eval_expr(const Expr& expr, Scope& scope, const size_t &line) +{ + return std::visit([&](const auto& e) -> Value + { + using T = std::decay_t; + + if constexpr (std::is_same_v || std::is_same_v) return e.value; + if constexpr (std::is_same_v) + { + if (e.name == "BOX_LEFT") return -Game::BOX_SIZE + Game::HALF_WIDTH / 32; + if (e.name == "BOX_RIGHT") return Game::BOX_SIZE - Game::HALF_WIDTH / 32; + if (e.name == "BOX_UP") return Game::BOX_SIZE + Game::HALF_HEIGHT / 32 - Game::HALF_HEIGHT/ 3; + if (e.name == "BOX_DOWN") return -Game::BOX_SIZE-Game::HALF_HEIGHT/32-Game::HALF_HEIGHT/3; + if (e.name == "HALF_WIDTH") return Game::HALF_WIDTH; + if (e.name == "HALF_HEIGHT") return Game::HALF_HEIGHT; + if (e.name == "BEAT") return 60000.f/ bpm; + + const Value* v = scope.find(e.name); + if (!v) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Unknown variable " + e.name); + return *v; + } + if constexpr (std::is_same_v) + { + Value value = eval_expr(*e.value, scope, line); + if (e.op == Op_Type::O_SUB) return -as_number(value); + if (e.op == Op_Type::O_ADD) return +as_number(value); + if (e.op == Op_Type::O_NOT) return !as_bool(value); + + return value; + } + if constexpr (std::is_same_v) + { + return eval_binary(e, scope, line); + } + if constexpr (std::is_same_v) + { + return eval_function(e, scope, line); + } + + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Invalid Expression"); + + }, expr.expr); +} + +void BulletHellCompiler::compile_if( + const IfStatement& stmt, + Scope& scope, const size_t &line) +{ + for (auto& branch : stmt.branches) + { + if (as_bool(eval_expr(*branch.condition, scope, line))) + { + Scope child; + child.parent = &scope; + + for (auto& s : branch.body) + compile_statement(*s, child); + + return; + } + } + + if (!stmt.else_body.empty()) + { + Scope child; + child.parent = &scope; + + for (auto& s : stmt.else_body) + compile_statement(*s, child); + } +} + +void BulletHellCompiler::compile_ascent(const AscentStatement &stmt, Scope &scope, const size_t &line) +{ + int start = static_cast(as_number(eval_expr(*stmt.start, scope, line))); + int end = static_cast(as_number(eval_expr(*stmt.end, scope, line))); + for (int i = start; i < end; i++) + { + Scope child; + child.parent = &scope; + child.variables[stmt.iterator] = static_cast(i); + + for (auto& st : stmt.body) + compile_statement(*st, child); + } +} + +void BulletHellCompiler::compile_assign(const AssignStatement& stmt, Scope& scope, const size_t &line) +{ + // if (stmt.op != Op_Type::O_ASSIGN && !scope.find(stmt.name)) + // { + // throw std::runtime_error( + // "DSL line " + std::to_string(line) + " error : Variable " + stmt.name + " not initialized"); + // } + + const Value v = eval_expr(*stmt.value, scope, line); + float num = as_number(v); + Value* var = scope.find(stmt.name); + + if (stmt.name == "@BPM") + { + bpm = num; + return; + } + + if (var) + { + if (stmt.op == Op_Type::O_ASSIGN) *var = num; + else if (stmt.op == Op_Type::O_ADD_ASSIGN) *var = as_number(*var) + num; + else if (stmt.op == Op_Type::O_SUB_ASSIGN) *var = as_number(*var) - num; + else if (stmt.op == Op_Type::O_MUL_ASSIGN) *var = as_number(*var) * num; + else if (stmt.op == Op_Type::O_DIV_ASSIGN) + { + if (num == 0) throw std::runtime_error("DSL line " + std::to_string(line) + " error : bro divide by zero"); + *var = as_number(*var) / num; + } + else if (stmt.op == Op_Type::O_MOD_ASSIGN) + { + if (num == 0) throw std::runtime_error("DSL line " + std::to_string(line) + " error : bro divide by zero"); + *var = (float)((int)as_number(*var) % (int)num); + } + } + else if (stmt.op == Op_Type::O_ASSIGN) + { + scope.variables[stmt.name] = num; + } +} + +void BulletHellCompiler::compile_statement(const Statement& stmt, Scope& scope) +{ + std::visit([&](auto&& s) + { + using T = std::decay_t; + + if constexpr (std::is_same_v) + compile_spawn(s, scope ,stmt.line); + + else if constexpr (std::is_same_v) + compile_assign(s, scope,stmt.line); + + else if constexpr (std::is_same_v) + compile_if(s, scope,stmt.line); + + else if constexpr (std::is_same_v) + compile_ascent(s, scope,stmt.line); + + else if constexpr (std::is_same_v) + { + Scope child; + child.parent = &scope; + for (auto& st : s.body) + compile_statement(*st, child); + } + + }, stmt.stmt); +} + +int ComputeBeat(const float bpm, const float beat) +{ + const float time_per_beat = 60000.f/ bpm; + return static_cast(std::round(beat * time_per_beat)); +} + +void BulletHellCompiler::compile_spawn(const SpawnStatement& stmt, Scope& scope, const size_t &line) +{ + std::vector args; + + for (auto &e: stmt.args) + { + Value v = eval_expr(*e, scope, line); + args.push_back(as_number(v)); + } + if (bpm == -1) + { + throw std::runtime_error("DSL line " + std::to_string(line) + " error : BPM not config"); + } + const int spawn_frame = ComputeBeat(bpm, args[0]); + + // Scalability is my passion. + if (args.size() == 4) bullet_reg.CreateBullet(spawn_frame,Game::Battle::BulletData{static_cast(args[1]),args[2],args[3]}); + else if (args.size() == 5) bullet_reg.CreateBullet(spawn_frame,Game::Battle::BulletData{static_cast(args[1]),args[2],args[3],args[4]}); + else if (args.size() == 6) bullet_reg.CreateBullet(spawn_frame,Game::Battle::BulletData{static_cast(args[1]),args[2],args[3],args[4],args[5]}); + else if (args.size() == 7) bullet_reg.CreateBullet(spawn_frame,Game::Battle::BulletData{static_cast(args[1]),args[2],args[3],args[4], args[5],args[6]}); + else if (args.size() == 8) bullet_reg.CreateBullet(spawn_frame,Game::Battle::BulletData{static_cast(args[1]),args[2],args[3],args[4],args[5], args[6],args[7]}); + else if (args.size() == 9) bullet_reg.CreateBullet(spawn_frame,Game::Battle::BulletData{static_cast(args[1]),args[2],args[3],args[4],args[5], args[6],args[7] ,static_cast(args[8])}); + else if (args.size() == 10) bullet_reg.CreateBullet(spawn_frame,Game::Battle::BulletData{static_cast(args[1]),args[2],args[3],args[4],args[5], args[6],args[7] ,static_cast(args[8]), args[9]}); + else if (args.size() == 11) bullet_reg.CreateBullet(spawn_frame,Game::Battle::BulletData{static_cast(args[1]),args[2],args[3],args[4],args[5], args[6],args[7] ,static_cast(args[8]), args[9], args[10]}); + else if (args.size() == 12) bullet_reg.CreateBullet(spawn_frame,Game::Battle::BulletData{static_cast(args[1]),args[2],args[3],args[4],args[5], args[6],args[7] ,static_cast(args[8]), args[9], args[10], args[11]}); + else if (args.size() == 13) bullet_reg.CreateBullet(spawn_frame,Game::Battle::BulletData{static_cast(args[1]),args[2],args[3],args[4],args[5], args[6],args[7] ,static_cast(args[8]), args[9], args[10], args[11], args[12]}); + else throw std::runtime_error("DSL line " + std::to_string(line) + " error : Invalid spawn argument count"); +} + +Game::Battle::BulletLoader BulletHellCompiler::extract_bullets(const std::string &content) +{ + tokens = tokenize(content); + ast = parser(tokens); + + bpm = -1; + bullet_reg = {}; + global = {}; + + Validator validator; + validator.validate(ast); + + // Currently doesn't convert beat -> millisecond + for (auto& stmt : ast.statements) + { + compile_statement(*stmt, global); + } + + // for (auto &[frame, bullets] : bullet_reg.batches) + // { + // for (const auto & b : bullets) + // LOG_INFO("A bullet no.%d spawns at frame %d at pos (%d,%d) with speed %d angle %d acc %d wvel %d with pattern %d", + // b.bullet_id, frame, (int)b.movement_data.posX, (int)b.movement_data.posY, (int)b.movement_data.vel, (int)b.movement_data.rot, + // (int)b.movement_data.acc, (int)b.movement_data.wvel, b.pattern.sequenceID); + // + // } + + return bullet_reg; +} diff --git a/src/dsl_reader/parser.cpp b/src/dsl_reader/parser.cpp new file mode 100644 index 00000000..1a8e666c --- /dev/null +++ b/src/dsl_reader/parser.cpp @@ -0,0 +1,457 @@ +#include "game/utils/Bullethell_DSL/parser.h" + +#include + +#include "utils/print_debug.h" + +using namespace DSL; + +int get_order(const Op_Type op) +{ + switch(op) + { + case Op_Type::O_MUL: + case Op_Type::O_DIV: + case Op_Type::O_MOD: + return 20; + + case Op_Type::O_ADD: + case Op_Type::O_SUB: + return 10; + + case Op_Type::O_EQ: + case Op_Type::O_NEQ: + case Op_Type::O_LT: + case Op_Type::O_GT: + case Op_Type::O_LTE: + case Op_Type::O_GTE: + return 5; + + case Op_Type::O_AND: + return 3; + + case Op_Type::O_OR: + return 2; + + default: + return -1; + } +} + +void validate_eof(const std::vector &tokens, size_t &pos, size_t &line) +{ + if (pos >= tokens.size() || tokens[pos].type == Token_Type::T_END) + { + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Unexpected End Of File"); + } +} + +void skip_newline(const std::vector &tokens, size_t &pos, size_t &line) +{ + while (tokens[pos].type == Token_Type::T_NEWLINE) + { + pos++; + line++; + } + validate_eof(tokens, pos, line); +} + +std::unique_ptr DSL::extract_binary_expr(const std::vector& tokens, size_t& pos, size_t& line, int min_order) +{ + validate_eof(tokens, pos, line); + auto left = extract_primary_expr(tokens, pos, line); + while (true) + { + if (tokens[pos].type != Token_Type::T_OP) + break; + + const Op_Type op = tokens[pos].op_type; + const int order = get_order(op); + + if (order < min_order) + break; + + pos++; + auto right = extract_binary_expr(tokens,pos,line,order + 1); + left = std::make_unique(BinaryExpr{ op, std::move(left), std::move(right) }); + } + + return left; +} + +std::unique_ptr DSL::extract_function_expr(const std::vector& tokens, size_t& pos, size_t& line) +{ + validate_eof(tokens, pos, line); + FunctionExpr func; + func.name = tokens[pos].text; + + pos += 2; + + while (true) + { + validate_eof(tokens, pos, line); + + if (tokens[pos].type == Token_Type::T_SYMBOL && tokens[pos].symbol_type == Symbol_Type::S_RPAREN) + { + pos++; + break; + } + + func.args.push_back( + extract_binary_expr(tokens, pos, line, 0)); + validate_eof(tokens, pos, line); + + if (tokens[pos].type == Token_Type::T_SYMBOL && tokens[pos].symbol_type == Symbol_Type::S_COMMA) + { + pos++; + continue; + } + + if (tokens[pos].type == Token_Type::T_SYMBOL && tokens[pos].symbol_type == Symbol_Type::S_RPAREN) + { + pos++; + break; + } + + throw std::runtime_error( + "DSL line " + std::to_string(line) + " error : Expected , or )"); + } + + return std::make_unique(std::move(func)); +} + +std::unique_ptr DSL::extract_primary_expr(const std::vector& tokens, size_t& pos, size_t& line) +{ + validate_eof(tokens, pos, line); + const Token& token = tokens[pos]; + + // number + if (token.type == Token_Type::T_NUM) + { + pos++; + return std::make_unique(NumberExpr{std::stof(token.text)}); + } + + // boolean + if (token.type == Token_Type::T_KEYWORD) + { + if (token.keyword_type == Keyword_Type::K_TRUE) + { + pos++; + return std::make_unique(BooleanExpr{true}); + } + + if (token.keyword_type == Keyword_Type::K_FALSE) + { + pos++; + return std::make_unique(BooleanExpr{false}); + } + } + + // identifier / function + if (token.type == Token_Type::T_ID) + { + // function + if (pos+1 < tokens.size() && tokens[pos + 1].type == Token_Type::T_SYMBOL && tokens[pos + 1].symbol_type == Symbol_Type::S_LPAREN) + return extract_function_expr(tokens, pos, line); + + // variable + pos++; + return std::make_unique(VariableExpr{token.text}); + } + + // unary + if (token.type == Token_Type::T_OP) + { + if (token.op_type == Op_Type::O_SUB || token.op_type == Op_Type::O_ADD || token.op_type == Op_Type::O_NOT) + { + const Op_Type op = token.op_type; + pos++; + auto value = extract_primary_expr(tokens, pos, line); + return std::make_unique( UnaryExpr{ op, std::move(value) }); + } + } + + // grouped expr + if (token.type == Token_Type::T_SYMBOL && + token.symbol_type == Symbol_Type::S_LPAREN) + { + pos++; + + auto expr = + extract_binary_expr(tokens, pos, line, 0); + + if (tokens[pos].type != Token_Type::T_SYMBOL || tokens[pos].symbol_type != Symbol_Type::S_RPAREN) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected )"); + + pos++; + return expr; + } + + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Invalid expression"); +} + + +std::unique_ptr DSL::parse_block(const std::vector &tokens, size_t &pos, size_t &line) +{ + BlockStatement block; + const size_t start_line = line; + skip_newline(tokens, pos, line); + if (tokens[pos].type != Token_Type::T_SYMBOL ||tokens[pos].symbol_type != Symbol_Type::S_LBRACE) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected { for initing a block"); + pos++; + while (true) + { + validate_eof(tokens, pos, line); + if (tokens[pos].type == Token_Type::T_END) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expect }"); + + skip_newline(tokens, pos, line); + + if (tokens[pos].type == Token_Type::T_SYMBOL && tokens[pos].symbol_type == Symbol_Type::S_RBRACE) + { + pos++; + break; + } + + block.body.push_back(parse_line(tokens, pos, line)); + } + return std::make_unique(std::move(block), start_line); +} +std::unique_ptr DSL::parse_if(const std::vector &tokens, size_t &pos, size_t &line) +{ + IfStatement stmt; + const size_t start_line = line; + validate_eof(tokens, pos, line); + while (true) + { + IfBranch branch; + bool has_condition = true; + + pos++; + + if (tokens[pos-1].type == Token_Type::T_KEYWORD && tokens[pos-1].keyword_type == Keyword_Type::K_ELSE) + { + if (tokens[pos].type == Token_Type::T_KEYWORD && tokens[pos].keyword_type == Keyword_Type::K_IF) + pos++; + else + has_condition = false; + } + skip_newline(tokens, pos, line); + if (has_condition) + { + if (tokens[pos].type != Token_Type::T_SYMBOL || tokens[pos].symbol_type != Symbol_Type::S_LPAREN) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected ( after if"); + + pos++; + + branch.condition = extract_binary_expr(tokens, pos, line); + validate_eof(tokens, pos, line); + if (tokens[pos].type != Token_Type::T_SYMBOL || tokens[pos].symbol_type != Symbol_Type::S_RPAREN) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected ) after if"); + + pos++; + } + auto body = parse_block(tokens, pos, line); + + auto* block = std::get_if(&body->stmt); + + if (has_condition) + { + branch.body = std::move(block->body); + stmt.branches.push_back(std::move(branch)); + } + else + { + stmt.else_body = std::move(block->body); + break; + } + + size_t temp_pos = pos; + size_t temp_line = line; + + skip_newline(tokens, pos, line); + if (!(tokens[pos].type == Token_Type::T_KEYWORD && tokens[pos].keyword_type == Keyword_Type::K_ELSE)) + { + pos = temp_pos; + line = temp_line; + break; + } + } + return std::make_unique(std::move(stmt), start_line); +} +std::unique_ptr DSL::parse_ascent(const std::vector &tokens, size_t &pos, size_t &line) +{ + AscentStatement stmt; + const size_t start_line = line; + validate_eof(tokens, pos, line); + if (tokens[pos].type != Token_Type::T_SYMBOL ||tokens[pos].symbol_type != Symbol_Type::S_LPAREN) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected ( after ascent"); + + pos++; + validate_eof(tokens, pos, line); + if (tokens[pos].type != Token_Type::T_ID) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected iterator name to ascent"); + + stmt.iterator = tokens[pos].text; + pos++; + validate_eof(tokens, pos, line); + if (tokens[pos].text != "in") + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected \"in\" in ascent"); + + pos++; + validate_eof(tokens, pos, line); + stmt.start = extract_binary_expr(tokens, pos, line); + validate_eof(tokens, pos, line); + if (tokens[pos].type != Token_Type::T_OP || tokens[pos].op_type != Op_Type::O_RANGE) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected .. in ascent"); + + pos++; + validate_eof(tokens, pos, line); + stmt.end = extract_binary_expr(tokens, pos, line); + validate_eof(tokens, pos, line); + if (tokens[pos].type != Token_Type::T_SYMBOL ||tokens[pos].symbol_type != Symbol_Type::S_RPAREN) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected ) after ascent"); + + pos++; + + auto body = parse_block(tokens, pos, line); + + auto* block = std::get_if(&body->stmt); + + stmt.body = std::move(block->body); + + return std::make_unique(std::move(stmt), start_line); +} + +std::unique_ptr DSL::parse_spawn(const std::vector &tokens, size_t &pos, size_t &line) +{ + SpawnStatement stmt; + const size_t start_line = line; + validate_eof(tokens, pos, line); + if (tokens[pos].type != Token_Type::T_SYMBOL ||tokens[pos].symbol_type != Symbol_Type::S_LPAREN) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected ( after spawn"); + pos++; + + while (true) + { + validate_eof(tokens, pos, line); + + stmt.args.push_back( + extract_binary_expr(tokens, pos, line)); + + if (tokens[pos].type == Token_Type::T_SYMBOL) + { + if (tokens[pos].symbol_type == Symbol_Type::S_COMMA) + { + pos++; + continue; + } + + if (tokens[pos].symbol_type == Symbol_Type::S_RPAREN) + { + pos++; + break; + } + } + + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected , or )"); + } + + return std::make_unique(std::move(stmt), start_line); +} +std::unique_ptr DSL::parse_assign(const std::vector &tokens, size_t &pos,size_t &line) +{ + const std::string name = tokens[pos].text; + pos++; + validate_eof(tokens, pos, line); + const Token &op = tokens[pos]; + if (op.type != Token_Type::T_OP) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : I don't understand the statement. " + name + "and then " + op.text + "?"); + if (!(op.op_type >= Op_Type::O_ASSIGN && op.op_type <= Op_Type::O_MOD_ASSIGN)) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : I don't understand the statement. What do you mean \"" + op.text + "\" as an assign statement?"); + pos++; + return std::make_unique(AssignStatement{name, op.op_type, extract_binary_expr(tokens, pos, line,0)}, line); +} + +std::unique_ptr DSL::parse_bpm(const std::vector &tokens, size_t &pos,size_t &line) +{ + const std::string name = "@" + tokens[pos].text; + pos++; + validate_eof(tokens, pos, line); + const Token &op = tokens[pos]; + if (op.type != Token_Type::T_OP && op.op_type != Op_Type::O_ASSIGN) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : To config the system, you use @var = x"); + pos++; + return std::make_unique(AssignStatement{name, op.op_type, extract_binary_expr(tokens, pos, line,0)}, line); +} + +std::unique_ptr DSL::parse_line(const std::vector &tokens, size_t &pos, size_t &line) { + const Token& cmd = tokens[pos]; + + if (cmd.type == Token_Type::T_KEYWORD) + { + if (cmd.keyword_type == Keyword_Type::K_IF) + { + return parse_if(tokens, pos, line); + } + + if (cmd.keyword_type == Keyword_Type::K_ASCENT) + { + pos++; + return parse_ascent(tokens, pos, line); + } + } + + if (cmd.type == Token_Type::T_SYMBOL) + { + if (cmd.symbol_type == Symbol_Type::S_LBRACE) + return parse_block(tokens, pos, line); + if (cmd.symbol_type == Symbol_Type::S_AMPERSAND) + { + pos++; + return parse_bpm(tokens, pos, line); + } + + } + + if (cmd.type == Token_Type::T_ID) + { + if (cmd.text == "spawn") + { + pos++; + return parse_spawn(tokens, pos, line); + } + return parse_assign(tokens, pos, line); + } + + throw std::runtime_error("DSL line " + std::to_string(line) + " error : I don't understand the statement"); +} + +AST DSL::parser(const std::vector &tokens) +{ + AST parsed; + auto &statements = parsed.statements; + size_t pos = 0; + size_t line = 1; + + while (tokens[pos].type != Token_Type::T_END) { + while (tokens[pos].type == Token_Type::T_NEWLINE) { + pos++; + line++; + } + + if (tokens[pos].type == Token_Type::T_END) break; + + statements.push_back(parse_line(tokens, pos, line)); + + if (tokens[pos].type != Token_Type::T_NEWLINE && + tokens[pos].type != Token_Type::T_END && + !(tokens[pos].type == Token_Type::T_SYMBOL && tokens[pos].symbol_type == Symbol_Type::S_RBRACE)) + { + throw std::runtime_error("DSL line " + std::to_string(line) + " error : Expected newline after statement"); + } + } + + return parsed; +} \ No newline at end of file diff --git a/src/dsl_reader/tokenizer.cpp b/src/dsl_reader/tokenizer.cpp new file mode 100644 index 00000000..9819b1fd --- /dev/null +++ b/src/dsl_reader/tokenizer.cpp @@ -0,0 +1,221 @@ +#include "game/utils/Bullethell_DSL/tokenizer.h" + +#include + +using namespace DSL; + +Op_Type check_op(const std::string &src, const size_t i, __out int &length) +{ + length = 2; + if(src.compare(i,length, "==")==0) return Op_Type::O_EQ; + if(src.compare(i,length, "!=")==0) return Op_Type::O_NEQ; + if(src.compare(i,length, "<=")==0) return Op_Type::O_LTE; + if(src.compare(i,length, ">=")==0) return Op_Type::O_GTE; + if(src.compare(i,length, "+=")==0) return Op_Type::O_ADD_ASSIGN; + if(src.compare(i,length, "-=")==0) return Op_Type::O_SUB_ASSIGN; + if(src.compare(i,length, "*=")==0) return Op_Type::O_MUL_ASSIGN; + if(src.compare(i,length, "/=")==0) return Op_Type::O_DIV_ASSIGN; + if(src.compare(i,length, "%=")==0) return Op_Type::O_MOD_ASSIGN; + if(src.compare(i,length, "..")==0) return Op_Type::O_RANGE; + if(src.compare(i,length, "&&")==0) return Op_Type::O_AND; + if(src.compare(i,length, "||")==0) return Op_Type::O_OR; + + length = 1; + if(src.compare(i,length, "+")==0) return Op_Type::O_ADD; + if(src.compare(i,length, "-")==0) return Op_Type::O_SUB; + if(src.compare(i,length, "*")==0) return Op_Type::O_MUL; + if(src.compare(i,length, "/")==0) return Op_Type::O_DIV; + if(src.compare(i,length, "%")==0) return Op_Type::O_MOD; + if(src.compare(i,length, "=")==0) return Op_Type::O_ASSIGN; + if(src.compare(i,length, ".")==0) return Op_Type::O_DOT; + if(src.compare(i,length, "!")==0) return Op_Type::O_NOT; + if(src.compare(i,length, "<")==0) return Op_Type::O_LT; + if(src.compare(i,length, ">")==0) return Op_Type::O_GT; + + return Op_Type::O_UNKNOWN; +} + +Symbol_Type check_symbol(const std::string &src, const size_t i, __out int &length) +{ + length = 1; + if(src.compare(i,1, "(")==0) return Symbol_Type::S_LPAREN; + if(src.compare(i,1, ")")==0) return Symbol_Type::S_RPAREN; + if(src.compare(i,1, "{")==0) return Symbol_Type::S_LBRACE; + if(src.compare(i,1, "}")==0) return Symbol_Type::S_RBRACE; + if(src.compare(i,1, ",")==0) return Symbol_Type::S_COMMA; + if(src.compare(i,1, "@")==0) return Symbol_Type::S_AMPERSAND; + return Symbol_Type::S_UNKNOWN; +} + +bool check_id_char(const char c) +{ + return isalnum(c) || c == '_'; +} + +Keyword_Type check_keyword(const std::string &src, const size_t i, __out int &length) +{ + auto checkNext = [&src,&i,&length]{ return i + length >= src.size() || !check_id_char(src[i + length]); }; + length = 6; + if(src.compare(i,length, "ascent")==0 && checkNext()) return Keyword_Type::K_ASCENT; + + length = 5; + if(src.compare(i,length, "false")==0 && checkNext()) return Keyword_Type::K_FALSE; + + length = 4; + if(src.compare(i,length, "else")==0 && checkNext()) return Keyword_Type::K_ELSE; + + if(src.compare(i,length, "true")==0 && checkNext()) return Keyword_Type::K_TRUE; + + length = 2; + if(src.compare(i,length, "if")==0 && checkNext()) return Keyword_Type::K_IF; + + + return Keyword_Type::K_UNKNOWN; +} + +std::vector DSL::tokenize(const std::string &src) { + std::vector tokens; + size_t i = 0; + size_t line = 1; + + while (i < src.size()) { + // skip whitespace + while (i < src.size() && (src[i] == ' ' || src[i] == '\t' || src[i] == '\r')) + i++; + + if (i >= src.size()) + break; + + const char c = src[i]; + + // newline + if (c == '\n') { + tokens.push_back({Token_Type::T_NEWLINE, "\\n"}); + i++; + line++; + continue; + } + + // comment + if (c == '/' && i + 1 < src.size() && src[i + 1] == '/') { + i += 2; + while (i < src.size() && src[i] != '\n') i++; + continue; + } + + if (c == '/' && i + 1 < src.size() && src[i + 1] == '*') { + i += 2; + bool done = false; + while (i+1 < src.size()) + { + if (src[i] == '\n') line++; + else if (src[i] == '*' && src[i + 1] == '/') + { + done = true; + break; + } + i++; + } + if (!done) throw std::runtime_error("DSL line " + std::to_string(line) + " error : Comment /* with no ending?"); + i+=2; + continue; + } + + // ignore ; for C lang lover + if (c == ';') { + i++; + continue; + } + + // op + int length = 0; + if (Op_Type op_type; (op_type = check_op(src, i, length)) != Op_Type::O_UNKNOWN) + { + Token token; + token.type = Token_Type::T_OP; + token.text = src.substr(i, length); + token.op_type = op_type; + tokens.push_back(token); + i+=length; + continue; + } + + if (Symbol_Type symbol_type; (symbol_type = check_symbol(src,i, length)) != Symbol_Type::S_UNKNOWN) + { + Token token; + token.type = Token_Type::T_SYMBOL; + token.text = src.substr(i, 1); + token.symbol_type = symbol_type; + tokens.push_back(token); + i+=length; + continue; + } + + if (Keyword_Type keyword_type; (keyword_type = check_keyword(src,i, length)) != Keyword_Type::K_UNKNOWN) + { + Token token; + token.type = Token_Type::T_KEYWORD; + token.text = src.substr(i, length); + token.keyword_type = keyword_type; + tokens.push_back(token); + i+=length; + continue; + } + + // number + if (isdigit(c)) { + const size_t start = i++; + bool floating_point = false; + + while (i < src.size()) + { + if (isdigit(src[i])) + { + i++; + continue; + } + if(src[i] == '.') + { + if(i + 1 < src.size() && src[i + 1] == '.') + break; + + if(floating_point) + throw std::runtime_error("DSL line " + std::to_string(line) + " error : 2 floating points?"); + + floating_point = true; + i++; + continue; + } + + break; + } + + + tokens.push_back({ + Token_Type::T_NUM, + src.substr(start, i - start) + }); + continue; + } + + // --- identifier --- + if (isalpha(c) || c == '_') { + const size_t start = i; + + while (i < src.size() && (isalnum(src[i]) || src[i] == '_')) + i++; + + tokens.push_back({ + Token_Type::T_ID, + src.substr(start, i - start) + }); + continue; + } + + // --- Unknown character --- + throw std::runtime_error("DSL line" + std::to_string(line) + " error : What do you mean by \"" + c + "\"?"); + } + + tokens.push_back({Token_Type::T_END, ""}); + return tokens; +} \ No newline at end of file diff --git a/src/scene/demo_bullethell.cpp b/src/scene/demo_bullethell.cpp index fbe03b1d..a2e1f459 100644 --- a/src/scene/demo_bullethell.cpp +++ b/src/scene/demo_bullethell.cpp @@ -1,3 +1,4 @@ +#include "game/utils/Bullethell_DSL/bullet_script.h" #include "scene.h" #include "system.h" @@ -45,52 +46,6 @@ Game::Render::AnimationDataRegistry Scene::init_anim_data() return anim_datas; } -Game::Battle::PatternContainer Scene::create_pattern_container() -{ - using namespace Game::Battle; - const std::vector demo_step = { - PatternStep(400, OP_SET, 0b1000, 300), - PatternStep(0, OP_SET, 0b0100, -90), - PatternStep(0, OP_SET, 0b0010, 0), - PatternStep(1000, OP_SET, 0b1000, 300), - PatternStep(0, OP_SET, 0b0010, 0), - -}; - const std::vector demo_pattern = { - PatternSequence(false, 0,1,2), - PatternSequence(false, 0,1,2,3), - - }; - auto demo_pattern_container = PatternContainer(demo_step,demo_pattern); - return { PatternContainer(demo_pattern_container) }; -} - -Game::Battle::BulletLoader Scene::create_bullet_data() - { - using namespace Game::Battle; - using namespace Game::Physics; - - BulletLoader loader; - return loader; - } - -Game::Battle::BulletLoader Scene::create_bullet_data2() -{ - using namespace Game::Battle; - using namespace Game::Physics; - - BulletLoader loader; - for (int i=0;i<500;i++) - { - for (int j=0;j<8;j++) - { - loader.CreateBullet(1000 + i*100, BulletData(0, 0, 100, (static_cast(j) *45) + 6 * (float)i, 50,-45.f,0, 5000, (i*8 + j)%152)); - } - } - - return loader; -} - Scene::DemoBulletHell Scene::DemoBulletHell::instance() { static DemoBulletHell instance; @@ -104,6 +59,8 @@ std::shared_ptr Scene::DemoBulletHell::init( init_graphics(tm); + Game::BulletHell::BulletScript script{"dsl/ShotData.th0","dsl/Demo.th0"}; + // Create and configure BattleState tm->create_entity Scene::DemoBulletHell::init( ( Game::Battle::BattleState(100, 100, Game::Battle::Difficulty()), Game::Battle::BulletHellState(10), - read_bullet_data_from_file("ShotData.txt"), - create_bullet_data(), - create_pattern_container(), + std::move(script.bullet_registry), + std::move(script.bullet_loader), + std::move(script.pattern_container), init_anim_data(), Game::Audio::init_sounds(), Game::Rhythm::KeyInput(), diff --git a/src/scene/demo_game.cpp b/src/scene/demo_game.cpp index 67290c3a..51a5c4a5 100644 --- a/src/scene/demo_game.cpp +++ b/src/scene/demo_game.cpp @@ -4,6 +4,7 @@ #include "system.h" #include "game.h" +#include "game/utils/Bullethell_DSL/bullet_script.h" void init_graphics(const std::shared_ptr& tm) { @@ -44,302 +45,6 @@ void init_graphics(const std::shared_ptr& tm) load_sprite("img/level1_bg.dds", "level1_bg", 3840, 2160); } -Game::Battle::PatternContainer create_pattern_container2() -{ - int t_beat = 447; - using namespace Game::Battle; - const std::vector demo_step = { - PatternStep(t_beat/2, OP_SET, 0b1110, 300, -90 ,0), - PatternStep(t_beat/2, OP_SET, 0b0010, -5000), - -}; - const std::vector demo_pattern = { - PatternSequence(false, 0), - // PatternSequence(false, 4), - - }; - auto demo_pattern_container = PatternContainer(demo_step,demo_pattern); - return { PatternContainer(demo_pattern_container) }; -} - -Game::Battle::BulletLoader Scene::DemoGame::create_bullet_test() -{ - using namespace Game::Battle; - using namespace Game::Physics; - - BulletLoader loader; - - int t_beat = 447; - float b_offset = 0; - constexpr float time_per_beat = 60000.f / 134.f; - auto beat_time = [](const float beat) - { - return static_cast(std::round(beat * time_per_beat)); - }; - - const auto box_left = -Game::BOX_SIZE + Game::HALF_WIDTH / 32; - const auto box_right = Game::BOX_SIZE - Game::HALF_WIDTH / 32; - const auto box_up = Game::BOX_SIZE + Game::HALF_HEIGHT / 32 - Game::HALF_HEIGHT/ 3; - const auto box_down = -Game::BOX_SIZE-Game::HALF_HEIGHT/32-Game::HALF_HEIGHT/3; - - for (int i=0; i < 16; i++ , b_offset+=0.5f) - { - int i8 = i%8; - float d = (i8 / 4 == 1) ? (float)(8 - i8 -1) : (float)i8; - loader.CreateBullet(beat_time(b_offset),BulletData(box_left + (box_right-box_left)*d/4 + rand_float(-50,50), box_up, 400, 90, -1000,0, 1,0,5000,12)); - loader.CreateBullet(beat_time(b_offset + 0.25f),BulletData(box_right - (box_right-box_left)*d/4 + rand_float(-50,50), box_up+100, 400, 90, -1000,0, 1,0,5000,13)); - } - for (int i=0; i < 15; i++, b_offset+=2.f) - { - float randX; - if (i%2==0) randX = box_left + rand_float(50,200); - else randX = box_right - rand_float(50,200); - float randAcc = -rand_float(1000,5000); - loader.CreateBullet(beat_time(b_offset),BulletData(randX , -Game::HALF_HEIGHT, 10000, 90, randAcc,0,0,t_beat,25)); - float bomb_y = -Game::HALF_HEIGHT/4 + randAcc/20; - for (int j=0; j < 8; j++) - { - loader.CreateBullet(beat_time(b_offset+1),BulletData(randX , bomb_y, 400, (float)j*45, 0,0,0,5000,1)); - } - } - - - b_offset = 116; - for (int i=0; i < 3; i++, b_offset+=2.0f) - { - float randX; - if (i%2==0) randX = box_left + rand_float(50,200); - else randX = box_right - rand_float(50,200); - float randAcc = -rand_float(1000,5000); - loader.CreateBullet(beat_time(b_offset),BulletData(randX , -Game::HALF_HEIGHT, 10000, 90, randAcc,0,0,t_beat,25)); - float bomb_y = -Game::HALF_HEIGHT/4 + randAcc/20; - - if (i==2) - { - loader.CreateBullet(beat_time(b_offset+0.f), BulletData(box_left+100 , box_down+250, 0, 0,t_beat*2,t_beat*3,172)); - loader.CreateBullet(beat_time(b_offset+0.5f), BulletData(box_right-100 , box_down+250, 0, 0,t_beat*2,t_beat*3,172)); - loader.CreateBullet(beat_time(b_offset+1.f), BulletData(box_left+100 , box_down+500, 0, 0,t_beat*2,t_beat*3,172)); - } - - for (int j=0; j < 8; j++) - { - loader.CreateBullet(beat_time(b_offset+1),BulletData(randX , bomb_y, 400, (float)j*45, 0,0,0,5000,1)); - } - - if (i==2) - { - loader.CreateBullet(beat_time(b_offset+1.5f), BulletData(box_right-100 , box_down+500, 0, 0,t_beat*2,t_beat*3,172)); - } - } - - b_offset+=2.f; - - for (int i=0; i < 4; i++, b_offset+=2.0f) - { - float randX; - if (i%2==0) randX = box_left + rand_float(50,200); - else randX = box_right - rand_float(50,200); - float randAcc = -rand_float(1000,5000); - loader.CreateBullet(beat_time(b_offset),BulletData(randX , -Game::HALF_HEIGHT, 10000, 90, randAcc,0,0,t_beat,25)); - float bomb_y = -Game::HALF_HEIGHT/4 + randAcc/20; - - if (i==2) - { - loader.CreateBullet(beat_time(b_offset+0.f), BulletData(Game::HALF_WIDTH, box_down + 100, 0, 180, t_beat*2,t_beat*3, 164)); - loader.CreateBullet(beat_time(b_offset+0.33f), BulletData(-Game::HALF_WIDTH, box_down + 200, 0, 0, t_beat*2,t_beat*3, 164)); - loader.CreateBullet(beat_time(b_offset+0.67f), BulletData(Game::HALF_WIDTH, box_down + 300, 0, 180, t_beat*2,t_beat*3, 164)); - } - - for (int j=0; j < 8; j++) - { - loader.CreateBullet(beat_time(b_offset+1),BulletData(randX , bomb_y, 400, (float)j*45, 0,0,0,5000,1)); - } - - if (i==2) - { - loader.CreateBullet(beat_time(b_offset+1.f), BulletData(-Game::HALF_WIDTH, box_down + 400, 0, 0, t_beat*2,t_beat*3, 164)); - loader.CreateBullet(beat_time(b_offset+1.33f), BulletData(Game::HALF_WIDTH, box_down + 500, 0, 180, t_beat*2,t_beat*3, 164)); - loader.CreateBullet(beat_time(b_offset+1.67f), BulletData(-Game::HALF_WIDTH, box_down + 600, 0, 0, t_beat*2,t_beat*3, 164)); - } - } - - b_offset+=2.f; - - for (int i=0; i < 3; i++, b_offset+=2.0f) - { - float randX; - if (i%2==0) randX = box_left + rand_float(50,200); - else randX = box_right - rand_float(50,200); - float randAcc = -rand_float(1000,5000); - loader.CreateBullet(beat_time(b_offset),BulletData(randX , -Game::HALF_HEIGHT, 10000, 90, randAcc,0,0,t_beat,25)); - float bomb_y = -Game::HALF_HEIGHT/4 + randAcc/20; - - if (i==1 || i==2) - { - for (int tof = 0; tof < 8; tof++) - { - loader.CreateBullet(beat_time(b_offset) + 40*tof,BulletData(0 , box_up, 500, static_cast(-155 + tof*15),0,5000,100)); - } - } - - for (int j=0; j < 8; j++) - { - loader.CreateBullet(beat_time(b_offset+1),BulletData(randX , bomb_y, 400, (float)j*45, 0,0,0,5000,1)); - } - - if (i==1 || i==2) - { - for (int tof = 0; tof < 8; tof++) - { - loader.CreateBullet(beat_time(b_offset+1) + 40*tof,BulletData(0 , box_up, 500, static_cast(-15 - tof*15),0,5000,100)); - } - } - } - - b_offset+=2.f; - - for (int i=0; i < 2; i++, b_offset+=2.0f) - { - float randX; - if (i%2==0) randX = box_left + rand_float(50,200); - else randX = box_right - rand_float(50,200); - float randAcc = -rand_float(1000,5000); - loader.CreateBullet(beat_time(b_offset),BulletData(randX , -Game::HALF_HEIGHT, 10000, 90, randAcc,0,0,t_beat,25)); - float bomb_y = -Game::HALF_HEIGHT/4 + randAcc/20; - for (int j=0; j < 8; j++) - { - loader.CreateBullet(beat_time(b_offset+1),BulletData(randX , bomb_y, 400, (float)j*45, 0,0,0,5000,1)); - } - } - - b_offset = 180; - loader.CreateBullet(beat_time(b_offset),BulletData(box_left + (box_right-box_left)/2, box_down + (box_up-box_down)/2, 0, 0, t_beat*2,t_beat*3, 176)); - for (int i=0;i<480;i++, b_offset+=0.125f) - { - loader.CreateBullet(beat_time(b_offset),BulletData(box_left + (box_right-box_left)/2, box_down + (box_up-box_down)/2, 300, (float)i*42.f,0,5000,53)); - if (i>=192 && i%16==0) - { - const float x = i % 32 == 0 ? box_left + 100 : box_right - 100; - const float y = (i%64/32)==0? box_down+250 : box_down+500; - loader.CreateBullet(beat_time(b_offset), BulletData(x , y, 0, 0,t_beat*2,t_beat*3,172)); - } - if (i>=256 && i%8==4) - { - loader.CreateBullet(beat_time(b_offset),BulletData(rand_float(box_left,box_right), box_up, 400, 90, -1000,0, 1,0,5000,144)); - } - } - - b_offset = 244; - for (int i=0; i < 3; i++, b_offset+=2.0f) - { - float randX; - if (i%2==0) randX = box_left + rand_float(50,200); - else randX = box_right - rand_float(50,200); - float randAcc = -rand_float(1000,5000); - loader.CreateBullet(beat_time(b_offset),BulletData(randX , -Game::HALF_HEIGHT, 10000, 90, randAcc,0,0,t_beat,25)); - float bomb_y = -Game::HALF_HEIGHT/4 + randAcc/20; - - if (i==2) - { - loader.CreateBullet(beat_time(b_offset+0.f), BulletData(box_left+100 , box_down+250, 0, 0,t_beat*2,t_beat*3,172)); - loader.CreateBullet(beat_time(b_offset+0.5f), BulletData(box_right-100 , box_down+250, 0, 0,t_beat*2,t_beat*3,172)); - loader.CreateBullet(beat_time(b_offset+1.f), BulletData(box_left+100 , box_down+500, 0, 0,t_beat*2,t_beat*3,172)); - } - - for (int j=0; j < 8; j++) - { - loader.CreateBullet(beat_time(b_offset+1),BulletData(randX , bomb_y, 400, (float)j*45, 0,0,0,5000,1)); - } - - if (i==2) - { - loader.CreateBullet(beat_time(b_offset+1.5f), BulletData(box_right-100 , box_down+500, 0, 0,t_beat*2,t_beat*3,172)); - } - } - - b_offset+=2.f; - - for (int i=0; i < 4; i++, b_offset+=2.0f) - { - float randX; - if (i%2==0) randX = box_left + rand_float(50,200); - else randX = box_right - rand_float(50,200); - float randAcc = -rand_float(1000,5000); - loader.CreateBullet(beat_time(b_offset),BulletData(randX , -Game::HALF_HEIGHT, 10000, 90, randAcc,0,0,t_beat,25)); - float bomb_y = -Game::HALF_HEIGHT/4 + randAcc/20; - - if (i==2) - { - loader.CreateBullet(beat_time(b_offset+0.f), BulletData(Game::HALF_WIDTH, box_down + 100, 0, 180, t_beat*2,t_beat*3, 164)); - loader.CreateBullet(beat_time(b_offset+0.33f), BulletData(-Game::HALF_WIDTH, box_down + 200, 0, 0, t_beat*2,t_beat*3, 164)); - loader.CreateBullet(beat_time(b_offset+0.67f), BulletData(Game::HALF_WIDTH, box_down + 300, 0, 180, t_beat*2,t_beat*3, 164)); - } - - for (int j=0; j < 8; j++) - { - loader.CreateBullet(beat_time(b_offset+1),BulletData(randX , bomb_y, 400, (float)j*45, 0,0,0,5000,1)); - } - - if (i==2) - { - loader.CreateBullet(beat_time(b_offset+1.f), BulletData(-Game::HALF_WIDTH, box_down + 400, 0, 0, t_beat*2,t_beat*3, 164)); - loader.CreateBullet(beat_time(b_offset+1.33f), BulletData(Game::HALF_WIDTH, box_down + 500, 0, 180, t_beat*2,t_beat*3, 164)); - loader.CreateBullet(beat_time(b_offset+1.67f), BulletData(-Game::HALF_WIDTH, box_down + 600, 0, 0, t_beat*2,t_beat*3, 164)); - } - } - - b_offset+=2.f; - - for (int i=0; i < 3; i++, b_offset+=2.0f) - { - float randX; - if (i%2==0) randX = box_left + rand_float(50,200); - else randX = box_right - rand_float(50,200); - float randAcc = -rand_float(1000,5000); - loader.CreateBullet(beat_time(b_offset),BulletData(randX , -Game::HALF_HEIGHT, 10000, 90, randAcc,0,0,t_beat,25)); - float bomb_y = -Game::HALF_HEIGHT/4 + randAcc/20; - - if (i==1 || i==2) - { - for (int tof = 0; tof < 8; tof++) - { - loader.CreateBullet(beat_time(b_offset) + 40*tof,BulletData(0 , box_up, 500, static_cast(-155 + tof*15),0,5000,100)); - } - } - - for (int j=0; j < 8; j++) - { - loader.CreateBullet(beat_time(b_offset+1),BulletData(randX , bomb_y, 400, (float)j*45, 0,0,0,5000,1)); - } - - if (i==1 || i==2) - { - for (int tof = 0; tof < 8; tof++) - { - loader.CreateBullet(beat_time(b_offset+1) + 40*tof,BulletData(0 , box_up, 500, static_cast(-15 - tof*15),0,5000,100)); - } - } - } - - b_offset+=2.f; - - for (int i=0; i < 2; i++, b_offset+=2.0f) - { - float randX; - if (i%2==0) randX = box_left + rand_float(50,200); - else randX = box_right - rand_float(50,200); - float randAcc = -rand_float(1000,5000); - loader.CreateBullet(beat_time(b_offset),BulletData(randX , -Game::HALF_HEIGHT, 10000, 90, randAcc,0,0,t_beat,25)); - float bomb_y = -Game::HALF_HEIGHT/4 + randAcc/20; - for (int j=0; j < 8; j++) - { - loader.CreateBullet(beat_time(b_offset+1),BulletData(randX , bomb_y, 400, (float)j*45, 0,0,0,5000,1)); - } - } - - return (loader); -} - inline Game::Battle::LevelData create_level1_chartdata() { Game::Battle::BpmInfo bpm; @@ -361,44 +66,6 @@ inline Game::Battle::LevelData create_level1_chartdata() ); } -Game::Battle::ChartData Scene::DemoGame::create_note_test() -{ - Game::Battle::ChartData chart; - - for (int lane = 0; lane < 4; ++lane) - { - chart.lanes[lane].lane_number = lane; - chart.lanes[lane].notes.clear(); - chart.lanes[lane].current_note = 0; - } - - // chart.lanes[0].notes.emplace_back(false, 5000 + 7500, 0, Game::Battle::RhythmType::NORMAL); - // chart.lanes[1].notes.emplace_back(false, 5000 + 8000, 0, Game::Battle::RhythmType::NORMAL); - // chart.lanes[2].notes.emplace_back(false, 5000 + 8500, 0, Game::Battle::RhythmType::NORMAL); - // chart.lanes[3].notes.emplace_back(false, 5000 + 9000, 0, Game::Battle::RhythmType::NORMAL); - // chart.lanes[0].notes.emplace_back(false, 5000 + 9500, 0, Game::Battle::RhythmType::ACCENT); - // chart.lanes[1].notes.emplace_back(false, 5000 + 10000, 0, Game::Battle::RhythmType::ACCENT); - // chart.lanes[2].notes.emplace_back(false, 5000 + 10500, 0, Game::Battle::RhythmType::ACCENT); - // chart.lanes[3].notes.emplace_back(false, 5000 + 11000, 0, Game::Battle::RhythmType::ACCENT); - // chart.lanes[0].notes.emplace_back(true, 5000 + 12000, 8000 + 13000, Game::Battle::RhythmType::ACCENT); - // chart.lanes[3].notes.emplace_back(true, 5000 + 12000, 8000 + 13000, Game::Battle::RhythmType::ACCENT); - // - // for (int i=0;i<80;i++) - // { - // chart.lanes[1].notes.emplace_back(false, 30000 + i*100, 0, Game::Battle::RhythmType::NORMAL); - // if (i>12) - // { - // if (i%8==0) - // chart.lanes[0].notes.emplace_back(false, 30000 + i*100, 0, Game::Battle::RhythmType::ACCENT); - // if (i%8==4) - // chart.lanes[3].notes.emplace_back(false, 30000 + i*100, 0, Game::Battle::RhythmType::ACCENT); - // } - // if (i==60) chart.lanes[2].notes.emplace_back(true, 30000 + i*100, 25000 + 80*100, Game::Battle::RhythmType::ACCENT); - // } - - return (chart); -} - Game::Render::Sprite Scene::assign_sprite(const int type) { if (type == 1) @@ -508,6 +175,7 @@ std::shared_ptr Scene::DemoGame::init() tm->create_entity(Game::Render::Camera2D{.offset = {}, .scaleX = 1920, .scaleY = 1080, .rotation = 0}); init_graphics(tm); + Game::BulletHell::BulletScript script{"dsl/ShotData.th0","dsl/Demo.th0"}; tm->create_entity Scene::DemoGame::init() ( Game::Battle::BattleState(200, 100, Game::Battle::Difficulty()), Game::Battle::BulletHellState(10), - Game::Battle::RhythmState(1, 500, 279, 5.0f, 5.0f), - read_bullet_data_from_file("ShotData.txt"), - create_bullet_test(), - create_pattern_container2(), + Game::Battle::RhythmState(1, 500, 247, 4.0f, 4.0f), + std::move(script.bullet_registry), + std::move(script.bullet_loader), + std::move(script.pattern_container), init_anim_data(), Game::Audio::init_sounds(), Game::Rhythm::KeyInput(), diff --git a/src/windows/scenes/CMakeLists.txt b/src/windows/scenes/CMakeLists.txt index 17fc66eb..4bd28b5f 100644 --- a/src/windows/scenes/CMakeLists.txt +++ b/src/windows/scenes/CMakeLists.txt @@ -14,6 +14,7 @@ target_link_libraries(os_scene PRIVATE scene renderer + dsl_reader user32 gdi32 kernel32 diff --git a/test/game_test.cpp b/test/game_test.cpp index 77399753..ef5f0421 100644 --- a/test/game_test.cpp +++ b/test/game_test.cpp @@ -1,795 +1,795 @@ -#include "system/ecs.h" -#include "game.h" -#include "pch.h" - -using System::ECS::pid; -using System::ECS::ResourcePool; -using System::ECS::SyscallType; -using System::ECS::TaskManager; -using Game::Battle::BattleState; -using Game::Battle::BulletHellState; -using Game::Battle::PatternContainer; -using namespace Game::BulletHell; -using namespace Game::Physics; - -// -- BULLET HELL TESTS -- - -extern "C" long double get_delta_time() -{ - return 1; // fake 60 FPS -} - -using BulletHell_Resource = System::ECS::ResourceManager<1000,Player, Bullet, Position, Scale, Velocity, Rotation, Acceleration, AngularVelocity, Booming, Delay, Laser, Particle, Pattern, CircularCollider, RectangularCollider, Game::Render::Material, BattleState, BulletHellState, PatternContainer>; - -using BulletHell_Syscall = System::ECS::Syscall<1000,Player, Bullet, Position, Scale, Velocity, Rotation, Acceleration, AngularVelocity, Booming, Delay, Laser, Particle, Pattern, CircularCollider, RectangularCollider, Game::Render::Material, BattleState, BulletHellState, PatternContainer>; - -pid CreatePlayer(BulletHell_Resource *resource, CircularCollider cc = {}, - Position pos = Position(0,0), Velocity vel = Velocity(0,0), Rotation rot = Rotation(0), Scale scl = {}, - Acceleration acc = Acceleration(0,0), AngularVelocity avel = AngularVelocity(0)) -{ - const pid id = resource->reserve_process(); - resource->add_resource(id, Player()); - resource->add_resource(id, CircularCollider(cc)); - resource->add_resource(id, Position(pos)); - resource->add_resource(id, Velocity(vel)); - resource->add_resource(id,Rotation(rot)); - resource->add_resource(id, Scale(scl)); - resource->add_resource(id,Acceleration(acc)); - resource->add_resource(id,AngularVelocity(avel)); - return id; -} - -pid CreateBullet1(BulletHell_Resource *resource, Bullet bullet = {}, - Position pos = Position(0,0), Velocity vel = Velocity(0,0), Rotation rot = Rotation(0), - Acceleration acc = Acceleration(0,999), AngularVelocity avel = AngularVelocity(0)) -{ - const pid id = resource->reserve_process(); - resource->add_resource(id, Bullet(bullet)); - resource->add_resource(id, Position(pos)); - resource->add_resource(id, Velocity(vel)); - resource->add_resource(id,Rotation(rot)); - resource->add_resource(id,Acceleration(acc)); - resource->add_resource(id,AngularVelocity(avel)); - return id; -} - -pid CreateBullet2(BulletHell_Resource *resource, Bullet bullet = {}, Delay delay = Delay(0), - Position pos = Position(0,0), Velocity vel = Velocity(0,0), Rotation rot = Rotation(0), - Acceleration acc = Acceleration(0,999), AngularVelocity avel = AngularVelocity(0)) -{ - const pid id = resource->reserve_process(); - resource->add_resource(id, Bullet(bullet)); - resource->add_resource(id, Delay(delay)); - resource->add_resource(id, Position(pos)); - resource->add_resource(id, Velocity(vel)); - resource->add_resource(id,Rotation(rot)); - resource->add_resource(id,Acceleration(acc)); - resource->add_resource(id,AngularVelocity(avel)); - return id; -} - -pid CreateCircleBullet(BulletHell_Resource *resource, Bullet bullet = {}, CircularCollider cc = CircularCollider(), - Position pos = Position(0,0), Velocity vel = Velocity(0,0), Rotation rot = Rotation(0), Scale scl = {}) -{ - const pid id = resource->reserve_process(); - resource->add_resource(id, Bullet(bullet)); - resource->add_resource(id, CircularCollider(cc)); - resource->add_resource(id, Position(pos)); - resource->add_resource(id, Velocity(vel)); - resource->add_resource(id,Rotation(rot)); - resource->add_resource(id, Scale(scl)); - resource->add_resource(id,Delay(0)); - return id; -} - -pid CreateRectangleBullet(BulletHell_Resource *resource, Bullet bullet = {}, RectangularCollider rc = RectangularCollider(), - Position pos = Position(0,0), Velocity vel = Velocity(0,0), Rotation rot = Rotation(0), Scale scl = {}) -{ - const pid id = resource->reserve_process(); - resource->add_resource(id, Bullet(bullet)); - resource->add_resource(id, RectangularCollider(rc)); - resource->add_resource(id, Position(pos)); - resource->add_resource(id, Velocity(vel)); - resource->add_resource(id,Rotation(rot)); - resource->add_resource(id, Scale(scl)); - resource->add_resource(id,Delay(0)); - return id; -} - - -pid CreateBoomerBullet(BulletHell_Resource *resource, Bullet bullet = {}, Booming booming = {},Delay delay = Delay(0), Particle particle = {}, - Position pos = {}, CircularCollider cc = {}) -{ - const pid id = resource->reserve_process(); - resource->add_resource(id, Bullet(bullet)); - resource->query().get(id).pierce = 999; - resource->add_resource(id, Delay(delay)); - resource->add_resource(id, Particle(particle)); - resource->add_resource(id, Booming(booming)); - resource->add_resource(id, Position(pos)); - resource->add_resource(id,{}); - resource->add_resource(id, Scale()); - resource->add_resource(id, Game::Render::Material(nullptr,{},0,nullptr,{},0)); - resource->add_resource(id, CircularCollider(cc)); - return id; -} - -pid CreateLaserBullet(BulletHell_Resource *resource, Bullet bullet = {}, Laser laser = {},Delay delay = Delay(0), Particle particle = {}, - Rotation rot = {}, Scale scl = {}, RectangularCollider rc = {}) -{ - const pid id = resource->reserve_process(); - resource->add_resource(id, Bullet(bullet)); - resource->query().get(id).pierce = 999; - resource->add_resource(id, Delay(delay)); - resource->add_resource(id, Particle(particle)); - resource->add_resource(id, Laser(laser)); - resource->add_resource(id, Position()); - resource->add_resource(id,Rotation(rot)); - resource->add_resource(id, Scale(scl)); - resource->add_resource(id, RectangularCollider(rc)); - resource->add_resource(id, Game::Render::Material(nullptr,{},0,nullptr,{},0)); - return id; -} - -pid CreateBattleState(BulletHell_Resource *resource) -{ - const pid id = resource->reserve_process(); - resource->add_resource(id, BattleState()); - return id; -} - -pid CreateBulletHellState(BulletHell_Resource *resource) -{ - const pid id = resource->reserve_process(); - resource->add_resource(id, BulletHellState()); - return id; -} - -PatternContainer InitPatternContainer() -{ - using namespace Game::Battle; - std::vector demo_step = { - PatternStep(3, OP_ADD, 0b0100, 15), // 3s Rot+15 - PatternStep(3, OP_ADD, 0b0100, -15), // 3s Rot-15 - PatternStep(0, OP_SET, 0b1000, 3), // 3s Vel=3 - }; - std::vector demo_pattern = { - PatternSequence(false, 0, 2), - PatternSequence(false, 1, 2), - PatternSequence(true, 0, 2), - PatternSequence(true, 1, 2), - }; - auto demo_pattern_container = PatternContainer(demo_step,demo_pattern); - return {PatternContainer(demo_pattern_container)}; -} - -pid CreatePatternContainer(BulletHell_Resource *resource) -{ - const pid id = resource->reserve_process(); - resource->add_resource(id, PatternContainer(InitPatternContainer())); - return id; -} - -// Test Entity Instantiation - -TEST(Game, instantiate_player) -{ - TaskManager task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - - const pid id = CreatePlayer(resource); - EXPECT_TRUE(resource->query().get(id).is_active); - EXPECT_TRUE(resource->query().has(id)); - EXPECT_TRUE(resource->query().has(id)); - EXPECT_TRUE(resource->query().has(id)); - EXPECT_TRUE(resource->query().has(id)); - EXPECT_TRUE(resource->query().has(id)); - EXPECT_TRUE(resource->query().has(id)); -} - - - -TEST(Game, instantiate_bullets) -{ - TaskManager task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - - const pid id_1 = CreateBullet1(resource); - - EXPECT_TRUE(resource->query().has(id_1)); - EXPECT_TRUE(resource->query().has(id_1)); - EXPECT_TRUE(resource->query().has(id_1)); - EXPECT_EQ(resource->query().get(id_1).damage, 0); - - const pid id_2 = CreateBullet1(resource); - const pid id_3 = CreateBullet1(resource); - - EXPECT_TRUE(resource->query().has(id_2)); - EXPECT_TRUE(resource->query().has(id_2)); - EXPECT_TRUE(resource->query().has(id_2)); - EXPECT_TRUE(resource->query().has(id_3)); - EXPECT_TRUE(resource->query().has(id_3)); - EXPECT_TRUE(resource->query().has(id_3)); - EXPECT_EQ(resource->query().get(id_2).damage, 0); - EXPECT_EQ(resource->query().get(id_3).damage, 0); -} - -TEST(Game, value_change) -{ - TaskManager task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - - const pid id_1 = CreateBullet1(resource); - const pid id_2 = CreateBullet1(resource); - - EXPECT_TRUE(resource->query().has(id_1)); - EXPECT_TRUE(resource->query().has(id_2)); - EXPECT_EQ(resource->query().get(id_1).damage, 0); - EXPECT_EQ(resource->query().get(id_2).damage, 0); - - resource->query().get(id_1).damage = 1; - - EXPECT_TRUE(resource->query().has(id_1)); - EXPECT_TRUE(resource->query().has(id_2)); - EXPECT_EQ(resource->query().get(id_1).damage, 1); - EXPECT_EQ(resource->query().get(id_2).damage, 0); -} - -// TODO: Tests failed from here onwards with a compile error, trying to find cause of error - -//Movement -TEST(Game, bullet_movement1) -{ - TaskManager> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - - const pid id = CreateBullet1(resource); - resource->query().set(id,Position(1,1)); - resource->query().set(id,Velocity(2,3)); - - EXPECT_EQ(resource->query().get(id).x, 1); - EXPECT_EQ(resource->query().get(id).y, 1); - EXPECT_EQ(resource->query().get(id).vx, 2); - EXPECT_EQ(resource->query().get(id).vy, 3); - - //Move 1 time : 1,1 -> 3,4 - task_manager.run_all(); - - EXPECT_EQ(resource->query().get(id).x, 3); - EXPECT_EQ(resource->query().get(id).y, 4); - EXPECT_EQ(resource->query().get(id).vx, 2); - EXPECT_EQ(resource->query().get(id).vy, 3); - - //Move 2 times : 3,4 -> 7,10 - task_manager.run_all(); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).x, 7); - EXPECT_EQ(resource->query().get(id).y, 10); - EXPECT_EQ(resource->query().get(id).vx, 2); - EXPECT_EQ(resource->query().get(id).vy, 3); -} - - - -TEST(Game, bullet_movement2) -{ - TaskManager> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - - const pid id = CreateBullet1(resource); - resource->query().set(id,Position(0,0)); - resource->query().set(id,Velocity(4,0)); - resource->query().set(id,Rotation(30)); - - //Object rotate 45 degree -> vx = 4*cos(angle) ; vy = 4*sin(angle) - task_manager.run_all(); - - EXPECT_EQ(round(resource->query().get(id).x), round(4*sqrt(3)/2)); - EXPECT_EQ(resource->query().get(id).y, 4*1/2); - EXPECT_EQ(resource->query().get(id).vx, 4); - EXPECT_EQ(resource->query().get(id).vy, 0); - EXPECT_EQ(resource->query().get(id).angleZ, 30); - - resource->query().set(id,Position(3,2)); - resource->query().set(id,Velocity(2,3)); - resource->query().set(id,Rotation(-90)); - - //Object rotate -90 degree -> vx = vy ; vy = -vx - task_manager.run_all(); - task_manager.run_all(); - - EXPECT_EQ(round(resource->query().get(id).x), 9); - EXPECT_EQ(round(resource->query().get(id).y), -2); - EXPECT_EQ(resource->query().get(id).vx, 2); - EXPECT_EQ(resource->query().get(id).vy, 3); - EXPECT_EQ(resource->query().get(id).angleZ, -90); -} - -TEST(Game, bullet_movement3) -{ - TaskManager, acceleration_system> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - - const pid id = CreateBullet1(resource); - resource->query().set(id,Position(1,1)); - resource->query().set(id,Velocity(2,3)); - resource->query().set(id,Acceleration(1,-1,999,-999)); - - //Move 1 time : Pos 1,1 -> 3,4 then Vel 2,3 -> 3,2 - task_manager.run_all(); - - EXPECT_EQ(resource->query().get(id).x, 3); - EXPECT_EQ(resource->query().get(id).y, 4); - EXPECT_EQ(resource->query().get(id).vx, 3); - EXPECT_EQ(resource->query().get(id).vy, 2); - - //Move 1 times : 3,4 -> 6,7 then Vel 2,3 -> 4,1 - task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).x, 6); - EXPECT_EQ(resource->query().get(id).y, 6); - EXPECT_EQ(resource->query().get(id).vx, 4); - EXPECT_EQ(resource->query().get(id).vy, 1); - - //Move 2 times : 6,6 -> 15,5 then Vel 4,1 -> 6,-1 - task_manager.run_all(); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).x, 15); - EXPECT_EQ(resource->query().get(id).y, 7); - EXPECT_EQ(resource->query().get(id).vx, 6); - EXPECT_EQ(resource->query().get(id).vy, -1); -} - -TEST(Game, bullet_movement4) -{ - TaskManager> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - - const pid id = CreateBullet1(resource); - resource->query().set(id,Rotation(30)); - resource->query().set(id,AngularVelocity(-15)); - - //Rotate 1 time : 30 -> 15 - task_manager.run_all(); - - EXPECT_EQ(resource->query().get(id).angleZ, 15); - EXPECT_EQ(resource->query().get(id).v, -15); - - //Rotate 2 time : 15 -> -15 - task_manager.run_all(); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).angleZ, -15); - EXPECT_EQ(resource->query().get(id).v, -15); -} - -TEST(Game, bullet_movement5) -{ - TaskManager, acceleration_system, rotation_system> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - - const pid id = CreateBullet1(resource); - resource->query().set(id,Position(1,1)); - resource->query().set(id,Velocity(1,1)); - resource->query().set(id,Acceleration(1,-1,999,999,-999,-999)); - resource->query().set(id,Rotation(0)); - resource->query().set(id,AngularVelocity(90)); - - task_manager.run_all(); - - EXPECT_EQ(resource->query().get(id).x, 2); - EXPECT_EQ(resource->query().get(id).y, 2); - - task_manager.run_all(); - EXPECT_EQ(round(resource->query().get(id).x), 2); - EXPECT_EQ(round(resource->query().get(id).y), 4); - - task_manager.run_all(); - EXPECT_EQ(round(resource->query().get(id).x), -1); - EXPECT_EQ(round(resource->query().get(id).y), 5); - - task_manager.run_all(); - EXPECT_EQ(round(resource->query().get(id).x), -3); - EXPECT_EQ(round(resource->query().get(id).y), 1); -} - -TEST(Game, bullets_movement6) -{ - TaskManager, acceleration_system, rotation_system> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - - const pid id_1 = CreateBullet1(resource,{},Position(1,1),Velocity(1,1),Rotation(0),Acceleration(1,-1,999,999,-999,-999),AngularVelocity(90)); - const pid id_2 = CreateBullet1(resource,{},Position(2,0),Velocity(2,3),Rotation(30),{},AngularVelocity(30)); - const pid id_3 = CreateBullet1(resource, {},Position(-1,-3),Velocity(2,1),Rotation(180),Acceleration(-1,-2,999,999,-999,-999), {}); - - EXPECT_EQ(resource->query().get(id_1).x, 1); - EXPECT_EQ(resource->query().get(id_1).y, 1); - EXPECT_EQ(resource->query().get(id_2).x, 2); - EXPECT_EQ(resource->query().get(id_2).y, 0); - EXPECT_EQ(resource->query().get(id_3).x, -1); - EXPECT_EQ(resource->query().get(id_3).y, -3); - task_manager.run_all(); - - EXPECT_EQ(resource->query().get(id_1).x, 2); - EXPECT_EQ(resource->query().get(id_1).y, 2); - EXPECT_EQ(round(resource->query().get(id_2).x), 2); - EXPECT_EQ(round(resource->query().get(id_2).y), 4); - EXPECT_EQ(resource->query().get(id_3).x, -3); - EXPECT_EQ(resource->query().get(id_3).y, -4); - - task_manager.run_all(); - task_manager.run_all(); - - EXPECT_EQ(round(resource->query().get(id_1).x), -1); - EXPECT_EQ(round(resource->query().get(id_1).y), 5); - EXPECT_EQ(round(resource->query().get(id_2).x), -2); - EXPECT_EQ(round(resource->query().get(id_2).y), 9); - EXPECT_EQ(round(resource->query().get(id_3).x), -4); - EXPECT_EQ(resource->query().get(id_3).y, 0); - -} - -TEST(Game, bullets_damagable) -{ - TaskManager, delay_system> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - - const pid id = CreateBullet2(resource,Bullet(3),Delay(2)); - EXPECT_FALSE(resource->query().get(id).is_damageable); - task_manager.run_all(); - EXPECT_FALSE(resource->query().get(id).is_damageable); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).delay, 0); - EXPECT_FALSE(resource->query().get(id).is_damageable); - task_manager.run_all(); - EXPECT_TRUE(resource->query().get(id).is_damageable); - task_manager.run_all(); - EXPECT_TRUE(resource->query().get(id).is_damageable); - EXPECT_EQ(resource->query().get(id).delay, 0); -} - -TEST(Game, bullets_booming) -{ - TaskManager, particle_system, boomer_system> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - - const pid id = CreateBoomerBullet(resource, {}, Booming(3,2), Delay(1002), Particle(1009) ); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).delay, 1001); - EXPECT_EQ(resource->query().get(id).scaleX, 3); - EXPECT_EQ(resource->query().get(id).scaleY, 3); - EXPECT_EQ(resource->query().get(id).color.a, 0.25); - for (int i=0;i<1000;i++) task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).delay, 1); - EXPECT_EQ(resource->query().get(id).scaleX, 0); - EXPECT_EQ(resource->query().get(id).scaleY, 0); - EXPECT_EQ(resource->query().get(id).color.a, 1); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).delay, 0); - EXPECT_EQ(resource->query().get(id).scaleX, 1.5f); - EXPECT_EQ(resource->query().get(id).scaleY, 1.5f); - EXPECT_EQ(resource->query().get(id).color.a, 1); - EXPECT_EQ(resource->query().get(id).lifetime, 7); - task_manager.run_all(); - task_manager.run_all(); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).delay, 1); - EXPECT_GE(resource->query().get(id).scaleX, 2.75f); - EXPECT_GE(resource->query().get(id).scaleY, 2.75f); - EXPECT_EQ(resource->query().get(id).color.a, 1); - EXPECT_EQ(resource->query().get(id).lifetime, 4); - task_manager.run_all(); - EXPECT_LE(resource->query().get(id).scaleY, 2.75f); - task_manager.run_all(); - EXPECT_EQ(round(resource->query().get(id).scaleX), 0); -} - -TEST(Game, bullets_laser) -{ - TaskManager, particle_system, laser_system> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - - const pid id = CreateLaserBullet(resource, {}, Laser(2,2,3,3), Delay(1003), Particle(1010),Rotation(30) ); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).delay, 1002); - EXPECT_EQ(resource->query().get(id).scaleX, 3); - EXPECT_EQ(resource->query().get(id).scaleY, 1); - EXPECT_EQ(round(resource->query().get(id).x * 100)/100.f, 3.3f); - EXPECT_EQ(resource->query().get(id).y, 2.75f); - EXPECT_EQ(resource->query().get(id).color.a, 0.25); - for (int i=0;i<1001;i++) task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).delay, 1); - EXPECT_EQ(resource->query().get(id).scaleX, 0); - EXPECT_EQ(resource->query().get(id).scaleY, 1); - EXPECT_EQ(resource->query().get(id).x, 2); - EXPECT_EQ(resource->query().get(id).y, 2); - EXPECT_EQ(resource->query().get(id).color.a, 1); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).delay, 0); - EXPECT_EQ(resource->query().get(id).scaleX, 1); - EXPECT_EQ(round(resource->query().get(id).x * 100)/100.f, 2.43f); - EXPECT_EQ(resource->query().get(id).y, 2.25); - EXPECT_EQ(resource->query().get(id).color.a, 1); - EXPECT_EQ(resource->query().get(id).lifetime, 7); - task_manager.run_all(); - EXPECT_EQ(round(resource->query().get(id).x * 100)/100.f, 2.87f); - EXPECT_EQ(resource->query().get(id).y, 2.5); - task_manager.run_all(); - - EXPECT_EQ(resource->query().get(id).delay, 1); - EXPECT_GE(resource->query().get(id).scaleX, 3); - EXPECT_EQ(round(resource->query().get(id).x * 100)/100.f, 3.3f); - EXPECT_EQ(resource->query().get(id).y, 2.75f); - EXPECT_EQ(resource->query().get(id).color.a, 1); - EXPECT_EQ(resource->query().get(id).lifetime, 5); - task_manager.run_all(); - task_manager.run_all(); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(id).scaleX, 0); - EXPECT_EQ(round(resource->query().get(id).x * 100)/100.f, 5.9f); - EXPECT_EQ(resource->query().get(id).y, 4.25); -} - -// Collision -TEST(Game, bullet_collision1) -{ - TaskManager, bullet_system> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - const pid bhs = CreateBulletHellState(resource); - - CreatePlayer(resource, CircularCollider(2)); - const pid bullet1_id = CreateCircleBullet(resource,Bullet(5,1), CircularCollider(1), Position(4,4)); - // Miss - task_manager.run_all(); - EXPECT_TRUE(resource->query().has(bullet1_id)); - EXPECT_LE(resource->query().get(bhs).iframe_time,0); - - // Near Collide - resource->query().get(bullet1_id) = Position(3.f,0); - task_manager.run_all(); - EXPECT_TRUE(resource->query().has(bullet1_id)); - EXPECT_LE(resource->query().get(bhs).iframe_time,0); - - // Collide - resource->query().get(bullet1_id) = Position(2.99f,0.f); - task_manager.run_all(); - EXPECT_FALSE(resource->query().has(bullet1_id)); - EXPECT_GT(resource->query().get(bhs).iframe_time,0); - - // Reset - resource->query().get(bhs).iframe_time = 0; - const pid bullet2_id = CreateRectangleBullet(resource, Bullet(5,1),RectangularCollider(1,2), Position(4,0)); - - // Miss - task_manager.run_all(); - EXPECT_TRUE(resource->query().has(bullet2_id)); - EXPECT_LE(resource->query().get(bhs).iframe_time,0); - - // Near Collide & Wide pass - resource->query().get(bullet2_id) = Position(2.99f,0); - task_manager.run_all(); - EXPECT_TRUE(resource->query().has(bullet2_id)); - EXPECT_LE(resource->query().get(bhs).iframe_time,0); - - // Collide - resource->query().get(bullet2_id) = Position(0.f,2.99f); - task_manager.run_all(); - EXPECT_FALSE(resource->query().has(bullet2_id)); - EXPECT_GT(resource->query().get(bhs).iframe_time,0); -} - -TEST(Game, bullet_collision2) -{ - TaskManager, bullet_system> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - const pid bhs = CreateBulletHellState(resource); - - CreatePlayer(resource, CircularCollider(2)); - pid bullet_id = CreateCircleBullet(resource,Bullet(5), CircularCollider(1.5f,1.f), Position(2,-2.3f), {},Rotation(30)); - // Miss - task_manager.run_all(); - EXPECT_TRUE(resource->query().has(bullet_id)); - EXPECT_LE(resource->query().get(bhs).iframe_time,0); - - // Collide - resource->query().get(bullet_id) = Rotation(0); - task_manager.run_all(); - EXPECT_FALSE(resource->query().has(bullet_id)); - EXPECT_GT(resource->query().get(bhs).iframe_time,0); - - // Reset - resource->query().get(bhs).iframe_time = 0; - bullet_id = CreateRectangleBullet(resource, Bullet(5),RectangularCollider(1,2), Position(-2.5f,0.62f),{},Rotation(0)); - - // Miss - task_manager.run_all(); - EXPECT_TRUE(resource->query().has(bullet_id)); - EXPECT_LE(resource->query().get(bhs).iframe_time,0); - - // Collide - resource->query().get(bullet_id) = Rotation(60); - task_manager.run_all(); - EXPECT_FALSE(resource->query().has(bullet_id)); - EXPECT_GT(resource->query().get(bhs).iframe_time,0); -} - -TEST(Game, bullet_collision3) -{ - TaskManager, bullet_collision, bullet_system> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - const pid bhs = CreateBulletHellState(resource); - - CreatePlayer(resource, CircularCollider(2)); - // Miss - const pid bullet1_id = CreateCircleBullet(resource,Bullet(5), CircularCollider(1.f,1.f), Position(5,5), Velocity(1),Rotation(-90)); - const pid bullet2_id = CreateRectangleBullet(resource,Bullet(5), RectangularCollider(1.f,1.f), Position(1.5f,-5.2f), Velocity(1.5),Rotation(135)); - //Hit - const pid bullet3_id = CreateCircleBullet(resource,Bullet(5), CircularCollider(1.f,0.5f), Position(3.5f,1.5f), Velocity(0.5),Rotation(210)); - const pid bullet4_id = CreateCircleBullet(resource,Bullet(5), CircularCollider(1.f,1.f), Position(0,-5), Velocity(0.75),Rotation(90)); - const pid bullet5_id = CreateRectangleBullet(resource,Bullet(5), RectangularCollider(1.5f,1.f), Position(-4,0), Velocity(0.5),Rotation(-30)); - - for (int i=0;i<10;i++) - { - task_manager.run_all(); - resource->query().get(bhs).iframe_time = 0; - } - - EXPECT_TRUE(resource->query().has(bullet1_id)); - EXPECT_TRUE(resource->query().has(bullet2_id)); - EXPECT_FALSE(resource->query().has(bullet3_id)); - EXPECT_FALSE(resource->query().has(bullet4_id)); - EXPECT_FALSE(resource->query().has(bullet5_id)); -} - -TEST(Game, bullet_collision4) -{ - TaskManager, particle_system, boomer_system, laser_system, bullet_collision, bullet_system> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - const pid bhs = CreateBulletHellState(resource); - - CreatePlayer(resource, CircularCollider(2)); - //Missing boom - CreateBoomerBullet(resource,Bullet(false, 5), Booming(1.5f, 1), Delay(4), Particle(3), Position(4,0), CircularCollider(1.f,1.f)); - //Hit Laser - CreateLaserBullet(resource,Bullet(false, 5), Laser(4.f, -4.f, 10, 1), Delay(6), Particle(8), Rotation(135), Scale(2),RectangularCollider(1.f,1.f)); - // Hit boom - CreateBoomerBullet(resource,Bullet(false, 5), Booming(3.f, 1), Delay(11), Particle(11), Position(4,0),CircularCollider(1.f,1.f)); - // Rotate to hit laser - const pid laser_id = CreateLaserBullet(resource,Bullet(false, 5), Laser(7.f, 0, 10, 1), Delay(20), Particle(20), Rotation(135), Scale(2),RectangularCollider(1.f,1.f)); - - task_manager.run_all(); - task_manager.run_all(); - EXPECT_LE(resource->query().get(bhs).iframe_time,0); - - // Boomer 1 Fired - for (int i=0;i<2;i++) task_manager.run_all(); - EXPECT_LE(resource->query().get(bhs).iframe_time,0); - - // Laser Fired - for (int i=0;i<3;i++) task_manager.run_all(); - EXPECT_GT(resource->query().get(bhs).iframe_time,0); - - // Laser Gone - for (int i=0;i<2;i++) task_manager.run_all(); - resource->query().get(bhs).iframe_time = 0; - task_manager.run_all(); - EXPECT_LE(resource->query().get(bhs).iframe_time,0); - task_manager.run_all(); - - // Boomer2 Come - EXPECT_GT(resource->query().get(bhs).iframe_time,0); - - for (int i=0;i<3;i++) task_manager.run_all(); - resource->query().get(bhs).iframe_time = 0; - task_manager.run_all(); - - // LaserSpin Come - EXPECT_GT(resource->query().get(laser_id).scaleX,0); - EXPECT_LE(resource->query().get(bhs).iframe_time,0); - for (int i=0;i<5;i++) - { - task_manager.run_all(); - resource->query().get(laser_id).angleZ += 5.f; - EXPECT_EQ(resource->query().get(laser_id).angleZ, 140 + 5*i); - if (i==4) - EXPECT_GT(resource->query().get(bhs).iframe_time,0); - else - EXPECT_LE(resource->query().get(bhs).iframe_time,0); - } - -} - -TEST(Game, bullet_collision5) -{ - TaskManager, bullet_collision, bullet_system> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - CreateBulletHellState(resource); - - const pid player_id = CreatePlayer(resource, CircularCollider(2)); - const pid bullet1_id = CreateCircleBullet(resource,Bullet(5), CircularCollider(1), Position(5,5)); - - resource->query().get(player_id).vx = 0.75f; - resource->query().get(player_id).vy = 0.75f; - - task_manager.run_all(); - EXPECT_EQ(resource->query().get(player_id).x, 0.75f); - EXPECT_EQ(resource->query().get(player_id).y, 0.75f); - EXPECT_TRUE(resource->query().has(bullet1_id)); - - task_manager.run_all(); - EXPECT_EQ(resource->query().get(player_id).x, 1.5f); - EXPECT_EQ(resource->query().get(player_id).y, 1.5f); - EXPECT_TRUE(resource->query().has(bullet1_id)); - - task_manager.run_all(); - EXPECT_EQ(resource->query().get(player_id).x, 2.25f); - EXPECT_EQ(resource->query().get(player_id).y, 2.25f); - EXPECT_TRUE(resource->query().has(bullet1_id)); - - task_manager.run_all(); - EXPECT_EQ(resource->query().get(player_id).x, 3.f); - EXPECT_EQ(resource->query().get(player_id).y, 3.f); - EXPECT_FALSE(resource->query().has(bullet1_id)); -} - -TEST(Game, bullet_pattern1) -{ - TaskManager, particle_system, acceleration_system, movement_system, rotation_system> task_manager{}; - BulletHell_Resource *resource = task_manager.get_rm(); - CreateBattleState(resource); - CreatePatternContainer(resource); - - const pid bullet1_id = CreateBullet1(resource,{}, Position(0,0), Velocity(2.5), Rotation(45)); - const pid bullet2_id = CreateBullet1(resource,{}, Position(2,3)); - - resource->add_resource(bullet1_id, Pattern(1)); - resource->add_resource(bullet2_id, Pattern(4)); - - task_manager.run_all(); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(bullet1_id).delay, 2); - EXPECT_EQ(resource->query().get(bullet2_id).delay, 2); - task_manager.run_all(); - task_manager.run_all(); - EXPECT_EQ(round(resource->query().get(bullet1_id).x * 100)/100.f, 7.07f); - EXPECT_EQ(round(resource->query().get(bullet1_id).y * 100)/100.f, 7.07f); - EXPECT_EQ(resource->query().get(bullet2_id).x, 2); - EXPECT_EQ(resource->query().get(bullet2_id).y, 3); - EXPECT_EQ(resource->query().get(bullet1_id).delay, 0); - EXPECT_EQ(resource->query().get(bullet2_id).delay, 0); - task_manager.run_all(); - EXPECT_EQ(round(resource->query().get(bullet1_id).x * 100)/100.f, 8.57f); - EXPECT_EQ(round(resource->query().get(bullet1_id).y * 100)/100.f, 9.67f); - EXPECT_EQ(round(resource->query().get(bullet2_id).x * 100)/100.f, 4.90f); - EXPECT_EQ(round(resource->query().get(bullet2_id).y * 100)/100.f, 2.22f); - EXPECT_EQ(resource->query().get(bullet2_id).angleZ, -15); - EXPECT_EQ(resource->query().get(bullet2_id).vx, 3); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(bullet1_id).delay, 0); - EXPECT_EQ(resource->query().get(bullet2_id).delay, 3); - task_manager.run_all(); - task_manager.run_all(); - task_manager.run_all(); - task_manager.run_all(); - EXPECT_EQ(resource->query().get(bullet2_id).angleZ, -30); - EXPECT_EQ(resource->query().get(bullet2_id).vx, 3); - -} - -// -- RHYTHM GAME TESTS -- - +// #include "system/ecs.h" +// #include "game.h" +// #include "pch.h" +// +// using System::ECS::pid; +// using System::ECS::ResourcePool; +// using System::ECS::SyscallType; +// using System::ECS::TaskManager; +// using Game::Battle::BattleState; +// using Game::Battle::BulletHellState; +// using Game::Battle::PatternContainer; +// using namespace Game::BulletHell; +// using namespace Game::Physics; +// +// // -- BULLET HELL TESTS -- +// +// extern "C" long double get_delta_time() +// { +// return 1; // fake 60 FPS +// } +// +// using BulletHell_Resource = System::ECS::ResourceManager<1000,Player, Bullet, Position, Scale, Velocity, Rotation, Acceleration, AngularVelocity, Booming, Delay, Laser, Particle, Pattern, CircularCollider, RectangularCollider, Game::Render::Material, BattleState, BulletHellState, PatternContainer>; +// +// using BulletHell_Syscall = System::ECS::Syscall<1000,Player, Bullet, Position, Scale, Velocity, Rotation, Acceleration, AngularVelocity, Booming, Delay, Laser, Particle, Pattern, CircularCollider, RectangularCollider, Game::Render::Material, BattleState, BulletHellState, PatternContainer>; +// +// pid CreatePlayer(BulletHell_Resource *resource, CircularCollider cc = {}, +// Position pos = Position(0,0), Velocity vel = Velocity(0,0), Rotation rot = Rotation(0), Scale scl = {}, +// Acceleration acc = Acceleration(0,0), AngularVelocity avel = AngularVelocity(0)) +// { +// const pid id = resource->reserve_process(); +// resource->add_resource(id, Player()); +// resource->add_resource(id, CircularCollider(cc)); +// resource->add_resource(id, Position(pos)); +// resource->add_resource(id, Velocity(vel)); +// resource->add_resource(id,Rotation(rot)); +// resource->add_resource(id, Scale(scl)); +// resource->add_resource(id,Acceleration(acc)); +// resource->add_resource(id,AngularVelocity(avel)); +// return id; +// } +// +// pid CreateBullet1(BulletHell_Resource *resource, Bullet bullet = {}, +// Position pos = Position(0,0), Velocity vel = Velocity(0,0), Rotation rot = Rotation(0), +// Acceleration acc = Acceleration(0,999), AngularVelocity avel = AngularVelocity(0)) +// { +// const pid id = resource->reserve_process(); +// resource->add_resource(id, Bullet(bullet)); +// resource->add_resource(id, Position(pos)); +// resource->add_resource(id, Velocity(vel)); +// resource->add_resource(id,Rotation(rot)); +// resource->add_resource(id,Acceleration(acc)); +// resource->add_resource(id,AngularVelocity(avel)); +// return id; +// } +// +// pid CreateBullet2(BulletHell_Resource *resource, Bullet bullet = {}, Delay delay = Delay(0), +// Position pos = Position(0,0), Velocity vel = Velocity(0,0), Rotation rot = Rotation(0), +// Acceleration acc = Acceleration(0,999), AngularVelocity avel = AngularVelocity(0)) +// { +// const pid id = resource->reserve_process(); +// resource->add_resource(id, Bullet(bullet)); +// resource->add_resource(id, Delay(delay)); +// resource->add_resource(id, Position(pos)); +// resource->add_resource(id, Velocity(vel)); +// resource->add_resource(id,Rotation(rot)); +// resource->add_resource(id,Acceleration(acc)); +// resource->add_resource(id,AngularVelocity(avel)); +// return id; +// } +// +// pid CreateCircleBullet(BulletHell_Resource *resource, Bullet bullet = {}, CircularCollider cc = CircularCollider(), +// Position pos = Position(0,0), Velocity vel = Velocity(0,0), Rotation rot = Rotation(0), Scale scl = {}) +// { +// const pid id = resource->reserve_process(); +// resource->add_resource(id, Bullet(bullet)); +// resource->add_resource(id, CircularCollider(cc)); +// resource->add_resource(id, Position(pos)); +// resource->add_resource(id, Velocity(vel)); +// resource->add_resource(id,Rotation(rot)); +// resource->add_resource(id, Scale(scl)); +// resource->add_resource(id,Delay(0)); +// return id; +// } +// +// pid CreateRectangleBullet(BulletHell_Resource *resource, Bullet bullet = {}, RectangularCollider rc = RectangularCollider(), +// Position pos = Position(0,0), Velocity vel = Velocity(0,0), Rotation rot = Rotation(0), Scale scl = {}) +// { +// const pid id = resource->reserve_process(); +// resource->add_resource(id, Bullet(bullet)); +// resource->add_resource(id, RectangularCollider(rc)); +// resource->add_resource(id, Position(pos)); +// resource->add_resource(id, Velocity(vel)); +// resource->add_resource(id,Rotation(rot)); +// resource->add_resource(id, Scale(scl)); +// resource->add_resource(id,Delay(0)); +// return id; +// } +// +// +// pid CreateBoomerBullet(BulletHell_Resource *resource, Bullet bullet = {}, Booming booming = {},Delay delay = Delay(0), Particle particle = {}, +// Position pos = {}, CircularCollider cc = {}) +// { +// const pid id = resource->reserve_process(); +// resource->add_resource(id, Bullet(bullet)); +// resource->query().get(id).pierce = 999; +// resource->add_resource(id, Delay(delay)); +// resource->add_resource(id, Particle(particle)); +// resource->add_resource(id, Booming(booming)); +// resource->add_resource(id, Position(pos)); +// resource->add_resource(id,{}); +// resource->add_resource(id, Scale()); +// resource->add_resource(id, Game::Render::Material(nullptr,{},0,nullptr,{},0)); +// resource->add_resource(id, CircularCollider(cc)); +// return id; +// } +// +// pid CreateLaserBullet(BulletHell_Resource *resource, Bullet bullet = {}, Laser laser = {},Delay delay = Delay(0), Particle particle = {}, +// Rotation rot = {}, Scale scl = {}, RectangularCollider rc = {}) +// { +// const pid id = resource->reserve_process(); +// resource->add_resource(id, Bullet(bullet)); +// resource->query().get(id).pierce = 999; +// resource->add_resource(id, Delay(delay)); +// resource->add_resource(id, Particle(particle)); +// resource->add_resource(id, Laser(laser)); +// resource->add_resource(id, Position()); +// resource->add_resource(id,Rotation(rot)); +// resource->add_resource(id, Scale(scl)); +// resource->add_resource(id, RectangularCollider(rc)); +// resource->add_resource(id, Game::Render::Material(nullptr,{},0,nullptr,{},0)); +// return id; +// } +// +// pid CreateBattleState(BulletHell_Resource *resource) +// { +// const pid id = resource->reserve_process(); +// resource->add_resource(id, BattleState()); +// return id; +// } +// +// pid CreateBulletHellState(BulletHell_Resource *resource) +// { +// const pid id = resource->reserve_process(); +// resource->add_resource(id, BulletHellState()); +// return id; +// } +// +// PatternContainer InitPatternContainer() +// { +// using namespace Game::Battle; +// std::vector demo_step = { +// PatternStep(3, OP_ADD, 0b0100, 15), // 3s Rot+15 +// PatternStep(3, OP_ADD, 0b0100, -15), // 3s Rot-15 +// PatternStep(0, OP_SET, 0b1000, 3), // 3s Vel=3 +// }; +// std::vector demo_pattern = { +// PatternSequence(false, 0, 2), +// PatternSequence(false, 1, 2), +// PatternSequence(true, 0, 2), +// PatternSequence(true, 1, 2), +// }; +// auto demo_pattern_container = PatternContainer(demo_step,demo_pattern); +// return {PatternContainer(demo_pattern_container)}; +// } +// +// pid CreatePatternContainer(BulletHell_Resource *resource) +// { +// const pid id = resource->reserve_process(); +// resource->add_resource(id, PatternContainer(InitPatternContainer())); +// return id; +// } +// +// // Test Entity Instantiation +// +// TEST(Game, instantiate_player) +// { +// TaskManager task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// +// const pid id = CreatePlayer(resource); +// EXPECT_TRUE(resource->query().get(id).is_active); +// EXPECT_TRUE(resource->query().has(id)); +// EXPECT_TRUE(resource->query().has(id)); +// EXPECT_TRUE(resource->query().has(id)); +// EXPECT_TRUE(resource->query().has(id)); +// EXPECT_TRUE(resource->query().has(id)); +// EXPECT_TRUE(resource->query().has(id)); +// } +// +// +// +// TEST(Game, instantiate_bullets) +// { +// TaskManager task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// +// const pid id_1 = CreateBullet1(resource); +// +// EXPECT_TRUE(resource->query().has(id_1)); +// EXPECT_TRUE(resource->query().has(id_1)); +// EXPECT_TRUE(resource->query().has(id_1)); +// EXPECT_EQ(resource->query().get(id_1).damage, 0); +// +// const pid id_2 = CreateBullet1(resource); +// const pid id_3 = CreateBullet1(resource); +// +// EXPECT_TRUE(resource->query().has(id_2)); +// EXPECT_TRUE(resource->query().has(id_2)); +// EXPECT_TRUE(resource->query().has(id_2)); +// EXPECT_TRUE(resource->query().has(id_3)); +// EXPECT_TRUE(resource->query().has(id_3)); +// EXPECT_TRUE(resource->query().has(id_3)); +// EXPECT_EQ(resource->query().get(id_2).damage, 0); +// EXPECT_EQ(resource->query().get(id_3).damage, 0); +// } +// +// TEST(Game, value_change) +// { +// TaskManager task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// +// const pid id_1 = CreateBullet1(resource); +// const pid id_2 = CreateBullet1(resource); +// +// EXPECT_TRUE(resource->query().has(id_1)); +// EXPECT_TRUE(resource->query().has(id_2)); +// EXPECT_EQ(resource->query().get(id_1).damage, 0); +// EXPECT_EQ(resource->query().get(id_2).damage, 0); +// +// resource->query().get(id_1).damage = 1; +// +// EXPECT_TRUE(resource->query().has(id_1)); +// EXPECT_TRUE(resource->query().has(id_2)); +// EXPECT_EQ(resource->query().get(id_1).damage, 1); +// EXPECT_EQ(resource->query().get(id_2).damage, 0); +// } +// +// // TODO: Tests failed from here onwards with a compile error, trying to find cause of error +// +// //Movement +// TEST(Game, bullet_movement1) +// { +// TaskManager> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// +// const pid id = CreateBullet1(resource); +// resource->query().set(id,Position(1,1)); +// resource->query().set(id,Velocity(2,3)); +// +// EXPECT_EQ(resource->query().get(id).x, 1); +// EXPECT_EQ(resource->query().get(id).y, 1); +// EXPECT_EQ(resource->query().get(id).vx, 2); +// EXPECT_EQ(resource->query().get(id).vy, 3); +// +// //Move 1 time : 1,1 -> 3,4 +// task_manager.run_all(); +// +// EXPECT_EQ(resource->query().get(id).x, 3); +// EXPECT_EQ(resource->query().get(id).y, 4); +// EXPECT_EQ(resource->query().get(id).vx, 2); +// EXPECT_EQ(resource->query().get(id).vy, 3); +// +// //Move 2 times : 3,4 -> 7,10 +// task_manager.run_all(); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).x, 7); +// EXPECT_EQ(resource->query().get(id).y, 10); +// EXPECT_EQ(resource->query().get(id).vx, 2); +// EXPECT_EQ(resource->query().get(id).vy, 3); +// } +// +// +// +// TEST(Game, bullet_movement2) +// { +// TaskManager> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// +// const pid id = CreateBullet1(resource); +// resource->query().set(id,Position(0,0)); +// resource->query().set(id,Velocity(4,0)); +// resource->query().set(id,Rotation(30)); +// +// //Object rotate 45 degree -> vx = 4*cos(angle) ; vy = 4*sin(angle) +// task_manager.run_all(); +// +// EXPECT_EQ(round(resource->query().get(id).x), round(4*sqrt(3)/2)); +// EXPECT_EQ(resource->query().get(id).y, 4*1/2); +// EXPECT_EQ(resource->query().get(id).vx, 4); +// EXPECT_EQ(resource->query().get(id).vy, 0); +// EXPECT_EQ(resource->query().get(id).angleZ, 30); +// +// resource->query().set(id,Position(3,2)); +// resource->query().set(id,Velocity(2,3)); +// resource->query().set(id,Rotation(-90)); +// +// //Object rotate -90 degree -> vx = vy ; vy = -vx +// task_manager.run_all(); +// task_manager.run_all(); +// +// EXPECT_EQ(round(resource->query().get(id).x), 9); +// EXPECT_EQ(round(resource->query().get(id).y), -2); +// EXPECT_EQ(resource->query().get(id).vx, 2); +// EXPECT_EQ(resource->query().get(id).vy, 3); +// EXPECT_EQ(resource->query().get(id).angleZ, -90); +// } +// +// TEST(Game, bullet_movement3) +// { +// TaskManager, acceleration_system> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// +// const pid id = CreateBullet1(resource); +// resource->query().set(id,Position(1,1)); +// resource->query().set(id,Velocity(2,3)); +// resource->query().set(id,Acceleration(1,-1,999,-999)); +// +// //Move 1 time : Pos 1,1 -> 3,4 then Vel 2,3 -> 3,2 +// task_manager.run_all(); +// +// EXPECT_EQ(resource->query().get(id).x, 3); +// EXPECT_EQ(resource->query().get(id).y, 4); +// EXPECT_EQ(resource->query().get(id).vx, 3); +// EXPECT_EQ(resource->query().get(id).vy, 2); +// +// //Move 1 times : 3,4 -> 6,7 then Vel 2,3 -> 4,1 +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).x, 6); +// EXPECT_EQ(resource->query().get(id).y, 6); +// EXPECT_EQ(resource->query().get(id).vx, 4); +// EXPECT_EQ(resource->query().get(id).vy, 1); +// +// //Move 2 times : 6,6 -> 15,5 then Vel 4,1 -> 6,-1 +// task_manager.run_all(); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).x, 15); +// EXPECT_EQ(resource->query().get(id).y, 7); +// EXPECT_EQ(resource->query().get(id).vx, 6); +// EXPECT_EQ(resource->query().get(id).vy, -1); +// } +// +// TEST(Game, bullet_movement4) +// { +// TaskManager> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// +// const pid id = CreateBullet1(resource); +// resource->query().set(id,Rotation(30)); +// resource->query().set(id,AngularVelocity(-15)); +// +// //Rotate 1 time : 30 -> 15 +// task_manager.run_all(); +// +// EXPECT_EQ(resource->query().get(id).angleZ, 15); +// EXPECT_EQ(resource->query().get(id).v, -15); +// +// //Rotate 2 time : 15 -> -15 +// task_manager.run_all(); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).angleZ, -15); +// EXPECT_EQ(resource->query().get(id).v, -15); +// } +// +// TEST(Game, bullet_movement5) +// { +// TaskManager, acceleration_system, rotation_system> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// +// const pid id = CreateBullet1(resource); +// resource->query().set(id,Position(1,1)); +// resource->query().set(id,Velocity(1,1)); +// resource->query().set(id,Acceleration(1,-1,999,999,-999,-999)); +// resource->query().set(id,Rotation(0)); +// resource->query().set(id,AngularVelocity(90)); +// +// task_manager.run_all(); +// +// EXPECT_EQ(resource->query().get(id).x, 2); +// EXPECT_EQ(resource->query().get(id).y, 2); +// +// task_manager.run_all(); +// EXPECT_EQ(round(resource->query().get(id).x), 2); +// EXPECT_EQ(round(resource->query().get(id).y), 4); +// +// task_manager.run_all(); +// EXPECT_EQ(round(resource->query().get(id).x), -1); +// EXPECT_EQ(round(resource->query().get(id).y), 5); +// +// task_manager.run_all(); +// EXPECT_EQ(round(resource->query().get(id).x), -3); +// EXPECT_EQ(round(resource->query().get(id).y), 1); +// } +// +// TEST(Game, bullets_movement6) +// { +// TaskManager, acceleration_system, rotation_system> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// +// const pid id_1 = CreateBullet1(resource,{},Position(1,1),Velocity(1,1),Rotation(0),Acceleration(1,-1,999,999,-999,-999),AngularVelocity(90)); +// const pid id_2 = CreateBullet1(resource,{},Position(2,0),Velocity(2,3),Rotation(30),{},AngularVelocity(30)); +// const pid id_3 = CreateBullet1(resource, {},Position(-1,-3),Velocity(2,1),Rotation(180),Acceleration(-1,-2,999,999,-999,-999), {}); +// +// EXPECT_EQ(resource->query().get(id_1).x, 1); +// EXPECT_EQ(resource->query().get(id_1).y, 1); +// EXPECT_EQ(resource->query().get(id_2).x, 2); +// EXPECT_EQ(resource->query().get(id_2).y, 0); +// EXPECT_EQ(resource->query().get(id_3).x, -1); +// EXPECT_EQ(resource->query().get(id_3).y, -3); +// task_manager.run_all(); +// +// EXPECT_EQ(resource->query().get(id_1).x, 2); +// EXPECT_EQ(resource->query().get(id_1).y, 2); +// EXPECT_EQ(round(resource->query().get(id_2).x), 2); +// EXPECT_EQ(round(resource->query().get(id_2).y), 4); +// EXPECT_EQ(resource->query().get(id_3).x, -3); +// EXPECT_EQ(resource->query().get(id_3).y, -4); +// +// task_manager.run_all(); +// task_manager.run_all(); +// +// EXPECT_EQ(round(resource->query().get(id_1).x), -1); +// EXPECT_EQ(round(resource->query().get(id_1).y), 5); +// EXPECT_EQ(round(resource->query().get(id_2).x), -2); +// EXPECT_EQ(round(resource->query().get(id_2).y), 9); +// EXPECT_EQ(round(resource->query().get(id_3).x), -4); +// EXPECT_EQ(resource->query().get(id_3).y, 0); +// +// } +// +// TEST(Game, bullets_damagable) +// { +// TaskManager, delay_system> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// +// const pid id = CreateBullet2(resource,Bullet(3),Delay(2)); +// EXPECT_FALSE(resource->query().get(id).is_damageable); +// task_manager.run_all(); +// EXPECT_FALSE(resource->query().get(id).is_damageable); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).delay, 0); +// EXPECT_FALSE(resource->query().get(id).is_damageable); +// task_manager.run_all(); +// EXPECT_TRUE(resource->query().get(id).is_damageable); +// task_manager.run_all(); +// EXPECT_TRUE(resource->query().get(id).is_damageable); +// EXPECT_EQ(resource->query().get(id).delay, 0); +// } +// +// TEST(Game, bullets_booming) +// { +// TaskManager, particle_system, boomer_system> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// +// const pid id = CreateBoomerBullet(resource, {}, Booming(3,2), Delay(1002), Particle(1009) ); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).delay, 1001); +// EXPECT_EQ(resource->query().get(id).scaleX, 3); +// EXPECT_EQ(resource->query().get(id).scaleY, 3); +// EXPECT_EQ(resource->query().get(id).color.a, 0.25); +// for (int i=0;i<1000;i++) task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).delay, 1); +// EXPECT_EQ(resource->query().get(id).scaleX, 0); +// EXPECT_EQ(resource->query().get(id).scaleY, 0); +// EXPECT_EQ(resource->query().get(id).color.a, 1); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).delay, 0); +// EXPECT_EQ(resource->query().get(id).scaleX, 1.5f); +// EXPECT_EQ(resource->query().get(id).scaleY, 1.5f); +// EXPECT_EQ(resource->query().get(id).color.a, 1); +// EXPECT_EQ(resource->query().get(id).lifetime, 7); +// task_manager.run_all(); +// task_manager.run_all(); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).delay, 1); +// EXPECT_GE(resource->query().get(id).scaleX, 2.75f); +// EXPECT_GE(resource->query().get(id).scaleY, 2.75f); +// EXPECT_EQ(resource->query().get(id).color.a, 1); +// EXPECT_EQ(resource->query().get(id).lifetime, 4); +// task_manager.run_all(); +// EXPECT_LE(resource->query().get(id).scaleY, 2.75f); +// task_manager.run_all(); +// EXPECT_EQ(round(resource->query().get(id).scaleX), 0); +// } +// +// TEST(Game, bullets_laser) +// { +// TaskManager, particle_system, laser_system> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// +// const pid id = CreateLaserBullet(resource, {}, Laser(2,2,3,3), Delay(1003), Particle(1010),Rotation(30) ); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).delay, 1002); +// EXPECT_EQ(resource->query().get(id).scaleX, 3); +// EXPECT_EQ(resource->query().get(id).scaleY, 1); +// EXPECT_EQ(round(resource->query().get(id).x * 100)/100.f, 3.3f); +// EXPECT_EQ(resource->query().get(id).y, 2.75f); +// EXPECT_EQ(resource->query().get(id).color.a, 0.25); +// for (int i=0;i<1001;i++) task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).delay, 1); +// EXPECT_EQ(resource->query().get(id).scaleX, 0); +// EXPECT_EQ(resource->query().get(id).scaleY, 1); +// EXPECT_EQ(resource->query().get(id).x, 2); +// EXPECT_EQ(resource->query().get(id).y, 2); +// EXPECT_EQ(resource->query().get(id).color.a, 1); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).delay, 0); +// EXPECT_EQ(resource->query().get(id).scaleX, 1); +// EXPECT_EQ(round(resource->query().get(id).x * 100)/100.f, 2.43f); +// EXPECT_EQ(resource->query().get(id).y, 2.25); +// EXPECT_EQ(resource->query().get(id).color.a, 1); +// EXPECT_EQ(resource->query().get(id).lifetime, 7); +// task_manager.run_all(); +// EXPECT_EQ(round(resource->query().get(id).x * 100)/100.f, 2.87f); +// EXPECT_EQ(resource->query().get(id).y, 2.5); +// task_manager.run_all(); +// +// EXPECT_EQ(resource->query().get(id).delay, 1); +// EXPECT_GE(resource->query().get(id).scaleX, 3); +// EXPECT_EQ(round(resource->query().get(id).x * 100)/100.f, 3.3f); +// EXPECT_EQ(resource->query().get(id).y, 2.75f); +// EXPECT_EQ(resource->query().get(id).color.a, 1); +// EXPECT_EQ(resource->query().get(id).lifetime, 5); +// task_manager.run_all(); +// task_manager.run_all(); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(id).scaleX, 0); +// EXPECT_EQ(round(resource->query().get(id).x * 100)/100.f, 5.9f); +// EXPECT_EQ(resource->query().get(id).y, 4.25); +// } +// +// // Collision +// TEST(Game, bullet_collision1) +// { +// TaskManager, bullet_system> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// const pid bhs = CreateBulletHellState(resource); +// +// CreatePlayer(resource, CircularCollider(2)); +// const pid bullet1_id = CreateCircleBullet(resource,Bullet(5,1), CircularCollider(1), Position(4,4)); +// // Miss +// task_manager.run_all(); +// EXPECT_TRUE(resource->query().has(bullet1_id)); +// EXPECT_LE(resource->query().get(bhs).iframe_time,0); +// +// // Near Collide +// resource->query().get(bullet1_id) = Position(3.f,0); +// task_manager.run_all(); +// EXPECT_TRUE(resource->query().has(bullet1_id)); +// EXPECT_LE(resource->query().get(bhs).iframe_time,0); +// +// // Collide +// resource->query().get(bullet1_id) = Position(2.99f,0.f); +// task_manager.run_all(); +// EXPECT_FALSE(resource->query().has(bullet1_id)); +// EXPECT_GT(resource->query().get(bhs).iframe_time,0); +// +// // Reset +// resource->query().get(bhs).iframe_time = 0; +// const pid bullet2_id = CreateRectangleBullet(resource, Bullet(5,1),RectangularCollider(1,2), Position(4,0)); +// +// // Miss +// task_manager.run_all(); +// EXPECT_TRUE(resource->query().has(bullet2_id)); +// EXPECT_LE(resource->query().get(bhs).iframe_time,0); +// +// // Near Collide & Wide pass +// resource->query().get(bullet2_id) = Position(2.99f,0); +// task_manager.run_all(); +// EXPECT_TRUE(resource->query().has(bullet2_id)); +// EXPECT_LE(resource->query().get(bhs).iframe_time,0); +// +// // Collide +// resource->query().get(bullet2_id) = Position(0.f,2.99f); +// task_manager.run_all(); +// EXPECT_FALSE(resource->query().has(bullet2_id)); +// EXPECT_GT(resource->query().get(bhs).iframe_time,0); +// } +// +// TEST(Game, bullet_collision2) +// { +// TaskManager, bullet_system> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// const pid bhs = CreateBulletHellState(resource); +// +// CreatePlayer(resource, CircularCollider(2)); +// pid bullet_id = CreateCircleBullet(resource,Bullet(5), CircularCollider(1.5f,1.f), Position(2,-2.3f), {},Rotation(30)); +// // Miss +// task_manager.run_all(); +// EXPECT_TRUE(resource->query().has(bullet_id)); +// EXPECT_LE(resource->query().get(bhs).iframe_time,0); +// +// // Collide +// resource->query().get(bullet_id) = Rotation(0); +// task_manager.run_all(); +// EXPECT_FALSE(resource->query().has(bullet_id)); +// EXPECT_GT(resource->query().get(bhs).iframe_time,0); +// +// // Reset +// resource->query().get(bhs).iframe_time = 0; +// bullet_id = CreateRectangleBullet(resource, Bullet(5),RectangularCollider(1,2), Position(-2.5f,0.62f),{},Rotation(0)); +// +// // Miss +// task_manager.run_all(); +// EXPECT_TRUE(resource->query().has(bullet_id)); +// EXPECT_LE(resource->query().get(bhs).iframe_time,0); +// +// // Collide +// resource->query().get(bullet_id) = Rotation(60); +// task_manager.run_all(); +// EXPECT_FALSE(resource->query().has(bullet_id)); +// EXPECT_GT(resource->query().get(bhs).iframe_time,0); +// } +// +// TEST(Game, bullet_collision3) +// { +// TaskManager, bullet_collision, bullet_system> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// const pid bhs = CreateBulletHellState(resource); +// +// CreatePlayer(resource, CircularCollider(2)); +// // Miss +// const pid bullet1_id = CreateCircleBullet(resource,Bullet(5), CircularCollider(1.f,1.f), Position(5,5), Velocity(1),Rotation(-90)); +// const pid bullet2_id = CreateRectangleBullet(resource,Bullet(5), RectangularCollider(1.f,1.f), Position(1.5f,-5.2f), Velocity(1.5),Rotation(135)); +// //Hit +// const pid bullet3_id = CreateCircleBullet(resource,Bullet(5), CircularCollider(1.f,0.5f), Position(3.5f,1.5f), Velocity(0.5),Rotation(210)); +// const pid bullet4_id = CreateCircleBullet(resource,Bullet(5), CircularCollider(1.f,1.f), Position(0,-5), Velocity(0.75),Rotation(90)); +// const pid bullet5_id = CreateRectangleBullet(resource,Bullet(5), RectangularCollider(1.5f,1.f), Position(-4,0), Velocity(0.5),Rotation(-30)); +// +// for (int i=0;i<10;i++) +// { +// task_manager.run_all(); +// resource->query().get(bhs).iframe_time = 0; +// } +// +// EXPECT_TRUE(resource->query().has(bullet1_id)); +// EXPECT_TRUE(resource->query().has(bullet2_id)); +// EXPECT_FALSE(resource->query().has(bullet3_id)); +// EXPECT_FALSE(resource->query().has(bullet4_id)); +// EXPECT_FALSE(resource->query().has(bullet5_id)); +// } +// +// TEST(Game, bullet_collision4) +// { +// TaskManager, particle_system, boomer_system, laser_system, bullet_collision, bullet_system> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// const pid bhs = CreateBulletHellState(resource); +// +// CreatePlayer(resource, CircularCollider(2)); +// //Missing boom +// CreateBoomerBullet(resource,Bullet(false, 5), Booming(1.5f, 1), Delay(4), Particle(3), Position(4,0), CircularCollider(1.f,1.f)); +// //Hit Laser +// CreateLaserBullet(resource,Bullet(false, 5), Laser(4.f, -4.f, 10, 1), Delay(6), Particle(8), Rotation(135), Scale(2),RectangularCollider(1.f,1.f)); +// // Hit boom +// CreateBoomerBullet(resource,Bullet(false, 5), Booming(3.f, 1), Delay(11), Particle(11), Position(4,0),CircularCollider(1.f,1.f)); +// // Rotate to hit laser +// const pid laser_id = CreateLaserBullet(resource,Bullet(false, 5), Laser(7.f, 0, 10, 1), Delay(20), Particle(20), Rotation(135), Scale(2),RectangularCollider(1.f,1.f)); +// +// task_manager.run_all(); +// task_manager.run_all(); +// EXPECT_LE(resource->query().get(bhs).iframe_time,0); +// +// // Boomer 1 Fired +// for (int i=0;i<2;i++) task_manager.run_all(); +// EXPECT_LE(resource->query().get(bhs).iframe_time,0); +// +// // Laser Fired +// for (int i=0;i<3;i++) task_manager.run_all(); +// EXPECT_GT(resource->query().get(bhs).iframe_time,0); +// +// // Laser Gone +// for (int i=0;i<2;i++) task_manager.run_all(); +// resource->query().get(bhs).iframe_time = 0; +// task_manager.run_all(); +// EXPECT_LE(resource->query().get(bhs).iframe_time,0); +// task_manager.run_all(); +// +// // Boomer2 Come +// EXPECT_GT(resource->query().get(bhs).iframe_time,0); +// +// for (int i=0;i<3;i++) task_manager.run_all(); +// resource->query().get(bhs).iframe_time = 0; +// task_manager.run_all(); +// +// // LaserSpin Come +// EXPECT_GT(resource->query().get(laser_id).scaleX,0); +// EXPECT_LE(resource->query().get(bhs).iframe_time,0); +// for (int i=0;i<5;i++) +// { +// task_manager.run_all(); +// resource->query().get(laser_id).angleZ += 5.f; +// EXPECT_EQ(resource->query().get(laser_id).angleZ, 140 + 5*i); +// if (i==4) +// EXPECT_GT(resource->query().get(bhs).iframe_time,0); +// else +// EXPECT_LE(resource->query().get(bhs).iframe_time,0); +// } +// +// } +// +// TEST(Game, bullet_collision5) +// { +// TaskManager, bullet_collision, bullet_system> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// CreateBulletHellState(resource); +// +// const pid player_id = CreatePlayer(resource, CircularCollider(2)); +// const pid bullet1_id = CreateCircleBullet(resource,Bullet(5), CircularCollider(1), Position(5,5)); +// +// resource->query().get(player_id).vx = 0.75f; +// resource->query().get(player_id).vy = 0.75f; +// +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(player_id).x, 0.75f); +// EXPECT_EQ(resource->query().get(player_id).y, 0.75f); +// EXPECT_TRUE(resource->query().has(bullet1_id)); +// +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(player_id).x, 1.5f); +// EXPECT_EQ(resource->query().get(player_id).y, 1.5f); +// EXPECT_TRUE(resource->query().has(bullet1_id)); +// +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(player_id).x, 2.25f); +// EXPECT_EQ(resource->query().get(player_id).y, 2.25f); +// EXPECT_TRUE(resource->query().has(bullet1_id)); +// +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(player_id).x, 3.f); +// EXPECT_EQ(resource->query().get(player_id).y, 3.f); +// EXPECT_FALSE(resource->query().has(bullet1_id)); +// } +// +// TEST(Game, bullet_pattern1) +// { +// TaskManager, particle_system, acceleration_system, movement_system, rotation_system> task_manager{}; +// BulletHell_Resource *resource = task_manager.get_rm(); +// CreateBattleState(resource); +// CreatePatternContainer(resource); +// +// const pid bullet1_id = CreateBullet1(resource,{}, Position(0,0), Velocity(2.5), Rotation(45)); +// const pid bullet2_id = CreateBullet1(resource,{}, Position(2,3)); +// +// resource->add_resource(bullet1_id, Pattern(1)); +// resource->add_resource(bullet2_id, Pattern(4)); +// +// task_manager.run_all(); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(bullet1_id).delay, 2); +// EXPECT_EQ(resource->query().get(bullet2_id).delay, 2); +// task_manager.run_all(); +// task_manager.run_all(); +// EXPECT_EQ(round(resource->query().get(bullet1_id).x * 100)/100.f, 7.07f); +// EXPECT_EQ(round(resource->query().get(bullet1_id).y * 100)/100.f, 7.07f); +// EXPECT_EQ(resource->query().get(bullet2_id).x, 2); +// EXPECT_EQ(resource->query().get(bullet2_id).y, 3); +// EXPECT_EQ(resource->query().get(bullet1_id).delay, 0); +// EXPECT_EQ(resource->query().get(bullet2_id).delay, 0); +// task_manager.run_all(); +// EXPECT_EQ(round(resource->query().get(bullet1_id).x * 100)/100.f, 8.57f); +// EXPECT_EQ(round(resource->query().get(bullet1_id).y * 100)/100.f, 9.67f); +// EXPECT_EQ(round(resource->query().get(bullet2_id).x * 100)/100.f, 4.90f); +// EXPECT_EQ(round(resource->query().get(bullet2_id).y * 100)/100.f, 2.22f); +// EXPECT_EQ(resource->query().get(bullet2_id).angleZ, -15); +// EXPECT_EQ(resource->query().get(bullet2_id).vx, 3); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(bullet1_id).delay, 0); +// EXPECT_EQ(resource->query().get(bullet2_id).delay, 3); +// task_manager.run_all(); +// task_manager.run_all(); +// task_manager.run_all(); +// task_manager.run_all(); +// EXPECT_EQ(resource->query().get(bullet2_id).angleZ, -30); +// EXPECT_EQ(resource->query().get(bullet2_id).vx, 3); +// +// } +// +// // -- RHYTHM GAME TESTS -- +// diff --git a/test/rhythm_test.cpp b/test/rhythm_test.cpp deleted file mode 100644 index 9a0eaa4f..00000000 --- a/test/rhythm_test.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "system/ecs.h" -#include "game.h" -#include "pch.h" - -using System::ECS::pid; -using System::ECS::ResourceManager; -using System::ECS::ResourcePool; -using System::ECS::Syscall; -using System::ECS::SyscallType; -using System::ECS::TaskManager; -using Game::Battle::BattleState; - -using namespace Game::Rhythm; - -using NoteResource = ResourceManager<1000, Lane, Timing, TimingEnd, NoteSpeed, HoldActive, NoteType, BattleState>; - -using NoteSyscall = Syscall<1000, Lane, Timing, TimingEnd, NoteSpeed, HoldActive, NoteType, BattleState>; - -pid CreateNote(NoteResource *resource, - Lane lane = Lane(0), Timing timing = Timing(1000), TimingEnd timing_end = TimingEnd(0), - NoteSpeed note_speed = NoteSpeed(1.0f), NoteType note_type = NoteType(0)) -{ - const pid id = resource->reserve_process(); - - resource->add_resource(id, Lane(lane)); - resource->add_resource(id, Timing(timing)); - resource->add_resource(id, TimingEnd(timing_end)); - resource->add_resource(id, NoteSpeed(note_speed)); - resource->add_resource(id, NoteType(note_type)); - return (id); -} - -// Test Entity Instantiation - -TEST(Game, instantiate_note) -{ - TaskManager task_manager{}; - NoteResource *resource = task_manager.get_rm(); - - const pid id = CreateNote(resource); - EXPECT_TRUE(resource->query().has(id)); - EXPECT_TRUE(resource->query().has(id)); - EXPECT_TRUE(resource->query().has(id)); - EXPECT_TRUE(resource->query().has(id)); - EXPECT_FALSE(resource->query().get(id).hold_active); - EXPECT_TRUE(resource->query().has(id)); -} \ No newline at end of file