Changeset - r7912:aac2d19fbcb0
[Not reviewed]
master
0 1 0
smatz - 17 years ago 2007-11-18 20:05:44
smatz@openttd.org
(svn r11465) -Fix: more user-friedly placement in X and Y directions (most noticeable with autoroad)
1 file changed with 14 insertions and 2 deletions:
0 comments (0 inline, 0 general)
src/viewport.cpp
Show inline comments
 
@@ -2052,795 +2052,807 @@ static void CheckClickOnLandscape(const 
 

	
 
	if (pt.x != -1) ClickTile(TileVirtXY(pt.x, pt.y));
 
}
 

	
 

	
 
static void SafeShowTrainViewWindow(const Vehicle* v)
 
{
 
	if (!IsFrontEngine(v)) v = v->First();
 
	ShowVehicleViewWindow(v);
 
}
 

	
 
static void SafeShowRoadVehViewWindow(const Vehicle *v)
 
{
 
	if (!IsRoadVehFront(v)) v = v->First();
 
	ShowVehicleViewWindow(v);
 
}
 

	
 
static void Nop(const Vehicle *v) {}
 

	
 
typedef void OnVehicleClickProc(const Vehicle *v);
 
static OnVehicleClickProc* const _on_vehicle_click_proc[] = {
 
	SafeShowTrainViewWindow,
 
	SafeShowRoadVehViewWindow,
 
	ShowVehicleViewWindow,
 
	ShowVehicleViewWindow,
 
	Nop, // Special vehicles
 
	Nop  // Disaster vehicles
 
};
 

	
 
void HandleViewportClicked(const ViewPort *vp, int x, int y)
 
{
 
	const Vehicle *v;
 

	
 
	if (CheckClickOnTown(vp, x, y)) return;
 
	if (CheckClickOnStation(vp, x, y)) return;
 
	if (CheckClickOnSign(vp, x, y)) return;
 
	if (CheckClickOnWaypoint(vp, x, y)) return;
 
	CheckClickOnLandscape(vp, x, y);
 

	
 
	v = CheckClickOnVehicle(vp, x, y);
 
	if (v != NULL) {
 
		DEBUG(misc, 2, "Vehicle %d (index %d) at %p", v->unitnumber, v->index, v);
 
		_on_vehicle_click_proc[v->type](v);
 
	}
 
}
 

	
 
Vehicle *CheckMouseOverVehicle()
 
{
 
	const Window *w;
 
	const ViewPort *vp;
 

	
 
	int x = _cursor.pos.x;
 
	int y = _cursor.pos.y;
 

	
 
	w = FindWindowFromPt(x, y);
 
	if (w == NULL) return NULL;
 

	
 
	vp = IsPtInWindowViewport(w, x, y);
 
	return (vp != NULL) ? CheckClickOnVehicle(vp, x, y) : NULL;
 
}
 

	
 

	
 

	
 
void PlaceObject()
 
{
 
	Point pt;
 
	Window *w;
 

	
 
	pt = GetTileBelowCursor();
 
	if (pt.x == -1) return;
 

	
 
	if (_thd.place_mode == VHM_POINT) {
 
		pt.x += 8;
 
		pt.y += 8;
 
	}
 

	
 
	_tile_fract_coords.x = pt.x & 0xF;
 
	_tile_fract_coords.y = pt.y & 0xF;
 

	
 
	w = GetCallbackWnd();
 
	if (w != NULL) {
 
		WindowEvent e;
 

	
 
		e.event = WE_PLACE_OBJ;
 
		e.we.place.pt = pt;
 
		e.we.place.tile = TileVirtXY(pt.x, pt.y);
 
		w->wndproc(w, &e);
 
	}
 
}
 

	
 

	
 
/* scrolls the viewport in a window to a given location */
 
bool ScrollWindowTo(int x , int y, Window *w, bool instant)
 
{
 
	Point pt;
 

	
 
	pt = MapXYZToViewport(w->viewport, x, y, GetSlopeZ(x, y));
 
	WP(w, vp_d).follow_vehicle = INVALID_VEHICLE;
 

	
 
	if (WP(w, vp_d).dest_scrollpos_x == pt.x && WP(w, vp_d).dest_scrollpos_y == pt.y)
 
		return false;
 

	
 
	if (instant) {
 
		WP(w, vp_d).scrollpos_x = pt.x;
 
		WP(w, vp_d).scrollpos_y = pt.y;
 
	}
 

	
 
	WP(w, vp_d).dest_scrollpos_x = pt.x;
 
	WP(w, vp_d).dest_scrollpos_y = pt.y;
 
	return true;
 
}
 

	
 

	
 
bool ScrollMainWindowTo(int x, int y, bool instant)
 
{
 
	Window *w;
 
	bool res = ScrollWindowTo(x, y, FindWindowById(WC_MAIN_WINDOW, 0), instant);
 

	
 
	/* If a user scrolls to a tile (via what way what so ever) and already is on
 
	 *  that tile (e.g.: pressed twice), move the smallmap to that location,
 
	 *  so you directly see where you are on the smallmap. */
 

	
 
	if (res) return res;
 

	
 
	w = FindWindowById(WC_SMALLMAP, 0);
 
	if (w == NULL) return res;
 

	
 
	SmallMapCenterOnCurrentPos(w);
 

	
 
	return res;
 
}
 

	
 

	
 
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
 
{
 
	return ScrollMainWindowTo(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, instant);
 
}
 

	
 
void SetRedErrorSquare(TileIndex tile)
 
{
 
	TileIndex old;
 

	
 
	old = _thd.redsq;
 
	_thd.redsq = tile;
 

	
 
	if (tile != old) {
 
		if (tile != 0) MarkTileDirtyByTile(tile);
 
		if (old  != 0) MarkTileDirtyByTile(old);
 
	}
 
}
 

	
 
void SetTileSelectSize(int w, int h)
 
{
 
	_thd.new_size.x = w * TILE_SIZE;
 
	_thd.new_size.y = h * TILE_SIZE;
 
	_thd.new_outersize.x = 0;
 
	_thd.new_outersize.y = 0;
 
}
 

	
 
void SetTileSelectBigSize(int ox, int oy, int sx, int sy)
 
{
 
	_thd.offs.x = ox * TILE_SIZE;
 
	_thd.offs.y = oy * TILE_SIZE;
 
	_thd.new_outersize.x = sx * TILE_SIZE;
 
	_thd.new_outersize.y = sy * TILE_SIZE;
 
}
 

	
 
/** returns the best autorail highlight type from map coordinates */
 
static byte GetAutorailHT(int x, int y)
 
{
 
	return HT_RAIL | _AutorailPiece[x & 0xF][y & 0xF];
 
}
 

	
 
/**
 
 * Updates tile highlighting for all cases.
 
 * Uses _thd.selstart and _thd.selend and _thd.place_mode (set elsewhere) to determine _thd.pos and _thd.size
 
 * Also drawstyle is determined. Uses _thd.new.* as a buffer and calls SetSelectionTilesDirty() twice,
 
 * Once for the old and once for the new selection.
 
 * _thd is TileHighlightData, found in viewport.h
 
 * Called by MouseLoop() in windows.cpp
 
 */
 
void UpdateTileSelection()
 
{
 
	int x1;
 
	int y1;
 

	
 
	_thd.new_drawstyle = 0;
 

	
 
	if (_thd.place_mode == VHM_SPECIAL) {
 
		x1 = _thd.selend.x;
 
		y1 = _thd.selend.y;
 
		if (x1 != -1) {
 
			int x2 = _thd.selstart.x;
 
			int y2 = _thd.selstart.y;
 
			int x2 = _thd.selstart.x & ~0xF;
 
			int y2 = _thd.selstart.y & ~0xF;
 
			x1 &= ~0xF;
 
			y1 &= ~0xF;
 

	
 
			if (x1 >= x2) Swap(x1, x2);
 
			if (y1 >= y2) Swap(y1, y2);
 
			_thd.new_pos.x = x1;
 
			_thd.new_pos.y = y1;
 
			_thd.new_size.x = x2 - x1 + TILE_SIZE;
 
			_thd.new_size.y = y2 - y1 + TILE_SIZE;
 
			_thd.new_drawstyle = _thd.next_drawstyle;
 
		}
 
	} else if (_thd.place_mode != VHM_NONE) {
 
		Point pt = GetTileBelowCursor();
 
		x1 = pt.x;
 
		y1 = pt.y;
 
		if (x1 != -1) {
 
			switch (_thd.place_mode) {
 
				case VHM_RECT:
 
					_thd.new_drawstyle = HT_RECT;
 
					break;
 
				case VHM_POINT:
 
					_thd.new_drawstyle = HT_POINT;
 
					x1 += 8;
 
					y1 += 8;
 
					break;
 
				case VHM_RAIL:
 
					_thd.new_drawstyle = GetAutorailHT(pt.x, pt.y); // draw one highlighted tile
 
			}
 
			_thd.new_pos.x = x1 & ~0xF;
 
			_thd.new_pos.y = y1 & ~0xF;
 
		}
 
	}
 

	
 
	/* redraw selection */
 
	if (_thd.drawstyle != _thd.new_drawstyle ||
 
			_thd.pos.x != _thd.new_pos.x || _thd.pos.y != _thd.new_pos.y ||
 
			_thd.size.x != _thd.new_size.x || _thd.size.y != _thd.new_size.y ||
 
			_thd.outersize.x != _thd.new_outersize.x ||
 
			_thd.outersize.y != _thd.new_outersize.y) {
 
		/* clear the old selection? */
 
		if (_thd.drawstyle) SetSelectionTilesDirty();
 

	
 
		_thd.drawstyle = _thd.new_drawstyle;
 
		_thd.pos = _thd.new_pos;
 
		_thd.size = _thd.new_size;
 
		_thd.outersize = _thd.new_outersize;
 
		_thd.dirty = 0xff;
 

	
 
		/* draw the new selection? */
 
		if (_thd.new_drawstyle) SetSelectionTilesDirty();
 
	}
 
}
 

	
 
/** highlighting tiles while only going over them with the mouse */
 
void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, byte process)
 
{
 
	_thd.select_method = method;
 
	_thd.select_proc   = process;
 
	_thd.selend.x = TileX(tile) * TILE_SIZE;
 
	_thd.selstart.x = TileX(tile) * TILE_SIZE;
 
	_thd.selend.y = TileY(tile) * TILE_SIZE;
 
	_thd.selstart.y = TileY(tile) * TILE_SIZE;
 

	
 
	/* Needed so several things (road, autoroad, bridges, ...) are placed correctly.
 
	 * In effect, placement starts from the centre of a tile
 
	 */
 
	if (method == VPM_X_OR_Y || method == VPM_FIX_X || method == VPM_FIX_Y) {
 
		_thd.selend.x += TILE_SIZE / 2;
 
		_thd.selend.y += TILE_SIZE / 2;
 
		_thd.selstart.x += TILE_SIZE / 2;
 
		_thd.selstart.y += TILE_SIZE / 2;
 
	}
 

	
 
	if (_thd.place_mode == VHM_RECT) {
 
		_thd.place_mode = VHM_SPECIAL;
 
		_thd.next_drawstyle = HT_RECT;
 
	} else if (_thd.place_mode == VHM_RAIL) { // autorail one piece
 
		_thd.place_mode = VHM_SPECIAL;
 
		_thd.next_drawstyle = _thd.drawstyle;
 
	} else {
 
		_thd.place_mode = VHM_SPECIAL;
 
		_thd.next_drawstyle = HT_POINT;
 
	}
 
	_special_mouse_mode = WSM_SIZING;
 
}
 

	
 
void VpSetPlaceSizingLimit(int limit)
 
{
 
	_thd.sizelimit = limit;
 
}
 

	
 
/**
 
* Highlights all tiles between a set of two tiles. Used in dock and tunnel placement
 
* @param from TileIndex of the first tile to highlight
 
* @param to TileIndex of the last tile to highlight */
 
void VpSetPresizeRange(TileIndex from, TileIndex to)
 
{
 
	uint64 distance = DistanceManhattan(from, to) + 1;
 

	
 
	_thd.selend.x = TileX(to) * TILE_SIZE;
 
	_thd.selend.y = TileY(to) * TILE_SIZE;
 
	_thd.selstart.x = TileX(from) * TILE_SIZE;
 
	_thd.selstart.y = TileY(from) * TILE_SIZE;
 
	_thd.next_drawstyle = HT_RECT;
 

	
 
	/* show measurement only if there is any length to speak of */
 
	if (distance > 1) GuiShowTooltipsWithArgs(STR_MEASURE_LENGTH, 1, &distance);
 
}
 

	
 
static void VpStartPreSizing()
 
{
 
	_thd.selend.x = -1;
 
	_special_mouse_mode = WSM_PRESIZE;
 
}
 

	
 
/** returns information about the 2x1 piece to be build.
 
 * The lower bits (0-3) are the track type. */
 
static byte Check2x1AutoRail(int mode)
 
{
 
	int fxpy = _tile_fract_coords.x + _tile_fract_coords.y;
 
	int sxpy = (_thd.selend.x & 0xF) + (_thd.selend.y & 0xF);
 
	int fxmy = _tile_fract_coords.x - _tile_fract_coords.y;
 
	int sxmy = (_thd.selend.x & 0xF) - (_thd.selend.y & 0xF);
 

	
 
	switch (mode) {
 
	case 0: // end piece is lower right
 
		if (fxpy >= 20 && sxpy <= 12) { /*SwapSelection(); DoRailroadTrack(0); */return 3; }
 
		if (fxmy < -3 && sxmy > 3) {/* DoRailroadTrack(0); */return 5; }
 
		return 1;
 

	
 
	case 1:
 
		if (fxmy > 3 && sxmy < -3) { /*SwapSelection(); DoRailroadTrack(0); */return 4; }
 
		if (fxpy <= 12 && sxpy >= 20) { /*DoRailroadTrack(0); */return 2; }
 
		return 1;
 

	
 
	case 2:
 
		if (fxmy > 3 && sxmy < -3) { /*DoRailroadTrack(3);*/ return 4; }
 
		if (fxpy >= 20 && sxpy <= 12) { /*SwapSelection(); DoRailroadTrack(0); */return 3; }
 
		return 0;
 

	
 
	case 3:
 
		if (fxmy < -3 && sxmy > 3) { /*SwapSelection(); DoRailroadTrack(3);*/ return 5; }
 
		if (fxpy <= 12 && sxpy >= 20) { /*DoRailroadTrack(0); */return 2; }
 
		return 0;
 
	}
 

	
 
	return 0; // avoids compiler warnings
 
}
 

	
 
/** Check if the direction of start and end tile should be swapped based on
 
 * the dragging-style. Default directions are:
 
 * in the case of a line (HT_RAIL, HT_LINE):  DIR_NE, DIR_NW, DIR_N, DIR_E
 
 * in the case of a rect (HT_RECT, HT_POINT): DIR_S, DIR_E
 
 * For example dragging a rectangle area from south to north should be swapped to
 
 * north-south (DIR_S) to obtain the same results with less code. This is what
 
 * the return value signifies.
 
 * @param style HighLightStyle dragging style
 
 * @param start_tile start tile of drag
 
 * @param end_tile end tile of drag
 
 * @return boolean value which when true means start/end should be swapped */
 
static bool SwapDirection(HighLightStyle style, TileIndex start_tile, TileIndex end_tile)
 
{
 
	uint start_x = TileX(start_tile);
 
	uint start_y = TileY(start_tile);
 
	uint end_x = TileX(end_tile);
 
	uint end_y = TileY(end_tile);
 

	
 
	switch (style & HT_DRAG_MASK) {
 
		case HT_RAIL:
 
		case HT_LINE: return (end_x > start_x || (end_x == start_x && end_y > start_y));
 

	
 
		case HT_RECT:
 
		case HT_POINT: return (end_x != start_x && end_y < start_y);
 
		default: NOT_REACHED();
 
	}
 

	
 
	return false;
 
}
 

	
 
/** Calculates height difference between one tile and another
 
* Multiplies the result to suit the standard given by minimap - 50 meters high
 
* To correctly get the height difference we need the direction we are dragging
 
* in, as well as with what kind of tool we are dragging. For example a horizontal
 
* autorail tool that starts in bottom and ends at the top of a tile will need the
 
* maximum of SW, S and SE, N corners respectively. This is handled by the lookup table below
 
* See _tileoffs_by_dir in map.c for the direction enums if you can't figure out
 
* the values yourself.
 
* @param style HightlightStyle of drag. This includes direction and style (autorail, rect, etc.)
 
* @param distance amount of tiles dragged, important for horizontal/vertical drags
 
*        ignored for others
 
* @param start_tile, end_tile start and end tile of drag operation
 
* @return height difference between two tiles. Tile measurement tool utilizes
 
* this value in its tooltips */
 
static int CalcHeightdiff(HighLightStyle style, uint distance, TileIndex start_tile, TileIndex end_tile)
 
{
 
	bool swap = SwapDirection(style, start_tile, end_tile);
 
	byte style_t;
 
	uint h0, h1, ht; // start heigth, end height, and temp variable
 

	
 
	if (start_tile == end_tile) return 0;
 
	if (swap) Swap(start_tile, end_tile);
 

	
 
	switch (style & HT_DRAG_MASK) {
 
		case HT_RECT: {
 
			static const TileIndexDiffC heightdiff_area_by_dir[] = {
 
				/* Start */ {1, 0}, /* Dragging east */ {0, 0}, /* Dragging south */
 
				/* End   */ {0, 1}, /* Dragging east */ {1, 1}  /* Dragging south */
 
			};
 

	
 
			/* In the case of an area we can determine whether we were dragging south or
 
			 * east by checking the X-coordinates of the tiles */
 
			style_t = (byte)(TileX(end_tile) > TileX(start_tile));
 
			start_tile = TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_area_by_dir[style_t]));
 
			end_tile   = TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_area_by_dir[2 + style_t]));
 
		}
 
		/* Fallthrough */
 
		case HT_POINT:
 
			h0 = TileHeight(start_tile);
 
			h1 = TileHeight(end_tile);
 
			break;
 
		default: { /* All other types, this is mostly only line/autorail */
 
			static const HighLightStyle flip_style_direction[] = {
 
				HT_DIR_X, HT_DIR_Y, HT_DIR_HL, HT_DIR_HU, HT_DIR_VR, HT_DIR_VL
 
			};
 
			static const TileIndexDiffC heightdiff_line_by_dir[] = {
 
				/* Start */ {1, 0}, {1, 1}, /* HT_DIR_X  */ {0, 1}, {1, 1}, /* HT_DIR_Y  */
 
				/* Start */ {1, 0}, {0, 0}, /* HT_DIR_HU */ {1, 0}, {1, 1}, /* HT_DIR_HL */
 
				/* Start */ {1, 0}, {1, 1}, /* HT_DIR_VL */ {0, 1}, {1, 1}, /* HT_DIR_VR */
 

	
 
				/* Start */ {0, 1}, {0, 0}, /* HT_DIR_X  */ {1, 0}, {0, 0}, /* HT_DIR_Y  */
 
				/* End   */ {0, 1}, {0, 0}, /* HT_DIR_HU */ {1, 1}, {0, 1}, /* HT_DIR_HL */
 
				/* End   */ {1, 0}, {0, 0}, /* HT_DIR_VL */ {0, 0}, {0, 1}, /* HT_DIR_VR */
 
			};
 

	
 
			distance %= 2; // we're only interested if the distance is even or uneven
 
			style &= HT_DIR_MASK;
 

	
 
			/* To handle autorail, we do some magic to be able to use a lookup table.
 
			 * Firstly if we drag the other way around, we switch start&end, and if needed
 
			 * also flip the drag-position. Eg if it was on the left, and the distance is even
 
			 * that means the end, which is now the start is on the right */
 
			if (swap && distance == 0) style = flip_style_direction[style];
 

	
 
			/* Use lookup table for start-tile based on HighLightStyle direction */
 
			style_t = style * 2;
 
			assert(style_t < lengthof(heightdiff_line_by_dir) - 13);
 
			h0 = TileHeight(TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_line_by_dir[style_t])));
 
			ht = TileHeight(TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_line_by_dir[style_t + 1])));
 
			h0 = max(h0, ht);
 

	
 
			/* Use lookup table for end-tile based on HighLightStyle direction
 
			 * flip around side (lower/upper, left/right) based on distance */
 
			if (distance == 0) style_t = flip_style_direction[style] * 2;
 
			assert(style_t < lengthof(heightdiff_line_by_dir) - 13);
 
			h1 = TileHeight(TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_line_by_dir[12 + style_t])));
 
			ht = TileHeight(TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_line_by_dir[12 + style_t + 1])));
 
			h1 = max(h1, ht);
 
		} break;
 
	}
 

	
 
	if (swap) Swap(h0, h1);
 
	/* Minimap shows height in intervals of 50 meters, let's do the same */
 
	return (int)(h1 - h0) * 50;
 
}
 

	
 
static const StringID measure_strings_length[] = {STR_NULL, STR_MEASURE_LENGTH, STR_MEASURE_LENGTH_HEIGHTDIFF};
 

	
 
/** while dragging */
 
static void CalcRaildirsDrawstyle(TileHighlightData *thd, int x, int y, int method)
 
