/** * Function performing a search around a center tile and going outward, thus in circle. * Although it really is a square search... * Every tile will be tested by means of the callback function proc, * which will determine if yes or no the given tile meets criteria of search. * @param tile to start the search from. Upon completion, it will return the tile matching the search * @param size: number of tiles per side of the desired search area * @param proc: callback testing function pointer. * @param user_data to be passed to the callback function. Depends on the implementation * @return result of the search * @pre proc != NULL * @pre size > 0 */ bool CircularTileSearch(TileIndex *tile, uint size, TestTileOnSearchProc proc, void *user_data) { assert(proc != NULL); assert(size > 0); if (size % 2 == 1) { /* If the length of the side is uneven, the center has to be checked * separately, as the pattern of uneven sides requires to go around the center */ if (proc(*tile, user_data)) return true; /* If tile test is not successful, get one tile up, * ready for a test in first circle around center tile */ *tile = TILE_ADD(*tile, TileOffsByDir(DIR_N)); return CircularTileSearch(tile, size / 2, 1, 1, proc, user_data); } else { return CircularTileSearch(tile, size / 2, 0, 0, proc, user_data); } }
/** * Recomputes Station::industries_near, list of industries possibly * accepting cargo in station's catchment radius */ void Station::RecomputeIndustriesNear() { this->industries_near.Clear(); if (this->rect.IsEmpty()) return; RectAndIndustryVector riv = { this->GetCatchmentRect(), &this->industries_near }; /* Compute maximum extent of acceptance rectangle wrt. station sign */ TileIndex start_tile = this->xy; uint max_radius = max( max(DistanceManhattan(start_tile, TileXY(riv.rect.left, riv.rect.top)), DistanceManhattan(start_tile, TileXY(riv.rect.left, riv.rect.bottom))), max(DistanceManhattan(start_tile, TileXY(riv.rect.right, riv.rect.top)), DistanceManhattan(start_tile, TileXY(riv.rect.right, riv.rect.bottom))) ); CircularTileSearch(&start_tile, 2 * max_radius + 1, &FindIndustryToDeliver, &riv); }
/** * This function will activate a search around a central tile, looking for some houses * that fit the requested characteristics * @param parameter that is given by the callback. * bits 0..6 radius of the search * bits 7..8 search type i.e.: 0 = houseID/ 1 = classID/ 2 = grfID * @param tile TileIndex from which to start the search * @param house the HouseID that is associated to the house, the callback is called for * @return the Manhattan distance from the center tile, if any, and 0 if failure */ static uint32 GetDistanceFromNearbyHouse(uint8 parameter, TileIndex tile, HouseID house) { static TestTileOnSearchProc * const search_procs[3] = { SearchNearbyHouseID, SearchNearbyHouseClass, SearchNearbyHouseGRFID, }; TileIndex found_tile = tile; uint8 searchtype = GB(parameter, 6, 2); uint8 searchradius = GB(parameter, 0, 6); if (searchtype >= lengthof(search_procs)) return 0; // do not run on ill-defined code if (searchradius < 1) return 0; // do not use a too low radius SearchNearbyHouseData nbhd; nbhd.hs = HouseSpec::Get(house); nbhd.north_tile = tile + GetHouseNorthPart(house); // modifies 'house'! /* Use a pointer for the tile to start the search. Will be required for calculating the distance*/ if (CircularTileSearch(&found_tile, 2 * searchradius + 1, search_procs[searchtype], &nbhd)) { return DistanceManhattan(found_tile, tile); } return 0; }
/** * Build a piece of canal. * @param tile end tile of stretch-dragging * @param flags type of operation * @param p1 start tile of stretch-dragging * @param p2 waterclass to build. sea and river can only be built in scenario editor * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { WaterClass wc = Extract<WaterClass, 0, 2>(p2); if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR; /* Outside of the editor you can only build canals, not oceans */ if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR; TileArea ta(tile, p1); /* Outside the editor you can only drag canals, and not areas */ if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR; CommandCost cost(EXPENSES_CONSTRUCTION); TILE_AREA_LOOP(tile, ta) { CommandCost ret; Slope slope = GetTileSlope(tile); if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) { return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); } /* can't make water of water! */ if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue; bool water = IsWaterTile(tile); ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; if (!water) cost.AddCost(ret); if (flags & DC_EXEC) { switch (wc) { case WATER_CLASS_RIVER: MakeRiver(tile, Random()); if (_game_mode == GM_EDITOR) { TileIndex tile2 = tile; CircularTileSearch(&tile2, 5, RiverModifyDesertZone, NULL); } break; case WATER_CLASS_SEA: if (TileHeight(tile) == 0) { MakeSea(tile); break; } FALLTHROUGH; default: MakeCanal(tile, _current_company, Random()); if (Company::IsValidID(_current_company)) { Company::Get(_current_company)->infrastructure.water++; DirtyCompanyInfrastructureWindows(_current_company); } break; } MarkTileDirtyByTile(tile); MarkCanalsAndRiversAroundDirty(tile); } cost.AddCost(_price[PR_BUILD_CANAL]); }