/*
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;
world.generator = worldgen;
world.generator_data = (void*)this;
soldier = new unit_soldier_t(this);
soldier->place(&world, v2f_t(0.5, 0.5));
soldier = new unit_soldier_t(this);
soldier->place(&world, v2f_t(1.5, 0.5));
soldier = new unit_soldier_t(this);
soldier->place(&world, v2f_t(2.5, 0.5));
select(rectf_t(v2f_t(0.5, 0.5), v2f_t(2.5, 0.5)));
resume();
}
void state_t::stop(void)
{
}
void state_t::group_say(std::string text)
{
interface->print(text::get(text::SAY_GROUP) + ": " + text);
}
void state_t::select(rectf_t x)
{
selection_cookie++;
selected_units.clear();
for (world::entity_t *ent : world.get_render_entities(x)) {
unit_t *unit;
if (ent->type != ET_UNIT)
continue;
unit = (unit_t*)ent;
if (!unit->controllable)
continue;
unit->selected = selection_cookie;
selected_units.insert(unit);
}
if (selected_units.size() == 1)
(*selected_units.begin())->say(text::get(text::SAY_READY));
else if (selected_units.size() > 1)
group_say(text::get(text::SAY_READY_GROUP));
}
enum {
COMMAND_MOVE,
COMMAND_FIRE,
COMMAND_STOP
};
bool state_t::populate_pie_menu(std::vector &items)
{
items.clear();
if (selected_units.size() == 0)
return false;
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});
return true;
}
void state_t::command(v2f_t x, int number)
{
v2f_t snap;
bool group;
if (!selected_units.size())
return;
snap[0] = std::round(x[0] - 0.5f) + 0.5f;
snap[1] = std::round(x[1] - 0.5f) + 0.5f;
group = selected_units.size() > 1;
if (group) switch (number) {
case COMMAND_MOVE:
group_say(text::get(text::SAY_MOVING_GROUP));
break;
case COMMAND_STOP:
group_say(text::get(text::SAY_STOPPING_GROUP));
break;
case COMMAND_FIRE:
group_say(text::get(text::SAY_FIRING_GROUP));
break;
}
for (unit_t *unit : selected_units) {
unit_soldier_t *soldier;
if (unit->type != unit_t::UNIT_SOLDIER)
continue;
soldier = dynamic_cast(unit);
if (soldier->dead)
continue;
if (!soldier->controllable)
continue;
switch (number) {
case COMMAND_MOVE:
if (!soldier->start_moving(snap))
soldier->say(text::get(text::SAY_NO_PATH));
else {
soldier->move_marker = std::make_unique(this, soldier->move.path.back());
if (!group)
soldier->say(text::get(text::SAY_MOVING));
}
break;
case COMMAND_STOP:
soldier->stop_moving();
soldier->manual_firing = false;
if (!group)
soldier->say(text::get(text::SAY_STOPPING));
break;
case COMMAND_FIRE:
soldier->manual_firing = true;
soldier->manual_firing_target = x;
if (!group)
soldier->say(text::get(text::SAY_FIRING));
break;
}
}
}
void state_t::spawn_soldier(v2f_t x)
{
unit_soldier_t *soldier;
world::cmodel_t cmodel;
soldier = new unit_soldier_t(this);
soldier->place(&world, x);
cmodel.bounds = soldier->cmodel.bounds;
cmodel.cflags = soldier->move.cflags;
if (world.test_rect(&cmodel, soldier))
soldier->destroy();
}
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?
dice_prng.seed(dice_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;
}
}
}
void state_t::compute_ambience(render::state_t *render)
{
const size_t samples = 25;
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 < 5; y++)
for (size_t x = 0; x < 5; x++) {
v2f_t point;
world::tile_t *tile;
int type;
point[0] = lerp(area[0][0], area[1][0], y / 4.0f);
point[1] = lerp(area[0][1], area[1][1], x / 4.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;
}
}
die_t::die_t(size_t sides_)
{
sides = sides_;
}
die_t::die_t(size_t count_, size_t sides_)
{
count = count_;
sides = sides_;
}
die_t::die_t(size_t count_, size_t sides_, size_t bonus_)
{
count = count_;
sides = sides_;
bonus = bonus_;
}
size_t state_t::roll(die_t die)
{
size_t total = 0;
for (size_t i = 0; i < die.count; i++)
total += dice_prng.next() % die.sides + 1;
return total + die.bonus;
}
bool load_assets(void)
{
assets::load();
return true;
}
} //namespace game