{
 
	HighLightStyle b;
 
	uint w, h;
 

	
 
	int dx = thd->selstart.x - (thd->selend.x & ~0xF);
 
	int dy = thd->selstart.y - (thd->selend.y & ~0xF);
 
	w = myabs(dx) + 16;
 
	h = myabs(dy) + 16;
 

	
 
	if (TileVirtXY(thd->selstart.x, thd->selstart.y) == TileVirtXY(x, y)) { // check if we're only within one tile
 
		if (method == VPM_RAILDIRS) {
 
			b = GetAutorailHT(x, y);
 
		} else { // rect for autosignals on one tile
 
			b = HT_RECT;
 
		}
 
	} else if (h == 16) { // Is this in X direction?
 
		if (dx == 16) { // 2x1 special handling
 
			b = (Check2x1AutoRail(3)) | HT_LINE;
 
		} else if (dx == -16) {
 
			b = (Check2x1AutoRail(2)) | HT_LINE;
 
		} else {
 
			b = HT_LINE | HT_DIR_X;
 
		}
 
		y = thd->selstart.y;
 
	} else if (w == 16) { // Or Y direction?
 
		if (dy == 16) { // 2x1 special handling
 
			b = (Check2x1AutoRail(1)) | HT_LINE;
 
		} else if (dy == -16) { // 2x1 other direction
 
			b = (Check2x1AutoRail(0)) | HT_LINE;
 
		} else {
 
			b = HT_LINE | HT_DIR_Y;
 
		}
 
		x = thd->selstart.x;
 
	} else if (w > h * 2) { // still count as x dir?
 
		b = HT_LINE | HT_DIR_X;
 
		y = thd->selstart.y;
 
	} else if (h > w * 2) { // still count as y dir?
 
		b = HT_LINE | HT_DIR_Y;
 
		x = thd->selstart.x;
 
	} else { // complicated direction
 
		int d = w - h;
 
		thd->selend.x = thd->selend.x & ~0xF;
 
		thd->selend.y = thd->selend.y & ~0xF;
 

	
 
		// four cases.
 
		if (x > thd->selstart.x) {
 
			if (y > thd->selstart.y) {
 
				// south
 
				if (d == 0) {
 
					b = (x & 0xF) > (y & 0xF) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
 
				} else if (d >= 0) {
 
					x = thd->selstart.x + h;
 
					b = HT_LINE | HT_DIR_VL;
 
					// return px == py || px == py + 16;
 
				} else {
 
					y = thd->selstart.y + w;
 
					b = HT_LINE | HT_DIR_VR;
 
				} // return px == py || px == py - 16;
 
			} else {
 
				// west
 
				if (d == 0) {
 
					b = (x & 0xF) + (y & 0xF) >= 0x10 ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU;
 
				} else if (d >= 0) {
 
					x = thd->selstart.x + h;
 
					b = HT_LINE | HT_DIR_HL;
 
				} else {
 
					y = thd->selstart.y - w;
 
					b = HT_LINE | HT_DIR_HU;
 
				}
 
			}
 
		} else {
 
			if (y > thd->selstart.y) {
 
				// east
 
				if (d == 0) {
 
					b = (x & 0xF) + (y & 0xF) >= 0x10 ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU;
 
				} else if (d >= 0) {
 
					x = thd->selstart.x - h;
 
					b = HT_LINE | HT_DIR_HU;
 
					// return px == -py || px == -py - 16;
 
				} else {
 
					y = thd->selstart.y + w;
 
					b = HT_LINE | HT_DIR_HL;
 
				} // return px == -py || px == -py + 16;
 
			} else {
 
				// north
 
				if (d == 0) {
 
					b = (x & 0xF) > (y & 0xF) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
 
				} else if (d >= 0) {
 
					x = thd->selstart.x - h;
 
					b = HT_LINE | HT_DIR_VR;
 
					// return px == py || px == py - 16;
 
				} else {
 
					y = thd->selstart.y - w;
 
					b = HT_LINE | HT_DIR_VL;
 
				} //return px == py || px == py + 16;
 
			}
 
		}
 
	}
 

	
 
	if (_patches.measure_tooltip) {
 
		TileIndex t0 = TileVirtXY(thd->selstart.x, thd->selstart.y);
 
		TileIndex t1 = TileVirtXY(x, y);
 
		uint distance = DistanceManhattan(t0, t1) + 1;
 
		byte index = 0;
 
		uint64 params[2];
 

	
 
		if (distance != 1) {
 
			int heightdiff = CalcHeightdiff(b, distance, t0, t1);
 
			/* If we are showing a tooltip for horizontal or vertical drags,
 
			 * 2 tiles have a length of 1. To bias towards the ceiling we add
 
			 * one before division. It feels more natural to count 3 lengths as 2 */
 
			if ((b & HT_DIR_MASK) != HT_DIR_X && (b & HT_DIR_MASK) != HT_DIR_Y) {
 
				distance = (distance + 1) / 2;
 
			}
 

	
 
			params[index++] = distance;
 
			if (heightdiff != 0) params[index++] = heightdiff;
 
		}
 

	
 
		GuiShowTooltipsWithArgs(measure_strings_length[index], index, params);
 
	}
 

	
 
	thd->selend.x = x;
 
	thd->selend.y = y;
 
	thd->next_drawstyle = b;
 
}
 

	
 
