Factorio-like gameplay wip
This commit is contained in:
parent
b0ab5c3d5b
commit
9bfbe69df9
1 changed files with 350 additions and 120 deletions
|
|
@ -16,8 +16,8 @@
|
|||
#include <psemek/log/log.hpp>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
|
||||
#include <variant>
|
||||
#include <deque>
|
||||
#include <format>
|
||||
|
||||
|
|
@ -26,23 +26,88 @@ namespace gmtk
|
|||
|
||||
using namespace psemek;
|
||||
|
||||
psemek_declare_enum(color, std::uint32_t,
|
||||
(red)
|
||||
(green)
|
||||
(blue)
|
||||
psemek_declare_enum(resource_type, std::uint32_t,
|
||||
(stone)
|
||||
(coal)
|
||||
(iron_ore)
|
||||
(copper_ore)
|
||||
(stone_brick)
|
||||
(iron_plate)
|
||||
(copper_plate)
|
||||
(red_science_pack)
|
||||
)
|
||||
|
||||
gfx::color_rgba to_color(color c)
|
||||
psemek_declare_enum(transformer_type, std::uint32_t,
|
||||
(furnace)
|
||||
(factory)
|
||||
)
|
||||
|
||||
gfx::color_rgba color_of(resource_type c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case color::red: return {255, 127, 127, 255};
|
||||
case color::green: return {127, 255, 127, 255};
|
||||
case color::blue: return {127, 127, 255, 255};
|
||||
case resource_type::stone: return {144, 128, 96, 255};
|
||||
case resource_type::coal: return {32, 32, 32, 255};
|
||||
case resource_type::iron_ore: return {96, 128, 144, 255};
|
||||
case resource_type::copper_ore: return {224, 128, 64, 255};
|
||||
case resource_type::stone_brick: return color_of(resource_type::stone);
|
||||
case resource_type::iron_plate: return color_of(resource_type::iron_ore);
|
||||
case resource_type::copper_plate: return color_of(resource_type::copper_ore);
|
||||
case resource_type::red_science_pack: return {192, 64, 64, 255};
|
||||
}
|
||||
|
||||
throw util::unknown_enum_value_exception{c};
|
||||
}
|
||||
|
||||
struct recipe
|
||||
{
|
||||
util::hash_set<resource_type> inputs;
|
||||
resource_type output;
|
||||
};
|
||||
|
||||
static util::hash_map<transformer_type, util::hash_map<resource_type, int>> const construction_recipe
|
||||
{
|
||||
{
|
||||
transformer_type::furnace,
|
||||
{
|
||||
{resource_type::stone, 30},
|
||||
},
|
||||
},
|
||||
{
|
||||
transformer_type::factory,
|
||||
{
|
||||
{resource_type::stone_brick, 30},
|
||||
{resource_type::iron_plate, 30},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static util::hash_map<transformer_type, std::vector<recipe>> const recipies
|
||||
{
|
||||
{
|
||||
transformer_type::furnace,
|
||||
{
|
||||
{{resource_type::coal, resource_type::stone}, resource_type::stone_brick},
|
||||
{{resource_type::coal, resource_type::iron_ore}, resource_type::iron_plate},
|
||||
{{resource_type::coal, resource_type::copper_ore}, resource_type::copper_plate},
|
||||
},
|
||||
},
|
||||
{
|
||||
transformer_type::factory,
|
||||
{
|
||||
{{resource_type::iron_plate, resource_type::copper_plate}, resource_type::red_science_pack},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
geom::vector<int, 2> const neighbours[4]
|
||||
{
|
||||
{1, 0},
|
||||
{0, 1},
|
||||
{-1, 0},
|
||||
{0, -1},
|
||||
};
|
||||
|
||||
struct location
|
||||
{
|
||||
int level;
|
||||
|
|
@ -118,15 +183,22 @@ namespace gmtk
|
|||
{
|
||||
psemek_ecs_declare_uuid("source")
|
||||
|
||||
color type;
|
||||
resource_type type;
|
||||
};
|
||||
|
||||
struct build_site
|
||||
{
|
||||
psemek_ecs_declare_uuid("build_site")
|
||||
|
||||
transformer_type type;
|
||||
util::hash_map<resource_type, int> resources = {};
|
||||
};
|
||||
|
||||
struct transformer
|
||||
{
|
||||
psemek_ecs_declare_uuid("transformer")
|
||||
|
||||
color input;
|
||||
color output;
|
||||
transformer_type type;
|
||||
};
|
||||
|
||||
struct zoomer
|
||||
|
|
@ -167,7 +239,7 @@ namespace gmtk
|
|||
{
|
||||
psemek_ecs_declare_uuid("item")
|
||||
|
||||
color type;
|
||||
resource_type type;
|
||||
location start;
|
||||
ecs::handle target = ecs::handle::null();
|
||||
float state = 0.f;
|
||||
|
|
@ -200,12 +272,9 @@ namespace gmtk
|
|||
{
|
||||
psemek_ecs_declare_uuid("task")
|
||||
|
||||
color type;
|
||||
resource_type type;
|
||||
|
||||
// In last 15 seconds
|
||||
std::deque<timestamp> received = {};
|
||||
|
||||
static constexpr int freq = 3;
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
struct map
|
||||
|
|
@ -335,9 +404,9 @@ namespace gmtk
|
|||
|
||||
void generate_next_task(random::generator & rng, map & map)
|
||||
{
|
||||
color type;
|
||||
resource_type type;
|
||||
|
||||
util::hash_set<color> existing_tasks;
|
||||
util::hash_set<resource_type> existing_tasks;
|
||||
int task_count = 0;
|
||||
map.world.apply<task const>([&](task const & task)
|
||||
{
|
||||
|
|
@ -347,7 +416,7 @@ namespace gmtk
|
|||
|
||||
while (true)
|
||||
{
|
||||
type = random::uniform_from(rng, color_values());
|
||||
type = random::uniform_from(rng, resource_type_values());
|
||||
if (task_count >= 3)
|
||||
break;
|
||||
|
||||
|
|
@ -381,7 +450,32 @@ namespace gmtk
|
|||
result.world.index<index>();
|
||||
result.world.index<path_index>();
|
||||
|
||||
generate_next_task(rng, result);
|
||||
result.world.create(
|
||||
vertex{{0, {-1, 1}}},
|
||||
source{resource_type::stone}
|
||||
);
|
||||
|
||||
result.world.create(
|
||||
vertex{{0, {0, 3}}},
|
||||
source{resource_type::iron_ore}
|
||||
);
|
||||
|
||||
result.world.create(
|
||||
vertex{{0, {1, 3}}},
|
||||
source{resource_type::coal}
|
||||
);
|
||||
|
||||
result.world.create(
|
||||
vertex{{0, {2, 3}}},
|
||||
source{resource_type::copper_ore}
|
||||
);
|
||||
|
||||
result.world.create(
|
||||
vertex{{0, {1, -1}}},
|
||||
task{resource_type::red_science_pack}
|
||||
);
|
||||
|
||||
(void)rng;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -447,23 +541,63 @@ namespace gmtk
|
|||
|
||||
map.world.apply<vertex const, source const>([&](vertex const & v, source const & s)
|
||||
{
|
||||
// float vs = std::pow(3.f, - v.location.level) * 0.01f;
|
||||
painter.rect(v.location.bbox(-0.2f), color_of(s.type));
|
||||
// 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, build_site const>([&](vertex const & v, build_site const & t)
|
||||
{
|
||||
draw_transformer(v.location, t.type, true);
|
||||
|
||||
float vs = std::pow(3.f, - v.location.level) * 0.01f;
|
||||
painter.rect(v.location.bbox(-0.2f), to_color(s.type));
|
||||
painter.text(v.location.center(), "180/m", {.scale = {vs, -vs}, .c = {0, 0, 0, 255}});
|
||||
auto text_bbox = v.location.bbox(-0.4f);
|
||||
auto pen = text_bbox.corner(0.5f, 1.f);
|
||||
|
||||
for (auto const & p : construction_recipe.at(t.type))
|
||||
{
|
||||
int have = t.resources.contains(p.first) ? t.resources.at(p.first) : 0;
|
||||
int need = p.second;
|
||||
|
||||
painter.text(pen, std::format("{}/{}", have, need), {.scale = {vs, -vs}, .c = color_of(p.first)});
|
||||
|
||||
pen[1] -= 12.f * vs;
|
||||
}
|
||||
});
|
||||
|
||||
map.world.apply<vertex const, transformer const>([&](vertex const & v, transformer const & t)
|
||||
{
|
||||
auto box = v.location.bbox(-0.2f);
|
||||
painter.triangle(box.corner(0, 0), box.corner(1, 1), box.corner(0, 1), to_color(t.input));
|
||||
painter.triangle(box.corner(0, 0), box.corner(1, 0), box.corner(1, 1), to_color(t.output));
|
||||
draw_transformer(v.location, t.type, false);
|
||||
});
|
||||
|
||||
map.world.apply<vertex const, task const>([&](vertex const & v, task const & t)
|
||||
{
|
||||
float vs = std::pow(3.f, - v.location.level) * 0.01f;
|
||||
painter.rect(v.location.bbox(-0.2f), to_color(t.type));
|
||||
painter.text(v.location.center(), std::format("{}/m", t.received.size() * task::freq), {.scale = {vs, -vs}, .c = {0, 0, 0, 255}});
|
||||
painter.rect(v.location.bbox(-0.2f), color_of(t.type));
|
||||
painter.text(v.location.center(), std::format("{}", t.count), {.scale = {vs, -vs}, .c = {0, 0, 0, 255}});
|
||||
});
|
||||
|
||||
map.world.apply<item const>([&](item const & i)
|
||||
|
|
@ -483,8 +617,36 @@ namespace gmtk
|
|||
}
|
||||
scale *= 3.f;
|
||||
|
||||
painter.circle(pos, 0.075f * scale, {0, 0, 0, 255});
|
||||
painter.circle(pos, 0.05f * scale, to_color(i.type));
|
||||
auto color = color_of(i.type);
|
||||
|
||||
switch (i.type)
|
||||
{
|
||||
case resource_type::coal:
|
||||
case resource_type::stone:
|
||||
case resource_type::iron_ore:
|
||||
case resource_type::copper_ore:
|
||||
painter.circle(pos, 0.075f * scale, {0, 0, 0, 255});
|
||||
painter.circle(pos, 0.05f * scale, color);
|
||||
break;
|
||||
case resource_type::stone_brick:
|
||||
case resource_type::iron_plate:
|
||||
case resource_type::copper_plate:
|
||||
{
|
||||
auto box = geom::expand(geom::box<float, 2>::singleton(pos), 0.075f * scale);
|
||||
painter.rect(box, {0, 0, 0, 255});
|
||||
box = geom::shrink(box, 0.025f * scale);
|
||||
painter.rect(box, color);
|
||||
}
|
||||
break;
|
||||
case resource_type::red_science_pack:
|
||||
{
|
||||
auto box = geom::expand(geom::box<float, 2>::singleton(pos), 0.075f * scale);
|
||||
painter.triangle(box.corner(0, 0), box.corner(1, 0), box.corner(0.5f, 1), {0, 0, 0, 255});
|
||||
box = geom::shrink(box, 0.025f * scale);
|
||||
painter.triangle(box.corner(0, 0), box.corner(1, 0), box.corner(0.5f, 1), color);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -546,74 +708,50 @@ namespace gmtk
|
|||
|
||||
void on_event(app::key_event const & event) override
|
||||
{
|
||||
if (event.down && event.key == app::keycode::S)
|
||||
if (event.down && event.key == app::keycode::B)
|
||||
{
|
||||
if (selected_ && !map_.world.index<index>().find(*selected_))
|
||||
if (selected_ && !belt_start_)
|
||||
{
|
||||
util::hash_set<color> types;
|
||||
int task_count = 0;
|
||||
|
||||
map_.world.apply<task const>([&](task const & t)
|
||||
{
|
||||
types.insert(t.type);
|
||||
task_count += 1;
|
||||
});
|
||||
|
||||
if (task_count > 1)
|
||||
for (auto t : color_values())
|
||||
types.insert(t);
|
||||
|
||||
map_.world.create(
|
||||
vertex{*selected_},
|
||||
source{random::uniform_from(rng_, types)}
|
||||
);
|
||||
belt_start_ = *selected_;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.down && event.key == app::keycode::B)
|
||||
if (!event.down && event.key == app::keycode::B)
|
||||
{
|
||||
if (selected_)
|
||||
if (selected_ && belt_start_ && selected_->level == belt_start_->level)
|
||||
{
|
||||
if (belt_start_)
|
||||
auto d = selected_->coords - belt_start_->coords;
|
||||
if (std::abs(d[0]) + std::abs(d[1]) == 1)
|
||||
{
|
||||
if (selected_->level == belt_start_->level)
|
||||
auto s = belt_start_->down();
|
||||
|
||||
location belt[4];
|
||||
for (int i = 0; i <= 3; ++i)
|
||||
belt[i] = s.moved(d * i);
|
||||
sink(map_.world, belt[0], belt[1]);
|
||||
sink(map_.world, belt[3], belt[2]);
|
||||
|
||||
auto & index = map_.world.index<path_index>();
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
auto d = selected_->coords - belt_start_->coords;
|
||||
if (std::abs(d[0]) + std::abs(d[1]) == 1)
|
||||
auto p = belt[i];
|
||||
auto q = belt[i + 1];
|
||||
|
||||
auto s = index.get(p);
|
||||
auto t = index.get(q);
|
||||
|
||||
auto & sv = map_.world.get(s).get<path_vertex>();
|
||||
|
||||
if (sv.belts_to.contains(t))
|
||||
remove_belt(map_.world, s, t);
|
||||
else
|
||||
{
|
||||
auto s = belt_start_->down();
|
||||
|
||||
location belt[4];
|
||||
for (int i = 0; i <= 3; ++i)
|
||||
belt[i] = s.moved(d * i);
|
||||
sink(map_.world, belt[0], belt[1]);
|
||||
sink(map_.world, belt[3], belt[2]);
|
||||
|
||||
auto & index = map_.world.index<path_index>();
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
auto p = belt[i];
|
||||
auto q = belt[i + 1];
|
||||
|
||||
auto s = index.get(p);
|
||||
auto t = index.get(q);
|
||||
|
||||
auto & sv = map_.world.get(s).get<path_vertex>();
|
||||
|
||||
if (sv.belts_to.contains(t))
|
||||
remove_belt(map_.world, s, t);
|
||||
else
|
||||
{
|
||||
remove_belt(map_.world, t, s);
|
||||
add_belt(map_.world, s, t);
|
||||
}
|
||||
}
|
||||
remove_belt(map_.world, t, s);
|
||||
add_belt(map_.world, s, t);
|
||||
}
|
||||
}
|
||||
belt_start_ = std::nullopt;
|
||||
}
|
||||
else if (within_grid(*selected_))
|
||||
belt_start_ = *selected_;
|
||||
belt_start_ = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -621,28 +759,20 @@ namespace gmtk
|
|||
{
|
||||
if (selected_ && !map_.world.index<index>().find(*selected_))
|
||||
{
|
||||
util::hash_set<color> types;
|
||||
int task_count = 0;
|
||||
|
||||
map_.world.apply<task const>([&](task const & t)
|
||||
{
|
||||
types.insert(t.type);
|
||||
task_count += 1;
|
||||
});
|
||||
|
||||
if (task_count == 1)
|
||||
{
|
||||
for (auto c : color_values())
|
||||
types.insert(c);
|
||||
}
|
||||
|
||||
color input = random::uniform_from(rng_, types);
|
||||
types.erase(input);
|
||||
color output = random::uniform_from(rng_, types);
|
||||
|
||||
map_.world.create(
|
||||
vertex{*selected_},
|
||||
transformer{input, output}
|
||||
build_site{transformer_type::furnace}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.down && event.key == app::keycode::G)
|
||||
{
|
||||
if (selected_ && !map_.world.index<index>().find(*selected_))
|
||||
{
|
||||
map_.world.create(
|
||||
vertex{*selected_},
|
||||
build_site{transformer_type::factory}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -670,8 +800,11 @@ namespace gmtk
|
|||
if (selected_)
|
||||
{
|
||||
if (auto entity = map_.world.index<index>().find(*selected_))
|
||||
if (!map_.world.get(*entity).contains<task>())
|
||||
{
|
||||
auto acc = map_.world.get(*entity);
|
||||
if (!acc.contains<source>() && !acc.contains<task>() && !acc.contains<zoomer>())
|
||||
map_.world.destroy(*entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -721,12 +854,97 @@ namespace gmtk
|
|||
);
|
||||
}
|
||||
|
||||
map_.world.apply<task>([&](task & t)
|
||||
map_.world.apply<build_site>([&](ecs::handle entity, build_site & b)
|
||||
{
|
||||
auto threshold = map_.time;
|
||||
threshold.trunc -= 60 / task::freq;
|
||||
while (!t.received.empty() && t.received.front() < threshold)
|
||||
t.received.pop_front();
|
||||
bool built = true;
|
||||
for (auto const & p : construction_recipe.at(b.type))
|
||||
{
|
||||
if (!b.resources.contains(p.first))
|
||||
{
|
||||
built = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (b.resources.at(p.first) < p.second)
|
||||
{
|
||||
built = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (built)
|
||||
{
|
||||
auto type = b.type;
|
||||
map_.world.detach<build_site>(entity);
|
||||
map_.world.attach(entity, transformer{type});
|
||||
}
|
||||
});
|
||||
|
||||
map_.world.apply<vertex const, transformer const>([&](vertex const & v, transformer const & t)
|
||||
{
|
||||
boost::container::flat_map<resource_type, location> has_inputs;
|
||||
|
||||
auto c = v.location.down();
|
||||
auto ce = map_.world.index<path_index>().get(c);
|
||||
|
||||
if (map_.world.get(ce).contains<occupied>())
|
||||
return;
|
||||
|
||||
for (auto n : neighbours)
|
||||
{
|
||||
auto p = c.moved(n);
|
||||
if (auto ne = map_.world.index<path_index>().find(p))
|
||||
if (map_.world.get(*ne).get<path_vertex>().belts_to.contains(ce))
|
||||
if (auto occ = map_.world.get(*ne).get_if<occupied const>())
|
||||
if (auto i = map_.world.get(occ->entity).get_if<item const>())
|
||||
if (i->start == p)
|
||||
has_inputs[i->type] = p;
|
||||
}
|
||||
|
||||
bool crafted = false;
|
||||
|
||||
for (auto const & recipe : recipies.at(t.type))
|
||||
{
|
||||
bool has_all = true;
|
||||
for (auto type : recipe.inputs)
|
||||
has_all &= has_inputs.contains(type);
|
||||
|
||||
if (!has_all)
|
||||
continue;
|
||||
|
||||
for (auto type : recipe.inputs)
|
||||
{
|
||||
auto l = has_inputs.at(type);
|
||||
auto e = map_.world.index<path_index>().get(l);
|
||||
auto re = map_.world.get(e).get<occupied>().entity;
|
||||
|
||||
map_.world.detach<occupied>(e);
|
||||
map_.world.destroy(re);
|
||||
}
|
||||
|
||||
auto r = map_.world.create(
|
||||
item{recipe.output, c}
|
||||
);
|
||||
|
||||
map_.world.attach(ce, occupied{r});
|
||||
|
||||
crafted = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
map_.world.apply<item>([&](ecs::handle entity, item & i)
|
||||
|
|
@ -768,18 +986,31 @@ namespace gmtk
|
|||
|
||||
if (auto cell = map_.world.index<index>().find(i.start.up()))
|
||||
{
|
||||
if (auto t = map_.world.get(*cell).get_if<task>())
|
||||
if (auto t = map_.world.get(*cell).get_if<build_site>())
|
||||
{
|
||||
if (t->type == i.type)
|
||||
t->received.push_back(map_.time);
|
||||
t->resources[i.type] += 1;
|
||||
map_.world.destroy(entity);
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto t = map_.world.get(*cell).get_if<transformer>())
|
||||
if (auto t = map_.world.get(*cell).get_if<task>())
|
||||
{
|
||||
if (t->input == i.type)
|
||||
i.type = t->output;
|
||||
if (t->type == i.type)
|
||||
t->count += 1;
|
||||
map_.world.destroy(entity);
|
||||
return;
|
||||
}
|
||||
|
||||
if (map_.world.get(*cell).get_if<transformer>())
|
||||
{
|
||||
auto se = map_.world.index<path_index>().get(i.start);
|
||||
auto ce = map_.world.index<path_index>().get(i.start.up().down());
|
||||
|
||||
if (map_.world.get(se).get<path_vertex>().belts_to.contains(ce))
|
||||
{
|
||||
map_.world.attach(map_.world.index<path_index>().get(i.start), occupied{entity});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -844,8 +1075,7 @@ namespace gmtk
|
|||
if (p.coords[0] >= 3 * l.coords[0] && p.coords[0] < 3 * l.coords[0] + 3 && p.coords[1] >= 3 * l.coords[1] && p.coords[1] < 3 * l.coords[1] + 3)
|
||||
selected_ = p;
|
||||
else if (auto entity = map_.world.index<index>().find(p))
|
||||
if (map_.world.get(*entity).contains<task>())
|
||||
selected_ = p;
|
||||
selected_ = p;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue