From fe0aa3267c8d85772f8f42dc391a68cd7aadec08 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Mon, 19 Apr 2021 01:11:03 +0200 Subject: [PATCH 01/24] tilespec.cpp: turn drawn_sprites * into vector This allows to get rid of an idiom where functions would append sprites to an array and return the number of new sprites. The hard-coded maximum of 80 sprites per call no longer exists, which enhances code safety. This is a small step towards breaking down tilespec.cpp, see #430. --- client/gui-qt/helpdlg.cpp | 25 ++- client/mapview_common.cpp | 46 +++--- client/mapview_common.h | 2 +- client/tilespec.cpp | 338 ++++++++++++++++---------------------- client/tilespec.h | 26 ++- 5 files changed, 190 insertions(+), 247 deletions(-) diff --git a/client/gui-qt/helpdlg.cpp b/client/gui-qt/helpdlg.cpp index 6a2b7cb9c7..dc7b84b1db 100644 --- a/client/gui-qt/helpdlg.cpp +++ b/client/gui-qt/helpdlg.cpp @@ -228,7 +228,6 @@ void help_dialog::make_tree() struct nation_type *nation; struct terrain *pterrain; struct unit_type *f_type; - struct drawn_sprite sprs[80]; for (const auto *pitem : qAsConst(*help_nodes)) { const char *s; @@ -251,11 +250,11 @@ void help_dialog::make_tree() icon = QIcon(); switch (pitem->type) { - case HELP_EXTRA: + case HELP_EXTRA: { pextra = extra_type_by_translated_name(s); - fill_basic_extra_sprite_array(tileset, sprs, pextra); - spite = sprs->sprite; - break; + auto sprs = fill_basic_extra_sprite_array(tileset, pextra); + spite = sprs.front().sprite; + } break; case HELP_GOVERNMENT: gov = government_by_translated_name(s); @@ -1150,8 +1149,7 @@ QPixmap *terrain_canvas(struct terrain *terrain, enum extra_cause cause) { QPixmap *canvas; - struct drawn_sprite sprs[80]; - int canvas_y, count, i, width, height; + int canvas_y, i, width, height; struct extra_type *pextra; width = tileset_full_tile_width(tileset); @@ -1161,8 +1159,9 @@ QPixmap *terrain_canvas(struct terrain *terrain, canvas = qtg_canvas_create(width, height); canvas->fill(Qt::transparent); for (i = 0; i < 3; ++i) { - count = fill_basic_terrain_layer_sprite_array(tileset, sprs, i, terrain); - put_drawn_sprites(canvas, 0, canvas_y, count, sprs, false); + auto sprites = + fill_basic_terrain_layer_sprite_array(tileset, i, terrain); + put_drawn_sprites(canvas, 0, canvas_y, sprites, false); } pextra = NULL; @@ -1174,13 +1173,13 @@ QPixmap *terrain_canvas(struct terrain *terrain, } extra_type_by_cause_iterate_end; fc_assert_ret_val(pextra, nullptr); - count = fill_basic_extra_sprite_array(tileset, sprs, pextra); - put_drawn_sprites(canvas, 0, canvas_y, count, sprs, false); + auto sprites = fill_basic_extra_sprite_array(tileset, pextra); + put_drawn_sprites(canvas, 0, canvas_y, sprites, false); } if (resource != NULL) { - count = fill_basic_extra_sprite_array(tileset, sprs, resource); - put_drawn_sprites(canvas, 0, canvas_y, count, sprs, false); + auto sprites = fill_basic_extra_sprite_array(tileset, resource); + put_drawn_sprites(canvas, 0, canvas_y, sprites, false); } return canvas; diff --git a/client/mapview_common.cpp b/client/mapview_common.cpp index 2e8e4fb35b..66107701b7 100644 --- a/client/mapview_common.cpp +++ b/client/mapview_common.cpp @@ -933,37 +933,32 @@ bool tile_visible_and_not_on_border_mapcanvas(struct tile *ptile) Draw an array of drawn sprites onto the canvas. */ void put_drawn_sprites(QPixmap *pcanvas, int canvas_x, int canvas_y, - int count, struct drawn_sprite *pdrawn, bool fog, + const std::vector &sprites, bool fog, bool city_dialog, bool city_unit) { - int i; - - for (i = 0; i < count; i++) { - if (!pdrawn[i].sprite) { + for (auto s : sprites) { + if (!s.sprite) { // This can happen, although it should probably be avoided. continue; } - if (city_unit - && (i == LAYER_CATEGORY_TILE || i == LAYER_UNIT - || i == LAYER_FOCUS_UNIT || i == LAYER_CATEGORY_TILE)) { - canvas_put_unit_fogged(pcanvas, canvas_x + pdrawn[i].offset_x, - canvas_y + pdrawn[i].offset_y, pdrawn[i].sprite, - true, canvas_x, canvas_y); + if (city_unit) { + canvas_put_unit_fogged(pcanvas, canvas_x + s.offset_x, + canvas_y + s.offset_y, s.sprite, true, canvas_x, + canvas_y); } else if (city_dialog) { - canvas_put_sprite_citymode(pcanvas, canvas_x + pdrawn[i].offset_x, - canvas_y + pdrawn[i].offset_y, - pdrawn[i].sprite, true, canvas_x, canvas_y); - } else if (fog && pdrawn[i].foggable) { - canvas_put_sprite_fogged(pcanvas, canvas_x + pdrawn[i].offset_x, - canvas_y + pdrawn[i].offset_y, - pdrawn[i].sprite, true, canvas_x, canvas_y); + canvas_put_sprite_citymode(pcanvas, canvas_x + s.offset_x, + canvas_y + s.offset_y, s.sprite, true, + canvas_x, canvas_y); + } else if (fog && s.foggable) { + canvas_put_sprite_fogged(pcanvas, canvas_x + s.offset_x, + canvas_y + s.offset_y, s.sprite, true, + canvas_x, canvas_y); } else { /* We avoid calling canvas_put_sprite_fogged, even though it * should be a valid thing to do, because gui-gtk-2.0 doesn't have * a full implementation. */ - canvas_put_sprite_full(pcanvas, canvas_x + pdrawn[i].offset_x, - canvas_y + pdrawn[i].offset_y, - pdrawn[i].sprite); + canvas_put_sprite_full(pcanvas, canvas_x + s.offset_x, + canvas_y + s.offset_y, s.sprite); } } } @@ -979,12 +974,11 @@ void put_one_element(QPixmap *pcanvas, enum mapview_layer layer, int canvas_x, int canvas_y, const struct unit_type *putype) { - struct drawn_sprite tile_sprs[80]; bool city_mode = false; bool city_unit = false; int dummy_x, dummy_y; - int count = fill_sprite_array(tileset, tile_sprs, layer, ptile, pedge, - pcorner, punit, pcity, putype); + auto sprites = fill_sprite_array(tileset, layer, ptile, pedge, pcorner, + punit, pcity, putype); bool fog = (ptile && gui_options.draw_fog_of_war && TILE_KNOWN_UNSEEN == client_tile_get_known(ptile)); if (ptile) { @@ -1004,8 +998,8 @@ void put_one_element(QPixmap *pcanvas, enum mapview_layer layer, } } /*** Draw terrain and specials ***/ - put_drawn_sprites(pcanvas, canvas_x, canvas_y, count, tile_sprs, fog, - city_mode, city_unit); + put_drawn_sprites(pcanvas, canvas_x, canvas_y, sprites, fog, city_mode, + city_unit); } /** diff --git a/client/mapview_common.h b/client/mapview_common.h index e59902125e..7eaf938cab 100644 --- a/client/mapview_common.h +++ b/client/mapview_common.h @@ -277,7 +277,7 @@ void put_one_element(QPixmap *pcanvas, enum mapview_layer layer, const struct unit_type *putype); void put_drawn_sprites(QPixmap *pcanvas, int canvas_x, int canvas_y, - int count, struct drawn_sprite *pdrawn, bool fog, + const std::vector &sprites, bool fog, bool citydialog = false, bool city_unit = false); void update_map_canvas(int canvas_x, int canvas_y, int width, int height); diff --git a/client/tilespec.cpp b/client/tilespec.cpp index 35ddd3f3f1..48ccebecf8 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -470,14 +470,14 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, bool verbose, int topology_id, float scale); -static int fill_unit_type_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct unit_type *putype, - enum direction8 facing); -static int fill_unit_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct unit *punit, bool stack, - bool backdrop); +static void fill_unit_type_sprite_array(const struct tileset *t, + std::vector &sprs, + const struct unit_type *putype, + enum direction8 facing); +static void fill_unit_sprite_array(const struct tileset *t, + std::vector &sprs, + const struct unit *punit, bool stack, + bool backdrop); static bool load_river_sprites(struct tileset *t, struct river_sprites *store, const char *tag_pfx); @@ -490,12 +490,12 @@ static void tileset_setup_road(struct tileset *t, struct extra_type *pextra, static bool is_extra_drawing_enabled(struct extra_type *pextra); -static int fill_basic_road_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct extra_type *pextra); -static int fill_basic_base_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct extra_type *pextra); +static void fill_basic_road_sprite_array(const struct tileset *t, + std::vector &sprs, + const struct extra_type *pextra); +static void fill_basic_base_sprite_array(const struct tileset *t, + std::vector &sprs, + const struct extra_type *pextra); static void tileset_player_free(struct tileset *t, int plrid); @@ -4166,9 +4166,9 @@ static QPixmap *get_unit_nation_flag_sprite(const struct tileset *t, #define FULL_TILE_Y_OFFSET (t->normal_tile_height - t->full_tile_height) #define ADD_SPRITE(s, draw_fog, x_offset, y_offset) \ - (fc_assert(s != NULL), sprs->sprite = s, \ - sprs->foggable = (draw_fog && t->fogstyle == FOG_AUTO), \ - sprs->offset_x = x_offset, sprs->offset_y = y_offset, sprs++) + (fc_assert(s != NULL), \ + sprs.emplace_back(drawn_sprite{(draw_fog && t->fogstyle == FOG_AUTO), s, \ + x_offset, y_offset})) #define ADD_SPRITE_SIMPLE(s) ADD_SPRITE(s, true, 0, 0) #define ADD_SPRITE_FULL(s) \ ADD_SPRITE(s, true, FULL_TILE_X_OFFSET, FULL_TILE_Y_OFFSET) @@ -4213,29 +4213,25 @@ static void build_tile_data(const struct tile *ptile, /** Fill in the sprite array for the unit type. */ -static int fill_unit_type_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct unit_type *putype, - enum direction8 facing) +static void fill_unit_type_sprite_array(const struct tileset *t, + std::vector &sprs, + const struct unit_type *putype, + enum direction8 facing) { - struct drawn_sprite *save_sprs = sprs; QPixmap *uspr = get_unittype_sprite(t, putype, facing); ADD_SPRITE(uspr, true, FULL_TILE_X_OFFSET + t->unit_offset_x, FULL_TILE_Y_OFFSET + t->unit_offset_y); - - return sprs - save_sprs; } /** Fill in the sprite array for the unit. */ -static int fill_unit_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct unit *punit, bool stack, - bool backdrop) +static void fill_unit_sprite_array(const struct tileset *t, + std::vector &sprs, + const struct unit *punit, bool stack, + bool backdrop) { - struct drawn_sprite *save_sprs = sprs; int ihp; const struct unit_type *ptype = unit_type_get(punit); @@ -4250,7 +4246,7 @@ static int fill_unit_sprite_array(const struct tileset *t, } // Add the sprite for the unit type. - sprs += fill_unit_type_sprite_array(t, sprs, ptype, punit->facing); + fill_unit_type_sprite_array(t, sprs, ptype, punit->facing); if (t->sprites.unit.loaded && unit_transported(punit)) { ADD_SPRITE_FULL(t->sprites.unit.loaded); @@ -4396,25 +4392,22 @@ static int fill_unit_sprite_array(const struct tileset *t, ihp = ((NUM_TILES_HP_BAR - 1) * punit->hp) / ptype->hp; ihp = CLIP(0, ihp, NUM_TILES_HP_BAR - 1); ADD_SPRITE_FULL(t->sprites.unit.hp_bar[ihp]); - - return sprs - save_sprs; } /** Add any corner road sprites to the sprite array. */ -static int fill_road_corner_sprites(const struct tileset *t, - const struct extra_type *pextra, - struct drawn_sprite *sprs, bool road, - bool *road_near, bool hider, - bool *hider_near) +static void fill_road_corner_sprites(const struct tileset *t, + const struct extra_type *pextra, + std::vector &sprs, + bool road, bool *road_near, bool hider, + bool *hider_near) { - struct drawn_sprite *saved_sprs = sprs; int i; int extra_idx = extra_index(pextra); if (is_cardinal_only_road(pextra)) { - return 0; + return; } /* Roads going diagonally adjacent to this tile need to be @@ -4447,21 +4440,19 @@ static int fill_road_corner_sprites(const struct tileset *t, } } } - - return sprs - saved_sprs; } /** Fill all road and rail sprites into the sprite array. */ -static int fill_road_sprite_array(const struct tileset *t, - const struct extra_type *pextra, - struct drawn_sprite *sprs, - bv_extras textras, bv_extras *textras_near, - struct terrain *tterrain_near[8], - const struct city *pcity) +static void fill_road_sprite_array(const struct tileset *t, + const struct extra_type *pextra, + std::vector &sprs, + bv_extras textras, + bv_extras *textras_near, + struct terrain *tterrain_near[8], + const struct city *pcity) { - struct drawn_sprite *saved_sprs = sprs; bool road, road_near[8], hider, hider_near[8]; bool land_near[8], hland_near[8]; bool draw_road[8], draw_single_road; @@ -4575,8 +4566,8 @@ static int fill_road_sprite_array(const struct tileset *t, } // Draw road corners - sprs += fill_road_corner_sprites(t, pextra, sprs, road, road_near, hider, - hider_near); + fill_road_corner_sprites(t, pextra, sprs, road, road_near, hider, + hider_near); if (extrastyle == ESTYLE_ROAD_ALL_SEPARATE) { /* With ESTYLE_ROAD_ALL_SEPARATE, we simply draw one road for every @@ -4659,8 +4650,6 @@ static int fill_road_sprite_array(const struct tileset *t, ADD_SPRITE_SIMPLE(t->sprites.extras[extra_idx].u.road.isolated); } } - - return sprs - saved_sprs; } /** @@ -4692,13 +4681,12 @@ static int get_irrigation_index(const struct tileset *t, /** Fill in the farmland/irrigation sprite for the tile. */ -static int fill_irrigation_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - bv_extras textras, - bv_extras *textras_near, - const struct city *pcity) +static void fill_irrigation_sprite_array(const struct tileset *t, + std::vector &sprs, + bv_extras textras, + bv_extras *textras_near, + const struct city *pcity) { - struct drawn_sprite *saved_sprs = sprs; /* We don't draw the irrigation if there's a city (it just gets overdrawn * anyway, and ends up looking bad). */ @@ -4730,28 +4718,25 @@ static int fill_irrigation_sprite_array(const struct tileset *t, } extra_type_list_iterate_end; } - - return sprs - saved_sprs; } /** Fill in the city overlays for the tile. This includes the citymap overlays on the mapview as well as the tile output sprites. */ -static int fill_city_overlays_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct tile *ptile, - const struct city *citymode) +static void fill_city_overlays_sprite_array(const struct tileset *t, + std::vector &sprs, + const struct tile *ptile, + const struct city *citymode) { const struct city *pcity; const struct city *pwork; struct unit *psettler = nullptr; - struct drawn_sprite *saved_sprs = sprs; int city_x, city_y; const int NUM_CITY_COLORS = t->sprites.city.worked_tile_overlay.size; if (NULL == ptile || TILE_UNKNOWN == client_tile_get_known(ptile)) { - return 0; + return; } pwork = tile_worked(ptile); @@ -4763,7 +4748,7 @@ static int fill_city_overlays_sprite_array(const struct tileset *t, /* Below code does not work if pcity is invisible. * Make sure it is not. */ - fc_assert_ret_val(pcity == NULL || pcity->tile != NULL, 0); + fc_assert_ret(pcity == NULL || pcity->tile != NULL); if (pcity && !pcity->tile) { pcity = NULL; } @@ -4818,21 +4803,18 @@ static int fill_city_overlays_sprite_array(const struct tileset *t, ADD_SPRITE_SIMPLE(t->sprites.city.unworked_tile_overlay.p[idx]); } - - return sprs - saved_sprs; } /** Helper function for fill_terrain_sprite_layer. Fill in the sprite array for blended terrain. */ -static int fill_terrain_sprite_blending(const struct tileset *t, - struct drawn_sprite *sprs, - const struct tile *ptile, - const struct terrain *pterrain, - struct terrain **tterrain_near) +static void fill_terrain_sprite_blending(const struct tileset *t, + std::vector &sprs, + const struct tile *ptile, + const struct terrain *pterrain, + struct terrain **tterrain_near) { - struct drawn_sprite *saved_sprs = sprs; const int W = t->normal_tile_width, H = t->normal_tile_height; const int offsets[4][2] = {{W / 2, 0}, {0, H / 2}, {W / 2, H / 2}, {0, 0}}; int dir = 0; @@ -4857,20 +4839,17 @@ static int fill_terrain_sprite_blending(const struct tileset *t, ADD_SPRITE(t->sprites.drawing[terrain_index(other)]->blend[dir], true, offsets[dir][0], offsets[dir][1]); } - - return sprs - saved_sprs; } /** Add sprites for fog (and some forms of darkness). */ -static int fill_fog_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct tile *ptile, - const struct tile_edge *pedge, - const struct tile_corner *pcorner) +static void fill_fog_sprite_array(const struct tileset *t, + std::vector &sprs, + const struct tile *ptile, + const struct tile_edge *pedge, + const struct tile_corner *pcorner) { - struct drawn_sprite *saved_sprs = sprs; if (t->fogstyle == FOG_SPRITE && gui_options.draw_fog_of_war && NULL != ptile @@ -4911,19 +4890,16 @@ static int fill_fog_sprite_array(const struct tileset *t, ADD_SPRITE_SIMPLE(t->sprites.tx.fullfog[tileno]); } } - - return sprs - saved_sprs; } /** Helper function for fill_terrain_sprite_layer. */ -static int fill_terrain_sprite_array( - struct tileset *t, struct drawn_sprite *sprs, int l, // layer_num +static void fill_terrain_sprite_array( + struct tileset *t, std::vector &sprs, int l, // layer_num const struct tile *ptile, const struct terrain *pterrain, struct terrain **tterrain_near, struct drawing_data *draw) { - struct drawn_sprite *saved_sprs = sprs; struct drawing_layer *dlp = &draw->layer[l]; int tthis = dlp->match_index[0]; int that = dlp->match_index[1]; @@ -5055,20 +5031,17 @@ static int fill_terrain_sprite_array( } }; #undef MATCH - - return sprs - saved_sprs; } /** Helper function for fill_terrain_sprite_layer. Fill in the sprite array of darkness. */ -static int fill_terrain_sprite_darkness(struct tileset *t, - struct drawn_sprite *sprs, - const struct tile *ptile, - struct terrain **tterrain_near) +static void fill_terrain_sprite_darkness(struct tileset *t, + std::vector &sprs, + const struct tile *ptile, + struct terrain **tterrain_near) { - struct drawn_sprite *saved_sprs = sprs; int i, tileno; struct tile *adjc_tile; @@ -5119,22 +5092,20 @@ static int fill_terrain_sprite_darkness(struct tileset *t, break; }; #undef UNKNOWN - - return sprs - saved_sprs; } /** Add sprites for the base tile to the sprite list. This doesn't include specials or rivers. */ -static int fill_terrain_sprite_layer(struct tileset *t, - struct drawn_sprite *sprs, - int layer_num, const struct tile *ptile, - const struct terrain *pterrain, - struct terrain **tterrain_near) +static void fill_terrain_sprite_layer(struct tileset *t, + std::vector &sprs, + int layer_num, + const struct tile *ptile, + const struct terrain *pterrain, + struct terrain **tterrain_near) { QPixmap *sprite; - struct drawn_sprite *saved_sprs = sprs; struct drawing_data *draw = t->sprites.drawing[terrain_index(pterrain)]; const int l = (draw->is_reversed ? (draw->num_layers - layer_num - 1) : layer_num); @@ -5148,23 +5119,18 @@ static int fill_terrain_sprite_layer(struct tileset *t, && (sprite = load_sprite(t, ptile->spec_sprite, true, false))) { if (l == 0) { ADD_SPRITE_SIMPLE(sprite); - return 1; - } else { - return 0; } + return; } if (l < draw->num_layers) { - sprs += fill_terrain_sprite_array(t, sprs, l, ptile, pterrain, - tterrain_near, draw); + fill_terrain_sprite_array(t, sprs, l, ptile, pterrain, tterrain_near, + draw); if ((l + 1) == draw->blending) { - sprs += fill_terrain_sprite_blending(t, sprs, ptile, pterrain, - tterrain_near); + fill_terrain_sprite_blending(t, sprs, ptile, pterrain, tterrain_near); } } - - return sprs - saved_sprs; } /** @@ -5190,14 +5156,12 @@ bool unit_drawn_with_city_outline(const struct unit *punit, bool check_focus) /** Fill in the grid sprites for the given tile, city, and unit. */ -static int fill_grid_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct tile *ptile, - const struct tile_edge *pedge, - const struct city *citymode) +static void fill_grid_sprite_array(const struct tileset *t, + std::vector &sprs, + const struct tile *ptile, + const struct tile_edge *pedge, + const struct city *citymode) { - struct drawn_sprite *saved_sprs = sprs; - if (pedge) { bool known[NUM_EDGE_TILES], city[NUM_EDGE_TILES]; bool unit[NUM_EDGE_TILES], worked[NUM_EDGE_TILES]; @@ -5330,20 +5294,17 @@ static int fill_grid_sprite_array(const struct tileset *t, } } } - - return sprs - saved_sprs; } /** Fill in the given sprite array with any needed goto sprites. */ -static int fill_goto_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct tile *ptile, - const struct tile_edge *pedge, - const struct tile_corner *pcorner) +static void fill_goto_sprite_array(const struct tileset *t, + std::vector &sprs, + const struct tile *ptile, + const struct tile_edge *pedge, + const struct tile_corner *pcorner) { - struct drawn_sprite *saved_sprs = sprs; QPixmap *sprite; bool warn = false; enum goto_tile_state state; @@ -5397,8 +5358,6 @@ static int fill_goto_sprite_array(const struct tileset *t, } } } - - return sprs - saved_sprs; } /** @@ -5469,19 +5428,18 @@ static bool is_extra_drawing_enabled(struct extra_type *pextra) pcity, if specified, gives the city. For tile drawing this should generally be tile_city(ptile); otherwise it can be any city. */ -int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, - enum mapview_layer layer, const struct tile *ptile, - const struct tile_edge *pedge, - const struct tile_corner *pcorner, - const struct unit *punit, const struct city *pcity, - const struct unit_type *putype) +std::vector +fill_sprite_array(struct tileset *t, enum mapview_layer layer, + const struct tile *ptile, const struct tile_edge *pedge, + const struct tile_corner *pcorner, + const struct unit *punit, const struct city *pcity, + const struct unit_type *putype) { int tileno, dir; bv_extras textras_near[8]; bv_extras textras; struct terrain *tterrain_near[8]; struct terrain *pterrain = NULL; - struct drawn_sprite *save_sprs = sprs; struct player *owner = NULL; /* Unit drawing is disabled when the view options are turned off, * but only where we're drawing on the mapview. */ @@ -5513,6 +5471,9 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, BV_CLR_ALL(textras); } + auto sprs = std::vector(); + sprs.reserve(80); + switch (layer) { case LAYER_BACKGROUND: // Set up background color. @@ -5532,29 +5493,26 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, case LAYER_TERRAIN1: if (NULL != pterrain && gui_options.draw_terrain && !solid_bg) { - sprs += fill_terrain_sprite_layer(t, sprs, 0, ptile, pterrain, - tterrain_near); + fill_terrain_sprite_layer(t, sprs, 0, ptile, pterrain, tterrain_near); } break; case LAYER_DARKNESS: if (NULL != pterrain && gui_options.draw_terrain && !solid_bg) { - sprs += fill_terrain_sprite_darkness(t, sprs, ptile, tterrain_near); + fill_terrain_sprite_darkness(t, sprs, ptile, tterrain_near); } break; case LAYER_TERRAIN2: if (NULL != pterrain && gui_options.draw_terrain && !solid_bg) { - sprs += fill_terrain_sprite_layer(t, sprs, 1, ptile, pterrain, - tterrain_near); + fill_terrain_sprite_layer(t, sprs, 1, ptile, pterrain, tterrain_near); } break; case LAYER_TERRAIN3: if (NULL != pterrain && gui_options.draw_terrain && !solid_bg) { fc_assert(MAX_NUM_LAYERS == 3); - sprs += fill_terrain_sprite_layer(t, sprs, 2, ptile, pterrain, - tterrain_near); + fill_terrain_sprite_layer(t, sprs, 2, ptile, pterrain, tterrain_near); } break; @@ -5578,8 +5536,7 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, } } - sprs += fill_irrigation_sprite_array(t, sprs, textras, textras_near, - pcity); + fill_irrigation_sprite_array(t, sprs, textras, textras_near, pcity); if (gui_options.draw_terrain && !solid_bg) { extra_type_list_iterate(t->style_lists[ESTYLE_RIVER], priver) @@ -5615,8 +5572,8 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, pextra) { if (is_extra_drawing_enabled(pextra)) { - sprs += fill_road_sprite_array(t, pextra, sprs, textras, - textras_near, tterrain_near, pcity); + fill_road_sprite_array(t, pextra, sprs, textras, textras_near, + tterrain_near, pcity); } } extra_type_list_iterate_end; @@ -5624,8 +5581,8 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, pextra) { if (is_extra_drawing_enabled(pextra)) { - sprs += fill_road_sprite_array(t, pextra, sprs, textras, - textras_near, tterrain_near, pcity); + fill_road_sprite_array(t, pextra, sprs, textras, textras_near, + tterrain_near, pcity); } } extra_type_list_iterate_end; @@ -5633,8 +5590,8 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, pextra) { if (is_extra_drawing_enabled(pextra)) { - sprs += fill_road_sprite_array(t, pextra, sprs, textras, - textras_near, tterrain_near, pcity); + fill_road_sprite_array(t, pextra, sprs, textras, textras_near, + tterrain_near, pcity); } } extra_type_list_iterate_end; @@ -5696,7 +5653,7 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, case LAYER_GRID1: if (t->type == TS_ISOMETRIC) { - sprs += fill_grid_sprite_array(t, sprs, ptile, pedge, citymode); + fill_grid_sprite_array(t, sprs, ptile, pedge, citymode); } break; @@ -5832,11 +5789,10 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, t->select_offset_x, t->select_offset_y); } - sprs += fill_unit_sprite_array(t, sprs, punit, stacked, backdrop); + fill_unit_sprite_array(t, sprs, punit, stacked, backdrop); } else if (putype != NULL && layer == LAYER_UNIT) { // Only the sprite for the unit type. - sprs += - fill_unit_type_sprite_array(t, sprs, putype, direction8_invalid()); + fill_unit_type_sprite_array(t, sprs, putype, direction8_invalid()); } break; @@ -5907,7 +5863,7 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, break; case LAYER_FOG: - sprs += fill_fog_sprite_array(t, sprs, ptile, pedge, pcorner); + fill_fog_sprite_array(t, sprs, ptile, pedge, pcorner); break; case LAYER_CITY2: @@ -5956,12 +5912,12 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, case LAYER_GRID2: if (t->type == TS_OVERHEAD) { - sprs += fill_grid_sprite_array(t, sprs, ptile, pedge, citymode); + fill_grid_sprite_array(t, sprs, ptile, pedge, citymode); } break; case LAYER_OVERLAYS: - sprs += fill_city_overlays_sprite_array(t, sprs, ptile, citymode); + fill_city_overlays_sprite_array(t, sprs, ptile, citymode); if (mapdeco_is_crosshair_set(ptile)) { ADD_SPRITE_SIMPLE(t->sprites.user.attention); } @@ -5974,7 +5930,7 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, case LAYER_GOTO: if (ptile && goto_is_active()) { - sprs += fill_goto_sprite_array(t, sprs, ptile, pedge, pcorner); + fill_goto_sprite_array(t, sprs, ptile, pedge, pcorner); } break; @@ -6072,7 +6028,7 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, break; } - return sprs - save_sprs; + return sprs; } /** @@ -6652,12 +6608,11 @@ void tileset_init(struct tileset *t) 1, 2. Using other values for 'layer' here will result in undefined behaviour. ;) */ -int fill_basic_terrain_layer_sprite_array(struct tileset *t, - struct drawn_sprite *sprs, - int layer, - struct terrain *pterrain) +std::vector +fill_basic_terrain_layer_sprite_array(struct tileset *t, int layer, + struct terrain *pterrain) { - struct drawn_sprite *save_sprs = sprs; + auto sprs = std::vector(); struct drawing_data *draw = t->sprites.drawing[terrain_index(pterrain)]; struct terrain *tterrain_near[8]; @@ -6675,21 +6630,22 @@ int fill_basic_terrain_layer_sprite_array(struct tileset *t, } i = draw->is_reversed ? draw->num_layers - layer - 1 : layer; - sprs += fill_terrain_sprite_array(t, sprs, i, &dummy_tile, pterrain, - tterrain_near, draw); + fill_terrain_sprite_array(t, sprs, i, &dummy_tile, pterrain, tterrain_near, + draw); - return sprs - save_sprs; + return sprs; } /** Return a representative sprite for the given extra type. */ -int fill_basic_extra_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct extra_type *pextra) +std::vector +fill_basic_extra_sprite_array(const struct tileset *t, + const struct extra_type *pextra) { + auto sprs = std::vector(); + int idx = extra_index(pextra); - struct drawn_sprite *saved_sprs = sprs; switch (t->sprites.extras[idx].extrastyle) { case ESTYLE_SINGLE1: @@ -6703,15 +6659,17 @@ int fill_basic_extra_sprite_array(const struct tileset *t, case ESTYLE_ROAD_PARITY_COMBINED: case ESTYLE_ROAD_ALL_COMBINED: case ESTYLE_RIVER: - return fill_basic_road_sprite_array(t, sprs, pextra); + fill_basic_road_sprite_array(t, sprs, pextra); + break; case ESTYLE_3LAYER: - return fill_basic_base_sprite_array(t, sprs, pextra); + fill_basic_base_sprite_array(t, sprs, pextra); + break; case ESTYLE_COUNT: fc_assert(t->sprites.extras[idx].extrastyle != ESTYLE_COUNT); break; } - return sprs - saved_sprs; + return sprs; } /** @@ -6719,23 +6677,22 @@ int fill_basic_extra_sprite_array(const struct tileset *t, image of the given road type. The image is suitable for use as an icon for the road type, for example. */ -int fill_basic_road_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct extra_type *pextra) +void fill_basic_road_sprite_array(const struct tileset *t, + std::vector &sprs, + const struct extra_type *pextra) { - struct drawn_sprite *saved_sprs = sprs; int idx; int i; int extrastyle; - if (!t || !sprs || !pextra) { - return 0; + if (!t || !pextra) { + return; } idx = extra_index(pextra); if (!(0 <= idx && idx < game.control.num_extra_types)) { - return 0; + return; } extrastyle = t->sprites.extras[idx].extrastyle; @@ -6759,8 +6716,6 @@ int fill_basic_road_sprite_array(const struct tileset *t, } } } - - return sprs - saved_sprs; } /** @@ -6768,21 +6723,20 @@ int fill_basic_road_sprite_array(const struct tileset *t, image of the given base type. The image is suitable for use as an icon for the base type, for example. */ -int fill_basic_base_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct extra_type *pextra) +void fill_basic_base_sprite_array(const struct tileset *t, + std::vector &sprs, + const struct extra_type *pextra) { - struct drawn_sprite *saved_sprs = sprs; int idx; - if (!t || !sprs || !pextra) { - return 0; + if (!t || !pextra) { + return; } idx = extra_index(pextra); if (!(0 <= idx && idx < game.control.num_extra_types)) { - return 0; + return; } #define ADD_SPRITE_IF_NOT_NULL(x) \ @@ -6798,8 +6752,6 @@ int fill_basic_base_sprite_array(const struct tileset *t, ADD_SPRITE_IF_NOT_NULL(t->sprites.extras[idx].u.bmf.foreground); #undef ADD_SPRITE_IF_NOT_NULL - - return sprs - saved_sprs; } /** diff --git a/client/tilespec.h b/client/tilespec.h index feed3d2afa..3fc68c0d69 100644 --- a/client/tilespec.h +++ b/client/tilespec.h @@ -236,17 +236,15 @@ bool tileset_layer_in_category(enum mapview_layer layer, enum layer_category cat); // Gfx support - -int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, - enum mapview_layer layer, const struct tile *ptile, - const struct tile_edge *pedge, - const struct tile_corner *pcorner, - const struct unit *punit, const struct city *pcity, - const struct unit_type *putype); -int fill_basic_terrain_layer_sprite_array(struct tileset *t, - struct drawn_sprite *sprs, - int layer, - struct terrain *pterrain); +std::vector +fill_sprite_array(struct tileset *t, enum mapview_layer layer, + const struct tile *ptile, const struct tile_edge *pedge, + const struct tile_corner *pcorner, + const struct unit *punit, const struct city *pcity, + const struct unit_type *putype); +std::vector +fill_basic_terrain_layer_sprite_array(struct tileset *t, int layer, + struct terrain *pterrain); int get_focus_unit_toggle_timeout(const struct tileset *t); void reset_focus_unit_state(struct tileset *t); @@ -346,9 +344,9 @@ QPixmap *get_unit_upkeep_sprite(const struct tileset *t, const struct unit *punit, const int *upkeep_cost); QPixmap *get_basic_fog_sprite(const struct tileset *t); -int fill_basic_extra_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct extra_type *pextra); +std::vector +fill_basic_extra_sprite_array(const struct tileset *t, + const struct extra_type *pextra); QPixmap *get_event_sprite(const struct tileset *t, enum event_type event); QPixmap *tiles_lookup_sprite_tag_alt(struct tileset *t, QtMsgType level, From af8183e564b125cedf3250c8426e8748793b88c1 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sat, 24 Apr 2021 21:31:19 +0200 Subject: [PATCH 02/24] Move mapview layer stuff out of tilespec.h The map drawing code will be modularized and layers can ~easily be abstracted away. See #430. --- client/layer.h | 91 +++++++++++++++++++++++++++++++++++++++++++++++ client/tilespec.h | 79 +--------------------------------------- 2 files changed, 92 insertions(+), 78 deletions(-) create mode 100644 client/layer.h diff --git a/client/layer.h b/client/layer.h new file mode 100644 index 0000000000..1df3d17939 --- /dev/null +++ b/client/layer.h @@ -0,0 +1,91 @@ +/*__ ___ *************************************** +/ \ / \ Copyright (c) 2021 Freeciv21 contributors. +\_ \ / __/ This file is part of Freeciv21. + _\ \ / /__ Freeciv21 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 3 of the License, + | or (at your option) any later version. + _/ /\ You should have received a copy of the GNU + /o) (o/\ \_ General Public License along with Freeciv21. + \_____/ / If not, see https://www.gnu.org/licenses/. + \____/ ********************************************************/ +#pragma once + +/* Items on the mapview are drawn in layers. Each entry below represents + * one layer. The names are basically arbitrary and just correspond to + * groups of elements in fill_sprite_array(). Callers of fill_sprite_array + * must call it once for each layer. */ +#define SPECENUM_NAME mapview_layer +#define SPECENUM_VALUE0 LAYER_BACKGROUND +#define SPECENUM_VALUE0NAME "Background" +// Adjust also TERRAIN_LAYER_COUNT if changing these +#define SPECENUM_VALUE1 LAYER_TERRAIN1 +#define SPECENUM_VALUE1NAME "Terrain1" +#define SPECENUM_VALUE2 LAYER_DARKNESS +#define SPECENUM_VALUE2NAME "Darkness" +#define SPECENUM_VALUE3 LAYER_TERRAIN2 +#define SPECENUM_VALUE3NAME "Terrain2" +#define SPECENUM_VALUE4 LAYER_TERRAIN3 +#define SPECENUM_VALUE4NAME "Terrain3" +#define SPECENUM_VALUE5 LAYER_WATER +#define SPECENUM_VALUE5NAME "Water" +#define SPECENUM_VALUE6 LAYER_ROADS +#define SPECENUM_VALUE6NAME "Roads" +#define SPECENUM_VALUE7 LAYER_SPECIAL1 +#define SPECENUM_VALUE7NAME "Special1" +#define SPECENUM_VALUE8 LAYER_GRID1 +#define SPECENUM_VALUE8NAME "Grid1" +#define SPECENUM_VALUE9 LAYER_CITY1 +#define SPECENUM_VALUE9NAME "City1" +#define SPECENUM_VALUE10 LAYER_SPECIAL2 +#define SPECENUM_VALUE10NAME "Special2" +#define SPECENUM_VALUE11 LAYER_FOG +#define SPECENUM_VALUE11NAME "Fog" +#define SPECENUM_VALUE12 LAYER_UNIT +#define SPECENUM_VALUE12NAME "Unit" +#define SPECENUM_VALUE13 LAYER_SPECIAL3 +#define SPECENUM_VALUE13NAME "Special3" +#define SPECENUM_VALUE14 LAYER_CITY2 +#define SPECENUM_VALUE14NAME "City2" +#define SPECENUM_VALUE15 LAYER_GRID2 +#define SPECENUM_VALUE15NAME "Grid2" +#define SPECENUM_VALUE16 LAYER_OVERLAYS +#define SPECENUM_VALUE16NAME "Overlays" +#define SPECENUM_VALUE17 LAYER_TILELABEL +#define SPECENUM_VALUE17NAME "TileLabel" +#define SPECENUM_VALUE18 LAYER_CITYBAR +#define SPECENUM_VALUE18NAME "CityBar" +#define SPECENUM_VALUE19 LAYER_FOCUS_UNIT +#define SPECENUM_VALUE19NAME "FocusUnit" +#define SPECENUM_VALUE20 LAYER_GOTO +#define SPECENUM_VALUE20NAME "Goto" +#define SPECENUM_VALUE21 LAYER_WORKERTASK +#define SPECENUM_VALUE21NAME "WorkerTask" +#define SPECENUM_VALUE22 LAYER_EDITOR +#define SPECENUM_VALUE22NAME "Editor" +#define SPECENUM_VALUE23 LAYER_INFRAWORK +#define SPECENUM_VALUE23NAME "InfraWork" +#define SPECENUM_COUNT LAYER_COUNT +#include "specenum_gen.h" + +#define TERRAIN_LAYER_COUNT 3 + +#define mapview_layer_iterate(layer) \ + { \ + enum mapview_layer layer; \ + int layer_index; \ + \ + for (layer_index = 0; layer_index < LAYER_COUNT; layer_index++) { \ + layer = tileset_get_layer(tileset, layer_index); + +#define mapview_layer_iterate_end \ + } \ + } + +// Layer categories can be used to only render part of a tile. +enum layer_category { + LAYER_CATEGORY_CITY, // Render cities + LAYER_CATEGORY_TILE, // Render terrain only + LAYER_CATEGORY_UNIT // Render units only +}; diff --git a/client/tilespec.h b/client/tilespec.h index 3fc68c0d69..ba906ac531 100644 --- a/client/tilespec.h +++ b/client/tilespec.h @@ -17,6 +17,7 @@ ***********************************************************************/ #pragma once +#include "layer.h" #include "options.h" class QPixmap; // opaque; gui-dep @@ -102,84 +103,6 @@ struct drawn_sprite { int offset_x, offset_y; // offset from tile origin }; -/* Items on the mapview are drawn in layers. Each entry below represents - * one layer. The names are basically arbitrary and just correspond to - * groups of elements in fill_sprite_array(). Callers of fill_sprite_array - * must call it once for each layer. */ -#define SPECENUM_NAME mapview_layer -#define SPECENUM_VALUE0 LAYER_BACKGROUND -#define SPECENUM_VALUE0NAME "Background" -// Adjust also TERRAIN_LAYER_COUNT if changing these -#define SPECENUM_VALUE1 LAYER_TERRAIN1 -#define SPECENUM_VALUE1NAME "Terrain1" -#define SPECENUM_VALUE2 LAYER_DARKNESS -#define SPECENUM_VALUE2NAME "Darkness" -#define SPECENUM_VALUE3 LAYER_TERRAIN2 -#define SPECENUM_VALUE3NAME "Terrain2" -#define SPECENUM_VALUE4 LAYER_TERRAIN3 -#define SPECENUM_VALUE4NAME "Terrain3" -#define SPECENUM_VALUE5 LAYER_WATER -#define SPECENUM_VALUE5NAME "Water" -#define SPECENUM_VALUE6 LAYER_ROADS -#define SPECENUM_VALUE6NAME "Roads" -#define SPECENUM_VALUE7 LAYER_SPECIAL1 -#define SPECENUM_VALUE7NAME "Special1" -#define SPECENUM_VALUE8 LAYER_GRID1 -#define SPECENUM_VALUE8NAME "Grid1" -#define SPECENUM_VALUE9 LAYER_CITY1 -#define SPECENUM_VALUE9NAME "City1" -#define SPECENUM_VALUE10 LAYER_SPECIAL2 -#define SPECENUM_VALUE10NAME "Special2" -#define SPECENUM_VALUE11 LAYER_FOG -#define SPECENUM_VALUE11NAME "Fog" -#define SPECENUM_VALUE12 LAYER_UNIT -#define SPECENUM_VALUE12NAME "Unit" -#define SPECENUM_VALUE13 LAYER_SPECIAL3 -#define SPECENUM_VALUE13NAME "Special3" -#define SPECENUM_VALUE14 LAYER_CITY2 -#define SPECENUM_VALUE14NAME "City2" -#define SPECENUM_VALUE15 LAYER_GRID2 -#define SPECENUM_VALUE15NAME "Grid2" -#define SPECENUM_VALUE16 LAYER_OVERLAYS -#define SPECENUM_VALUE16NAME "Overlays" -#define SPECENUM_VALUE17 LAYER_TILELABEL -#define SPECENUM_VALUE17NAME "TileLabel" -#define SPECENUM_VALUE18 LAYER_CITYBAR -#define SPECENUM_VALUE18NAME "CityBar" -#define SPECENUM_VALUE19 LAYER_FOCUS_UNIT -#define SPECENUM_VALUE19NAME "FocusUnit" -#define SPECENUM_VALUE20 LAYER_GOTO -#define SPECENUM_VALUE20NAME "Goto" -#define SPECENUM_VALUE21 LAYER_WORKERTASK -#define SPECENUM_VALUE21NAME "WorkerTask" -#define SPECENUM_VALUE22 LAYER_EDITOR -#define SPECENUM_VALUE22NAME "Editor" -#define SPECENUM_VALUE23 LAYER_INFRAWORK -#define SPECENUM_VALUE23NAME "InfraWork" -#define SPECENUM_COUNT LAYER_COUNT -#include "specenum_gen.h" - -#define TERRAIN_LAYER_COUNT 3 - -#define mapview_layer_iterate(layer) \ - { \ - enum mapview_layer layer; \ - int layer_index; \ - \ - for (layer_index = 0; layer_index < LAYER_COUNT; layer_index++) { \ - layer = tileset_get_layer(tileset, layer_index); - -#define mapview_layer_iterate_end \ - } \ - } - -// Layer categories can be used to only render part of a tile. -enum layer_category { - LAYER_CATEGORY_CITY, // Render cities - LAYER_CATEGORY_TILE, // Render terrain only - LAYER_CATEGORY_UNIT // Render units only -}; - #define NUM_TILES_PROGRESS 8 #define MAX_NUM_CITIZEN_SPRITES 6 From 11380d19eb35459dbdd854fa030e313d386f0983 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 25 Apr 2021 00:51:10 +0200 Subject: [PATCH 03/24] Add a class for map layers This will allow removing the big switch() in fill_sprite_array(). See #430. --- client/CMakeLists.txt | 1 + client/layer.cpp | 29 +++++++++++++++++++++ client/layer.h | 59 +++++++++++++++++++++++++++++++++++++++++++ client/tilespec.h | 30 ---------------------- 4 files changed, 89 insertions(+), 30 deletions(-) create mode 100644 client/layer.cpp diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 6fb97988af..956898f201 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -55,6 +55,7 @@ add_library( gui_interface.cpp goto.cpp helpdata.cpp + layer.cpp luaconsole_common.cpp mapctrl_common.cpp mapview_common.cpp diff --git a/client/layer.cpp b/client/layer.cpp new file mode 100644 index 0000000000..ab530f0279 --- /dev/null +++ b/client/layer.cpp @@ -0,0 +1,29 @@ +/*__ ___ *************************************** +/ \ / \ Copyright (c) 2021 Freeciv21 contributors. +\_ \ / __/ This file is part of Freeciv21. + _\ \ / /__ Freeciv21 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 3 of the License, + | or (at your option) any later version. + _/ /\ You should have received a copy of the GNU + /o) (o/\ \_ General Public License along with Freeciv21. + \_____/ / If not, see https://www.gnu.org/licenses/. + \____/ ********************************************************/ + +#include "tilespec.h" + +#include "layer.h" + +namespace freeciv { + +std::vector +layer::fill_sprite_array(const tile *ptile, const tile_edge *pedge, + const tile_corner *pcorner, const unit *punit, + const city *pcity, const unit_type *putype) const +{ + return ::fill_sprite_array(m_ts, m_layer, ptile, pedge, pcorner, punit, + pcity, putype); +} + +} // namespace freeciv diff --git a/client/layer.h b/client/layer.h index 1df3d17939..3033c853a3 100644 --- a/client/layer.h +++ b/client/layer.h @@ -12,6 +12,42 @@ \____/ ********************************************************/ #pragma once +// Forward declarations +class QPixmap; + +struct city; +struct unit; +struct unit_type; + +/* An edge is the border between two tiles. This structure represents one + * edge. The tiles are given in the same order as the enumeration name. */ +enum edge_type { + EDGE_NS, // North and south + EDGE_WE, // West and east + EDGE_UD, /* Up and down (nw/se), for hex_width tilesets */ + EDGE_LR, /* Left and right (ne/sw), for hex_height tilesets */ + EDGE_COUNT +}; + +struct tile_edge { + edge_type type; +#define NUM_EDGE_TILES 2 + const struct tile *tile[NUM_EDGE_TILES]; +}; + +/* A corner is the endpoint of several edges. At each corner 4 tiles will + * meet (3 in hex view). Tiles are in clockwise order NESW. */ +struct tile_corner { +#define NUM_CORNER_TILES 4 + const struct tile *tile[NUM_CORNER_TILES]; +}; + +struct drawn_sprite { + bool foggable; // Set to FALSE for sprites that are never fogged. + QPixmap *sprite; + int offset_x, offset_y; // offset from tile origin +}; + /* Items on the mapview are drawn in layers. Each entry below represents * one layer. The names are basically arbitrary and just correspond to * groups of elements in fill_sprite_array(). Callers of fill_sprite_array @@ -89,3 +125,26 @@ enum layer_category { LAYER_CATEGORY_TILE, // Render terrain only LAYER_CATEGORY_UNIT // Render units only }; + +struct tileset; + +namespace freeciv { + +/** + * A layer when drawing the map. + */ +class layer { +public: + layer(tileset *ts, mapview_layer layer) : m_ts(ts), m_layer(layer) {} + + virtual std::vector + fill_sprite_array(const tile *ptile, const tile_edge *pedge, + const tile_corner *pcorner, const unit *punit, + const city *pcity, const unit_type *putype) const; + +private: + tileset *m_ts; + mapview_layer m_layer; +}; + +} // namespace freeciv diff --git a/client/tilespec.h b/client/tilespec.h index ba906ac531..0080d3b41a 100644 --- a/client/tilespec.h +++ b/client/tilespec.h @@ -20,8 +20,6 @@ #include "layer.h" #include "options.h" -class QPixmap; // opaque; gui-dep - struct base_type; struct resource_type; @@ -75,34 +73,6 @@ struct resource_type; #define SPECENUM_VALUE4NAME "Corner" #include "specenum_gen.h" -/* An edge is the border between two tiles. This structure represents one - * edge. The tiles are given in the same order as the enumeration name. */ -enum edge_type { - EDGE_NS, // North and south - EDGE_WE, // West and east - EDGE_UD, /* Up and down (nw/se), for hex_width tilesets */ - EDGE_LR, /* Left and right (ne/sw), for hex_height tilesets */ - EDGE_COUNT -}; -struct tile_edge { - enum edge_type type; -#define NUM_EDGE_TILES 2 - const struct tile *tile[NUM_EDGE_TILES]; -}; - -/* A corner is the endpoint of several edges. At each corner 4 tiles will - * meet (3 in hex view). Tiles are in clockwise order NESW. */ -struct tile_corner { -#define NUM_CORNER_TILES 4 - const struct tile *tile[NUM_CORNER_TILES]; -}; - -struct drawn_sprite { - bool foggable; // Set to FALSE for sprites that are never fogged. - QPixmap *sprite; - int offset_x, offset_y; // offset from tile origin -}; - #define NUM_TILES_PROGRESS 8 #define MAX_NUM_CITIZEN_SPRITES 6 From 279d15c72569a3549b15173d36d01b3390332e2d Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 25 Apr 2021 00:58:26 +0200 Subject: [PATCH 04/24] tilespec.cpp: rename layers -> terrain_layers Layers will soon become much more general. Free the name for a new variable. See #430. --- client/tilespec.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/tilespec.cpp b/client/tilespec.cpp index 48ccebecf8..c52e7dd43d 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -442,7 +442,7 @@ struct tileset { int num_valid_tileset_dirs, num_cardinal_tileset_dirs; int num_index_valid, num_index_cardinal; enum direction8 valid_tileset_dirs[8], cardinal_tileset_dirs[8]; - struct tileset_layer layers[MAX_NUM_LAYERS]; + std::array terrain_layers; QSet *specfiles; QSet *small_sprites; // This hash table maps tilespec tags to struct small_sprites. @@ -1086,7 +1086,7 @@ static void tileset_free_toplevel(struct tileset *t) } for (i = 0; i < MAX_NUM_LAYERS; i++) { - struct tileset_layer *tslp = &t->layers[i]; + struct tileset_layer *tslp = &t->terrain_layers[i]; if (tslp->match_types) { for (j = 0; j < tslp->match_count; j++) { @@ -2136,7 +2136,7 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, // Terrain layer info. for (i = 0; i < MAX_NUM_LAYERS; i++) { - struct tileset_layer *tslp = &t->layers[i]; + struct tileset_layer *tslp = &t->terrain_layers[i]; int j, k; tslp->match_types = const_cast(secfile_lookup_str_vec( @@ -2201,7 +2201,7 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, for (l = 0; l < draw->num_layers; l++) { struct drawing_layer *dlp = &draw->layer[l]; - struct tileset_layer *tslp = &t->layers[l]; + struct tileset_layer *tslp = &t->terrain_layers[l]; const char *match_type; const char **match_with; size_t count; @@ -3852,7 +3852,7 @@ void tileset_setup_tile_type(struct tileset *t, // Set up each layer of the drawing. for (l = 0; l < draw->num_layers; l++) { struct drawing_layer *dlp = &draw->layer[l]; - struct tileset_layer *tslp = &t->layers[l]; + struct tileset_layer *tslp = &t->terrain_layers[l]; sprite_vector_init(&dlp->base); sprite_vector_init(&dlp->allocated); From e7a9c56c8c787c2178877a0ef9c952376721c0aa Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 25 Apr 2021 01:13:08 +0200 Subject: [PATCH 05/24] Expose layers through layer objects This will make them more powerful. See #430. --- client/layer.h | 14 ++--------- client/mapview_common.cpp | 50 ++++++++++++++++----------------------- client/tilespec.cpp | 29 +++++++++++++---------- client/tilespec.h | 4 ++-- 4 files changed, 40 insertions(+), 57 deletions(-) diff --git a/client/layer.h b/client/layer.h index 3033c853a3..596132a5b2 100644 --- a/client/layer.h +++ b/client/layer.h @@ -107,18 +107,6 @@ struct drawn_sprite { #define TERRAIN_LAYER_COUNT 3 -#define mapview_layer_iterate(layer) \ - { \ - enum mapview_layer layer; \ - int layer_index; \ - \ - for (layer_index = 0; layer_index < LAYER_COUNT; layer_index++) { \ - layer = tileset_get_layer(tileset, layer_index); - -#define mapview_layer_iterate_end \ - } \ - } - // Layer categories can be used to only render part of a tile. enum layer_category { LAYER_CATEGORY_CITY, // Render cities @@ -142,6 +130,8 @@ class layer { const tile_corner *pcorner, const unit *punit, const city *pcity, const unit_type *putype) const; + mapview_layer type() const { return m_layer; } + private: tileset *m_ts; mapview_layer m_layer; diff --git a/client/mapview_common.cpp b/client/mapview_common.cpp index 66107701b7..80ecf82223 100644 --- a/client/mapview_common.cpp +++ b/client/mapview_common.cpp @@ -1010,12 +1010,10 @@ void put_unit(const struct unit *punit, QPixmap *pcanvas, int canvas_x, int canvas_y) { canvas_y += (tileset_unit_height(tileset) - tileset_tile_height(tileset)); - mapview_layer_iterate(layer) - { - put_one_element(pcanvas, layer, NULL, NULL, NULL, punit, NULL, canvas_x, - canvas_y, NULL); + for (const auto &layer : tileset_get_layers(tileset)) { + put_one_element(pcanvas, layer->type(), NULL, NULL, NULL, punit, NULL, + canvas_x, canvas_y, NULL); } - mapview_layer_iterate_end; } /** @@ -1026,12 +1024,10 @@ void put_unittype(const struct unit_type *putype, QPixmap *pcanvas, int canvas_x, int canvas_y) { canvas_y += (tileset_unit_height(tileset) - tileset_tile_height(tileset)); - mapview_layer_iterate(layer) - { - put_one_element(pcanvas, layer, NULL, NULL, NULL, NULL, NULL, canvas_x, - canvas_y, putype); + for (const auto &layer : tileset_get_layers(tileset)) { + put_one_element(pcanvas, layer->type(), NULL, NULL, NULL, NULL, NULL, + canvas_x, canvas_y, putype); } - mapview_layer_iterate_end; } /** @@ -1044,12 +1040,10 @@ void put_city(struct city *pcity, QPixmap *pcanvas, int canvas_x, { canvas_y += (tileset_full_tile_height(tileset) - tileset_tile_height(tileset)); - mapview_layer_iterate(layer) - { - put_one_element(pcanvas, layer, NULL, NULL, NULL, NULL, pcity, canvas_x, - canvas_y, NULL); + for (const auto &layer : tileset_get_layers(tileset)) { + put_one_element(pcanvas, layer->type(), NULL, NULL, NULL, NULL, pcity, + canvas_x, canvas_y, NULL); } - mapview_layer_iterate_end; } /** @@ -1064,12 +1058,10 @@ void put_terrain(struct tile *ptile, QPixmap *pcanvas, int canvas_x, // Use full tile height, even for terrains. canvas_y += (tileset_full_tile_height(tileset) - tileset_tile_height(tileset)); - mapview_layer_iterate(layer) - { - put_one_element(pcanvas, layer, ptile, NULL, NULL, NULL, NULL, canvas_x, - canvas_y, NULL); + for (const auto &layer : tileset_get_layers(tileset)) { + put_one_element(pcanvas, layer->type(), ptile, NULL, NULL, NULL, NULL, + canvas_x, canvas_y, NULL); } - mapview_layer_iterate_end; } /** @@ -1374,12 +1366,11 @@ void update_map_canvas(int canvas_x, int canvas_y, int width, int height) get_color(tileset, COLOR_MAPVIEW_UNKNOWN), canvas_x, canvas_y, width, height); - mapview_layer_iterate(layer) - { - if (layer == LAYER_TILELABEL) { + for (const auto &layer : tileset_get_layers(tileset)) { + if (layer->type() == LAYER_TILELABEL) { show_tile_labels(canvas_x, canvas_y, width, height); } - if (layer == LAYER_CITYBAR) { + if (layer->type() == LAYER_CITYBAR) { show_city_descriptions(canvas_x, canvas_y, width, height); continue; } @@ -1393,20 +1384,19 @@ void update_map_canvas(int canvas_x, int canvas_y, int width, int height) const int cx = gui_x - mapview.gui_x0, cy = gui_y - mapview.gui_y0; if (ptile) { - put_one_tile(mapview.store, layer, ptile, cx, cy); + put_one_tile(mapview.store, layer->type(), ptile, cx, cy); } else if (pedge) { - put_one_element(mapview.store, layer, NULL, pedge, NULL, NULL, NULL, - cx, cy, NULL); + put_one_element(mapview.store, layer->type(), NULL, pedge, NULL, + NULL, NULL, cx, cy, NULL); } else if (pcorner) { - put_one_element(mapview.store, layer, NULL, NULL, pcorner, NULL, - NULL, cx, cy, NULL); + put_one_element(mapview.store, layer->type(), NULL, NULL, pcorner, + NULL, NULL, cx, cy, NULL); } else { // This can happen, for instance for unreal tiles. } } gui_rect_iterate_coord_end; } - mapview_layer_iterate_end; draw_trade_routes(); link_marks_draw_all(); diff --git a/client/tilespec.cpp b/client/tilespec.cpp index c52e7dd43d..61cfbd851e 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -401,7 +401,7 @@ struct tileset { char *for_ruleset; - enum mapview_layer layer_order[LAYER_COUNT]; + std::vector> layers; enum ts_type type; int hex_width, hex_height; @@ -2086,6 +2086,7 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, layer_order = secfile_lookup_str_vec(file, &num_layers, "tilespec.layer_order"); if (layer_order != NULL) { + mapview_layer order[LAYER_COUNT]; for (i = 0; i < num_layers; i++) { int j; enum mapview_layer layer = @@ -2099,13 +2100,13 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, } // Check for duplicates. for (j = 0; j < i; j++) { - if (t->layer_order[j] == layer) { + if (order[j] == layer) { qCritical("layer_order: Duplicate layer \"%s\"", layer_order[i]); tileset_stop_read(t, file, fname, sections, layer_order); return nullptr; } } - t->layer_order[i] = layer; + order[i] = layer; } /* Now check that all layers are present. Doing it now allows for a @@ -2115,7 +2116,7 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, bool found = false; for (j = 0; j < num_layers; j++) { - if (i == t->layer_order[j]) { + if (i == order[j]) { found = true; break; } @@ -2127,10 +2128,15 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, return nullptr; } } + + for (auto layer : order) { + t->layers.push_back(std::make_unique(t, layer)); + } } else { // There is no layer_order tag in the specfile -> use the default - for (i = 0; i < LAYER_COUNT; i++) { - t->layer_order[i] = static_cast(i); + for (i = 0; i < LAYER_COUNT; ++i) { + t->layers.push_back(std::make_unique( + t, static_cast(i))); } } @@ -6754,17 +6760,14 @@ void fill_basic_base_sprite_array(const struct tileset *t, #undef ADD_SPRITE_IF_NOT_NULL } -/** - Gets the nth layer of the tileset. - */ -enum mapview_layer tileset_get_layer(const struct tileset *t, int n) +const std::vector> & +tileset_get_layers(const struct tileset *t) { - fc_assert(n < LAYER_COUNT); - return t->layer_order[n]; + return t->layers; } /** - Gets the nth layer of the tileset. + Checks that a layer is within a category. */ bool tileset_layer_in_category(enum mapview_layer layer, enum layer_category cat) diff --git a/client/tilespec.h b/client/tilespec.h index 0080d3b41a..4db7a02ed8 100644 --- a/client/tilespec.h +++ b/client/tilespec.h @@ -123,8 +123,8 @@ void tileset_background_init(struct tileset *t); void tileset_background_free(struct tileset *t); // Layer order - -enum mapview_layer tileset_get_layer(const struct tileset *t, int n); +const std::vector> & +tileset_get_layers(const struct tileset *t); bool tileset_layer_in_category(enum mapview_layer layer, enum layer_category cat); From c53d3899595d3357790e1613237472fb975eefaf Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 25 Apr 2021 01:50:06 +0200 Subject: [PATCH 06/24] Use layer::fill_sprite_array() when rendering Using the virtual function will allow to offload drawing code to subclasses. See #430. --- client/mapview_common.cpp | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/client/mapview_common.cpp b/client/mapview_common.cpp index 80ecf82223..158de4946e 100644 --- a/client/mapview_common.cpp +++ b/client/mapview_common.cpp @@ -967,7 +967,8 @@ void put_drawn_sprites(QPixmap *pcanvas, int canvas_x, int canvas_y, Draw one layer of a tile, edge, corner, unit, and/or city onto the canvas at the given position. */ -void put_one_element(QPixmap *pcanvas, enum mapview_layer layer, +void put_one_element(QPixmap *pcanvas, + const std::unique_ptr &layer, const struct tile *ptile, const struct tile_edge *pedge, const struct tile_corner *pcorner, const struct unit *punit, const struct city *pcity, @@ -977,8 +978,8 @@ void put_one_element(QPixmap *pcanvas, enum mapview_layer layer, bool city_mode = false; bool city_unit = false; int dummy_x, dummy_y; - auto sprites = fill_sprite_array(tileset, layer, ptile, pedge, pcorner, - punit, pcity, putype); + auto sprites = + layer->fill_sprite_array(ptile, pedge, pcorner, punit, pcity, putype); bool fog = (ptile && gui_options.draw_fog_of_war && TILE_KNOWN_UNSEEN == client_tile_get_known(ptile)); if (ptile) { @@ -1011,8 +1012,8 @@ void put_unit(const struct unit *punit, QPixmap *pcanvas, int canvas_x, { canvas_y += (tileset_unit_height(tileset) - tileset_tile_height(tileset)); for (const auto &layer : tileset_get_layers(tileset)) { - put_one_element(pcanvas, layer->type(), NULL, NULL, NULL, punit, NULL, - canvas_x, canvas_y, NULL); + put_one_element(pcanvas, layer, NULL, NULL, NULL, punit, NULL, canvas_x, + canvas_y, NULL); } } @@ -1025,8 +1026,8 @@ void put_unittype(const struct unit_type *putype, QPixmap *pcanvas, { canvas_y += (tileset_unit_height(tileset) - tileset_tile_height(tileset)); for (const auto &layer : tileset_get_layers(tileset)) { - put_one_element(pcanvas, layer->type(), NULL, NULL, NULL, NULL, NULL, - canvas_x, canvas_y, putype); + put_one_element(pcanvas, layer, NULL, NULL, NULL, NULL, NULL, canvas_x, + canvas_y, putype); } } @@ -1041,8 +1042,8 @@ void put_city(struct city *pcity, QPixmap *pcanvas, int canvas_x, canvas_y += (tileset_full_tile_height(tileset) - tileset_tile_height(tileset)); for (const auto &layer : tileset_get_layers(tileset)) { - put_one_element(pcanvas, layer->type(), NULL, NULL, NULL, NULL, pcity, - canvas_x, canvas_y, NULL); + put_one_element(pcanvas, layer, NULL, NULL, NULL, NULL, pcity, canvas_x, + canvas_y, NULL); } } @@ -1059,8 +1060,8 @@ void put_terrain(struct tile *ptile, QPixmap *pcanvas, int canvas_x, canvas_y += (tileset_full_tile_height(tileset) - tileset_tile_height(tileset)); for (const auto &layer : tileset_get_layers(tileset)) { - put_one_element(pcanvas, layer->type(), ptile, NULL, NULL, NULL, NULL, - canvas_x, canvas_y, NULL); + put_one_element(pcanvas, layer, ptile, NULL, NULL, NULL, NULL, canvas_x, + canvas_y, NULL); } } @@ -1178,7 +1179,8 @@ void put_nuke_mushroom_pixmaps(struct tile *ptile) /** Draw some or all of a tile onto the canvas. */ -static void put_one_tile(QPixmap *pcanvas, enum mapview_layer layer, +static void put_one_tile(QPixmap *pcanvas, + const std::unique_ptr &layer, struct tile *ptile, int canvas_x, int canvas_y) { if (client_tile_get_known(ptile) != TILE_UNKNOWN @@ -1384,13 +1386,13 @@ void update_map_canvas(int canvas_x, int canvas_y, int width, int height) const int cx = gui_x - mapview.gui_x0, cy = gui_y - mapview.gui_y0; if (ptile) { - put_one_tile(mapview.store, layer->type(), ptile, cx, cy); + put_one_tile(mapview.store, layer, ptile, cx, cy); } else if (pedge) { - put_one_element(mapview.store, layer->type(), NULL, pedge, NULL, - NULL, NULL, cx, cy, NULL); + put_one_element(mapview.store, layer, NULL, pedge, NULL, NULL, NULL, + cx, cy, NULL); } else if (pcorner) { - put_one_element(mapview.store, layer->type(), NULL, NULL, pcorner, - NULL, NULL, cx, cy, NULL); + put_one_element(mapview.store, layer, NULL, NULL, pcorner, NULL, + NULL, cx, cy, NULL); } else { // This can happen, for instance for unreal tiles. } From b3f0a3f07bc4668ea5082a14234bff65368b6b80 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 25 Apr 2021 02:45:54 +0200 Subject: [PATCH 07/24] Improve const-correctness in the map drawing code See #430. --- client/gui-qt/canvas.cpp | 39 ++++++++++++++++++++----------------- client/gui-qt/canvas.h | 6 +++--- client/gui-qt/helpdlg.cpp | 2 +- client/gui-qt/qtg_cxxside.h | 39 ++++++++++++++++++++----------------- client/gui-qt/sprite.cpp | 9 +++++---- client/gui_interface.cpp | 36 ++++++++++++++++++---------------- client/gui_interface.h | 38 +++++++++++++++++++----------------- client/include/canvas_g.h | 31 ++++++++++++++++------------- client/include/sprite_g.h | 14 ++++++------- 9 files changed, 114 insertions(+), 100 deletions(-) diff --git a/client/gui-qt/canvas.cpp b/client/gui-qt/canvas.cpp index b0fc5e369b..205f356d6f 100644 --- a/client/gui-qt/canvas.cpp +++ b/client/gui-qt/canvas.cpp @@ -54,7 +54,7 @@ void qtg_canvas_free(QPixmap *store) { delete store; } /** Copies an area from the source canvas to the destination canvas. */ -void qtg_canvas_copy(QPixmap *dest, QPixmap *src, int src_x, int src_y, +void qtg_canvas_copy(QPixmap *dest, const QPixmap *src, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { QRectF source_rect(src_x, src_y, width, height); @@ -73,7 +73,7 @@ void qtg_canvas_copy(QPixmap *dest, QPixmap *src, int src_x, int src_y, /** Copies an area from the source pixmap to the destination pixmap. */ -void pixmap_copy(QPixmap *dest, QPixmap *src, int src_x, int src_y, +void pixmap_copy(QPixmap *dest, const QPixmap *src, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { QRectF source_rect(src_x, src_y, width, height); @@ -92,8 +92,8 @@ void pixmap_copy(QPixmap *dest, QPixmap *src, int src_x, int src_y, /** Copies an area from the source image to the destination image. */ -void image_copy(QImage *dest, QImage *src, int src_x, int src_y, int dest_x, - int dest_y, int width, int height) +void image_copy(QImage *dest, const QImage *src, int src_x, int src_y, + int dest_x, int dest_y, int width, int height) { QRectF source_rect(src_x, src_y, width, height); QRectF dest_rect(dest_x, dest_y, width, height); @@ -112,7 +112,7 @@ void image_copy(QImage *dest, QImage *src, int src_x, int src_y, int dest_x, Draw some or all of a sprite onto the canvas. */ void qtg_canvas_put_sprite(QPixmap *pcanvas, int canvas_x, int canvas_y, - QPixmap *sprite, int offset_x, int offset_y, + const QPixmap *sprite, int offset_x, int offset_y, int width, int height) { QPainter p; @@ -127,7 +127,7 @@ void qtg_canvas_put_sprite(QPixmap *pcanvas, int canvas_x, int canvas_y, Draw a full sprite onto the canvas. */ void qtg_canvas_put_sprite_full(QPixmap *pcanvas, int canvas_x, int canvas_y, - QPixmap *sprite) + const QPixmap *sprite) { int width, height; @@ -141,8 +141,8 @@ void qtg_canvas_put_sprite_full(QPixmap *pcanvas, int canvas_x, int canvas_y, fog. */ void qtg_canvas_put_sprite_fogged(QPixmap *pcanvas, int canvas_x, - int canvas_y, QPixmap *psprite, bool fog, - int fog_x, int fog_y) + int canvas_y, const QPixmap *psprite, + bool fog, int fog_x, int fog_y) { Q_UNUSED(fog_x) Q_UNUSED(fog_y) @@ -165,8 +165,8 @@ void qtg_canvas_put_sprite_fogged(QPixmap *pcanvas, int canvas_x, Draw fog outside city map when city is opened */ void qtg_canvas_put_sprite_citymode(QPixmap *pcanvas, int canvas_x, - int canvas_y, QPixmap *psprite, bool fog, - int fog_x, int fog_y) + int canvas_y, const QPixmap *psprite, + bool fog, int fog_x, int fog_y) { Q_UNUSED(fog_x) Q_UNUSED(fog_y) @@ -183,7 +183,8 @@ void qtg_canvas_put_sprite_citymode(QPixmap *pcanvas, int canvas_x, Put unit in city area when city dialog is open */ void canvas_put_unit_fogged(QPixmap *pcanvas, int canvas_x, int canvas_y, - QPixmap *psprite, bool fog, int fog_x, int fog_y) + const QPixmap *psprite, bool fog, int fog_x, + int fog_y) { Q_UNUSED(fog_y) Q_UNUSED(fog_x) @@ -197,8 +198,9 @@ void canvas_put_unit_fogged(QPixmap *pcanvas, int canvas_x, int canvas_y, /** Draw a filled-in colored rectangle onto canvas. */ -void qtg_canvas_put_rectangle(QPixmap *pcanvas, QColor *pcolor, int canvas_x, - int canvas_y, int width, int height) +void qtg_canvas_put_rectangle(QPixmap *pcanvas, const QColor *pcolor, + int canvas_x, int canvas_y, int width, + int height) { QBrush brush(*pcolor); QPen pen(*pcolor); @@ -223,8 +225,9 @@ void qtg_canvas_put_rectangle(QPixmap *pcanvas, QColor *pcolor, int canvas_x, /** Fill the area covered by the sprite with the given color. */ -void qtg_canvas_fill_sprite_area(QPixmap *pcanvas, QPixmap *psprite, - QColor *pcolor, int canvas_x, int canvas_y) +void qtg_canvas_fill_sprite_area(QPixmap *pcanvas, const QPixmap *psprite, + const QColor *pcolor, int canvas_x, + int canvas_y) { int width, height; @@ -236,7 +239,7 @@ void qtg_canvas_fill_sprite_area(QPixmap *pcanvas, QPixmap *psprite, /** Draw a 1-pixel-width colored line onto the canvas. */ -void qtg_canvas_put_line(QPixmap *pcanvas, QColor *pcolor, +void qtg_canvas_put_line(QPixmap *pcanvas, const QColor *pcolor, enum line_type ltype, int start_x, int start_y, int dx, int dy) { @@ -274,7 +277,7 @@ void qtg_canvas_put_line(QPixmap *pcanvas, QColor *pcolor, /** Draw a 1-pixel-width colored curved line onto the canvas. */ -void qtg_canvas_put_curved_line(QPixmap *pcanvas, QColor *pcolor, +void qtg_canvas_put_curved_line(QPixmap *pcanvas, const QColor *pcolor, enum line_type ltype, int start_x, int start_y, int dx, int dy) { @@ -340,7 +343,7 @@ void qtg_get_text_size(int *width, int *height, enum client_font font, take care of this manually. The text will not be NULL but may be empty. */ void qtg_canvas_put_text(QPixmap *pcanvas, int canvas_x, int canvas_y, - enum client_font font, QColor *pcolor, + enum client_font font, const QColor *pcolor, const QString &text) { QPainter p; diff --git a/client/gui-qt/canvas.h b/client/gui-qt/canvas.h index 8d62cb6d6e..cb615efd02 100644 --- a/client/gui-qt/canvas.h +++ b/client/gui-qt/canvas.h @@ -15,8 +15,8 @@ #include QPixmap *qtg_canvas_create(int width, int height); -void pixmap_copy(QPixmap *dest, QPixmap *src, int src_x, int src_y, +void pixmap_copy(QPixmap *dest, const QPixmap *src, int src_x, int src_y, int dest_x, int dest_y, int width, int height); -void image_copy(QImage *dest, QImage *src, int src_x, int src_y, int dest_x, - int dest_y, int width, int height); +void image_copy(QImage *dest, const QImage *src, int src_x, int src_y, + int dest_x, int dest_y, int width, int height); QRect zealous_crop_rect(QImage &p); diff --git a/client/gui-qt/helpdlg.cpp b/client/gui-qt/helpdlg.cpp index dc7b84b1db..b5e4ed1079 100644 --- a/client/gui-qt/helpdlg.cpp +++ b/client/gui-qt/helpdlg.cpp @@ -219,7 +219,7 @@ void help_dialog::make_tree() QHash hash; QIcon icon; QTreeWidgetItem *item; - QPixmap *spite; + const QPixmap *spite; struct advance *padvance; QPixmap *pcan; struct extra_type *pextra; diff --git a/client/gui-qt/qtg_cxxside.h b/client/gui-qt/qtg_cxxside.h index 348103b807..0931d98239 100644 --- a/client/gui-qt/qtg_cxxside.h +++ b/client/gui-qt/qtg_cxxside.h @@ -38,10 +38,11 @@ bool qtg_is_view_supported(enum ts_type type); void qtg_tileset_type_set(enum ts_type type); void qtg_free_intro_radar_sprites(); QPixmap *qtg_load_gfxfile(const char *filename); -QPixmap *qtg_create_sprite(int width, int height, QColor *pcolor); -void qtg_get_sprite_dimensions(QPixmap *sprite, int *width, int *height); -QPixmap *qtg_crop_sprite(QPixmap *source, int x, int y, int width, - int height, QPixmap *mask, int mask_offset_x, +QPixmap *qtg_create_sprite(int width, int height, const QColor *pcolor); +void qtg_get_sprite_dimensions(const QPixmap *sprite, int *width, + int *height); +QPixmap *qtg_crop_sprite(const QPixmap *source, int x, int y, int width, + int height, const QPixmap *mask, int mask_offset_x, int mask_offset_y, float scale, bool smooth); void qtg_free_sprite(QPixmap *s); @@ -50,33 +51,35 @@ void qtg_color_free(QColor *pcolor); QPixmap *qtg_canvas_create(int width, int height); void qtg_canvas_free(QPixmap *store); -void qtg_canvas_copy(QPixmap *dest, QPixmap *src, int src_x, int src_y, +void qtg_canvas_copy(QPixmap *dest, const QPixmap *src, int src_x, int src_y, int dest_x, int dest_y, int width, int height); void qtg_canvas_put_sprite(QPixmap *pcanvas, int canvas_x, int canvas_y, - QPixmap *sprite, int offset_x, int offset_y, + const QPixmap *sprite, int offset_x, int offset_y, int width, int height); void qtg_canvas_put_sprite_full(QPixmap *pcanvas, int canvas_x, int canvas_y, - QPixmap *sprite); + const QPixmap *sprite); void qtg_canvas_put_sprite_fogged(QPixmap *pcanvas, int canvas_x, - int canvas_y, QPixmap *psprite, bool fog, - int fog_x, int fog_y); + int canvas_y, const QPixmap *psprite, + bool fog, int fog_x, int fog_y); void qtg_canvas_put_sprite_citymode(QPixmap *pcanvas, int canvas_x, - int canvas_y, QPixmap *psprite, bool fog, - int fog_x, int fog_y); -void qtg_canvas_put_rectangle(QPixmap *pcanvas, QColor *pcolor, int canvas_x, - int canvas_y, int width, int height); -void qtg_canvas_fill_sprite_area(QPixmap *pcanvas, QPixmap *psprite, - QColor *pcolor, int canvas_x, int canvas_y); -void qtg_canvas_put_line(QPixmap *pcanvas, QColor *pcolor, + int canvas_y, const QPixmap *psprite, + bool fog, int fog_x, int fog_y); +void qtg_canvas_put_rectangle(QPixmap *pcanvas, const QColor *pcolor, + int canvas_x, int canvas_y, int width, + int height); +void qtg_canvas_fill_sprite_area(QPixmap *pcanvas, const QPixmap *psprite, + const QColor *pcolor, int canvas_x, + int canvas_y); +void qtg_canvas_put_line(QPixmap *pcanvas, const QColor *pcolor, enum line_type ltype, int start_x, int start_y, int dx, int dy); -void qtg_canvas_put_curved_line(QPixmap *pcanvas, QColor *pcolor, +void qtg_canvas_put_curved_line(QPixmap *pcanvas, const QColor *pcolor, enum line_type ltype, int start_x, int start_y, int dx, int dy); void qtg_get_text_size(int *width, int *height, enum client_font font, const QString &); void qtg_canvas_put_text(QPixmap *pcanvas, int canvas_x, int canvas_y, - enum client_font font, QColor *pcolor, + enum client_font font, const QColor *pcolor, const QString &text); void qtg_set_rulesets(int num_rulesets, QStringList rulesets); diff --git a/client/gui-qt/sprite.cpp b/client/gui-qt/sprite.cpp index 157b9d7ec8..bda240faa5 100644 --- a/client/gui-qt/sprite.cpp +++ b/client/gui-qt/sprite.cpp @@ -59,8 +59,8 @@ QPixmap *qtg_load_gfxfile(const char *filename) in the mask image will be used to clip pixel (0,0) in the source image which is pixel (-x,-y) in the new image. */ -QPixmap *qtg_crop_sprite(QPixmap *source, int x, int y, int width, - int height, QPixmap *mask, int mask_offset_x, +QPixmap *qtg_crop_sprite(const QPixmap *source, int x, int y, int width, + int height, const QPixmap *mask, int mask_offset_x, int mask_offset_y, float scale, bool smooth) { QPainter p; @@ -116,7 +116,8 @@ QPixmap *qtg_crop_sprite(QPixmap *source, int x, int y, int width, /** Find the dimensions of the sprite. */ -void qtg_get_sprite_dimensions(QPixmap *sprite, int *width, int *height) +void qtg_get_sprite_dimensions(const QPixmap *sprite, int *width, + int *height) { *width = sprite->width(); *height = sprite->height(); @@ -130,7 +131,7 @@ void qtg_free_sprite(QPixmap *s) { delete s; } /** Create a new sprite with the given height, width and color. */ -QPixmap *qtg_create_sprite(int width, int height, QColor *pcolor) +QPixmap *qtg_create_sprite(int width, int height, const QColor *pcolor) { QPixmap *created = new QPixmap; diff --git a/client/gui_interface.cpp b/client/gui_interface.cpp index 81a0b33b73..a2d4cf13f5 100644 --- a/client/gui_interface.cpp +++ b/client/gui_interface.cpp @@ -111,7 +111,7 @@ QPixmap *load_gfxfile(const char *filename) /** Call create_sprite callback */ -QPixmap *create_sprite(int width, int height, QColor *pcolor) +QPixmap *create_sprite(int width, int height, const QColor *pcolor) { return funcs.create_sprite(width, height, pcolor); } @@ -119,7 +119,7 @@ QPixmap *create_sprite(int width, int height, QColor *pcolor) /** Call get_sprite_dimensions callback */ -void get_sprite_dimensions(QPixmap *sprite, int *width, int *height) +void get_sprite_dimensions(const QPixmap *sprite, int *width, int *height) { funcs.get_sprite_dimensions(sprite, width, height); } @@ -127,9 +127,9 @@ void get_sprite_dimensions(QPixmap *sprite, int *width, int *height) /** Call crop_sprite callback */ -QPixmap *crop_sprite(QPixmap *source, int x, int y, int width, int height, - QPixmap *mask, int mask_offset_x, int mask_offset_y, - float scale, bool smooth) +QPixmap *crop_sprite(const QPixmap *source, int x, int y, int width, + int height, const QPixmap *mask, int mask_offset_x, + int mask_offset_y, float scale, bool smooth) { return funcs.crop_sprite(source, x, y, width, height, mask, mask_offset_x, mask_offset_y, scale, smooth); @@ -169,7 +169,7 @@ void canvas_free(QPixmap *store) { funcs.canvas_free(store); } /** Call canvas_copy callback */ -void canvas_copy(QPixmap *dest, QPixmap *src, int src_x, int src_y, +void canvas_copy(QPixmap *dest, const QPixmap *src, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { funcs.canvas_copy(dest, src, src_x, src_y, dest_x, dest_y, width, height); @@ -179,7 +179,7 @@ void canvas_copy(QPixmap *dest, QPixmap *src, int src_x, int src_y, Call canvas_put_sprite callback */ void canvas_put_sprite(QPixmap *pcanvas, int canvas_x, int canvas_y, - QPixmap *psprite, int offset_x, int offset_y, + const QPixmap *psprite, int offset_x, int offset_y, int width, int height) { funcs.canvas_put_sprite(pcanvas, canvas_x, canvas_y, psprite, offset_x, @@ -190,7 +190,7 @@ void canvas_put_sprite(QPixmap *pcanvas, int canvas_x, int canvas_y, Call canvas_put_sprite_full callback */ void canvas_put_sprite_full(QPixmap *pcanvas, int canvas_x, int canvas_y, - QPixmap *psprite) + const QPixmap *psprite) { funcs.canvas_put_sprite_full(pcanvas, canvas_x, canvas_y, psprite); } @@ -199,7 +199,7 @@ void canvas_put_sprite_full(QPixmap *pcanvas, int canvas_x, int canvas_y, Call canvas_put_sprite_fogged callback */ void canvas_put_sprite_fogged(QPixmap *pcanvas, int canvas_x, int canvas_y, - QPixmap *psprite, bool fog, int fog_x, + const QPixmap *psprite, bool fog, int fog_x, int fog_y) { funcs.canvas_put_sprite_fogged(pcanvas, canvas_x, canvas_y, psprite, fog, @@ -207,7 +207,7 @@ void canvas_put_sprite_fogged(QPixmap *pcanvas, int canvas_x, int canvas_y, } void canvas_put_sprite_citymode(QPixmap *pcanvas, int canvas_x, int canvas_y, - QPixmap *psprite, bool fog, int fog_x, + const QPixmap *psprite, bool fog, int fog_x, int fog_y) { funcs.canvas_put_sprite_citymode(pcanvas, canvas_x, canvas_y, psprite, fog, @@ -217,8 +217,8 @@ void canvas_put_sprite_citymode(QPixmap *pcanvas, int canvas_x, int canvas_y, /** Call canvas_put_rectangle callback */ -void canvas_put_rectangle(QPixmap *pcanvas, QColor *pcolor, int canvas_x, - int canvas_y, int width, int height) +void canvas_put_rectangle(QPixmap *pcanvas, const QColor *pcolor, + int canvas_x, int canvas_y, int width, int height) { funcs.canvas_put_rectangle(pcanvas, pcolor, canvas_x, canvas_y, width, height); @@ -228,7 +228,8 @@ void canvas_put_rectangle(QPixmap *pcanvas, QColor *pcolor, int canvas_x, Call canvas_fill_sprite_area callback */ void canvas_fill_sprite_area(QPixmap *pcanvas, QPixmap *psprite, - QColor *pcolor, int canvas_x, int canvas_y) + const QColor *pcolor, int canvas_x, + int canvas_y) { funcs.canvas_fill_sprite_area(pcanvas, psprite, pcolor, canvas_x, canvas_y); @@ -237,8 +238,9 @@ void canvas_fill_sprite_area(QPixmap *pcanvas, QPixmap *psprite, /** Call canvas_put_line callback */ -void canvas_put_line(QPixmap *pcanvas, QColor *pcolor, enum line_type ltype, - int start_x, int start_y, int dx, int dy) +void canvas_put_line(QPixmap *pcanvas, const QColor *pcolor, + enum line_type ltype, int start_x, int start_y, int dx, + int dy) { funcs.canvas_put_line(pcanvas, pcolor, ltype, start_x, start_y, dx, dy); } @@ -246,7 +248,7 @@ void canvas_put_line(QPixmap *pcanvas, QColor *pcolor, enum line_type ltype, /** Call canvas_put_curved_line callback */ -void canvas_put_curved_line(QPixmap *pcanvas, QColor *pcolor, +void canvas_put_curved_line(QPixmap *pcanvas, const QColor *pcolor, enum line_type ltype, int start_x, int start_y, int dx, int dy) { @@ -267,7 +269,7 @@ void get_text_size(int *width, int *height, enum client_font font, Call canvas_put_text callback */ void canvas_put_text(QPixmap *pcanvas, int canvas_x, int canvas_y, - enum client_font font, QColor *pcolor, + enum client_font font, const QColor *pcolor, const QString &text) { funcs.canvas_put_text(pcanvas, canvas_x, canvas_y, font, pcolor, text); diff --git a/client/gui_interface.h b/client/gui_interface.h index efcfd6f706..4ddc121839 100644 --- a/client/gui_interface.h +++ b/client/gui_interface.h @@ -45,10 +45,11 @@ struct gui_funcs { void (*tileset_type_set)(enum ts_type type); void (*free_intro_radar_sprites)(); QPixmap *(*load_gfxfile)(const char *filename); - QPixmap *(*create_sprite)(int width, int height, QColor *pcolor); - void (*get_sprite_dimensions)(QPixmap *sprite, int *width, int *height); - QPixmap *(*crop_sprite)(QPixmap *source, int x, int y, int width, - int height, QPixmap *mask, int mask_offset_x, + QPixmap *(*create_sprite)(int width, int height, const QColor *pcolor); + void (*get_sprite_dimensions)(const QPixmap *sprite, int *width, + int *height); + QPixmap *(*crop_sprite)(const QPixmap *source, int x, int y, int width, + int height, const QPixmap *mask, int mask_offset_x, int mask_offset_y, float scale, bool smooth); void (*free_sprite)(QPixmap *s); @@ -57,35 +58,36 @@ struct gui_funcs { QPixmap *(*canvas_create)(int width, int height); void (*canvas_free)(QPixmap *store); - void (*canvas_copy)(QPixmap *dest, QPixmap *src, int src_x, int src_y, - int dest_x, int dest_y, int width, int height); + void (*canvas_copy)(QPixmap *dest, const QPixmap *src, int src_x, + int src_y, int dest_x, int dest_y, int width, + int height); void (*canvas_put_sprite)(QPixmap *pcanvas, int canvas_x, int canvas_y, - QPixmap *psprite, int offset_x, int offset_y, - int width, int height); + const QPixmap *psprite, int offset_x, + int offset_y, int width, int height); void (*canvas_put_sprite_full)(QPixmap *pcanvas, int canvas_x, - int canvas_y, QPixmap *psprite); + int canvas_y, const QPixmap *psprite); void (*canvas_put_sprite_fogged)(QPixmap *pcanvas, int canvas_x, - int canvas_y, QPixmap *psprite, bool fog, - int fog_x, int fog_y); + int canvas_y, const QPixmap *psprite, + bool fog, int fog_x, int fog_y); void (*canvas_put_sprite_citymode)(QPixmap *pcanvas, int canvas_x, - int canvas_y, QPixmap *psprite, + int canvas_y, const QPixmap *psprite, bool fog, int fog_x, int fog_y); - void (*canvas_put_rectangle)(QPixmap *pcanvas, QColor *pcolor, + void (*canvas_put_rectangle)(QPixmap *pcanvas, const QColor *pcolor, int canvas_x, int canvas_y, int width, int height); - void (*canvas_fill_sprite_area)(QPixmap *pcanvas, QPixmap *psprite, - QColor *pcolor, int canvas_x, + void (*canvas_fill_sprite_area)(QPixmap *pcanvas, const QPixmap *psprite, + const QColor *pcolor, int canvas_x, int canvas_y); - void (*canvas_put_line)(QPixmap *pcanvas, QColor *pcolor, + void (*canvas_put_line)(QPixmap *pcanvas, const QColor *pcolor, enum line_type ltype, int start_x, int start_y, int dx, int dy); - void (*canvas_put_curved_line)(QPixmap *pcanvas, QColor *pcolor, + void (*canvas_put_curved_line)(QPixmap *pcanvas, const QColor *pcolor, enum line_type ltype, int start_x, int start_y, int dx, int dy); void (*get_text_size)(int *width, int *height, enum client_font font, const QString &text); void (*canvas_put_text)(QPixmap *pcanvas, int canvas_x, int canvas_y, - enum client_font font, QColor *pcolor, + enum client_font font, const QColor *pcolor, const QString &); void (*set_rulesets)(int num_rulesets, QStringList rulesets); diff --git a/client/include/canvas_g.h b/client/include/canvas_g.h index 9fb69d4252..b5c9dc29f0 100644 --- a/client/include/canvas_g.h +++ b/client/include/canvas_g.h @@ -26,31 +26,34 @@ GUI_FUNC_PROTO(QPixmap *, canvas_create, int width, int height) GUI_FUNC_PROTO(void, canvas_free, QPixmap *store) // Drawing functions -GUI_FUNC_PROTO(void, canvas_copy, QPixmap *dest, QPixmap *src, int src_x, - int src_y, int dest_x, int dest_y, int width, int height) +GUI_FUNC_PROTO(void, canvas_copy, QPixmap *dest, const QPixmap *src, + int src_x, int src_y, int dest_x, int dest_y, int width, + int height) GUI_FUNC_PROTO(void, canvas_put_sprite, QPixmap *pcanvas, int canvas_x, - int canvas_y, QPixmap *sprite, int offset_x, int offset_y, - int width, int height); + int canvas_y, const QPixmap *sprite, int offset_x, + int offset_y, int width, int height); GUI_FUNC_PROTO(void, canvas_put_sprite_full, QPixmap *pcanvas, int canvas_x, - int canvas_y, QPixmap *sprite) + int canvas_y, const QPixmap *sprite) GUI_FUNC_PROTO(void, canvas_put_sprite_fogged, QPixmap *pcanvas, - int canvas_x, int canvas_y, QPixmap *psprite, bool fog, + int canvas_x, int canvas_y, const QPixmap *psprite, bool fog, int fog_x, int fog_y) GUI_FUNC_PROTO(void, canvas_put_sprite_citymode, QPixmap *pcanvas, - int canvas_x, int canvas_y, QPixmap *psprite, bool fog, + int canvas_x, int canvas_y, const QPixmap *psprite, bool fog, int fog_x, int fog_y) -GUI_FUNC_PROTO(void, canvas_put_rectangle, QPixmap *pcanvas, QColor *pcolor, - int canvas_x, int canvas_y, int width, int height) +GUI_FUNC_PROTO(void, canvas_put_rectangle, QPixmap *pcanvas, + const QColor *pcolor, int canvas_x, int canvas_y, int width, + int height) GUI_FUNC_PROTO(void, canvas_fill_sprite_area, QPixmap *pcanvas, - QPixmap *psprite, QColor *pcolor, int canvas_x, int canvas_y) -GUI_FUNC_PROTO(void, canvas_put_line, QPixmap *pcanvas, QColor *pcolor, + QPixmap *psprite, const QColor *pcolor, int canvas_x, + int canvas_y) +GUI_FUNC_PROTO(void, canvas_put_line, QPixmap *pcanvas, const QColor *pcolor, enum line_type ltype, int start_x, int start_y, int dx, int dy) GUI_FUNC_PROTO(void, canvas_put_curved_line, QPixmap *pcanvas, - QColor *pcolor, enum line_type ltype, int start_x, + const QColor *pcolor, enum line_type ltype, int start_x, int start_y, int dx, int dy) void canvas_put_unit_fogged(QPixmap *pcanvas, int canvas_x, int canvas_y, - QPixmap *psprite, bool fog, int fog_x, + const QPixmap *psprite, bool fog, int fog_x, int fog_y); // Text drawing functions enum client_font { @@ -62,7 +65,7 @@ enum client_font { GUI_FUNC_PROTO(void, get_text_size, int *width, int *height, enum client_font font, const QString &text) GUI_FUNC_PROTO(void, canvas_put_text, QPixmap *pcanvas, int canvas_x, - int canvas_y, enum client_font font, QColor *pcolor, + int canvas_y, enum client_font font, const QColor *pcolor, const QString &text) QFont *get_font(enum client_font font); diff --git a/client/include/sprite_g.h b/client/include/sprite_g.h index ab56dd1912..0f1560f824 100644 --- a/client/include/sprite_g.h +++ b/client/include/sprite_g.h @@ -13,15 +13,15 @@ _ ._ Copyright (c) 1996-2021 Freeciv21 and Freeciv contributors. #include "gui_proto_constructor.h" #include "support.h" -class QPixmap; // opaque type, real type is gui-dep -struct color; +class QColor; +class QPixmap; GUI_FUNC_PROTO(QPixmap *, load_gfxfile, const char *filename) -GUI_FUNC_PROTO(QPixmap *, crop_sprite, QPixmap *source, int x, int y, - int width, int height, QPixmap *mask, int mask_offset_x, +GUI_FUNC_PROTO(QPixmap *, crop_sprite, const QPixmap *source, int x, int y, + int width, int height, const QPixmap *mask, int mask_offset_x, int mask_offset_y, float scale, bool smooth) GUI_FUNC_PROTO(QPixmap *, create_sprite, int width, int height, - QColor *pcolor) -GUI_FUNC_PROTO(void, get_sprite_dimensions, QPixmap *sprite, int *width, - int *height) + const QColor *pcolor) +GUI_FUNC_PROTO(void, get_sprite_dimensions, const QPixmap *sprite, + int *width, int *height) GUI_FUNC_PROTO(void, free_sprite, QPixmap *s) From e5f57d7d81f876242edbfc22135e9d99b9546c9f Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 25 Apr 2021 03:00:39 +0200 Subject: [PATCH 08/24] Add constructors to drawn_sprite This greatly simplifies the definition of ADD_SPRITE in the tileset code and will make it easier to refactor it. See #430. --- client/layer.cpp | 9 +++++++++ client/layer.h | 13 +++++++++---- client/tilespec.cpp | 4 +--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/client/layer.cpp b/client/layer.cpp index ab530f0279..e281ce3403 100644 --- a/client/layer.cpp +++ b/client/layer.cpp @@ -14,6 +14,15 @@ #include "tilespec.h" #include "layer.h" +#include "log.h" + +drawn_sprite::drawn_sprite(const struct tileset *ts, const QPixmap *sprite, + bool foggable, int offset_x, int offset_y) + : sprite(sprite), foggable(foggable && tileset_use_hard_coded_fog(ts)), + offset_x(offset_x), offset_y(offset_y) +{ + fc_assert(sprite); +} namespace freeciv { diff --git a/client/layer.h b/client/layer.h index 596132a5b2..81dd9a3ebc 100644 --- a/client/layer.h +++ b/client/layer.h @@ -16,6 +16,7 @@ class QPixmap; struct city; +struct tileset; struct unit; struct unit_type; @@ -43,8 +44,14 @@ struct tile_corner { }; struct drawn_sprite { - bool foggable; // Set to FALSE for sprites that are never fogged. - QPixmap *sprite; + explicit drawn_sprite(const tileset *ts, const QPixmap *sprite, + bool foggable = true, int offset_x = 0, + int offset_y = 0); + drawn_sprite(const drawn_sprite &other) = default; + drawn_sprite(drawn_sprite &&other) = default; + + const QPixmap *sprite; + bool foggable; // Set to FALSE for sprites that are never fogged. int offset_x, offset_y; // offset from tile origin }; @@ -114,8 +121,6 @@ enum layer_category { LAYER_CATEGORY_UNIT // Render units only }; -struct tileset; - namespace freeciv { /** diff --git a/client/tilespec.cpp b/client/tilespec.cpp index 61cfbd851e..c19ee15369 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -4172,9 +4172,7 @@ static QPixmap *get_unit_nation_flag_sprite(const struct tileset *t, #define FULL_TILE_Y_OFFSET (t->normal_tile_height - t->full_tile_height) #define ADD_SPRITE(s, draw_fog, x_offset, y_offset) \ - (fc_assert(s != NULL), \ - sprs.emplace_back(drawn_sprite{(draw_fog && t->fogstyle == FOG_AUTO), s, \ - x_offset, y_offset})) + sprs.emplace_back(t, s, draw_fog, x_offset, y_offset) #define ADD_SPRITE_SIMPLE(s) ADD_SPRITE(s, true, 0, 0) #define ADD_SPRITE_FULL(s) \ ADD_SPRITE(s, true, FULL_TILE_X_OFFSET, FULL_TILE_Y_OFFSET) From e82f55046d781462b8e329a21ff32d4b3a5fd3c9 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 25 Apr 2021 03:03:32 +0200 Subject: [PATCH 09/24] Eliminate ADD_SPRITE and ADD_SPRITE_SIMPLE They are now simple enough to be replaced by direct calls to sprs.emplace_back(). Of the old ADD_SPRITE macros, only ADD_SPRITE_FULL is left. See #430. --- client/tilespec.cpp | 303 +++++++++++++++++++++++--------------------- 1 file changed, 156 insertions(+), 147 deletions(-) diff --git a/client/tilespec.cpp b/client/tilespec.cpp index c19ee15369..c9d4afb833 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -4170,12 +4170,8 @@ static QPixmap *get_unit_nation_flag_sprite(const struct tileset *t, #define FULL_TILE_X_OFFSET ((t->normal_tile_width - t->full_tile_width) / 2) #define FULL_TILE_Y_OFFSET (t->normal_tile_height - t->full_tile_height) - -#define ADD_SPRITE(s, draw_fog, x_offset, y_offset) \ - sprs.emplace_back(t, s, draw_fog, x_offset, y_offset) -#define ADD_SPRITE_SIMPLE(s) ADD_SPRITE(s, true, 0, 0) #define ADD_SPRITE_FULL(s) \ - ADD_SPRITE(s, true, FULL_TILE_X_OFFSET, FULL_TILE_Y_OFFSET) + sprs.emplace_back(t, s, true, FULL_TILE_X_OFFSET, FULL_TILE_Y_OFFSET) /** Assemble some data that is used in building the tile sprite arrays. @@ -4224,8 +4220,8 @@ static void fill_unit_type_sprite_array(const struct tileset *t, { QPixmap *uspr = get_unittype_sprite(t, putype, facing); - ADD_SPRITE(uspr, true, FULL_TILE_X_OFFSET + t->unit_offset_x, - FULL_TILE_Y_OFFSET + t->unit_offset_y); + sprs.emplace_back(t, uspr, true, FULL_TILE_X_OFFSET + t->unit_offset_x, + FULL_TILE_Y_OFFSET + t->unit_offset_y); } /** @@ -4241,9 +4237,9 @@ static void fill_unit_sprite_array(const struct tileset *t, if (backdrop) { if (!gui_options.solid_color_behind_units) { - ADD_SPRITE(get_unit_nation_flag_sprite(t, punit), true, - FULL_TILE_X_OFFSET + t->unit_flag_offset_x, - FULL_TILE_Y_OFFSET + t->unit_flag_offset_y); + sprs.emplace_back(t, get_unit_nation_flag_sprite(t, punit), true, + FULL_TILE_X_OFFSET + t->unit_flag_offset_x, + FULL_TILE_Y_OFFSET + t->unit_flag_offset_y); } else { // Taken care of in the LAYER_BACKGROUND. } @@ -4317,8 +4313,9 @@ static void fill_unit_sprite_array(const struct tileset *t, } if (s != NULL) { - ADD_SPRITE(s, true, FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back(t, s, true, + FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); } } @@ -4345,8 +4342,8 @@ static void fill_unit_sprite_array(const struct tileset *t, } if (s != NULL) { - ADD_SPRITE(s, true, FULL_TILE_X_OFFSET + offset_x, - FULL_TILE_Y_OFFSET + offset_y); + sprs.emplace_back(t, s, true, FULL_TILE_X_OFFSET + offset_x, + FULL_TILE_Y_OFFSET + offset_y); } } @@ -4354,19 +4351,19 @@ static void fill_unit_sprite_array(const struct tileset *t, if (punit->orders.repeat) { ADD_SPRITE_FULL(t->sprites.unit.patrol); } else if (punit->activity != ACTIVITY_IDLE) { - ADD_SPRITE_SIMPLE(t->sprites.unit.connect); + sprs.emplace_back(t, t->sprites.unit.connect); } else { - ADD_SPRITE(t->sprites.unit.go_to, true, - FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back(t, t->sprites.unit.go_to, true, + FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); } } if (t->sprites.unit.action_decision_want != NULL && should_ask_server_for_actions(punit)) { - ADD_SPRITE(t->sprites.unit.action_decision_want, true, - FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back(t, t->sprites.unit.action_decision_want, true, + FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); } if (punit->battlegroup != BATTLEGROUP_NONE) { @@ -4440,7 +4437,8 @@ static void fill_road_corner_sprites(const struct tileset *t, && (road_near[cwdir] && road_near[ccwdir] && !(hider_near[cwdir] && hider_near[ccwdir])) && !(road && road_near[dir] && !(hider && hider_near[dir]))) { - ADD_SPRITE_SIMPLE(t->sprites.extras[extra_idx].u.road.corner[dir]); + sprs.emplace_back(t, + t->sprites.extras[extra_idx].u.road.corner[dir]); } } } @@ -4583,7 +4581,8 @@ static void fill_road_sprite_array(const struct tileset *t, if (road) { for (i = 0; i < t->num_valid_tileset_dirs; i++) { if (draw_road[t->valid_tileset_dirs[i]]) { - ADD_SPRITE_SIMPLE(t->sprites.extras[extra_idx].u.road.ru.dir[i]); + sprs.emplace_back(t, + t->sprites.extras[extra_idx].u.road.ru.dir[i]); } } } @@ -4612,12 +4611,12 @@ static void fill_road_sprite_array(const struct tileset *t, /* Draw the cardinal/even roads first. */ if (road_even_tileno != 0) { - ADD_SPRITE_SIMPLE(t->sprites.extras[extra_idx] - .u.road.ru.combo.even[road_even_tileno]); + sprs.emplace_back(t, t->sprites.extras[extra_idx] + .u.road.ru.combo.even[road_even_tileno]); } if (road_odd_tileno != 0) { - ADD_SPRITE_SIMPLE(t->sprites.extras[extra_idx] - .u.road.ru.combo.odd[road_odd_tileno]); + sprs.emplace_back(t, t->sprites.extras[extra_idx] + .u.road.ru.combo.odd[road_odd_tileno]); } } } else if (extrastyle == ESTYLE_ROAD_ALL_COMBINED) { @@ -4638,8 +4637,8 @@ static void fill_road_sprite_array(const struct tileset *t, } if (road_tileno != 0 || draw_single_road) { - ADD_SPRITE_SIMPLE( - t->sprites.extras[extra_idx].u.road.ru.total[road_tileno]); + sprs.emplace_back( + t, t->sprites.extras[extra_idx].u.road.ru.total[road_tileno]); } } } else { @@ -4651,7 +4650,7 @@ static void fill_road_sprite_array(const struct tileset *t, if (extrastyle == ESTYLE_ROAD_ALL_SEPARATE || extrastyle == ESTYLE_ROAD_PARITY_COMBINED) { if (draw_single_road) { - ADD_SPRITE_SIMPLE(t->sprites.extras[extra_idx].u.road.isolated); + sprs.emplace_back(t, t->sprites.extras[extra_idx].u.road.isolated); } } } @@ -4715,7 +4714,7 @@ static void fill_irrigation_sprite_array(const struct tileset *t, if (!hidden) { int idx = get_irrigation_index(t, pextra, textras_near); - ADD_SPRITE_SIMPLE(t->sprites.extras[eidx].u.cardinals[idx]); + sprs.emplace_back(t, t->sprites.extras[eidx].u.cardinals[idx]); } } } @@ -4764,9 +4763,9 @@ static void fill_city_overlays_sprite_array(const struct tileset *t, int idx = pcity->client.color_index % NUM_CITY_COLORS; if (NULL != pwork && pwork == pcity) { - ADD_SPRITE_SIMPLE(t->sprites.city.worked_tile_overlay.p[idx]); + sprs.emplace_back(t, t->sprites.city.worked_tile_overlay.p[idx]); } else if (city_can_work_tile(pcity, ptile)) { - ADD_SPRITE_SIMPLE(t->sprites.city.unworked_tile_overlay.p[idx]); + sprs.emplace_back(t, t->sprites.city.unworked_tile_overlay.p[idx]); } } else if (NULL != pwork && pwork == pcity && (citymode || gui_options.draw_city_output @@ -4780,15 +4779,16 @@ static void fill_city_overlays_sprite_array(const struct tileset *t, int food = city_tile_output_now(pcity, ptile, O_FOOD); food = CLIP(0, food / game.info.granularity, t->sprites.city.tile_foodnum.size() - 1); - ADD_SPRITE( - const_cast(&t->sprites.city.tile_foodnum[food]), true, - ox, oy); + sprs.emplace_back( + t, const_cast(&t->sprites.city.tile_foodnum[food]), + true, ox, oy); } if (!t->sprites.city.tile_shieldnum.empty()) { int shields = city_tile_output_now(pcity, ptile, O_SHIELD); shields = CLIP(0, shields / game.info.granularity, t->sprites.city.tile_shieldnum.size() - 1); - ADD_SPRITE( + sprs.emplace_back( + t, const_cast(&t->sprites.city.tile_shieldnum[shields]), true, ox, oy); } @@ -4796,8 +4796,8 @@ static void fill_city_overlays_sprite_array(const struct tileset *t, int trade = city_tile_output_now(pcity, ptile, O_TRADE); trade = CLIP(0, trade / game.info.granularity, t->sprites.city.tile_tradenum.size() - 1); - ADD_SPRITE( - const_cast(&t->sprites.city.tile_tradenum[trade]), + sprs.emplace_back( + t, const_cast(&t->sprites.city.tile_tradenum[trade]), true, ox, oy); } } @@ -4805,7 +4805,7 @@ static void fill_city_overlays_sprite_array(const struct tileset *t, // Add citymap overlay for a unit. int idx = psettler->client.color_index % NUM_CITY_COLORS; - ADD_SPRITE_SIMPLE(t->sprites.city.unworked_tile_overlay.p[idx]); + sprs.emplace_back(t, t->sprites.city.unworked_tile_overlay.p[idx]); } } @@ -4840,8 +4840,9 @@ static void fill_terrain_sprite_blending(const struct tileset *t, continue; } - ADD_SPRITE(t->sprites.drawing[terrain_index(other)]->blend[dir], true, - offsets[dir][0], offsets[dir][1]); + sprs.emplace_back(t, + t->sprites.drawing[terrain_index(other)]->blend[dir], + true, offsets[dir][0], offsets[dir][1]); } } @@ -4859,7 +4860,7 @@ static void fill_fog_sprite_array(const struct tileset *t, && NULL != ptile && TILE_KNOWN_UNSEEN == client_tile_get_known(ptile)) { // With FOG_AUTO, fog is done this way. - ADD_SPRITE_SIMPLE(t->sprites.tx.fog); + sprs.emplace_back(t, t->sprites.tx.fog); } if (t->darkness_style == DARKNESS_CORNER && pcorner @@ -4891,7 +4892,7 @@ static void fill_fog_sprite_array(const struct tileset *t, } if (t->sprites.tx.fullfog[tileno]) { - ADD_SPRITE_SIMPLE(t->sprites.tx.fullfog[tileno]); + sprs.emplace_back(t, t->sprites.tx.fullfog[tileno]); } } } @@ -4933,7 +4934,7 @@ static void fill_terrain_sprite_array( ox += FULL_TILE_X_OFFSET; oy += FULL_TILE_Y_OFFSET; } - ADD_SPRITE(dlp->base.p[count], true, ox, oy); + sprs.emplace_back(t, dlp->base.p[count], true, ox, oy); } break; } @@ -4953,7 +4954,7 @@ static void fill_terrain_sprite_array( ox += FULL_TILE_X_OFFSET; oy += FULL_TILE_Y_OFFSET; } - ADD_SPRITE(dlp->match[tileno], true, ox, oy); + sprs.emplace_back(t, dlp->match[tileno], true, ox, oy); break; } case MATCH_PAIR: @@ -5028,7 +5029,7 @@ static void fill_terrain_sprite_array( s = dlp->cells[array_index]; if (s) { - ADD_SPRITE(s, true, x, y); + sprs.emplace_back(t, s, true, x, y); } } break; @@ -5062,15 +5063,15 @@ static void fill_terrain_sprite_darkness(struct tileset *t, int offsets[4][2] = {{W / 2, 0}, {0, H / 2}, {W / 2, H / 2}, {0, 0}}; if (UNKNOWN(static_cast(DIR4_TO_DIR8[i]))) { - ADD_SPRITE(t->sprites.tx.darkness[i], true, offsets[i][0], - offsets[i][1]); + sprs.emplace_back(t, t->sprites.tx.darkness[i], true, offsets[i][0], + offsets[i][1]); } } break; case DARKNESS_CARD_SINGLE: for (i = 0; i < t->num_cardinal_tileset_dirs; i++) { if (UNKNOWN(t->cardinal_tileset_dirs[i])) { - ADD_SPRITE_SIMPLE(t->sprites.tx.darkness[i]); + sprs.emplace_back(t, t->sprites.tx.darkness[i]); } } break; @@ -5088,7 +5089,7 @@ static void fill_terrain_sprite_darkness(struct tileset *t, } if (tileno != 0) { - ADD_SPRITE_SIMPLE(t->sprites.tx.darkness[tileno]); + sprs.emplace_back(t, t->sprites.tx.darkness[tileno]); } break; case DARKNESS_CORNER: @@ -5122,7 +5123,7 @@ static void fill_terrain_sprite_layer(struct tileset *t, if (ptile->spec_sprite && (sprite = load_sprite(t, ptile->spec_sprite, true, false))) { if (l == 0) { - ADD_SPRITE_SIMPLE(sprite); + sprs.emplace_back(t, sprite); } return; } @@ -5217,33 +5218,33 @@ static void fill_grid_sprite_array(const struct tileset *t, // Draw city grid for main citymap if (tile && citymode && city_base_to_city_map(&dummy_x, &dummy_y, citymode, tile)) { - ADD_SPRITE_SIMPLE(t->sprites.grid.selected[pedge->type]); + sprs.emplace_back(t, t->sprites.grid.selected[pedge->type]); } } if (mapdeco_is_highlight_set(pedge->tile[0]) || mapdeco_is_highlight_set(pedge->tile[1])) { - ADD_SPRITE_SIMPLE(t->sprites.grid.selected[pedge->type]); + sprs.emplace_back(t, t->sprites.grid.selected[pedge->type]); } else if (!gui_options.draw_terrain && gui_options.draw_coastline && pedge->tile[0] && pedge->tile[1] && known[0] && known[1] && (is_ocean_tile(pedge->tile[0]) ^ is_ocean_tile(pedge->tile[1]))) { - ADD_SPRITE_SIMPLE(t->sprites.grid.coastline[pedge->type]); + sprs.emplace_back(t, t->sprites.grid.coastline[pedge->type]); } else { if (gui_options.draw_map_grid) { if (worked[0] || worked[1]) { - ADD_SPRITE_SIMPLE(t->sprites.grid.worked[pedge->type]); + sprs.emplace_back(t, t->sprites.grid.worked[pedge->type]); } else if (city[0] || city[1]) { - ADD_SPRITE_SIMPLE(t->sprites.grid.city[pedge->type]); + sprs.emplace_back(t, t->sprites.grid.city[pedge->type]); } else if (known[0] || known[1]) { - ADD_SPRITE_SIMPLE(t->sprites.grid.main[pedge->type]); + sprs.emplace_back(t, t->sprites.grid.main[pedge->type]); } } if (gui_options.draw_city_outlines) { if (XOR(city[0], city[1])) { - ADD_SPRITE_SIMPLE(t->sprites.grid.city[pedge->type]); + sprs.emplace_back(t, t->sprites.grid.city[pedge->type]); } if (XOR(unit[0], unit[1])) { - ADD_SPRITE_SIMPLE(t->sprites.grid.worked[pedge->type]); + sprs.emplace_back(t, t->sprites.grid.worked[pedge->type]); } } } @@ -5256,13 +5257,13 @@ static void fill_grid_sprite_array(const struct tileset *t, if (owner0 != owner1) { if (owner0) { int plrid = player_index(owner0); - ADD_SPRITE_SIMPLE( - t->sprites.player[plrid].grid_borders[pedge->type][0]); + sprs.emplace_back( + t, t->sprites.player[plrid].grid_borders[pedge->type][0]); } if (owner1) { int plrid = player_index(owner1); - ADD_SPRITE_SIMPLE( - t->sprites.player[plrid].grid_borders[pedge->type][1]); + sprs.emplace_back( + t, t->sprites.player[plrid].grid_borders[pedge->type][1]); } } } @@ -5273,7 +5274,7 @@ static void fill_grid_sprite_array(const struct tileset *t, // test to ensure valid coordinates? && city_base_to_city_map(&cx, &cy, citymode, ptile) && !client_city_can_work_tile(citymode, ptile)) { - ADD_SPRITE_SIMPLE(t->sprites.grid.unavailable); + sprs.emplace_back(t, t->sprites.grid.unavailable); } if (gui_options.draw_native && citymode == NULL) { @@ -5291,9 +5292,9 @@ static void fill_grid_sprite_array(const struct tileset *t, if (!native) { if (t->sprites.grid.nonnative != NULL) { - ADD_SPRITE_SIMPLE(t->sprites.grid.nonnative); + sprs.emplace_back(t, t->sprites.grid.nonnative); } else { - ADD_SPRITE_SIMPLE(t->sprites.grid.unavailable); + sprs.emplace_back(t, t->sprites.grid.unavailable); } } } @@ -5322,20 +5323,20 @@ static void fill_goto_sprite_array(const struct tileset *t, sprite = t->sprites.path.s[state].specific; if (sprite != NULL) { - ADD_SPRITE(sprite, false, 0, 0); + sprs.emplace_back(t, sprite, false, 0, 0); } sprite = t->sprites.path.s[state].turns[length % 10]; - ADD_SPRITE_SIMPLE(sprite); + sprs.emplace_back(t, sprite); if (length >= 10) { sprite = t->sprites.path.s[state].turns_tens[(length / 10) % 10]; - ADD_SPRITE_SIMPLE(sprite); + sprs.emplace_back(t, sprite); if (length >= 100) { sprite = t->sprites.path.s[state].turns_hundreds[(length / 100) % 10]; if (sprite != NULL) { - ADD_SPRITE_SIMPLE(sprite); + sprs.emplace_back(t, sprite); if (length >= 1000) { warn = true; } @@ -5347,7 +5348,7 @@ static void fill_goto_sprite_array(const struct tileset *t, } if (waypoint) { - ADD_SPRITE(t->sprites.path.waypoint, false, 0, 0); + sprs.emplace_back(t, t->sprites.path.waypoint, false, 0, 0); } if (warn) { @@ -5489,9 +5490,10 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, } } if (owner) { - ADD_SPRITE_SIMPLE(t->sprites.player[player_index(owner)].background); + sprs.emplace_back(t, + t->sprites.player[player_index(owner)].background); } else if (ptile && !gui_options.draw_terrain) { - ADD_SPRITE_SIMPLE(t->sprites.background.graphic); + sprs.emplace_back(t, t->sprites.background.graphic); } break; @@ -5532,8 +5534,8 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, int idx = extra_index(priver); if (BV_ISSET(textras_near[didx], idx)) { - ADD_SPRITE_SIMPLE( - t->sprites.extras[idx].u.road.ru.rivers.outlet[dir]); + sprs.emplace_back( + t, t->sprites.extras[idx].u.road.ru.rivers.outlet[dir]); } } extra_type_list_iterate_end; @@ -5561,8 +5563,8 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, } } - ADD_SPRITE_SIMPLE( - t->sprites.extras[idx].u.road.ru.rivers.spec[tileno]); + sprs.emplace_back( + t, t->sprites.extras[idx].u.road.ru.rivers.spec[tileno]); } } extra_type_list_iterate_end; @@ -5646,8 +5648,8 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, extra_type_list_iterate_end; if (!hidden) { - ADD_SPRITE_SIMPLE( - t->sprites.extras[extra_index(pextra)].u.single); + sprs.emplace_back( + t, t->sprites.extras[extra_index(pextra)].u.single); } } } @@ -5665,17 +5667,17 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, // City. Some city sprites are drawn later. if (pcity && gui_options.draw_cities) { if (!citybar_painter::current()->has_flag()) { - ADD_SPRITE(get_city_flag_sprite(t, pcity), true, - FULL_TILE_X_OFFSET + t->city_flag_offset_x, - FULL_TILE_Y_OFFSET + t->city_flag_offset_y); + sprs.emplace_back(t, get_city_flag_sprite(t, pcity), true, + FULL_TILE_X_OFFSET + t->city_flag_offset_x, + FULL_TILE_Y_OFFSET + t->city_flag_offset_y); } /* For iso-view the city.wall graphics include the full city, whereas * for non-iso view they are an overlay on top of the base city * graphic. */ if (t->type == TS_OVERHEAD || pcity->client.walls <= 0) { - ADD_SPRITE(get_city_sprite(t->sprites.city.tile, pcity), true, - FULL_TILE_X_OFFSET + t->city_offset_x, - FULL_TILE_Y_OFFSET + t->city_offset_y); + sprs.emplace_back(t, get_city_sprite(t->sprites.city.tile, pcity), + true, FULL_TILE_X_OFFSET + t->city_offset_x, + FULL_TILE_Y_OFFSET + t->city_offset_y); } if (t->type == TS_ISOMETRIC && pcity->client.walls > 0) { struct city_sprite *cspr = @@ -5693,14 +5695,16 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, } if (spr != NULL) { - ADD_SPRITE(spr, true, FULL_TILE_X_OFFSET + t->city_offset_x, - FULL_TILE_Y_OFFSET + t->city_offset_y); + sprs.emplace_back(t, spr, true, + FULL_TILE_X_OFFSET + t->city_offset_x, + FULL_TILE_Y_OFFSET + t->city_offset_y); } } if (!citybar_painter::current()->has_units()) { - ADD_SPRITE(get_city_sprite(t->sprites.city.occupied, pcity), true, - FULL_TILE_X_OFFSET + t->occupied_offset_x, - FULL_TILE_Y_OFFSET + t->occupied_offset_y); + sprs.emplace_back(t, + get_city_sprite(t->sprites.city.occupied, pcity), + true, FULL_TILE_X_OFFSET + t->occupied_offset_x, + FULL_TILE_Y_OFFSET + t->occupied_offset_y); } if (t->type == TS_OVERHEAD && pcity->client.walls > 0) { struct city_sprite *cspr = @@ -5771,8 +5775,8 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, extra_type_list_iterate_end; if (!hidden) { - ADD_SPRITE_SIMPLE( - t->sprites.extras[extra_index(pextra)].u.single); + sprs.emplace_back( + t, t->sprites.extras[extra_index(pextra)].u.single); } } } @@ -5789,8 +5793,8 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, if (ptile && unit_is_in_focus(punit) && t->sprites.unit.select[0]) { /* Special case for drawing the selection rectangle. The blinking * unit is handled separately, inside get_drawable_unit(). */ - ADD_SPRITE(t->sprites.unit.select[focus_unit_state], true, - t->select_offset_x, t->select_offset_y); + sprs.emplace_back(t, t->sprites.unit.select[focus_unit_state], true, + t->select_offset_x, t->select_offset_y); } fill_unit_sprite_array(t, sprs, punit, stacked, backdrop); @@ -5857,9 +5861,10 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, extra_type_list_iterate_end; if (show_flag) { - ADD_SPRITE(get_nation_flag_sprite(t, nation_of_player(eowner)), - true, FULL_TILE_X_OFFSET + t->city_flag_offset_x, - FULL_TILE_Y_OFFSET + t->city_flag_offset_y); + sprs.emplace_back( + t, get_nation_flag_sprite(t, nation_of_player(eowner)), true, + FULL_TILE_X_OFFSET + t->city_flag_offset_x, + FULL_TILE_Y_OFFSET + t->city_flag_offset_y); } } } @@ -5877,20 +5882,20 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, bool warn = false; unsigned int size = city_size_get(pcity); - ADD_SPRITE(t->sprites.city.size[size % 10], false, - FULL_TILE_X_OFFSET + t->city_size_offset_x, - FULL_TILE_Y_OFFSET + t->city_size_offset_y); + sprs.emplace_back(t, t->sprites.city.size[size % 10], false, + FULL_TILE_X_OFFSET + t->city_size_offset_x, + FULL_TILE_Y_OFFSET + t->city_size_offset_y); if (10 <= size) { - ADD_SPRITE(t->sprites.city.size_tens[(size / 10) % 10], false, - FULL_TILE_X_OFFSET + t->city_size_offset_x, - FULL_TILE_Y_OFFSET + t->city_size_offset_y); + sprs.emplace_back(t, t->sprites.city.size_tens[(size / 10) % 10], + false, FULL_TILE_X_OFFSET + t->city_size_offset_x, + FULL_TILE_Y_OFFSET + t->city_size_offset_y); if (100 <= size) { QPixmap *sprite = t->sprites.city.size_hundreds[(size / 100) % 10]; if (NULL != sprite) { - ADD_SPRITE(sprite, false, - FULL_TILE_X_OFFSET + t->city_size_offset_x, - FULL_TILE_Y_OFFSET + t->city_size_offset_y); + sprs.emplace_back(t, sprite, false, + FULL_TILE_X_OFFSET + t->city_size_offset_x, + FULL_TILE_Y_OFFSET + t->city_size_offset_y); } else { warn = true; } @@ -5923,7 +5928,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, case LAYER_OVERLAYS: fill_city_overlays_sprite_array(t, sprs, ptile, citymode); if (mapdeco_is_crosshair_set(ptile)) { - ADD_SPRITE_SIMPLE(t->sprites.user.attention); + sprs.emplace_back(t, t->sprites.user.attention); } break; @@ -5946,51 +5951,55 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, switch (ptask->act) { case ACTIVITY_MINE: if (ptask->tgt == NULL) { - ADD_SPRITE(t->sprites.unit.plant, true, - FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back(t, t->sprites.unit.plant, true, + FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); } else { - ADD_SPRITE(t->sprites.extras[extra_index(ptask->tgt)].activity, - true, FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back( + t, t->sprites.extras[extra_index(ptask->tgt)].activity, + true, FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); } break; case ACTIVITY_PLANT: - ADD_SPRITE(t->sprites.unit.plant, true, - FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back(t, t->sprites.unit.plant, true, + FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); break; case ACTIVITY_IRRIGATE: if (ptask->tgt == NULL) { - ADD_SPRITE(t->sprites.unit.irrigate, true, - FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back(t, t->sprites.unit.irrigate, true, + FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); } else { - ADD_SPRITE(t->sprites.extras[extra_index(ptask->tgt)].activity, - true, FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back( + t, t->sprites.extras[extra_index(ptask->tgt)].activity, + true, FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); } break; case ACTIVITY_CULTIVATE: - ADD_SPRITE(t->sprites.unit.irrigate, true, - FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back(t, t->sprites.unit.irrigate, true, + FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); break; case ACTIVITY_GEN_ROAD: - ADD_SPRITE(t->sprites.extras[extra_index(ptask->tgt)].activity, - true, FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back( + t, t->sprites.extras[extra_index(ptask->tgt)].activity, true, + FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); break; case ACTIVITY_TRANSFORM: - ADD_SPRITE(t->sprites.unit.transform, true, - FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back(t, t->sprites.unit.transform, true, + FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); break; case ACTIVITY_POLLUTION: case ACTIVITY_FALLOUT: - ADD_SPRITE(t->sprites.extras[extra_index(ptask->tgt)].rmact, - true, FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back( + t, t->sprites.extras[extra_index(ptask->tgt)].rmact, true, + FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); break; default: break; @@ -6005,12 +6014,12 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, if (ptile && editor_is_active()) { if (editor_tile_is_selected(ptile)) { int color = 2 % tileset_num_city_colors(tileset); - ADD_SPRITE_SIMPLE(t->sprites.city.unworked_tile_overlay.p[color]); + sprs.emplace_back(t, t->sprites.city.unworked_tile_overlay.p[color]); } if (NULL != map_startpos_get(ptile)) { // FIXME: Use a more representative sprite. - ADD_SPRITE_SIMPLE(t->sprites.user.attention); + sprs.emplace_back(t, t->sprites.user.attention); } } break; @@ -6020,9 +6029,9 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, const int id = extra_index(ptile->placing); if (t->sprites.extras[id].activity != NULL) { - ADD_SPRITE(t->sprites.extras[id].activity, true, - FULL_TILE_X_OFFSET + t->activity_offset_x, - FULL_TILE_Y_OFFSET + t->activity_offset_y); + sprs.emplace_back(t, t->sprites.extras[id].activity, true, + FULL_TILE_X_OFFSET + t->activity_offset_x, + FULL_TILE_Y_OFFSET + t->activity_offset_y); } } break; @@ -6654,10 +6663,10 @@ fill_basic_extra_sprite_array(const struct tileset *t, switch (t->sprites.extras[idx].extrastyle) { case ESTYLE_SINGLE1: case ESTYLE_SINGLE2: - ADD_SPRITE_SIMPLE(t->sprites.extras[idx].u.single); + sprs.emplace_back(t, t->sprites.extras[idx].u.single); break; case ESTYLE_CARDINALS: - ADD_SPRITE_SIMPLE(t->sprites.extras[idx].u.cardinals[0]); + sprs.emplace_back(t, t->sprites.extras[idx].u.cardinals[0]); break; case ESTYLE_ROAD_ALL_SEPARATE: case ESTYLE_ROAD_PARITY_COMBINED: @@ -6702,21 +6711,21 @@ void fill_basic_road_sprite_array(const struct tileset *t, extrastyle = t->sprites.extras[idx].extrastyle; if (extrastyle == ESTYLE_RIVER) { - ADD_SPRITE_SIMPLE(t->sprites.extras[idx].u.road.ru.rivers.spec[0]); + sprs.emplace_back(t, t->sprites.extras[idx].u.road.ru.rivers.spec[0]); } else { for (i = 0; i < t->num_valid_tileset_dirs; i++) { if (!t->valid_tileset_dirs[i]) { continue; } if (extrastyle == ESTYLE_ROAD_ALL_SEPARATE) { - ADD_SPRITE_SIMPLE(t->sprites.extras[idx].u.road.ru.dir[i]); + sprs.emplace_back(t, t->sprites.extras[idx].u.road.ru.dir[i]); } else if (extrastyle == ESTYLE_ROAD_PARITY_COMBINED) { if ((i % 2) == 0) { - ADD_SPRITE_SIMPLE( - t->sprites.extras[idx].u.road.ru.combo.even[1 << (i / 2)]); + sprs.emplace_back( + t, t->sprites.extras[idx].u.road.ru.combo.even[1 << (i / 2)]); } } else if (extrastyle == ESTYLE_ROAD_ALL_COMBINED) { - ADD_SPRITE_SIMPLE(t->sprites.extras[idx].u.road.ru.total[1 << i]); + sprs.emplace_back(t, t->sprites.extras[idx].u.road.ru.total[1 << i]); } } } From 82f5a33e3f97829436b675dc37b455f77a13a308 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 25 Apr 2021 19:47:21 +0200 Subject: [PATCH 10/24] Reroute drawing LAYER_BACKGROUND through a class The dedicated class layer_background is for now a tiny wrapper around ::fill_sprite_array that works only with LAYER_BACKGROUND. All the code for LAYER_BACKGROUND will be moved to that class in a later commit. See #430. --- client/CMakeLists.txt | 1 + client/layer.h | 9 +++++++-- client/layer_background.cpp | 28 ++++++++++++++++++++++++++++ client/layer_background.h | 31 +++++++++++++++++++++++++++++++ client/tilespec.cpp | 21 ++++++++++++++++++--- 5 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 client/layer_background.cpp create mode 100644 client/layer_background.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 956898f201..92cf13ab72 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -56,6 +56,7 @@ add_library( goto.cpp helpdata.cpp layer.cpp + layer_background.cpp luaconsole_common.cpp mapctrl_common.cpp mapview_common.cpp diff --git a/client/layer.h b/client/layer.h index 81dd9a3ebc..9d4df048b2 100644 --- a/client/layer.h +++ b/client/layer.h @@ -128,7 +128,9 @@ namespace freeciv { */ class layer { public: - layer(tileset *ts, mapview_layer layer) : m_ts(ts), m_layer(layer) {} + layer(struct tileset *ts, mapview_layer layer) : m_ts(ts), m_layer(layer) + { + } virtual std::vector fill_sprite_array(const tile *ptile, const tile_edge *pedge, @@ -137,8 +139,11 @@ class layer { mapview_layer type() const { return m_layer; } +protected: + struct tileset *tileset() const { return m_ts; } + private: - tileset *m_ts; + struct tileset *m_ts; mapview_layer m_layer; }; diff --git a/client/layer_background.cpp b/client/layer_background.cpp new file mode 100644 index 0000000000..938b713f43 --- /dev/null +++ b/client/layer_background.cpp @@ -0,0 +1,28 @@ +/*__ ___ *************************************** +/ \ / \ Copyright (c) 2021 Freeciv21 contributors. +\_ \ / __/ This file is part of Freeciv21. + _\ \ / /__ Freeciv21 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 3 of the License, + | or (at your option) any later version. + _/ /\ You should have received a copy of the GNU + /o) (o/\ \_ General Public License along with Freeciv21. + \_____/ / If not, see https://www.gnu.org/licenses/. + \____/ ********************************************************/ + +#include "layer_background.h" + +#include "tilespec.h" + +namespace freeciv { + +std::vector layer_background::fill_sprite_array( + const tile *ptile, const tile_edge *pedge, const tile_corner *pcorner, + const unit *punit, const city *pcity, const unit_type *putype) const +{ + return ::fill_sprite_array(tileset(), LAYER_BACKGROUND, ptile, pedge, + pcorner, punit, pcity, putype); +} + +} // namespace freeciv diff --git a/client/layer_background.h b/client/layer_background.h new file mode 100644 index 0000000000..7dbce136a5 --- /dev/null +++ b/client/layer_background.h @@ -0,0 +1,31 @@ +/*__ ___ *************************************** +/ \ / \ Copyright (c) 2021 Freeciv21 contributors. +\_ \ / __/ This file is part of Freeciv21. + _\ \ / /__ Freeciv21 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 3 of the License, + | or (at your option) any later version. + _/ /\ You should have received a copy of the GNU + /o) (o/\ \_ General Public License along with Freeciv21. + \_____/ / If not, see https://www.gnu.org/licenses/. + \____/ ********************************************************/ +#pragma once + +#include "layer.h" + +namespace freeciv { + +class layer_background : public layer { +public: + layer_background(struct tileset *ts) : freeciv::layer(ts, LAYER_BACKGROUND) + { + } + + std::vector + fill_sprite_array(const tile *ptile, const tile_edge *pedge, + const tile_corner *pcorner, const unit *punit, + const city *pcity, const unit_type *putype) const; +}; + +} // namespace freeciv diff --git a/client/tilespec.cpp b/client/tilespec.cpp index c9d4afb833..cd79875ee8 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -78,6 +78,7 @@ #include "editor.h" #include "goto.h" #include "helpdata.h" +#include "layer_background.h" #include "options.h" // for fill_xxx #include "themes_common.h" @@ -2130,13 +2131,27 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, } for (auto layer : order) { - t->layers.push_back(std::make_unique(t, layer)); + switch (layer) { + case LAYER_BACKGROUND: + t->layers.push_back(std::make_unique(t)); + break; + default: + t->layers.push_back(std::make_unique(t, layer)); + break; + } } } else { // There is no layer_order tag in the specfile -> use the default for (i = 0; i < LAYER_COUNT; ++i) { - t->layers.push_back(std::make_unique( - t, static_cast(i))); + auto layer = static_cast(i); + switch (layer) { + case LAYER_BACKGROUND: + t->layers.push_back(std::make_unique(t)); + break; + default: + t->layers.push_back(std::make_unique(t, layer)); + break; + } } } From 4993bd573ce8e5ec2166c9e2a499caae2ccd5eda Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Mon, 26 Apr 2021 03:12:02 +0200 Subject: [PATCH 11/24] Migrate the LAYER_BACKGROUND code to its class This commit migrates the code to draw the LAYER_BACKGROUND sprites to the layer_background class. The feature that allowed to hide the terrain was not migrated; see #449. Closes #449. See #430. --- client/control.cpp | 18 ----- client/control.h | 2 - client/layer.h | 17 +++++ client/layer_background.cpp | 69 ++++++++++++++++- client/layer_background.h | 19 ++++- client/options.cpp | 5 -- client/options.h | 1 - client/packhand.cpp | 2 - client/tilespec.cpp | 144 ++++++++---------------------------- client/tilespec.h | 3 +- 10 files changed, 130 insertions(+), 150 deletions(-) diff --git a/client/control.cpp b/client/control.cpp index f0047e3b3b..6e4298b2eb 100644 --- a/client/control.cpp +++ b/client/control.cpp @@ -2365,19 +2365,6 @@ void request_toggle_city_trade_routes() update_map_canvas_visible(); } -/** - Toggle display of terrain - */ -void request_toggle_terrain() -{ - if (!can_client_change_view()) { - return; - } - - gui_options.draw_terrain ^= 1; - update_map_canvas_visible(); -} - /** Toggle display of coastline */ @@ -3692,11 +3679,6 @@ void key_city_productions_toggle() { request_toggle_city_productions(); } */ void key_city_trade_routes_toggle() { request_toggle_city_trade_routes(); } -/** - Handle user 'toggle terrain display' input - */ -void key_terrain_toggle() { request_toggle_terrain(); } - /** Handle user 'toggle coastline display' input */ diff --git a/client/control.h b/client/control.h index f7f5ea91f0..1f699ab4b1 100644 --- a/client/control.h +++ b/client/control.h @@ -139,7 +139,6 @@ void request_toggle_city_growth(); void request_toggle_city_productions(); void request_toggle_city_buycost(); void request_toggle_city_trade_routes(); -void request_toggle_terrain(); void request_toggle_coastline(); void request_toggle_roads_rails(); void request_toggle_irrigation(); @@ -194,7 +193,6 @@ void key_city_growth_toggle(); void key_city_productions_toggle(); void key_city_buycost_toggle(); void key_city_trade_routes_toggle(); -void key_terrain_toggle(); void key_coastline_toggle(); void key_roads_rails_toggle(); void key_irrigation_toggle(); diff --git a/client/layer.h b/client/layer.h index 9d4df048b2..ed0450ebc7 100644 --- a/client/layer.h +++ b/client/layer.h @@ -16,6 +16,7 @@ class QPixmap; struct city; +struct player; struct tileset; struct unit; struct unit_type; @@ -137,6 +138,22 @@ class layer { const tile_corner *pcorner, const unit *punit, const city *pcity, const unit_type *putype) const; + /** + * Initializes data specific to one player. This allows to cache tiles + * depending on the actual players in a game. + * + * \see free_player + */ + virtual void initialize_player(const player *player) { Q_UNUSED(player); } + + /** + * Frees data initialized by initialize_player. Note that this takes the + * player ID and not a pointer to the player. + * + * \see initialize_player + */ + virtual void free_player(int player_id) { Q_UNUSED(player_id); } + mapview_layer type() const { return m_layer; } protected: diff --git a/client/layer_background.cpp b/client/layer_background.cpp index 938b713f43..ebb87d9a00 100644 --- a/client/layer_background.cpp +++ b/client/layer_background.cpp @@ -13,16 +13,81 @@ #include "layer_background.h" +#include "client_main.h" +#include "colors_common.h" +#include "control.h" // unit_is_in_focus +#include "game.h" +#include "sprite_g.h" #include "tilespec.h" namespace freeciv { +layer_background::layer_background(struct tileset *ts) + : freeciv::layer(ts, LAYER_BACKGROUND) +{ +} + std::vector layer_background::fill_sprite_array( const tile *ptile, const tile_edge *pedge, const tile_corner *pcorner, const unit *punit, const city *pcity, const unit_type *putype) const { - return ::fill_sprite_array(tileset(), LAYER_BACKGROUND, ptile, pedge, - pcorner, punit, pcity, putype); + // Set up background color. + player *owner = nullptr; + if (gui_options.solid_color_behind_units) { + /* Unit drawing is disabled when the view options are turned off, + * but only where we're drawing on the mapview. */ + bool do_draw_unit = + (punit + && (gui_options.draw_units || !ptile + || (gui_options.draw_focus_unit && unit_is_in_focus(punit)))); + if (do_draw_unit) { + owner = unit_owner(punit); + } else if (pcity && gui_options.draw_cities) { + owner = city_owner(pcity); + } + } + if (owner) { + return {drawn_sprite(tileset(), + m_player_background[player_index(owner)].get())}; + } else { + return {}; + } +} + +void layer_background::initialize_player(const player *player) +{ + // Get the color. In pregame, players have no color so we just use red. + const auto color = player_has_color(tileset(), player) + ? *get_player_color(tileset(), player) + : Qt::red; + + auto sprite = create_player_sprite(color); + + const auto id = player_index(player); + m_player_background.at(id) = std::unique_ptr( + crop_sprite(sprite.get(), 0, 0, tileset_tile_width(tileset()), + tileset_tile_height(tileset()), get_mask_sprite(tileset()), + 0, 0, tileset_scale(tileset()), false)); +} + +void layer_background::free_player(int player_id) +{ + m_player_background.at(player_id) = nullptr; +} + +/** + * Create a sprite with the given color. + */ +std::unique_ptr +layer_background::create_player_sprite(const QColor &pcolor) const +{ + if (tileset_scale(tileset()) == 1.0f) { + return std::unique_ptr(create_sprite(128, 64, &pcolor)); + } else { + return std::unique_ptr( + create_sprite(tileset_full_tile_width(tileset()), + tileset_full_tile_height(tileset()), &pcolor)); + } } } // namespace freeciv diff --git a/client/layer_background.h b/client/layer_background.h index 7dbce136a5..ce6ff77700 100644 --- a/client/layer_background.h +++ b/client/layer_background.h @@ -12,20 +12,31 @@ \____/ ********************************************************/ #pragma once +#include "fc_types.h" #include "layer.h" +#include + namespace freeciv { class layer_background : public layer { public: - layer_background(struct tileset *ts) : freeciv::layer(ts, LAYER_BACKGROUND) - { - } + explicit layer_background(struct tileset *ts); std::vector fill_sprite_array(const tile *ptile, const tile_edge *pedge, const tile_corner *pcorner, const unit *punit, - const city *pcity, const unit_type *putype) const; + const city *pcity, + const unit_type *putype) const override; + + void initialize_player(const player *player) override; + void free_player(int player_id) override; + +private: + std::unique_ptr create_player_sprite(const QColor &pcolor) const; + + std::array, MAX_NUM_PLAYER_SLOTS> + m_player_background; }; } // namespace freeciv diff --git a/client/options.cpp b/client/options.cpp index 86a237a9b1..92aa67dd58 100644 --- a/client/options.cpp +++ b/client/options.cpp @@ -152,7 +152,6 @@ struct client_options gui_options = { true, //.draw_city_productions = false, //.draw_city_buycost = false, //.draw_city_trade_routes = - true, //.draw_terrain = false, //.draw_coastline = true, //.draw_roads_rails = true, //.draw_irrigation = @@ -1636,10 +1635,6 @@ static struct client_option client_options[] = { "between cities which have trade routes."), COC_GRAPHICS, GUI_STUB, false, view_option_changed_callback), - GEN_BOOL_OPTION(draw_terrain, N_("Draw the terrain"), - N_("Setting this option will draw the terrain."), - COC_GRAPHICS, GUI_STUB, true, - view_option_changed_callback), GEN_BOOL_OPTION( draw_coastline, N_("Draw the coast line"), N_("Setting this option will draw a line to separate the " diff --git a/client/options.h b/client/options.h index 4810df6a0f..cc54269297 100644 --- a/client/options.h +++ b/client/options.h @@ -125,7 +125,6 @@ struct client_options { bool draw_city_productions; bool draw_city_buycost; bool draw_city_trade_routes; - bool draw_terrain; bool draw_coastline; bool draw_roads_rails; bool draw_irrigation; diff --git a/client/packhand.cpp b/client/packhand.cpp index ee4d77d9e6..2941f3ce44 100644 --- a/client/packhand.cpp +++ b/client/packhand.cpp @@ -4557,8 +4557,6 @@ void handle_ruleset_game(const struct packet_ruleset_game *packet) game.plr_bg_color = rgbcolor_new(packet->background_red, packet->background_green, packet->background_blue); - - tileset_background_init(tileset); } /** diff --git a/client/tilespec.cpp b/client/tilespec.cpp index cd79875ee8..01a484ccd6 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -344,14 +344,9 @@ struct named_sprites { struct { struct sprite_vector overlays; } colors; - struct { - QPixmap *color; // Generic background color - QPixmap *graphic; // Generic background graphic - } background; struct { QPixmap *grid_borders[EDGE_COUNT][2]; QPixmap *color; - QPixmap *background; } player[MAX_NUM_PLAYER_SLOTS]; struct drawing_data *drawing[MAX_NUM_ITEMS]; @@ -1251,9 +1246,6 @@ bool tilespec_reread(const char *new_tileset_name, tileset_load_tiles(tileset); if (game_fully_initialized) { - if (game.client.ruleset_ready) { - tileset_background_init(tileset); - } // else we'll get round to it on PACKET_RULESET_GAME players_iterate(pplayer) { tileset_player_init(tileset, pplayer); } players_iterate_end; boot_help_texts(); // "About Current Tileset" @@ -2579,25 +2571,6 @@ static QPixmap *load_sprite(struct tileset *t, const QString &tag_name, return ss->sprite; } -/** - Create a sprite with the given color and tag. - */ -static QPixmap *create_plr_sprite(QColor *pcolor) -{ - QPixmap *sprite; - - fc_assert_ret_val(pcolor != NULL, NULL); - - if (tileset->scale == 1.0f) { - sprite = create_sprite(128, 64, pcolor); - } else { - sprite = create_sprite(tileset->full_tile_width, - tileset->full_tile_height, pcolor); - } - - return sprite; -} - /** Unloads the sprite. Decrease the reference counter. If the last reference is removed the sprite is freed. @@ -5239,11 +5212,6 @@ static void fill_grid_sprite_array(const struct tileset *t, if (mapdeco_is_highlight_set(pedge->tile[0]) || mapdeco_is_highlight_set(pedge->tile[1])) { sprs.emplace_back(t, t->sprites.grid.selected[pedge->type]); - } else if (!gui_options.draw_terrain && gui_options.draw_coastline - && pedge->tile[0] && pedge->tile[1] && known[0] && known[1] - && (is_ocean_tile(pedge->tile[0]) - ^ is_ocean_tile(pedge->tile[1]))) { - sprs.emplace_back(t, t->sprites.grid.coastline[pedge->type]); } else { if (gui_options.draw_map_grid) { if (worked[0] || worked[1]) { @@ -5460,7 +5428,6 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, bv_extras textras; struct terrain *tterrain_near[8]; struct terrain *pterrain = NULL; - struct player *owner = NULL; /* Unit drawing is disabled when the view options are turned off, * but only where we're drawing on the mapview. */ bool do_draw_unit = @@ -5468,8 +5435,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, && (gui_options.draw_units || !ptile || (gui_options.draw_focus_unit && unit_is_in_focus(punit)))); bool solid_bg = (gui_options.solid_color_behind_units - && (do_draw_unit || (pcity && gui_options.draw_cities) - || (ptile && !gui_options.draw_terrain))); + && (do_draw_unit || (pcity && gui_options.draw_cities))); const city *citymode = is_any_city_dialog_open(); @@ -5496,42 +5462,29 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, switch (layer) { case LAYER_BACKGROUND: - // Set up background color. - if (gui_options.solid_color_behind_units) { - if (do_draw_unit) { - owner = unit_owner(punit); - } else if (pcity && gui_options.draw_cities) { - owner = city_owner(pcity); - } - } - if (owner) { - sprs.emplace_back(t, - t->sprites.player[player_index(owner)].background); - } else if (ptile && !gui_options.draw_terrain) { - sprs.emplace_back(t, t->sprites.background.graphic); - } + fc_assert_ret_val(false, {}); break; case LAYER_TERRAIN1: - if (NULL != pterrain && gui_options.draw_terrain && !solid_bg) { + if (NULL != pterrain && !solid_bg) { fill_terrain_sprite_layer(t, sprs, 0, ptile, pterrain, tterrain_near); } break; case LAYER_DARKNESS: - if (NULL != pterrain && gui_options.draw_terrain && !solid_bg) { + if (NULL != pterrain && !solid_bg) { fill_terrain_sprite_darkness(t, sprs, ptile, tterrain_near); } break; case LAYER_TERRAIN2: - if (NULL != pterrain && gui_options.draw_terrain && !solid_bg) { + if (NULL != pterrain && !solid_bg) { fill_terrain_sprite_layer(t, sprs, 1, ptile, pterrain, tterrain_near); } break; case LAYER_TERRAIN3: - if (NULL != pterrain && gui_options.draw_terrain && !solid_bg) { + if (NULL != pterrain && !solid_bg) { fc_assert(MAX_NUM_LAYERS == 3); fill_terrain_sprite_layer(t, sprs, 2, ptile, pterrain, tterrain_near); } @@ -5539,8 +5492,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, case LAYER_WATER: if (NULL != pterrain) { - if (gui_options.draw_terrain && !solid_bg - && terrain_type_terrain_class(pterrain) == TC_OCEAN) { + if (!solid_bg && terrain_type_terrain_class(pterrain) == TC_OCEAN) { for (dir = 0; dir < t->num_cardinal_tileset_dirs; dir++) { int didx = t->cardinal_tileset_dirs[dir]; @@ -5559,7 +5511,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, fill_irrigation_sprite_array(t, sprs, textras, textras_near, pcity); - if (gui_options.draw_terrain && !solid_bg) { + if (!solid_bg) { extra_type_list_iterate(t->style_lists[ESTYLE_RIVER], priver) { int idx = extra_index(priver); @@ -6262,8 +6214,6 @@ void tileset_free_tiles(struct tileset *t) sprite_vector_free(&t->sprites.nation_flag); sprite_vector_free(&t->sprites.nation_shield); sprite_vector_free(&t->sprites.citybar.occupancy); - - tileset_background_free(t); } /** @@ -6434,6 +6384,14 @@ QPixmap *get_event_sprite(const struct tileset *t, enum event_type event) return t->sprites.events[event]; } +/** + * Return tile mask sprite + */ +QPixmap *get_mask_sprite(const struct tileset *t) +{ + return t->sprites.mask.tile; +} + /** Return a thumbs-up/thumbs-down sprite to show treaty approval or disapproval. @@ -6606,9 +6564,6 @@ void tileset_init(struct tileset *t) t->sprites.city.occupied = NULL; - t->sprites.background.color = NULL; - t->sprites.background.graphic = NULL; - player_slots_iterate(pslot) { int edge, j, id = player_slot_index(pslot); @@ -6620,7 +6575,6 @@ void tileset_init(struct tileset *t) } t->sprites.player[id].color = NULL; - t->sprites.player[id].background = NULL; } player_slots_iterate_end; @@ -6848,22 +6802,18 @@ void tileset_player_init(struct tileset *t, struct player *pplayer) // Free all data before recreating it. tileset_player_free(t, plrid); - if (player_has_color(t, pplayer)) { - t->sprites.player[plrid].color = color = - create_plr_sprite(get_player_color(t, pplayer)); - } else { - /* XXX: if player hasn't been assigned a color, perhaps there's no - * point proceeding with an arbitrary color; this should only happen - * in pregame. Probably blank sprites would be better. */ - - fc_assert_ret(t->sprites.background.color != NULL); - - color = t->sprites.background.color; + for (auto &&layer : t->layers) { + layer->initialize_player(pplayer); } - t->sprites.player[plrid].background = - crop_sprite(color, 0, 0, t->normal_tile_width, t->normal_tile_height, - t->sprites.mask.tile, 0, 0, t->scale, false); + QColor c = Qt::black; + if (player_has_color(t, pplayer)) { + c = *get_player_color(t, pplayer); + } + color = tileset_scale(t) == 1.0f + ? create_sprite(128, 64, &c) + : create_sprite(tileset_full_tile_width(t), + tileset_full_tile_height(t), &c); for (i = 0; i < EDGE_COUNT; i++) { for (j = 0; j < 2; j++) { @@ -6891,14 +6841,14 @@ static void tileset_player_free(struct tileset *t, int plrid) fc_assert_ret(plrid >= 0); fc_assert_ret(plrid < ARRAY_SIZE(t->sprites.player)); + for (auto &&layer : t->layers) { + layer->free_player(plrid); + } + if (t->sprites.player[plrid].color) { free_sprite(t->sprites.player[plrid].color); t->sprites.player[plrid].color = NULL; } - if (t->sprites.player[plrid].background) { - free_sprite(t->sprites.player[plrid].background); - t->sprites.player[plrid].background = NULL; - } for (i = 0; i < EDGE_COUNT; i++) { for (j = 0; j < 2; j++) { @@ -6910,40 +6860,6 @@ static void tileset_player_free(struct tileset *t, int plrid) } } -/** - Setup tiles for the background. - */ -void tileset_background_init(struct tileset *t) -{ - // Free all data before recreating it. - tileset_background_free(t); - - // generate background color - t->sprites.background.color = - create_plr_sprite(ensure_color(game.plr_bg_color)); - - // Chop up and build the background graphics. - t->sprites.background.graphic = crop_sprite( - t->sprites.background.color, 0, 0, t->normal_tile_width, - t->normal_tile_height, t->sprites.mask.tile, 0, 0, t->scale, false); -} - -/** - Free tiles for the background. - */ -void tileset_background_free(struct tileset *t) -{ - if (t->sprites.background.color) { - free_sprite(t->sprites.background.color); - t->sprites.background.color = NULL; - } - - if (t->sprites.background.graphic) { - free_sprite(t->sprites.background.graphic); - t->sprites.background.graphic = NULL; - } -} - /** Reset tileset data specific to ruleset. */ diff --git a/client/tilespec.h b/client/tilespec.h index 4db7a02ed8..876ecb6e9c 100644 --- a/client/tilespec.h +++ b/client/tilespec.h @@ -119,8 +119,6 @@ void tileset_setup_nation_flag(struct tileset *t, void tileset_setup_city_tiles(struct tileset *t, int style); void tileset_player_init(struct tileset *t, struct player *pplayer); -void tileset_background_init(struct tileset *t); -void tileset_background_free(struct tileset *t); // Layer order const std::vector> & @@ -241,6 +239,7 @@ std::vector fill_basic_extra_sprite_array(const struct tileset *t, const struct extra_type *pextra); QPixmap *get_event_sprite(const struct tileset *t, enum event_type event); +QPixmap *get_mask_sprite(const struct tileset *t); QPixmap *tiles_lookup_sprite_tag_alt(struct tileset *t, QtMsgType level, const char *tag, const char *alt, From ec8877f0d88baeb58946ed9848a8529b7dbe229e Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Mon, 26 Apr 2021 03:21:39 +0200 Subject: [PATCH 12/24] Remove extraneous blank lines --- client/tilespec.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/tilespec.cpp b/client/tilespec.cpp index 01a484ccd6..59c1e51268 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -4678,7 +4678,6 @@ static void fill_irrigation_sprite_array(const struct tileset *t, bv_extras *textras_near, const struct city *pcity) { - /* We don't draw the irrigation if there's a city (it just gets overdrawn * anyway, and ends up looking bad). */ if (!(pcity && gui_options.draw_cities)) { @@ -4843,7 +4842,6 @@ static void fill_fog_sprite_array(const struct tileset *t, const struct tile_edge *pedge, const struct tile_corner *pcorner) { - if (t->fogstyle == FOG_SPRITE && gui_options.draw_fog_of_war && NULL != ptile && TILE_KNOWN_UNSEEN == client_tile_get_known(ptile)) { From 7eb092ff52d525536c68effe000d073a9bb76d40 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Thu, 19 Aug 2021 01:19:57 +0200 Subject: [PATCH 13/24] Fix "documentation" of tileset layer "Special1" Extras with style "Single2" are only drawn on layer "Special2" (they don't have two sprites as was implied by the comments). Also remove some extraneous whitespace. --- data/3d.tilespec | 13 ++++++------- data/alio.tilespec | 9 ++++----- data/amplio.tilespec | 9 ++++----- data/amplio2.tilespec | 9 ++++----- data/cimpletoon.tilespec | 11 +++++------ data/hex2t.tilespec | 9 ++++----- data/hexemplio.tilespec | 3 +-- data/isophex.tilespec | 11 +++++------ data/isotrident.tilespec | 11 +++++------ data/toonhex.tilespec | 9 ++++----- data/trident.tilespec | 13 ++++++------- 11 files changed, 48 insertions(+), 59 deletions(-) diff --git a/data/3d.tilespec b/data/3d.tilespec index cfbb608e8f..c3cdfb6419 100644 --- a/data/3d.tilespec +++ b/data/3d.tilespec @@ -14,7 +14,7 @@ priority = 5 summary = _("Basic small overhead tileset.") ;description = "" -; TODO: add more overall information fields on tiles, +; TODO: add more overall information fields on tiles, ; eg, authors, colors, etc. ; What is the primary ruleset this tileset is meant for. @@ -35,7 +35,7 @@ fog_style = "Darkness" ; Was darkness style "CardinalFull" (15 sprites) darkness_style = "Corner" - + ; offset the flags by this amount when drawing units unit_flag_offset_x = 0 unit_flag_offset_y = 0 @@ -45,7 +45,7 @@ city_flag_offset_y = 0 ; offset the city occupied sprite by this amount occupied_offset_x = 0 occupied_offset_y = 0 - + ; offset the units by this amount unit_offset_x = 0 unit_offset_y = 0 @@ -109,8 +109,7 @@ unit_upkeep_small_offset_y = 20 ; "Water", ; All extras with "River" style. ; "Roads", ; All extras with style "RoadAllSeparate", ; ; "RoadParityCombined" or "RoadAllCombined". -; "Special1", ; 1st layer for extras with style "3Layers", "Single2" or -; ; "Single1". +; "Special1", ; 1st layer for extras with style "3Layers" or "Single1". ; "Grid1", ; Grid layer for isometric tilesets. ; "City1", ; City and walls. ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. @@ -131,8 +130,8 @@ unit_upkeep_small_offset_y = 20 ; This is special because it gets freed and reloaded as required: main_intro_file = "misc/intro" -; Below, the graphics spec files; must be somewhere (anywhere) in -; the data path. Order may be important for color allocation on +; Below, the graphics spec files; must be somewhere (anywhere) in +; the data path. Order may be important for color allocation on ; low-color systems, and if there are any duplicate tags (lattermost ; tag is used). files = diff --git a/data/alio.tilespec b/data/alio.tilespec index 8c0a52c1c5..ae9ff24e65 100644 --- a/data/alio.tilespec +++ b/data/alio.tilespec @@ -21,7 +21,7 @@ Tileset containing graphics suitable for playing with alien ruleset.\ ;description = "" -; TODO: add more overall information fields on tiles, +; TODO: add more overall information fields on tiles, ; eg, authors, colors, etc. ; What is the primary ruleset this tileset is meant for. @@ -115,8 +115,7 @@ layer_order = "Water", ; All extras with "River" style. "Roads", ; All extras with style "RoadAllSeparate", ; "RoadParityCombined" or "RoadAllCombined". - "Special1", ; 1st layer for extras with style "3Layers", "Single2" or - ; "Single1". + "Special1", ; 1st layer for extras with style "3Layers" or "Single1". "Grid1", ; Grid layer for isometric tilesets. "City1", ; City and walls. "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. @@ -137,8 +136,8 @@ layer_order = ; This is special because it gets freed and reloaded as required: main_intro_file = "misc/intro" -; Below, the graphics spec files; must be somewhere (anywhere) in -; the data path. Order may be important for color allocation on +; Below, the graphics spec files; must be somewhere (anywhere) in +; the data path. Order may be important for color allocation on ; low-color systems, and if there are any duplicate tags (lattermost ; tag is used). diff --git a/data/amplio.tilespec b/data/amplio.tilespec index 82e09e453d..cb484750ef 100644 --- a/data/amplio.tilespec +++ b/data/amplio.tilespec @@ -15,7 +15,7 @@ priority = 20 summary = _("Older variant of the current default tileset, Amplio2") ;description = "" -; TODO: add more overall information fields on tiles, +; TODO: add more overall information fields on tiles, ; eg, authors, colors, etc. ; What is the primary ruleset this tileset is meant for. @@ -106,8 +106,7 @@ tilelabel_offset_y = 15 ; "Water", ; All extras with "River" style. ; "Roads", ; All extras with style "RoadAllSeparate", ; ; "RoadParityCombined" or "RoadAllCombined". -; "Special1", ; 1st layer for extras with style "3Layers", "Single2" or -; ; "Single1". +; "Special1", ; 1st layer for extras with style "3Layers" or "Single1". ; "Grid1", ; Grid layer for isometric tilesets. ; "City1", ; City and walls. ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. @@ -128,8 +127,8 @@ tilelabel_offset_y = 15 ; This is special because it gets freed and reloaded as required: main_intro_file = "misc/intro" -; Below, the graphics spec files; must be somewhere (anywhere) in -; the data path. Order may be important for color allocation on +; Below, the graphics spec files; must be somewhere (anywhere) in +; the data path. Order may be important for color allocation on ; low-color systems, and if there are any duplicate tags (lattermost ; tag is used). files = diff --git a/data/amplio2.tilespec b/data/amplio2.tilespec index c4404c85be..e99dc3005b 100644 --- a/data/amplio2.tilespec +++ b/data/amplio2.tilespec @@ -15,7 +15,7 @@ priority = 25 summary = _("Large isometric tileset.") ;description = "" -; TODO: add more overall information fields on tiles, +; TODO: add more overall information fields on tiles, ; eg, authors, colors, etc. ; What is the primary ruleset this tileset is meant for. @@ -106,8 +106,7 @@ tilelabel_offset_y = 15 ; "Water", ; All extras with "River" style. ; "Roads", ; All extras with style "RoadAllSeparate", ; ; "RoadParityCombined" or "RoadAllCombined". -; "Special1", ; 1st layer for extras with style "3Layers", "Single2" or -; ; "Single1". +; "Special1", ; 1st layer for extras with style "3Layers" or "Single1". ; "Grid1", ; Grid layer for isometric tilesets. ; "City1", ; City and walls. ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. @@ -128,8 +127,8 @@ tilelabel_offset_y = 15 ; This is special because it gets freed and reloaded as required: main_intro_file = "misc/intro" -; Below, the graphics spec files; must be somewhere (anywhere) in -; the data path. Order may be important for color allocation on +; Below, the graphics spec files; must be somewhere (anywhere) in +; the data path. Order may be important for color allocation on ; low-color systems, and if there are any duplicate tags (lattermost ; tag is used). files = diff --git a/data/cimpletoon.tilespec b/data/cimpletoon.tilespec index 01fbabfeed..8132eb4bb1 100644 --- a/data/cimpletoon.tilespec +++ b/data/cimpletoon.tilespec @@ -18,7 +18,7 @@ direction the unit is facing.\ ") ;description = "" -; TODO: add more overall information fields on tiles, +; TODO: add more overall information fields on tiles, ; eg, authors, colors, etc. ; What is the primary ruleset this tileset is meant for. @@ -37,7 +37,7 @@ is_hex = FALSE ; Blended fog fog_style = "Darkness" darkness_style = "Corner" - + ; offset the flags by this amount when drawing units unit_flag_offset_x = 25 unit_flag_offset_y = 16 @@ -109,8 +109,7 @@ unit_default_orientation = "s" ; "Water", ; All extras with "River" style. ; "Roads", ; All extras with style "RoadAllSeparate", ; ; "RoadParityCombined" or "RoadAllCombined". -; "Special1", ; 1st layer for extras with style "3Layers", "Single2" or -; ; "Single1". +; "Special1", ; 1st layer for extras with style "3Layers" or "Single1". ; "Grid1", ; Grid layer for isometric tilesets. ; "City1", ; City and walls. ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. @@ -131,8 +130,8 @@ unit_default_orientation = "s" ; This is special because it gets freed and reloaded as required: main_intro_file = "misc/intro" -; Below, the graphics spec files; must be somewhere (anywhere) in -; the data path. Order may be important for color allocation on +; Below, the graphics spec files; must be somewhere (anywhere) in +; the data path. Order may be important for color allocation on ; low-color systems, and if there are any duplicate tags (lattermost ; tag is used). files = diff --git a/data/hex2t.tilespec b/data/hex2t.tilespec index 399d8acd01..265dc0769e 100644 --- a/data/hex2t.tilespec +++ b/data/hex2t.tilespec @@ -15,7 +15,7 @@ priority = 5 summary = _("Small hex tileset.") ;description = "" -; TODO: add more overall information fields on tiles, +; TODO: add more overall information fields on tiles, ; eg, authors, colors, etc. ; What is the primary ruleset this tileset is meant for. @@ -109,8 +109,7 @@ tilelabel_offset_y = 28 ; "Water", ; All extras with "River" style. ; "Roads", ; All extras with style "RoadAllSeparate", ; ; "RoadParityCombined" or "RoadAllCombined". -; "Special1", ; 1st layer for extras with style "3Layers", "Single2" or -; ; "Single1". +; "Special1", ; 1st layer for extras with style "3Layers" or "Single1". ; "Grid1", ; Grid layer for isometric tilesets. ; "City1", ; City and walls. ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. @@ -131,8 +130,8 @@ tilelabel_offset_y = 28 ; This is special because it gets freed and reloaded as required: main_intro_file = "misc/intro" -; Below, the graphics spec files; must be somewhere (anywhere) in -; the data path. Order may be important for color allocation on +; Below, the graphics spec files; must be somewhere (anywhere) in +; the data path. Order may be important for color allocation on ; low-color systems, and if there are any duplicate tags (lattermost ; tag is used). files = diff --git a/data/hexemplio.tilespec b/data/hexemplio.tilespec index 4f1da7e513..bdefdab126 100644 --- a/data/hexemplio.tilespec +++ b/data/hexemplio.tilespec @@ -111,8 +111,7 @@ layer_order = "Water", ; All extras with "River" style. "Roads", ; All extras with style "RoadAllSeparate", ; "RoadParityCombined" or "RoadAllCombined". - "Special1", ; 1st layer for extras with style "3Layers", "Single2" or - ; "Single1". + "Special1", ; 1st layer for extras with style "3Layers" or "Single1". "Grid1", ; Grid layer for isometric tilesets. "City1", ; City and walls. "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. diff --git a/data/isophex.tilespec b/data/isophex.tilespec index e921ebb735..b6aeb4bd63 100644 --- a/data/isophex.tilespec +++ b/data/isophex.tilespec @@ -15,7 +15,7 @@ priority = 3 summary = _("Small iso-hex tileset.") ;description = "" -; TODO: add more overall information fields on tiles, +; TODO: add more overall information fields on tiles, ; eg, authors, colors, etc. ; What is the primary ruleset this tileset is meant for. @@ -37,7 +37,7 @@ fog_style = "Auto" ; Was darkness style "IsoRect" (single-sprite) darkness_style = "CardinalSingle" - + ; offset the flags by this amount when drawing units unit_flag_offset_x = 17 unit_flag_offset_y = 11 @@ -109,8 +109,7 @@ tilelabel_offset_y = 10 ; "Water", ; All extras with "River" style. ; "Roads", ; All extras with style "RoadAllSeparate", ; ; "RoadParityCombined" or "RoadAllCombined". -; "Special1", ; 1st layer for extras with style "3Layers", "Single2" or -; ; "Single1". +; "Special1", ; 1st layer for extras with style "3Layers" or "Single1". ; "Grid1", ; Grid layer for isometric tilesets. ; "City1", ; City and walls. ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. @@ -131,8 +130,8 @@ tilelabel_offset_y = 10 ; This is special because it gets freed and reloaded as required: main_intro_file = "misc/intro" -; Below, the graphics spec files; must be somewhere (anywhere) in -; the data path. Order may be important for color allocation on +; Below, the graphics spec files; must be somewhere (anywhere) in +; the data path. Order may be important for color allocation on ; low-color systems, and if there are any duplicate tags (lattermost ; tag is used). files = diff --git a/data/isotrident.tilespec b/data/isotrident.tilespec index ee9d5a9c8f..64bb120f49 100644 --- a/data/isotrident.tilespec +++ b/data/isotrident.tilespec @@ -15,7 +15,7 @@ priority = 10 summary = _("Isometric tileset based on Trident tileset.") ;description = "" -; TODO: add more overall information fields on tiles, +; TODO: add more overall information fields on tiles, ; eg, authors, colors, etc. ; What is the primary ruleset this tileset is meant for. @@ -36,7 +36,7 @@ fog_style = "Darkness" ; Was darkness style "IsoRect" (single-sprite) darkness_style = "Corner" - + ; offset the flags by this amount when drawing units unit_flag_offset_x = 17 unit_flag_offset_y = 11 @@ -108,8 +108,7 @@ tilelabel_offset_y = 10 ; "Water", ; All extras with "River" style. ; "Roads", ; All extras with style "RoadAllSeparate", ; ; "RoadParityCombined" or "RoadAllCombined". -; "Special1", ; 1st layer for extras with style "3Layers", "Single2" or -; ; "Single1". +; "Special1", ; 1st layer for extras with style "3Layers" or "Single1". ; "Grid1", ; Grid layer for isometric tilesets. ; "City1", ; City and walls. ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. @@ -130,8 +129,8 @@ tilelabel_offset_y = 10 ; This is special because it gets freed and reloaded as required: main_intro_file = "misc/intro" -; Below, the graphics spec files; must be somewhere (anywhere) in -; the data path. Order may be important for color allocation on +; Below, the graphics spec files; must be somewhere (anywhere) in +; the data path. Order may be important for color allocation on ; low-color systems, and if there are any duplicate tags (lattermost ; tag is used). files = diff --git a/data/toonhex.tilespec b/data/toonhex.tilespec index 1ae508fdc2..8ae631ed0c 100644 --- a/data/toonhex.tilespec +++ b/data/toonhex.tilespec @@ -19,7 +19,7 @@ summary = _("Large iso-hex tileset combining Hexemplio's terrains \ and Cimpletoon's units with orientation.") ;description = "" -; TODO: add more overall information fields on tiles, +; TODO: add more overall information fields on tiles, ; eg, authors, colors, etc. ; What is the primary ruleset this tileset is meant for. @@ -113,8 +113,7 @@ layer_order = "Water", ; All extras with "River" style. "Roads", ; All extras with style "RoadAllSeparate", ; "RoadParityCombined" or "RoadAllCombined". - "Special1", ; 1st layer for extras with style "3Layers", "Single2" or - ; "Single1". + "Special1", ; 1st layer for extras with style "3Layers" or "Single1". "Grid1", ; Grid layer for isometric tilesets. "City1", ; City and walls. "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. @@ -135,8 +134,8 @@ layer_order = ; This is special because it gets freed and reloaded as required: main_intro_file = "misc/intro" -; Below, the graphics spec files; must be somewhere (anywhere) in -; the data path. Order may be important for color allocation on +; Below, the graphics spec files; must be somewhere (anywhere) in +; the data path. Order may be important for color allocation on ; low-color systems, and if there are any duplicate tags (lattermost ; tag is used). diff --git a/data/trident.tilespec b/data/trident.tilespec index 132f93af42..3161ffc31d 100644 --- a/data/trident.tilespec +++ b/data/trident.tilespec @@ -14,7 +14,7 @@ priority = 5 summary = _("Small overhead tileset.") ;description = "" -; TODO: add more overall information fields on tiles, +; TODO: add more overall information fields on tiles, ; eg, authors, colors, etc. ; What is the primary ruleset this tileset is meant for. @@ -35,7 +35,7 @@ fog_style = "Darkness" ; Was darkness style "CardinalFull" (15 sprites) darkness_style = "Corner" - + ; offset the flags by this amount when drawing units unit_flag_offset_x = 0 unit_flag_offset_y = 0 @@ -45,7 +45,7 @@ city_flag_offset_y = 0 ; offset the city occupied sprite by this amount occupied_offset_x = 0 occupied_offset_y = 0 - + ; offset the units by this amount unit_offset_x = 0 unit_offset_y = 0 @@ -109,8 +109,7 @@ unit_upkeep_small_offset_y = 20 ; "Water", ; All extras with "River" style. ; "Roads", ; All extras with style "RoadAllSeparate", ; ; "RoadParityCombined" or "RoadAllCombined". -; "Special1", ; 1st layer for extras with style "3Layers", "Single2" or -; ; "Single1". +; "Special1", ; 1st layer for extras with style "3Layers" or "Single1". ; "Grid1", ; Grid layer for isometric tilesets. ; "City1", ; City and walls. ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. @@ -131,8 +130,8 @@ unit_upkeep_small_offset_y = 20 ; This is special because it gets freed and reloaded as required: main_intro_file = "misc/intro" -; Below, the graphics spec files; must be somewhere (anywhere) in -; the data path. Order may be important for color allocation on +; Below, the graphics spec files; must be somewhere (anywhere) in +; the data path. Order may be important for color allocation on ; low-color systems, and if there are any duplicate tags (lattermost ; tag is used). files = From ee8383569aba6958a017e28aee2646d15912ba65 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Thu, 19 Aug 2021 03:28:34 +0200 Subject: [PATCH 14/24] Simplify fill_basic_extra_sprite_array Instead of duplicating the drawing logic from fill_sprite_array, call fill_sprite_array with a virtual tile that has only the requested extra set. Adjustments were needed to make fill_sprite_array work with this kind of tiles. As a consequence of this change, fill_basic_extra_sprite_array now returns several sprites for shipped tilesets. The help dialog was adjusted to take them all into account, with the limitation that the first sprite dictates the size of the rest and relative offsets are not supported. --- client/gui-qt/helpdlg.cpp | 11 +- client/mapview_common.cpp | 5 + client/tilespec.cpp | 372 ++++++++++++++------------------------ common/fc_interface.h | 2 +- common/tile.cpp | 13 +- common/tile.h | 4 +- server/maphand.cpp | 4 +- server/maphand.h | 4 +- 8 files changed, 163 insertions(+), 252 deletions(-) diff --git a/client/gui-qt/helpdlg.cpp b/client/gui-qt/helpdlg.cpp index b5e4ed1079..5f2805a0d6 100644 --- a/client/gui-qt/helpdlg.cpp +++ b/client/gui-qt/helpdlg.cpp @@ -11,6 +11,7 @@ // Qt #include #include +#include #include #include #include @@ -253,7 +254,15 @@ void help_dialog::make_tree() case HELP_EXTRA: { pextra = extra_type_by_translated_name(s); auto sprs = fill_basic_extra_sprite_array(tileset, pextra); - spite = sprs.front().sprite; + if (!sprs.empty()) { + QPixmap pix(*sprs.front().sprite); + QPainter p; + p.begin(&pix); + for (std::size_t i = 1; i < sprs.size(); ++i) { + p.drawPixmap(0, 0, *sprs[i].sprite); + } + icon = QIcon(pix); + } } break; case HELP_GOVERNMENT: diff --git a/client/mapview_common.cpp b/client/mapview_common.cpp index 158de4946e..ba6cea7565 100644 --- a/client/mapview_common.cpp +++ b/client/mapview_common.cpp @@ -1877,6 +1877,11 @@ void move_unit_map_canvas(struct unit *punit, struct tile *src_tile, int dx, struct city *find_city_or_settler_near_tile(const struct tile *ptile, struct unit **punit) { + // Rule g + if (tile_virtual_check(ptile)) { + return nullptr; + } + struct city *closest_city; struct city *pcity; struct unit *closest_settler = NULL, *best_settler = NULL; diff --git a/client/tilespec.cpp b/client/tilespec.cpp index 59c1e51268..6ce7cb5a07 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -486,13 +486,6 @@ static void tileset_setup_road(struct tileset *t, struct extra_type *pextra, static bool is_extra_drawing_enabled(struct extra_type *pextra); -static void fill_basic_road_sprite_array(const struct tileset *t, - std::vector &sprs, - const struct extra_type *pextra); -static void fill_basic_base_sprite_array(const struct tileset *t, - std::vector &sprs, - const struct extra_type *pextra); - static void tileset_player_free(struct tileset *t, int plrid); /** @@ -5424,7 +5417,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, int tileno, dir; bv_extras textras_near[8]; bv_extras textras; - struct terrain *tterrain_near[8]; + struct terrain *tterrain_near[8] = {nullptr}; struct terrain *pterrain = NULL; /* Unit drawing is disabled when the view options are turned off, * but only where we're drawing on the mapview. */ @@ -5447,7 +5440,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, || layer == LAYER_ROADS) { build_tile_data(ptile, pterrain, tterrain_near, textras_near); } - } else { + } else if (!tile_virtual_check(ptile)) { qCritical("fill_sprite_array() tile (%d,%d) has no terrain!", TILE_XY(ptile)); } @@ -5506,96 +5499,91 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, extra_type_list_iterate_end; } } + } - fill_irrigation_sprite_array(t, sprs, textras, textras_near, pcity); + fill_irrigation_sprite_array(t, sprs, textras, textras_near, pcity); - if (!solid_bg) { - extra_type_list_iterate(t->style_lists[ESTYLE_RIVER], priver) - { - int idx = extra_index(priver); + if (!solid_bg) { + extra_type_list_iterate(t->style_lists[ESTYLE_RIVER], priver) + { + int idx = extra_index(priver); - if (BV_ISSET(textras, idx)) { - int i; + if (BV_ISSET(textras, idx)) { + int i; - // Draw rivers on top of irrigation. - tileno = 0; - for (i = 0; i < t->num_cardinal_tileset_dirs; i++) { - enum direction8 cdir = t->cardinal_tileset_dirs[i]; + // Draw rivers on top of irrigation. + tileno = 0; + for (i = 0; i < t->num_cardinal_tileset_dirs; i++) { + enum direction8 cdir = t->cardinal_tileset_dirs[i]; - if (terrain_type_terrain_class(tterrain_near[cdir]) == TC_OCEAN - || BV_ISSET(textras_near[cdir], idx)) { - tileno |= 1 << i; - } + if (tterrain_near[cdir] == nullptr + || terrain_type_terrain_class(tterrain_near[cdir]) + == TC_OCEAN + || BV_ISSET(textras_near[cdir], idx)) { + tileno |= 1 << i; } - - sprs.emplace_back( - t, t->sprites.extras[idx].u.road.ru.rivers.spec[tileno]); } + + sprs.emplace_back( + t, t->sprites.extras[idx].u.road.ru.rivers.spec[tileno]); } - extra_type_list_iterate_end; } + extra_type_list_iterate_end; } break; case LAYER_ROADS: - if (NULL != pterrain) { - extra_type_list_iterate(t->style_lists[ESTYLE_ROAD_ALL_SEPARATE], - pextra) - { - if (is_extra_drawing_enabled(pextra)) { - fill_road_sprite_array(t, pextra, sprs, textras, textras_near, - tterrain_near, pcity); - } + extra_type_list_iterate(t->style_lists[ESTYLE_ROAD_ALL_SEPARATE], pextra) + { + if (is_extra_drawing_enabled(pextra)) { + fill_road_sprite_array(t, pextra, sprs, textras, textras_near, + tterrain_near, pcity); } - extra_type_list_iterate_end; - extra_type_list_iterate(t->style_lists[ESTYLE_ROAD_PARITY_COMBINED], - pextra) - { - if (is_extra_drawing_enabled(pextra)) { - fill_road_sprite_array(t, pextra, sprs, textras, textras_near, - tterrain_near, pcity); - } + } + extra_type_list_iterate_end; + extra_type_list_iterate(t->style_lists[ESTYLE_ROAD_PARITY_COMBINED], + pextra) + { + if (is_extra_drawing_enabled(pextra)) { + fill_road_sprite_array(t, pextra, sprs, textras, textras_near, + tterrain_near, pcity); } - extra_type_list_iterate_end; - extra_type_list_iterate(t->style_lists[ESTYLE_ROAD_ALL_COMBINED], - pextra) - { - if (is_extra_drawing_enabled(pextra)) { - fill_road_sprite_array(t, pextra, sprs, textras, textras_near, - tterrain_near, pcity); - } + } + extra_type_list_iterate_end; + extra_type_list_iterate(t->style_lists[ESTYLE_ROAD_ALL_COMBINED], pextra) + { + if (is_extra_drawing_enabled(pextra)) { + fill_road_sprite_array(t, pextra, sprs, textras, textras_near, + tterrain_near, pcity); } - extra_type_list_iterate_end; } + extra_type_list_iterate_end; break; case LAYER_SPECIAL1: - if (NULL != pterrain) { - if (ptile) { - extra_type_list_iterate(t->style_lists[ESTYLE_3LAYER], pextra) - { - if (tile_has_extra(ptile, pextra) - && is_extra_drawing_enabled(pextra) - && t->sprites.extras[extra_index(pextra)].u.bmf.background) { - bool hidden = false; + if (ptile) { + extra_type_list_iterate(t->style_lists[ESTYLE_3LAYER], pextra) + { + if (tile_has_extra(ptile, pextra) && is_extra_drawing_enabled(pextra) + && t->sprites.extras[extra_index(pextra)].u.bmf.background) { + bool hidden = false; - extra_type_list_iterate(pextra->hiders, phider) - { - if (BV_ISSET(textras, extra_index(phider))) { - hidden = true; - break; - } + extra_type_list_iterate(pextra->hiders, phider) + { + if (BV_ISSET(textras, extra_index(phider))) { + hidden = true; + break; } - extra_type_list_iterate_end; + } + extra_type_list_iterate_end; - if (!hidden) { - ADD_SPRITE_FULL( - t->sprites.extras[extra_index(pextra)].u.bmf.background); - } + if (!hidden) { + ADD_SPRITE_FULL( + t->sprites.extras[extra_index(pextra)].u.bmf.background); } } - extra_type_list_iterate_end; } + extra_type_list_iterate_end; extra_type_list_iterate(t->style_lists[ESTYLE_SINGLE1], pextra) { @@ -5697,32 +5685,29 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, break; case LAYER_SPECIAL2: - if (NULL != pterrain) { - if (ptile) { - extra_type_list_iterate(t->style_lists[ESTYLE_3LAYER], pextra) - { - if (tile_has_extra(ptile, pextra) - && is_extra_drawing_enabled(pextra) - && t->sprites.extras[extra_index(pextra)].u.bmf.middleground) { - bool hidden = false; + if (ptile) { + extra_type_list_iterate(t->style_lists[ESTYLE_3LAYER], pextra) + { + if (tile_has_extra(ptile, pextra) && is_extra_drawing_enabled(pextra) + && t->sprites.extras[extra_index(pextra)].u.bmf.middleground) { + bool hidden = false; - extra_type_list_iterate(pextra->hiders, phider) - { - if (BV_ISSET(textras, extra_index(phider))) { - hidden = true; - break; - } + extra_type_list_iterate(pextra->hiders, phider) + { + if (BV_ISSET(textras, extra_index(phider))) { + hidden = true; + break; } - extra_type_list_iterate_end; + } + extra_type_list_iterate_end; - if (!hidden) { - ADD_SPRITE_FULL( - t->sprites.extras[extra_index(pextra)].u.bmf.middleground); - } + if (!hidden) { + ADD_SPRITE_FULL( + t->sprites.extras[extra_index(pextra)].u.bmf.middleground); } } - extra_type_list_iterate_end; } + extra_type_list_iterate_end; extra_type_list_iterate(t->style_lists[ESTYLE_SINGLE2], pextra) { @@ -5770,16 +5755,41 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, break; case LAYER_SPECIAL3: - if (NULL != pterrain) { - if (ptile) { - bool show_flag = false; - struct player *eowner = extra_owner(ptile); + if (ptile) { + bool show_flag = false; + struct player *eowner = extra_owner(ptile); - extra_type_list_iterate(t->style_lists[ESTYLE_3LAYER], pextra) + extra_type_list_iterate(t->style_lists[ESTYLE_3LAYER], pextra) + { + if (is_extra_drawing_enabled(pextra) && tile_has_extra(ptile, pextra) + && t->sprites.extras[extra_index(pextra)].u.bmf.foreground) { + bool hidden = false; + + extra_type_list_iterate(pextra->hiders, phider) + { + if (BV_ISSET(textras, extra_index(phider))) { + hidden = true; + break; + } + } + extra_type_list_iterate_end; + + if (!hidden) { + if (t->sprites.extras[extra_index(pextra)].u.bmf.foreground) { + ADD_SPRITE_FULL( + t->sprites.extras[extra_index(pextra)].u.bmf.foreground); + } + } + } + } + extra_type_list_iterate_end; + + /* Show base flag. Not part of previous iteration as + * "extras of ESTYLE_3_LAYER" != "bases" */ + if (eowner != NULL) { + extra_type_list_iterate(t->flagged_bases_list, pextra) { - if (is_extra_drawing_enabled(pextra) - && tile_has_extra(ptile, pextra) - && t->sprites.extras[extra_index(pextra)].u.bmf.foreground) { + if (tile_has_extra(ptile, pextra)) { bool hidden = false; extra_type_list_iterate(pextra->hiders, phider) @@ -5792,45 +5802,17 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, extra_type_list_iterate_end; if (!hidden) { - if (t->sprites.extras[extra_index(pextra)].u.bmf.foreground) { - ADD_SPRITE_FULL( - t->sprites.extras[extra_index(pextra)].u.bmf.foreground); - } + show_flag = true; } } } extra_type_list_iterate_end; - /* Show base flag. Not part of previous iteration as - * "extras of ESTYLE_3_LAYER" != "bases" */ - if (eowner != NULL) { - extra_type_list_iterate(t->flagged_bases_list, pextra) - { - if (tile_has_extra(ptile, pextra)) { - bool hidden = false; - - extra_type_list_iterate(pextra->hiders, phider) - { - if (BV_ISSET(textras, extra_index(phider))) { - hidden = true; - break; - } - } - extra_type_list_iterate_end; - - if (!hidden) { - show_flag = true; - } - } - } - extra_type_list_iterate_end; - - if (show_flag) { - sprs.emplace_back( - t, get_nation_flag_sprite(t, nation_of_player(eowner)), true, - FULL_TILE_X_OFFSET + t->city_flag_offset_x, - FULL_TILE_Y_OFFSET + t->city_flag_offset_y); - } + if (show_flag) { + sprs.emplace_back( + t, get_nation_flag_sprite(t, nation_of_player(eowner)), true, + FULL_TILE_X_OFFSET + t->city_flag_offset_x, + FULL_TILE_Y_OFFSET + t->city_flag_offset_y); } } } @@ -6623,115 +6605,25 @@ std::vector fill_basic_extra_sprite_array(const struct tileset *t, const struct extra_type *pextra) { - auto sprs = std::vector(); - - int idx = extra_index(pextra); + // We create a virtual tile with only the requested extra, then collect + // sprites from every layer. + auto tile = tile_virtual_new(nullptr); + BV_CLR_ALL(tile->extras); + BV_SET(tile->extras, pextra->id); - switch (t->sprites.extras[idx].extrastyle) { - case ESTYLE_SINGLE1: - case ESTYLE_SINGLE2: - sprs.emplace_back(t, t->sprites.extras[idx].u.single); - break; - case ESTYLE_CARDINALS: - sprs.emplace_back(t, t->sprites.extras[idx].u.cardinals[0]); - break; - case ESTYLE_ROAD_ALL_SEPARATE: - case ESTYLE_ROAD_PARITY_COMBINED: - case ESTYLE_ROAD_ALL_COMBINED: - case ESTYLE_RIVER: - fill_basic_road_sprite_array(t, sprs, pextra); - break; - case ESTYLE_3LAYER: - fill_basic_base_sprite_array(t, sprs, pextra); - break; - case ESTYLE_COUNT: - fc_assert(t->sprites.extras[idx].extrastyle != ESTYLE_COUNT); - break; - } - - return sprs; -} - -/** - Fills the sprite array with sprites that together make a representative - image of the given road type. The image is suitable for use as an icon - for the road type, for example. - */ -void fill_basic_road_sprite_array(const struct tileset *t, - std::vector &sprs, - const struct extra_type *pextra) -{ - int idx; - int i; - int extrastyle; - - if (!t || !pextra) { - return; - } - - idx = extra_index(pextra); - - if (!(0 <= idx && idx < game.control.num_extra_types)) { - return; - } - - extrastyle = t->sprites.extras[idx].extrastyle; - - if (extrastyle == ESTYLE_RIVER) { - sprs.emplace_back(t, t->sprites.extras[idx].u.road.ru.rivers.spec[0]); - } else { - for (i = 0; i < t->num_valid_tileset_dirs; i++) { - if (!t->valid_tileset_dirs[i]) { - continue; - } - if (extrastyle == ESTYLE_ROAD_ALL_SEPARATE) { - sprs.emplace_back(t, t->sprites.extras[idx].u.road.ru.dir[i]); - } else if (extrastyle == ESTYLE_ROAD_PARITY_COMBINED) { - if ((i % 2) == 0) { - sprs.emplace_back( - t, t->sprites.extras[idx].u.road.ru.combo.even[1 << (i / 2)]); - } - } else if (extrastyle == ESTYLE_ROAD_ALL_COMBINED) { - sprs.emplace_back(t, t->sprites.extras[idx].u.road.ru.total[1 << i]); - } + auto sprs = std::vector(); + for (const auto &layer : t->layers) { + const auto lsprs = layer->fill_sprite_array(tile, nullptr, nullptr, + nullptr, nullptr, nullptr); + // Merge by hand because drawn_sprite isn't copyable (but it is + // copy-constructible) + for (const auto &sprite : lsprs) { + sprs.emplace_back(sprite); } } -} - -/** - Fills the sprite array with sprites that together make a representative - image of the given base type. The image is suitable for use as an icon - for the base type, for example. - */ -void fill_basic_base_sprite_array(const struct tileset *t, - std::vector &sprs, - const struct extra_type *pextra) -{ - int idx; - - if (!t || !pextra) { - return; - } - idx = extra_index(pextra); - - if (!(0 <= idx && idx < game.control.num_extra_types)) { - return; - } - -#define ADD_SPRITE_IF_NOT_NULL(x) \ - do { \ - if ((x) != NULL) { \ - ADD_SPRITE_FULL(x); \ - } \ - } while (false) - - // Corresponds to LAYER_SPECIAL{1,2,3} order. - ADD_SPRITE_IF_NOT_NULL(t->sprites.extras[idx].u.bmf.background); - ADD_SPRITE_IF_NOT_NULL(t->sprites.extras[idx].u.bmf.middleground); - ADD_SPRITE_IF_NOT_NULL(t->sprites.extras[idx].u.bmf.foreground); - -#undef ADD_SPRITE_IF_NOT_NULL + tile_virtual_destroy(tile); + return sprs; } const std::vector> & diff --git a/common/fc_interface.h b/common/fc_interface.h index 5b59f71f14..b674ecfaba 100644 --- a/common/fc_interface.h +++ b/common/fc_interface.h @@ -28,7 +28,7 @@ struct functions { bool (*server_setting_val_bool_get)(server_setting_id id); int (*server_setting_val_int_get)(server_setting_id id); unsigned int (*server_setting_val_bitwise_get)(server_setting_id id); - void (*create_extra)(struct tile *ptile, struct extra_type *pextra, + void (*create_extra)(struct tile *ptile, const extra_type *pextra, struct player *pplayer); void (*destroy_extra)(struct tile *ptile, struct extra_type *pextra); /* Returns iff the player 'pplayer' has the vision in the layer diff --git a/common/tile.cpp b/common/tile.cpp index f9028d54a3..7d5b48a12a 100644 --- a/common/tile.cpp +++ b/common/tile.cpp @@ -411,6 +411,9 @@ void tile_set_continent(struct tile *ptile, Continent_id val) enum known_type tile_get_known(const struct tile *ptile, const struct player *pplayer) { + if (tile_virtual_check(ptile)) { + return TILE_KNOWN_SEEN; + } if (!pplayer->tile_known->at(tile_index(ptile))) { return TILE_UNKNOWN; } else if (!fc_funcs->player_tile_vision_get(ptile, pplayer, V_MAIN)) { @@ -470,7 +473,7 @@ int tile_activity_time(enum unit_activity activity, const struct tile *ptile, /** Create extra to tile. */ -static void tile_create_extra(struct tile *ptile, struct extra_type *pextra) +static void tile_create_extra(struct tile *ptile, const extra_type *pextra) { if (fc_funcs->create_extra != NULL) { // Assume callback calls tile_add_extra() itself. @@ -518,7 +521,7 @@ void tile_change_terrain(struct tile *ptile, struct terrain *pterrain) Recursively add all extra dependencies to add given extra. */ static bool add_recursive_extras(struct tile *ptile, - struct extra_type *pextra, int rec) + const extra_type *pextra, int rec) { if (rec > MAX_EXTRA_TYPES) { // Infinite recursion @@ -585,7 +588,7 @@ static bool rm_recursive_extras(struct tile *ptile, Pass virtual tile to the function if you are not sure it will success and don't want extras adjusted at all in case of failure. */ -bool tile_extra_apply(struct tile *ptile, struct extra_type *tgt) +bool tile_extra_apply(struct tile *ptile, const extra_type *tgt) { // Add extra with its dependencies if (!add_recursive_extras(ptile, tgt, 0)) { @@ -1116,12 +1119,14 @@ void tile_virtual_destroy(struct tile *vtile) /** Check if the given tile is a virtual one or not. */ -bool tile_virtual_check(struct tile *vtile) +bool tile_virtual_check(const tile *vtile) { int tindex; if (!vtile || map_is_empty()) { return false; + } else if (tile_index(vtile) == TILE_INDEX_NONE) { + return true; } tindex = tile_index(vtile); diff --git a/common/tile.h b/common/tile.h index 6ec7ebbd05..b3f1650f67 100644 --- a/common/tile.h +++ b/common/tile.h @@ -128,7 +128,7 @@ int tile_roads_output_incr(const struct tile *ptile, enum output_type_id o); int tile_roads_output_bonus(const struct tile *ptile, enum output_type_id o); bool tile_has_river(const struct tile *tile); -bool tile_extra_apply(struct tile *ptile, struct extra_type *tgt); +bool tile_extra_apply(struct tile *ptile, const extra_type *tgt); bool tile_extra_rm_apply(struct tile *ptile, struct extra_type *tgt); #define tile_has_extra(ptile, pextra) \ BV_ISSET(ptile->extras, extra_index(pextra)) @@ -170,7 +170,7 @@ const char *tile_get_info_text(const struct tile *ptile, // Virtual tiles are tiles that do not exist on the game map. struct tile *tile_virtual_new(const struct tile *ptile); void tile_virtual_destroy(struct tile *vtile); -bool tile_virtual_check(struct tile *vtile); +bool tile_virtual_check(const tile *vtile); bool tile_set_label(struct tile *ptile, const char *label); bool tile_is_placing(const struct tile *ptile); diff --git a/server/maphand.cpp b/server/maphand.cpp index f3f27333c3..56c66fd700 100644 --- a/server/maphand.cpp +++ b/server/maphand.cpp @@ -2356,7 +2356,7 @@ void map_calculate_borders() /** Claim base to player's ownership. */ -void map_claim_base(struct tile *ptile, struct extra_type *pextra, +void map_claim_base(struct tile *ptile, const extra_type *pextra, struct player *powner, struct player *ploser) { struct base_type *pbase; @@ -2488,7 +2488,7 @@ void vision_clear_sight(struct vision *vision) /** Create extra to tile. */ -void create_extra(struct tile *ptile, struct extra_type *pextra, +void create_extra(struct tile *ptile, const extra_type *pextra, struct player *pplayer) { bool extras_removed = false; diff --git a/server/maphand.h b/server/maphand.h index 333874fc88..d807336d24 100644 --- a/server/maphand.h +++ b/server/maphand.h @@ -117,7 +117,7 @@ void map_update_border(struct tile *ptile, struct player *owner, int old_radius_sq, int new_radius_sq); void tile_claim_bases(struct tile *ptile, struct player *powner); -void map_claim_base(struct tile *ptile, struct extra_type *pextra, +void map_claim_base(struct tile *ptile, const extra_type *pextra, struct player *powner, struct player *ploser); void terrain_changed(struct tile *ptile); @@ -134,7 +134,7 @@ void vision_clear_sight(struct vision *vision); void change_playertile_site(struct player_tile *ptile, struct vision_site *new_site); -void create_extra(struct tile *ptile, struct extra_type *pextra, +void create_extra(struct tile *ptile, const extra_type *pextra, struct player *pplayer); void destroy_extra(struct tile *ptile, struct extra_type *pextra); From 5a24b0a33c14aa52ee817ec38274c706c99de0df Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Fri, 20 Aug 2021 00:42:21 +0200 Subject: [PATCH 15/24] Move LAYER_SPECIAL* to a class This change allows significant deduplications. The drawing of Single1, Single2 and 3Layers specials is unified: each layer_special may or may not have sprites for each special (which specials is decided when loading the tileset). See #430. --- client/CMakeLists.txt | 1 + client/layer_special.cpp | 83 +++++++++++++++ client/layer_special.h | 39 +++++++ client/tilespec.cpp | 218 ++++++++++----------------------------- client/tilespec.h | 24 +++++ 5 files changed, 200 insertions(+), 165 deletions(-) create mode 100644 client/layer_special.cpp create mode 100644 client/layer_special.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 92cf13ab72..be57e66760 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -57,6 +57,7 @@ add_library( helpdata.cpp layer.cpp layer_background.cpp + layer_special.cpp luaconsole_common.cpp mapctrl_common.cpp mapview_common.cpp diff --git a/client/layer_special.cpp b/client/layer_special.cpp new file mode 100644 index 0000000000..3b92977975 --- /dev/null +++ b/client/layer_special.cpp @@ -0,0 +1,83 @@ +/*__ ___ *************************************** +/ \ / \ Copyright (c) 2021 Freeciv21 contributors. +\_ \ / __/ This file is part of Freeciv21. + _\ \ / /__ Freeciv21 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 3 of the License, + | or (at your option) any later version. + _/ /\ You should have received a copy of the GNU + /o) (o/\ \_ General Public License along with Freeciv21. + \_____/ / If not, see https://www.gnu.org/licenses/. + \____/ ********************************************************/ + +#include "layer_special.h" + +#include "extras.h" +#include "game.h" // For extra_type_iterate +#include "sprite_g.h" +#include "tilespec.h" + +namespace freeciv { + +layer_special::layer_special(struct tileset *ts, mapview_layer layer) + : freeciv::layer(ts, layer), m_sprites{nullptr} +{ + fc_assert(layer == LAYER_SPECIAL1 || layer == LAYER_SPECIAL2 + || layer == LAYER_SPECIAL3); +} + +void layer_special::set_sprite(const extra_type *extra, const QString &tag, + int offset_x, int offset_y) +{ + fc_assert_ret(extra != nullptr); + + auto sprite = load_sprite(tileset(), tag); + if (sprite) { + m_sprites.at(extra->id) = std::make_unique( + tileset(), sprite, true, offset_x, offset_y); + } +} + +std::vector layer_special::fill_sprite_array( + const tile *ptile, const tile_edge *pedge, const tile_corner *pcorner, + const unit *punit, const city *pcity, const unit_type *putype) const +{ + Q_UNUSED(pedge); + Q_UNUSED(pcorner); + Q_UNUSED(punit); + Q_UNUSED(pcity); + Q_UNUSED(putype); + + if (ptile == nullptr) { + return {}; + } + + auto sprites = std::vector(); + + extra_type_iterate(pextra) + { + if (tile_has_extra(ptile, pextra) && is_extra_drawing_enabled(pextra) + && m_sprites[extra_index(pextra)]) { + // Check whether the extra is hidden by some other extra. + bool hidden = false; + extra_type_list_iterate(pextra->hiders, phider) + { + if (BV_ISSET(ptile->extras, extra_index(phider))) { + hidden = true; + break; + } + } + extra_type_list_iterate_end; + + if (!hidden) { + sprites.push_back(*m_sprites[extra_index(pextra)]); + } + } + } + extra_type_iterate_end; + + return sprites; +} + +} // namespace freeciv diff --git a/client/layer_special.h b/client/layer_special.h new file mode 100644 index 0000000000..41b1c7e189 --- /dev/null +++ b/client/layer_special.h @@ -0,0 +1,39 @@ +/*__ ___ *************************************** +/ \ / \ Copyright (c) 2021 Freeciv21 contributors. +\_ \ / __/ This file is part of Freeciv21. + _\ \ / /__ Freeciv21 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 3 of the License, + | or (at your option) any later version. + _/ /\ You should have received a copy of the GNU + /o) (o/\ \_ General Public License along with Freeciv21. + \_____/ / If not, see https://www.gnu.org/licenses/. + \____/ ********************************************************/ +#pragma once + +#include "fc_types.h" +#include "layer.h" + +#include + +namespace freeciv { + +class layer_special : public layer { +public: + explicit layer_special(struct tileset *ts, mapview_layer layer); + + void set_sprite(const extra_type *extra, const QString &tag, + int offset_x = 0, int offset_y = 0); + + std::vector + fill_sprite_array(const tile *ptile, const tile_edge *pedge, + const tile_corner *pcorner, const unit *punit, + const city *pcity, + const unit_type *putype) const override; + +private: + std::array, MAX_EXTRA_TYPES> m_sprites; +}; + +} // namespace freeciv diff --git a/client/tilespec.cpp b/client/tilespec.cpp index 6ce7cb5a07..d8bb3c39f5 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -79,6 +79,7 @@ #include "goto.h" #include "helpdata.h" #include "layer_background.h" +#include "layer_special.h" #include "options.h" // for fill_xxx #include "themes_common.h" @@ -121,25 +122,8 @@ #define NUM_TILES_SELECT 4 #define MAX_NUM_UPKEEP_SPRITES 10 -#define SPECENUM_NAME extrastyle_id -#define SPECENUM_VALUE0 ESTYLE_ROAD_ALL_SEPARATE -#define SPECENUM_VALUE0NAME "RoadAllSeparate" -#define SPECENUM_VALUE1 ESTYLE_ROAD_PARITY_COMBINED -#define SPECENUM_VALUE1NAME "RoadParityCombined" -#define SPECENUM_VALUE2 ESTYLE_ROAD_ALL_COMBINED -#define SPECENUM_VALUE2NAME "RoadAllCombined" -#define SPECENUM_VALUE3 ESTYLE_RIVER -#define SPECENUM_VALUE3NAME "River" -#define SPECENUM_VALUE4 ESTYLE_SINGLE1 -#define SPECENUM_VALUE4NAME "Single1" -#define SPECENUM_VALUE5 ESTYLE_SINGLE2 -#define SPECENUM_VALUE5NAME "Single2" -#define SPECENUM_VALUE6 ESTYLE_3LAYER -#define SPECENUM_VALUE6NAME "3Layer" -#define SPECENUM_VALUE7 ESTYLE_CARDINALS -#define SPECENUM_VALUE7NAME "Cardinals" -#define SPECENUM_COUNT ESTYLE_COUNT -#include "specenum_gen.h" +#define FULL_TILE_X_OFFSET ((t->normal_tile_width - t->full_tile_width) / 2) +#define FULL_TILE_Y_OFFSET (t->normal_tile_height - t->full_tile_height) /* This could be moved to common/map.h if there's more use for it. */ enum direction4 { DIR4_NORTH = 0, DIR4_SOUTH, DIR4_EAST, DIR4_WEST }; @@ -309,11 +293,7 @@ struct named_sprites { QPixmap *activity, *rmact; int extrastyle; union { - QPixmap *single; QPixmap *cardinals[MAX_INDEX_CARDINAL]; - struct { - QPixmap *background, *middleground, *foreground; - } bmf; struct { QPixmap /* for extrastyles ESTYLE_ROAD_ALL_SEPARATE and @@ -398,6 +378,9 @@ struct tileset { char *for_ruleset; std::vector> layers; + struct { + freeciv::layer_special *background, *middleground, *foreground; + } special_layers; enum ts_type type; int hex_width, hex_height; @@ -484,7 +467,7 @@ static void tileset_setup_base(struct tileset *t, static void tileset_setup_road(struct tileset *t, struct extra_type *pextra, const char *tag); -static bool is_extra_drawing_enabled(struct extra_type *pextra); +bool is_extra_drawing_enabled(struct extra_type *pextra); static void tileset_player_free(struct tileset *t, int plrid); @@ -2120,6 +2103,21 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, case LAYER_BACKGROUND: t->layers.push_back(std::make_unique(t)); break; + case LAYER_SPECIAL1: { + auto l = std::make_unique(t, layer); + t->special_layers.background = l.get(); + t->layers.emplace_back(std::move(l)); + } break; + case LAYER_SPECIAL2: { + auto l = std::make_unique(t, layer); + t->special_layers.middleground = l.get(); + t->layers.emplace_back(std::move(l)); + } break; + case LAYER_SPECIAL3: { + auto l = std::make_unique(t, layer); + t->special_layers.foreground = l.get(); + t->layers.emplace_back(std::move(l)); + } break; default: t->layers.push_back(std::make_unique(t, layer)); break; @@ -2133,6 +2131,21 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, case LAYER_BACKGROUND: t->layers.push_back(std::make_unique(t)); break; + case LAYER_SPECIAL1: { + auto l = std::make_unique(t, layer); + t->special_layers.background = l.get(); + t->layers.emplace_back(std::move(l)); + } break; + case LAYER_SPECIAL2: { + auto l = std::make_unique(t, layer); + t->special_layers.middleground = l.get(); + t->layers.emplace_back(std::move(l)); + } break; + case LAYER_SPECIAL3: { + auto l = std::make_unique(t, layer); + t->special_layers.foreground = l.get(); + t->layers.emplace_back(std::move(l)); + } break; default: t->layers.push_back(std::make_unique(t, layer)); break; @@ -2501,8 +2514,8 @@ static QString &valid_index_str(const struct tileset *t, int idx) Scale means if sprite should be scaled, smooth if scaling might use other scaling algorithm than nearest neighbor. */ -static QPixmap *load_sprite(struct tileset *t, const QString &tag_name, - bool scale, bool smooth) +QPixmap *load_sprite(struct tileset *t, const QString &tag_name, bool scale, + bool smooth) { struct small_sprite *ss; float sprite_scale = 1.0f; @@ -3598,8 +3611,10 @@ void tileset_setup_extra(struct tileset *t, struct extra_type *pextra) break; case ESTYLE_SINGLE1: + t->special_layers.background->set_sprite(pextra, tag); + break; case ESTYLE_SINGLE2: - SET_SPRITE(extras[id].u.single, tag); + t->special_layers.middleground->set_sprite(pextra, tag); break; case ESTYLE_CARDINALS: { @@ -3787,25 +3802,16 @@ static void tileset_setup_base(struct tileset *t, fc_assert_ret(id >= 0 && id < extra_count()); QString full_tag_name = QString(tag) + QStringLiteral("_bg"); - t->sprites.extras[id].u.bmf.background = - load_sprite(t, qUtf8Printable(full_tag_name), true, true); + t->special_layers.background->set_sprite( + pextra, full_tag_name, FULL_TILE_X_OFFSET, FULL_TILE_Y_OFFSET); full_tag_name = QString(tag) + QStringLiteral("_mg"); - t->sprites.extras[id].u.bmf.middleground = - load_sprite(t, qUtf8Printable(full_tag_name), true, true); + t->special_layers.middleground->set_sprite( + pextra, full_tag_name, FULL_TILE_X_OFFSET, FULL_TILE_Y_OFFSET); full_tag_name = QString(tag) + QStringLiteral("_fg"); - t->sprites.extras[id].u.bmf.foreground = - load_sprite(t, qUtf8Printable(full_tag_name), true, true); - - if (t->sprites.extras[id].u.bmf.background == NULL - && t->sprites.extras[id].u.bmf.middleground == NULL - && t->sprites.extras[id].u.bmf.foreground == NULL) { - // There was an extra style definition but no matching graphics - tileset_error(LOG_FATAL, - _("No graphics with tag \"%s_bg/mg/fg\" for extra \"%s\""), - tag, extra_rule_name(pextra)); - } + t->special_layers.foreground->set_sprite( + pextra, full_tag_name, FULL_TILE_X_OFFSET, FULL_TILE_Y_OFFSET); } /** @@ -4149,8 +4155,6 @@ static QPixmap *get_unit_nation_flag_sprite(const struct tileset *t, } } -#define FULL_TILE_X_OFFSET ((t->normal_tile_width - t->full_tile_width) / 2) -#define FULL_TILE_Y_OFFSET (t->normal_tile_height - t->full_tile_height) #define ADD_SPRITE_FULL(s) \ sprs.emplace_back(t, s, true, FULL_TILE_X_OFFSET, FULL_TILE_Y_OFFSET) @@ -5343,7 +5347,7 @@ static void fill_goto_sprite_array(const struct tileset *t, Should the given extra be drawn FIXME: Some extras can not be switched */ -static bool is_extra_drawing_enabled(struct extra_type *pextra) +bool is_extra_drawing_enabled(struct extra_type *pextra) { bool no_disable = true; // Draw if matches no cause @@ -5561,53 +5565,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, break; case LAYER_SPECIAL1: - if (ptile) { - extra_type_list_iterate(t->style_lists[ESTYLE_3LAYER], pextra) - { - if (tile_has_extra(ptile, pextra) && is_extra_drawing_enabled(pextra) - && t->sprites.extras[extra_index(pextra)].u.bmf.background) { - bool hidden = false; - - extra_type_list_iterate(pextra->hiders, phider) - { - if (BV_ISSET(textras, extra_index(phider))) { - hidden = true; - break; - } - } - extra_type_list_iterate_end; - - if (!hidden) { - ADD_SPRITE_FULL( - t->sprites.extras[extra_index(pextra)].u.bmf.background); - } - } - } - extra_type_list_iterate_end; - - extra_type_list_iterate(t->style_lists[ESTYLE_SINGLE1], pextra) - { - if (BV_ISSET(textras, extra_index(pextra)) - && is_extra_drawing_enabled(pextra)) { - bool hidden = false; - - extra_type_list_iterate(pextra->hiders, phider) - { - if (BV_ISSET(textras, extra_index(phider))) { - hidden = true; - break; - } - } - extra_type_list_iterate_end; - - if (!hidden) { - sprs.emplace_back( - t, t->sprites.extras[extra_index(pextra)].u.single); - } - } - } - extra_type_list_iterate_end; - } + fc_assert_ret_val(false, {}); break; case LAYER_GRID1: @@ -5685,53 +5643,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, break; case LAYER_SPECIAL2: - if (ptile) { - extra_type_list_iterate(t->style_lists[ESTYLE_3LAYER], pextra) - { - if (tile_has_extra(ptile, pextra) && is_extra_drawing_enabled(pextra) - && t->sprites.extras[extra_index(pextra)].u.bmf.middleground) { - bool hidden = false; - - extra_type_list_iterate(pextra->hiders, phider) - { - if (BV_ISSET(textras, extra_index(phider))) { - hidden = true; - break; - } - } - extra_type_list_iterate_end; - - if (!hidden) { - ADD_SPRITE_FULL( - t->sprites.extras[extra_index(pextra)].u.bmf.middleground); - } - } - } - extra_type_list_iterate_end; - - extra_type_list_iterate(t->style_lists[ESTYLE_SINGLE2], pextra) - { - if (BV_ISSET(textras, extra_index(pextra)) - && is_extra_drawing_enabled(pextra)) { - bool hidden = false; - - extra_type_list_iterate(pextra->hiders, phider) - { - if (BV_ISSET(textras, extra_index(phider))) { - hidden = true; - break; - } - } - extra_type_list_iterate_end; - - if (!hidden) { - sprs.emplace_back( - t, t->sprites.extras[extra_index(pextra)].u.single); - } - } - } - extra_type_list_iterate_end; - } + fc_assert_ret_val(false, {}); break; case LAYER_UNIT: @@ -5755,35 +5667,10 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, break; case LAYER_SPECIAL3: - if (ptile) { + if (NULL != pterrain) { bool show_flag = false; struct player *eowner = extra_owner(ptile); - extra_type_list_iterate(t->style_lists[ESTYLE_3LAYER], pextra) - { - if (is_extra_drawing_enabled(pextra) && tile_has_extra(ptile, pextra) - && t->sprites.extras[extra_index(pextra)].u.bmf.foreground) { - bool hidden = false; - - extra_type_list_iterate(pextra->hiders, phider) - { - if (BV_ISSET(textras, extra_index(phider))) { - hidden = true; - break; - } - } - extra_type_list_iterate_end; - - if (!hidden) { - if (t->sprites.extras[extra_index(pextra)].u.bmf.foreground) { - ADD_SPRITE_FULL( - t->sprites.extras[extra_index(pextra)].u.bmf.foreground); - } - } - } - } - extra_type_list_iterate_end; - /* Show base flag. Not part of previous iteration as * "extras of ESTYLE_3_LAYER" != "bases" */ if (eowner != NULL) { @@ -5816,6 +5703,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, } } } + fc_assert_ret_val(false, {}); break; case LAYER_FOG: diff --git a/client/tilespec.h b/client/tilespec.h index 876ecb6e9c..4ac8b2b494 100644 --- a/client/tilespec.h +++ b/client/tilespec.h @@ -73,6 +73,26 @@ struct resource_type; #define SPECENUM_VALUE4NAME "Corner" #include "specenum_gen.h" +#define SPECENUM_NAME extrastyle_id +#define SPECENUM_VALUE0 ESTYLE_ROAD_ALL_SEPARATE +#define SPECENUM_VALUE0NAME "RoadAllSeparate" +#define SPECENUM_VALUE1 ESTYLE_ROAD_PARITY_COMBINED +#define SPECENUM_VALUE1NAME "RoadParityCombined" +#define SPECENUM_VALUE2 ESTYLE_ROAD_ALL_COMBINED +#define SPECENUM_VALUE2NAME "RoadAllCombined" +#define SPECENUM_VALUE3 ESTYLE_RIVER +#define SPECENUM_VALUE3NAME "River" +#define SPECENUM_VALUE4 ESTYLE_SINGLE1 +#define SPECENUM_VALUE4NAME "Single1" +#define SPECENUM_VALUE5 ESTYLE_SINGLE2 +#define SPECENUM_VALUE5NAME "Single2" +#define SPECENUM_VALUE6 ESTYLE_3LAYER +#define SPECENUM_VALUE6NAME "3Layer" +#define SPECENUM_VALUE7 ESTYLE_CARDINALS +#define SPECENUM_VALUE7NAME "Cardinals" +#define SPECENUM_COUNT ESTYLE_COUNT +#include "specenum_gen.h" + #define NUM_TILES_PROGRESS 8 #define MAX_NUM_CITIZEN_SPRITES 6 @@ -127,6 +147,9 @@ bool tileset_layer_in_category(enum mapview_layer layer, enum layer_category cat); // Gfx support +QPixmap *load_sprite(struct tileset *t, const QString &tag_name, + bool scale = true, bool smooth = true); + std::vector fill_sprite_array(struct tileset *t, enum mapview_layer layer, const struct tile *ptile, const struct tile_edge *pedge, @@ -238,6 +261,7 @@ QPixmap *get_basic_fog_sprite(const struct tileset *t); std::vector fill_basic_extra_sprite_array(const struct tileset *t, const struct extra_type *pextra); +bool is_extra_drawing_enabled(struct extra_type *pextra); QPixmap *get_event_sprite(const struct tileset *t, enum event_type event); QPixmap *get_mask_sprite(const struct tileset *t); From d57535dd885445a186ab1c81a53f5624d119974e Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Fri, 20 Aug 2021 01:09:22 +0200 Subject: [PATCH 16/24] Add a layer for base flags This re-enables the base flags lost in the previous commit. --- client/layer.h | 42 +++++++++++++++++++++------------------- client/tilespec.cpp | 6 +++++- data/3d.tilespec | 3 ++- data/alio.tilespec | 3 ++- data/amplio.tilespec | 3 ++- data/amplio2.tilespec | 3 ++- data/cimpletoon.tilespec | 3 ++- data/hex2t.tilespec | 3 ++- data/hexemplio.tilespec | 3 ++- data/isophex.tilespec | 3 ++- data/isotrident.tilespec | 3 ++- data/toonhex.tilespec | 3 ++- data/trident.tilespec | 3 ++- 13 files changed, 49 insertions(+), 32 deletions(-) diff --git a/client/layer.h b/client/layer.h index ed0450ebc7..b1222a0bd3 100644 --- a/client/layer.h +++ b/client/layer.h @@ -90,26 +90,28 @@ struct drawn_sprite { #define SPECENUM_VALUE12NAME "Unit" #define SPECENUM_VALUE13 LAYER_SPECIAL3 #define SPECENUM_VALUE13NAME "Special3" -#define SPECENUM_VALUE14 LAYER_CITY2 -#define SPECENUM_VALUE14NAME "City2" -#define SPECENUM_VALUE15 LAYER_GRID2 -#define SPECENUM_VALUE15NAME "Grid2" -#define SPECENUM_VALUE16 LAYER_OVERLAYS -#define SPECENUM_VALUE16NAME "Overlays" -#define SPECENUM_VALUE17 LAYER_TILELABEL -#define SPECENUM_VALUE17NAME "TileLabel" -#define SPECENUM_VALUE18 LAYER_CITYBAR -#define SPECENUM_VALUE18NAME "CityBar" -#define SPECENUM_VALUE19 LAYER_FOCUS_UNIT -#define SPECENUM_VALUE19NAME "FocusUnit" -#define SPECENUM_VALUE20 LAYER_GOTO -#define SPECENUM_VALUE20NAME "Goto" -#define SPECENUM_VALUE21 LAYER_WORKERTASK -#define SPECENUM_VALUE21NAME "WorkerTask" -#define SPECENUM_VALUE22 LAYER_EDITOR -#define SPECENUM_VALUE22NAME "Editor" -#define SPECENUM_VALUE23 LAYER_INFRAWORK -#define SPECENUM_VALUE23NAME "InfraWork" +#define SPECENUM_VALUE14 LAYER_BASE_FLAGS +#define SPECENUM_VALUE14NAME "BaseFlags" +#define SPECENUM_VALUE15 LAYER_CITY2 +#define SPECENUM_VALUE15NAME "City2" +#define SPECENUM_VALUE16 LAYER_GRID2 +#define SPECENUM_VALUE16NAME "Grid2" +#define SPECENUM_VALUE17 LAYER_OVERLAYS +#define SPECENUM_VALUE17NAME "Overlays" +#define SPECENUM_VALUE18 LAYER_TILELABEL +#define SPECENUM_VALUE18NAME "TileLabel" +#define SPECENUM_VALUE19 LAYER_CITYBAR +#define SPECENUM_VALUE19NAME "CityBar" +#define SPECENUM_VALUE20 LAYER_FOCUS_UNIT +#define SPECENUM_VALUE20NAME "FocusUnit" +#define SPECENUM_VALUE21 LAYER_GOTO +#define SPECENUM_VALUE21NAME "Goto" +#define SPECENUM_VALUE22 LAYER_WORKERTASK +#define SPECENUM_VALUE22NAME "WorkerTask" +#define SPECENUM_VALUE23 LAYER_EDITOR +#define SPECENUM_VALUE23NAME "Editor" +#define SPECENUM_VALUE24 LAYER_INFRAWORK +#define SPECENUM_VALUE24NAME "InfraWork" #define SPECENUM_COUNT LAYER_COUNT #include "specenum_gen.h" diff --git a/client/tilespec.cpp b/client/tilespec.cpp index d8bb3c39f5..219b46ebb9 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -5667,6 +5667,10 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, break; case LAYER_SPECIAL3: + fc_assert_ret_val(false, {}); + break; + + case LAYER_BASE_FLAGS: if (NULL != pterrain) { bool show_flag = false; struct player *eowner = extra_owner(ptile); @@ -5703,7 +5707,6 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, } } } - fc_assert_ret_val(false, {}); break; case LAYER_FOG: @@ -6537,6 +6540,7 @@ bool tileset_layer_in_category(enum mapview_layer layer, case LAYER_SPECIAL1: case LAYER_SPECIAL2: case LAYER_SPECIAL3: + case LAYER_BASE_FLAGS: return cat == LAYER_CATEGORY_CITY || cat == LAYER_CATEGORY_TILE; case LAYER_CITY1: case LAYER_CITY2: diff --git a/data/3d.tilespec b/data/3d.tilespec index c3cdfb6419..ab5c36493a 100644 --- a/data/3d.tilespec +++ b/data/3d.tilespec @@ -115,7 +115,8 @@ unit_upkeep_small_offset_y = 20 ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. ; "Fog", ; Fog of war (on tiles one knows but doesn`t see). ; "Unit", ; Units except the selected one(s). -; "Special3", ; 3rd layer for extras with "3Layers" style, and base flags. +; "Special3", ; 3rd layer for extras with "3Layers" style. +; "BaseFlags", ; Base flags. ; "City2", ; City size when the city bar is disabled. ; "Grid2", ; Second grid layer (overhead tilesets only). ; "Overlays", ; Tile output sprites. diff --git a/data/alio.tilespec b/data/alio.tilespec index ae9ff24e65..584953b464 100644 --- a/data/alio.tilespec +++ b/data/alio.tilespec @@ -121,7 +121,8 @@ layer_order = "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. "Fog", ; Fog of war (on tiles one knows but doesn`t see). "Unit", ; Units except the selected one(s). - "Special3", ; 3rd layer for extras with "3Layers" style, and base flags. + "Special3", ; 3rd layer for extras with "3Layers" style. + "BaseFlags", ; Base flags. "City2", ; City size when the city bar is disabled. "Grid2", ; Second grid layer (overhead tilesets only). "Overlays", ; Tile output sprites. diff --git a/data/amplio.tilespec b/data/amplio.tilespec index cb484750ef..7acf43c75c 100644 --- a/data/amplio.tilespec +++ b/data/amplio.tilespec @@ -112,7 +112,8 @@ tilelabel_offset_y = 15 ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. ; "Fog", ; Fog of war (on tiles one knows but doesn`t see). ; "Unit", ; Units except the selected one(s). -; "Special3", ; 3rd layer for extras with "3Layers" style, and base flags. +; "Special3", ; 3rd layer for extras with "3Layers" style. +; "BaseFlags", ; Base flags. ; "City2", ; City size when the city bar is disabled. ; "Grid2", ; Second grid layer (overhead tilesets only). ; "Overlays", ; Tile output sprites. diff --git a/data/amplio2.tilespec b/data/amplio2.tilespec index e99dc3005b..7632a4cdf0 100644 --- a/data/amplio2.tilespec +++ b/data/amplio2.tilespec @@ -112,7 +112,8 @@ tilelabel_offset_y = 15 ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. ; "Fog", ; Fog of war (on tiles one knows but doesn`t see). ; "Unit", ; Units except the selected one(s). -; "Special3", ; 3rd layer for extras with "3Layers" style, and base flags. +; "Special3", ; 3rd layer for extras with "3Layers" style. +; "BaseFlags", ; Base flags. ; "City2", ; City size when the city bar is disabled. ; "Grid2", ; Second grid layer (overhead tilesets only). ; "Overlays", ; Tile output sprites. diff --git a/data/cimpletoon.tilespec b/data/cimpletoon.tilespec index 8132eb4bb1..bacbcc2906 100644 --- a/data/cimpletoon.tilespec +++ b/data/cimpletoon.tilespec @@ -115,7 +115,8 @@ unit_default_orientation = "s" ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. ; "Fog", ; Fog of war (on tiles one knows but doesn`t see). ; "Unit", ; Units except the selected one(s). -; "Special3", ; 3rd layer for extras with "3Layers" style, and base flags. +; "Special3", ; 3rd layer for extras with "3Layers" style. +; "BaseFlags", ; Base flags. ; "City2", ; City size when the city bar is disabled. ; "Grid2", ; Second grid layer (overhead tilesets only). ; "Overlays", ; Tile output sprites. diff --git a/data/hex2t.tilespec b/data/hex2t.tilespec index 265dc0769e..819351aa56 100644 --- a/data/hex2t.tilespec +++ b/data/hex2t.tilespec @@ -115,7 +115,8 @@ tilelabel_offset_y = 28 ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. ; "Fog", ; Fog of war (on tiles one knows but doesn`t see). ; "Unit", ; Units except the selected one(s). -; "Special3", ; 3rd layer for extras with "3Layers" style, and base flags. +; "Special3", ; 3rd layer for extras with "3Layers" style. +; "BaseFlags", ; Base flags. ; "City2", ; City size when the city bar is disabled. ; "Grid2", ; Second grid layer (overhead tilesets only). ; "Overlays", ; Tile output sprites. diff --git a/data/hexemplio.tilespec b/data/hexemplio.tilespec index bdefdab126..bf2dfa0ad8 100644 --- a/data/hexemplio.tilespec +++ b/data/hexemplio.tilespec @@ -117,7 +117,8 @@ layer_order = "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. "Fog", ; Fog of war (on tiles one knows but doesn`t see). "Unit", ; Units except the selected one(s). - "Special3", ; 3rd layer for extras with "3Layers" style, and base flags. + "Special3", ; 3rd layer for extras with "3Layers" style. + "BaseFlags", ; Base flags. "City2", ; City size when the city bar is disabled. "Grid2", ; Second grid layer (overhead tilesets only). "Overlays", ; Tile output sprites. diff --git a/data/isophex.tilespec b/data/isophex.tilespec index b6aeb4bd63..cc51c3965b 100644 --- a/data/isophex.tilespec +++ b/data/isophex.tilespec @@ -115,7 +115,8 @@ tilelabel_offset_y = 10 ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. ; "Fog", ; Fog of war (on tiles one knows but doesn`t see). ; "Unit", ; Units except the selected one(s). -; "Special3", ; 3rd layer for extras with "3Layers" style, and base flags. +; "Special3", ; 3rd layer for extras with "3Layers" style. +; "BaseFlags", ; Base flags. ; "City2", ; City size when the city bar is disabled. ; "Grid2", ; Second grid layer (overhead tilesets only). ; "Overlays", ; Tile output sprites. diff --git a/data/isotrident.tilespec b/data/isotrident.tilespec index 64bb120f49..0a88c8fe60 100644 --- a/data/isotrident.tilespec +++ b/data/isotrident.tilespec @@ -114,7 +114,8 @@ tilelabel_offset_y = 10 ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. ; "Fog", ; Fog of war (on tiles one knows but doesn`t see). ; "Unit", ; Units except the selected one(s). -; "Special3", ; 3rd layer for extras with "3Layers" style, and base flags. +; "Special3", ; 3rd layer for extras with "3Layers" style. +; "BaseFlags", ; Base flags. ; "City2", ; City size when the city bar is disabled. ; "Grid2", ; Second grid layer (overhead tilesets only). ; "Overlays", ; Tile output sprites. diff --git a/data/toonhex.tilespec b/data/toonhex.tilespec index 8ae631ed0c..864d30ca0f 100644 --- a/data/toonhex.tilespec +++ b/data/toonhex.tilespec @@ -119,7 +119,8 @@ layer_order = "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. "Fog", ; Fog of war (on tiles one knows but doesn`t see). "Unit", ; Units except the selected one(s). - "Special3", ; 3rd layer for extras with "3Layers" style, and base flags. + "Special3", ; 3rd layer for extras with "3Layers" style. + "BaseFlags", ; Base flags. "City2", ; City size when the city bar is disabled. "Grid2", ; Second grid layer (overhead tilesets only). "Overlays", ; Tile output sprites. diff --git a/data/trident.tilespec b/data/trident.tilespec index 3161ffc31d..0ddbbfa67d 100644 --- a/data/trident.tilespec +++ b/data/trident.tilespec @@ -115,7 +115,8 @@ unit_upkeep_small_offset_y = 20 ; "Special2", ; 2nd layer for extras with "3Layers" and "Single2" styles. ; "Fog", ; Fog of war (on tiles one knows but doesn`t see). ; "Unit", ; Units except the selected one(s). -; "Special3", ; 3rd layer for extras with "3Layers" style, and base flags. +; "Special3", ; 3rd layer for extras with "3Layers" style. +; "BaseFlags", ; Base flags. ; "City2", ; City size when the city bar is disabled. ; "Grid2", ; Second grid layer (overhead tilesets only). ; "Overlays", ; Tile output sprites. From 44b18e7c3149790f41462220949f51ed891b24b1 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 22 Aug 2021 00:16:04 +0200 Subject: [PATCH 17/24] Factor out creating layer objects The same switch(layer) was repeated twice. This was fine when there weren't too many layers, but the amount of duplication grew when adding more. This commit factors out the switch() to a new function. --- client/tilespec.cpp | 81 +++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 47 deletions(-) diff --git a/client/tilespec.cpp b/client/tilespec.cpp index 219b46ebb9..24ae99d351 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -1727,6 +1727,38 @@ static void tileset_stop_read(struct tileset *t, struct section_file *file, section_list_destroy(sections); } } + +/** + * Creates a layer object for the given enumerated type and appends it to the + * layers of `t`. Also fills layer pointers in `*t` if needed. + */ +static void tileset_add_layer(struct tileset *t, mapview_layer layer) +{ + switch (layer) { + case LAYER_BACKGROUND: + t->layers.push_back(std::make_unique(t)); + break; + case LAYER_SPECIAL1: { + auto l = std::make_unique(t, layer); + t->special_layers.background = l.get(); + t->layers.emplace_back(std::move(l)); + } break; + case LAYER_SPECIAL2: { + auto l = std::make_unique(t, layer); + t->special_layers.middleground = l.get(); + t->layers.emplace_back(std::move(l)); + } break; + case LAYER_SPECIAL3: { + auto l = std::make_unique(t, layer); + t->special_layers.foreground = l.get(); + t->layers.emplace_back(std::move(l)); + } break; + default: + t->layers.push_back(std::make_unique(t, layer)); + break; + } +} + /** Finds and reads the toplevel tilespec file based on given name. Sets global variables, including tile sizes and full names for @@ -2099,57 +2131,12 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, } for (auto layer : order) { - switch (layer) { - case LAYER_BACKGROUND: - t->layers.push_back(std::make_unique(t)); - break; - case LAYER_SPECIAL1: { - auto l = std::make_unique(t, layer); - t->special_layers.background = l.get(); - t->layers.emplace_back(std::move(l)); - } break; - case LAYER_SPECIAL2: { - auto l = std::make_unique(t, layer); - t->special_layers.middleground = l.get(); - t->layers.emplace_back(std::move(l)); - } break; - case LAYER_SPECIAL3: { - auto l = std::make_unique(t, layer); - t->special_layers.foreground = l.get(); - t->layers.emplace_back(std::move(l)); - } break; - default: - t->layers.push_back(std::make_unique(t, layer)); - break; - } + tileset_add_layer(t, layer); } } else { // There is no layer_order tag in the specfile -> use the default for (i = 0; i < LAYER_COUNT; ++i) { - auto layer = static_cast(i); - switch (layer) { - case LAYER_BACKGROUND: - t->layers.push_back(std::make_unique(t)); - break; - case LAYER_SPECIAL1: { - auto l = std::make_unique(t, layer); - t->special_layers.background = l.get(); - t->layers.emplace_back(std::move(l)); - } break; - case LAYER_SPECIAL2: { - auto l = std::make_unique(t, layer); - t->special_layers.middleground = l.get(); - t->layers.emplace_back(std::move(l)); - } break; - case LAYER_SPECIAL3: { - auto l = std::make_unique(t, layer); - t->special_layers.foreground = l.get(); - t->layers.emplace_back(std::move(l)); - } break; - default: - t->layers.push_back(std::make_unique(t, layer)); - break; - } + tileset_add_layer(t, static_cast(i)); } } From 1de6a6adee1415aa7aa5e4b7e949df1cc85c79a0 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 22 Aug 2021 00:53:00 +0200 Subject: [PATCH 18/24] Move LAYER_BASE_FLAGS to its own class Straightforward. See #430. --- client/CMakeLists.txt | 1 + client/layer_base_flags.cpp | 84 +++++++++++++++++++++++++++++++++++++ client/layer_base_flags.h | 34 +++++++++++++++ client/tilespec.cpp | 60 ++++---------------------- 4 files changed, 127 insertions(+), 52 deletions(-) create mode 100644 client/layer_base_flags.cpp create mode 100644 client/layer_base_flags.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index be57e66760..48c0d45179 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -57,6 +57,7 @@ add_library( helpdata.cpp layer.cpp layer_background.cpp + layer_base_flags.cpp layer_special.cpp luaconsole_common.cpp mapctrl_common.cpp diff --git a/client/layer_base_flags.cpp b/client/layer_base_flags.cpp new file mode 100644 index 0000000000..cfab41fc4d --- /dev/null +++ b/client/layer_base_flags.cpp @@ -0,0 +1,84 @@ +/*__ ___ *************************************** +/ \ / \ Copyright (c) 2021 Freeciv21 contributors. +\_ \ / __/ This file is part of Freeciv21. + _\ \ / /__ Freeciv21 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 3 of the License, + | or (at your option) any later version. + _/ /\ You should have received a copy of the GNU + /o) (o/\ \_ General Public License along with Freeciv21. + \_____/ / If not, see https://www.gnu.org/licenses/. + \____/ ********************************************************/ + +#include "layer_base_flags.h" + +#include "extras.h" +#include "game.h" // For extra_type_iterate +#include "nation.h" +#include "tilespec.h" + +namespace freeciv { + +/** + * @class layer_base_flags + * + * Map layer that draws flags for bases that have @ref EF_SHOW_FLAG set. + */ + +layer_base_flags::layer_base_flags(struct tileset *ts, int offset_x, + int offset_y) + : freeciv::layer(ts, LAYER_BASE_FLAGS), m_offset_x(offset_x), + m_offset_y(offset_y) +{ +} + +std::vector layer_base_flags::fill_sprite_array( + const tile *ptile, const tile_edge *pedge, const tile_corner *pcorner, + const unit *punit, const city *pcity, const unit_type *putype) const +{ + Q_UNUSED(pedge); + Q_UNUSED(pcorner); + Q_UNUSED(punit); + Q_UNUSED(pcity); + Q_UNUSED(putype); + + if (ptile == nullptr) { + return {}; + } + + const auto eowner = extra_owner(ptile); + if (eowner == nullptr) { + return {}; + } + + extra_type_iterate(pextra) + { + if (tile_has_extra(ptile, pextra) + && extra_has_flag(pextra, EF_SHOW_FLAG)) { + bool hidden = false; + + extra_type_list_iterate(pextra->hiders, phider) + { + if (tile_has_extra(ptile, phider)) { + hidden = true; + break; + } + } + extra_type_list_iterate_end; + + if (!hidden) { + // Draw a flag for this extra + return {drawn_sprite( + tileset(), + get_nation_flag_sprite(tileset(), nation_of_player(eowner)), + true, m_offset_x, m_offset_y)}; + } + } + } + extra_type_iterate_end; + + return {}; +} + +} // namespace freeciv diff --git a/client/layer_base_flags.h b/client/layer_base_flags.h new file mode 100644 index 0000000000..2c522d6022 --- /dev/null +++ b/client/layer_base_flags.h @@ -0,0 +1,34 @@ +/*__ ___ *************************************** +/ \ / \ Copyright (c) 2021 Freeciv21 contributors. +\_ \ / __/ This file is part of Freeciv21. + _\ \ / /__ Freeciv21 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 3 of the License, + | or (at your option) any later version. + _/ /\ You should have received a copy of the GNU + /o) (o/\ \_ General Public License along with Freeciv21. + \_____/ / If not, see https://www.gnu.org/licenses/. + \____/ ********************************************************/ +#pragma once + +#include "fc_types.h" +#include "layer.h" + +namespace freeciv { + +class layer_base_flags : public layer { +public: + explicit layer_base_flags(struct tileset *ts, int offset_x, int offset_y); + + std::vector + fill_sprite_array(const tile *ptile, const tile_edge *pedge, + const tile_corner *pcorner, const unit *punit, + const city *pcity, + const unit_type *putype) const override; + +private: + int m_offset_x, m_offset_y; +}; + +} // namespace freeciv diff --git a/client/tilespec.cpp b/client/tilespec.cpp index 24ae99d351..b09a6f3d98 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -79,6 +79,7 @@ #include "goto.h" #include "helpdata.h" #include "layer_background.h" +#include "layer_base_flags.h" #include "layer_special.h" #include "options.h" // for fill_xxx #include "themes_common.h" @@ -432,7 +433,6 @@ struct tileset { struct named_sprites sprites; struct color_system *color_system; struct extra_type_list *style_lists[ESTYLE_COUNT]; - struct extra_type_list *flagged_bases_list; int num_preferred_themes; char **preferred_themes; @@ -1052,11 +1052,6 @@ static void tileset_free_toplevel(struct tileset *t) } } - if (t->flagged_bases_list != NULL) { - extra_type_list_destroy(t->flagged_bases_list); - t->flagged_bases_list = NULL; - } - for (i = 0; i < MAX_NUM_LAYERS; i++) { struct tileset_layer *tslp = &t->terrain_layers[i]; @@ -1753,6 +1748,12 @@ static void tileset_add_layer(struct tileset *t, mapview_layer layer) t->special_layers.foreground = l.get(); t->layers.emplace_back(std::move(l)); } break; + case LAYER_BASE_FLAGS: { + auto l = std::make_unique( + t, FULL_TILE_X_OFFSET + t->city_flag_offset_x, + FULL_TILE_Y_OFFSET + t->city_flag_offset_y); + t->layers.emplace_back(std::move(l)); + } break; default: t->layers.push_back(std::make_unique(t, layer)); break; @@ -2347,7 +2348,6 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, for (i = 0; i < ESTYLE_COUNT; i++) { t->style_lists[i] = extra_type_list_new(); } - t->flagged_bases_list = extra_type_list_new(); for (i = 0; (extraname = secfile_lookup_str_default( file, NULL, "extras.styles%d.name", i)); @@ -3581,10 +3581,6 @@ void tileset_setup_extra(struct tileset *t, struct extra_type *pextra) extra_type_list_append(t->style_lists[extrastyle], pextra); - if (extra_has_flag(pextra, EF_SHOW_FLAG)) { - extra_type_list_append(t->flagged_bases_list, pextra); - } - switch (extrastyle) { case ESTYLE_3LAYER: tileset_setup_base(t, pextra, tag); @@ -5658,42 +5654,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, break; case LAYER_BASE_FLAGS: - if (NULL != pterrain) { - bool show_flag = false; - struct player *eowner = extra_owner(ptile); - - /* Show base flag. Not part of previous iteration as - * "extras of ESTYLE_3_LAYER" != "bases" */ - if (eowner != NULL) { - extra_type_list_iterate(t->flagged_bases_list, pextra) - { - if (tile_has_extra(ptile, pextra)) { - bool hidden = false; - - extra_type_list_iterate(pextra->hiders, phider) - { - if (BV_ISSET(textras, extra_index(phider))) { - hidden = true; - break; - } - } - extra_type_list_iterate_end; - - if (!hidden) { - show_flag = true; - } - } - } - extra_type_list_iterate_end; - - if (show_flag) { - sprs.emplace_back( - t, get_nation_flag_sprite(t, nation_of_player(eowner)), true, - FULL_TILE_X_OFFSET + t->city_flag_offset_x, - FULL_TILE_Y_OFFSET + t->city_flag_offset_y); - } - } - } + fc_assert_ret_val(false, {}); break; case LAYER_FOG: @@ -6642,11 +6603,6 @@ void tileset_ruleset_reset(struct tileset *t) t->style_lists[i] = extra_type_list_new(); } } - - if (t->flagged_bases_list != NULL) { - extra_type_list_destroy(t->flagged_bases_list); - t->flagged_bases_list = extra_type_list_new(); - } } /** From f991cb838b6f45523d7928b6a7cee02dcb4188e9 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 22 Aug 2021 11:34:29 +0200 Subject: [PATCH 19/24] Add missing header to climap.h --- client/climap.h | 1 + 1 file changed, 1 insertion(+) diff --git a/client/climap.h b/client/climap.h index df7af69736..84fdb1ca56 100644 --- a/client/climap.h +++ b/client/climap.h @@ -12,6 +12,7 @@ // common #include "fc_types.h" // enum direction8, struct tile +#include "tile.h" // known_type enum known_type client_tile_get_known(const struct tile *ptile); From 5c59306a9f6c7b41f0085232ebf09b7b7702c552 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 22 Aug 2021 15:55:02 +0200 Subject: [PATCH 20/24] Move LAYER_DARKNESS to its own class See #430. --- client/CMakeLists.txt | 1 + client/layer_darkness.cpp | 111 +++++++++++++++++++++ client/layer_darkness.h | 79 +++++++++++++++ client/tilespec.cpp | 201 ++++++++++++++++---------------------- client/tilespec.h | 35 +++---- 5 files changed, 287 insertions(+), 140 deletions(-) create mode 100644 client/layer_darkness.cpp create mode 100644 client/layer_darkness.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 48c0d45179..55d282ee74 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -58,6 +58,7 @@ add_library( layer.cpp layer_background.cpp layer_base_flags.cpp + layer_darkness.cpp layer_special.cpp luaconsole_common.cpp mapctrl_common.cpp diff --git a/client/layer_darkness.cpp b/client/layer_darkness.cpp new file mode 100644 index 0000000000..db75bb89f4 --- /dev/null +++ b/client/layer_darkness.cpp @@ -0,0 +1,111 @@ +/*__ ___ *************************************** +/ \ / \ Copyright (c) 2021 Freeciv21 contributors. +\_ \ / __/ This file is part of Freeciv21. + _\ \ / /__ Freeciv21 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 3 of the License, + | or (at your option) any later version. + _/ /\ You should have received a copy of the GNU + /o) (o/\ \_ General Public License along with Freeciv21. + \_____/ / If not, see https://www.gnu.org/licenses/. + \____/ ********************************************************/ + +#include "layer_darkness.h" + +#include "climap.h" +#include "control.h" +#include "extras.h" +#include "game.h" +#include "map.h" +#include "sprite_g.h" +#include "tilespec.h" + +namespace freeciv { + +layer_darkness::layer_darkness(struct tileset *ts) + : freeciv::layer(ts, LAYER_DARKNESS), m_sprites{} +{ +} + +std::vector layer_darkness::fill_sprite_array( + const tile *ptile, const tile_edge *pedge, const tile_corner *pcorner, + const unit *punit, const city *pcity, const unit_type *putype) const +{ + Q_UNUSED(pedge); + Q_UNUSED(pcorner); + Q_UNUSED(putype); + + if (!ptile) { + return {}; + } + + // Don't draw darkness when the solid background is used + bool do_draw_unit = + (punit + && (gui_options.draw_units || !ptile + || (gui_options.draw_focus_unit && unit_is_in_focus(punit)))); + + if (!(gui_options.solid_color_behind_units + && (do_draw_unit || (pcity && gui_options.draw_cities)))) { + return {}; + } + + auto sprites = std::vector(); + + int i, tileno; + struct tile *adjc_tile; + + const auto is_unknown = [&](direction8 dir) { + return ((adjc_tile = mapstep(&(wld.map), ptile, (dir))) + && client_tile_get_known(adjc_tile) == TILE_UNKNOWN); + }; + + switch (m_style) { + case DARKNESS_NONE: + break; + case DARKNESS_ISORECT: + for (i = 0; i < 4; i++) { + const int W = tileset_tile_width(tileset()), + H = tileset_tile_height(tileset()); + int offsets[4][2] = {{W / 2, 0}, {0, H / 2}, {W / 2, H / 2}, {0, 0}}; + + if (is_unknown(DIR4_TO_DIR8[i])) { + sprites.emplace_back(tileset(), &m_sprites[i], true, offsets[i][0], + offsets[i][1]); + } + } + break; + case DARKNESS_CARD_SINGLE: + for (i = 0; i < tileset_num_cardinal_dirs(tileset()); i++) { + if (is_unknown(tileset_cardinal_dirs(tileset())[i])) { + sprites.emplace_back(tileset(), &m_sprites[i]); + } + } + break; + case DARKNESS_CARD_FULL: + /* We're looking to find the INDEX_NSEW for the directions that + * are unknown. We want to mark unknown tiles so that an unreal + * tile will be given the same marking as our current tile - that + * way we won't get the "unknown" dither along the edge of the + * map. */ + tileno = 0; + for (i = 0; i < tileset_num_cardinal_dirs(tileset()); i++) { + if (is_unknown(tileset_cardinal_dirs(tileset())[i])) { + tileno |= 1 << i; + } + } + + if (tileno != 0) { + sprites.emplace_back(tileset(), &m_sprites[tileno]); + } + break; + case DARKNESS_CORNER: + // Handled separately. + break; + }; + + return sprites; +} + +} // namespace freeciv diff --git a/client/layer_darkness.h b/client/layer_darkness.h new file mode 100644 index 0000000000..d554557f87 --- /dev/null +++ b/client/layer_darkness.h @@ -0,0 +1,79 @@ +/*__ ___ *************************************** +/ \ / \ Copyright (c) 2021 Freeciv21 contributors. +\_ \ / __/ This file is part of Freeciv21. + _\ \ / /__ Freeciv21 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 3 of the License, + | or (at your option) any later version. + _/ /\ You should have received a copy of the GNU + /o) (o/\ \_ General Public License along with Freeciv21. + \_____/ / If not, see https://www.gnu.org/licenses/. + \____/ ********************************************************/ +#pragma once + +#include "fc_types.h" +#include "layer.h" +#include "tilespec.h" + +#include + +namespace freeciv { + +#define SPECENUM_NAME darkness_style +// No darkness sprites are drawn. +#define SPECENUM_VALUE0 DARKNESS_NONE +#define SPECENUM_VALUE0NAME "None" +/* 1 sprite that is split into 4 parts and treated as a darkness4. Only + * works in iso-view. */ +#define SPECENUM_VALUE1 DARKNESS_ISORECT +#define SPECENUM_VALUE1NAME "IsoRect" +/* 4 sprites, one per direction. More than one sprite per tile may be + * drawn. */ +#define SPECENUM_VALUE2 DARKNESS_CARD_SINGLE +#define SPECENUM_VALUE2NAME "CardinalSingle" +/* 15=2^4-1 sprites. A single sprite is drawn, chosen based on whether + * there's darkness in _each_ of the cardinal directions. */ +#define SPECENUM_VALUE3 DARKNESS_CARD_FULL +#define SPECENUM_VALUE3NAME "CardinalFull" +// Corner darkness & fog. 3^4 = 81 sprites. +#define SPECENUM_VALUE4 DARKNESS_CORNER +#define SPECENUM_VALUE4NAME "Corner" +#include "specenum_gen.h" + +class layer_darkness : public layer { +public: + explicit layer_darkness(struct tileset *ts); + + /** + * Sets the way in which the darkness is drawn. + */ + void set_darkness_style(darkness_style style) { m_style = style; } + + /** + * Gets the way in which the darkness is drawn. + */ + darkness_style style() const { return m_style; } + + /** + * Sets one of the sprites used to draw the darkness. + */ + void set_sprite(std::size_t index, const QPixmap &p) + { + m_sprites[index] = p; + } + + std::vector + fill_sprite_array(const tile *ptile, const tile_edge *pedge, + const tile_corner *pcorner, const unit *punit, + const city *pcity, + const unit_type *putype) const override; + +private: + darkness_style m_style; + + // First unused + std::array m_sprites; +}; + +} // namespace freeciv diff --git a/client/tilespec.cpp b/client/tilespec.cpp index b09a6f3d98..9e176f07af 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -80,6 +80,7 @@ #include "helpdata.h" #include "layer_background.h" #include "layer_base_flags.h" +#include "layer_darkness.h" #include "layer_special.h" #include "options.h" // for fill_xxx #include "themes_common.h" @@ -113,11 +114,6 @@ #define TILESPEC_SUFFIX ".tilespec" #define TILE_SECTION_PREFIX "tile_" -// This the way directional indices are now encoded: -#define MAX_INDEX_CARDINAL 64 -#define MAX_INDEX_HALF 16 -#define MAX_INDEX_VALID 256 - #define NUM_TILES_HP_BAR 11 #define NUM_TILES_DIGITS 10 #define NUM_TILES_SELECT 4 @@ -126,17 +122,10 @@ #define FULL_TILE_X_OFFSET ((t->normal_tile_width - t->full_tile_width) / 2) #define FULL_TILE_Y_OFFSET (t->normal_tile_height - t->full_tile_height) -/* This could be moved to common/map.h if there's more use for it. */ -enum direction4 { DIR4_NORTH = 0, DIR4_SOUTH, DIR4_EAST, DIR4_WEST }; - -/// lol sveinung 5 in 4 static const char direction4letters[5] = "udrl"; // This must correspond to enum edge_type. static const char edge_name[EDGE_COUNT][3] = {"ns", "we", "ud", "lr"}; -static const int DIR4_TO_DIR8[4] = {DIR8_NORTH, DIR8_SOUTH, DIR8_EAST, - DIR8_WEST}; - enum match_style { MATCH_NONE, MATCH_SAME, // "boolean" match @@ -287,9 +276,8 @@ struct named_sprites { QPixmap *attention; } user; struct { - QPixmap *fog, **fullfog, - *darkness[MAX_INDEX_CARDINAL]; // first unused - } tx; // terrain extra + QPixmap *fog, **fullfog; + } tx; // terrain extra struct { QPixmap *activity, *rmact; int extrastyle; @@ -382,6 +370,7 @@ struct tileset { struct { freeciv::layer_special *background, *middleground, *foreground; } special_layers; + freeciv::layer_darkness *darkness_layer; enum ts_type type; int hex_width, hex_height; @@ -399,7 +388,6 @@ struct tileset { enum direction8 unit_default_orientation; enum fog_style fogstyle; - enum darkness_style darkness_style; int unit_flag_offset_x, unit_flag_offset_y; int city_flag_offset_x, city_flag_offset_y; @@ -421,7 +409,7 @@ struct tileset { #define NUM_CORNER_DIRS 4 int num_valid_tileset_dirs, num_cardinal_tileset_dirs; int num_index_valid, num_index_cardinal; - enum direction8 valid_tileset_dirs[8], cardinal_tileset_dirs[8]; + std::array valid_tileset_dirs, cardinal_tileset_dirs; std::array terrain_layers; QSet *specfiles; QSet *small_sprites; @@ -805,6 +793,25 @@ bool tileset_use_hard_coded_fog(const struct tileset *t) return FOG_AUTO == t->fogstyle; } +/** + * @brief Returns the number of cardinal directions used by the tileset. + * @see tileset_cardinal_dirs + */ +int tileset_num_cardinal_dirs(const struct tileset *t) +{ + return t->num_cardinal_tileset_dirs; +} + +/** + * @brief Returns the cardinal directions used by the tileset. + * + * Only the first @ref tileset_num_cardinal_dirs items should be used. + */ +std::array tileset_cardinal_dirs(const struct tileset *t) +{ + return t->cardinal_tileset_dirs; +} + /** Initialize. */ @@ -1733,6 +1740,11 @@ static void tileset_add_layer(struct tileset *t, mapview_layer layer) case LAYER_BACKGROUND: t->layers.push_back(std::make_unique(t)); break; + case LAYER_DARKNESS: { + auto l = std::make_unique(t); + t->darkness_layer = l.get(); + t->layers.emplace_back(std::move(l)); + } break; case LAYER_SPECIAL1: { auto l = std::make_unique(t, layer); t->special_layers.background = l.get(); @@ -2028,28 +2040,6 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, return nullptr; } - tstr = secfile_lookup_str(file, "tilespec.darkness_style"); - if (tstr == NULL) { - qCritical("Tileset \"%s\": no darkness_style", t->name); - tileset_stop_read(t, file, fname, sections, layer_order); - return nullptr; - } - - t->darkness_style = darkness_style_by_name(tstr, fc_strcasecmp); - if (!darkness_style_is_valid(t->darkness_style)) { - qCritical("Tileset \"%s\": unknown darkness_style \"%s\"", t->name, - tstr); - tileset_stop_read(t, file, fname, sections, layer_order); - return nullptr; - } - - if (t->darkness_style == DARKNESS_ISORECT - && (t->type == TS_OVERHEAD || t->hex_width > 0 || t->hex_height > 0)) { - qCritical("Invalid darkness style set in tileset \"%s\".", t->name); - tileset_stop_read(t, file, fname, sections, layer_order); - return nullptr; - } - if (tileset_invalid_offsets(t, file)) { qCritical("Tileset \"%s\" invalid: %s", t->name, secfile_error()); tileset_stop_read(t, file, fname, sections, layer_order); @@ -2141,6 +2131,30 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, } } + tstr = secfile_lookup_str(file, "tilespec.darkness_style"); + if (tstr == NULL) { + qCritical("Tileset \"%s\": no darkness_style", t->name); + tileset_stop_read(t, file, fname, sections, layer_order); + return nullptr; + } + + auto darkness_style = freeciv::darkness_style_by_name(tstr, fc_strcasecmp); + if (!darkness_style_is_valid(darkness_style)) { + qCritical("Tileset \"%s\": unknown darkness_style \"%s\"", t->name, + tstr); + tileset_stop_read(t, file, fname, sections, layer_order); + return nullptr; + } + + if (darkness_style == freeciv::DARKNESS_ISORECT + && (t->type == TS_OVERHEAD || t->hex_width > 0 || t->hex_height > 0)) { + qCritical("Invalid darkness style set in tileset \"%s\".", t->name); + tileset_stop_read(t, file, fname, sections, layer_order); + return nullptr; + } + + t->darkness_layer->set_darkness_style(darkness_style); + // Terrain layer info. for (i = 0; i < MAX_NUM_LAYERS; i++) { struct tileset_layer *tslp = &t->terrain_layers[i]; @@ -3246,11 +3260,11 @@ static void tileset_lookup_sprite_tags(struct tileset *t) } } - switch (t->darkness_style) { - case DARKNESS_NONE: + switch (t->darkness_layer->style()) { + case freeciv::DARKNESS_NONE: // Nothing. break; - case DARKNESS_ISORECT: { + case freeciv::DARKNESS_ISORECT: { // Isometric: take a single tx.darkness tile and split it into 4. QPixmap *darkness = load_sprite(t, QStringLiteral("tx.darkness"), true, false); @@ -3262,28 +3276,42 @@ static void tileset_lookup_sprite_tags(struct tileset *t) tileset_error(LOG_FATAL, _("Sprite tx.darkness missing.")); } for (i = 0; i < 4; i++) { - t->sprites.tx.darkness[i] = - crop_sprite(darkness, offsets[i][0], offsets[i][1], ntw / 2, - nth / 2, NULL, 0, 0, 1.0f, false); + t->darkness_layer->set_sprite( + i, *crop_sprite(darkness, offsets[i][0], offsets[i][1], ntw / 2, + nth / 2, NULL, 0, 0, 1.0f, false)); } } break; - case DARKNESS_CARD_SINGLE: + case freeciv::DARKNESS_CARD_SINGLE: for (i = 0; i < t->num_cardinal_tileset_dirs; i++) { enum direction8 dir = t->cardinal_tileset_dirs[i]; buffer = QStringLiteral("tx.darkness_%1").arg(dir_get_tileset_name(dir)); - SET_SPRITE_NOTSMOOTH(tx.darkness[i], buffer); + + const auto sprite = load_sprite(t, buffer, true, false); + if (sprite) { + t->darkness_layer->set_sprite(i, *sprite); + } else { + tileset_error(LOG_FATAL, _("Sprite for tag '%s' missing."), + qUtf8Printable(buffer)); + } } break; - case DARKNESS_CARD_FULL: + case freeciv::DARKNESS_CARD_FULL: for (i = 1; i < t->num_index_cardinal; i++) { buffer = QStringLiteral("tx.darkness_%1").arg(cardinal_index_str(t, i)); - SET_SPRITE_NOTSMOOTH(tx.darkness[i], buffer); + + const auto sprite = load_sprite(t, buffer, true, false); + if (sprite) { + t->darkness_layer->set_sprite(i, *sprite); + } else { + tileset_error(LOG_FATAL, _("Sprite for tag '%s' missing."), + qUtf8Printable(buffer)); + } } break; - case DARKNESS_CORNER: + case freeciv::DARKNESS_CORNER: t->sprites.tx.fullfog = static_cast(fc_realloc( t->sprites.tx.fullfog, 81 * sizeof(*t->sprites.tx.fullfog))); for (i = 0; i < 81; i++) { @@ -4796,8 +4824,7 @@ static void fill_terrain_sprite_blending(const struct tileset *t, * get the "unknown" dither along the edge of the map. */ for (; dir < 4; dir++) { - struct tile *tile1 = mapstep(&(wld.map), ptile, - static_cast(DIR4_TO_DIR8[dir])); + struct tile *tile1 = mapstep(&(wld.map), ptile, DIR4_TO_DIR8[dir]); struct terrain *other; if (!tile1 || client_tile_get_known(tile1) == TILE_UNKNOWN @@ -4829,7 +4856,7 @@ static void fill_fog_sprite_array(const struct tileset *t, sprs.emplace_back(t, t->sprites.tx.fog); } - if (t->darkness_style == DARKNESS_CORNER && pcorner + if (t->darkness_layer->style() == freeciv::DARKNESS_CORNER && pcorner && gui_options.draw_fog_of_war) { int i, tileno = 0; @@ -4949,8 +4976,7 @@ static void fill_terrain_sprite_array( for (i = 0; i < NUM_CORNER_DIRS; i++) { const int count = dlp->match_indices; int array_index = 0; - enum direction8 dir = - dir_ccw(static_cast(DIR4_TO_DIR8[i])); + enum direction8 dir = dir_ccw(DIR4_TO_DIR8[i]); int x = (t->type == TS_ISOMETRIC ? iso_offsets[i][0] : noniso_offsets[i][0]); int y = (t->type == TS_ISOMETRIC ? iso_offsets[i][1] @@ -5004,67 +5030,6 @@ static void fill_terrain_sprite_array( #undef MATCH } -/** - Helper function for fill_terrain_sprite_layer. - Fill in the sprite array of darkness. - */ -static void fill_terrain_sprite_darkness(struct tileset *t, - std::vector &sprs, - const struct tile *ptile, - struct terrain **tterrain_near) -{ - int i, tileno; - struct tile *adjc_tile; - -#define UNKNOWN(dir) \ - ((adjc_tile = mapstep(&(wld.map), ptile, (dir))) \ - && client_tile_get_known(adjc_tile) == TILE_UNKNOWN) - - switch (t->darkness_style) { - case DARKNESS_NONE: - break; - case DARKNESS_ISORECT: - for (i = 0; i < 4; i++) { - const int W = t->normal_tile_width, H = t->normal_tile_height; - int offsets[4][2] = {{W / 2, 0}, {0, H / 2}, {W / 2, H / 2}, {0, 0}}; - - if (UNKNOWN(static_cast(DIR4_TO_DIR8[i]))) { - sprs.emplace_back(t, t->sprites.tx.darkness[i], true, offsets[i][0], - offsets[i][1]); - } - } - break; - case DARKNESS_CARD_SINGLE: - for (i = 0; i < t->num_cardinal_tileset_dirs; i++) { - if (UNKNOWN(t->cardinal_tileset_dirs[i])) { - sprs.emplace_back(t, t->sprites.tx.darkness[i]); - } - } - break; - case DARKNESS_CARD_FULL: - /* We're looking to find the INDEX_NSEW for the directions that - * are unknown. We want to mark unknown tiles so that an unreal - * tile will be given the same marking as our current tile - that - * way we won't get the "unknown" dither along the edge of the - * map. */ - tileno = 0; - for (i = 0; i < t->num_cardinal_tileset_dirs; i++) { - if (UNKNOWN(t->cardinal_tileset_dirs[i])) { - tileno |= 1 << i; - } - } - - if (tileno != 0) { - sprs.emplace_back(t, t->sprites.tx.darkness[tileno]); - } - break; - case DARKNESS_CORNER: - // Handled separately. - break; - }; -#undef UNKNOWN -} - /** Add sprites for the base tile to the sprite list. This doesn't include specials or rivers. @@ -5450,9 +5415,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, break; case LAYER_DARKNESS: - if (NULL != pterrain && !solid_bg) { - fill_terrain_sprite_darkness(t, sprs, ptile, tterrain_near); - } + fc_assert_ret_val(false, {}); break; case LAYER_TERRAIN2: diff --git a/client/tilespec.h b/client/tilespec.h index 4ac8b2b494..3485179d7f 100644 --- a/client/tilespec.h +++ b/client/tilespec.h @@ -52,27 +52,6 @@ struct resource_type; #define SPECENUM_VALUE2NAME "Darkness" #include "specenum_gen.h" -#define SPECENUM_NAME darkness_style -// No darkness sprites are drawn. -#define SPECENUM_VALUE0 DARKNESS_NONE -#define SPECENUM_VALUE0NAME "None" -/* 1 sprite that is split into 4 parts and treated as a darkness4. Only - * works in iso-view. */ -#define SPECENUM_VALUE1 DARKNESS_ISORECT -#define SPECENUM_VALUE1NAME "IsoRect" -/* 4 sprites, one per direction. More than one sprite per tile may be - * drawn. */ -#define SPECENUM_VALUE2 DARKNESS_CARD_SINGLE -#define SPECENUM_VALUE2NAME "CardinalSingle" -/* 15=2^4-1 sprites. A single sprite is drawn, chosen based on whether - * there's darkness in _each_ of the cardinal directions. */ -#define SPECENUM_VALUE3 DARKNESS_CARD_FULL -#define SPECENUM_VALUE3NAME "CardinalFull" -// Corner darkness & fog. 3^4 = 81 sprites. -#define SPECENUM_VALUE4 DARKNESS_CORNER -#define SPECENUM_VALUE4NAME "Corner" -#include "specenum_gen.h" - #define SPECENUM_NAME extrastyle_id #define SPECENUM_VALUE0 ESTYLE_ROAD_ALL_SEPARATE #define SPECENUM_VALUE0NAME "RoadAllSeparate" @@ -93,12 +72,23 @@ struct resource_type; #define SPECENUM_COUNT ESTYLE_COUNT #include "specenum_gen.h" +// This the way directional indices are now encoded: +#define MAX_INDEX_CARDINAL 64 +#define MAX_INDEX_HALF 16 +#define MAX_INDEX_VALID 256 + #define NUM_TILES_PROGRESS 8 #define MAX_NUM_CITIZEN_SPRITES 6 enum arrow_type { ARROW_RIGHT, ARROW_PLUS, ARROW_MINUS, ARROW_LAST }; +// This could be moved to common/map.h if there's more use for it. +enum direction4 { DIR4_NORTH = 0, DIR4_SOUTH, DIR4_EAST, DIR4_WEST }; + +constexpr direction8 DIR4_TO_DIR8[4] = {DIR8_NORTH, DIR8_SOUTH, DIR8_EAST, + DIR8_WEST}; + struct tileset; extern struct tileset *tileset; @@ -298,6 +288,9 @@ const char *tileset_main_intro_filename(const struct tileset *t); int tileset_num_city_colors(const struct tileset *t); bool tileset_use_hard_coded_fog(const struct tileset *t); +int tileset_num_cardinal_dirs(const struct tileset *t); +std::array tileset_cardinal_dirs(const struct tileset *t); + /* These are used as array index -> can't be changed freely to values bigger than size of those arrays. */ #define TS_TOPO_SQUARE 0 From 15b4f27cc93b56d6ba9270e2800e3fe3044f4ae5 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 22 Aug 2021 17:34:34 +0200 Subject: [PATCH 21/24] Add helper functions to the base layer class They correspond to two variables declared at the beginning of fill_sprite_array(), do_draw_unit and solid_bg, that are used by several layer types. See #430. --- client/layer.cpp | 55 +++++++++++++++++++++++++++++++++++++++ client/layer.h | 4 +++ client/layer_darkness.cpp | 8 +----- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/client/layer.cpp b/client/layer.cpp index e281ce3403..7d7a4ee94b 100644 --- a/client/layer.cpp +++ b/client/layer.cpp @@ -11,6 +11,7 @@ \_____/ / If not, see https://www.gnu.org/licenses/. \____/ ********************************************************/ +#include "control.h" #include "tilespec.h" #include "layer.h" @@ -35,4 +36,58 @@ layer::fill_sprite_array(const tile *ptile, const tile_edge *pedge, pcity, putype); } +/** + * @brief Whether a unit should be drawn. + * @param ptile The tile where to draw (can be null) + * @param punit The unit that should be drawn (can be null) + */ +bool layer::do_draw_unit(const tile *ptile, const unit *punit) const +{ + if (!punit) { + // Can't draw a non-existing unit. + return false; + } + + // There is a unit. + + if (!ptile) { + // No tile (so not on the map) => always draw. + return true; + } + + // Handle options to turn off drawing units. + if (gui_options.draw_units) { + return true; + } else if (gui_options.draw_focus_unit && unit_is_in_focus(punit)) { + return true; + } else { + return false; + } +} + +/** + * @brief Whether a solid background should be drawn on a tile instead of its + * terrain. + * + * Query this function to know whether you should refrain from drawing + * "terrain-like" sprites (terrain, darkness, water follow this setting). + * + * @returns `true` when the solid background is enabled and a unit or city is + * drawn on the tile. + * + * @param ptile The tile where to draw (can be null) + * @param punit The unit that could be drawn (can be null) + * @param pcity The city that could be drawn (can be null) + */ +bool layer::solid_background(const tile *ptile, const unit *punit, + const city *pcity) const +{ + if (!gui_options.solid_color_behind_units) { + // Solid background turned off (the default). + return false; + } + + return do_draw_unit(ptile, punit) || (gui_options.draw_cities && pcity); +} + } // namespace freeciv diff --git a/client/layer.h b/client/layer.h index b1222a0bd3..0e0ed93cdc 100644 --- a/client/layer.h +++ b/client/layer.h @@ -161,6 +161,10 @@ class layer { protected: struct tileset *tileset() const { return m_ts; } + bool do_draw_unit(const tile *ptile, const unit *punit) const; + bool solid_background(const tile *ptile, const unit *punit, + const city *pcity) const; + private: struct tileset *m_ts; mapview_layer m_layer; diff --git a/client/layer_darkness.cpp b/client/layer_darkness.cpp index db75bb89f4..6082439297 100644 --- a/client/layer_darkness.cpp +++ b/client/layer_darkness.cpp @@ -41,13 +41,7 @@ std::vector layer_darkness::fill_sprite_array( } // Don't draw darkness when the solid background is used - bool do_draw_unit = - (punit - && (gui_options.draw_units || !ptile - || (gui_options.draw_focus_unit && unit_is_in_focus(punit)))); - - if (!(gui_options.solid_color_behind_units - && (do_draw_unit || (pcity && gui_options.draw_cities)))) { + if (!solid_background(ptile, punit, pcity)) { return {}; } From 7fa450cef8afab32685ef32cd9b2b913fcedb1e3 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Mon, 30 Aug 2021 03:07:14 +0200 Subject: [PATCH 22/24] Migrate LAYER_TERRAIN* to a class This is a very large one with heavy refactoring. The code was split in smaller functions, boosting readability. Documentation was also improved. No new functionality was added. The unused is_reversed flag was dropped; it was not used in any known tileset and layer_order provides a better alternative. The factorization makes it very straightforward to add more than 3 terrain layers, limited only by the tileset reading code. All shipped tilesets load correctly. See #430. --- client/CMakeLists.txt | 1 + client/layer.h | 9 + client/layer_terrain.cpp | 843 ++++++++++++++++++++++++++++++++++ client/layer_terrain.h | 141 ++++++ client/tilespec.cpp | 954 +++++++-------------------------------- client/tilespec.h | 7 + doc/README.graphics | 9 +- 7 files changed, 1160 insertions(+), 804 deletions(-) create mode 100644 client/layer_terrain.cpp create mode 100644 client/layer_terrain.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 55d282ee74..0013daa4e8 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -60,6 +60,7 @@ add_library( layer_base_flags.cpp layer_darkness.cpp layer_special.cpp + layer_terrain.cpp luaconsole_common.cpp mapctrl_common.cpp mapview_common.cpp diff --git a/client/layer.h b/client/layer.h index 0e0ed93cdc..75cae127cf 100644 --- a/client/layer.h +++ b/client/layer.h @@ -17,6 +17,7 @@ class QPixmap; struct city; struct player; +struct terrain; struct tileset; struct unit; struct unit_type; @@ -156,6 +157,14 @@ class layer { */ virtual void free_player(int player_id) { Q_UNUSED(player_id); } + /** + * Initializes terrain-specific data. + */ + virtual void initialize_terrain(const terrain *terrain) + { + Q_UNUSED(terrain); + } + mapview_layer type() const { return m_layer; } protected: diff --git a/client/layer_terrain.cpp b/client/layer_terrain.cpp new file mode 100644 index 0000000000..6262a38f2a --- /dev/null +++ b/client/layer_terrain.cpp @@ -0,0 +1,843 @@ +/*__ ___ *************************************** +/ \ / \ Copyright (c) 2021 Freeciv21 contributors. +\_ \ / __/ This file is part of Freeciv21. + _\ \ / /__ Freeciv21 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 3 of the License, + | or (at your option) any later version. + _/ /\ You should have received a copy of the GNU + /o) (o/\ \_ General Public License along with Freeciv21. + \_____/ / If not, see https://www.gnu.org/licenses/. + \____/ ********************************************************/ + +#include "layer_terrain.h" + +#include "climap.h" +#include "extras.h" +#include "game.h" // For extra_type_iterate +#include "map.h" +#include "rand.h" +#include "sprite_g.h" +#include "tilespec.h" + +/** + * \class freeciv::layer_terrain + * \brief Draws terrain sprites on the map. + * + * Terrain layers are a core feature of the map. They are used to draw + * sprites representing terrain. Up to three layers can be used. Within each + * layer, one can choose how each terrain will be drawn. + * + * The configuration is based on *tags* that are used when constructing + * sprite identifiers. Tags represent sets of related sprites that, drawn in + * a specific way, construct the visual representation of a tile. The tag + * used to represent a terrain is taken from its `graphic_str` or + * `graphic_alt` properties. + * + * Each tag has a set of properties that influence how sprites are drawn. The + * offsets at which the sprites are drawn is set using \ref set_tag_offsets. + * The tiles can be drawn in two ways: either a single sprite is used for the + * whole tile (\ref CELL_WHOLE), or one sprite is used to draw each corner of + * the tile + * (\ref CELL_CORNER). This is set with \ref set_tag_sprite_type. "Matching" + * with adjacent terrain types provides a powerful mechanism for + * sophisticated effects. For isometric tilesets, it is also possible to use + * a mask to blend adjacent tiles together with \ref enable_blending. + * + * Matching + * -------- + * + * The sprites used to draw a tile can depend on adjacent terrains, allowing + * the representation of continuous coasts, ridges and other edges. + * + * To use tag matching, one first defines a number of terrain groups ("match + * type" in `spec` files). Groups are created with \ref + * create_matching_group. Every tag must be in a group, set with \ref + * set_tag_matching_group. The first letter of group names must be unique + * within a layer. Each tag can then be matched against any number of groups. + * There will be one sprite for each combination of groups next to the tile + * of interest. + * + * The simplest matching is no matching, in which case the sprite used + * doesn't depend on adjacent terrain. This is available for both \ref + * CELL_WHOLE and \ref CELL_CORNER, although there is little use for the + * second. The sprite names for \ref CELL_WHOLE are formed like + * `t.l0.grassland1`, where 0 is the layer number, `grassland` appears in the + * name of the `tilespec` section, and 1 is an index (when there are several + * sprites with indices 1, 2, 3, ..., one is picked at random). For + * \ref CELL_CORNER, the names are like `t.l0.grassland_cell_u`, where `u` + * ("up") indicates the direction of the corner. It can be `u` ("up"), `d` + * ("down"), `r` ("right"), or `l` ("left"). + * + * When a tag is matched against one group, there are two possibilities: + * + * * The matched group is the same as the tag group. For \ref CELL_WHOLE, + * this requires sprites with names like `t.l0.grassland_n1e0s0w0`, where the + * `n1` indicates that the terrain in the north direction is in the same + * group as the tile that is being drawn, and the 0's indicate that other + * terrains are different. Sprites must be provided for all 16 combinations + * of 0's and 1's. Amplio2 forests and hills are drawn this way. + * + * For \ref CELL_CORNER, this requires 24 sprites with names like + * `t.l0.grassland_cell_u010`. `t.l0.grassland_cell_u` is like in the no + * match case, and `010` indicates which sides of the corner match the + * terrain being drawn. Amplio2 ice uses this method. + * + * * The matched group is different from the tag group (only supported for + * \ref CELL_CORNER). There are again 24 sprites, this time with names + * like `t.l0.grassland_cell_u_a_a_b`. The first letter, in this example `u`, + * is the direction of the corner. The other three indicate which terrains + * are found on the three external sides of the corner. They are the first + * letter of the name of a matching group: the group being matched against if + * the adjacent terrain is of that group, and otherwise the group of the + * sprite being drawn. The coasts of Amplio2 lakes use this method. + * + * When more than one group is used, which is only supported for + * \ref CELL_CORNER, the sprites are named like `t.l0.cellgroup_a_b_c_d`. + * Each sprite represents the junction of four tiles with the group names + * first letters `a`, `b`, `c`, and `d`. Each sprite is split in four to + * provide four corner sprites. Amplio2 coasts are drawn this way. + */ + +namespace { +static const char direction4letters[5] = "udrl"; +} + +namespace freeciv { + +layer_terrain::layer_terrain(struct tileset *ts, int number) + : freeciv::layer(ts, LAYER_TERRAIN1), m_number(number) +{ +} + +/** + * \brief Creates a matching group with the given name. + * \returns Whether the operation succeeded. + */ +bool layer_terrain::create_matching_group(const QString &name) +{ + if (name.isEmpty()) { + return false; + } + + if (m_matching_groups.count(name.at(0)) != 0) { + tileset_error( + LOG_FATAL, + _("[layer%d] match_types: \"%s\" initial ('%c') is not unique."), + m_number, qUtf8Printable(name), qUtf8Printable(name.at(0))); + return false; + } + + m_matching_groups[name.at(0)] = + matching_group{static_cast(m_matching_groups.size()), name}; + + return true; +} + +/** + * \brief Makes a terrain tag available for use by this layer. + * + * This function only makes the tag available. Its properties must be set + * using the `set_tag_*` functions. + * + * \returns True if the tag did not exist. + */ +bool layer_terrain::add_tag(const QString &tag, const QString &sprite_name) +{ + bool ok = true; + if (m_terrain_tag_info.count(tag) > 0) { + qWarning("Multiple tile sections containing terrain tag \"%s\".", + qUtf8Printable(tag)); + ok = false; + } + + m_terrain_tag_info[tag].sprite_name = sprite_name; // Creates the data + return ok; +} + +/** + * \brief Sets the type of sprite used to draw the specified tag. + * + * The tag must have been created using \ref add_tag. + */ +bool layer_terrain::set_tag_sprite_type(const QString &tag, sprite_type type) +{ + // FIXME The ancient code did not handle CELL_CORNER for "tall" terrain or + // sprite offsets. Does the new code work support that? + m_terrain_tag_info.at(tag).type = type; + return true; +} + +/** + * \brief Sets the offsets used to draw the specified tag. + * + * The tag must have been created using \ref add_tag. + */ +bool layer_terrain::set_tag_offsets(const QString &tag, int offset_x, + int offset_y) +{ + m_terrain_tag_info.at(tag).offset_x = offset_x; + m_terrain_tag_info.at(tag).offset_y = offset_y; + return true; +} + +/** + * \brief Sets the matching group for the specified tag. + * + * The tag must have been created using \ref add_tag. + */ +bool layer_terrain::set_tag_matching_group(const QString &tag, + const QString &group_name) +{ + if (auto g = group(group_name); g != nullptr) { + m_terrain_tag_info.at(tag).group = g; + // Tags always match with themselves + m_terrain_tag_info.at(tag).matches_with.push_back(g); + return true; + } else { + return false; + } +} + +/** + * \brief Sets the specified tag to be matched against a group. + * + * The tag must have been created using \ref add_tag. + */ +bool layer_terrain::set_tag_matches_with(const QString &tag, + const QString &group_name) +{ + if (auto g = group(group_name); g != nullptr) { + m_terrain_tag_info.at(tag).matches_with.push_back(g); + return true; + } else { + return false; + } +} + +/** + * \brief Enable blending on this layer for the given terrain tag. + */ +void layer_terrain::enable_blending(const QString &tag) +{ + if (!tileset_is_isometric(tileset())) { + qCritical() << "Blending is only supported for isometric tilesets"; + } + // Create the entry + m_terrain_tag_info.at(tag).blend = true; +} + +/** + * \brief Sets up the structure to draw the specified terrain. + */ +void layer_terrain::initialize_terrain(const terrain *terrain) +{ + // Find the good terrain_info + terrain_info info; + if (m_terrain_tag_info.count(terrain->graphic_str) > 0) { + info = m_terrain_tag_info[terrain->graphic_str]; + } else if (m_terrain_tag_info.count(terrain->graphic_alt) > 0) { + info = m_terrain_tag_info[terrain->graphic_alt]; + } else if (m_number == 0) { + // All terrains should be present in layer 0... + tileset_error(LOG_WARN, + _("Terrain \"%s\": no graphic tile \"%s\" or \"%s\"."), + terrain_rule_name(terrain), terrain->graphic_str, + terrain->graphic_alt); + return; + } else { + // Terrain is not drawn by this layer + return; + } + + // Determine the match style + switch (info.matches_with.size()) { + case 0: + case 1: + info.style = MATCH_NONE; + break; + case 2: + if (info.matches_with.front() == info.matches_with.back()) { + info.style = MATCH_SAME; + } else { + info.style = MATCH_PAIR; + } + break; + default: + info.style = MATCH_FULL; + break; + } + + // Build graphics data + // TODO Use inheritance? + switch (info.type) { + case CELL_WHOLE: + switch (info.style) { + case MATCH_NONE: + initialize_cell_whole_match_none(terrain, info); + break; + case MATCH_SAME: + initialize_cell_whole_match_same(terrain, info); + break; + case MATCH_PAIR: + case MATCH_FULL: + qWarning() << "Not implemented CELL_WHOLE + MATCH_FULL/MATCH_PAIR."; + return; + } + break; + case CELL_CORNER: + switch (info.style) { + case MATCH_NONE: + initialize_cell_corner_match_none(terrain, info); + break; + case MATCH_SAME: + initialize_cell_corner_match_same(terrain, info); + break; + case MATCH_PAIR: + initialize_cell_corner_match_pair(terrain, info); + break; + case MATCH_FULL: + initialize_cell_corner_match_full(terrain, info); + break; + } + break; + } + + // Blending + initialize_blending(terrain, info); + + // Copy it to the terrain index + m_terrain_info[terrain_index(terrain)] = info; +} + +/** + * \brief Sets up terrain information for \ref CELL_WHOLE and `MATCH_SAME`. + */ +void layer_terrain::initialize_cell_whole_match_none(const terrain *terrain, + terrain_info &info) +{ + QString buffer; + + // Load whole sprites for this tile. + for (int i = 0;; i++) { + buffer = QStringLiteral("t.l%1.%2%3") + .arg(m_number) + .arg(info.sprite_name) + .arg(i + 1); + auto sprite = load_sprite(tileset(), buffer, true, false); + if (!sprite) { + break; + } + info.sprites.push_back(sprite); + } + + // check for base sprite, allowing missing sprites above base + if (m_number == 0 && info.sprites.empty()) { + // TRANS: 'base' means 'base of terrain gfx', not 'military base' + tileset_error(LOG_FATAL, _("Missing base sprite for tag \"%s\"."), + qUtf8Printable(buffer)); + } +} + +/** + * \brief Sets up terrain information for \ref CELL_WHOLE and `MATCH_SAME`. + */ +void layer_terrain::initialize_cell_whole_match_same(const terrain *terrain, + terrain_info &info) +{ + // Load 16 cardinally-matched sprites. + for (int i = 0; i < tileset_num_index_cardinals(tileset()); i++) { + auto buffer = QStringLiteral("t.l%1.%2_%3") + .arg(m_number) + .arg(info.sprite_name) + .arg(cardinal_index_str(tileset(), i)); + info.sprites.push_back(tiles_lookup_sprite_tag_alt( + tileset(), LOG_FATAL, qUtf8Printable(buffer), "", "matched terrain", + terrain_rule_name(terrain), true)); + } +} + +/** + * \brief Sets up terrain information for \ref CELL_CORNER and `MATCH_NONE`. + */ +void layer_terrain::initialize_cell_corner_match_none(const terrain *terrain, + terrain_info &info) +{ + // Determine how many sprites we need + int number = NUM_CORNER_DIRS; + + // Load the sprites + for (int i = 0; i < number; i++) { + enum direction4 dir = static_cast(i % NUM_CORNER_DIRS); + auto buffer = QStringLiteral("t.l%1.%2_cell_%3") + .arg(m_number) + .arg(info.sprite_name) + .arg(direction4letters[dir]); + info.sprites.push_back(tiles_lookup_sprite_tag_alt( + tileset(), LOG_FATAL, qUtf8Printable(buffer), "", "cell terrain", + terrain_rule_name(terrain), true)); + } +} + +/** + * \brief Sets up terrain information for \ref CELL_CORNER and `MATCH_SAME`. + */ +void layer_terrain::initialize_cell_corner_match_same(const terrain *terrain, + terrain_info &info) +{ + // Determine how many sprites we need + int number = NUM_CORNER_DIRS * 2 * 2 * 2; + + // Load the sprites + for (int i = 0; i < number; i++) { + enum direction4 dir = static_cast(i % NUM_CORNER_DIRS); + int value = i / NUM_CORNER_DIRS; + + auto buffer = QStringLiteral("t.l%1.%2_cell_%3%4%5%6") + .arg(m_number) + .arg(info.sprite_name) + .arg(direction4letters[dir]) + .arg(value & 1) + .arg((value >> 1) & 1) + .arg((value >> 2) & 1); + info.sprites.push_back(tiles_lookup_sprite_tag_alt( + tileset(), LOG_FATAL, qUtf8Printable(buffer), "", + "same cell terrain", terrain_rule_name(terrain), true)); + } +} + +/** + * \brief Sets up terrain information for \ref CELL_CORNER and `MATCH_PAIR`. + */ +void layer_terrain::initialize_cell_corner_match_pair(const terrain *terrain, + terrain_info &info) +{ + // Determine how many sprites we need + int number = NUM_CORNER_DIRS * 2 * 2 * 2; + + // Load the sprites + for (int i = 0; i < number; i++) { + enum direction4 dir = static_cast(i % NUM_CORNER_DIRS); + int value = i / NUM_CORNER_DIRS; + + QChar letters[2] = {info.group->name[0], + info.matches_with.front()->name[0]}; + auto buffer = QStringLiteral("t.l%1.%2_cell_%3_%4_%5_%6") + .arg(m_number) + .arg(info.sprite_name, QChar(direction4letters[dir]), + letters[value & 1], letters[(value >> 1) & 1], + letters[(value >> 2) & 1]); + + info.sprites.push_back(tiles_lookup_sprite_tag_alt( + tileset(), LOG_FATAL, qUtf8Printable(buffer), "", + "cell pair terrain", terrain_rule_name(terrain), true)); + } +} + +/** + * \brief Sets up terrain information for \ref CELL_CORNER and `MATCH_FULL`. + */ +void layer_terrain::initialize_cell_corner_match_full(const terrain *terrain, + terrain_info &info) +{ + // Determine how many sprites we need + // N directions (NSEW) * 3 dimensions of matching + // could use exp() or expi() here? + const int count = info.matches_with.size(); + const int number = NUM_CORNER_DIRS * count * count * count; + + // Load the sprites + for (int i = 0; i < number; i++) { + enum direction4 dir = static_cast(i % NUM_CORNER_DIRS); + int value = i / NUM_CORNER_DIRS; + + const auto g1 = info.matches_with[value % count]; + value /= count; + const auto g2 = info.matches_with[value % count]; + value /= count; + const auto g3 = info.matches_with[value % count]; + + const matching_group *n, *s, *e, *w; + // Assume merged cells. This should be a separate option. + switch (dir) { + case DIR4_NORTH: + s = info.group; + w = g1; + n = g2; + e = g3; + break; + case DIR4_EAST: + w = info.group; + n = g1; + e = g2; + s = g3; + break; + case DIR4_SOUTH: + n = info.group; + e = g1; + s = g2; + w = g3; + break; + case DIR4_WEST: + default: // avoid warnings + e = info.group; + s = g1; + w = g2; + n = g3; + break; + }; + + // Use first character of match_types, already checked for uniqueness. + auto buffer = QStringLiteral("t.l%1.cellgroup_%2_%3_%4_%5") + .arg(QString::number(m_number), n->name[0], e->name[0], + s->name[0], w->name[0]); + auto sprite = load_sprite(tileset(), buffer, true, false); + + if (sprite) { + // Crop the sprite to separate this cell. + const int W = sprite->width(); + const int H = sprite->height(); + int x[4] = {W / 4, W / 4, 0, W / 2}; + int y[4] = {H / 2, 0, H / 4, H / 4}; + int xo[4] = {0, 0, -W / 2, W / 2}; + int yo[4] = {H / 2, -H / 2, 0, 0}; + + sprite = crop_sprite(sprite, x[dir], y[dir], W / 2, H / 2, + get_mask_sprite(tileset()), xo[dir], yo[dir], + 1.0f, false); + // We allocated new sprite with crop_sprite. Store its address so we + // can free it. + m_allocated.push_back(sprite); + } else { + qCritical("Terrain graphics sprite for tag \"%s\" missing.", + qUtf8Printable(buffer)); + } + + info.sprites.push_back(sprite); + } +} + +/** + * \brief Initializes blending sprites. + */ +void layer_terrain::initialize_blending(const terrain *terrain, + terrain_info &info) +{ + // Get the blending info + if (!info.blend) { + // No blending + return; + } + + // try an optional special name + auto buffer = QStringLiteral("t.blend.%1").arg(info.sprite_name); + auto blender = tiles_lookup_sprite_tag_alt( + tileset(), LOG_VERBOSE, qUtf8Printable(buffer), "", "blend terrain", + terrain_rule_name(terrain), true); + + if (blender == nullptr) { + // try an unloaded base name + // Need to pass "1" as an argument because %21 is interpreted as argument + // 21 + buffer = QStringLiteral("t.l%1.%2%3") + .arg(m_number) + .arg(info.sprite_name) + .arg(1); + blender = tiles_lookup_sprite_tag_alt( + tileset(), LOG_ERROR, qUtf8Printable(buffer), "", + "base (blend) terrain", terrain_rule_name(terrain), true); + } + + if (blender == nullptr) { + qCritical( + "Cannot find sprite for blending terrain with tag %s on layer %d", + qUtf8Printable(info.sprite_name), m_number); + return; + } + + // Set up blending sprites. This only works in iso-view! + const int W = tileset_tile_width(tileset()); + const int H = tileset_tile_height(tileset()); + const int offsets[4][2] = {{W / 2, 0}, {0, H / 2}, {W / 2, H / 2}, {0, 0}}; + int dir = 0; + + for (; dir < 4; dir++) { + info.blend_sprites[dir] = + crop_sprite(blender, offsets[dir][0], offsets[dir][1], W / 2, H / 2, + get_dither_sprite(tileset()), 0, 0, 1.0f, false); + } +} + +/** + * \implements layer::fill_sprite_array + */ +std::vector layer_terrain::fill_sprite_array( + const tile *ptile, const tile_edge *pedge, const tile_corner *pcorner, + const unit *punit, const city *pcity, const unit_type *putype) const +{ + if (ptile == nullptr) { + return {}; + } + + const auto terrain = ptile->terrain; + if (terrain == nullptr) { + return {}; + } + + // Don't draw terrain when the solid background is used + if (solid_background(ptile, punit, pcity)) { + return {}; + } + + auto sprites = std::vector(); + + // Handle scenario-defined sprites: scenarios can instruct the client to + // draw a specific sprite at some location. + // FIXME: this should avoid calling load_sprite since it's slow and + // increases the refcount without limit. + if (QPixmap * sprite; + ptile->spec_sprite + && (sprite = + load_sprite(tileset(), ptile->spec_sprite, true, false))) { + if (m_number == 0) { + sprites.emplace_back(tileset(), sprite); + } + // Skip the normal drawing process. + return sprites; + } + + struct terrain *terrain_near[8] = {nullptr}; + bv_extras extras_near[8]; // dummy + build_tile_data(ptile, terrain, terrain_near, extras_near); + + fill_terrain_sprite_array(sprites, ptile, terrain, terrain_near); + fill_blending_sprite_array(sprites, ptile, terrain, terrain_near); + + return sprites; +} + +/** + * Retrieves the group structure of the provided name. + * \param name The name to look for. + * \returns The terrain group structure, or `nullptr` on failure. + */ +layer_terrain::matching_group *layer_terrain::group(const QString &name) +{ + if (name.isEmpty() || m_matching_groups.count(name[0]) == 0) { + qCritical() << "No matching group called" << name; + return nullptr; + } + + auto candidate = &m_matching_groups.at(name[0]); + if (candidate->name == name) { + return candidate; + } else { + // Should not happen + return nullptr; + } +} + +/** + * Retrieves the group number for a given terrain. + * \param pterrain The terrain to look for. Can be null. + * \returns The terrain group number, or -1 if not drawn in this layer. + */ +int layer_terrain::terrain_group(const terrain *pterrain) const +{ + if (!pterrain || m_terrain_info.count(terrain_index(pterrain)) == 0) { + return -1; + } + + return m_terrain_info.at(terrain_index(pterrain)).group->number; +} + +/** + * Helper function for fill_sprite_array. + */ +void layer_terrain::fill_terrain_sprite_array( + std::vector &sprs, const tile *ptile, + const terrain *pterrain, terrain **tterrain_near) const +{ + if (m_terrain_info.find(terrain_index(pterrain)) == m_terrain_info.end()) { + // Not drawn in this layer + return; + } + + const auto info = m_terrain_info.at(terrain_index(pterrain)); + +#define MATCH(dir) terrain_group(tterrain_near[(dir)]) + + switch (info.type) { + case CELL_WHOLE: { + switch (info.style) { + case MATCH_NONE: { + if (!info.sprites.empty()) { + /* Pseudo-random reproducable algorithm to pick a sprite. Use + * modulo to limit the number to a handleable size [0..32000). */ + const int i = + fc_randomly(tile_index(ptile) % 32000, info.sprites.size()); + sprs.emplace_back(tileset(), info.sprites[i], true, info.offset_x, + info.offset_y); + } + break; + } + case MATCH_SAME: { + fc_assert_ret(info.matches_with.size() == 2); + fc_assert_ret(info.sprites.size() + == tileset_num_index_cardinals(tileset())); + int tileno = 0; + + for (int i = 0; i < tileset_num_cardinal_dirs(tileset()); i++) { + enum direction8 dir = tileset_cardinal_dirs(tileset())[i]; + + if (MATCH(dir) == info.matches_with.back()->number) { + tileno |= 1 << i; + } + } + sprs.emplace_back(tileset(), info.sprites[tileno], true, info.offset_x, + info.offset_y); + break; + } + case MATCH_PAIR: + case MATCH_FULL: + fc_assert(false); // not yet defined + break; + }; + break; + } + case CELL_CORNER: { + /* Divide the tile up into four rectangular cells. Each of these + * cells covers one corner, and each is adjacent to 3 different + * tiles. For each cell we pick a sprite based upon the adjacent + * terrains at each of those tiles. Thus, we have 8 different sprites + * for each of the 4 cells (32 sprites total). + * + * These arrays correspond to the direction4 ordering. */ + const int W = tileset_tile_width(tileset()); + const int H = tileset_tile_height(tileset()); + const int iso_offsets[4][2] = { + {W / 4, 0}, {W / 4, H / 2}, {W / 2, H / 4}, {0, H / 4}}; + const int noniso_offsets[4][2] = { + {0, 0}, {W / 2, H / 2}, {W / 2, 0}, {0, H / 2}}; + + // put corner cells + for (int i = 0; i < NUM_CORNER_DIRS; i++) { + const int count = info.matches_with.size(); + enum direction8 dir = dir_ccw(DIR4_TO_DIR8[i]); + int x = (tileset_is_isometric(tileset()) ? iso_offsets[i][0] + : noniso_offsets[i][0]); + int y = (tileset_is_isometric(tileset()) ? iso_offsets[i][1] + : noniso_offsets[i][1]); + int m[3] = {MATCH(dir_ccw(dir)), MATCH(dir), MATCH(dir_cw(dir))}; + + int array_index = 0; + switch (info.style) { + case MATCH_NONE: + // We have no need for matching, just plug the piece in place. + break; + case MATCH_SAME: + fc_assert_ret(info.matches_with.size() == 2); + array_index = array_index * 2 + (m[2] != info.group->number); + array_index = array_index * 2 + (m[1] != info.group->number); + array_index = array_index * 2 + (m[0] != info.group->number); + break; + case MATCH_PAIR: { + fc_assert_ret(info.matches_with.size() == 2); + const auto that = info.matches_with.back()->number; + array_index = array_index * 2 + (m[2] == that); + array_index = array_index * 2 + (m[1] == that); + array_index = array_index * 2 + (m[0] == that); + } break; + case MATCH_FULL: + default: { + int n[3]; + for (int j = 0; j < 3; j++) { + for (int k = 0; k < count; k++) { + n[j] = k; // default to last entry + if (m[j] == info.matches_with[k]->number) { + break; + } + } + } + array_index = array_index * count + n[2]; + array_index = array_index * count + n[1]; + array_index = array_index * count + n[0]; + } break; + }; + array_index = array_index * NUM_CORNER_DIRS + i; + + const auto sprite = info.sprites[array_index]; + if (sprite) { + sprs.emplace_back(tileset(), sprite, true, x, y); + } + } + break; + } + }; +#undef MATCH +} + +/** + * Helper function for fill_sprite_array. + * Fill in the sprite array for blended terrain. + * + * This function assumes that m_blending contains a value for pterrain. + */ +void layer_terrain::fill_blending_sprite_array( + std::vector &sprs, const tile *ptile, + const terrain *pterrain, terrain **tterrain_near) const +{ + // By how much the blending sprites need to be offset + const int W = tileset_tile_width(tileset()); + const int H = tileset_tile_height(tileset()); + const int offsets[4][2] = {{W / 2, 0}, {0, H / 2}, {W / 2, H / 2}, {0, 0}}; + + // Not drawn + if (m_terrain_info.count(terrain_index(pterrain)) == 0) { + return; + } + + // Not blended + auto info = m_terrain_info.at(terrain_index(pterrain)); + if (!info.blend) { + return; + } + + for (int dir = 0; dir < 4; dir++) { + struct tile *neighbor = mapstep(&(wld.map), ptile, DIR4_TO_DIR8[dir]); + struct terrain *other; + + // No other tile, don't "blend" at the edge of the map + if (!neighbor) { + continue; + } + + // Other tile is unknown + if (client_tile_get_known(neighbor) == TILE_UNKNOWN) { + continue; + } + + // No blending between identical terrains + if (pterrain == (other = tterrain_near[DIR4_TO_DIR8[dir]])) { + continue; + } + + // Other terrain is not drawn + if (m_terrain_info.count(terrain_index(other)) == 0) { + continue; + } + + // Other terrain is not blended + auto other_info = m_terrain_info.at(terrain_index(other)); + if (!other_info.blend) { + continue; + } + + // Pick the blending sprite and add it + sprs.emplace_back(tileset(), other_info.blend_sprites.at(dir), true, + offsets[dir][0], offsets[dir][1]); + } +} + +} // namespace freeciv diff --git a/client/layer_terrain.h b/client/layer_terrain.h new file mode 100644 index 0000000000..ddcf5adeda --- /dev/null +++ b/client/layer_terrain.h @@ -0,0 +1,141 @@ +/*__ ___ *************************************** +/ \ / \ Copyright (c) 2021 Freeciv21 contributors. +\_ \ / __/ This file is part of Freeciv21. + _\ \ / /__ Freeciv21 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 3 of the License, + | or (at your option) any later version. + _/ /\ You should have received a copy of the GNU + /o) (o/\ \_ General Public License along with Freeciv21. + \_____/ / If not, see https://www.gnu.org/licenses/. + \____/ ********************************************************/ +#pragma once + +#include "fc_types.h" +#include "layer.h" +#include "tilespec.h" + +#include + +namespace freeciv { + +class layer_terrain : public layer { +public: + /// Indicates how many sprites are used to draw a tile. + enum sprite_type { + CELL_WHOLE, ///< One sprite for the entire tile. + CELL_CORNER ///< One sprite for each corner of the tile. + }; + +private: + struct matching_group { + int number; + QString name; + }; + + enum match_style { + MATCH_NONE, + MATCH_SAME, // "boolean" match + MATCH_PAIR, + MATCH_FULL + }; + + struct terrain_info { + sprite_type type = CELL_WHOLE; + QString sprite_name; + int offset_x = 0, offset_y = 0; + + match_style style = MATCH_NONE; + matching_group *group = nullptr; + std::vector matches_with; + + std::vector sprites = {}; + + bool blend = false; + std::array blend_sprites = {nullptr}; + }; + +public: + constexpr static auto MAX_NUM_MATCH_WITH = 8; + + explicit layer_terrain(struct tileset *ts, int number); + + bool create_matching_group(const QString &name); + + bool add_tag(const QString &tag, const QString &sprite_name); + bool set_tag_sprite_type(const QString &tag, sprite_type type); + bool set_tag_offsets(const QString &tag, int offset_x, int offset_y); + bool set_tag_matching_group(const QString &tag, const QString &group_name); + bool set_tag_matches_with(const QString &tag, const QString &group_name); + void enable_blending(const QString &tag); + + void initialize_terrain(const terrain *terrain) override; + + std::vector + fill_sprite_array(const tile *ptile, const tile_edge *pedge, + const tile_corner *pcorner, const unit *punit, + const city *pcity, + const unit_type *putype) const override; + +private: + matching_group *group(const QString &name); + + void initialize_cell_whole_match_none(const terrain *terrain, + terrain_info &info); + void initialize_cell_whole_match_same(const terrain *terrain, + terrain_info &info); + void initialize_cell_corner_match_none(const terrain *terrain, + terrain_info &info); + void initialize_cell_corner_match_same(const terrain *terrain, + terrain_info &info); + void initialize_cell_corner_match_pair(const terrain *terrain, + terrain_info &info); + void initialize_cell_corner_match_full(const terrain *terrain, + terrain_info &info); + void initialize_blending(const terrain *terrain, terrain_info &info); + + int terrain_group(const terrain *pterrain) const; + void fill_terrain_sprite_array(std::vector &sprs, + const tile *ptile, const terrain *pterrain, + terrain **tterrain_near) const; + void fill_blending_sprite_array(std::vector &sprs, + const tile *ptile, const terrain *pterrain, + terrain **tterrain_near) const; + + int m_number = 0; + + /* List of those sprites in 'cells' that are allocated by some other + * means than load_sprite() and thus are not freed by unload_all_sprites(). + */ + std::vector m_allocated; + + std::map m_matching_groups; + + /** + * Before terrains are loaded, this contains the list of available terrain + * tags. + */ + std::map m_terrain_tag_info; + + /// Every terrain drawn in this layer appears here + std::map m_terrain_info; + + // CELL_WHOLE + // len(match_with) == 0 or 1 => MATCH_NONE => 1 sprite (or random) 1 + // ex: amplio2 base layer + // len(match_with) == 2, identical => MATCH_SAME => n0e1s0w1 16 + // ex: amplio2 mountains/hills, forest/jungle + // len(match_with) == 2, different => MATCH_PAIR => doesn't exist + // len(match_with) > 2 => MATCH_FULL => doesn't exist + + // CELL_CORNER + // len(match_with) == 0 or 1 => MATCH_NONE => x_cell_[urdl] 4 ex: useless?? + // unused at least len(match_with) == 2, identical => MATCH_SAME => + // x_cell_[urdl][01]{3} 24 = 4*2^3 ex: trident lake l0 len(match_with) == + // 2, different => MATCH_PAIR => x_cell_[urdl]_a_b_c 24 = 4*2^3 ex: + // amplio2 lake l0 len(match_with) > 2 => MATCH_FULL => cellgroup_a_b_c_d + // (4*)n^4 ex: amplio2 coast l0 +}; + +} // namespace freeciv diff --git a/client/tilespec.cpp b/client/tilespec.cpp index 9e176f07af..69c90fea15 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -82,6 +82,7 @@ #include "layer_base_flags.h" #include "layer_darkness.h" #include "layer_special.h" +#include "layer_terrain.h" #include "options.h" // for fill_xxx #include "themes_common.h" @@ -122,60 +123,11 @@ #define FULL_TILE_X_OFFSET ((t->normal_tile_width - t->full_tile_width) / 2) #define FULL_TILE_Y_OFFSET (t->normal_tile_height - t->full_tile_height) -static const char direction4letters[5] = "udrl"; // This must correspond to enum edge_type. static const char edge_name[EDGE_COUNT][3] = {"ns", "we", "ud", "lr"}; -enum match_style { - MATCH_NONE, - MATCH_SAME, // "boolean" match - MATCH_PAIR, - MATCH_FULL -}; - -enum sprite_type { - CELL_WHOLE, // entire tile - CELL_CORNER // corner of tile -}; - -struct drawing_layer { - bool is_tall; - int offset_x, offset_y; - -#define MAX_NUM_MATCH_WITH 8 - enum match_style match_style; - int match_index[1 + MAX_NUM_MATCH_WITH]; - int match_indices; // 0 = no match_type, 1 = no match_with - - enum sprite_type sprite_type; - - struct sprite_vector base; - QPixmap *match[MAX_INDEX_CARDINAL]; - QPixmap **cells; - - /* List of those sprites in 'cells' that are allocated by some other - * means than load_sprite() and thus are not freed by unload_all_sprites(). - */ - struct sprite_vector allocated; -}; - -struct drawing_data { - bool init; - - QString name; - - int num_layers; // 1 thru MAX_NUM_LAYERS. #define MAX_NUM_LAYERS 3 - struct drawing_layer layer[MAX_NUM_LAYERS]; - - bool is_reversed; - - int blending; // layer, 0 = none - QPixmap *blender; - QPixmap *blend[4]; // indexed by a direction4 -}; - struct city_style_threshold { QPixmap *sprite; }; @@ -317,8 +269,6 @@ struct named_sprites { QPixmap *grid_borders[EDGE_COUNT][2]; QPixmap *color; } player[MAX_NUM_PLAYER_SLOTS]; - - struct drawing_data *drawing[MAX_NUM_ITEMS]; }; struct specfile { @@ -347,13 +297,6 @@ struct small_sprite { QPixmap *sprite; }; -static void drawing_data_destroy(struct drawing_data *draw); - -struct tileset_layer { - char **match_types; - size_t match_count; -}; - struct tileset { char name[512]; char given_name[MAX_LEN_NAME]; @@ -370,6 +313,7 @@ struct tileset { struct { freeciv::layer_special *background, *middleground, *foreground; } special_layers; + std::array terrain_layers; freeciv::layer_darkness *darkness_layer; enum ts_type type; @@ -406,17 +350,13 @@ struct tileset { int unit_upkeep_offset_y; int unit_upkeep_small_offset_y; -#define NUM_CORNER_DIRS 4 int num_valid_tileset_dirs, num_cardinal_tileset_dirs; int num_index_valid, num_index_cardinal; std::array valid_tileset_dirs, cardinal_tileset_dirs; - std::array terrain_layers; QSet *specfiles; QSet *small_sprites; // This hash table maps tilespec tags to struct small_sprites. QHash *sprite_hash; - // This hash table maps terrain graphic strings to drawing data. - QHash *tile_hash; QHash *estyle_hash; struct named_sprites sprites; struct color_system *color_system; @@ -481,45 +421,6 @@ void tileset_error(QtMsgType level, const char *format, ...) } } -/** - Create a new drawing data. - */ -static struct drawing_data *drawing_data_new() -{ - struct drawing_data *draw = new drawing_data[1](); - - return draw; -} - -/** - Free a drawing data. - */ -static void drawing_data_destroy(struct drawing_data *draw) -{ - int i; - - fc_assert_ret(NULL != draw); - - for (i = 0; i < 4; i++) { - if (draw->blend[i]) { - free_sprite(draw->blend[i]); - } - } - for (i = 0; i < draw->num_layers; i++) { - int vec_size = sprite_vector_size(&draw->layer[i].allocated); - int j; - - for (j = 0; j < vec_size; j++) { - free_sprite(draw->layer[i].allocated.p[j]); - } - - sprite_vector_free(&draw->layer[i].base); - sprite_vector_free(&draw->layer[i].allocated); - delete[] draw->layer[i].cells; - } - delete[] draw; -} - /** Return unscaled tileset if it exists, or default otherwise */ @@ -802,6 +703,16 @@ int tileset_num_cardinal_dirs(const struct tileset *t) return t->num_cardinal_tileset_dirs; } +/** + * @brief Returns the number of cardinal indices used by the tileset. + * + * This is `2^tileset_num_cardinal_dirs(t)`. + */ +int tileset_num_index_cardinals(const struct tileset *t) +{ + return t->num_index_cardinal; +} + /** * @brief Returns the cardinal directions used by the tileset. * @@ -1027,7 +938,7 @@ static bool check_tilespec_capabilities(struct section_file *file, */ static void tileset_free_toplevel(struct tileset *t) { - int i, j; + int i; if (t->main_intro_filename) { FCPP_FREE(t->main_intro_filename); @@ -1042,12 +953,6 @@ static void tileset_free_toplevel(struct tileset *t) } t->num_preferred_themes = 0; - if (t->tile_hash) { - for (auto *a : qAsConst(*t->tile_hash)) { - drawing_data_destroy(a); - } - FC_FREE(t->tile_hash); - } if (t->estyle_hash) { delete t->estyle_hash; t->estyle_hash = NULL; @@ -1059,17 +964,6 @@ static void tileset_free_toplevel(struct tileset *t) } } - for (i = 0; i < MAX_NUM_LAYERS; i++) { - struct tileset_layer *tslp = &t->terrain_layers[i]; - - if (tslp->match_types) { - for (j = 0; j < tslp->match_count; j++) { - delete[] tslp->match_types[j]; - } - FCPP_FREE(tslp->match_types); - } - } - if (t->color_system) { color_system_free(t->color_system); t->color_system = NULL; @@ -1626,20 +1520,20 @@ static char *tilespec_gfx_filename(const char *gfx_filename) /** Determine the sprite_type string. */ -static int check_sprite_type(const char *sprite_type, - const char *tile_section) +static freeciv::layer_terrain::sprite_type +check_sprite_type(const char *sprite_type, const char *tile_section) { if (fc_strcasecmp(sprite_type, "corner") == 0) { - return CELL_CORNER; + return freeciv::layer_terrain::CELL_CORNER; } if (fc_strcasecmp(sprite_type, "single") == 0) { - return CELL_WHOLE; + return freeciv::layer_terrain::CELL_WHOLE; } if (fc_strcasecmp(sprite_type, "whole") == 0) { - return CELL_WHOLE; + return freeciv::layer_terrain::CELL_WHOLE; } qCritical("[%s] unknown sprite_type \"%s\".", tile_section, sprite_type); - return CELL_WHOLE; + return freeciv::layer_terrain::CELL_WHOLE; } static bool tileset_invalid_offsets(struct tileset *t, @@ -1745,6 +1639,21 @@ static void tileset_add_layer(struct tileset *t, mapview_layer layer) t->darkness_layer = l.get(); t->layers.emplace_back(std::move(l)); } break; + case LAYER_TERRAIN1: { + auto l = std::make_unique(t, 0); + t->terrain_layers[0] = l.get(); + t->layers.emplace_back(std::move(l)); + } break; + case LAYER_TERRAIN2: { + auto l = std::make_unique(t, 1); + t->terrain_layers[1] = l.get(); + t->layers.emplace_back(std::move(l)); + } break; + case LAYER_TERRAIN3: { + auto l = std::make_unique(t, 2); + t->terrain_layers[2] = l.get(); + t->layers.emplace_back(std::move(l)); + } break; case LAYER_SPECIAL1: { auto l = std::make_unique(t, layer); t->special_layers.background = l.get(); @@ -2157,22 +2066,13 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, // Terrain layer info. for (i = 0; i < MAX_NUM_LAYERS; i++) { - struct tileset_layer *tslp = &t->terrain_layers[i]; - int j, k; - - tslp->match_types = const_cast(secfile_lookup_str_vec( - file, &tslp->match_count, "layer%d.match_types", i)); - for (j = 0; j < tslp->match_count; j++) { - tslp->match_types[j] = fc_strdup(tslp->match_types[j]); - - for (k = 0; k < j; k++) { - if (tslp->match_types[k][0] == tslp->match_types[j][0]) { - tileset_error(LOG_FATAL, - _("[layer%d] match_types: \"%s\" initial " - "('%c') is not unique."), - i, tslp->match_types[j], tslp->match_types[j][0]); - // FIXME: Returns NULL. - } + std::size_t count = 0; + auto match_types = + secfile_lookup_str_vec(file, &count, "layer%d.match_types", i); + for (int j = 0; j < count; j++) { + if (!t->terrain_layers[i]->create_matching_group(match_types[j])) { + tileset_stop_read(t, file, fname, sections, layer_order); + return nullptr; } } } @@ -2187,170 +2087,107 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, return nullptr; } - fc_assert(t->tile_hash == NULL); - t->tile_hash = new QHash; - section_list_iterate(sections, psection) { - const char *sec_name = section_name(psection); - struct drawing_data *draw = drawing_data_new(); - const char *sprite_type; - int l; - const char *terrain_name; + auto sec_name = section_name(psection); - terrain_name = secfile_lookup_str(file, "%s.tag", sec_name); - - if (terrain_name != NULL) { - draw->name = fc_strdup(terrain_name); - } else { - tileset_error(LOG_ERROR, _("No terrain tag given in section [%s]."), - sec_name); - drawing_data_destroy(draw); - tileset_stop_read(t, file, fname, sections, layer_order); - return nullptr; + QString tag; + { + auto c_tag = secfile_lookup_str(file, "%s.tag", sec_name); + if (c_tag != NULL) { + tag = c_tag; + } else { + tileset_error(LOG_ERROR, _("No terrain tag given in section [%s]."), + sec_name); + tileset_stop_read(t, file, fname, sections, layer_order); + return nullptr; + } } - draw->blending = - secfile_lookup_int_default(file, 0, "%s.blend_layer", sec_name); - draw->blending = CLIP(0, draw->blending, MAX_NUM_LAYERS); - - draw->is_reversed = - secfile_lookup_bool_default(file, false, "%s.is_reversed", sec_name); - draw->num_layers = - secfile_lookup_int_default(file, 0, "%s.num_layers", sec_name); - draw->num_layers = CLIP(1, draw->num_layers, MAX_NUM_LAYERS); - - for (l = 0; l < draw->num_layers; l++) { - struct drawing_layer *dlp = &draw->layer[l]; - struct tileset_layer *tslp = &t->terrain_layers[l]; - const char *match_type; - const char **match_with; - size_t count; - - dlp->is_tall = secfile_lookup_bool_default( - file, false, "%s.layer%d_is_tall", sec_name, l); - dlp->offset_x = secfile_lookup_int_default( - file, 0, "%s.layer%d_offset_x", sec_name, l); - dlp->offset_y = secfile_lookup_int_default( - file, 0, "%s.layer%d_offset_y", sec_name, l); - dlp->offset_x = ceil(t->scale * dlp->offset_x); - dlp->offset_y = ceil(t->scale * dlp->offset_y); - - match_type = secfile_lookup_str_default( - file, NULL, "%s.layer%d_match_type", sec_name, l); - if (match_type) { - int j; - - // Determine our match_type. - for (j = 0; j < tslp->match_count; j++) { - if (fc_strcasecmp(tslp->match_types[j], match_type) == 0) { - break; - } - } - if (j >= tslp->match_count) { - qCritical("[%s] invalid match_type \"%s\".", sec_name, match_type); - } else { - dlp->match_index[dlp->match_indices++] = j; + { + auto num_layers = + secfile_lookup_int_default(file, 0, "%s.num_layers", sec_name); + num_layers = CLIP(1, num_layers, MAX_NUM_LAYERS); + + for (int l = 0; l < num_layers; l++) { + if (!t->terrain_layers[l]->add_tag( + tag, QString(sec_name).mid(strlen(TILE_SECTION_PREFIX)))) { + tileset_stop_read(t, file, fname, sections, layer_order); + return nullptr; } - } - match_with = secfile_lookup_str_vec( - file, &count, "%s.layer%d_match_with", sec_name, l); - if (match_with) { - int j, k; + // Offsets + { + auto is_tall = secfile_lookup_bool_default( + file, false, "%s.layer%d_is_tall", sec_name, l); + + auto offset_x = secfile_lookup_int_default( + file, 0, "%s.layer%d_offset_x", sec_name, l); + offset_x = ceil(t->scale * offset_x); + if (is_tall) { + offset_x += FULL_TILE_X_OFFSET; + } + + auto offset_y = secfile_lookup_int_default( + file, 0, "%s.layer%d_offset_y", sec_name, l); + offset_y = ceil(t->scale * offset_y); + if (is_tall) { + offset_y += FULL_TILE_Y_OFFSET; + } - if (count > MAX_NUM_MATCH_WITH) { - qCritical("[%s] match_with has too many types (%d, max %d)", - sec_name, static_cast(count), MAX_NUM_MATCH_WITH); - count = MAX_NUM_MATCH_WITH; + if (!t->terrain_layers[l]->set_tag_offsets(tag, offset_x, + offset_y)) { + tileset_stop_read(t, file, fname, sections, layer_order); + return nullptr; + } } - if (1 < dlp->match_indices) { - qCritical("[%s] previous match_with ignored.", sec_name); - dlp->match_indices = 1; - } else if (1 > dlp->match_indices) { - qCritical("[%s] missing match_type, using \"%s\".", sec_name, - tslp->match_types[0]); - dlp->match_index[0] = 0; - dlp->match_indices = 1; + // Sprite type + { + auto type_str = secfile_lookup_str_default( + file, "whole", "%s.layer%d_sprite_type", sec_name, l); + if (!t->terrain_layers[l]->set_tag_sprite_type( + tag, check_sprite_type(type_str, sec_name))) { + tileset_stop_read(t, file, fname, sections, layer_order); + return nullptr; + } } - for (k = 0; k < count; k++) { - for (j = 0; j < tslp->match_count; j++) { - if (fc_strcasecmp(tslp->match_types[j], match_with[k]) == 0) { - break; + // Matching + { + auto matching_group = secfile_lookup_str_default( + file, NULL, "%s.layer%d_match_type", sec_name, l); + + if (matching_group) { + if (!t->terrain_layers[l]->set_tag_matching_group( + tag, matching_group)) { + tileset_stop_read(t, file, fname, sections, layer_order); + return nullptr; } } - if (j >= tslp->match_count) { - qCritical("[%s] layer%d_match_with: invalid \"%s\".", sec_name, - l, match_with[k]); - } else if (1 < count) { - int m; - - for (m = 0; m < dlp->match_indices; m++) { - if (dlp->match_index[m] == j) { - qCritical("[%s] layer%d_match_with: duplicate \"%s\".", - sec_name, l, match_with[k]); - break; + + std::size_t count = 0; + auto match_with = secfile_lookup_str_vec( + file, &count, "%s.layer%d_match_with", sec_name, l); + if (match_with) { + for (std::size_t j = 0; j < count; ++j) { + if (!t->terrain_layers[l]->set_tag_matches_with( + tag, match_with[j])) { + tileset_stop_read(t, file, fname, sections, layer_order); + return nullptr; } } - if (m >= dlp->match_indices) { - dlp->match_index[dlp->match_indices++] = j; - } - } else { - dlp->match_index[dlp->match_indices++] = j; } } - FCPP_FREE(match_with); } - - // Check match_indices - switch (dlp->match_indices) { - case 0: - case 1: - dlp->match_style = MATCH_NONE; - break; - case 2: - if (dlp->match_index[0] == dlp->match_index[1]) { - dlp->match_style = MATCH_SAME; - } else { - dlp->match_style = MATCH_PAIR; - } - break; - default: - dlp->match_style = MATCH_FULL; - break; - }; - - sprite_type = secfile_lookup_str_default( - file, "whole", "%s.layer%d_sprite_type", sec_name, l); - dlp->sprite_type = static_cast( - check_sprite_type(sprite_type, sec_name)); - - switch (dlp->sprite_type) { - case CELL_WHOLE: - // OK, no problem - break; - case CELL_CORNER: - if (dlp->is_tall || dlp->offset_x > 0 || dlp->offset_y > 0) { - qCritical("[%s] layer %d: you cannot have tall terrain or\n" - "a sprite offset with a cell-based drawing method.", - sec_name, l); - dlp->is_tall = false; - dlp->offset_x = dlp->offset_y = 0; + { + auto blending = + secfile_lookup_int_default(file, 0, "%s.blend_layer", sec_name); + if (blending > 0) { + t->terrain_layers[CLIP(0, blending - 1, MAX_NUM_LAYERS - 1)] + ->enable_blending(tag); } - break; - }; - } - - bool hc = t->tile_hash->contains(draw->name); - t->tile_hash->insert(draw->name, draw); - if (hc) { - qCritical( - "warning: multiple tile sections containing terrain tag \"%s\".", - qUtf8Printable(draw->name)); - tileset_stop_read(t, file, fname, sections, layer_order); - return nullptr; + } } } section_list_iterate_end; @@ -2470,13 +2307,10 @@ static const char *citizen_rule_name(enum citizen_category citizen) binary value 1000 will be converted into "n1e0s0w0". This is in a clockwise ordering. */ -static QString &cardinal_index_str(const struct tileset *t, int idx) +QString cardinal_index_str(const struct tileset *t, int idx) { - static QString c; - int i; - - c = QString(); - for (i = 0; i < t->num_cardinal_tileset_dirs; i++) { + auto c = QString(); + for (int i = 0; i < t->num_cardinal_tileset_dirs; i++) { int value = (idx >> i) & 1; c += QStringLiteral("%1%2").arg( @@ -3832,268 +3666,9 @@ static void tileset_setup_base(struct tileset *t, void tileset_setup_tile_type(struct tileset *t, const struct terrain *pterrain) { - struct drawing_data *draw; - QPixmap *sprite; - QString buffer; - int i, l; - - if (!t->tile_hash->contains(pterrain->graphic_str) - && !t->tile_hash->contains(pterrain->graphic_alt)) { - tileset_error(LOG_FATAL, - _("Terrain \"%s\": no graphic tile \"%s\" or \"%s\"."), - terrain_rule_name(pterrain), pterrain->graphic_str, - pterrain->graphic_alt); - } - draw = t->tile_hash->value(pterrain->graphic_str); - if (!draw) { - draw = t->tile_hash->value(pterrain->graphic_alt); + for (auto &layer : t->layers) { + layer->initialize_terrain(pterrain); } - if (draw->init) { - t->sprites.drawing[terrain_index(pterrain)] = draw; - return; - } - - // Set up each layer of the drawing. - for (l = 0; l < draw->num_layers; l++) { - struct drawing_layer *dlp = &draw->layer[l]; - struct tileset_layer *tslp = &t->terrain_layers[l]; - sprite_vector_init(&dlp->base); - sprite_vector_init(&dlp->allocated); - - switch (dlp->sprite_type) { - case CELL_WHOLE: - switch (dlp->match_style) { - case MATCH_NONE: - // Load whole sprites for this tile. - for (i = 0;; i++) { - buffer = QStringLiteral("t.l%1.%2%3") - .arg(QString::number(l), draw->name, - QString::number(i + 1)); - sprite = load_sprite(t, buffer, true, false); - if (!sprite) { - break; - } - sprite_vector_reserve(&dlp->base, i + 1); - dlp->base.p[i] = sprite; - } - // check for base sprite, allowing missing sprites above base - if (0 == i && 0 == l) { - /* TRANS: 'base' means 'base of terrain gfx', not 'military base' - */ - tileset_error(LOG_FATAL, _("Missing base sprite for tag \"%s\"."), - qUtf8Printable(buffer)); - } - break; - case MATCH_SAME: - // Load 16 cardinally-matched sprites. - for (i = 0; i < t->num_index_cardinal; i++) { - buffer = QStringLiteral("t.l%1.%2_%3") - .arg(QString::number(l), draw->name, - cardinal_index_str(t, i)); - dlp->match[i] = tiles_lookup_sprite_tag_alt( - t, LOG_FATAL, qUtf8Printable(buffer), "", "matched terrain", - terrain_rule_name(pterrain), true); - } - break; - case MATCH_PAIR: - case MATCH_FULL: - fc_assert(false); // not yet defined - break; - }; - break; - case CELL_CORNER: { - const int count = dlp->match_indices; - int number = NUM_CORNER_DIRS; - - switch (dlp->match_style) { - case MATCH_NONE: - // do nothing - break; - case MATCH_PAIR: - case MATCH_SAME: - // N directions (NSEW) * 3 dimensions of matching - fc_assert(count == 2); - number = NUM_CORNER_DIRS * 2 * 2 * 2; - break; - case MATCH_FULL: - default: - // N directions (NSEW) * 3 dimensions of matching - // could use exp() or expi() here? - number = NUM_CORNER_DIRS * count * count * count; - break; - }; - - dlp->cells = new QPixmap *[number](); - - for (i = 0; i < number; i++) { - enum direction4 dir = static_cast(i % NUM_CORNER_DIRS); - int value = i / NUM_CORNER_DIRS; - - switch (dlp->match_style) { - case MATCH_NONE: - buffer = QStringLiteral("t.l%1.%2_cell_%3") - .arg(QString::number(l), draw->name, - QString(direction4letters[dir])); - dlp->cells[i] = tiles_lookup_sprite_tag_alt( - t, LOG_FATAL, qUtf8Printable(buffer), "", "cell terrain", - terrain_rule_name(pterrain), true); - break; - case MATCH_SAME: - buffer = QStringLiteral("t.l%1.%2_cell_%3%4%5%6") - .arg(QString::number(l), draw->name, - QString(direction4letters[dir]), - QString::number((value) &1), - QString::number((value >> 1) & 1), - QString::number((value >> 2) & 1)); - dlp->cells[i] = tiles_lookup_sprite_tag_alt( - t, LOG_FATAL, qUtf8Printable(buffer), "", "same cell terrain", - terrain_rule_name(pterrain), true); - break; - case MATCH_PAIR: - buffer = - QStringLiteral("t.l%1.%2_cell_%3_%4_%5_%6") - .arg(QString::number(l), draw->name, - QChar(direction4letters[dir]), - QChar(tslp->match_types[dlp->match_index[(value) &1]] - [0]), - QChar(tslp->match_types[dlp->match_index[(value >> 1) - & 1]][0]), - QChar(tslp->match_types[dlp->match_index[(value >> 2) - & 1]][0])); - - dlp->cells[i] = tiles_lookup_sprite_tag_alt( - t, LOG_FATAL, qUtf8Printable(buffer), "", "cell pair terrain", - terrain_rule_name(pterrain), true); - break; - case MATCH_FULL: { - int tthis = dlp->match_index[0]; - int n, s, e, w; - int v1, v2, v3; - - v1 = dlp->match_index[value % count]; - value /= count; - v2 = dlp->match_index[value % count]; - value /= count; - v3 = dlp->match_index[value % count]; - - fc_assert(v1 < count && v2 < count && v3 < count); - - // Assume merged cells. This should be a separate option. - switch (dir) { - case DIR4_NORTH: - s = tthis; - w = v1; - n = v2; - e = v3; - break; - case DIR4_EAST: - w = tthis; - n = v1; - e = v2; - s = v3; - break; - case DIR4_SOUTH: - n = tthis; - e = v1; - s = v2; - w = v3; - break; - case DIR4_WEST: - default: // avoid warnings - e = tthis; - s = v1; - w = v2; - n = v3; - break; - }; - - /* Use first character of match_types, - * already checked for uniqueness. */ - buffer = - QStringLiteral("t.l%1.cellgroup_%2_%3_%4_%5") - .arg(QString::number(l), QChar(tslp->match_types[n][0]), - QChar(tslp->match_types[e][0]), - QChar(tslp->match_types[s][0]), - QChar(tslp->match_types[w][0])); - sprite = load_sprite(t, buffer, true, false); - - if (sprite) { - // Crop the sprite to separate this cell. - int vec_size = sprite_vector_size(&dlp->allocated); - - const int W = t->normal_tile_width; - const int H = t->normal_tile_height; - int x[4] = {W / 4, W / 4, 0, W / 2}; - int y[4] = {H / 2, 0, H / 4, H / 4}; - int xo[4] = {0, 0, -W / 2, W / 2}; - int yo[4] = {H / 2, -H / 2, 0, 0}; - - sprite = crop_sprite(sprite, x[dir], y[dir], W / 2, H / 2, - t->sprites.mask.tile, xo[dir], yo[dir], - 1.0f, false); - /* We allocated new sprite with crop_sprite. Store its - * address so we can free it. */ - sprite_vector_reserve(&dlp->allocated, vec_size + 1); - dlp->allocated.p[vec_size] = sprite; - } else { - qCritical("Terrain graphics sprite for tag \"%s\" missing.", - qUtf8Printable(buffer)); - } - - dlp->cells[i] = sprite; - } break; - }; - } - } break; - }; - } - - // try an optional special name - buffer = QStringLiteral("t.blend.%1").arg(draw->name); - draw->blender = tiles_lookup_sprite_tag_alt( - t, LOG_VERBOSE, qUtf8Printable(buffer), "", "blend terrain", - terrain_rule_name(pterrain), true); - - if (draw->blending > 0) { - const int bl = draw->blending - 1; - - if (NULL == draw->blender) { - int li = 0; - - // try an already loaded base - while (NULL == draw->blender && li < draw->blending - && 0 < draw->layer[li].base.size) { - draw->blender = draw->layer[li++].base.p[0]; - } - } - - if (NULL == draw->blender) { - // try an unloaded base name - buffer = - QStringLiteral("t.l%1.%21").arg(QString::number(bl), draw->name); - draw->blender = tiles_lookup_sprite_tag_alt( - t, LOG_FATAL, qUtf8Printable(buffer), "", "base (blend) terrain", - terrain_rule_name(pterrain), true); - } - } - - if (NULL != draw->blender) { - // Set up blending sprites. This only works in iso-view! - const int W = t->normal_tile_width; - const int H = t->normal_tile_height; - const int offsets[4][2] = { - {W / 2, 0}, {0, H / 2}, {W / 2, H / 2}, {0, 0}}; - int dir = 0; - - for (; dir < 4; dir++) { - draw->blend[dir] = - crop_sprite(draw->blender, offsets[dir][0], offsets[dir][1], W / 2, - H / 2, t->sprites.dither_tile, 0, 0, 1.0f, false); - } - } - - draw->init = true; - t->sprites.drawing[terrain_index(pterrain)] = draw; } /** @@ -4176,10 +3751,8 @@ static QPixmap *get_unit_nation_flag_sprite(const struct tileset *t, tterrain_near : terrain types of all adjacent terrain tspecial_near : specials of all adjacent terrain */ -static void build_tile_data(const struct tile *ptile, - struct terrain *pterrain, - struct terrain **tterrain_near, - bv_extras *textras_near) +void build_tile_data(const struct tile *ptile, struct terrain *pterrain, + struct terrain **tterrain_near, bv_extras *textras_near) { int dir; @@ -4804,42 +4377,6 @@ static void fill_city_overlays_sprite_array(const struct tileset *t, } } -/** - Helper function for fill_terrain_sprite_layer. - Fill in the sprite array for blended terrain. - */ -static void fill_terrain_sprite_blending(const struct tileset *t, - std::vector &sprs, - const struct tile *ptile, - const struct terrain *pterrain, - struct terrain **tterrain_near) -{ - const int W = t->normal_tile_width, H = t->normal_tile_height; - const int offsets[4][2] = {{W / 2, 0}, {0, H / 2}, {W / 2, H / 2}, {0, 0}}; - int dir = 0; - - /* - * We want to mark unknown tiles so that an unreal tile will be - * given the same marking as our current tile - that way we won't - * get the "unknown" dither along the edge of the map. - */ - for (; dir < 4; dir++) { - struct tile *tile1 = mapstep(&(wld.map), ptile, DIR4_TO_DIR8[dir]); - struct terrain *other; - - if (!tile1 || client_tile_get_known(tile1) == TILE_UNKNOWN - || pterrain == (other = tterrain_near[DIR4_TO_DIR8[dir]]) - || (0 == t->sprites.drawing[terrain_index(other)]->blending - && NULL == t->sprites.drawing[terrain_index(other)]->blender)) { - continue; - } - - sprs.emplace_back(t, - t->sprites.drawing[terrain_index(other)]->blend[dir], - true, offsets[dir][0], offsets[dir][1]); - } -} - /** Add sprites for fog (and some forms of darkness). */ @@ -4890,185 +4427,6 @@ static void fill_fog_sprite_array(const struct tileset *t, } } -/** - Helper function for fill_terrain_sprite_layer. - */ -static void fill_terrain_sprite_array( - struct tileset *t, std::vector &sprs, int l, // layer_num - const struct tile *ptile, const struct terrain *pterrain, - struct terrain **tterrain_near, struct drawing_data *draw) -{ - struct drawing_layer *dlp = &draw->layer[l]; - int tthis = dlp->match_index[0]; - int that = dlp->match_index[1]; - int ox = dlp->offset_x; - int oy = dlp->offset_y; - int i; - -#define MATCH(dir) \ - (t->sprites.drawing[terrain_index(tterrain_near[(dir)])]->num_layers > l \ - ? t->sprites.drawing[terrain_index(tterrain_near[(dir)])] \ - ->layer[l] \ - .match_index[0] \ - : -1) - - switch (dlp->sprite_type) { - case CELL_WHOLE: { - switch (dlp->match_style) { - case MATCH_NONE: { - int count = sprite_vector_size(&dlp->base); - - if (count > 0) { - /* Pseudo-random reproducable algorithm to pick a sprite. Use - * modulo to limit the number to a handleable size [0..32000). */ - count = fc_randomly(tile_index(ptile) % 32000, count); - - if (dlp->is_tall) { - ox += FULL_TILE_X_OFFSET; - oy += FULL_TILE_Y_OFFSET; - } - sprs.emplace_back(t, dlp->base.p[count], true, ox, oy); - } - break; - } - case MATCH_SAME: { - int tileno = 0; - - for (i = 0; i < t->num_cardinal_tileset_dirs; i++) { - enum direction8 dir = - t->cardinal_tileset_dirs[static_cast(i)]; - - if (MATCH(dir) == tthis) { - tileno |= 1 << i; - } - } - - if (dlp->is_tall) { - ox += FULL_TILE_X_OFFSET; - oy += FULL_TILE_Y_OFFSET; - } - sprs.emplace_back(t, dlp->match[tileno], true, ox, oy); - break; - } - case MATCH_PAIR: - case MATCH_FULL: - fc_assert(false); // not yet defined - break; - }; - break; - } - case CELL_CORNER: { - /* Divide the tile up into four rectangular cells. Each of these - * cells covers one corner, and each is adjacent to 3 different - * tiles. For each cell we pick a sprite based upon the adjacent - * terrains at each of those tiles. Thus, we have 8 different sprites - * for each of the 4 cells (32 sprites total). - * - * These arrays correspond to the direction4 ordering. */ - const int W = t->normal_tile_width; - const int H = t->normal_tile_height; - const int iso_offsets[4][2] = { - {W / 4, 0}, {W / 4, H / 2}, {W / 2, H / 4}, {0, H / 4}}; - const int noniso_offsets[4][2] = { - {0, 0}, {W / 2, H / 2}, {W / 2, 0}, {0, H / 2}}; - - // put corner cells - for (i = 0; i < NUM_CORNER_DIRS; i++) { - const int count = dlp->match_indices; - int array_index = 0; - enum direction8 dir = dir_ccw(DIR4_TO_DIR8[i]); - int x = (t->type == TS_ISOMETRIC ? iso_offsets[i][0] - : noniso_offsets[i][0]); - int y = (t->type == TS_ISOMETRIC ? iso_offsets[i][1] - : noniso_offsets[i][1]); - int m[3] = {MATCH(dir_ccw(dir)), MATCH(dir), MATCH(dir_cw(dir))}; - QPixmap *s; - - // synthesize 4 dimensional array? - switch (dlp->match_style) { - case MATCH_NONE: - // We have no need for matching, just plug the piece in place. - break; - case MATCH_SAME: - array_index = array_index * 2 + (m[2] != tthis); - array_index = array_index * 2 + (m[1] != tthis); - array_index = array_index * 2 + (m[0] != tthis); - break; - case MATCH_PAIR: - array_index = array_index * 2 + (m[2] == that); - array_index = array_index * 2 + (m[1] == that); - array_index = array_index * 2 + (m[0] == that); - break; - case MATCH_FULL: - default: { - int n[3]; - int j = 0; - for (; j < 3; j++) { - int k = 0; - for (; k < count; k++) { - n[j] = k; // default to last entry - if (m[j] == dlp->match_index[k]) { - break; - } - } - } - array_index = array_index * count + n[2]; - array_index = array_index * count + n[1]; - array_index = array_index * count + n[0]; - } break; - }; - array_index = array_index * NUM_CORNER_DIRS + i; - - s = dlp->cells[array_index]; - if (s) { - sprs.emplace_back(t, s, true, x, y); - } - } - break; - } - }; -#undef MATCH -} - -/** - Add sprites for the base tile to the sprite list. This doesn't - include specials or rivers. - */ -static void fill_terrain_sprite_layer(struct tileset *t, - std::vector &sprs, - int layer_num, - const struct tile *ptile, - const struct terrain *pterrain, - struct terrain **tterrain_near) -{ - QPixmap *sprite; - struct drawing_data *draw = t->sprites.drawing[terrain_index(pterrain)]; - const int l = - (draw->is_reversed ? (draw->num_layers - layer_num - 1) : layer_num); - - fc_assert(layer_num < TERRAIN_LAYER_COUNT); - - // Skip the normal drawing process. - /* FIXME: this should avoid calling load_sprite since it's slow and - * increases the refcount without limit. */ - if (ptile->spec_sprite - && (sprite = load_sprite(t, ptile->spec_sprite, true, false))) { - if (l == 0) { - sprs.emplace_back(t, sprite); - } - return; - } - - if (l < draw->num_layers) { - fill_terrain_sprite_array(t, sprs, l, ptile, pterrain, tterrain_near, - draw); - - if ((l + 1) == draw->blending) { - fill_terrain_sprite_blending(t, sprs, ptile, pterrain, tterrain_near); - } - } -} - /** Indicate whether a unit is to be drawn with a surrounding city outline under current conditions. @@ -5409,9 +4767,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, break; case LAYER_TERRAIN1: - if (NULL != pterrain && !solid_bg) { - fill_terrain_sprite_layer(t, sprs, 0, ptile, pterrain, tterrain_near); - } + fc_assert_ret_val(false, {}); break; case LAYER_DARKNESS: @@ -5419,16 +4775,11 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, break; case LAYER_TERRAIN2: - if (NULL != pterrain && !solid_bg) { - fill_terrain_sprite_layer(t, sprs, 1, ptile, pterrain, tterrain_near); - } + fc_assert_ret_val(false, {}); break; case LAYER_TERRAIN3: - if (NULL != pterrain && !solid_bg) { - fc_assert(MAX_NUM_LAYERS == 3); - fill_terrain_sprite_layer(t, sprs, 2, ptile, pterrain, tterrain_near); - } + fc_assert_ret_val(false, {}); break; case LAYER_WATER: @@ -6166,6 +5517,14 @@ QPixmap *get_event_sprite(const struct tileset *t, enum event_type event) return t->sprites.events[event]; } +/** + * Return dither sprite + */ +QPixmap *get_dither_sprite(const struct tileset *t) +{ + return t->sprites.dither_tile; +} + /** * Return tile mask sprite */ @@ -6376,27 +5735,24 @@ std::vector fill_basic_terrain_layer_sprite_array(struct tileset *t, int layer, struct terrain *pterrain) { - auto sprs = std::vector(); - struct drawing_data *draw = t->sprites.drawing[terrain_index(pterrain)]; - - struct terrain *tterrain_near[8]; - bv_special tspecial_near[8]; - - struct tile dummy_tile; // :( - - int i; - - memset(&dummy_tile, 0, sizeof(struct tile)); + // We create a virtual tile with only the requested terrain, then collect + // sprites from every layer. + auto tile = tile_virtual_new(nullptr); + BV_CLR_ALL(tile->extras); + tile->terrain = pterrain; - for (i = 0; i < 8; i++) { - tterrain_near[i] = pterrain; - BV_CLR_ALL(tspecial_near[i]); + auto sprs = std::vector(); + for (const auto &layer : t->layers) { + const auto lsprs = layer->fill_sprite_array(tile, nullptr, nullptr, + nullptr, nullptr, nullptr); + // Merge by hand because drawn_sprite isn't copyable (but it is + // copy-constructible) + for (const auto &sprite : lsprs) { + sprs.emplace_back(sprite); + } } - i = draw->is_reversed ? draw->num_layers - layer - 1 : layer; - fill_terrain_sprite_array(t, sprs, i, &dummy_tile, pterrain, tterrain_near, - draw); - + tile_virtual_destroy(tile); return sprs; } diff --git a/client/tilespec.h b/client/tilespec.h index 3485179d7f..179c6b9c69 100644 --- a/client/tilespec.h +++ b/client/tilespec.h @@ -78,6 +78,7 @@ struct resource_type; #define MAX_INDEX_VALID 256 #define NUM_TILES_PROGRESS 8 +#define NUM_CORNER_DIRS 4 #define MAX_NUM_CITIZEN_SPRITES 6 @@ -215,6 +216,9 @@ QPixmap *get_citizen_sprite(const struct tileset *t, const struct city *pcity); QPixmap *get_city_flag_sprite(const struct tileset *t, const struct city *pcity); +void build_tile_data(const struct tile *ptile, struct terrain *pterrain, + struct terrain **tterrain_near, + bv_extras *textras_near); QPixmap *get_nation_flag_sprite(const struct tileset *t, const struct nation_type *nation); QPixmap *get_nation_shield_sprite(const struct tileset *t, @@ -253,6 +257,7 @@ fill_basic_extra_sprite_array(const struct tileset *t, const struct extra_type *pextra); bool is_extra_drawing_enabled(struct extra_type *pextra); QPixmap *get_event_sprite(const struct tileset *t, enum event_type event); +QPixmap *get_dither_sprite(const struct tileset *t); QPixmap *get_mask_sprite(const struct tileset *t); QPixmap *tiles_lookup_sprite_tag_alt(struct tileset *t, QtMsgType level, @@ -289,7 +294,9 @@ int tileset_num_city_colors(const struct tileset *t); bool tileset_use_hard_coded_fog(const struct tileset *t); int tileset_num_cardinal_dirs(const struct tileset *t); +int tileset_num_index_cardinals(const struct tileset *t); std::array tileset_cardinal_dirs(const struct tileset *t); +QString cardinal_index_str(const struct tileset *t, int idx); /* These are used as array index -> can't be changed freely to values bigger than size of those arrays. */ diff --git a/doc/README.graphics b/doc/README.graphics index fa70ffd28d..41380adb35 100644 --- a/doc/README.graphics +++ b/doc/README.graphics @@ -8,7 +8,7 @@ Using Graphics: To use different graphics with Freeciv, use the '--tiles' argument to the Freeciv client. Eg, to use the 'engels' graphics, start the client as: - + freeciv-gtk3 --tiles engels What Freeciv actually does in this case is look for a file called @@ -199,7 +199,6 @@ This section contains information on how to draw this terrain type. currently supports blending. Only the base graphic will be blended. The blending mask has sprite t.dither_tile. - is_reversed : Draw layers in reverse order. num_layers : The number of layers in the terrain. This value must be 1, 2 or 3. Each layer is drawn separately. The layerN options below control the @@ -208,7 +207,7 @@ This section contains information on how to draw this terrain type. on normal_tile_width and normal_tile_height, but to corner of the full tile. layerN_offset_x : Offset for terrain sprites - layerN_offset_y + layerN_offset_y layerN_match_type : If 0 or unset, no terrain matching will be done and the base sprite will be drawn for the terrain. If non-zero, then terrain matching will be done. A @@ -330,7 +329,7 @@ having to modify earlier files in the list. Tag prefixes: ------------- -To help keep the tags organised, there is a rough prefix system used +To help keep the tags organised, there is a rough prefix system used for standard tags: f. national flags @@ -396,7 +395,7 @@ sprites. progress indicators: There are three types of progress indicator. "science_bulb" indicates - progress toward the current research target. "warming_sun" indicates + progress toward the current research target. "warming_sun" indicates progress toward global warming. "cooling_flake" indicates progress toward nuclear winter. Each indicator should have 8 states, numbered 0 (least) through 7 (most). The sprite names are "s._". From b5c5b433182405b9666e31437c82d2eca8ca14b1 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 12 Sep 2021 00:03:17 +0200 Subject: [PATCH 23/24] Fix loading CELL_CORNER/MATCH_PAIR sprites info.matches_with contains two elements: the first is equal to info.group and the second is the group being matched against. The first element was erroneously used; use the second instead. See #430. --- client/layer_terrain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/layer_terrain.cpp b/client/layer_terrain.cpp index 6262a38f2a..955fd30dfd 100644 --- a/client/layer_terrain.cpp +++ b/client/layer_terrain.cpp @@ -422,7 +422,7 @@ void layer_terrain::initialize_cell_corner_match_pair(const terrain *terrain, int value = i / NUM_CORNER_DIRS; QChar letters[2] = {info.group->name[0], - info.matches_with.front()->name[0]}; + info.matches_with.back()->name[0]}; auto buffer = QStringLiteral("t.l%1.%2_cell_%3_%4_%5_%6") .arg(m_number) .arg(info.sprite_name, QChar(direction4letters[dir]), From 2c3a4eb245b134b7e8f3fb605c94ced420855622 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 12 Sep 2021 19:42:33 +0200 Subject: [PATCH 24/24] Always initialize textras_near This prevents undefined behavior (in practice, artifacts with Trident). See #450. --- client/tilespec.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/tilespec.cpp b/client/tilespec.cpp index 69c90fea15..a87c9593c6 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -4725,7 +4725,7 @@ fill_sprite_array(struct tileset *t, enum mapview_layer layer, const struct unit_type *putype) { int tileno, dir; - bv_extras textras_near[8]; + bv_extras textras_near[8]{}; bv_extras textras; struct terrain *tterrain_near[8] = {nullptr}; struct terrain *pterrain = NULL;