#include "input_command_handler.h" #include "../controllers/action_vfx.h" #include "../controllers/command_controller.h" #include "../models/cursor_manager.h" #include "../models/cursor_mode.h" #include "../models/hover_tracker.h" #include "../utils/movement_utils.h" #include "game/core/component.h" #include "game/core/world.h" #include "game/game_config.h" #include "game/systems/command_service.h" #include "game/systems/formation_planner.h" #include "game/systems/picking_service.h" #include "game/systems/selection_system.h" #include "render/gl/camera.h" InputCommandHandler::InputCommandHandler( Engine::Core::World *world, Game::Systems::SelectionController *selection_controller, App::Controllers::CommandController *command_controller, CursorManager *cursor_manager, HoverTracker *hover_tracker, Game::Systems::PickingService *picking_service, Render::GL::Camera *camera) : m_world(world), m_selection_controller(selection_controller), m_command_controller(command_controller), m_cursor_manager(cursor_manager), m_hover_tracker(hover_tracker), m_picking_service(picking_service), m_camera(camera) {} void InputCommandHandler::on_map_clicked(qreal sx, qreal sy, int local_owner_id, const ViewportState &viewport) { if (m_is_spectator_mode) { return; } if (m_selection_controller && m_camera) { m_selection_controller->on_click_select(sx, sy, false, viewport.width, viewport.height, m_camera, local_owner_id); } } namespace { void handle_move_command(Engine::Core::World *world, const std::vector &selected, Game::Systems::PickingService *picking_service, Render::GL::Camera *camera, qreal sx, qreal sy, int local_owner_id, const ViewportState &viewport) { if (!picking_service || !camera || !world) { return; } Engine::Core::EntityID const target_id = picking_service->pick_unit_first(float(sx), float(sy), *world, *camera, viewport.width, viewport.height, 0); if (target_id != 0U) { auto *target_entity = world->get_entity(target_id); if (target_entity != nullptr) { auto *target_unit = target_entity->get_component(); if (target_unit != nullptr) { bool const is_enemy = (target_unit->owner_id != local_owner_id); bool const is_building = target_entity->has_component(); if (is_enemy && !is_building) { Game::Systems::CommandService::attack_target(*world, selected, target_id, true); return; } } } } QVector3D hit; if (picking_service->screen_to_ground(QPointF(sx, sy), *camera, viewport.width, viewport.height, hit)) { auto formation_result = Game::Systems::FormationPlanner::get_formation_with_facing( *world, selected, hit, Game::GameConfig::instance().gameplay().formation_spacing_default); for (size_t i = 0; i < selected.size(); ++i) { auto *entity = world->get_entity(selected[i]); if (entity == nullptr) { continue; } auto *formation_mode = entity->get_component(); if ((formation_mode == nullptr) || !formation_mode->active) { continue; } auto *transform = entity->get_component(); if (transform != nullptr && i < formation_result.facing_angles.size()) { transform->desired_yaw = formation_result.facing_angles[i]; transform->has_desired_yaw = true; } } Game::Systems::CommandService::MoveOptions opts; opts.group_move = selected.size() > 1; Game::Systems::CommandService::move_units(*world, selected, formation_result.positions, opts); } } } // namespace void InputCommandHandler::on_right_click(qreal sx, qreal sy, int local_owner_id, const ViewportState &viewport) { if (m_is_spectator_mode || !m_world) { return; } auto *selection_system = m_world->get_system(); if (selection_system == nullptr) { return; } if (m_cursor_manager->mode() == CursorMode::Patrol || m_cursor_manager->mode() == CursorMode::Attack || m_cursor_manager->mode() == CursorMode::Guard || m_cursor_manager->mode() == CursorMode::PlaceBuilding || m_cursor_manager->mode() == CursorMode::Heal || m_cursor_manager->mode() == CursorMode::Build) { m_cursor_manager->set_mode(CursorMode::Normal); return; } const auto &sel = selection_system->get_selected_units(); if (sel.empty()) { return; } if (m_command_controller) { m_command_controller->disable_run_mode_for_selected(); } handle_move_command(m_world, sel, m_picking_service, m_camera, sx, sy, local_owner_id, viewport); } void InputCommandHandler::on_right_double_click(qreal sx, qreal sy, int local_owner_id, const ViewportState &viewport) { if (m_is_spectator_mode || !m_world) { return; } auto *selection_system = m_world->get_system(); if (selection_system == nullptr) { return; } if (m_cursor_manager->mode() == CursorMode::Patrol || m_cursor_manager->mode() == CursorMode::Attack || m_cursor_manager->mode() == CursorMode::Guard || m_cursor_manager->mode() == CursorMode::PlaceBuilding || m_cursor_manager->mode() == CursorMode::Heal || m_cursor_manager->mode() == CursorMode::Build) { m_cursor_manager->set_mode(CursorMode::Normal); return; } const auto &sel = selection_system->get_selected_units(); if (sel.empty()) { return; } if (m_command_controller) { m_command_controller->enable_run_mode_for_selected(); } handle_move_command(m_world, sel, m_picking_service, m_camera, sx, sy, local_owner_id, viewport); } void InputCommandHandler::on_attack_click(qreal sx, qreal sy, const ViewportState &viewport) { if (m_is_spectator_mode) { return; } if (!m_command_controller || !m_camera) { return; } auto result = m_command_controller->on_attack_click( sx, sy, viewport.width, viewport.height, m_camera); auto *selection_system = m_world->get_system(); if ((selection_system == nullptr) || !m_picking_service || !m_camera || !m_world) { return; } const auto &selected = selection_system->get_selected_units(); if (!selected.empty()) { Engine::Core::EntityID const target_id = m_picking_service->pick_unit_first( float(sx), float(sy), *m_world, *m_camera, viewport.width, viewport.height, 0); if (target_id != 0) { auto *target_entity = m_world->get_entity(target_id); if (target_entity != nullptr) { auto *target_unit = target_entity->get_component(); if ((target_unit != nullptr)) { App::Controllers::ActionVFX::spawnAttackArrow(m_world, target_id); } } } } if (result.reset_cursor_to_normal) { m_cursor_manager->set_mode(CursorMode::Normal); } } void InputCommandHandler::reset_movement(Engine::Core::Entity *entity) { App::Utils::reset_movement(entity); } void InputCommandHandler::on_stop_command() { if (m_is_spectator_mode) { return; } if (!m_command_controller) { return; } auto result = m_command_controller->on_stop_command(); if (result.reset_cursor_to_normal) { m_cursor_manager->set_mode(CursorMode::Normal); } } void InputCommandHandler::on_hold_command() { if (m_is_spectator_mode) { return; } if (!m_command_controller) { return; } auto result = m_command_controller->on_hold_command(); if (result.reset_cursor_to_normal) { m_cursor_manager->set_mode(CursorMode::Normal); } } void InputCommandHandler::on_guard_command() { if (m_is_spectator_mode) { return; } if (!m_command_controller) { return; } auto result = m_command_controller->on_guard_command(); if (result.reset_cursor_to_normal) { m_cursor_manager->set_mode(CursorMode::Normal); } } void InputCommandHandler::on_formation_command() { if (m_is_spectator_mode) { return; } if (!m_command_controller) { return; } auto result = m_command_controller->on_formation_command(); if (result.reset_cursor_to_normal) { m_cursor_manager->set_mode(CursorMode::Normal); } } void InputCommandHandler::on_run_command() { if (m_is_spectator_mode) { return; } if (!m_command_controller) { return; } auto result = m_command_controller->on_run_command(); if (result.reset_cursor_to_normal) { m_cursor_manager->set_mode(CursorMode::Normal); } } void InputCommandHandler::on_guard_click(qreal sx, qreal sy, const ViewportState &viewport) { if (m_is_spectator_mode) { return; } if (!m_command_controller || !m_camera) { return; } auto result = m_command_controller->on_guard_click(sx, sy, viewport.width, viewport.height, m_camera); if (result.reset_cursor_to_normal) { m_cursor_manager->set_mode(CursorMode::Normal); } } auto InputCommandHandler::any_selected_in_hold_mode() const -> bool { if (!m_command_controller) { return false; } return m_command_controller->any_selected_in_hold_mode(); } auto InputCommandHandler::any_selected_in_guard_mode() const -> bool { if (!m_command_controller) { return false; } return m_command_controller->any_selected_in_guard_mode(); } auto InputCommandHandler::any_selected_in_formation_mode() const -> bool { if (!m_command_controller) { return false; } return m_command_controller->any_selected_in_formation_mode(); } auto InputCommandHandler::any_selected_in_run_mode() const -> bool { if (!m_command_controller) { return false; } return m_command_controller->any_selected_in_run_mode(); } auto InputCommandHandler::is_placing_formation() const -> bool { if (!m_command_controller) { return false; } return m_command_controller->is_placing_formation(); } void InputCommandHandler::on_formation_mouse_move( qreal sx, qreal sy, const ViewportState &viewport) { if (!m_command_controller || !m_camera || !m_picking_service) { return; } if (!m_command_controller->is_placing_formation()) { return; } QVector3D hit; if (m_picking_service->screen_to_ground( QPointF(sx, sy), *m_camera, viewport.width, viewport.height, hit)) { m_command_controller->update_formation_placement(hit); } } void InputCommandHandler::on_formation_scroll(float delta) { if (!m_command_controller) { return; } if (!m_command_controller->is_placing_formation()) { return; } float current_angle = m_command_controller->get_formation_placement_angle(); float new_angle = current_angle + delta * 5.0F; while (new_angle < 0.0F) { new_angle += 360.0F; } while (new_angle >= 360.0F) { new_angle -= 360.0F; } m_command_controller->update_formation_rotation(new_angle); } void InputCommandHandler::on_formation_confirm() { if (!m_command_controller) { return; } m_command_controller->confirm_formation_placement(); } void InputCommandHandler::on_formation_cancel() { if (!m_command_controller) { return; } m_command_controller->cancel_formation_placement(); } void InputCommandHandler::on_patrol_click(qreal sx, qreal sy, const ViewportState &viewport) { if (m_is_spectator_mode) { return; } if (!m_command_controller || !m_camera) { return; } auto result = m_command_controller->on_patrol_click( sx, sy, viewport.width, viewport.height, m_camera); if (result.reset_cursor_to_normal) { m_cursor_manager->set_mode(CursorMode::Normal); } } void InputCommandHandler::on_click_select(qreal sx, qreal sy, bool additive, int local_owner_id, const ViewportState &viewport) { if (m_is_spectator_mode) { return; } if (m_selection_controller && m_camera) { m_selection_controller->on_click_select(sx, sy, additive, viewport.width, viewport.height, m_camera, local_owner_id); } } void InputCommandHandler::on_area_selected(qreal x1, qreal y1, qreal x2, qreal y2, bool additive, int local_owner_id, const ViewportState &viewport) { if (m_is_spectator_mode) { return; } if (m_selection_controller && m_camera) { m_selection_controller->on_area_selected(x1, y1, x2, y2, additive, viewport.width, viewport.height, m_camera, local_owner_id); } } void InputCommandHandler::select_all_troops(int local_owner_id) { if (m_is_spectator_mode) { return; } if (m_selection_controller) { m_selection_controller->select_all_player_troops(local_owner_id); } } void InputCommandHandler::select_unit_by_id(int unit_id, int local_owner_id) { if (m_is_spectator_mode) { return; } if (!m_selection_controller || (unit_id <= 0)) { return; } m_selection_controller->select_single_unit( static_cast(unit_id), local_owner_id); } void InputCommandHandler::set_hover_at_screen(qreal sx, qreal sy, const ViewportState &viewport) { if (!m_hover_tracker || !m_camera || !m_world) { return; } m_hover_tracker->update_hover(float(sx), float(sy), *m_world, *m_camera, viewport.width, viewport.height); }