void TileLoopClearHelper(TileIndex tile)
{
	bool self = (IsTileType(tile, MP_CLEAR) && IsClearGround(tile, CLEAR_FIELDS));
	bool dirty = false;

	bool neighbour = (IsTileType(TILE_ADDXY(tile, 1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 1, 0), CLEAR_FIELDS));
	if (GetFenceSW(tile) == 0) {
		if (self != neighbour) {
			SetFenceSW(tile, 3);
			dirty = true;
		}
	} else {
		if (self == 0 && neighbour == 0) {
			SetFenceSW(tile, 0);
			dirty = true;
		}
	}

	neighbour = (IsTileType(TILE_ADDXY(tile, 0, 1), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 0, 1), CLEAR_FIELDS));
	if (GetFenceSE(tile) == 0) {
		if (self != neighbour) {
			SetFenceSE(tile, 3);
			dirty = true;
		}
	} else {
		if (self == 0 && neighbour == 0) {
			SetFenceSE(tile, 0);
			dirty = true;
		}
	}

	if (dirty) MarkTileDirtyByTile(tile);
}
Example #2
0
/**
 * Test if this is safe to copy and paste contents of the map instantly, without
 * using an intermediate buffer.
 *
 * If the copy and the paste areas are close enough (especially when they intersect),
 * sequential copy-pasting may alter at some point of time those tile of the copy
 * area which hasn't been copied yet. In this case, further copy-pasting will read
 * modified values, not the original and this is somthing we don't want to happen.
 * We can deal with it by firstly copying all the content to the clipboard buffer and
 * then pasting it onto the map. This function tels us whether we should use the
 * clipboard as an intermediate buffer because there may happen such a colision.
 *
 * @param copy_paste What, where and how we are copying.
 * @return \c true if intermediate buffer might be required, \c false if it's surely not required
 *
 * @pre booth the source area and the destination area are on the main map
 *
 * @see CalcMaxPasteRange
 */
