diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4a1c397..5fad248 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -15,16 +15,11 @@ repos: - id: no-commit-to-branch args: [--branch, master, --branch, develop] - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v14.0.6 + rev: v15.0.7 hooks: - id: clang-format types_or: [c++, c] args: ["-style=file"] - - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.3.1 - hooks: - - id: forbid-crlf - - id: forbid-tabs #- repo: https://github.com/pocc/pre-commit-hooks # rev: v1.3.5 # hooks: diff --git a/godot/characters/enemies/WalkingEnemy.gd b/godot/characters/enemies/WalkingEnemy.gd index f3b424f..1542fd8 100644 --- a/godot/characters/enemies/WalkingEnemy.gd +++ b/godot/characters/enemies/WalkingEnemy.gd @@ -6,39 +6,50 @@ export var direction = -1 export var detect_edges = true export var speed = 25 export var gravity = 9.8 +var timer := Timer.new() func _ready() -> void: - if direction == 1: - $AnimatedSprite.flip_h = true - $FloorChecker.position.x = $CollisionShape2D.shape.get_extents().x * direction - $FloorChecker.enabled = detect_edges - Event.connect("level_loaded", self, "_on_level_loaded") + if direction == 1: + $AnimatedSprite.flip_h = true + $FloorChecker.position.x = $CollisionShape2D.shape.get_extents().x * direction + $FloorChecker.enabled = detect_edges + Event.connect("level_loaded", self, "_on_level_loaded") + + add_child(timer) + timer.wait_time = 0.1 + var err = timer.connect("timeout", self, "_on_timer_timeout") + if err == OK: + timer.start() func _physics_process(_delta: float) -> void: - if is_on_wall() or not $FloorChecker.is_colliding() and is_on_floor() and $FloorChecker.enabled: - direction *= -1 - $AnimatedSprite.flip_h = not $AnimatedSprite.flip_h - $FloorChecker.position.x = $CollisionShape2D.shape.get_extents().x * direction + if is_on_wall() or not $FloorChecker.is_colliding() and is_on_floor() and $FloorChecker.enabled: + direction *= -1 + $AnimatedSprite.flip_h = not $AnimatedSprite.flip_h + $FloorChecker.position.x = $CollisionShape2D.shape.get_extents().x * direction - velocity.y += gravity - velocity.x = speed * direction - velocity = move_and_slide(velocity, Vector2.UP) + velocity.y += gravity + velocity.x = speed * direction + velocity = move_and_slide(velocity, Vector2.UP) - for i in get_slide_count(): - var collision = get_slide_collision(i) - if collision.collider.name == "Player": - Event.emit_signal("player_touched", 3) + for i in get_slide_count(): + var collision = get_slide_collision(i) + if collision.collider.name == "Player": + Event.emit_signal("player_touched", 3) - Event.emit_signal("object_updated", self.get_name(), "Walking", global_position, velocity) - Event.emit_signal("report_object", self.get_name(), "Walking", global_position, velocity) + Event.emit_signal("object_updated", self.get_name(), "Walking", global_position, velocity) func squash() -> void: - Event.emit_signal("object_removed", self.get_name()) - queue_free() + Event.emit_signal("object_removed", self.get_name()) + queue_free() func _on_level_loaded() -> void: - Event.emit_signal("object_created", self.get_name(), "Walking", global_position, Vector2(0, 0)) + Event.emit_signal("object_created", self.get_name(), "Walking", global_position, Vector2(0, 0)) + + +func _on_timer_timeout() -> void: + Event.emit_signal("report_object", self.get_name(), "walking", global_position, velocity) + timer.start() diff --git a/godot/collectables/coin/Coin.gd b/godot/collectables/coin/Coin.gd index 7ab13fe..e31cd98 100644 --- a/godot/collectables/coin/Coin.gd +++ b/godot/collectables/coin/Coin.gd @@ -1,5 +1,17 @@ extends Area2D -func _process(_delta: float) -> void: - Event.emit_signal("report_object", self.get_name(), "not collected", global_position, Vector2()) +var timer := Timer.new() + + +func _ready() -> void: + add_child(timer) + timer.wait_time = 0.1 + var err = timer.connect("timeout", self, "_on_timer_timeout") + if err == OK: + timer.start() + + +func _on_timer_timeout() -> void: + Event.emit_signal("report_object", self.get_name(), "not collected", global_position, Vector2()) + timer.start() diff --git a/godot/goal/Goal.gd b/godot/goal/Goal.gd index f9aa6db..07f76af 100644 --- a/godot/goal/Goal.gd +++ b/godot/goal/Goal.gd @@ -1,5 +1,17 @@ extends Area2D -func _process(_delta: float) -> void: - Event.emit_signal("report_object", self.get_name(), "not touched", global_position, Vector2()) +var timer := Timer.new() + + +func _ready() -> void: + add_child(timer) + timer.wait_time = 0.1 + var err = timer.connect("timeout", self, "_on_timer_timeout") + if err == OK: + timer.start() + + +func _on_timer_timeout() -> void: + Event.emit_signal("report_object", self.get_name(), "not touched", global_position, Vector2()) + timer.start() diff --git a/src/player/AI.cpp b/src/player/AI.cpp index d667bf0..4b80c27 100644 --- a/src/player/AI.cpp +++ b/src/player/AI.cpp @@ -1,10 +1,13 @@ #include "player/AI.h" +#include + void alai::player::AI::_register_methods() { godot::register_method("_ready", &AI::_ready); godot::register_method("_physics_process", &AI::_physics_process); godot::register_method("_on_object_report", &AI::_on_object_report); + godot::register_method("_on_timer_timeout", &AI::_on_timer_timeout); } alai::player::AI::AI() @@ -27,78 +30,142 @@ void alai::player::AI::_ready() event = get_node("/root/Event"); event->connect("report_object", this, "_on_object_report"); + + add_child(timer); + timer->set_wait_time(1); + auto err = timer->connect("timeout", this, "_on_timer_timeout"); + if (err != godot::Error::OK) + { + godot::Godot::print("Timer could not be connected!"); + } } void alai::player::AI::_physics_process(float delta) { + auto player_position = parent->get_global_position(); + auto player_direction = parent->get_velocity(); + bool walking = false; + + std::sort(entities.begin(), entities.end(), [](const Entity &a, const Entity &b) -> bool + { + return a.position.x < b.position.x; + }); + while (!entities.empty()) { - const auto& entity = entities.front(); + const auto &entity = entities.front(); - if (obelisk->query(entity.name.utf8().get_data(), "is", "touchable") > 0) + auto entityName = entity.name.utf8().get_data(); + + double distance = std::abs(player_position.x - entity.position.x); + + int jump_distance = -1; + auto action = obelisk->queryAction(entityName, "is", "bouncable"); + if (action == "jump on") + { + jump_distance = 100; + } + else if (action == "jump over") + { + jump_distance = 50; + } + + if (lastEntity == "") + { + if (jump_distance != -1 && distance < jump_distance) + { + lastEntity = entity.originalName; + timer->start(); + if (player_position.x < entity.position.x) + { + auto ev = godot::InputEventAction()._new(); + ev->set_action("jump"); + ev->set_pressed(true); + _input->parse_input_event(ev); + } + else if (player_position.x > entity.position.x) + { + auto ev = godot::InputEventAction()._new(); + ev->set_action("jump"); + ev->set_pressed(true); + _input->parse_input_event(ev); + } + } + } + + if (obelisk->query(entityName, "is", "gold") > 0 && obelisk->query(std::string("gold ") + entityName, "is", "collectable") > 0) { - auto player_position = parent->get_global_position(); if (entity.position.x > player_position.x) { - _input->action_press("right"); + if (!walking && !_input->is_action_pressed("left") && !_input->is_action_pressed("right")) + { + walking = true; + _input->action_press("right"); + } } else { - _input->action_press("left"); - } - } - - if (obelisk->query(entity.name.utf8().get_data(), "is", "bouncable") > 0) - { - auto player_position = parent->get_global_position(); - auto distance = sqrt(pow(player_position.x - entity.position.x, 2) + pow(player_position.y - entity.position.y, 2)); - int jump_distance = 0; - - auto player_direction = parent->get_velocity(); - - auto action = obelisk->queryAction(entity.name.utf8().get_data(), "is", "bouncable"); - if (action == "jump on") - { - jump_distance = 100; - } - else if (action == "jump over") - { - jump_distance = 50; - } - - if (distance < jump_distance) - { - if (_input->is_action_pressed("right") && player_position.x < entity.position.x) + if (!walking && !_input->is_action_pressed("left") && !_input->is_action_pressed("right")) { - _input->action_press("jump"); - } - else if (_input->is_action_pressed("left") && player_position.x > entity.position.x) - { - _input->action_press("jump"); + walking = true; + _input->action_press("left"); } } - } - if (obelisk->query(entity.name.utf8().get_data(), "is", "gold") > 0 && obelisk->query(std::string("gold ") + entity.name.utf8().get_data(), "is", "collectable") > 0) - { - auto player_position = parent->get_global_position(); - float distance = player_position.x - entity.position.x; if (distance > -10 && distance < 10 && player_position.y - entity.position.y > 20) { - _input->action_press("jump"); + timer->stop(); + lastEntity = ""; + if (!_input->is_action_pressed("jump")) + { + _input->action_press("jump"); + } } } - entities.pop(); + if (obelisk->query(entityName, "is", "touchable") > 0) + { + if (!walking && !_input->is_action_pressed("left") && !_input->is_action_pressed("right")) + { + walking = true; + if (entity.position.x > player_position.x) + { + _input->action_press("right"); + } + else + { + _input->action_press("left"); + } + } + } + + entities.pop_front(); } } +void alai::player::AI::unset_action(godot::String action) +{ + auto ev = godot::InputEventAction()._new(); + ev->set_action(action); + ev->set_pressed(false); + _input->parse_input_event(ev); +} + +void alai::player::AI::_on_timer_timeout() +{ + unset_action("left"); + unset_action("right"); + unset_action("jump"); + lastEntity = ""; +} + void alai::player::AI::_on_object_report(godot::String name, godot::String state, godot::Vector2 position, godot::Vector2 velocity) { - auto entity = Entity(); - entity.name = name.to_lower().rstrip("0123456789"); - entity.state = state; - entity.position = position; - entity.velocity = velocity; - entities.push(entity); + auto entity = Entity(); + entity.originalName = name; + entity.name = name.to_lower().rstrip("0123456789"); + entity.state = state; + entity.position = position; + entity.velocity = velocity; + entities.emplace_front(entity); } diff --git a/src/player/AI.h b/src/player/AI.h index e3ae57d..72d76c8 100644 --- a/src/player/AI.h +++ b/src/player/AI.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -18,6 +19,7 @@ namespace alai class Entity { public: + godot::String originalName; godot::String name; godot::String state; godot::Vector2 position; @@ -43,10 +45,14 @@ namespace alai alai::Event* event; - std::queue entities; + std::deque entities; alai::player::Player* parent; + godot::String lastEntity; + + godot::Timer* timer = godot::Timer()._new(); + public: /** * @brief This method registers classes with Godot. @@ -56,7 +62,16 @@ namespace alai static void _register_methods(); + /** + * @brief Construct a new AI object. + * + */ AI(); + + /** + * @brief Destroy the AI object. + * + */ ~AI(); /** @@ -80,6 +95,18 @@ namespace alai */ void _physics_process(float delta); + void _on_timer_timeout(); + + void unset_action(godot::String action); + + /** + * @brief Callback that receives information about the game environment. + * + * @param[in] name The name of the object. + * @param[in] state The state the object is in. + * @param[in] position The position of the object. + * @param[in] velocity The object's velocity. + */ void _on_object_report(godot::String name, godot::String state, godot::Vector2 position, godot::Vector2 velocity); }; } // namespace player