/* 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 { static std::string builder_name(procgen::prng_t *prng) { std::stringstream ss; static const char *names[] = { "Janusz", "Henryk", "Wiesław", "Bolesław", "Edward", "Roman", "Mirosław", "Zbigniew", "Jerzy", "Józef", "Jan" }; ss << "Pan "; ss << names[prng->next() % (sizeof(names) / sizeof(names[0]))]; return ss.str(); } unit_builder_t::unit_builder_t(game::state_t *game) : unit_t(game, UNIT_BUILDER) { size[0] = v2f_t(-0.3f, +0.0f); size[1] = v2f_t(+0.3f, +0.3f); render_size[0] = v2f_t(-0.5f, -1.2f); render_size[1] = v2f_t(+0.5f, +0.3f); cmodel.cflags = CF_BODY; move.cflags = CF_SOLID | CF_BODY | CF_WATER; name = builder_name(&game->prng); wake(); friendly = true; controllable = true; health = max_health = 45; } void unit_builder_t::command_stop(void) { stop_moving(); move_marker.reset(); repairing = false; repair_marker.reset(); say("Stop.", false); } void unit_builder_t::command_repair(v2f_t where) { repairing = true; building = false; repairing_at = where; repair_marker = std::make_unique(game, where); say("Idę naprawiać.", false); } void unit_builder_t::command_build(v2f_t where, type_t what) { world::trace_t trace; unit_t *built; size_t price; world::cmodel_t cmodel; if ((x - where).len() > 1.3f) { say("Tak daleko tego nie postawię!"); return; } trace = world->ray_v_all(x, where, CF_SOLID|CF_WATER|CF_DECOS, this); if (trace.hit) { say("Coś stoi mi na drodze."); return; } switch (what) { case UNIT_TELEPORTER: built = new unit_teleporter_t(game); price = 250; break; case UNIT_REPLICATOR: built = new unit_replicator_t(game); price = 200; break; default: abort(); } if (price > game->crystals) { game->interface.print("Insufficient crystals; " + std::to_string(price - game->crystals) + " more needed"); delete built; return; } cmodel.bounds[0] = where + built->size[0]; cmodel.bounds[1] = where + built->size[1]; cmodel.cflags = CF_SOLID|CF_SURFACE2|CF_WATER|CF_DECOS|CF_BODY|CF_BODY_SMALL; if (world->test_rect(&cmodel, nullptr)) { say("Nie ma tutaj miejsca."); delete built; return; } game->crystals -= price; built->place(world, where); repairing = true; building = true; repairing_at = where; say("Rozpoczynam budowę."); } void unit_builder_t::repair(void) { rectf_t rect; world::trace_t trace; unit_t *unit; if (!move.moving) { world::cflags_t save = move.cflags; move.cflags &= ~CF_SOLID; start_moving(repairing_at); move.cflags = save; } if ((x - repairing_at).len() > 1.0f) return; if (next_repair && next_repair > game->time) return; trace = game->world.ray_v_all_p3d(x, repairing_at, CF_SOLID, CF_SURFACE2, this); if (!trace.hit || !trace.ent || trace.ent->type != ET_UNIT) { repairing = false; return; } unit = dynamic_cast(trace.ent); if ((unit->type != UNIT_TELEPORTER && unit->type != UNIT_REPLICATOR) || unit->health >= unit->max_health) { repairing = false; return; } if (!game->crystals) { game->interface.print("Insufficient crystals; 1 more needed."); if (building) say("Nie mam czym konstruować!", false); else say("Nie mam czym naprawiać!", false); return; } game->crystals--; assets::ui.crystal_tick.play(); if (unit->max_health - unit->health < 3) { repairing = false; unit->health = unit->max_health; unit->constructed = true; if (building) say("Budowa zakończona."); else say("Naprawa ukończona."); } else unit->health += 2; next_repair = game->time + MSEC(250); last_repair = game->time; } void unit_builder_t::on_think(void) { keep_moving(2.0); game->wake_area(x); if (!move.moving) move_marker.reset(); if (repairing) repair(); else repair_marker.reset(); if (move.moving && (x - move.last_step).len() > 0.5f) { move.last_step = x; assets::soldier.step_stone.play_3d(x); } } void unit_builder_t::on_damage(unit_t *attacker) { if (health < -30) { assets::fx.gibbing.play_3d(x); game->deletion_list.insert(this); return; } else if (health < -10) { render_size[0] = v2f_t(-0.5f, -1.2f); render_size[1] = v2f_t(+0.5f, +0.3f); if (!gibbed) assets::fx.gibbing.play_3d(x); else assets::fx.corpse_hit.play_3d(x); place(world, x); gibbed = true; } else if (health <= 0) { render_size[0] = v2f_t(-0.75f, -0.5f); render_size[1] = v2f_t(+0.75f, +0.5f); if (!dead) assets::soldier.death.play_3d(x); else assets::fx.corpse_hit.play_3d(x); place(world, x); } else assets::soldier.pain.play_3d(x); } void unit_builder_t::on_death(void) { render_layer = render::LAYER_FLAT; cmodel.cflags = CF_BACKGROUND; place(world, x); controllable = false; move_marker.reset(); } void unit_builder_t::render_to(render::state_t *render) { sf::Color selection_color; if (selected == selection_cookie) { if (health == max_health) selection_color = sf::Color::Green; else if (health >= max_health / 2) selection_color = sf::Color::Yellow; else if (dead) selection_color = sf::Color::Black; else selection_color = sf::Color::Red; render->render(0.0, &assets::unit_selected, cmodel.bounds, selection_color); } if (!dead && last_repair + MSEC(300) > game->time) { render->render(game->now * 2.0f, &assets::builder.repairing, render_bounds); } else if (!dead) { render::oriented_sprite_t *legs; if (move.moving && !move.blocked) legs = &assets::soldier.legs_walking; else legs = &assets::soldier.legs_idle; render->render(game->now * 10, legs, render_bounds, move.angle); render->render(game->now * 10, &assets::builder.body_idle, render_bounds, move.angle); render->render(game->now * 10, &assets::builder.head_idle, render_bounds, move.angle); } else { float phase = clamp((game->now - death_time) * 5, 0, 0.9); if (gibbed) render->render(phase, &assets::builder.gibbing, render_bounds); else render->render(phase, &assets::builder.dead, render_bounds); } unit_t::render_to(render); } void unit_builder_t::render_late_to(render::state_t *render) { if (selected == selection_cookie) render->render(0.0, &assets::unit_selected_halo, cmodel.bounds, selection_color); } }