static bool CopyPasteAreasMayColide(const CopyPasteParams &copy_paste)
{
	/* No need to check surroundings if we are not terraforming. Just test for content intersection. */
	if ((copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_NONE) return copy_paste.src_area.Intersects(copy_paste.dst_area);

	/* As we are interested in tile heights, increase areas to include all tile
	 * corners, also these at SW and SE borders. */
	TileArea src_corner_area(AsMainMapTile(copy_paste.src_area.tile), copy_paste.src_area.w + 1, copy_paste.src_area.h + 1);
	TileArea dst_corner_area(AsMainMapTile(copy_paste.dst_area.tile), copy_paste.dst_area.w + 1, copy_paste.dst_area.h + 1);

	DirTransformation inv_transformation = InvertDirTransform(copy_paste.transformation);
	/* source of the destination area most northern tile corner */
	TileIndex source_of_north = dst_corner_area.TransformedNorth(src_corner_area.tile, inv_transformation);

	/* calculate current and new heights on destination area corners */
	/* N */
	TileIndex dst_corner = dst_corner_area.tile;
	TileIndex src_corner = source_of_north;
	uint curr_n = TileHeight(dst_corner);
	uint new_n = TileHeight(src_corner) + copy_paste.height_delta;
	/* W */
	dst_corner = TILE_ADDXY(dst_corner_area.tile, dst_corner_area.w, 0);
	src_corner = dst_corner_area.TransformTile(dst_corner, source_of_north, inv_transformation);
	uint curr_w = TileHeight(dst_corner);
	uint new_w = TileHeight(src_corner) + copy_paste.height_delta;
	/* S */
	dst_corner = TILE_ADDXY(dst_corner_area.tile, dst_corner_area.w, dst_corner_area.h);
	src_corner = dst_corner_area.TransformTile(dst_corner, source_of_north, inv_transformation);
	uint curr_s = TileHeight(dst_corner);
	uint new_s = TileHeight(src_corner) + copy_paste.height_delta;
	/* E */
	dst_corner = TILE_ADDXY(dst_corner_area.tile, 0, dst_corner_area.h);
	src_corner = dst_corner_area.TransformTile(dst_corner, source_of_north, inv_transformation);
	uint curr_e = TileHeight(dst_corner);
	uint new_e = TileHeight(src_corner) + copy_paste.height_delta;

	/* calculate how far tiles can be altered beyon the paste area (safe approximation) */
	uint range_ne = CalcMaxPasteRange(curr_n, new_n, curr_e, new_e, dst_corner_area.h - 1);
	uint range_sw = CalcMaxPasteRange(curr_s, new_s, curr_w, new_w, dst_corner_area.h - 1);
	uint range_nw = CalcMaxPasteRange(curr_n, new_n, curr_w, new_w, dst_corner_area.w - 1);
	uint range_se = CalcMaxPasteRange(curr_s, new_s, curr_e, new_e, dst_corner_area.w - 1);

	/* calculate the exact area which may be altered by the paste process */
	uint x = TileX(dst_corner_area.tile);
	uint y = TileY(dst_corner_area.tile);
	range_ne = max(range_ne, x); // cut the area at the NE border (don't let x to go below 0)
	range_nw = max(range_nw, y); // cut the area at the NW border (don't let y to go below 0)
	TileArea forbidden_area(
			TileXY(x - range_ne, y - range_nw),
			dst_corner_area.w + range_ne + range_sw,
			dst_corner_area.h + range_nw + range_se);

	/* test if the source area is within the paste range */
	return src_corner_area.Intersects(forbidden_area);
}
Example #3
0
static void UpdateFences(TileIndex tile)
{
	assert(IsTileType(tile, MP_CLEAR) && IsClearGround(tile, CLEAR_FIELDS));
	bool dirty = false;

	bool neighbour = (IsTileType(TILE_ADDXY(tile, 1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 1, 0), CLEAR_FIELDS));
	if (!neighbour && GetFence(tile, DIAGDIR_SW) == 0) {
		SetFence(tile, DIAGDIR_SW, 3);
		dirty = true;
	}

	neighbour = (IsTileType(TILE_ADDXY(tile, 0, 1), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 0, 1), CLEAR_FIELDS));
	if (!neighbour && GetFence(tile, DIAGDIR_SE) == 0) {
		SetFence(tile, DIAGDIR_SE, 3);
		dirty = true;
	}

	neighbour = (IsTileType(TILE_ADDXY(tile, -1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, -1, 0), CLEAR_FIELDS));
	if (!neighbour && GetFence(tile, DIAGDIR_NE) == 0) {
		SetFence(tile, DIAGDIR_NE, 3);
		dirty = true;
	}

	neighbour = (IsTileType(TILE_ADDXY(tile, 0, -1), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 0, -1), CLEAR_FIELDS));
	if (!neighbour && GetFence(tile, DIAGDIR_NW) == 0) {
		SetFence(tile, DIAGDIR_NW, 3);
		dirty = true;
	}

	if (dirty) MarkTileDirtyByTile(tile);
}
/**
 * Gets the other end of the aqueduct, if possible.
 * @param tile_from     The begin tile for the aqueduct.
 * @param [out] tile_to The tile till where to show a selection for the aqueduct.
 * @return The other end of the aqueduct, or otherwise a tile in line with the aqueduct to cause the right error message.
 */
static TileIndex GetOtherAqueductEnd(TileIndex tile_from, TileIndex *tile_to = NULL)
{
	int z;
	DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile_from, &z));

	/* If the direction isn't right, just return the next tile so the command
	 * complains about the wrong slope instead of the ends not matching up.
	 * Make sure the coordinate is always a valid tile within the map, so we
	 * don't go "off" the map. That would cause the wrong error message. */
	if (!IsValidDiagDirection(dir)) return TILE_ADDXY(tile_from, TileX(tile_from) > 2 ? -1 : 1, 0);

	/* Direction the aqueduct is built to. */
	TileIndexDiff offset = TileOffsByDiagDir(ReverseDiagDir(dir));
	/* The maximum length of the aqueduct. */
	int max_length = min(_settings_game.construction.max_bridge_length, DistanceFromEdgeDir(tile_from, ReverseDiagDir(dir)) - 1);

	TileIndex endtile = tile_from;
	for (int length = 0; IsValidTile(endtile) && TileX(endtile) != 0 && TileY(endtile) != 0; length++) {
		endtile = TILE_ADD(endtile, offset);

		if (length > max_length) break;

		if (GetTileMaxZ(endtile) > z) {
			if (tile_to != NULL) *tile_to = endtile;
			break;
		}
	}

	return endtile;
}
Example #5
0
bool StationRect::AfterRemoveRect(BaseStation *st, TileArea ta)
{
	assert(this->PtInExtendedRect(TileX(ta.tile), TileY(ta.tile)));
	assert(this->PtInExtendedRect(TileX(ta.tile) + ta.w - 1, TileY(ta.tile) + ta.h - 1));

	bool empty = this->AfterRemoveTile(st, ta.tile);
	if (ta.w != 1 || ta.h != 1) empty = empty || this->AfterRemoveTile(st, TILE_ADDXY(ta.tile, ta.w - 1, ta.h - 1));
	return empty;
}
Example #6
0
bool StationRect::AfterRemoveRect(BaseStation *st, TileIndex tile, int w, int h)
{
	assert(PtInExtendedRect(TileX(tile), TileY(tile)));
	assert(PtInExtendedRect(TileX(tile) + w - 1, TileY(tile) + h - 1));

	bool empty = this->AfterRemoveTile(st, tile);
	if (w != 1 || h != 1) empty = empty || AfterRemoveTile(st, TILE_ADDXY(tile, w - 1, h - 1));
	return empty;
}
static void DoTriggerHouse(TileIndex tile, HouseTrigger trigger, byte base_random, bool first)
{
	ResolverObject object;

	/* We can't trigger a non-existent building... */
	assert(IsTileType(tile, MP_HOUSE));

	HouseID hid = GetHouseType(tile);
	HouseSpec *hs = HouseSpec::Get(hid);

	if (hs->spritegroup == NULL) return;

	NewHouseResolver(&object, hid, tile, Town::GetByTile(tile));

	object.callback = CBID_RANDOM_TRIGGER;
	object.trigger = trigger;

	const SpriteGroup *group = SpriteGroup::Resolve(hs->spritegroup, &object);
	if (group == NULL) return;

	byte new_random_bits = Random();
	byte random_bits = GetHouseRandomBits(tile);
	random_bits &= ~object.reseed;
	random_bits |= (first ? new_random_bits : base_random) & object.reseed;
	SetHouseRandomBits(tile, random_bits);

	switch (trigger) {
		case HOUSE_TRIGGER_TILE_LOOP:
			/* Random value already set. */
			break;

		case HOUSE_TRIGGER_TILE_LOOP_TOP:
			if (!first) {
				/* The top tile is marked dirty by the usual TileLoop */
				MarkTileDirtyByTile(tile);
				break;
			}
			/* Random value of first tile already set. */
			if (hs->building_flags & BUILDING_2_TILES_Y)   DoTriggerHouse(TILE_ADDXY(tile, 0, 1), trigger, random_bits, false);
			if (hs->building_flags & BUILDING_2_TILES_X)   DoTriggerHouse(TILE_ADDXY(tile, 1, 0), trigger, random_bits, false);
			if (hs->building_flags & BUILDING_HAS_4_TILES) DoTriggerHouse(TILE_ADDXY(tile, 1, 1), trigger, random_bits, false);
			break;
	}
}
Example #8
0
CommandCost StationRect::BeforeAddRect(TileIndex tile, int w, int h, StationRectMode mode)
{
	if (mode == ADD_FORCE || (w <= _settings_game.station.station_spread && h <= _settings_game.station.station_spread)) {
		/* Important when the old rect is completely inside the new rect, resp. the old one was empty. */
		CommandCost ret = this->BeforeAddTile(tile, mode);
		if (ret.Succeeded()) ret = this->BeforeAddTile(TILE_ADDXY(tile, w - 1, h - 1), mode);
		return ret;
	}
	return CommandCost();
}
bool NewHouseTileLoop(TileIndex tile)
{
	const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));

	if (GetHouseProcessingTime(tile) > 0) {
		DecHouseProcessingTime(tile);
		return true;
	}

	TriggerHouse(tile, HOUSE_TRIGGER_TILE_LOOP);
	if (hs->building_flags & BUILDING_HAS_1_TILE) TriggerHouse(tile, HOUSE_TRIGGER_TILE_LOOP_TOP);

	if (HasBit(hs->callback_mask, CBM_HOUSE_ANIMATION_START_STOP)) {
		/* If this house is marked as having a synchronised callback, all the
		 * tiles will have the callback called at once, rather than when the
		 * tile loop reaches them. This should only be enabled for the northern
		 * tile, or strange things will happen (here, and in TTDPatch). */
		if (hs->extra_flags & SYNCHRONISED_CALLBACK_1B) {
			uint16 random = GB(Random(), 0, 16);

			if (hs->building_flags & BUILDING_HAS_1_TILE)  AnimationControl(tile, random);
			if (hs->building_flags & BUILDING_2_TILES_Y)   AnimationControl(TILE_ADDXY(tile, 0, 1), random);
			if (hs->building_flags & BUILDING_2_TILES_X)   AnimationControl(TILE_ADDXY(tile, 1, 0), random);
			if (hs->building_flags & BUILDING_HAS_4_TILES) AnimationControl(TILE_ADDXY(tile, 1, 1), random);
		} else {
			AnimationControl(tile, 0);
		}
	}

	/* Check callback 21, which determines if a house should be destroyed. */
	if (HasBit(hs->callback_mask, CBM_HOUSE_DESTRUCTION)) {
		uint16 callback_res = GetHouseCallback(CBID_HOUSE_DESTRUCTION, 0, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
		if (callback_res != CALLBACK_FAILED && Convert8bitBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_DESTRUCTION, callback_res)) {
			ClearTownHouse(Town::GetByTile(tile), tile);
			return false;
		}
	}

	SetHouseProcessingTime(tile, hs->processing_time);
	MarkTileDirtyByTile(tile);
	return true;
}
/**
 * Run watched cargo accepted callback for a house.
 * @param tile House tile.
 * @param trigger_cargoes Triggering cargo types.
 * @pre IsTileType(t, MP_HOUSE)
 */
