Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #define USE_FLOODFILL 1
- #if USE_FLOODFILL
- // BEGIN FLOODFILL AMMENDMENT
- #include <vector>
- struct point_t
- {
- point_t(int _x, int _y) : x(_x), y(_y)
- {
- }
- void operator=(point_t &p)
- {
- x = p.x;
- y = p.y;
- }
- int x;
- int y;
- };
- template <typename T>
- struct floodfill_stack_t
- {
- floodfill_stack_t()
- {
- stack.reserve(16);
- stack_pointer = 0;
- max_stack = 0;
- }
- bool push(T t)
- {
- if (stack_pointer == stack.size() - 1) {
- stack.resize(stack.size() + 16);
- }
- stack[stack_pointer] = t;
- stack_pointer++;
- if (stack_pointer > max_stack) {
- max_stack = stack_pointer;
- }
- return 1;
- }
- bool pop(T &t)
- {
- if (stack_pointer > 0) {
- t = stack[stack_pointer - 1];
- stack_pointer--;
- return 1;
- } else {
- return 0;
- }
- }
- void clear()
- {
- stack_pointer = 0;
- }
- int stack_pointer;
- int max_stack;
- std::vector<T> stack;
- };
- // type T must store >= (last - first) bits
- template <typename T>
- T getbits(const void *p, const int first, const int last)
- {
- using U8T = std::uint8_t;
- int bits = last - first + 1;
- int offset = first;
- T dat = 0;
- U8T *src = (U8T *)p;
- src += (offset/8);
- offset -= (offset/8)*8;
- if (offset > 0) {
- dat |= *src >> offset;
- if (bits < 8) {
- dat &= ((1 << bits) - 1);
- }
- src++;
- offset = (8 - offset);
- bits -= offset;
- }
- while (bits >= 8) {
- dat |= *src << offset;
- src++;
- offset += 8;
- bits -= 8;
- }
- if (bits > 0) {
- dat |= (*src << offset) & (((1 << bits) - 1) << offset);
- }
- return dat;
- }
- // type T must store >= (last - first) bits
- template <typename T>
- void setbits(void *p, const int first, const int last, T dat)
- {
- using U8T = std::uint8_t;
- int bits = last - first + 1;
- int offset = first;
- U8T *dest = (U8T *)p;
- dest += (offset/8);
- offset -= (offset/8)*8;
- if (offset) {
- U8T mask = 255 >> (8 - offset);
- if (bits < 8 - offset) {
- U8T amask = ~(255 >> (8 - bits - offset));
- mask |= amask;
- }
- *dest &= mask;
- *dest |= (dat << offset) & (U8T)~mask;
- dest++;
- bits -= (8 - offset);
- dat >>= (8 - offset);
- }
- while (bits >= 8) {
- *dest = dat & 255;
- dest++;
- bits -= 8;
- dat >>= 8;
- }
- if (bits > 0) {
- U8T mask = ((1 << bits) - 1);
- *dest &= ~mask;
- *dest |= dat & mask;
- }
- }
- std::uintmax_t read_tile_xy_bits(int x, int y, const int first, const int last, const std::uintmax_t mask)
- {
- const TileIndex ti = TileXY(x, y);
- return getbits<std::uintmax_t>(&_m[ti], first, last) & mask;
- }
- void write_tile_xy_bits(int x, int y, const int first, const int last, const std::uintmax_t data, const std::uintmax_t mask)
- {
- const TileIndex ti = TileXY(x, y);
- std::uintmax_t existing_data = read_tile_xy_bits(x, y, first, last, ~mask);
- setbits<std::uintmax_t>(&_m[ti], first, last, existing_data | (data & mask));
- }
- void floodfill(TileIndex t, const int8 *new_data_ptr, const int8 *target_data_ptr, const int first_bit, const int last_bit, std::uintmax_t read_mask, std::uintmax_t write_mask)
- {
- using BT = std::uintmax_t;
- using T = point_t;
- floodfill_stack_t<T> stack;
- BT target_data = getbits<BT>(target_data_ptr, first_bit, last_bit) & read_mask;
- BT new_data_read_mask = getbits<BT>(new_data_ptr, first_bit, last_bit) & read_mask;
- // ensure the new data does not match the targeted data (escape from ouroboros)
- assert(target_data != new_data_read_mask);
- if (target_data == new_data_read_mask) {
- return;
- }
- BT new_data = getbits<BT>(new_data_ptr, first_bit, last_bit) & write_mask;
- point_t p(TileX(t), TileY(t));
- const int width = MapSizeX();
- const int height = MapSizeY();
- const int x_min = 1;
- const int x_max = width - 1;
- const int y_min = 1;
- const int y_max = height - 1;
- stack.clear();
- if (!stack.push(p)) {
- return;
- }
- while (stack.pop(p)) {
- int y1 = p.y;
- while (y1 >= y_min) {
- BT c = read_tile_xy_bits(p.x, y1, first_bit, last_bit, read_mask);
- if (c != target_data) {
- break;
- }
- y1--;
- }
- y1++;
- bool span_left = false;
- bool span_right = false;
- while (y1 < y_max) {
- BT c = read_tile_xy_bits(p.x, y1, first_bit, last_bit, read_mask);
- if (c != target_data) {
- break;
- }
- // target match, write the new data
- write_tile_xy_bits(p.x, y1, first_bit, last_bit, new_data, write_mask);
- if (!span_left && p.x > x_min && read_tile_xy_bits(p.x - 1, y1, first_bit, last_bit, read_mask) == target_data) {
- if (!stack.push(point_t(p.x - 1, y1))) {
- return;
- }
- span_left = true;
- } else if (span_left && p.x > x_min && read_tile_xy_bits(p.x - 1, y1, first_bit, last_bit, read_mask) != target_data) {
- span_left = false;
- }
- if (!span_right && p.x < x_max && read_tile_xy_bits(p.x + 1, y1, first_bit, last_bit, read_mask) == target_data) {
- if (!stack.push(point_t(p.x + 1, y1))) {
- return;
- }
- span_right = true;
- } else if (span_right && p.x < x_max && read_tile_xy_bits(p.x + 1, y1, first_bit, last_bit, read_mask) != target_data) {
- span_right = false;
- }
- y1++;
- }
- }
- }
- /**
- * Floodfill from selected tile
- * @param tile end tile of stretch-dragging
- * @param flags type of operation
- * @param p1 start tile of stretch-dragging
- * @param p2 {1, 2} waterclass. {3 to 8} unused. {9 to 16} height to add during fill. {17 to 32} unused.
- * @param text unused
- * @return the cost of this operation or an error
- */
- CommandCost CmdFloodFill(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, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr);
- }
- 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);
- CheckForDockingTile(tile);
- }
- cost.AddCost(_price[PR_BUILD_CANAL]);
- }
- if (cost.GetCost() == 0) {
- return_cmd_error(STR_ERROR_ALREADY_BUILT);
- } else {
- return cost;
- }
- }
- // END FLOODFILL AMMENDMENT
- #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement