diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 6fb97988af..0013daa4e8 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -55,6 +55,12 @@ add_library( gui_interface.cpp goto.cpp helpdata.cpp + layer.cpp + layer_background.cpp + 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/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); 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/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 6a2b7cb9c7..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 @@ -219,7 +220,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; @@ -228,7 +229,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 +251,19 @@ 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); + 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: gov = government_by_translated_name(s); @@ -1150,8 +1158,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 +1168,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 +1182,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/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) diff --git a/client/layer.cpp b/client/layer.cpp new file mode 100644 index 0000000000..7d7a4ee94b --- /dev/null +++ b/client/layer.cpp @@ -0,0 +1,93 @@ +/*__ ___ *************************************** +/ \ / \ 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 "control.h" +#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 { + +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); +} + +/** + * @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 new file mode 100644 index 0000000000..75cae127cf --- /dev/null +++ b/client/layer.h @@ -0,0 +1,182 @@ +/*__ ___ *************************************** +/ \ / \ 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 + +// Forward declarations +class QPixmap; + +struct city; +struct player; +struct terrain; +struct tileset; +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 { + 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 +}; + +/* 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_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" + +#define TERRAIN_LAYER_COUNT 3 + +// 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 +}; + +namespace freeciv { + +/** + * A layer when drawing the map. + */ +class layer { +public: + 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, + 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); } + + /** + * Initializes terrain-specific data. + */ + virtual void initialize_terrain(const terrain *terrain) + { + Q_UNUSED(terrain); + } + + mapview_layer type() const { return m_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; +}; + +} // namespace freeciv diff --git a/client/layer_background.cpp b/client/layer_background.cpp new file mode 100644 index 0000000000..ebb87d9a00 --- /dev/null +++ b/client/layer_background.cpp @@ -0,0 +1,93 @@ +/*__ ___ *************************************** +/ \ / \ 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 "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 +{ + // 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 new file mode 100644 index 0000000000..ce6ff77700 --- /dev/null +++ b/client/layer_background.h @@ -0,0 +1,42 @@ +/*__ ___ *************************************** +/ \ / \ 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_background : public layer { +public: + 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 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/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/layer_darkness.cpp b/client/layer_darkness.cpp new file mode 100644 index 0000000000..6082439297 --- /dev/null +++ b/client/layer_darkness.cpp @@ -0,0 +1,105 @@ +/*__ ___ *************************************** +/ \ / \ 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 + if (!solid_background(ptile, punit, pcity)) { + 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/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/layer_terrain.cpp b/client/layer_terrain.cpp new file mode 100644 index 0000000000..955fd30dfd --- /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.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]), + 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/mapview_common.cpp b/client/mapview_common.cpp index 2e8e4fb35b..ba6cea7565 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); } } } @@ -972,19 +967,19 @@ 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, 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 = + 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) { @@ -1004,8 +999,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); } /** @@ -1016,12 +1011,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) - { + for (const auto &layer : tileset_get_layers(tileset)) { put_one_element(pcanvas, layer, NULL, NULL, NULL, punit, NULL, canvas_x, canvas_y, NULL); } - mapview_layer_iterate_end; } /** @@ -1032,12 +1025,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) - { + for (const auto &layer : tileset_get_layers(tileset)) { put_one_element(pcanvas, layer, NULL, NULL, NULL, NULL, NULL, canvas_x, canvas_y, putype); } - mapview_layer_iterate_end; } /** @@ -1050,12 +1041,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) - { + for (const auto &layer : tileset_get_layers(tileset)) { put_one_element(pcanvas, layer, NULL, NULL, NULL, NULL, pcity, canvas_x, canvas_y, NULL); } - mapview_layer_iterate_end; } /** @@ -1070,12 +1059,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) - { + for (const auto &layer : tileset_get_layers(tileset)) { put_one_element(pcanvas, layer, ptile, NULL, NULL, NULL, NULL, canvas_x, canvas_y, NULL); } - mapview_layer_iterate_end; } /** @@ -1192,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 @@ -1380,12 +1368,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; } @@ -1412,7 +1399,6 @@ void update_map_canvas(int canvas_x, int canvas_y, int width, int height) } gui_rect_iterate_coord_end; } - mapview_layer_iterate_end; draw_trade_routes(); link_marks_draw_all(); @@ -1891,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/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/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 35ddd3f3f1..a87c9593c6 100644 --- a/client/tilespec.cpp +++ b/client/tilespec.cpp @@ -78,6 +78,11 @@ #include "editor.h" #include "goto.h" #include "helpdata.h" +#include "layer_background.h" +#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" @@ -110,97 +115,19 @@ #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 #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" - -/* 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"; +#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 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 - 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; }; @@ -301,18 +228,13 @@ 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; union { - QPixmap *single; QPixmap *cardinals[MAX_INDEX_CARDINAL]; - struct { - QPixmap *background, *middleground, *foreground; - } bmf; struct { QPixmap /* for extrastyles ESTYLE_ROAD_ALL_SEPARATE and @@ -343,17 +265,10 @@ 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]; }; struct specfile { @@ -382,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]; @@ -401,7 +309,12 @@ struct tileset { char *for_ruleset; - enum mapview_layer layer_order[LAYER_COUNT]; + std::vector> layers; + struct { + freeciv::layer_special *background, *middleground, *foreground; + } special_layers; + std::array terrain_layers; + freeciv::layer_darkness *darkness_layer; enum ts_type type; int hex_width, hex_height; @@ -419,7 +332,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; @@ -438,22 +350,17 @@ 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; - enum direction8 valid_tileset_dirs[8], cardinal_tileset_dirs[8]; - struct tileset_layer layers[MAX_NUM_LAYERS]; + std::array valid_tileset_dirs, cardinal_tileset_dirs; 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; struct extra_type_list *style_lists[ESTYLE_COUNT]; - struct extra_type_list *flagged_bases_list; int num_preferred_themes; char **preferred_themes; @@ -470,14 +377,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); @@ -488,14 +395,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); - -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); +bool is_extra_drawing_enabled(struct extra_type *pextra); static void tileset_player_free(struct tileset *t, int plrid); @@ -521,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 */ @@ -833,6 +694,35 @@ 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 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. + * + * 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. */ @@ -1048,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); @@ -1063,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; @@ -1080,22 +964,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->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; @@ -1250,9 +1118,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" @@ -1655,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, @@ -1758,6 +1623,64 @@ 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_DARKNESS: { + auto l = std::make_unique(t); + 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(); + 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; + 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; + } +} + /** Finds and reads the toplevel tilespec file based on given name. Sets global variables, including tile sizes and full names for @@ -2026,28 +1949,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); @@ -2086,6 +1987,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 +2001,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 +2017,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,31 +2029,50 @@ static struct tileset *tileset_read_toplevel(const char *tileset_name, return nullptr; } } + + for (auto layer : order) { + 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++) { - t->layer_order[i] = static_cast(i); + for (i = 0; i < LAYER_COUNT; ++i) { + tileset_add_layer(t, static_cast(i)); } } - // Terrain layer info. - for (i = 0; i < MAX_NUM_LAYERS; i++) { - struct tileset_layer *tslp = &t->layers[i]; - int j, k; + 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; + } - 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]); + 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; + } - 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. - } + 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++) { + 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; } } } @@ -2166,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->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; @@ -2341,7 +2199,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)); @@ -2450,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( @@ -2495,8 +2349,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; @@ -2558,25 +2412,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. @@ -3259,11 +3094,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); @@ -3275,28 +3110,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++) { @@ -3594,10 +3443,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); @@ -3611,8 +3456,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: { @@ -3800,25 +3647,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); } /** @@ -3828,279 +3666,20 @@ 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); - } - if (draw->init) { - t->sprites.drawing[terrain_index(pterrain)] = draw; - return; + for (auto &layer : t->layers) { + layer->initialize_terrain(pterrain); } +} - // 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]; - 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; -} - -/** - Set government sprite value; should only happen after - tilespec_load_tiles(). - */ -void tileset_setup_government(struct tileset *t, struct government *gov) -{ - t->sprites.government[government_index(gov)] = tiles_lookup_sprite_tag_alt( - t, LOG_FATAL, gov->graphic_str, gov->graphic_alt, "government", - government_rule_name(gov), false); +/** + Set government sprite value; should only happen after + tilespec_load_tiles(). + */ +void tileset_setup_government(struct tileset *t, struct government *gov) +{ + t->sprites.government[government_index(gov)] = tiles_lookup_sprite_tag_alt( + t, LOG_FATAL, gov->graphic_str, gov->graphic_alt, "government", + government_rule_name(gov), false); // should probably do something if NULL, eg generic default? } @@ -4162,16 +3741,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) \ - (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++) -#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. @@ -4180,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; @@ -4213,44 +3782,40 @@ 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; + sprs.emplace_back(t, uspr, true, FULL_TILE_X_OFFSET + t->unit_offset_x, + FULL_TILE_Y_OFFSET + t->unit_offset_y); } /** 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); 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. } } // 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); @@ -4317,8 +3882,9 @@ static int 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 +3911,8 @@ static int 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 +3920,19 @@ static int 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) { @@ -4396,25 +3962,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 @@ -4443,25 +4006,24 @@ static int 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]); } } } - - 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 +4137,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 @@ -4588,7 +4150,8 @@ static int 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]); } } } @@ -4617,12 +4180,12 @@ static int 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) { @@ -4643,8 +4206,8 @@ static int 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 { @@ -4656,11 +4219,9 @@ static int 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); } } - - return sprs - saved_sprs; } /** @@ -4692,14 +4253,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). */ if (!(pcity && gui_options.draw_cities)) { @@ -4723,35 +4282,32 @@ static int 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]); } } } } 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 +4319,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; } @@ -4775,9 +4331,9 @@ static int 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 @@ -4791,15 +4347,16 @@ static int 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); } @@ -4807,8 +4364,8 @@ static int 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); } } @@ -4816,70 +4373,27 @@ static int 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]); } - - 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) -{ - 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; - - /* - * 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, - static_cast(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; - } - - 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 && 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 + if (t->darkness_layer->style() == freeciv::DARKNESS_CORNER && pcorner && gui_options.draw_fog_of_war) { int i, tileno = 0; @@ -4908,263 +4422,9 @@ static int fill_fog_sprite_array(const struct tileset *t, } if (t->sprites.tx.fullfog[tileno]) { - 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 - 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]; - 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; - } - ADD_SPRITE(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; - } - ADD_SPRITE(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(static_cast(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) { - ADD_SPRITE(s, true, x, y); - } + sprs.emplace_back(t, t->sprites.tx.fullfog[tileno]); } - break; } - }; -#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) -{ - struct drawn_sprite *saved_sprs = sprs; - 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]))) { - ADD_SPRITE(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]); - } - } - 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) { - ADD_SPRITE_SIMPLE(t->sprites.tx.darkness[tileno]); - } - break; - case DARKNESS_CORNER: - // Handled separately. - 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) -{ - 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); - - 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) { - ADD_SPRITE_SIMPLE(sprite); - return 1; - } else { - return 0; - } - } - - if (l < draw->num_layers) { - sprs += 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); - } - } - - return sprs - saved_sprs; } /** @@ -5190,14 +4450,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]; @@ -5249,33 +4507,28 @@ static int 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]); - } 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.selected[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]); } } } @@ -5288,13 +4541,13 @@ static int 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]); } } } @@ -5305,7 +4558,7 @@ static int 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) { @@ -5323,27 +4576,24 @@ static int 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); } } } } - - 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; @@ -5357,20 +4607,20 @@ static int 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; } @@ -5382,7 +4632,7 @@ static int 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) { @@ -5397,15 +4647,13 @@ static int fill_goto_sprite_array(const struct tileset *t, } } } - - return sprs - saved_sprs; } /** 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 @@ -5469,20 +4717,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_near[8]{}; bv_extras textras; - struct terrain *tterrain_near[8]; + struct terrain *tterrain_near[8] = {nullptr}; 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. */ bool do_draw_unit = @@ -5490,8 +4736,7 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, && (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(); @@ -5505,7 +4750,7 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, || 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)); } @@ -5513,55 +4758,33 @@ 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. - 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) { - ADD_SPRITE_SIMPLE(t->sprites.player[player_index(owner)].background); - } else if (ptile && !gui_options.draw_terrain) { - ADD_SPRITE_SIMPLE(t->sprites.background.graphic); - } + fc_assert_ret_val(false, {}); break; case LAYER_TERRAIN1: - if (NULL != pterrain && gui_options.draw_terrain && !solid_bg) { - sprs += fill_terrain_sprite_layer(t, sprs, 0, ptile, pterrain, - tterrain_near); - } + fc_assert_ret_val(false, {}); break; case LAYER_DARKNESS: - if (NULL != pterrain && gui_options.draw_terrain && !solid_bg) { - sprs += fill_terrain_sprite_darkness(t, sprs, ptile, tterrain_near); - } + fc_assert_ret_val(false, {}); 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); - } + fc_assert_ret_val(false, {}); 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); - } + fc_assert_ret_val(false, {}); break; 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]; @@ -5570,133 +4793,81 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, 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; } } + } - 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) - { - 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; } - - 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; } + 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)) { - sprs += 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)) { - sprs += 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)) { - sprs += 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; - - 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) { - ADD_SPRITE_SIMPLE( - t->sprites.extras[extra_index(pextra)].u.single); - } - } - } - extra_type_list_iterate_end; - } + fc_assert_ret_val(false, {}); break; 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; @@ -5704,17 +4875,17 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, // 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 = @@ -5732,14 +4903,16 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, } 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 = @@ -5767,56 +4940,7 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, 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; - - 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) { - ADD_SPRITE_SIMPLE( - t->sprites.extras[extra_index(pextra)].u.single); - } - } - } - extra_type_list_iterate_end; - } + fc_assert_ret_val(false, {}); break; case LAYER_UNIT: @@ -5828,86 +4952,27 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, 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); } - 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; case LAYER_SPECIAL3: - if (NULL != pterrain) { - if (ptile) { - 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) { - 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; + fc_assert_ret_val(false, {}); + break; - 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); - } - } - } - } + case LAYER_BASE_FLAGS: + fc_assert_ret_val(false, {}); 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: @@ -5917,20 +4982,20 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, 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; } @@ -5956,14 +5021,14 @@ 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); + sprs.emplace_back(t, t->sprites.user.attention); } break; @@ -5974,7 +5039,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; @@ -5986,51 +5051,55 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, 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; @@ -6045,12 +5114,12 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, 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; @@ -6060,9 +5129,9 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, 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; @@ -6072,7 +5141,7 @@ int fill_sprite_array(struct tileset *t, struct drawn_sprite *sprs, break; } - return sprs - save_sprs; + return sprs; } /** @@ -6278,8 +5347,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); } /** @@ -6450,6 +5517,22 @@ 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 + */ +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. @@ -6622,9 +5705,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); @@ -6636,7 +5716,6 @@ void tileset_init(struct tileset *t) } t->sprites.player[id].color = NULL; - t->sprites.player[id].background = NULL; } player_slots_iterate_end; @@ -6652,167 +5731,67 @@ 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; - struct drawing_data *draw = t->sprites.drawing[terrain_index(pterrain)]; - - struct terrain *tterrain_near[8]; - bv_special tspecial_near[8]; + // 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; - struct tile dummy_tile; // :( - - int i; - - memset(&dummy_tile, 0, sizeof(struct tile)); - - 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; - sprs += fill_terrain_sprite_array(t, sprs, i, &dummy_tile, pterrain, - tterrain_near, draw); - - return sprs - save_sprs; + tile_virtual_destroy(tile); + 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) -{ - int idx = extra_index(pextra); - struct drawn_sprite *saved_sprs = sprs; - - switch (t->sprites.extras[idx].extrastyle) { - case ESTYLE_SINGLE1: - case ESTYLE_SINGLE2: - ADD_SPRITE_SIMPLE(t->sprites.extras[idx].u.single); - break; - case ESTYLE_CARDINALS: - ADD_SPRITE_SIMPLE(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: - return fill_basic_road_sprite_array(t, sprs, pextra); - case ESTYLE_3LAYER: - return fill_basic_base_sprite_array(t, sprs, pextra); - case ESTYLE_COUNT: - fc_assert(t->sprites.extras[idx].extrastyle != ESTYLE_COUNT); - break; - } - - return sprs - saved_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. - */ -int fill_basic_road_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) { - struct drawn_sprite *saved_sprs = sprs; - int idx; - int i; - int extrastyle; - - if (!t || !sprs || !pextra) { - return 0; - } - - 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); - if (!(0 <= idx && idx < game.control.num_extra_types)) { - return 0; - } - - extrastyle = t->sprites.extras[idx].extrastyle; - - if (extrastyle == ESTYLE_RIVER) { - ADD_SPRITE_SIMPLE(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]); - } 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)]); - } - } else if (extrastyle == ESTYLE_ROAD_ALL_COMBINED) { - ADD_SPRITE_SIMPLE(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); } } - return sprs - saved_sprs; + tile_virtual_destroy(tile); + return sprs; } -/** - 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. - */ -int fill_basic_base_sprite_array(const struct tileset *t, - struct drawn_sprite *sprs, - const struct extra_type *pextra) +const std::vector> & +tileset_get_layers(const struct tileset *t) { - struct drawn_sprite *saved_sprs = sprs; - int idx; - - if (!t || !sprs || !pextra) { - return 0; - } - - idx = extra_index(pextra); - - if (!(0 <= idx && idx < game.control.num_extra_types)) { - return 0; - } - -#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 - - return sprs - saved_sprs; -} - -/** - Gets the nth layer of the tileset. - */ -enum mapview_layer tileset_get_layer(const struct tileset *t, int n) -{ - 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) @@ -6828,6 +5807,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: @@ -6871,22 +5851,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++) { @@ -6914,14 +5890,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++) { @@ -6933,40 +5909,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. */ @@ -6980,11 +5922,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(); - } } /** diff --git a/client/tilespec.h b/client/tilespec.h index feed3d2afa..179c6b9c69 100644 --- a/client/tilespec.h +++ b/client/tilespec.h @@ -17,10 +17,9 @@ ***********************************************************************/ #pragma once +#include "layer.h" #include "options.h" -class QPixmap; // opaque; gui-dep - struct base_type; struct resource_type; @@ -53,139 +52,44 @@ 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" +#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" -/* 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 -}; - -/* 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 -}; +// 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 NUM_CORNER_DIRS 4 #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; @@ -226,27 +130,26 @@ 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 - -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); // 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); +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, + 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); @@ -313,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, @@ -346,10 +252,13 @@ 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); +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, const char *tag, const char *alt, @@ -384,6 +293,11 @@ 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); +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. */ #define TS_TOPO_SQUARE 0 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/data/3d.tilespec b/data/3d.tilespec index cfbb608e8f..ab5c36493a 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,14 +109,14 @@ 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. ; "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. @@ -131,8 +131,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..584953b464 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,14 +115,14 @@ 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. "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. @@ -137,8 +137,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..7acf43c75c 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,14 +106,14 @@ 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. ; "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. @@ -128,8 +128,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..7632a4cdf0 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,14 +106,14 @@ 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. ; "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. @@ -128,8 +128,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..bacbcc2906 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,14 +109,14 @@ 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. ; "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. @@ -131,8 +131,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..819351aa56 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,14 +109,14 @@ 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. ; "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. @@ -131,8 +131,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..bf2dfa0ad8 100644 --- a/data/hexemplio.tilespec +++ b/data/hexemplio.tilespec @@ -111,14 +111,14 @@ 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. "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 e921ebb735..cc51c3965b 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,14 +109,14 @@ 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. ; "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. @@ -131,8 +131,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..0a88c8fe60 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,14 +108,14 @@ 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. ; "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. @@ -130,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/toonhex.tilespec b/data/toonhex.tilespec index 1ae508fdc2..864d30ca0f 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,14 +113,14 @@ 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. "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. @@ -135,8 +135,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..0ddbbfa67d 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,14 +109,14 @@ 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. ; "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. @@ -131,8 +131,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/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._". 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);