game: add a named areas page to the city maps

This commit is contained in:
Crypto City 2023-03-17 20:58:52 +00:00
parent 0feee23f0d
commit 660e78acea
6 changed files with 174 additions and 48 deletions

View File

@ -80,6 +80,7 @@ TBLayout: axis: y, distribution-position: "left top", distribution: "gravity"
TBLayout: axis: y
TBTabContainer: id: "maps", gravity: "all"
tabs
TBButton: text: "Places"
TBButton: text: "Elevation"
TBButton: text: "Stability"
TBButton: text: "Agriculture"
@ -90,6 +91,9 @@ TBLayout: axis: y, distribution-position: "left top", distribution: "gravity"
TBButton: text: "Stone quantity"
TBButton: text: "Gemstone"
TBButton: text: "Manual"
TBLayout: axis: y, gravity: "all", size: "available"
TBTextField: text: "Hover mouse to see area names"
UITBAnimatedImageWidget: id: "places-map"
TBLayout: axis: y, gravity: "all", size: "available"
TBTextField: text: "Higher score denotes higher elevation, water area in blue"
TBTextField: text: "Preview only available at zoom level 5 or less"

View File

@ -88,24 +88,6 @@ static uint32_t index_data_template[36] = {
static inline int is_power_of_2(unsigned n) { return n && (n & (n-1)) == 0; }
// https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
static Color GetRandomColor(size_t n, float alpha = 1.0f)
{
static constexpr float s = .85f;
static constexpr float v = .95f;
static constexpr float golden_ratio_conjugate = 0.618033988749895;
float h = 0;
while (n--)
{
h += golden_ratio_conjugate;
h -= trunc(h);
}
Color c;
c.FromHSV(h, s, v);
c.a_ = alpha;
return c;
}
static uint16_t get_material_for_block(const std::shared_ptr<Flag> &flag, uint8_t block)
{
const uint16_t variant = block < flag->palette.size() ? flag->palette[block] : (uint16_t)cc::BLOCK_VARIANT_NONE;
@ -2020,7 +2002,7 @@ void CityMeshSection::AddAreas(const std::shared_ptr<Flag> &flag)
Technique *technique = cache->GetResource<Technique>("MaterialEffects/Techniques/DiffEmissiveGlow.xml");
material->SetTechnique(0, technique);
}
material->SetShaderParameter("MatDiffColor", GetRandomColor(flag->owner, 1.0f));
material->SetShaderParameter("MatDiffColor", game_util::GetRandomColor(flag->owner, 1.0f));
resources.ownership_materials[std::make_pair(flag->owner, is_burning)] = material;
}

View File

@ -1,6 +1,7 @@
#include <memory>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>
#include <Urho3D/Math/Color.h>
#include "reg_exp_definer.h"
#include "common/util.h"
#include "common/ipfs.h"
@ -826,4 +827,22 @@ void update_accrual(std::vector<cryptonote::matchable_order_as_string_t> &orders
});
}
// https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
Urho3D::Color GetRandomColor(size_t n, float alpha)
{
static constexpr float s = .85f;
static constexpr float v = .95f;
static constexpr float golden_ratio_conjugate = 0.618033988749895;
float h = 0;
while (n--)
{
h += golden_ratio_conjugate;
h -= trunc(h);
}
Urho3D::Color c;
c.FromHSV(h, s, v);
c.a_ = alpha;
return c;
}
}

View File

@ -6,6 +6,11 @@
#include <map>
#include "cryptonote_basic/cc_command_defs.h"
namespace Urho3D
{
class Color;
}
class GameState;
class GameWallet;
class Flag;
@ -37,5 +42,6 @@ uint64_t get_role_bonus_adjusted_shares(const GameState *game, uint64_t shares,
bool get_resource_deltas(const GameState *game, const std::shared_ptr<Flag> &flag, uint32_t cartography_level, uint32_t logging_level, uint32_t quarrying_level, uint32_t management_level, int64_t &delta_gold, std::map<uint32_t, int32_t> &delta_items, int32_t &delta_food, int32_t &delta_heating);
std::tuple<std::string, uint64_t, std::string> get_scarcest_resource(uint64_t balance, const std::map<uint32_t, uint32_t> &item_balances, int64_t delta_gold, int32_t delta_sandstone, int32_t delta_pine, int32_t delta_labour, int32_t delta_food, int32_t delta_heating);
void update_accrual(std::vector<cryptonote::matchable_order_as_string_t> &orders, uint64_t height, bool ascending);
Urho3D::Color GetRandomColor(size_t n, float alpha = 1.0f);
}

View File

