Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // (c) authors of openttd, gpl, etc etc
- //
- // this code is the bare minimum implementation for a flood-fill,
- // not including the additional enums and specification for adding the trigger button
- // or other configuration to the UI
- #define USE_FLOODFILL 1
- #if USE_FLOODFILL
- // BEGIN FLOODFILL AMENDMENT
- #include <vector>
- struct point_t
- {
- 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.resize(16);
- stack_pointer = 0;
- max_stack = 0;
- }
- bool push(T t)
- {
- if (stack_pointer == stack.size() - 1) {
- stack.reserve(stack.size() + 16);
- 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;
- }
- // index to last entry + 1
- int stack_pointer;
- // record of largest used stack size
- int max_stack;
- std::vector<T> stack;
- };
- // apply change to selected tile
- using ffcb_apply_t = void(*)(TileIndex ti, const void *config_ptr, void *state_ptr);
- // return selected tile == target
- using ffcb_match_t = bool(*)(TileIndex ti, const void *target_ptr, const void *config_ptr, const void *state_ptr);
- void floodfill(TileIndex t, ffcb_match_t match, ffcb_apply_t apply,
- const void *config, const void *target, void *state)
- {
- using BT = std::uintmax_t;
- using T = point_t;
- floodfill_stack_t<T> stack;
- 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) {
- const TileIndex cursor = TileXY(p.x, y1);
- const bool mark = match(cursor, target, config, state);
- if (!mark) {
- break;
- }
- y1--;
- }
- y1++;
- bool span_left = false;
- bool span_right = false;
- while (y1 < y_max) {
- const TileIndex cursor = TileXY(p.x, y1);
- const bool mark = match(cursor, target, config, state);
- if (!mark) {
- break;
- }
- // target match, write the new data
- apply(cursor, config, state);
- // ensure the modified data no longer matches the target!
- //assert(match(cursor, target, config, state) == false);
- if (match(cursor, target, config, state)) {
- __debugbreak();
- // (escape infinite loop)
- return;
- }
- if (!span_left && p.x > x_min && match(TileXY(p.x - 1, y1), target, config, state)) {
- if (!stack.push(point_t(p.x - 1, y1))) {
- return;
- }
- span_left = true;
- } else if (span_left && p.x > x_min && !match(TileXY(p.x - 1, y1), target, config, state)) {
- span_left = false;
- }
- if (!span_right && p.x < x_max && match(TileXY(p.x + 1, y1), target, config, state)) {
- if (!stack.push(point_t(p.x + 1, y1))) {
- return;
- }
- span_right = true;
- } else if (span_right && p.x < x_max && !match(TileXY(p.x + 1, y1), target, config, state)) {
- span_right = false;
- }
- y1++;
- }
- }
- }
- struct ffcb_apply_river_config_t
- {
- int height;
- const void *target;
- };
- void ffcb_apply_river(TileIndex tile, const void *config_ptr, void *state_ptr)
- {
- DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
- MakeRiver(tile, Random());
- TileIndex tile2 = tile;
- CircularTileSearch(&tile2, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr);
- MarkTileDirtyByTile(tile);
- MarkCanalsAndRiversAroundDirty(tile);
- CheckForDockingTile(tile);
- }
- struct ffcb_match_flat_height_target_t
- {
- int height;
- };
- bool ffcb_match_flat_height(TileIndex tile, const void *target_ptr, const void *config_ptr, const void *state_ptr)
- {
- if (GetTileSlope(tile) != SLOPE_FLAT) {
- return false;
- }
- assert(target_ptr != nullptr);
- const ffcb_match_flat_height_target_t &target =
- *(ffcb_match_flat_height_target_t *)target_ptr;
- return target.height == TileHeight(tile) && !IsWaterTile(tile);
- }
- /**
- * Floodfill from selected tile
- * @param tile target tile
- * @param flags type of operation
- * @param p1 unused
- * @param p2 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);
- /* Only the editor can execute a floodfill */
- if (_game_mode != GM_EDITOR) return CMD_ERROR;
- //CommandCost cost(EXPENSES_CONSTRUCTION);
- //CommandCost ret;
- if (GetTileSlope(tile) != SLOPE_FLAT) {
- return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
- }
- /* can't make water of water! */
- bool is_water = IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA);
- bool is_water_water = IsWaterTile(tile);
- if (flags & DC_EXEC) {
- ffcb_match_flat_height_target_t target;
- target.height = TileHeight(tile);
- ffcb_apply_river_config_t config;
- config.height = TileHeight(tile);// + fill height;
- config.target = ⌖
- floodfill(tile, &ffcb_match_flat_height, &ffcb_apply_river, &config, &target, nullptr);
- }
- //cost.AddCost(_price[PR_BUILD_CANAL]);
- CommandCost cost;
- cost.AddCost(0);
- //return_cmd_error(STR_ERROR_ALREADY_BUILT);
- return cost;
- }
- // END FLOODFILL AMENDMENT
- #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement