* Build an object object * @param flags type of operation * @param tile tile where the object will be located * @param type the object type to build * @param view the view for the object * @return the cost of this operation or an error */
| 208 | * @return the cost of this operation or an error |
| 209 | */ |
| 210 | CommandCost CmdBuildObject(DoCommandFlags flags, TileIndex tile, ObjectType type, uint8_t view) |
| 211 | { |
| 212 | CommandCost cost(EXPENSES_CONSTRUCTION); |
| 213 | |
| 214 | if (type >= ObjectSpec::Count()) return CMD_ERROR; |
| 215 | const ObjectSpec *spec = ObjectSpec::Get(type); |
| 216 | if (_game_mode == GM_NORMAL && !spec->IsAvailable() && !_generating_world) return CMD_ERROR; |
| 217 | if ((_game_mode == GM_EDITOR || _generating_world) && !spec->WasEverAvailable()) return CMD_ERROR; |
| 218 | |
| 219 | if (spec->flags.Test(ObjectFlag::OnlyInScenedit) && ((!_generating_world && _game_mode != GM_EDITOR) || _current_company != OWNER_NONE)) return CMD_ERROR; |
| 220 | if (spec->flags.Test(ObjectFlag::OnlyInGame) && (_generating_world || _game_mode != GM_NORMAL || _current_company > MAX_COMPANIES)) return CMD_ERROR; |
| 221 | if (view >= spec->views) return CMD_ERROR; |
| 222 | |
| 223 | if (!Object::CanAllocateItem()) return CommandCost(STR_ERROR_TOO_MANY_OBJECTS); |
| 224 | if (Town::GetNumItems() == 0) return CommandCost(STR_ERROR_MUST_FOUND_TOWN_FIRST); |
| 225 | |
| 226 | int size_x = GB(spec->size, HasBit(view, 0) ? 4 : 0, 4); |
| 227 | int size_y = GB(spec->size, HasBit(view, 0) ? 0 : 4, 4); |
| 228 | TileArea ta(tile, size_x, size_y); |
| 229 | for (TileIndex t : ta) { |
| 230 | if (!IsValidTile(t)) return CommandCost(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP_SUB); // Might be off the map |
| 231 | } |
| 232 | |
| 233 | if (type == OBJECT_OWNED_LAND) { |
| 234 | /* Owned land is special as it can be placed on any slope. */ |
| 235 | cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile)); |
| 236 | } else { |
| 237 | /* Check the surface to build on. At this time we can't actually execute the |
| 238 | * the CLEAR_TILE commands since the newgrf callback later on can check |
| 239 | * some information about the tiles. */ |
| 240 | bool allow_water = spec->flags.Any({ObjectFlag::BuiltOnWater, ObjectFlag::NotOnLand}); |
| 241 | bool allow_ground = !spec->flags.Test(ObjectFlag::NotOnLand); |
| 242 | for (TileIndex t : ta) { |
| 243 | if (HasTileWaterGround(t)) { |
| 244 | if (!allow_water) return CommandCost(STR_ERROR_CAN_T_BUILD_ON_WATER); |
| 245 | if (!IsWaterTile(t)) { |
| 246 | /* Normal water tiles don't have to be cleared. For all other tile types clear |
| 247 | * the tile but leave the water. */ |
| 248 | cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(DoCommandFlags{flags}.Reset({DoCommandFlag::NoWater, DoCommandFlag::Execute}), t)); |
| 249 | } else { |
| 250 | /* Can't build on water owned by another company. */ |
| 251 | Owner o = GetTileOwner(t); |
| 252 | if (o != OWNER_NONE && o != OWNER_WATER) cost.AddCost(CheckOwnership(o, t)); |
| 253 | |
| 254 | /* If freeform edges are disabled, don't allow building on edge tiles. */ |
| 255 | if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(t), 1, Map::MaxX() - 1) || !IsInsideMM(TileY(t), 1, Map::MaxY() - 1))) { |
| 256 | return CommandCost(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP); |
| 257 | } |
| 258 | |
| 259 | /* However, the tile has to be clear of vehicles. */ |
| 260 | cost.AddCost(EnsureNoVehicleOnGround(t)); |
| 261 | } |
| 262 | } else { |
| 263 | if (!allow_ground) return CommandCost(STR_ERROR_MUST_BE_BUILT_ON_WATER); |
| 264 | /* For non-water tiles, we'll have to clear it before building. */ |
| 265 | |
| 266 | /* When relocating HQ, allow it to be relocated (partial) on itself. */ |
| 267 | if (!(type == OBJECT_HQ && |
no test coverage detected