Index: lang/english.txt =================================================================== --- lang/english.txt (revision 5694) +++ lang/english.txt (working copy) @@ -1805,6 +1805,13 @@ STR_4829_REQUIRES :{BLACK}Requires: {YELLOW}{STRING}, {STRING}, {STRING} ############ range for requires ends +############ range for smallmap-specific start +STR_RECENTRE_SMALLMAP :{BLACK}Recenter the view +STR_ZOOM_IN_SMALLMAP :{BLACK}zoom in +STR_ZOOM_OUT_SMALLMAP :{BLACK}zoom out +STR_SMALLMAP_ZOOMDISPLAY :{TINYFONT}{WHITE}zoom: {COMMA}% ({COMMA}) +############ range for smallmap-specific end + STR_482A_PRODUCTION_LAST_MONTH :{BLACK}Production last month: STR_482B_TRANSPORTED :{YELLOW}{STRING1}{BLACK} ({COMMA}% transported) STR_482C_CENTER_THE_MAIN_VIEW_ON :{BLACK}Centre the main view on industry location Index: smallmap_gui.c =================================================================== --- smallmap_gui.c (revision 5694) +++ smallmap_gui.c (working copy) @@ -36,9 +36,11 @@ { WWT_IMGBTN, RESIZE_LRTB, 13, 380, 401, 280, 301, SPR_IMG_SHOW_ROUTES, STR_0194_SHOW_TRANSPORT_ROUTES_ON}, { WWT_IMGBTN, RESIZE_LRTB, 13, 402, 423, 280, 301, SPR_IMG_PLANTTREES, STR_0195_SHOW_VEGETATION_ON_MAP}, { WWT_IMGBTN, RESIZE_LRTB, 13, 424, 445, 280, 301, SPR_IMG_COMPANY_GENERAL, STR_0196_SHOW_LAND_OWNERS_ON_MAP}, -{ WWT_IMGBTN, RESIZE_LRTB, 13, 358, 379, 258, 279, 0x0, STR_NULL}, +{ WWT_PANEL, RESIZE_LRTB, 13, 358, 379, 258, 279, 683, STR_RECENTRE_SMALLMAP}, { WWT_IMGBTN, RESIZE_LRTB, 13, 358, 379, 280, 301, SPR_IMG_TOWN, STR_0197_TOGGLE_TOWN_NAMES_ON_OFF}, -{ WWT_IMGBTN, RESIZE_RTB, 13, 0, 357, 258, 301, 0x0, STR_NULL}, +{ WWT_PANEL, RESIZE_LRTB, 13, 336, 357, 258, 279, 0x2DF, STR_ZOOM_IN_SMALLMAP}, +{ WWT_PANEL, RESIZE_LRTB, 13, 336, 357, 280, 301, 0x2E0, STR_ZOOM_OUT_SMALLMAP}, +{ WWT_IMGBTN, RESIZE_RTB, 13, 0, 335, 258, 301, 0x0, STR_NULL}, { WWT_PANEL, RESIZE_RTB, 13, 0, 433, 302, 313, 0x0, STR_NULL}, { WWT_RESIZEBOX, RESIZE_LRTB, 13, 434, 445, 302, 313, 0x0, STR_RESIZE_BUTTON}, { WIDGETS_END}, @@ -46,11 +48,16 @@ static int _smallmap_type; static bool _smallmap_show_towns = true; +void SmallMapRecentre(void); +void SmallMapRecentreZoom(void); #define MK(a,b) a, b #define MKEND() 0xFFFF #define MS(a,b) (a | 0x100), b +static int max_zoom = 800; +static int min_zoom = 4; + /* Legend text giving the colours to look for on the minimap */ static const uint16 _legend_land_contours[] = { MK(0x5A,STR_00F0_100M), @@ -328,16 +335,19 @@ * @param proc Pointer to the colour function * @see GetSmallMapPixels(TileIndex) */ -static void DrawSmallMapStuff(Pixel *dst, uint xc, uint yc, int pitch, int reps, uint32 mask, GetSmallMapPixels *proc) +static void DrawSmallMapStuff(Pixel *dst, uint xc, uint yc, int pitch, int reps, uint32 mask, GetSmallMapPixels *proc, int zoom) { Pixel *dst_ptr_end = _screen.dst_ptr + _screen.width * _screen.height - _screen.width; do { + uint x = xc * 100 / zoom; + uint y = yc * 100 / zoom; + // check if the tile (xc,yc) is within the map range - if (xc < MapMaxX() && yc < MapMaxY()) { + if (x < MapMaxX() && y < MapMaxY()) { // check if the dst pointer points to a pixel inside the screen buffer if (dst > _screen.dst_ptr && dst < dst_ptr_end) - WRITE_PIXELS_OR(dst, proc(TileXY(xc, yc)) & mask); + WRITE_PIXELS_OR(dst, proc(TileXY(x, y)) & mask); } // switch to next tile in the column } while (xc++, yc++, dst += pitch, --reps != 0); @@ -596,6 +606,7 @@ int tile_x; int tile_y; ViewPort *vp; + int zoom = WP(w, smallmap_d).zoom; old_dpi = _cur_dpi; _cur_dpi = dpi; @@ -672,7 +683,7 @@ reps = (dpi->height - y + 1) / 2; if (reps > 0) { // assert(ptr >= dpi->dst_ptr); - DrawSmallMapStuff(ptr, tile_x, tile_y, dpi->pitch * 2, reps, mask, _smallmap_draw_procs[type]); + DrawSmallMapStuff(ptr, tile_x, tile_y, dpi->pitch*2, reps, mask, _smallmap_draw_procs[type], zoom); } skip_column: @@ -700,8 +711,8 @@ (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0) { // Remap into flat coordinates. Point pt = RemapCoords( - (v->x_pos - WP(w,smallmap_d).scroll_x) / TILE_SIZE, - (v->y_pos - WP(w,smallmap_d).scroll_y) / TILE_SIZE, + ((v->x_pos * zoom / 100) - WP(w, smallmap_d).scroll_x) / TILE_SIZE, + ((v->y_pos * zoom / 100) - WP(w, smallmap_d).scroll_y) / TILE_SIZE, 0); x = pt.x; y = pt.y; @@ -745,8 +756,8 @@ if (t->xy != 0) { // Remap the town coordinate Point pt = RemapCoords( - (int)(TileX(t->xy) * TILE_SIZE - WP(w, smallmap_d).scroll_x) / TILE_SIZE, - (int)(TileY(t->xy) * TILE_SIZE - WP(w, smallmap_d).scroll_y) / TILE_SIZE, + (int)((TileX(t->xy) * zoom / 100) * TILE_SIZE - WP(w, smallmap_d).scroll_x) / TILE_SIZE, + (int)((TileY(t->xy) * zoom / 100) * TILE_SIZE - WP(w, smallmap_d).scroll_y) / TILE_SIZE, 0); x = pt.x - WP(w,smallmap_d).subscroll + 3 - (t->sign.width_2 >> 1); y = pt.y; @@ -773,10 +784,10 @@ pt = RemapCoords(WP(w, smallmap_d).scroll_x, WP(w, smallmap_d).scroll_y, 0); - x = vp->virtual_left - pt.x; - y = vp->virtual_top - pt.y; - x2 = (x + vp->virtual_width) / TILE_SIZE; - y2 = (y + vp->virtual_height) / TILE_SIZE; + x = (vp->virtual_left * zoom / 100) - pt.x; + y = (vp->virtual_top * zoom / 100) - pt.y; + x2 = (x + (vp->virtual_width * zoom / 100)) / TILE_SIZE; + y2 = (y + (vp->virtual_height * zoom / 100)) / TILE_SIZE; x /= TILE_SIZE; y /= TILE_SIZE; @@ -792,8 +803,105 @@ _cur_dpi = old_dpi; } +void SmallMapRecentre(void) +{ + int x, y, wx, wy; + int zoom; + Window *w; + ViewPort *vp; + + /* find the smallmap-window */ + w = FindWindowById(WC_SMALLMAP, 0); + + /* get its zoom */ + zoom = WP(w, smallmap_d).zoom; + + /* find the main viewport */ + vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport; + + wx = (w->widget[4].right - w->widget[4].left) / 2; + wy = (w->widget[4].bottom - w->widget[4].top ) / 2; + x = (((((vp->virtual_width * zoom / 100) - (wx * 32)) / 2) + (vp->virtual_left * zoom / 100)) / 4); + y = (((((vp->virtual_height * zoom / 100) - (wy * 32)) / 2) + (vp->virtual_top * zoom / 100)) / 2) - 32; + + WP(w, smallmap_d).scroll_x = (y - x) & ~0xF; + WP(w, smallmap_d).scroll_y = (x + y) & ~0xF; + WP(w, smallmap_d).subscroll = 0; + + /* handle disabled states of buttons */ + CLRBIT(w->disabled_state, 13); + CLRBIT(w->disabled_state, 14); + + if (zoom >= max_zoom) SETBIT(w->disabled_state, 13); + if (zoom <= min_zoom) SETBIT(w->disabled_state, 14); +} + +void ScrollSmallmap(int mx, int my) +{ + int hx, hy, hvx, hvy, x, y, sub, maxx, maxy, zoom; + + Window *w = FindWindowById(WC_SMALLMAP, 0); + zoom = WP(w, smallmap_d).zoom; + + x = WP(w, smallmap_d).scroll_x; + y = WP(w, smallmap_d).scroll_y; + + sub = WP(w, smallmap_d).subscroll + mx; + + x -= (sub >> 2) << 4; + y += (sub >> 2) << 4; + sub &= 3; + + x += (my >> 1) << 4; + y += (my >> 1) << 4; + + if (my & 1) { + x += 16; + sub += 2; + if (sub > 3) { + sub -= 4; + x -= 16; + y += 16; + } + } + + hx = (w->widget[4].right - w->widget[4].left) / 2; + hy = (w->widget[4].bottom - w->widget[4].top ) / 2; + hvx = hx * -4 + hy * 8; + hvy = hx * 4 + hy * 8; + if (x < -hvx) { + x = -hvx; + sub = 0; + } + maxx = ((MapMaxX() * 16) * zoom / 100) - hvx; + if (x >= maxx) { + x = maxx; + sub = 0; + } + if (y < -hvy) { + y = -hvy; + sub = 0; + } + maxy = ((MapMaxY() * 16) * zoom / 100) - hvy; + if (y >= maxy) { + y = maxy; + sub = 0; + } + WP(w, smallmap_d).scroll_x = x; + WP(w, smallmap_d).scroll_y = y; + WP(w, smallmap_d).subscroll = sub; +} + +void SmallMapRecentreZoom(void) +{ + /* todo: rewrite this to work with zooming directly, not focusing everytime to the viewport! */ + SmallMapRecentre(); +} + static void SmallMapWindowProc(Window *w, WindowEvent *e) { + int zoom = WP(w,smallmap_d).zoom; + switch (e->event) { case WE_PAINT: { const uint16 *tbl; @@ -829,6 +937,11 @@ return; DrawSmallMap(&new_dpi, w, _smallmap_type, _smallmap_show_towns); + SetDParam(0, zoom); + SetDParam(1, zoom / 10); + DrawString(4, w->height - 64 - 2, STR_SMALLMAP_ZOOMDISPLAY, 12); + + } break; case WE_CLICK: @@ -842,8 +955,9 @@ w2 = FindWindowById(WC_MAIN_WINDOW, 0); pt = RemapCoords(WP(w,smallmap_d).scroll_x, WP(w,smallmap_d).scroll_y, 0); - WP(w2,vp_d).scrollpos_x = pt.x + ((_cursor.pos.x - w->left + 2) << 4) - (w2->viewport->virtual_width >> 1); - WP(w2,vp_d).scrollpos_y = pt.y + ((_cursor.pos.y - w->top - 16) << 4) - (w2->viewport->virtual_height >> 1); + WP(w2,vp_d).scrollpos_x = (pt.x * 100 / zoom) + (((_cursor.pos.x - w->left + 2) << 4) * 100 / zoom) - (w2->viewport->virtual_width >> 1); + WP(w2,vp_d).scrollpos_y = (pt.y * 100 / zoom) + (((_cursor.pos.y - w->top - 16) << 4) * 100 / zoom) - (w2->viewport->virtual_height >> 1); + SetWindowDirty(w); /* focus-rect should not lag */ } break; case 5: /* Show land contours */ @@ -860,12 +974,36 @@ SndPlayFx(SND_15_BEEP); break; + case 11: /* centre location */ + SmallMapRecentre(); + SetWindowDirty(w); + SndPlayFx(SND_15_BEEP); + break; + case 12: /* toggle town names */ w->click_state ^= (1 << 12); _smallmap_show_towns = (w->click_state >> 12) & 1; SetWindowDirty(w); SndPlayFx(SND_15_BEEP); break; + + case 13: /* zoom in */ + if (zoom < max_zoom) { // can zoom in up to 5 times + WP(w, smallmap_d).zoom_target = min(max_zoom, WP(w, smallmap_d).zoom * 2); + SmallMapRecentreZoom(); + SetWindowDirty(w); + SndPlayFx(SND_15_BEEP); + } + break; + + case 14: /* zoom out */ + if (zoom > min_zoom) { + WP(w, smallmap_d).zoom_target = max(min_zoom, WP(w, smallmap_d).zoom / 2); + SmallMapRecentreZoom(); + SetWindowDirty(w); + SndPlayFx(SND_15_BEEP); + } + break; } break; @@ -882,6 +1020,52 @@ /* update the window every now and then */ if ((++w->vscroll.pos & 0x1F) == 0) SetWindowDirty(w); break; + + case WE_SCROLL: + _cursor.fix_at = true; + ScrollSmallmap(e->scroll.delta.x,e->scroll.delta.y); + SetWindowDirty(w); + break; + + case WE_MOUSEWHEEL: + if (e->wheel.wheel < 0 && zoom < max_zoom) { + /* can zoom in up to 5 times */ + WP(w, smallmap_d).zoom_target = min(max_zoom, WP(w,smallmap_d).zoom * (-2 * e->wheel.wheel)); + } else if (e->wheel.wheel > 0 && zoom > min_zoom) { + WP(w, smallmap_d).zoom_target = max(min_zoom, WP(w,smallmap_d).zoom / ( 2 * e->wheel.wheel)); + } else { + return; + } + SmallMapRecentreZoom(); + SetWindowDirty(w); + break; + + case WE_TICK: + /* zoom smoothly :) */ + if (WP(w,smallmap_d).zoom != WP(w,smallmap_d).zoom_target) { + int diff = WP(w,smallmap_d).zoom_target - WP(w,smallmap_d).zoom; + int diff_abs = myabs(diff); + + if (diff_abs < zoom / 10) { + WP(w,smallmap_d).zoom = WP(w,smallmap_d).zoom_target; + } else { + WP(w,smallmap_d).zoom += diff / 5; + } + + SmallMapRecentreZoom(); + SetWindowDirty(w); + break; + + case WE_RECENTER: + SmallMapRecentre(); + SetWindowDirty(w); + break; + } + break; + + default: + break; + } } @@ -897,22 +1081,27 @@ { Window *w; ViewPort *vp; - int x,y; + int x, y, wx, wy, zoom; w = AllocateWindowDescFront(&_smallmap_desc, 0); - if (w != NULL) { - w->click_state = ((1<<5) << _smallmap_type) | (_smallmap_show_towns << 12); - w->resize.width = 350; - w->resize.height = 250; + if (w == NULL) return; - vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport; + w->click_state = ((1<<5) << _smallmap_type) | (_smallmap_show_towns << 12); + w->resize.width = 350; + w->resize.height = 250; + WP(w, smallmap_d).zoom = 100; + WP(w, smallmap_d).zoom_target = 100; - x = ((vp->virtual_width - 220 * 32) / 2 + vp->virtual_left) / 4; - y = ((vp->virtual_height - 120 * 32) / 2 + vp->virtual_top ) / 2 - 32; - WP(w,smallmap_d).scroll_x = (y - x) & ~0xF; - WP(w,smallmap_d).scroll_y = (x + y) & ~0xF; - WP(w,smallmap_d).subscroll = 0; - } + zoom = WP(w, smallmap_d).zoom; + vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport; + + wx = (w->widget[4].right - w->widget[4].left) / 2; + wy = (w->widget[4].bottom - w->widget[4].top ) / 2; + x = ((((vp->virtual_width * 100 / zoom) - (wx * 32)) / 2) + vp->virtual_left) / (zoom / 25); + y = (((((vp->virtual_height * 100 / zoom) - (wy * 32)) / 2) + vp->virtual_top) / (zoom / 50)) - 32; + WP(w, smallmap_d).scroll_x = (y - x) & ~0xF; + WP(w, smallmap_d).scroll_y = (x + y) & ~0xF; + WP(w, smallmap_d).subscroll = 0; } /* Extra ViewPort Window Stuff */ Index: viewport.c =================================================================== --- viewport.c (revision 5694) +++ viewport.c (working copy) @@ -1769,7 +1769,19 @@ bool ScrollMainWindowTo(int x, int y) { - return ScrollWindowTo(x, y, FindWindowById(WC_MAIN_WINDOW, 0)); + bool res = false; + res = ScrollWindowTo(x, y, FindWindowById(WC_MAIN_WINDOW, 0)); + + /* recenter the smallmap after 2nd click */ + Window *sw = FindWindowById(WC_SMALLMAP, 0); + if (sw == NULL) { + return res; + } else { + WindowEvent we; + we.event = WE_RECENTER; + sw->wndproc(sw, &we); + return res; + } } Index: window.c =================================================================== --- window.c (revision 5694) +++ window.c (working copy) @@ -118,9 +118,15 @@ { const Widget *wi1, *wi2; Scrollbar *sb; + WindowEvent e; if (widget < 0) return; + /* send WE_MOUSEWHEEL event to window */ + e.event = WE_MOUSEWHEEL; + e.wheel.wheel = wheel; + w->wndproc(w, &e); + wi1 = &w->widget[widget]; wi2 = &w->widget[widget + 1]; @@ -1179,64 +1185,12 @@ WP(w,vp_d).scrollpos_y += dy << vp->zoom; } else { - int x; - int y; - int sub; - int hx; - int hy; - int hvx; - int hvy; - - _cursor.fix_at = true; - - x = WP(w,smallmap_d).scroll_x; - y = WP(w,smallmap_d).scroll_y; - - sub = WP(w,smallmap_d).subscroll + dx; - - x -= (sub >> 2) << 4; - y += (sub >> 2) << 4; - sub &= 3; - - x += (dy >> 1) << 4; - y += (dy >> 1) << 4; - - if (dy & 1) { - x += TILE_SIZE; - sub += 2; - if (sub > 3) { - sub -= 4; - x -= TILE_SIZE; - y += TILE_SIZE; - } - } - - hx = (w->widget[4].right - w->widget[4].left) / 2; - hy = (w->widget[4].bottom - w->widget[4].top ) / 2; - hvx = hx * -4 + hy * 8; - hvy = hx * 4 + hy * 8; - if (x < -hvx) { - x = -hvx; - sub = 0; - } - if (x > (int)MapMaxX() * TILE_SIZE - hvx) { - x = MapMaxX() * TILE_SIZE - hvx; - sub = 0; - } - if (y < -hvy) { - y = -hvy; - sub = 0; - } - if (y > (int)MapMaxY() * TILE_SIZE - hvy) { - y = MapMaxY() * TILE_SIZE - hvy; - sub = 0; - } - - WP(w,smallmap_d).scroll_x = x; - WP(w,smallmap_d).scroll_y = y; - WP(w,smallmap_d).subscroll = sub; - - SetWindowDirty(w); + /* create a scroll-event and send it to the client */ + WindowEvent we; + we.event = WE_SCROLL; + we.scroll.delta.x = _cursor.delta.x; + we.scroll.delta.y = _cursor.delta.y; + w->wndproc(w, &we); } _cursor.delta.x = 0; Index: window.h =================================================================== --- window.h (revision 5694) +++ window.h (working copy) @@ -134,6 +134,16 @@ uint wparam; // additional message-specific information uint lparam; // additional message-specific information } message; + + struct { + byte event; + Point delta; // cursor delta information + } scroll; + + struct { + byte event; + int wheel; // scrollwheel change + } wheel; }; enum WindowKeyCodes { @@ -371,8 +381,10 @@ int32 scroll_x; int32 scroll_y; int32 subscroll; + int zoom; + int zoom_target; } smallmap_d; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(traindetails_d)); +assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(smallmap_d)); typedef struct { uint32 face; @@ -492,7 +504,10 @@ WE_MOUSEOVER = 20, WE_ON_EDIT_TEXT_CANCEL = 21, WE_RESIZE = 22, - WE_MESSAGE = 23 + WE_MESSAGE = 23, + WE_SCROLL = 24, + WE_MOUSEWHEEL = 25, + WE_RECENTER = 26 };