This commit is contained in:
Nikita Lisitsa 2024-08-19 03:09:51 +03:00
parent e812ea3e3a
commit d7a484f58d

View file

@ -19,8 +19,6 @@
#include <boost/container/flat_set.hpp>
#include <boost/container/flat_map.hpp>
#include <format>
namespace gmtk
{
@ -47,6 +45,13 @@ namespace gmtk
(factory)
)
psemek_declare_enum(card_type, std::uint32_t,
(crossing)
(furnace)
(factory)
(zoomer)
)
gfx::color_rgba color_of(resource_type c)
{
switch (c)
@ -268,37 +273,59 @@ namespace gmtk
return l.coords[0] >= 0 && l.coords[0] < max && l.coords[1] >= 0 && l.coords[1] < max;
}
struct timestamp
{
int trunc = 0;
float frac = 0.f;
timestamp & operator += (float dt)
{
frac += dt;
int t = std::floor(frac);
trunc += t;
frac -= t;
return *this;
}
friend auto operator <=> (timestamp const & x, timestamp const & y) = default;
};
struct lab
{
psemek_ecs_declare_uuid("lab")
util::hash_map<resource_type, int> count = {};
};
struct map
{
ecs::container world;
timestamp time = {};
util::hash_map<card_type, int> cards = {};
int stage = 0;
int resource_count = 0;
float spawn_timer = 0.f;
bool take_card(card_type type)
{
if (!cards.contains(type))
return false;
if (cards.at(type) == 0)
return false;
cards.at(type) -= 1;
return true;
}
void put_card(card_type type)
{
cards[type] += 1;
}
};
struct stage_info
{
resource_type type;
int count;
std::vector<card_type> cards;
std::vector<resource_type> sources;
};
static stage_info stages[]
{
{resource_type::stone, 0, {}, {resource_type::stone}},
{resource_type::stone, 10, {card_type::furnace}, {resource_type::coal}},
{resource_type::stone_brick, 30, {card_type::furnace}, {resource_type::iron_ore}},
{resource_type::iron_plate, 30, {card_type::factory, card_type::crossing, card_type::crossing}, {resource_type::copper_ore}},
{resource_type::red_science_pack, 30, {card_type::furnace, card_type::factory, card_type::crossing, card_type::crossing, card_type::zoomer, card_type::zoomer, card_type::zoomer}, {}},
{resource_type::inserter, 60, {card_type::factory, card_type::factory, card_type::factory, card_type::factory, card_type::crossing, card_type::crossing, card_type::zoomer, card_type::zoomer, card_type::zoomer}, {resource_type::coal}},
{resource_type::green_science_pack, 60, {}, {}},
};
template <typename Component, util::uuid UUID>
@ -417,61 +444,31 @@ namespace gmtk
sink(world, c, m);
}
map starting_map(random::generator & rng)
map starting_map()
{
map result;
result.world.index<index>();
result.world.index<path_index>();
result.world.create(
vertex{{0, {-1, 1}}},
source{resource_type::stone}
);
result.world.create(
vertex{{0, {-1, 2}}},
source{resource_type::iron_ore}
);
result.world.create(
vertex{{0, {1, 3}}},
source{resource_type::coal}
);
result.world.create(
vertex{{0, {3, 2}}},
source{resource_type::copper_ore}
);
result.world.create(
vertex{{0, {1, -1}}},
lab{}
);
(void)rng;
return result;
}
void draw_grid(location origin, float view_level, gfx::painter & painter, bool in_construction)
void draw_grid(geom::box<float, 2> const & box, float view_level, gfx::painter & painter)
{
origin.level += 1;
origin.coords[0] *= 3;
origin.coords[1] *= 3;
float const grid_width = 0.1f * std::pow(3.f, -std::max<float>(view_level + 1.f, origin.level));
auto box = origin.bbox();
float const grid_width = 0.1f * std::min(box[0].length() / 3.f, std::pow(3.f, -1.f - view_level));
auto color = gfx::black.as_color_rgba();
if (in_construction)
color[3] = 127;
for (int x = 0; x <= 3; ++x)
{
painter.line(box.corner(x, 0), box.corner(x, 3), grid_width, color, true);
painter.line(box.corner(0, x), box.corner(3, x), grid_width, color, true);
painter.line(box.corner(x / 3.f, 0.f), box.corner(x / 3.f, 1.f), grid_width, color, true);
painter.line(box.corner(0.f, x / 3.f), box.corner(1.f, x / 3.f), grid_width, color, true);
}
}
@ -527,13 +524,63 @@ namespace gmtk
}
}
void draw_structure(geom::box<float, 2> const & bbox, transformer const & t, gfx::painter & painter)
{
auto box = geom::shrink(bbox, bbox[0].length() * 0.2f);
auto p0 = box.corner(0, 0);
auto p1 = box.corner(1, 0);
auto p2 = box.corner(0.8f, 1);
auto p3 = box.corner(0.2f, 1);
gfx::color_rgba color = {255, 0, 255, 255};
switch (t.type)
{
case transformer_type::furnace: color = color_of(resource_type::stone); break;
case transformer_type::factory: color = color_of(resource_type::iron_ore); break;
}
painter.triangle(p0, p1, p2, color);
painter.triangle(p0, p2, p3, color);
}
void draw_structure(geom::box<float, 2> const & bbox, crossing const &, gfx::painter & painter)
{
auto wbox = geom::shrink(bbox, bbox[0].length() * 0.2f);
auto sbox = geom::shrink(bbox, bbox[0].length() * 0.3f);
gfx::color_rgba color{64, 64, 64, 255};
painter.rect({wbox[0], sbox[1]}, color);
painter.rect({sbox[0], wbox[1]}, color);
}
void draw_card(geom::box<float, 2> const & bbox, card_type type, gfx::painter & painter)
{
switch (type)
{
case card_type::crossing:
draw_structure(bbox, crossing{}, painter);
break;
case card_type::furnace:
draw_structure(bbox, transformer{transformer_type::furnace}, painter);
break;
case card_type::factory:
draw_structure(bbox, transformer{transformer_type::factory}, painter);
break;
case card_type::zoomer:
draw_grid(bbox, -1.f, painter);
break;
}
}
void draw(map & map, float view_level, gfx::painter & painter)
{
draw_grid({-1, {0, 0}}, view_level, painter, false);
draw_grid({{{0.f, 3.f}, {0.f, 3.f}}}, view_level, painter);
map.world.apply<vertex const, zoomer const>([&](vertex const & v, zoomer const &)
{
draw_grid(v.location, view_level, painter, false);
draw_grid(v.location.bbox(), view_level, painter);
});
map.world.apply<path_vertex const>([&](path_vertex const & vertex)
@ -576,46 +623,17 @@ namespace gmtk
// painter.text(v.location.center(), "180/m", {.scale = {vs, -vs}, .c = {0, 0, 0, 255}});
});
auto draw_transformer = [&](location const & l, transformer_type type, bool in_construction)
{
auto box = l.bbox(-0.2f);
auto p0 = box.corner(0, 0);
auto p1 = box.corner(1, 0);
auto p2 = box.corner(0.8f, 1);
auto p3 = box.corner(0.2f, 1);
gfx::color_rgba color = {255, 0, 255, 255};
switch (type)
{
case transformer_type::furnace: color = color_of(resource_type::stone); break;
case transformer_type::factory: color = color_of(resource_type::iron_ore); break;
}
if (in_construction)
color[3] = 127;
painter.triangle(p0, p1, p2, color);
painter.triangle(p0, p2, p3, color);
};
map.world.apply<vertex const, transformer const>([&](vertex const & v, transformer const & t)
{
draw_transformer(v.location, t.type, false);
draw_structure(v.location.bbox(), t, painter);
});
map.world.apply<vertex const, crossing const>([&](vertex const & v, crossing const &)
map.world.apply<vertex const, crossing const>([&](vertex const & v, crossing const & c)
{
auto wbox = v.location.bbox(-0.2f);
auto sbox = v.location.bbox(-0.3f);
gfx::color_rgba color{64, 64, 64, 255};
painter.rect({wbox[0], sbox[1]}, color);
painter.rect({sbox[0], wbox[1]}, color);
draw_structure(v.location.bbox(), c, painter);
});
map.world.apply<vertex const, lab const>([&](vertex const & v, lab const & l)
map.world.apply<vertex const, lab const>([&](vertex const & v, lab const &)
{
painter.rect(v.location.bbox(-0.2f), {128, 192, 255, 255});
@ -623,14 +641,11 @@ namespace gmtk
float vs = std::pow(3.f, - v.location.level) * 0.01f;
for (auto type : {resource_type::red_science_pack, resource_type::green_science_pack})
{
if (l.count.contains(type))
{
painter.text(pen, std::to_string(l.count.at(type)), {.scale = {vs, -vs}, .c = color_of(type)});
pen[1] -= 12.f * vs;
}
}
draw_item(stages[map.stage].type, pen, 1.f, painter);
pen[1] -= 24.f * vs;
painter.text(pen, std::format("{}/{}", map.resource_count, stages[map.stage].count), {.scale = {vs, -vs}, .c = {0, 0, 0, 255}});
});
map.world.apply<item const>([&](item const & i)
@ -650,13 +665,22 @@ namespace gmtk
painter.line(b.corner(0, 1), b.corner(0, 0), s, color, true);
}
std::uint64_t make_seed()
{
random::device d;
return (std::uint64_t(d()) << 32) | d();
}
struct application
: app::application
{
application(options const &, context const &)
: rng_{random::device{}}
, map_(starting_map(rng_))
: seed_(make_seed())
, map_rng_{seed_, 0xf24130ddef6fb31full}
, item_rng_{seed_, 0xb9fc3979f9860bbdull}
, map_(starting_map())
{
log::info() << "Starting seed: " << seed_;
view_stack_.push_back({-1, {0, 0}});
}
@ -665,32 +689,14 @@ namespace gmtk
screen_size_ = event.size;
}
void on_event(app::mouse_move_event const & event) override
void on_event(app::mouse_wheel_event const & event) override
{
mouse_ = event.position;
}
void on_event(app::mouse_button_event const & event) override
{
if (event.down && event.button == app::mouse_button::left)
if (event.delta > 0)
{
bool destroyed_item = false;
bool transitioned = false;
if (selected_item_)
if (!selected_item_ && selected_ && !view_transition_)
{
auto const & i = map_.world.get(*selected_item_).get<item const>();
if (i.target)
map_.world.detach<occupied>(i.target);
else
map_.world.detach<occupied>(map_.world.index<path_index>().get(i.start));
map_.world.destroy(*selected_item_);
selected_item_ = std::nullopt;
destroyed_item = true;
}
bool transitioned = false;
if (!destroyed_item && selected_ && !view_transition_)
{
if (auto entity = map_.world.index<index>().find(*selected_))
if (map_.world.get(*entity).contains<zoomer>())
{
@ -708,8 +714,31 @@ namespace gmtk
transitioned = true;
}
}
}
if (!destroyed_item && !transitioned && selected_ && !belt_start_)
if (event.delta < 0)
{
if (!view_transition_ && view_stack_.size() > 1)
{
view_transition_ = {view_stack_.back()};
view_stack_.pop_back();
selected_ = std::nullopt;
}
}
}
void on_event(app::mouse_move_event const & event) override
{
mouse_ = event.position;
}
void on_event(app::mouse_button_event const & event) override
{
if (event.down && event.button == app::mouse_button::left)
{
lmb_down_ = true;
if (!selected_item_ && selected_ && !belt_start_)
{
belt_start_ = *selected_;
}
@ -717,6 +746,8 @@ namespace gmtk
if (!event.down && event.button == app::mouse_button::left)
{
lmb_down_ = false;
if (selected_ && belt_start_ && selected_->level == belt_start_->level)
{
auto d = selected_->coords - belt_start_->coords;
@ -753,28 +784,20 @@ namespace gmtk
}
belt_start_ = std::nullopt;
}
if (event.down && event.button == app::mouse_button::right)
{
if (!view_transition_ && view_stack_.size() > 1)
{
view_transition_ = {view_stack_.back()};
view_stack_.pop_back();
selected_ = std::nullopt;
}
}
}
void on_event(app::key_event const & event) override
{
if (event.down && event.key == app::keycode::F)
{
if (selected_ && !map_.world.index<index>().find(*selected_))
{
map_.world.create(
vertex{*selected_},
transformer{transformer_type::furnace}
);
if (map_.take_card(card_type::furnace))
map_.world.create(
vertex{*selected_},
transformer{transformer_type::furnace}
);
}
}
@ -782,10 +805,11 @@ namespace gmtk
{
if (selected_ && !map_.world.index<index>().find(*selected_))
{
map_.world.create(
vertex{*selected_},
transformer{transformer_type::factory}
);
if (map_.take_card(card_type::factory))
map_.world.create(
vertex{*selected_},
transformer{transformer_type::factory}
);
}
}
@ -793,10 +817,11 @@ namespace gmtk
{
if (selected_ && !map_.world.index<index>().find(*selected_))
{
map_.world.create(
vertex{*selected_},
crossing{}
);
if (map_.take_card(card_type::crossing))
map_.world.create(
vertex{*selected_},
crossing{}
);
}
}
@ -804,17 +829,20 @@ namespace gmtk
{
if (selected_ && !map_.world.index<index>().find(*selected_))
{
map_.world.create(
vertex{*selected_},
zoomer{}
);
if (map_.take_card(card_type::zoomer))
{
map_.world.create(
vertex{*selected_},
zoomer{}
);
auto c = selected_->down();
auto c = selected_->down();
sink_belt(map_.world, c.left());
sink_belt(map_.world, c.right());
sink_belt(map_.world, c.bottom());
sink_belt(map_.world, c.top());
sink_belt(map_.world, c.left());
sink_belt(map_.world, c.right());
sink_belt(map_.world, c.bottom());
sink_belt(map_.world, c.top());
}
}
}
@ -826,7 +854,20 @@ namespace gmtk
{
auto acc = map_.world.get(*entity);
if (!acc.contains<source>() && !acc.contains<lab>() && !acc.contains<zoomer>())
{
if (acc.contains<crossing>())
map_.put_card(card_type::crossing);
else if (acc.contains<zoomer>())
map_.put_card(card_type::zoomer);
else if (auto t = acc.get_if<transformer>())
{
if (t->type == transformer_type::furnace)
map_.put_card(card_type::furnace);
else if (t->type == transformer_type::factory)
map_.put_card(card_type::factory);
}
map_.world.destroy(*entity);
}
}
}
}
@ -846,8 +887,6 @@ namespace gmtk
{
float const dt = clock_.restart().count();
map_.time += dt;
map_.spawn_timer += 3.f * dt;
if (map_.spawn_timer >= 1.f)
{
@ -872,6 +911,41 @@ namespace gmtk
);
}
if (map_.stage + 1 < std::size(stages) && map_.resource_count >= stages[map_.stage].count)
{
for (auto card : stages[map_.stage].cards)
map_.cards[card] += 1;
for (auto type : stages[map_.stage].sources)
{
util::hash_set<location, location_hash> spots;
auto add = [&](location l)
{
if (!map_.world.index<index>().find(l))
spots.insert(l);
};
for (int i = 0; i < 3; ++i)
{
add({0, {-1, i}});
add({0, {3, i}});
add({0, {i, 3}});
}
if (!spots.empty())
{
map_.world.create(
vertex{random::uniform_from(map_rng_, spots)},
source{type}
);
}
}
map_.stage += 1;
map_.resource_count = 0;
}
map_.world.apply<vertex const, transformer const>([&](vertex const & v, transformer const & t)
{
boost::container::flat_map<resource_type, location> has_inputs;
@ -928,14 +1002,6 @@ namespace gmtk
if (!crafted && !has_inputs.empty())
{
return;
auto p = random::uniform_from(rng_, has_inputs);
auto e = map_.world.index<path_index>().get(p.second);
auto re = map_.world.get(e).get<occupied>().entity;
map_.world.detach<occupied>(e);
map_.world.destroy(re);
}
});
@ -978,10 +1044,17 @@ namespace gmtk
if (auto cell = map_.world.index<index>().find(i.start.up()))
{
if (auto l = map_.world.get(*cell).get_if<lab>())
if (map_.world.get(*cell).get_if<lab>())
{
l->count[i.type] += 1;
map_.world.destroy(entity);
if (i.type == stages[map_.stage].type)
{
map_.resource_count += 1;
map_.world.destroy(entity);
}
else
{
map_.world.attach(map_.world.index<path_index>().get(i.start), occupied{entity});
}
return;
}
@ -1032,7 +1105,7 @@ namespace gmtk
targets.push_back(b);
if (!targets.empty())
i.target = random::uniform_from(rng_, targets);
i.target = random::uniform_from(item_rng_, targets);
if (i.target)
map_.world.attach(i.target, occupied{entity});
@ -1082,6 +1155,17 @@ namespace gmtk
selected_item_ = entity;
});
if (selected_item_ && lmb_down_)
{
auto const & i = map_.world.get(*selected_item_).get<item const>();
if (i.target)
map_.world.detach<occupied>(i.target);
else
map_.world.detach<occupied>(map_.world.index<path_index>().get(i.start));
map_.world.destroy(*selected_item_);
selected_item_ = std::nullopt;
}
{
float s = std::pow(3.f, 1 + view_stack_.back().level);
@ -1208,19 +1292,41 @@ namespace gmtk
}
}
{
float const step = std::pow(3.f, - 2.f - view_level);
float vs = std::pow(3.f, - 1.f - view_level) * 0.01f;
geom::point<float, 2> pen = view_box_.corner(1, 1) + geom::vector{-1.f, -1.f} * view_box_[1].length() / 10.f;
for (auto type : card_type_values())
{
if (!map_.cards.contains(type))
continue;
draw_card(geom::expand(geom::box<float, 2>::singleton(pen), step), type, painter_);
painter_.text(pen, std::to_string(map_.cards.at(type)), {.scale = {vs, -vs}, .c = {0, 0, 0, 255}});
pen[1] -= step * 2.f;
}
}
painter_.render(geom::orthographic_camera{view_box_}.transform());
}
private:
bool running_ = true;
random::generator rng_;
std::uint64_t seed_;
random::generator map_rng_;
random::generator item_rng_;
map map_;
util::clock<> clock_;
geom::vector<int, 2> screen_size_{1, 1};
geom::point<int, 2> mouse_{0, 0};
bool lmb_down_ = false;
gfx::painter painter_;