/**
 
 * Selects tiles while dragging
 
 * @param x X coordinate of end of selection
 
 * @param y Y coordinate of end of selection
 
 * @param method modifies the way tiles are selected. Possible
 
 * methods are VPM_* in viewport.h */
 
void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method)
 
{
 
	int sx, sy;
 
	HighLightStyle style;
 

	
 
	if (x == -1) {
 
		_thd.selend.x = -1;
 
		return;
 
	}
 

	
 
	/* Special handling of drag in any (8-way) direction */
 
	if (method == VPM_RAILDIRS || method == VPM_SIGNALDIRS) {
 
		_thd.selend.x = x;
 
		_thd.selend.y = y;
 
		CalcRaildirsDrawstyle(&_thd, x, y, method);
 
		return;
 
	}
 

	
 
	/* Needed so level-land is placed correctly */
 
	if (_thd.next_drawstyle == HT_POINT) {
 
		x += TILE_SIZE / 2;
 
		y += TILE_SIZE / 2;
 
	}
 

	
 
	sx = _thd.selstart.x;
 
	sy = _thd.selstart.y;
 

	
 
	switch (method) {
 
		case VPM_X_OR_Y: /* drag in X or Y direction */
 
			if (myabs(sy - y) < myabs(sx - x)) {
 
				y = sy;
 
				style = HT_DIR_X;
 
			} else {
 
				x = sx;
 
				style = HT_DIR_Y;
 
			}
 
			goto calc_heightdiff_single_direction;
 
		case VPM_FIX_X: /* drag in Y direction */
 
			x = sx;
 
			style = HT_DIR_Y;
 
			goto calc_heightdiff_single_direction;
 
		case VPM_FIX_Y: /* drag in X direction */
 
			y = sy;
 
			style = HT_DIR_X;
 

	
 
calc_heightdiff_single_direction:;
 
			if (_patches.measure_tooltip) {
 
				TileIndex t0 = TileVirtXY(sx, sy);
 
				TileIndex t1 = TileVirtXY(x, y);
 
				uint distance = DistanceManhattan(t0, t1) + 1;
 
				byte index = 0;
 
				uint64 params[2];
 

	
 
				if (distance != 1) {
 
					/* With current code passing a HT_LINE style to calculate the height
 
					 * difference is enough. However if/when a point-tool is created
 
					 * with this method, function should be called with new_style (below)
 
					 * instead of HT_LINE | style case HT_POINT is handled specially
 
					 * new_style := (_thd.next_drawstyle & HT_RECT) ? HT_LINE | style : _thd.next_drawstyle; */
 
					int heightdiff = CalcHeightdiff(HT_LINE | style, 0, t0, t1);
 

	
 
					params[index++] = distance;
 
					if (heightdiff != 0) params[index++] = heightdiff;
 
				}
 

	
 
				GuiShowTooltipsWithArgs(measure_strings_length[index], index, params);
 
			} break;
 

	
 
		case VPM_X_AND_Y_LIMITED: { /* drag an X by Y constrained rect area */
 
			int limit = (_thd.sizelimit - 1) * TILE_SIZE;
 
			x = sx + clamp(x - sx, -limit, limit);
 
			y = sy + clamp(y - sy, -limit, limit);
 
			} /* Fallthrough */
 
		case VPM_X_AND_Y: { /* drag an X by Y area */
 
			if (_patches.measure_tooltip) {
 
				static const StringID measure_strings_area[] = {
 
					STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF
 
				};
 

	
 
				TileIndex t0 = TileVirtXY(sx, sy);
 
				TileIndex t1 = TileVirtXY(x, y);
 
				uint dx = delta(TileX(t0), TileX(t1)) + 1;
 
				uint dy = delta(TileY(t0), TileY(t1)) + 1;
 
				byte index = 0;
 
				uint64 params[3];
 

	
 
				/* If dragging an area (eg dynamite tool) and it is actually a single
 
				 * row/column, change the type to 'line' to get proper calculation for height */
 
				style = _thd.next_drawstyle;
 
				if (style & HT_RECT) {
 
					if (dx == 1) {
 
						style = HT_LINE | HT_DIR_Y;
 
					} else if (dy == 1) {
 
						style = HT_LINE | HT_DIR_X;
 
					}
 
				}
 

	
 
				if (dx != 1 || dy != 1) {
 
					int heightdiff = CalcHeightdiff(style, 0, t0, t1);
 

	
 
					params[index++] = dx;
 
					params[index++] = dy;
 
					if (heightdiff != 0) params[index++] = heightdiff;
 
				}
 

	
 
				GuiShowTooltipsWithArgs(measure_strings_area[index], index, params);
 
			}
 
		break;
 

	
 
		}
 
		default: NOT_REACHED();
 
	}
 

	
 
	_thd.selend.x = x;
 
	_thd.selend.y = y;
 
}
 

	
 