void WatchedCargoCallback(TileIndex tile, uint32 trigger_cargoes)
{
	assert(IsTileType(tile, MP_HOUSE));
	HouseID id = GetHouseType(tile);
	const HouseSpec *hs = HouseSpec::Get(id);

	trigger_cargoes &= hs->watched_cargoes;
	/* None of the trigger cargoes is watched? */
	if (trigger_cargoes == 0) return;

	/* Same random value for all tiles of a multi-tile house. */
	uint16 r = Random();

	/* Do the callback, start at northern tile. */
	TileIndex north = tile + GetHouseNorthPart(id);
	hs = HouseSpec::Get(id);

	DoWatchedCargoCallback(north, tile, trigger_cargoes, r);
	if (hs->building_flags & BUILDING_2_TILES_Y)   DoWatchedCargoCallback(TILE_ADDXY(north, 0, 1), tile, trigger_cargoes, r);
	if (hs->building_flags & BUILDING_2_TILES_X)   DoWatchedCargoCallback(TILE_ADDXY(north, 1, 0), tile, trigger_cargoes, r);
	if (hs->building_flags & BUILDING_HAS_4_TILES) DoWatchedCargoCallback(TILE_ADDXY(north, 1, 1), tile, trigger_cargoes, r);
}
Example #11
0
/**
 * Callback for generating a heightmap. Supports 8bpp grayscale only.
 * @param userdata Pointer to user data.
 * @param buf      Destination buffer.
 * @param y        Line number of the first line to write.
 * @param pitch    Number of pixels to write (1 byte for 8bpp, 4 bytes for 32bpp). @see Colour
 * @param n        Number of lines to write.
 * @see ScreenshotCallback
 */
