90 #include "table/strings.h"
95 Point _tile_fract_coords;
178 bool _draw_bounding_boxes =
false;
179 bool _draw_dirty_blocks =
false;
180 uint _dirty_block_colour = 0;
183 static Point MapXYZToViewport(
const ViewPort *vp,
int x,
int y,
int z)
191 void DeleteWindowViewport(
Window *w)
213 int width,
int height, uint32 follow_flags,
ZoomLevel zoom)
231 if (follow_flags & 0x80000000) {
242 pt = MapXYZToViewport(vp, x, y, GetSlopePixelZ(x, y));
257 static Point _vp_move_offs;
259 static void DoSetViewportPosition(
const Window *w,
int left,
int top,
int width,
int height)
262 if (left + width > w->
left &&
264 top + height > w->
top &&
267 if (left < w->left) {
268 DoSetViewportPosition(w, left, top, w->
left - left, height);
269 DoSetViewportPosition(w, left + (w->
left - left), top, width - (w->
left - left), height);
274 DoSetViewportPosition(w, left, top, (w->
left + w->
width - left), height);
275 DoSetViewportPosition(w, left + (w->
left + w->
width - left), top, width - (w->
left + w->
width - left), height);
280 DoSetViewportPosition(w, left, top, width, (w->
top - top));
281 DoSetViewportPosition(w, left, top + (w->
top - top), width, height - (w->
top - top));
286 DoSetViewportPosition(w, left, top, width, (w->
top + w->
height - top));
287 DoSetViewportPosition(w, left, top + (w->
top + w->
height - top), width, height - (w->
top + w->
height - top));
296 int xo = _vp_move_offs.x;
297 int yo = _vp_move_offs.y;
299 if (
abs(xo) >= width ||
abs(yo) >= height) {
301 RedrawScreenRect(left, top, left + width, top + height);
305 GfxScroll(left, top, width, height, xo, yo);
308 RedrawScreenRect(left, top, xo + left, top + height);
312 RedrawScreenRect(left + width + xo, top, left + width, top + height);
317 RedrawScreenRect(left, top, width + left, top + yo);
319 RedrawScreenRect(left, top + height + yo, width + left, top + height);
324 static void SetViewportPosition(
Window *w,
int x,
int y)
330 int left, top, width, height;
346 if (old_top == 0 && old_left == 0)
return;
348 _vp_move_offs.x = old_left;
349 _vp_move_offs.y = old_top;
361 i = left + width - _screen.width;
362 if (i >= 0) width -= i;
370 i = top + height - _screen.height;
371 if (i >= 0) height -= i;
373 if (height > 0) DoSetViewportPosition(w->
z_front, left, top, width, height);
411 if ( (uint)(x -= vp->
left) >= (uint)vp->
width ||
412 (uint)(y -= vp->
top) >= (uint)vp->
height) {
430 b =
Clamp(b, -extra_tiles * TILE_SIZE,
MapMaxY() * TILE_SIZE - 1);
462 static Point GetTileFromScreenXY(
int x,
int y,
int zoom_x,
int zoom_y)
476 Point GetTileBelowCursor()
478 return GetTileFromScreenXY(_cursor.
pos.x, _cursor.
pos.y, _cursor.
pos.x, _cursor.
pos.y);
488 x = ((_cursor.
pos.x - vp->
left) >> 1) + (vp->
width >> 2);
489 y = ((_cursor.
pos.y - vp->
top) >> 1) + (vp->
height >> 2);
495 return GetTileFromScreenXY(_cursor.
pos.x, _cursor.
pos.y, x + vp->
left, y + vp->
top);
536 ts->
x = pt.x + extra_offs_x;
537 ts->
y = pt.y + extra_offs_y;
554 assert(
IsInsideMM(foundation_part, 0, FOUNDATION_PART_END));
555 assert(_vd.
foundation[foundation_part] != -1);
559 int *old_child = _vd.last_child;
562 AddChildSpriteScreen(image, pal, offs.x + extra_offs_x, offs.y + extra_offs_y,
false, sub,
false);
565 _vd.last_child = old_child;
590 AddTileSpriteToDraw(image, pal, _cur_ti->
x + x, _cur_ti->
y + y, _cur_ti->
z + z, sub, extra_offs_x * ZOOM_LVL_BASE, extra_offs_y * ZOOM_LVL_BASE);
626 default: NOT_REACHED();
653 if (pt.x + spr->
x_offs >= _vd.dpi.left + _vd.dpi.width ||
655 pt.y + spr->
y_offs >= _vd.dpi.top + _vd.dpi.height ||
688 void AddSortableSpriteToDraw(
SpriteID image,
PaletteID pal,
int x,
int y,
int w,
int h,
int dz,
int z,
bool transparent,
int bb_offset_x,
int bb_offset_y,
int bb_offset_z,
const SubSprite *sub)
690 int32 left, right, top, bottom;
705 _vd.last_child = NULL;
708 int tmp_left, tmp_top, tmp_x = pt.x, tmp_y = pt.y;
711 if (image == SPR_EMPTY_BOUNDING_BOX) {
712 left = tmp_left =
RemapCoords(x + w , y + bb_offset_y, z + bb_offset_z).x;
713 right =
RemapCoords(x + bb_offset_x, y + h , z + bb_offset_z).x + 1;
714 top = tmp_top =
RemapCoords(x + bb_offset_x, y + bb_offset_y, z + dz ).y;
715 bottom =
RemapCoords(x + w , y + h , z + bb_offset_z).y + 1;
718 left = tmp_left = (pt.x += spr->
x_offs);
719 right = (pt.x + spr->
width );
720 top = tmp_top = (pt.y += spr->
y_offs);
721 bottom = (pt.y + spr->
height);
724 if (_draw_bounding_boxes && (image != SPR_EMPTY_BOUNDING_BOX)) {
726 left =
min(left ,
RemapCoords(x + w , y + bb_offset_y, z + bb_offset_z).x);
727 right =
max(right ,
RemapCoords(x + bb_offset_x, y + h , z + bb_offset_z).x + 1);
728 top =
min(top ,
RemapCoords(x + bb_offset_x, y + bb_offset_y, z + dz ).y);
729 bottom =
max(bottom,
RemapCoords(x + w , y + h , z + bb_offset_z).y + 1);
733 if (left >= _vd.dpi.left + _vd.dpi.width ||
734 right <= _vd.dpi.left ||
735 top >= _vd.dpi.top + _vd.dpi.height ||
736 bottom <= _vd.dpi.top) {
750 ps->
xmin = x + bb_offset_x;
751 ps->
xmax = x +
max(bb_offset_x, w) - 1;
753 ps->
ymin = y + bb_offset_y;
754 ps->
ymax = y +
max(bb_offset_y, h) - 1;
756 ps->
zmin = z + bb_offset_z;
757 ps->
zmax = z +
max(bb_offset_z, dz) - 1;
812 if (begin > end)
Swap(begin, end);
813 return begin <= check && check <= end;
824 int dist_a = (_thd.
size.x + _thd.
size.y);
825 int dist_b = (_thd.
size.x - _thd.
size.y);
826 int a = ((x - _thd.
pos.x) + (y - _thd.
pos.y));
827 int b = ((x - _thd.
pos.x) - (y - _thd.
pos.y));
848 if (_vd.last_child == NULL)
return;
856 *_vd.last_child = _vd.child_screen_sprites_to_draw.
Length();
862 cs->x = scale ? x * ZOOM_LVL_BASE : x;
863 cs->y = scale ? y * ZOOM_LVL_BASE : y;
871 _vd.last_child = &cs->
next;
874 static void AddStringToDraw(
int x,
int y,
StringID string, uint64 params_1, uint64 params_2, Colours colour, uint16 width)
881 ss->params[0] = params_1;
882 ss->params[1] = params_2;
924 SpriteID sel2 = SPR_HALFTILE_SELECTION_FLAT + halftile_corner;
929 sel = SPR_HALFTILE_SELECTION_DOWN;
933 sel += opposite_corner;
940 static bool IsPartOfAutoLine(
int px,
int py)
950 case HT_DIR_HU:
return px == -py || px == -py - 16;
951 case HT_DIR_HL:
return px == -py || px == -py + 16;
952 case HT_DIR_VL:
return px == py || px == py + 16;
953 case HT_DIR_VR:
return px == py || px == py - 16;
986 static const uint _lower_rail[4] = { 5U, 2U, 4U, 3U };
988 if (autorail_type != _lower_rail[halftile_corner]) {
995 offset = _AutorailTilehSprite[autorail_tileh][autorail_type];
997 image = SPR_AUTORAIL_BASE + offset;
1000 image = SPR_AUTORAIL_BASE - offset;
1041 if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z +=
TILE_HEIGHT;
1042 if (halftile_corner != CORNER_S) {
1053 }
else if (IsPartOfAutoLine(ti->
x, ti->
y)) {
1071 if (!is_redsq && _thd.
outersize.x > 0 &&
1097 assert(_vd.dpi.top <= _vd.dpi.top + _vd.dpi.height);
1098 assert(_vd.dpi.left <= _vd.dpi.left + _vd.dpi.width);
1116 int left_column = (upper_left.y - upper_left.x) / (
int)
TILE_SIZE - 2;
1117 int right_column = (upper_right.y - upper_right.x) / (
int)
TILE_SIZE + 2;
1125 int row = (upper_left.x + upper_left.y) / (
int)
TILE_SIZE - 2;
1126 bool last_row =
false;
1127 for (; !last_row; row++) {
1129 for (
int column = left_column; column <= right_column; column++) {
1131 if ((row + column) % 2 != 0)
continue;
1134 tilecoord.x = (row - column) / 2;
1135 tilecoord.y = (row + column) / 2;
1136 assert(column == tilecoord.y - tilecoord.x);
1137 assert(row == tilecoord.y + tilecoord.x);
1141 _cur_ti = &tile_info;
1147 tile_info.
tile =
TileXY(tilecoord.x, tilecoord.y);
1171 int min_visible_height = viewport_y - (_vd.dpi.top + _vd.dpi.height);
1172 bool tile_visible = min_visible_height <= 0;
1191 if ((tilecoord.x <= 0 || tilecoord.y <= 0) && min_visible_height < potential_bridge_height +
MAX_TILE_EXTENT_TOP) last_row =
false;
1221 bool small = dpi->zoom >= small_from;
1223 int left = dpi->left;
1225 int right = left + dpi->width;
1226 int bottom = top + dpi->height;
1231 if (bottom < sign->top ||
1232 top > sign->
top + sign_height ||
1233 right < sign->center - sign_half_width ||
1234 left > sign->
center + sign_half_width) {
1239 AddStringToDraw(sign->
center - sign_half_width, sign->
top, string_normal, params_1, params_2, colour, sign->
width_normal);
1241 int shadow_offset = 0;
1242 if (string_small_shadow != STR_NULL) {
1244 AddStringToDraw(sign->
center - sign_half_width + shadow_offset, sign->
top, string_small_shadow, params_1, params_2, INVALID_COLOUR, sign->
width_small);
1246 AddStringToDraw(sign->
center - sign_half_width, sign->
top - shadow_offset, string_small, params_1, params_2,
1259 STR_VIEWPORT_TOWN_TINY_WHITE, STR_VIEWPORT_TOWN_TINY_BLACK,
1270 FOR_ALL_BASE_STATIONS(st) {
1281 is_station ? STR_VIEWPORT_STATION : STR_VIEWPORT_WAYPOINT,
1282 (is_station ? STR_VIEWPORT_STATION : STR_VIEWPORT_WAYPOINT) + 1, STR_NULL,
1322 GetString(buffer, str,
lastof(buffer));
1327 if (str_small != STR_NULL) {
1328 GetString(buffer, str_small,
lastof(buffer));
1348 zoomlevels[zoom].top = this->top -
ScaleByZoom(1, zoom);
1354 FOR_ALL_WINDOWS_FROM_BACK(w) {
1356 if (vp != NULL && vp->
zoom <= maxzoom) {
1357 assert(vp->
width != 0);
1383 while (psd != psdvend) {
1429 *psd3 = *(psd3 - 1);
1444 while (child_idx >= 0) {
1446 child_idx = cs->
next;
1467 pt2.x - pt1.x, pt2.y - pt1.y,
1468 pt3.x - pt1.x, pt3.y - pt1.y,
1469 pt4.x - pt1.x, pt4.y - pt1.y);
1488 byte bo =
UnScaleByZoom(dpi->left + dpi->top, dpi->zoom) & 1;
1490 for (
int i = (bo ^= 1); i < right; i += 2) blitter->
SetPixel(dst, i, 0, (uint8)colour);
1491 dst = blitter->
MoveTo(dst, 0, 1);
1492 }
while (--bottom > 0);
1500 bool small =
HasBit(ss->width, 15);
1501 int w =
GB(ss->width, 0, 15);
1509 if (ss->colour != INVALID_COLOUR) {
1522 x, y, x + w, y + h, ss->colour,
1532 void ViewportDoDraw(
const ViewPort *vp,
int left,
int top,
int right,
int bottom)
1535 _cur_dpi = &_vd.dpi;
1537 _vd.dpi.zoom = vp->
zoom;
1542 _vd.dpi.width = (right - left) & mask;
1543 _vd.dpi.height = (bottom - top) & mask;
1544 _vd.dpi.left = left & mask;
1545 _vd.dpi.top = top & mask;
1546 _vd.dpi.pitch = old_dpi->pitch;
1547 _vd.last_child = NULL;
1557 ViewportAddTownNames(&_vd.dpi);
1558 ViewportAddStationNames(&_vd.dpi);
1559 ViewportAddSigns(&_vd.dpi);
1561 DrawTextEffects(&_vd.dpi);
1563 if (_vd.tile_sprites_to_draw.
Length() != 0) ViewportDrawTileSprites(&_vd.tile_sprites_to_draw);
1587 vp->overlay->
Draw(&dp);
1590 if (_vd.string_sprites_to_draw.
Length() != 0) {
1594 ViewportDrawStrings(zoom, &_vd.string_sprites_to_draw);
1599 _vd.string_sprites_to_draw.
Clear();
1600 _vd.tile_sprites_to_draw.
Clear();
1601 _vd.parent_sprites_to_draw.
Clear();
1603 _vd.child_screen_sprites_to_draw.
Clear();
1613 if ((bottom - top) > (right - left)) {
1614 int t = (top + bottom) >> 1;
1618 int t = (left + right) >> 1;
1632 static inline void ViewportDraw(
const ViewPort *vp,
int left,
int top,
int right,
int bottom)
1634 if (right <= vp->left || bottom <= vp->top)
return;
1636 if (left >= vp->
left + vp->
width)
return;
1638 if (left < vp->left) left = vp->
left;
1641 if (top >= vp->
top + vp->
height)
return;
1643 if (top < vp->top) top = vp->
top;
1656 dpi->left += this->
left;
1657 dpi->top += this->
top;
1659 ViewportDraw(this->
viewport, dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height);
1661 dpi->left -= this->
left;
1662 dpi->top -= this->
top;
1697 iter =
Clamp(iter + offset, 0, iter_limit);
1699 }
while (continue_criteria(iter, iter_limit, sy, sy_limit));
1718 static inline int ClampXYToMap(
Point &curr_tile,
int &iter,
int iter_limit,
int start,
int &other_ref,
int other_value,
int vp_value,
int other_limit,
int vp_top,
int vp_bottom)
1730 other_ref = upper_edge ? 0 : other_limit;
1737 min_iter =
min(min_iter, max_iter);
1740 int max_heightlevel_at_edge = 0;
1741 for (iter = min_iter; iter <= max_iter; iter += 10) {
1742 max_heightlevel_at_edge =
max(max_heightlevel_at_edge, (
int)
TileHeight(
TileXY(curr_tile.x, curr_tile.y)));
1748 max(vp_value, -max_heightlevel_at_edge * (
int)(
TILE_HEIGHT * 2 * ZOOM_LVL_BASE)) :
1752 static inline void ClampViewportToMap(
const ViewPort *vp,
int &x,
int &y)
1762 int vx = -x + y * 2;
1767 int tx = vx / (int)(
TILE_SIZE * 4 * ZOOM_LVL_BASE);
1768 int ty = vy / (int)(
TILE_SIZE * 4 * ZOOM_LVL_BASE);
1797 SetViewportPosition(w, pt.x, pt.y);
1805 bool update_overlay =
false;
1806 if (delta_x != 0 || delta_y != 0) {
1823 if (update_overlay) RebuildViewportOverlay(w);
1839 right += (1 << vp->
zoom) - 1;
1840 bottom += (1 << vp->
zoom) - 1;
1843 if (right <= 0)
return;
1846 if (bottom <= 0)
return;
1875 FOR_ALL_WINDOWS_FROM_BACK(w) {
1878 assert(vp->
width != 0);
1884 void ConstrainAllViewportsZoom()
1887 FOR_ALL_WINDOWS_FROM_FRONT(w) {
1939 int x_size = _thd.
size.x;
1940 int y_size = _thd.
size.y;
1943 int x_start = _thd.
pos.x;
1944 int y_start = _thd.
pos.y;
1948 x_start += _thd.
offs.x;
1950 y_start += _thd.
offs.y;
1956 assert(x_size >= 0);
1957 assert(y_size >= 0);
1966 assert((x_end | y_end | x_start | y_start) %
TILE_SIZE == 0);
1987 int top_y = y_start;
2008 static const int OVERLAY_WIDTH = 4 * ZOOM_LVL_BASE;
2014 if (top_x != x_start) {
2021 if (bot_y != y_end) {
2026 }
while (bot_x >= top_x);
2029 int a_size = x_size + y_size, b_size = x_size - y_size;
2034 for (
int a = -interval_a; a != a_size + interval_a; a += interval_a) {
2035 for (
int b = -interval_b; b != b_size + interval_b; b += interval_b) {
2048 void SetSelectionRed(
bool b)
2071 return y >= sign->
top && y < sign->
top + sign_height &&
2072 x >= sign->
center - sign_half_width && x < sign->
center + sign_half_width;
2075 static bool CheckClickOnTown(
const ViewPort *vp,
int x,
int y)
2082 ShowTownViewWindow(t->
index);
2090 static bool CheckClickOnStation(
const ViewPort *vp,
int x,
int y)
2095 FOR_ALL_BASE_STATIONS(st) {
2119 static bool CheckClickOnSign(
const ViewPort *vp,
int x,
int y)
2128 if (si->owner ==
OWNER_DEITY && _game_mode != GM_EDITOR)
continue;
2140 static bool CheckClickOnLandscape(
const ViewPort *vp,
int x,
int y)
2144 if (pt.x != -1)
return ClickTile(
TileVirtXY(pt.x, pt.y));
2148 static void PlaceObject()
2153 pt = GetTileBelowCursor();
2154 if (pt.x == -1)
return;
2169 bool HandleViewportClicked(
const ViewPort *vp,
int x,
int y)
2183 if (CheckClickOnTown(vp, x, y))
return true;
2184 if (CheckClickOnStation(vp, x, y))
return true;
2185 if (CheckClickOnSign(vp, x, y))
return true;
2186 bool result = CheckClickOnLandscape(vp, x, y);
2203 void RebuildViewportOverlay(
Window *w)
2205 if (w->
viewport->overlay != NULL &&
2228 z = GetSlopePixelZ(x, y);
2242 RebuildViewportOverlay(w);
2303 void SetTileSelectBigSize(
int ox,
int oy,
int sx,
int sy)
2361 bool new_diagonal =
false;
2373 new_diagonal =
true;
2375 if (x1 >= x2)
Swap(x1, x2);
2376 if (y1 >= y2)
Swap(y1, y2);
2382 if (!new_diagonal) {
2389 Point pt = GetTileBelowCursor();
2421 default: NOT_REACHED();
2504 void VpSetPlaceSizingLimit(
int limit)
2528 static void VpStartPreSizing()
2540 int fxpy = _tile_fract_coords.x + _tile_fract_coords.y;
2542 int fxmy = _tile_fract_coords.x - _tile_fract_coords.y;
2546 default: NOT_REACHED();
2548 if (fxpy >= 20 && sxpy <= 12)
return HT_DIR_HL;
2549 if (fxmy < -3 && sxmy > 3)
return HT_DIR_VR;
2553 if (fxmy > 3 && sxmy < -3)
return HT_DIR_VL;
2554 if (fxpy <= 12 && sxpy >= 20)
return HT_DIR_HU;
2558 if (fxmy > 3 && sxmy < -3)
return HT_DIR_VL;
2559 if (fxpy >= 20 && sxpy <= 12)
return HT_DIR_HL;
2563 if (fxmy < -3 && sxmy > 3)
return HT_DIR_VR;
2564 if (fxpy <= 12 && sxpy >= 20)
return HT_DIR_HU;
2584 uint start_x =
TileX(start_tile);
2585 uint start_y =
TileY(start_tile);
2586 uint end_x =
TileX(end_tile);
2587 uint end_y =
TileY(end_tile);
2591 case HT_LINE:
return (end_x > start_x || (end_x == start_x && end_y > start_y));
2594 case HT_POINT:
return (end_x != start_x && end_y < start_y);
2595 default: NOT_REACHED();
2621 if (start_tile == end_tile)
return 0;
2622 if (swap)
Swap(start_tile, end_tile);
2633 byte style_t = (byte)(
TileX(end_tile) >
TileX(start_tile));
2648 {1, 0}, {1, 1}, {0, 1}, {1, 1},
2649 {1, 0}, {0, 0}, {1, 0}, {1, 1},
2650 {1, 0}, {1, 1}, {0, 1}, {1, 1},
2652 {0, 1}, {0, 0}, {1, 0}, {0, 0},
2653 {0, 1}, {0, 0}, {1, 1}, {0, 1},
2654 {1, 0}, {0, 0}, {0, 0}, {0, 1},
2664 if (swap && distance == 0) style = flip_style_direction[style];
2667 byte style_t = style * 2;
2668 assert(style_t <
lengthof(heightdiff_line_by_dir) - 13);
2675 if (distance == 0) style_t = flip_style_direction[style] * 2;
2676 assert(style_t <
lengthof(heightdiff_line_by_dir) - 13);
2684 if (swap)
Swap(h0, h1);
2688 static const StringID measure_strings_length[] = {STR_NULL, STR_MEASURE_LENGTH, STR_MEASURE_LENGTH_HEIGHTDIFF};
2698 if (test >= 0)
return;
2700 other += mult * test;
2713 if (test <= max)
return;
2715 other += mult * (test -
max);
2758 int offset = (raw_dx - raw_dy) / 2;
2793 int offset = (raw_dx + raw_dy + (int)
TILE_SIZE) / 2;
2842 }
else if (w > h * 2) {
2845 }
else if (h > w * 2) {
2859 }
else if (d >= 0) {
2870 }
else if (d >= 0) {
2883 }
else if (d >= 0) {
2894 }
else if (d >= 0) {
2912 if (distance != 1) {
2918 distance =
CeilDiv(distance, 2);
2921 params[index++] = distance;
2922 if (heightdiff != 0) params[index++] = heightdiff;
2971 if (
abs(sy - y) <
abs(sx - x)) {
2978 goto calc_heightdiff_single_direction;
2987 goto calc_heightdiff_single_direction;
2997 calc_heightdiff_single_direction:;
2999 x = sx +
Clamp(x - sx, -limit, limit);
3000 y = sy +
Clamp(y - sy, -limit, limit);
3009 if (distance != 1) {
3017 params[index++] = distance;
3018 if (heightdiff != 0) params[index++] = heightdiff;
3027 x = sx +
Clamp(x - sx, -limit, limit);
3028 y = sy +
Clamp(y - sy, -limit, limit);
3033 static const StringID measure_strings_area[] = {
3034 STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF
3060 int a_max = dist_x + dist_y;
3061 int b_max = dist_y - dist_x;
3065 a_max =
abs(a_max + (a_max > 0 ? 2 : -2)) / 2;
3066 b_max =
abs(b_max + (b_max > 0 ? 2 : -2)) / 2;
3072 if (a_max != 1 || b_max != 1) {
3079 }
else if (dy == 1) {
3084 if (dx != 1 || dy != 1) {
3087 params[index++] = dx - (style &
HT_POINT ? 1 : 0);
3088 params[index++] = dy - (style &
HT_POINT ? 1 : 0);
3089 if (heightdiff != 0) params[index++] = heightdiff;
3096 default: NOT_REACHED();
3237 { &ViewportSortParentSpritesSSE41Checker, &ViewportSortParentSpritesSSE41 },
3245 for (uint i = 0; i <
lengthof(_vp_sprite_sorters); i++) {
3246 if (_vp_sprite_sorters[i].fct_checker()) {
3247 _vp_sprite_sorter = _vp_sprite_sorters[i].
fct_sorter;
3251 assert(_vp_sprite_sorter != NULL);