MCPcopy Create free account
hub / github.com/OpenTTD/OpenTTD / CmdBuildObject

Function CmdBuildObject

src/object_cmd.cpp:210–385  ·  view source on GitHub ↗

* 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 */

Source from the content-addressed store, hash-verified

208 * @return the cost of this operation or an error
209 */
210CommandCost 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 &&

Callers 1

GenerateObjectsFunction · 0.85

Calls 15

CommandCostClass · 0.85
GBFunction · 0.85
HasBitFunction · 0.85
IsValidTileFunction · 0.85
HasTileWaterGroundFunction · 0.85
IsWaterTileFunction · 0.85
GetTileOwnerFunction · 0.85
CheckOwnershipFunction · 0.85
IsInsideMMFunction · 0.85
TileXFunction · 0.85
TileYFunction · 0.85
EnsureNoVehicleOnGroundFunction · 0.85

Tested by

no test coverage detected