static void HeightmapCallback(void *userdata, void *buffer, uint y, uint pitch, uint n)
{
	byte *buf = (byte *)buffer;
	while (n > 0) {
		TileIndex ti = TileXY(MapMaxX(), y);
		for (uint x = MapMaxX(); true; x--) {
			*buf = 16 * TileHeight(ti);
			buf++;
			if (x == 0) break;
			ti = TILE_ADDXY(ti, -1, 0);
		}
		y++;
		n--;
	}
}
Example #12
0
/* virtual */ uint32 CanalScopeResolver::GetVariable(byte variable, uint32 parameter, bool *available) const
{
	switch (variable) {
		/* Height of tile */
		case 0x80: {
			int z = GetTileZ(this->tile);
			/* Return consistent height within locks */
			if (IsTileType(this->tile, MP_WATER) && IsLock(this->tile) && GetLockPart(this->tile) == LOCK_PART_UPPER) z--;
			return z;
		}

		/* Terrain type */
		case 0x81: return GetTerrainType(this->tile);

		/* Dike map: Connectivity info for river and canal tiles
		 *
		 * Assignment of bits to directions defined in agreement with
		 * http://projects.tt-forums.net/projects/ttdpatch/repository/revisions/2367/entry/trunk/patches/water.asm#L879
		 *         7
		 *      3     0
		 *   6     *     4
		 *      2     1
		 *         5
		 */
		case 0x82: {
			uint32 connectivity =
				  (!IsWateredTile(TILE_ADDXY(tile, -1,  0), DIR_SW) << 0)  // NE
				+ (!IsWateredTile(TILE_ADDXY(tile,  0,  1), DIR_NW) << 1)  // SE
				+ (!IsWateredTile(TILE_ADDXY(tile,  1,  0), DIR_NE) << 2)  // SW
				+ (!IsWateredTile(TILE_ADDXY(tile,  0, -1), DIR_SE) << 3)  // NW
				+ (!IsWateredTile(TILE_ADDXY(tile, -1,  1), DIR_W)  << 4)  // E
				+ (!IsWateredTile(TILE_ADDXY(tile,  1,  1), DIR_N)  << 5)  // S
				+ (!IsWateredTile(TILE_ADDXY(tile,  1, -1), DIR_E)  << 6)  // W
				+ (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)  << 7); // N
			return connectivity;
		}

		/* Random data for river or canal tiles, otherwise zero */
		case 0x83: return IsTileType(this->tile, MP_WATER) ? GetWaterTileRandomBits(this->tile) : 0;
	}

	DEBUG(grf, 1, "Unhandled canal variable 0x%02X", variable);

	*available = false;
	return UINT_MAX;
}
Example #13
0
bool StationRect::BeforeAddRect(TileIndex tile, int w, int h, StationRectMode mode)
{
	return (mode == ADD_FORCE || (w <= _settings_game.station.station_spread && h <= _settings_game.station.station_spread)) && // important when the old rect is completely inside the new rect, resp. the old one was empty
			this->BeforeAddTile(tile, mode) && this->BeforeAddTile(TILE_ADDXY(tile, w - 1, h - 1), mode);
}