#include "Battle.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "components.h" #include "Grid.h" #include "inspect.h" #include "lines.h" #include "markers.h" #include "Pause.h" #include "PopupEmitter.h" #include "Session.h" #include "Summary.h" namespace kurator { Battle::Battle(std::shared_ptr _session) : Battle(_session, campaign::scenarios::example()) { } Battle::Battle(std::shared_ptr _session, campaign::Scenario scenario, Battle::Callback _report) : session {std::move(_session)}, ctx {sim::load_scenario(scenario)}, simulation_base {sim::base_simulation_systems(ctx)}, report {std::move(_report)} { ctx.dispatcher.sink().connect<&Battle::on_end>(*this); ctx.dispatcher.sink().connect<&Battle::on_hit>(*this); ctx.dispatcher.sink().connect<&Battle::on_destroyed>(*this); ctx.dispatcher.sink().connect<&Battle::on_ship_left>(*this); ctx.camera.scale = std::min(GetScreenWidth()/30000.0, GetScreenHeight()/30000.0); attach_markers(ctx); balance.update(ctx.registry); } Battle::~Battle() { ctx.dispatcher.sink().disconnect(*this); ctx.dispatcher.sink().disconnect(*this); ctx.dispatcher.sink().disconnect(*this); ctx.dispatcher.sink().disconnect(*this); } void time_controls(const char* id, float& time_factor) { ImGui::PushID(id); ImGui::PushItemWidth(-std::numeric_limits::min()); if (ImGui::Button("1x")) time_factor = 1.0f; ImGui::SameLine(); ImGui::SliderFloat("##time_factor", &time_factor, 0.1f, 10.0f); ImGui::PopItemWidth(); ImGui::PopID(); } static void progress_timers(sim::State& ctx); static void move_ui_pops(sim::State& ctx); static void blink_crosses(sim::State& ctx); void Battle::update() { if (IsKeyPressed(KEY_ESCAPE)) return session->set(std::make_shared(session, session->current())); if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) { const auto delta = GetMouseDelta(); ctx.camera.offset.x -= delta.x / ctx.camera.scale; ctx.camera.offset.y -= delta.y / ctx.camera.scale; } if (IsWindowResized()) ctx.camera.scale = std::min(GetScreenWidth()/30000.0, GetScreenHeight()/30000.0); ctx.clock.update(); simulation_base(ctx); update_markers(ctx, std::bind(&InspectionWindow::select, &inspect, std::placeholders::_1)); progress_timers(ctx); move_ui_pops(ctx); blink_crosses(ctx); animate_lines(ctx); balance.update(ctx.registry); ImGui::SetNextWindowPos({GetScreenWidth()/2.0f, GetScreenHeight()-100.0f}, ImGuiCond_Once, {0.5f, 0.5f}); ImGui::SetNextWindowSize({240.0f, 0.0f}, ImGuiCond_Once); if (ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_NoFocusOnAppearing)) time_controls("TimeControls", ctx.clock.time_factor); ImGui::End(); inspect.show(); } void progress_timers(sim::State& ctx) { auto timers = ctx.registry.view(); for (auto&& [entity, timer] : timers.each()) { timer.left -= timer.scaled ? ctx.clock.dt : ctx.clock.ui; if (timer.left < 0.0) ctx.registry.destroy(entity); } } void move_ui_pops(sim::State& ctx) { auto pops = ctx.registry.view(); for (auto&& [entity, pop, offset] : pops.each()) { const auto speed = pop.speed.scale(ctx.clock.ui); offset.x += speed.x; offset.y += speed.y; const auto damp = pop.speed.scale(pop.damp).scale(ctx.clock.ui); pop.speed.x -= damp.x; pop.speed.y -= damp.y; } } void blink_crosses(sim::State& ctx) { auto crosses = ctx.registry.view(); for (auto&& [entity, cross] : crosses.each()) { cross.timer += ctx.clock.ui; const auto dphase = cross.phase * 2; if (cross.timer > dphase) cross.timer -= dphase; } } static void draw_crosses(const sim::State& ctx); static void draw_pops(const sim::State& ctx); void Battle::draw() const { ClearBackground(BLACK); Grid().draw(ctx.camera); draw_crosses(ctx); draw_lines(ctx); draw_markers(ctx); draw_pops(ctx); balance.draw(); } void draw_crosses(const sim::State& ctx) { auto crosses = ctx.registry.view(); for (const auto& [entity, transform, cross] : crosses.each()) { if (cross.timer > cross.phase) continue; const auto pos = ctx.camera.to_screen(transform.position); DrawLine( pos.x - cross.hlength, pos.y - cross.hlength, pos.x + cross.hlength, pos.y + cross.hlength, cross.color); DrawLine( pos.x + cross.hlength, pos.y - cross.hlength, pos.x - cross.hlength, pos.y + cross.hlength, cross.color); } } void draw_pops(const sim::State& ctx) { auto pops = ctx.registry.view(); for (const auto& [entity, text, transform, offset] : pops.each()) { const auto screen = ctx.camera.to_screen(transform.position); const auto pos = screen.subtract(text.width/2, text.font_size/2) + offset; DrawText(text.text.c_str(), pos.x, pos.y, text.font_size, text.color); } } void Battle::on_end(const sim::End&) { if (report) report(log); session->set(std::make_shared(session, log)); } void Battle::on_hit(const sim::Hit& hit) { if (!ctx.registry.valid(hit.victim)) return; const auto& source = ctx.registry.get(hit.source); const auto& victim = ctx.registry.get(hit.victim); auto& popup = ctx.registry.get(hit.victim); popup.emit(ctx.registry, victim, hit.damage); const auto line = ctx.registry.create(); ctx.registry.emplace(line, 0.2, true); ctx.registry.emplace( line, RED, source.position, victim.position, 1000.0 / (victim.position - source.position).magnitude(), 0.2); } void Battle::on_destroyed(const sim::Destroyed& event) { if (!ctx.registry.valid(event.victim)) return; const auto& victim = ctx.registry.get(event.victim); const auto cross = ctx.registry.create(); ctx.registry.emplace(cross, 1.6); ctx.registry.emplace(cross, 0.2, 6.0); ctx.registry.emplace(cross, victim.position, 0.0); } void Battle::on_ship_left(const stats::ShipLeft& event) { log.push_back(event); } } // namespace kurator