@ -34,6 +34,7 @@ UICityMapsDialog::UICityMapsDialog(Context *ctx, const GameState *game, mode_t m
mode(mode),
refresh(true),
cost(0),
placesMap(NULL),
elevationMap(NULL),
stabilityMap(NULL),
agricultureMap(NULL),
@ -56,6 +57,7 @@ UICityMapsDialog::UICityMapsDialog(Context *ctx, const GameState *game, mode_t m
seedWidget = GetWidgetByIDAndType<TBEditField>(TBIDC("seed"));
nameWidget = GetWidgetByIDAndType<TBEditField>(TBIDC("name"));
mapsWidget = GetWidgetByIDAndType<TBTabContainer>(TBIDC("maps"));
placesMapWidget = GetWidgetByIDAndType<UITBAnimatedImageWidget>(TBIDC("places-map"));
elevationMapWidget = GetWidgetByIDAndType<UITBAnimatedImageWidget>(TBIDC("elevation-map"));
stabilityMapWidget = GetWidgetByIDAndType<UITBAnimatedImageWidget>(TBIDC("stability-map"));
agricultureMapWidget = GetWidgetByIDAndType<UITBAnimatedImageWidget>(TBIDC("agriculture-map"));
@ -116,8 +118,22 @@ UICityMapsDialog::UICityMapsDialog(Context *ctx, const GameState *game, mode_t m
if (!game->debug)
{
TBButton *button = TBSafeCast<TBButton>(mapsWidget->GetTabLayout()->GetLastChild());
if (button)
TBWidget *widget = TBSafeCast<TBWidget>(mapsWidget->GetContentRoot()->GetLastChild());
if (button && widget)
{
mapsWidget->GetTabLayout()->RemoveChild(button);
mapsWidget->GetContentRoot()->RemoveChild(widget);
}
}
if (mode == mode_found)
{
TBButton *button = TBSafeCast<TBButton>(mapsWidget->GetTabLayout()->GetFirstChild());
TBWidget *widget = TBSafeCast<TBWidget>(mapsWidget->GetContentRoot()->GetFirstChild());
if (button && widget)
{
mapsWidget->GetTabLayout()->RemoveChild(button);
mapsWidget->GetContentRoot()->RemoveChild(widget);
}
}
offsetsContainer->SetValue(game->debug);
@ -143,6 +159,7 @@ UICityMapsDialog::UICityMapsDialog(Context *ctx, const GameState *game, mode_t m
UICityMapsDialog::~UICityMapsDialog()
{
delete[] placesMap;
delete[] elevationMap;
delete[] stabilityMap;
delete[] agricultureMap;
@ -310,27 +327,14 @@ void UICityMapsDialog::UpdateManualParameters()
void UICityMapsDialog::UpdateValue()
{
const int selected = mapsWidget->GetValue();
const int selected_offset = (mode == mode_found ? 1 : 0); // in found mode, the first page (areas) is removed
const int selected = mapsWidget->GetValue() + selected_offset;
const int32_t dx = dxWidget->GetValue();
const int32_t dy = dyWidget->GetValue();
const int zoom = zoomWidget->GetValue();
auto update_map = [zoom, dx, dy](UITBAnimatedImageWidget *widget, uint8_t *rgb, const std::function<uint32_t(uint32_t, uint32_t)> &f)
auto draw_town_square = [zoom](uint8_t *rgb)
{
for (uint32_t y = 0; y < map_size; ++y)
{
uint32_t pidx = (map_size - 1 - y) * map_size;
const uint32_t mapy = dy + (y - map_size / 2) * (1 << zoom);
for (uint32_t x = 0; x < map_size; ++x, ++pidx)
{
const uint32_t mapx = dx + (x - map_size / 2) * (1 << zoom);
const uint32_t value = f(mapx, mapy);
rgb[pidx*4+0] = value >> 16;
rgb[pidx*4+1] = value >> 8;
rgb[pidx*4+2] = value;
rgb[pidx*4+3] = 0xff;
}
}
const uint32_t town_square_half_width = 128 / (1 << zoom);
const uint32_t town_square_half_height = 128 / (1 << zoom);
for (uint32_t y = map_size / 2 - town_square_half_height; y <= map_size / 2 + town_square_half_height; ++y)
@ -351,6 +355,80 @@ void UICityMapsDialog::UpdateValue()
rgb[pidx*4+0] = 0xff;
rgb[pidx*4+1] = 0;
}
};
auto update_map = [zoom, dx, dy, &draw_town_square](UITBAnimatedImageWidget *widget, uint8_t *rgb, const std::function<uint32_t(uint32_t, uint32_t)> &f)
{
for (uint32_t y = 0; y < map_size; ++y)
{
uint32_t pidx = (map_size - 1 - y) * map_size;
const uint32_t mapy = dy + (y - map_size / 2) * (1 << zoom);
for (uint32_t x = 0; x < map_size; ++x, ++pidx)
{
const uint32_t mapx = dx + (x - map_size / 2) * (1 << zoom);
const uint32_t value = f(mapx, mapy);
rgb[pidx*4+0] = value >> 16;
rgb[pidx*4+1] = value >> 8;
rgb[pidx*4+2] = value;
rgb[pidx*4+3] = 0xff;
}
}
draw_town_square(rgb);
std::shared_ptr<tb::TBBitmap> bitmap(UTBRendererBatcher::Singleton().CreateBitmap(map_size, map_size, (uint32_t*)rgb));
widget->SetAnimation(std::vector<std::pair<std::shared_ptr<tb::TBBitmap>, unsigned int>>(1, std::make_pair(std::move(bitmap), 1)));
};
auto update_places_map = [this, zoom, dx, dy, &draw_town_square](UITBAnimatedImageWidget *widget, uint8_t *rgb, const std::vector<std::shared_ptr<cc::place_t>> &places)
{
for (uint32_t pidx = 0; pidx < map_size * map_size; ++pidx)
{
rgb[pidx*4+0] = 0;
rgb[pidx*4+1] = 0;
rgb[pidx*4+2] = 0;
rgb[pidx*4+3] = 0xff;
}
for (const std::shared_ptr<cc::place_t> &place: places)
{
if (place->name.empty())
continue;
const int32_t px0 = (int32_t)((place->x0 - ox + dx)) / (1 << zoom) + map_size / 2;
const int32_t py0 = (int32_t)((place->y0 - oy + dy)) / (1 << zoom) + map_size / 2;
const int32_t px1 = (int32_t)((place->x1 - ox + dx)) / (1 << zoom) + map_size / 2;
const int32_t py1 = (int32_t)((place->y1 - oy + dy)) / (1 << zoom) + map_size / 2;
if (px0 < 0) continue;
if (py0 < 0) continue;
if (px1 > (int32_t)map_size) continue;
if (py1 > (int32_t)map_size) continue;
uint32_t c = 0;
auto i = area_colors.find(place->name);
if (i == area_colors.end())
{
const Urho3D::Color color = game_util::GetRandomColor(area_colors.size());
const uint8_t r = (uint8_t)(color.r_ * 255.0f);
const uint8_t g = (uint8_t)(color.g_ * 255.0f);
const uint8_t b = (uint8_t)(color.b_ * 255.0f);
c = ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
i = area_colors.insert(std::make_pair(place->name, c)).first;
color_areas[c] = place->name;
}
else
c = i->second;
const uint8_t r = c >> 16;
const uint8_t g = c >> 8;
const uint8_t b = c;
for (int32_t y = py0; y < py1; ++y)
{
uint32_t pidx = (map_size - 1 - y) * map_size + px0;
for (int32_t x = px0; x < px1; ++x, ++pidx)
{
rgb[pidx*4+0] = r;
rgb[pidx*4+1] = g;
rgb[pidx*4+2] = b;
rgb[pidx*4+3] = 0xff;
}
}
}
draw_town_square(rgb);
std::shared_ptr<tb::TBBitmap> bitmap(UTBRendererBatcher::Singleton().CreateBitmap(map_size, map_size, (uint32_t*)rgb));
widget->SetAnimation(std::vector<std::pair<std::shared_ptr<tb::TBBitmap>, unsigned int>>(1, std::make_pair(std::move(bitmap), 1)));
};
@ -371,6 +449,23 @@ void UICityMapsDialog::UpdateValue()
switch (selected)
{
case 0:
if (!placesMap)
{
placesMap = new uint8_t[4 * map_size * map_size];
std::set<std::shared_ptr<cc::place_t>> places;
game->map.get_all_places(expected_city_id, places);
std::vector<std::shared_ptr<cc::place_t>> sorted_places;
sorted_places.reserve(places.size());
for (const std::shared_ptr<cc::place_t> &place: places)
sorted_places.push_back(place);
std::sort(sorted_places.begin(), sorted_places.end(), [](const std::shared_ptr<cc::place_t> &p0, const std::shared_ptr<cc::place_t> &p1){
return p0->priority < p1->priority;
});
update_places_map(placesMapWidget, placesMap, sorted_places);
}
rgb = placesMap;
break;
case 1:
if (!elevationMap)
{
if (zoom <= 5)
@ -405,39 +500,39 @@ void UICityMapsDialog::UpdateValue()
}
rgb = elevationMap;
break;
case 1:
case 2:
UPDATE_MAP(stability, cc::get_cc_stability);
rgb = stabilityMap;
break;
case 2:
case 3:
UPDATE_MAP(agriculture, cc::get_cc_agricultural_potential);
rgb = agricultureMap;
break;
case 3:
case 4:
UPDATE_MAP(geothermalHeating, cc::get_cc_geothermal_heating);
rgb = geothermalHeatingMap;
break;
case 4:
case 5:
UPDATE_MAP(woodType, cc::get_cc_wood_type_potential);
rgb = woodTypeMap;
break;
case 5:
case 6:
UPDATE_MAP(woodQuantity, cc::get_cc_wood_quantity_potential);
rgb = woodQuantityMap;
break;
case 6:
case 7:
UPDATE_MAP(stoneType, cc::get_cc_stone_type_potential);
rgb = stoneTypeMap;
break;
case 7:
case 8:
UPDATE_MAP(stoneQuantity, cc::get_cc_stone_quantity_potential);
rgb = stoneQuantityMap;
break;
case 8:
case 9:
UPDATE_MAP(gemstone, cc::get_cc_gemstone_potential);
rgb = gemstoneMap;
break;
case 9:
case 10:
{
if (!manualMap)
{
@ -481,10 +576,23 @@ void UICityMapsDialog::UpdateValue()
valueWidget->SetText("");
return;
}
xWidget->SetText(std::to_string((int32_t)(dx + mouse_x - map_size / 2)).c_str());
yWidget->SetText(std::to_string((int32_t)(dy + mouse_y - map_size / 2)).c_str());
const uint8_t value = rgb[4 * (mouse_x + mouse_y * map_size) + 2];
valueWidget->SetText(String((unsigned)value).CString());
if (selected == 0)
{
const uint8_t r = rgb[4 * (mouse_x + mouse_y * map_size) + 0];
const uint8_t g = rgb[4 * (mouse_x + mouse_y * map_size) + 1];
const uint8_t b = rgb[4 * (mouse_x + mouse_y * map_size) + 2];
const uint32_t c = ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
const auto i = color_areas.find(c);
valueWidget->SetText(i == color_areas.end() ? "" : i->second.c_str());
}
else
{
const uint8_t value = rgb[4 * (mouse_x + mouse_y * map_size) + 2];
valueWidget->SetText(String((unsigned)value).CString());
}
}
void UICityMapsDialog::UpdateMaps()
@ -497,6 +605,7 @@ void UICityMapsDialog::UpdateMaps()
state = cc::get_cc_potential_state(id == -1 ? expected_city_id : id, seed);
cc::get_city_origin(id == -1 ? expected_city_id : id, ox, oy);
delete[] placesMap; placesMap = NULL;
delete[] elevationMap; elevationMap = NULL;
delete[] stabilityMap; stabilityMap = NULL;
delete[] agricultureMap; agricultureMap = NULL;
@ -599,6 +708,7 @@ void UICityMapsDialog::HandleTBMessage(StringHash eventType, VariantMap& eventDa
}
else if (ev->type == EVENT_TYPE_POINTER_MOVE)
{
CONNECT("places-map", HandleMouseMotion);
CONNECT("elevation-map", HandleMouseMotion);
CONNECT("stability-map", HandleMouseMotion);
CONNECT("agriculture-map", HandleMouseMotion);

View File

@ -1,5 +1,5 @@
#ifndef UI_CITY_MAPS_H
#define UI_FOUND_CITY_H
#define UI_CITY_MAPS_H
#include <Urho3D/Core/Object.h>
#include <Urho3D/Container/Str.h>
@ -82,6 +82,7 @@ private:
tb::TBEditField *seedWidget;
tb::TBEditField *nameWidget;
tb::TBTabContainer *mapsWidget;
UITBAnimatedImageWidget *placesMapWidget;
UITBAnimatedImageWidget *elevationMapWidget;
UITBAnimatedImageWidget *stabilityMapWidget;
UITBAnimatedImageWidget *agricultureMapWidget;
@ -118,6 +119,7 @@ private:
uint64_t cost;
uint32_t expected_city_id;
uint8_t *placesMap;
uint8_t *elevationMap;
uint8_t *stabilityMap;
uint8_t *agricultureMap;
@ -147,6 +149,9 @@ private:
uint32_t ox;
uint32_t oy;
std::shared_ptr<cc::cc_potential_state_t> state;
std::map<std::string, uint32_t> area_colors;
std::map<uint32_t, std::string> color_areas;
};
#endif