/* This file is part of Minitrem. Minitrem is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. Minitrem is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Minitrem. If not, see . */ #include "game.hpp" namespace game { size_t selection_cookie = 1; entity_t::entity_t(game::state_t *game_, int type_) : world::entity_t(type_) { game = game_; } entity_t::~entity_t(void) { } void entity_t::destroy(void) { unlink(); game->awake_entities.erase(this); delete this; } void entity_t::place(world::world_t *world_) { bool do_spawn = false; if (!world) do_spawn = true; link(world_); if (do_spawn) on_spawn(); if (!ignore_waking) wake(); } void entity_t::place(world::world_t *world, v2f_t x_) { x = x_; cmodel.bounds = size + x; render_bounds = render_size + x; place(world); } void entity_t::wake(void) { awake = true; wake_time = game->now; game->awake_entities.insert(this); if (!ignore_waking) on_wake(); } void entity_t::sleep(void) { awake = false; game->awake_entities.erase(this); } void state_t::start(void) { unit_soldier_t *soldier; unit_repl_t *repl; world.generator = worldgen; world.generator_data = (void*)this; soldier = new unit_soldier_t(this); soldier->place(&world, v2f_t(0.5, 0.5)); repl = new unit_repl_t(this); repl->place(&world, v2f_t(1.5, 0.5)); resume(); } void state_t::stop(void) { } bool state_t::select_unit(unit_t *unit, int type) { switch (type) { case SELECT_NEW: case SELECT_OR: if (unit->selected == selection_cookie) return false; else goto do_select; case SELECT_XOR: if (unit->selected == selection_cookie) goto do_deselect; else goto do_select; } do_select: unit->selected = selection_cookie; selected_units.insert(unit); return true; do_deselect: unit->selected = selection_cookie - 1; selected_units.erase(unit); return false; } void state_t::select(rectf_t rect, int type) { bool select_one; std::list ents; if (type == SELECT_NEW) { selection_cookie++; selected_units.clear(); } select_one = rect.area() < 0.2f; ents = world.get_render_entities(rect); if (select_one) ents.sort(render::visibility_order); for (world::entity_t *ent : ents) { unit_t *unit; if (ent->type != ET_UNIT) continue; unit = (unit_t*)ent; if (!unit->controllable) continue; if (select_unit(unit, type)) { if (unit->type == unit_t::UNIT_SOLDIER) unit->say(text::get(text::SAY_READY)); } if (select_one) break; } } enum { COMMAND_MOVE, COMMAND_FIRE, COMMAND_STOP, COMMAND_REPL }; bool state_t::populate_pie_menu(std::vector &items) { bool soldiers = false, repls = false; items.clear(); if (selected_units.size() == 0) return false; for (unit_t *unit : selected_units) { if (unit->dead || !unit->controllable) continue; switch (unit->type) { case unit_t::UNIT_SOLDIER: soldiers = true; break; case unit_t::UNIT_REPL: repls = true; break; default:; } } if (soldiers) { items.push_back((interface::pie_item_t){"Move", COMMAND_MOVE}); items.push_back((interface::pie_item_t){"Fire", COMMAND_FIRE}); items.push_back((interface::pie_item_t){"Stop", COMMAND_STOP}); } if (repls) { items.push_back((interface::pie_item_t){"Replicate", COMMAND_REPL}); } return true; } static void command_soldier(unit_soldier_t *soldier, v2f_t x, int number) { switch (number) { case COMMAND_MOVE: if (!soldier->start_moving(x)) soldier->say(text::get(text::SAY_NO_PATH)); else { soldier->move_marker = std::make_unique(soldier->game, soldier->move.path.back()); soldier->say(text::get(text::SAY_MOVING)); } break; case COMMAND_STOP: soldier->stop_moving(); soldier->manual_firing = false; soldier->say(text::get(text::SAY_STOPPING)); break; case COMMAND_FIRE: soldier->manual_firing = true; soldier->manual_firing_target = x; soldier->say(text::get(text::SAY_FIRING)); break; } } static void command_repl(unit_repl_t *repl, v2f_t x, int number) { switch (number) { case COMMAND_REPL: repl->activate(); break; } } void state_t::command(v2f_t x, int number) { v2f_t snap; if (!selected_units.size()) return; // debugging if (number == 666) { for (size_t i = 0; i < 100; i++) { v2f_t rad; world::trace_t best; best.hit = false; best.frac = 2.0f; rad = v2f_t::rad(i / 99.0f * 2.0f * M_PI); for (unit_t *unit : selected_units) { world::trace_t trace; trace = world::ray_v_rect(x, x + rad * 10, unit->cmodel.bounds); if (trace.frac < best.frac) best = trace; } debug_render->render_line(x, best.end, (best.hit ? sf::Color::Yellow : sf::Color::White)); } return; } snap[0] = std::round(x[0] - 0.5f) + 0.5f; snap[1] = std::round(x[1] - 0.5f) + 0.5f; for (unit_t *unit : selected_units) { if (unit->dead || !unit->controllable) continue; switch (unit->type) { case unit_t::UNIT_SOLDIER: command_soldier(dynamic_cast(unit), snap, number); break; case unit_t::UNIT_REPL: command_repl(dynamic_cast(unit), snap, number); break; default:; } } } void state_t::pause(void) { paused = true; } void state_t::resume(void) { t0 = nclock(); frames_since_t0 = 0; paused = false; } #define TIME_DELTA ((ntime_t)1000000000 / 60) void state_t::tick(ntime_t time_) { size_t target, frames_this_tick = 0; if (paused) return; target = (time_ - t0) / TIME_DELTA; while (frames_since_t0 < target) { // FIXME: Is this non-deterministic enough? prng.seed(prng.next() ^ time); // setting up old variables (refactor them out eventually) now = time * 1.0e-9; dt = TIME_DELTA * 1.0e-9; // on_think can insert/erase elements of awake_entities so iterate // over a copy of it. auto copy = awake_entities; for (entity_t *ent : copy) ent->on_think(); frames++; frames_since_t0++; frames_this_tick++; time += TIME_DELTA; if (frames_this_tick == 3) { t0 = time_; frames_since_t0 = 0; frames_behind++; break; } } } #define XRES 9 #define YRES 9 void state_t::compute_ambience(render::state_t *render) { const size_t samples = XRES * YRES; rectf_t area; v2f_t origins[AMBIENT_COUNT]; size_t hits[AMBIENT_COUNT]; area = render->window_in_world_space(); for (size_t i = 0; i < AMBIENT_COUNT; i++) { origins[i] = v2f_t(0, 0); hits[i] = 0; } // resolution chosen arbitrarily for (size_t y = 0; y < YRES; y++) for (size_t x = 0; x < XRES; x++) { v2f_t point; world::tile_t *tile; int type; point[0] = lerp(area[0][0], area[1][0], y / (YRES - 1.0f)); point[1] = lerp(area[0][1], area[1][1], x / (XRES - 1.0f)); tile = world.get_tile(world::tile_index_t(point), world::SECTOR_FULL); switch (tile->type) { case TILE_DIRT: type = AMBIENT_WIND; break; case TILE_GRAVEL: case TILE_STONE: type = AMBIENT_CHASM; break; case TILE_WATER: type = AMBIENT_WATER; break; case TILE_DIRT_RED: case TILE_STONE_RED: type = AMBIENT_NEXUS; break; default: continue; } origins[type] += point; hits[type]++; } for (size_t i = 0; i < AMBIENT_COUNT; i++) { assets::ambients[i].origin = origins[i] / hits[i]; assets::ambients[i].weight = (float)hits[i] / samples; } } bool load_assets(void) { assets::load(); return true; } } //namespace game