/** while dragging */
 
bool VpHandlePlaceSizingDrag()
 
{
 
	Window *w;
 
	WindowEvent e;
 

	
 
	if (_special_mouse_mode != WSM_SIZING) return true;
 

	
 
	e.we.place.select_method = _thd.select_method;
 
	e.we.place.select_proc   = _thd.select_proc;
 

	
 
	/* stop drag mode if the window has been closed */
 
	w = FindWindowById(_thd.window_class, _thd.window_number);
 
	if (w == NULL) {
 
		ResetObjectToPlace();
 
		return false;
 
	}
 

	
 
	/* while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() ) */
 
	if (_left_button_down) {
 
		e.event = WE_PLACE_DRAG;
 
		e.we.place.pt = GetTileBelowCursor();
 
		w->wndproc(w, &e);
 
		return false;
 
	}
 

	
 
	/* mouse button released..
 
	 * keep the selected tool, but reset it to the original mode. */
 
	_special_mouse_mode = WSM_NONE;
 
	if (_thd.next_drawstyle == HT_RECT) {
 
		_thd.place_mode = VHM_RECT;
 
	} else if (e.we.place.select_method == VPM_SIGNALDIRS) { // some might call this a hack... -- Dominik
 
		_thd.place_mode = VHM_RECT;
 
	} else if (_thd.next_drawstyle & HT_LINE) {
 
		_thd.place_mode = VHM_RAIL;
 
	} else if (_thd.next_drawstyle & HT_RAIL) {
 
		_thd.place_mode = VHM_RAIL;
 
	} else {
 
		_thd.place_mode = VHM_POINT;
 
	}
 
	SetTileSelectSize(1, 1);
 

	
 
	/* and call the mouseup event. */
 
	e.event = WE_PLACE_MOUSEUP;
 
	e.we.place.pt = _thd.selend;
 
	e.we.place.tile = TileVirtXY(e.we.place.pt.x, e.we.place.pt.y);
 
	e.we.place.starttile = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
 
	w->wndproc(w, &e);
 

	
 
	return false;
 
}
 

	
 
void SetObjectToPlaceWnd(CursorID icon, SpriteID pal, ViewportHighlightMode mode, Window *w)
 
{
 
	SetObjectToPlace(icon, pal, mode, w->window_class, w->window_number);
 
}
 

	
 
#include "table/animcursors.h"
 

	
 
void SetObjectToPlace(CursorID icon, SpriteID pal, ViewportHighlightMode mode, WindowClass window_class, WindowNumber window_num)
 
{
 
	Window *w;
 

	
 
	/* undo clicking on button */
 
	if (_thd.place_mode != 0) {
 
		_thd.place_mode = 0;
 
		w = FindWindowById(_thd.window_class, _thd.window_number);
 
		if (w != NULL) CallWindowEventNP(w, WE_ABORT_PLACE_OBJ);
 
	}
 

	
 
	SetTileSelectSize(1, 1);
 

	
 
	_thd.make_square_red = false;
 

	
 
	if (mode == VHM_DRAG) { // VHM_DRAG is for dragdropping trains in the depot window
 
		mode = VHM_NONE;
 
		_special_mouse_mode = WSM_DRAGDROP;
 
	} else {
 
		_special_mouse_mode = WSM_NONE;
 
	}
 

	
 
	_thd.place_mode = mode;
 
	_thd.window_class = window_class;
 
	_thd.window_number = window_num;
 

	
 
	if (mode == VHM_SPECIAL) // special tools, like tunnels or docks start with presizing mode
 
		VpStartPreSizing();
 

	
 
	if ( (int)icon < 0)
 
		SetAnimatedMouseCursor(_animcursors[~icon]);
 
	else
 
		SetMouseCursor(icon, pal);
 
}
 

	
0 comments (0 inline, 0 general)