#include "game.hpp" namespace game { world::cmodel_t unit_t::make_cmodel(v2f_t at) { world::cmodel_t cmodel; cmodel.bounds[0] = at + size[0]; cmodel.bounds[1] = at + size[1]; cmodel.cflags = cflags; return cmodel; } void unit_t::compute_bounds() { render_bounds[0] = x + render_size[0]; render_bounds[1] = x + render_size[1]; } unit_t::unit_t(unit_t::type_t type_) : entity_t(ET_UNIT) { type = type_; } void unit_t::render_to(render::state_t *render) { if (selected == selection_cookie) render->render_hlrect(render_bounds, sf::Color::Blue); if (move.moving && debug_draw_paths) render->debug_path(&move.path); } void unit_t::say(const wchar_t *wstr, double now) { say_text = wstr; say_time = now; std::cout << (void*)this << ": " << wstr << "\n"; } void unit_t::place(world::world_t *world_, v2f_t x_) { world = world_; x = x_; move.moving = false; unlink(); cmodel = make_cmodel(x); compute_bounds(); link(world); } bool unit_t::keep_moving(double now, double dt, double speed) { float time; if (!move.moving) return false; if (move.blocked && now < move.next_attempt) return false; time = dt * speed; while (time > 0.0f) { v2f_t delta, next, x_new; world::cmodel_t cmodel_next; if (!move.path.size()) { move.moving = false; break; } next = *move.path.begin(); delta = next - x; move.angle = delta.angle(); if (delta.len() >= time) { x_new = x + delta * time / delta.len(); time -= delta.len(); } else { x_new = next; time -= delta.len(); move.path.pop_front(); } cmodel_next = make_cmodel(x_new); if (!world->test_rect(&cmodel_next, this)) { x = x_new; cmodel = cmodel_next; continue; } if (move.attempts_left) { move.blocked = true; move.attempts_left--; move.next_attempt = now + 0.2f; } else { if ((x - move.dst).len() > 1.5f) say(text::unit_blocked, now); move.moving = false; } break; } unlink(); compute_bounds(); link(world); return true; } bool unit_t::start_moving(v2f_t dst_, double now) { if (!world) { printf("unit_t::start_moving: entity is not linked\n"); return false; } move.dst = dst_; move.path.clear(); if (!world->find_path(x, move.dst, &cmodel, this, &move.path)) { say(text::unit_no_path, now); move.moving = false; return false; } move.moving = true; move.blocked = false; move.attempts_left = 10; move.next_attempt = -INFINITY; return true; } void unit_t::damage(double now, int points) { health -= points; if (health < 0) { printf("%p died\n", this); dead = true; death_time = now; cflags = 0; die(now); } } human_t::human_t() : unit_t(UNIT_HUMAN) { cflags = CF_HUMAN; health = 20; size[0] = v2f_t(-0.4f, -0.4f); size[1] = v2f_t(+0.4f, +0.4f); render_size[0] = v2f_t(-0.5f, -1.0f); render_size[1] = v2f_t(+0.5f, +0.5f); } void human_t::wake(double now, unit_t *by_whom) { } void human_t::sleep(double now) { } void human_t::think(double now, double dt) { keep_moving(now, dt, 4.0); } void human_t::die(double now) { render_size[0] = v2f_t(-0.75f, -0.5f); render_size[1] = v2f_t(+0.75f, +0.5f); unlink(); compute_bounds(); link(world); } void human_t::render_to(render::state_t *render) { if (!dead) { bool moving; moving = move.moving && !move.blocked; render->render((moving ? &assets::human.legs_walking : &assets::human.legs_idle), render_bounds, move.angle); render->render(&assets::human.body_idle, render_bounds, move.angle); render->render(&assets::human.head_idle, render_bounds, move.angle); } else render->render(&assets::human.dead, render_bounds); if (say_time + 5.0 > render->now) { v2f_t text_pos; float height; text_pos = render_bounds[0] + v2f_t(render_bounds.dim(0) / 2, -render_bounds.dim(1) * 0.1); height = size.dim_min() * 0.20f; render->render_text(text_pos, height, say_text, render::ALIGN_CENTER_BOTTOM, sf::Color::White); } unit_t::render_to(render); } alien_t::alien_t() : unit_t(UNIT_ALIEN) { cflags = CF_SOLID; health = 4; size[0] = v2f_t(-0.2f, -0.2f); size[1] = v2f_t(+0.2f, +0.2f); render_size[0] = v2f_t(-0.3f, -0.3f); render_size[1] = v2f_t(+0.3f, +0.3f); } void alien_t::wake(double now, unit_t *by_whom) { start_moving(by_whom->x, now); next_targetting = now + 0.4; } void alien_t::sleep(double now) { } void alien_t::die(double now) { } static unit_t *find_target(world::world_t *world, v2f_t x, float r, unit_t::type_t type) { rectf_t rect; float nearest_dist = INFINITY; unit_t *nearest = NULL; rect[0] = x - v2f_t(r, r); rect[1] = x + v2f_t(r, r); for (world::entity_t *ent : world->get_entities(rect, -1)) { unit_t *unit; float dist; if (ent->type != ET_UNIT) continue; unit = (unit_t*)ent; if (unit->type != type) continue; if (unit->dead) continue; dist = (unit->x - x).len(); if (dist < nearest_dist) { nearest_dist = dist; nearest = unit; } } return nearest; } void alien_t::attack(double now, unit_t *target, float range) { world::trace_t trace; if (now < next_attack) return; if ((x - target->x).len() > range) return; trace = world->trace(x, target->x, CF_SOLID); if (!trace.hit) target->damage(now, 12); next_attack = now + 1.0; } void alien_t::think(double now, double dt) { if (now > next_targetting) { unit_t *target; target = find_target(world, x, 5.0f, UNIT_HUMAN); if (target) { attack(now, target, 0.75f); start_moving(target->x, now); } next_targetting = now + 0.2; } keep_moving(now, dt, 7.0); } void alien_t::render_to(render::state_t *render) { bool moving; moving = move.moving && !move.blocked; render->render((moving ? &assets::alien.walking : &assets::alien.idle), render_bounds, move.angle); unit_t::render_to(render); } } // namespace game