/** * Get the EngineID of the replacement for a vehicle * @param v The vehicle to find a replacement for * @param c The vehicle's owner (it's faster to forward the pointer than refinding it) * @param [out] e the EngineID of the replacement. INVALID_ENGINE if no replacement is found * @return Error if the engine to build is not available */ static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, EngineID &e) { assert(v->type != VEH_TRAIN || !v->IsArticulatedPart()); e = INVALID_ENGINE; if (v->type == VEH_TRAIN && Train::From(v)->IsRearDualheaded()) { /* we build the rear ends of multiheaded trains with the front ones */ return CommandCost(); } e = EngineReplacementForCompany(c, v->engine_type, v->group_id); /* Autoreplace, if engine is available */ if (e != INVALID_ENGINE && IsEngineBuildable(e, v->type, _current_company)) { return CommandCost(); } /* Autorenew if needed */ if (v->NeedsAutorenewing(c)) e = v->engine_type; /* Nothing to do or all is fine? */ if (e == INVALID_ENGINE || IsEngineBuildable(e, v->type, _current_company)) return CommandCost(); /* The engine we need is not available. Report error to user */ return CommandCost(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + v->type); }
/** * Finish a paste process. * @return Total cost. */ static CommandCost FinalizePasting() { /* Set error string parameters */ CopyInDParam(0, _current_pasting->err_params, lengthof(_current_pasting->err_params)); /* Set error summary message (see COPY_PASTE_ERR_SUMMARY_PARAM for details). */ SetDParam(COPY_PASTE_ERR_SUMMARY_PARAM, _current_pasting->err_summary); /* Store the error tile so the GUI (CcPaste) can highlight it. */ _paste_err_tile = _current_pasting->err_tile; CommandCost ret; if (_current_pasting->had_success) { /* Return overal cost of the operation */ ret = CommandCost(EXPENSES_CONSTRUCTION, _current_pasting->overal_cost); /* Here we are about to return a success. However, there could occured some meaningful * errors (those except "already built", "already leveled" etc.) and we should inform * the user that not everything went right. Show the message now. */ if ((_current_pasting->dc_flags & DC_EXEC) && IsLocalCompany() && GetPasteErrorImportance(_current_pasting->err_message) > GetPasteErrorImportance(STR_ERROR_NOTHING_TO_DO)) { ShowErrorMessage(_current_pasting->err_summary, _current_pasting->err_message, WL_INFO); } else { /* If we are not showing error message then clear the error tile to prevent GUI * (CcPaste) from higlighting it. */ _paste_err_tile = INVALID_TILE; } } else { /* Return an error if we didn't have any success. */ ret = CommandCost(_current_pasting->err_message); } /* cleanup */ delete _current_pasting; _current_pasting = NULL; return ret; }
/** * Add an engine replacement to the given renewlist. * @param erl The renewlist to add to. * @param old_engine The original engine type. * @param new_engine The replacement engine type. * @param group The group related to this replacement. * @param replace_when_old Replace when old or always? * @param flags The calling command flags. * @return 0 on success, CMD_ERROR on failure. */ CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlag flags) { /* Check if the old vehicle is already in the list */ EngineRenew *er = GetEngineReplacement(*erl, old_engine, group); if (er != NULL) { if (flags & DC_EXEC) { er->to = new_engine; er->replace_when_old = replace_when_old; } return CommandCost(); } if (!EngineRenew::CanAllocateItem()) return CMD_ERROR; if (flags & DC_EXEC) { er = new EngineRenew(old_engine, new_engine); er->group_id = group; er->replace_when_old = replace_when_old; /* Insert before the first element */ er->next = (EngineRenew *)(*erl); *erl = (EngineRenewList)er; } return CommandCost(); }
/** * Create a new custom news item. * @param tile unused * @param flags type of operation * @param p1 various bitstuffed elements * - p1 = (bit 0 - 7) - NewsType of the message. * - p1 = (bit 8 - 15) - NewsReferenceType of first reference. * - p1 = (bit 16 - 23) - Company this news message is for. * @param p2 First reference of the news message. * @param text The text of the news message. * @return the cost of this operation or an error */ CommandCost CmdCustomNewsItem(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (_current_company != OWNER_DEITY) return CMD_ERROR; NewsType type = (NewsType)GB(p1, 0, 8); NewsReferenceType reftype1 = (NewsReferenceType)GB(p1, 8, 8); CompanyID company = (CompanyID)GB(p1, 16, 8); if (company != INVALID_OWNER && !Company::IsValidID(company)) return CMD_ERROR; if (type >= NT_END) return CMD_ERROR; if (StrEmpty(text)) return CMD_ERROR; switch (reftype1) { case NR_NONE: break; case NR_TILE: if (!IsValidTile(p2)) return CMD_ERROR; break; case NR_VEHICLE: if (!Vehicle::IsValidID(p2)) return CMD_ERROR; break; case NR_STATION: if (!Station::IsValidID(p2)) return CMD_ERROR; break; case NR_INDUSTRY: if (!Industry::IsValidID(p2)) return CMD_ERROR; break; case NR_TOWN: if (!Town::IsValidID(p2)) return CMD_ERROR; break; case NR_ENGINE: if (!Engine::IsValidID(p2)) return CMD_ERROR; break; default: return CMD_ERROR; } if (company != INVALID_OWNER && company != _local_company) return CommandCost(); if (flags & DC_EXEC) { char *news = stredup(text); SetDParamStr(0, news); AddNewsItem(STR_NEWS_CUSTOM_ITEM, type, NF_NORMAL, reftype1, p2, NR_NONE, UINT32_MAX, news); } return CommandCost(); }
static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags) { if (!IsShipDepot(tile)) return CMD_ERROR; CommandCost ret = CheckTileOwnership(tile); if (ret.Failed()) return ret; TileIndex tile2 = GetOtherShipDepotTile(tile); /* do not check for ship on tile when company goes bankrupt */ if (!(flags & DC_BANKRUPT)) { CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2); if (ret.Failed()) return ret; } if (flags & DC_EXEC) { delete Depot::GetByTile(tile); Company *c = Company::GetIfValid(GetTileOwner(tile)); if (c != NULL) { c->infrastructure.water -= 2 * LOCK_DEPOT_TILE_FACTOR; DirtyCompanyInfrastructureWindows(c->index); } MakeWaterKeepingClass(tile, GetTileOwner(tile)); MakeWaterKeepingClass(tile2, GetTileOwner(tile2)); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]); }
/** Learn the price of refitting a certain engine * @param engine_type Which engine to refit * @return Price for refitting */ static CommandCost GetRefitCost(EngineID engine_type) { ExpensesType expense_type; const Engine *e = Engine::Get(engine_type); Price base_price; uint cost_factor = e->info.refit_cost; switch (e->type) { case VEH_SHIP: base_price = PR_BUILD_VEHICLE_SHIP; expense_type = EXPENSES_SHIP_RUN; break; case VEH_ROAD: base_price = PR_BUILD_VEHICLE_ROAD; expense_type = EXPENSES_ROADVEH_RUN; break; case VEH_AIRCRAFT: base_price = PR_BUILD_VEHICLE_AIRCRAFT; expense_type = EXPENSES_AIRCRAFT_RUN; break; case VEH_TRAIN: base_price = (e->u.rail.railveh_type == RAILVEH_WAGON) ? PR_BUILD_VEHICLE_WAGON : PR_BUILD_VEHICLE_TRAIN; cost_factor <<= 1; expense_type = EXPENSES_TRAIN_RUN; break; default: NOT_REACHED(); } return CommandCost(expense_type, GetPrice(base_price, cost_factor, e->grffile, -10)); }
/** * Rename a depot. * @param tile unused * @param flags type of operation * @param p1 id of depot * @param p2 unused * @param text the new name or an empty string when resetting to the default * @return the cost of this operation or an error */ CommandCost CmdRenameDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Depot *d = Depot::GetIfValid(p1); if (d == NULL) return CMD_ERROR; CommandCost ret = CheckTileOwnership(d->xy); if (ret.Failed()) return ret; bool reset = StrEmpty(text); if (!reset) { if (Utf8StringLength(text) >= MAX_LENGTH_DEPOT_NAME_CHARS) return CMD_ERROR; if (!IsUniqueDepotName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE); } if (flags & DC_EXEC) { free(d->name); if (reset) { d->name = NULL; MakeDefaultName(d); } else { d->name = strdup(text); } /* Update the orders and depot */ SetWindowClassesDirty(WC_VEHICLE_ORDERS); SetWindowDirty(WC_VEHICLE_DEPOT, d->xy); /* Update the depot list */ VehicleType vt = GetDepotVehicleType(d->xy); SetWindowDirty(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_DEPOT_LIST, vt, GetTileOwner(d->xy), d->index).Pack()); } return CommandCost(); }
/** * Remove a lock. * @param tile Central tile of the lock. * @param flags Operation to perform. * @return The cost in case of success, or an error code if it failed. */ static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags) { if (GetTileOwner(tile) != OWNER_NONE) { CommandCost ret = CheckTileOwnership(tile); if (ret.Failed()) return ret; } TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile)); /* make sure no vehicle is on the tile. */ CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta); if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta); if (ret.Failed()) return ret; if (flags & DC_EXEC) { DoClearSquare(tile); MakeWaterKeepingClass(tile + delta, GetTileOwner(tile + delta)); MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta)); MarkCanalsAndRiversAroundDirty(tile - delta); MarkCanalsAndRiversAroundDirty(tile + delta); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_LOCK]); }
static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags) { if (!IsShipDepot(tile)) return CMD_ERROR; CommandCost ret = CheckTileOwnership(tile); if (ret.Failed()) return ret; TileIndex tile2 = GetOtherShipDepotTile(tile); /* do not check for ship on tile when company goes bankrupt */ if (!(flags & DC_BANKRUPT)) { CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2); if (ret.Failed()) return ret; } if (flags & DC_EXEC) { delete Depot::GetByTile(tile); MakeWaterKeepingClass(tile, GetTileOwner(tile)); MakeWaterKeepingClass(tile2, GetTileOwner(tile2)); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]); }
/** * Terraform tiles as a part of a pasting process. * @param iter iterator to use when terraforming */ static void TerraformPasteTiles(TerraformingIterator *iter) { TileIndex start_tile = *iter; /* Do actual terraforming. */ TerraformTilesResult ret = TerraformTiles(iter, _current_pasting->dc_flags | DC_ALL_TILES, _current_pasting->GetAvailableMoney()); /* When copy-pasting, we want to higlight error tiles more frequently. TerraformTiles * doesn't always set the _terraform_err_tile (on some errors it's just INVALID_TILE). * We will assume the start tile in these cases. This will give a better overview on * what area failed to paste. */ if (_terraform_err_tile == INVALID_TILE) _terraform_err_tile = start_tile; /* Collect overal cost of the operation. */ if (ret.had_success) { _current_pasting->CollectCost(CommandCost(EXPENSES_CONSTRUCTION, ret.cost), _terraform_err_tile, STR_ERROR_CAN_T_LEVEL_LAND_HERE); } /* Handle _additional_cash_required */ if ((_current_pasting->dc_flags & DC_EXEC) && _additional_cash_required > 0) { SetDParam(0, _additional_cash_required); _current_pasting->CollectError(_terraform_err_tile, STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY, STR_ERROR_CAN_T_LEVEL_LAND_HERE); } /* Collect last error, if any. */ if (ret.last_error != STR_NULL) { _current_pasting->CollectError(_terraform_err_tile, ret.last_error, STR_ERROR_CAN_T_LEVEL_LAND_HERE); } }
/** * Remove an engine replacement from a given renewlist. * @param erl The renewlist from which to remove the replacement * @param engine The original engine type. * @param group The group related to this replacement. * @param flags The calling command flags. * @return 0 on success, CMD_ERROR on failure. */ CommandCost RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, GroupID group, DoCommandFlag flags) { EngineRenew *er = (EngineRenew *)(*erl); EngineRenew *prev = NULL; while (er != NULL) { if (er->from == engine && er->group_id == group) { if (flags & DC_EXEC) { if (prev == NULL) { // First element /* The second becomes the new first element */ *erl = (EngineRenewList)er->next; } else { /* Cut this element out */ prev->next = er->next; } delete er; } return CommandCost(); } prev = er; er = er->next; } return CMD_ERROR; }
/** * Starts or stops a lot of vehicles * @param tile Tile of the depot where the vehicles are started/stopped (only used for depots) * @param flags type of operation * @param p1 bitmask * - bit 0 set = start vehicles, unset = stop vehicles * - bit 1 if set, then it's a vehicle list window, not a depot and Tile is ignored in this case * @param p2 packed VehicleListIdentifier * @param text unused * @return the cost of this operation or an error */ CommandCost CmdMassStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { VehicleList list; bool do_start = HasBit(p1, 0); bool vehicle_list_window = HasBit(p1, 1); VehicleListIdentifier vli; if (!vli.Unpack(p2)) return CMD_ERROR; if (!IsCompanyBuildableVehicleType(vli.vtype)) return CMD_ERROR; if (vehicle_list_window) { if (!GenerateVehicleSortList(&list, vli)) return CMD_ERROR; } else { /* Get the list of vehicles in the depot */ BuildDepotVehicleList(vli.vtype, tile, &list, NULL); } for (uint i = 0; i < list.Length(); i++) { const Vehicle *v = list[i]; if (!!(v->vehstatus & VS_STOPPED) != do_start) continue; if (!vehicle_list_window && !v->IsChainInDepot()) continue; /* Just try and don't care if some vehicle's can't be stopped. */ DoCommand(tile, v->index, 0, flags, CMD_START_STOP_VEHICLE); } return CommandCost(); }
/** * Rename a sign. If the new name of the sign is empty, we assume * the user wanted to delete it. So delete it. Ownership of signs * has no meaning/effect whatsoever except for eyecandy * @param tile unused * @param flags type of operation * @param p1 index of the sign to be renamed/removed * @param p2 unused * @param text the new name or an empty string when resetting to the default * @return the cost of this operation or an error */ CommandCost CmdRenameSign(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Sign *si = Sign::GetIfValid(p1); if (si == NULL) return CMD_ERROR; /* Rename the signs when empty, otherwise remove it */ if (!StrEmpty(text)) { if (Utf8StringLength(text) >= MAX_LENGTH_SIGN_NAME_CHARS) return CMD_ERROR; if (flags & DC_EXEC) { /* Delete the old name */ free(si->name); /* Assign the new one */ si->name = strdup(text); si->owner = _current_company; si->UpdateVirtCoord(); InvalidateWindowData(WC_SIGN_LIST, 0, 1); } } else { // Delete sign if (flags & DC_EXEC) { si->sign.MarkDirty(); delete si; InvalidateWindowData(WC_SIGN_LIST, 0, 0); } } return CommandCost(); }
/** * Change the name of the president. * @param tile unused * @param flags operation to perform * @param p1 unused * @param p2 unused * @param text the new name or an empty string when resetting to the default * @return the cost of this operation or an error */ CommandCost CmdRenamePresident(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { bool reset = StrEmpty(text); if (!reset) { if (Utf8StringLength(text) >= MAX_LENGTH_PRESIDENT_NAME_CHARS) return CMD_ERROR; if (!IsUniquePresidentName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE); } if (flags & DC_EXEC) { Company *c = Company::Get(_current_company); free(c->president_name); if (reset) { c->president_name = NULL; } else { c->president_name = strdup(text); if (c->name_1 == STR_SV_UNNAMED && c->name == NULL) { char buf[80]; snprintf(buf, lengthof(buf), "%s Transport", text); DoCommand(0, 0, 0, DC_EXEC, CMD_RENAME_COMPANY, buf); } } MarkWholeScreenDirty(); CompanyAdminUpdate(c); } return CommandCost(); }
/** * Copy head specific things to the new vehicle chain after it was successfully constructed * @param old_head The old front vehicle (no wagons attached anymore) * @param new_head The new head of the completely replaced vehicle chain * @param flags the command flags to use */ static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlag flags) { CommandCost cost = CommandCost(); /* Share orders */ if (cost.Succeeded() && old_head != new_head) cost.AddCost(DoCommand(0, new_head->index | CO_SHARE << 30, old_head->index, DC_EXEC, CMD_CLONE_ORDER)); /* Copy group membership */ if (cost.Succeeded() && old_head != new_head) cost.AddCost(DoCommand(0, old_head->group_id, new_head->index, DC_EXEC, CMD_ADD_VEHICLE_GROUP)); /* Perform start/stop check whether the new vehicle suits newgrf restrictions etc. */ if (cost.Succeeded()) { /* Start the vehicle, might be denied by certain things */ assert((new_head->vehstatus & VS_STOPPED) != 0); cost.AddCost(CmdStartStopVehicle(new_head, true)); /* Stop the vehicle again, but do not care about evil newgrfs allowing starting but not stopping :p */ if (cost.Succeeded()) cost.AddCost(CmdStartStopVehicle(new_head, false)); } /* Last do those things which do never fail (resp. we do not care about), but which are not undo-able */ if (cost.Succeeded() && old_head != new_head && (flags & DC_EXEC) != 0) { /* Copy other things which cannot be copied by a command and which shall not stay resetted from the build vehicle command */ new_head->CopyVehicleConfigAndStatistics(old_head); /* Switch vehicle windows/news to the new vehicle, so they are not closed/deleted when the old vehicle is sold */ ChangeVehicleViewports(old_head->index, new_head->index); ChangeVehicleViewWindow(old_head->index, new_head->index); ChangeVehicleNews(old_head->index, new_head->index); } return cost; }
/** * Send all vehicles of type to depots * @param flags the flags used for DoCommand() * @param service should the vehicles only get service in the depots * @param vli identifier of the vehicle list * @return 0 for success and CMD_ERROR if no vehicle is able to go to depot */ static CommandCost SendAllVehiclesToDepot(DoCommandFlag flags, bool service, const VehicleListIdentifier &vli) { VehicleList list; if (!GenerateVehicleSortList(&list, vli)) return CMD_ERROR; /* Send all the vehicles to a depot */ bool had_success = false; for (uint i = 0; i < list.Length(); i++) { const Vehicle *v = list[i]; CommandCost ret = DoCommand(v->tile, v->index | (service ? DEPOT_SERVICE : 0U) | DEPOT_DONT_CANCEL, 0, flags, GetCmdSendToDepot(vli.vtype)); if (ret.Succeeded()) { had_success = true; /* Return 0 if DC_EXEC is not set this is a valid goto depot command) * In this case we know that at least one vehicle can be sent to a depot * and we will issue the command. We can now safely quit the loop, knowing * it will succeed at least once. With DC_EXEC we really need to send them to the depot */ if (!(flags & DC_EXEC)) break; } } return had_success ? CommandCost() : CMD_ERROR; }
/** * Levels a selected (rectangle) area of land * @param tile end tile of area-drag * @param flags for this command type * @param p1 start tile of area drag * @param p2 various bitstuffed data. * bit 0: Whether to use the Orthogonal (0) or Diagonal (1) iterator. * bits 1 - 2: Mode of leveling \c LevelMode. * @param text unused * @return the cost of this operation or an error */ CommandCost CmdLevelLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (p1 >= MapSize()) return CMD_ERROR; /* compute new height */ int h = TileHeight(p1); switch ((LevelMode)GB(p2, 1, 2)) { case LM_LEVEL: break; case LM_RAISE: h++; break; case LM_LOWER: h--; break; default: return CMD_ERROR; } TerraformTilesResult ret; if (HasBit(p2, 0)) { DiagonalLandLevelingIterator iter(tile, p1, h); ret = TerraformTiles(&iter, flags); } else { OrthogonalLandLevelingIterator iter(TileArea(tile, p1), h); ret = TerraformTiles(&iter, flags); } /* If there were only errors then fail with the last one. */ if (!ret.had_success && ret.last_error != STR_NULL) return_cmd_error(ret.last_error); /* Return overal cost. */ return CommandCost(EXPENSES_CONSTRUCTION, ret.cost); }
/** * Create a new story page. * @param tile unused. * @param flags type of operation * @param p1 various bitstuffed elements * - p1 = (bit 0 - 7) - Company for which this story page belongs to. * @param p2 unused. * @param text Title of the story page. Null is allowed in wich case a generic page title is provided by OpenTTD. * @return the cost of this operation or an error */ CommandCost CmdCreateStoryPage(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (!StoryPage::CanAllocateItem()) return CMD_ERROR; CompanyID company = (CompanyID)GB(p1, 0, 8); if (_current_company != OWNER_DEITY) return CMD_ERROR; if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR; if (flags & DC_EXEC) { if (_story_page_pool.items == 0) { /* Initialize the next sort value variable. */ _story_page_next_sort_value = 0; } StoryPage *s = new StoryPage(); s->sort_value = _story_page_next_sort_value; s->date = _date; s->company = company; if (StrEmpty(text)) { s->title = NULL; } else { s->title = strdup(text); } InvalidateWindowClassesData(WC_STORY_BOOK, -1); if (StoryPage::GetNumItems() == 1) InvalidateWindowData(WC_MAIN_TOOLBAR, 0); _new_story_page_id = s->index; _story_page_next_sort_value++; } return CommandCost(); }
CommandCost StationRect::BeforeAddTile(TileIndex tile, StationRectMode mode) { int x = TileX(tile); int y = TileY(tile); if (this->IsEmpty()) { /* we are adding the first station tile */ if (mode != ADD_TEST) { this->left = this->right = x; this->top = this->bottom = y; } } else if (!this->PtInExtendedRect(x, y)) { /* current rect is not empty and new point is outside this rect * make new spread-out rectangle */ Rect new_rect = {min(x, this->left), min(y, this->top), max(x, this->right), max(y, this->bottom)}; /* check new rect dimensions against preset max */ int w = new_rect.right - new_rect.left + 1; int h = new_rect.bottom - new_rect.top + 1; if (mode != ADD_FORCE && (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread)) { assert(mode != ADD_TRY); return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT); } /* spread-out ok, return true */ if (mode != ADD_TEST) { /* we should update the station rect */ *this = new_rect; } } else { ; // new point is inside the rect, we don't need to do anything } return CommandCost(); }
/** * Set the start date of the timetable. * @param tile Not used. * @param flags Operation to perform. * @param p1 Vehicle id. * @param p2 The timetable start date in ticks. */ CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (!_settings_game.order.timetabling) return CMD_ERROR; Vehicle *v = Vehicle::GetIfValid(GB(p1, 0, 16)); if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR; CommandCost ret = CheckOwnership(v->owner); if (ret.Failed()) return ret; /* Don't let a timetable start more than 15 years into the future or 1 year in the past. */ Date start_date = (Date)p2; if (start_date < 0 || start_date > MAX_DAY) return CMD_ERROR; if (start_date - _date > 15 * DAYS_IN_LEAP_YEAR) return CMD_ERROR; if (_date - start_date > DAYS_IN_LEAP_YEAR) return CMD_ERROR; if (flags & DC_EXEC) { v->lateness_counter = 0; ClrBit(v->vehicle_flags, VF_TIMETABLE_STARTED); v->timetable_start = start_date; SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index); } return CommandCost(); }
/** * Rename a waypoint. * @param tile unused * @param flags type of operation * @param p1 id of waypoint * @param p2 unused * @param text the new name or an empty string when resetting to the default * @return the cost of this operation or an error */ CommandCost CmdRenameWaypoint(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Waypoint *wp = Waypoint::GetIfValid(p1); if (wp == NULL) return CMD_ERROR; if (wp->owner != OWNER_NONE) { CommandCost ret = CheckOwnership(wp->owner); if (ret.Failed()) return ret; } bool reset = StrEmpty(text); if (!reset) { if (Utf8StringLength(text) >= MAX_LENGTH_STATION_NAME_CHARS) return CMD_ERROR; if (!IsUniqueWaypointName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE); } if (flags & DC_EXEC) { free(wp->name); wp->name = reset ? NULL : strdup(text); wp->UpdateVirtCoord(); } return CommandCost(); }
/** * Place a sign at the given coordinates. Ownership of sign has * no effect whatsoever except for the colour the sign gets for easy recognition, * but everybody is able to rename/remove it. * @param tile tile to place sign at * @param flags type of operation * @param p1 unused * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdPlaceSign(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { /* Try to locate a new sign */ if (!Sign::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_SIGNS); /* Check sign text length if any */ if (!StrEmpty(text) && Utf8StringLength(text) >= MAX_LENGTH_SIGN_NAME_CHARS) return CMD_ERROR; /* When we execute, really make the sign */ if (flags & DC_EXEC) { Sign *si = new Sign(_current_company); int x = TileX(tile) * TILE_SIZE; int y = TileY(tile) * TILE_SIZE; si->x = x; si->y = y; si->z = GetSlopeZ(x, y); if (!StrEmpty(text)) { si->name = strdup(text); } si->UpdateVirtCoord(); InvalidateWindowData(WC_SIGN_LIST, 0, 0); _new_sign_id = si->index; } return CommandCost(); }
/** * Check whether the given tile is suitable for a waypoint. * @param tile the tile to check for suitability * @param axis the axis of the waypoint * @param waypoint Waypoint the waypoint to check for is already joined to. If we find another waypoint it can join to it will throw an error. */ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint) { /* if waypoint is set, then we have special handling to allow building on top of already existing waypoints. * so waypoint points to INVALID_STATION if we can build on any waypoint. * Or it points to a waypoint if we're only allowed to build on exactly that waypoint. */ if (waypoint != NULL && IsTileType(tile, MP_STATION)) { if (!IsRailWaypoint(tile)) { return ClearTile_Station(tile, DC_AUTO); // get error message } else { StationID wp = GetStationIndex(tile); if (*waypoint == INVALID_STATION) { *waypoint = wp; } else if (*waypoint != wp) { return_cmd_error(STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING); } } } if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); Owner owner = GetTileOwner(tile); CommandCost ret = CheckOwnership(owner); if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile); if (ret.Failed()) return ret; Slope tileh = GetTileSlope(tile); if (tileh != SLOPE_FLAT && (!_settings_game.construction.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) { return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); } if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); return CommandCost(); }
/** * Remove a buoy * @param tile TileIndex been queried * @param flags operation to perform * @pre IsBuoyTile(tile) * @return cost or failure of operation */ CommandCost RemoveBuoy(TileIndex tile, DoCommandFlag flags) { /* XXX: strange stuff, allow clearing as invalid company when clearing landscape */ if (!Company::IsValidID(_current_company) && !(flags & DC_BANKRUPT)) return_cmd_error(INVALID_STRING_ID); Waypoint *wp = Waypoint::GetByTile(tile); if (HasStationInUse(wp->index, false, _current_company)) return_cmd_error(STR_ERROR_BUOY_IS_IN_USE); /* remove the buoy if there is a ship on tile when company goes bankrupt... */ if (!(flags & DC_BANKRUPT)) { CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Failed()) return ret; } if (flags & DC_EXEC) { wp->facilities &= ~FACIL_DOCK; InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index); /* We have to set the water tile's state to the same state as before the * buoy was placed. Otherwise one could plant a buoy on a canal edge, * remove it and flood the land (if the canal edge is at level 0) */ MakeWaterKeepingClass(tile, GetTileOwner(tile)); wp->rect.AfterRemoveTile(wp, tile); wp->UpdateVirtCoord(); wp->delete_ctr = 0; } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WAYPOINT_BUOY]); }
/** Decrease the loan of your company. * @param tile unused * @param flags operation to perform * @param p1 amount to decrease the loan with, multitude of LOAN_INTERVAL. Only used when p2 == 2. * @param p2 when 0: pays back LOAN_INTERVAL * when 1: pays back the maximum loan permitting money (press CTRL), * when 2: pays back the amount specified in p1 * @param text unused * @return the cost of this operation or an error */ CommandCost CmdDecreaseLoan(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Company *c = Company::Get(_current_company); if (c->current_loan == 0) return_cmd_error(STR_ERROR_LOAN_ALREADY_REPAYED); Money loan; switch (p2) { default: return CMD_ERROR; // Invalid method case 0: // Pay back one step loan = min(c->current_loan, (Money)LOAN_INTERVAL); break; case 1: // Pay back as much as possible loan = max(min(c->current_loan, c->money), (Money)LOAN_INTERVAL); loan -= loan % LOAN_INTERVAL; break; case 2: // Repay the given amount of loan if (p1 % LOAN_INTERVAL != 0 || (int32)p1 < LOAN_INTERVAL || p1 > c->current_loan) return CMD_ERROR; // Invalid amount to loan loan = p1; break; } if (c->money < loan) { SetDParam(0, loan); return_cmd_error(STR_ERROR_CURRENCY_REQUIRED); } if (flags & DC_EXEC) { c->money -= loan; c->current_loan -= loan; InvalidateCompanyWindows(c); } return CommandCost(); }
/** Increase the loan of your company. * @param tile unused * @param flags operation to perform * @param p1 amount to increase the loan with, multitude of LOAN_INTERVAL. Only used when p2 == 2. * @param p2 when 0: loans LOAN_INTERVAL * when 1: loans the maximum loan permitting money (press CTRL), * when 2: loans the amount specified in p1 * @param text unused * @return the cost of this operation or an error */ CommandCost CmdIncreaseLoan(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Company *c = Company::Get(_current_company); if (c->current_loan >= _economy.max_loan) { SetDParam(0, _economy.max_loan); return_cmd_error(STR_ERROR_MAXIMUM_PERMITTED_LOAN); } Money loan; switch (p2) { default: return CMD_ERROR; // Invalid method case 0: // Take some extra loan loan = LOAN_INTERVAL; break; case 1: // Take a loan as big as possible loan = _economy.max_loan - c->current_loan; break; case 2: // Take the given amount of loan if ((int32)p1 < LOAN_INTERVAL || c->current_loan + (int32)p1 > _economy.max_loan || p1 % LOAN_INTERVAL != 0) return CMD_ERROR; loan = p1; break; } /* Overflow protection */ if (c->money + c->current_loan + loan < c->money) return CMD_ERROR; if (flags & DC_EXEC) { c->money += loan; c->current_loan += loan; InvalidateCompanyWindows(c); } return CommandCost(EXPENSES_OTHER); }
/** Change the financial flow of your company. * This is normally only enabled in offline mode, but if there is a debug * build, you can cheat (to test). * @param tile unused * @param flags operation to perform * @param p1 the amount of money to receive (if negative), or spend (if positive) * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdMoneyCheat(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { #ifndef _DEBUG if (_networking) return CMD_ERROR; #endif return CommandCost(EXPENSES_OTHER, -(int32)p1); }
/** * Change the service interval of a vehicle * @param tile unused * @param flags type of operation * @param p1 vehicle ID that is being service-interval-changed * @param p2 bitmask * - p2 = (bit 0-15) - new service interval * - p2 = (bit 16) - service interval is custom flag * - p2 = (bit 17) - service interval is percentage flag * @param text unused * @return the cost of this operation or an error */ CommandCost CmdChangeServiceInt(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Vehicle *v = Vehicle::GetIfValid(p1); if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR; CommandCost ret = CheckOwnership(v->owner); if (ret.Failed()) return ret; const Company *company = Company::Get(v->owner); bool iscustom = HasBit(p2, 16); bool ispercent = iscustom ? HasBit(p2, 17) : company->settings.vehicle.servint_ispercent; uint16 serv_int; if (iscustom) { serv_int = GB(p2, 0, 16); if (serv_int != GetServiceIntervalClamped(serv_int, ispercent)) return CMD_ERROR; } else { serv_int = CompanyServiceInterval(company, v->type); } if (flags & DC_EXEC) { v->SetServiceInterval(serv_int); v->SetServiceIntervalIsCustom(iscustom); v->SetServiceIntervalIsPercent(ispercent); SetWindowDirty(WC_VEHICLE_DETAILS, v->index); } return CommandCost(); }
/** * Build a ship. * @param tile tile of the depot where ship is built. * @param flags type of operation. * @param e the engine to build. * @param data unused. * @param ret[out] the vehicle that has been built. * @return the cost of this operation or an error. */ CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **ret) { tile = GetShipDepotNorthTile(tile); if (flags & DC_EXEC) { int x; int y; const ShipVehicleInfo *svi = &e->u.ship; Ship *v = new Ship(); *ret = v; v->owner = _current_company; v->tile = tile; x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2; y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2; v->x_pos = x; v->y_pos = y; v->z_pos = GetSlopeZ(x, y); v->UpdateDeltaXY(v->direction); v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; v->spritenum = svi->image_index; v->cargo_type = e->GetDefaultCargoType(); v->cargo_cap = svi->capacity; v->last_station_visited = INVALID_STATION; v->engine_type = e->index; v->reliability = e->reliability; v->reliability_spd_dec = e->reliability_spd_dec; v->max_age = e->GetLifeLengthInDays(); _new_vehicle_id = v->index; v->state = TRACK_BIT_DEPOT; v->service_interval = Company::Get(_current_company)->settings.vehicle.servint_ships; v->date_of_last_service = _date; v->build_year = _cur_year; v->cur_image = SPR_IMG_QUERY; v->random_bits = VehicleRandomBits(); v->UpdateCache(); if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE); v->InvalidateNewGRFCacheOfChain(); v->cargo_cap = GetVehicleCapacity(v); v->InvalidateNewGRFCacheOfChain(); VehicleMove(v, false); } return CommandCost(); }
/** * Build a ship depot. * @param tile tile where ship depot is built * @param flags type of operation * @param p1 bit 0 depot orientation (Axis) * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Axis axis = Extract<Axis, 0, 1>(p1); TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) { return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); } if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) || (MayHaveBridgeAbove(tile2) && IsBridgeAbove(tile2))) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); if (!IsTileFlat(tile) || !IsTileFlat(tile2)) { /* Prevent depots on rapids */ return_cmd_error(STR_ERROR_SITE_UNSUITABLE); } if (!Depot::CanAllocateItem()) return CMD_ERROR; WaterClass wc1 = GetWaterClass(tile); WaterClass wc2 = GetWaterClass(tile2); CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]); bool add_cost = !IsWaterTile(tile); CommandCost ret = DoCommand(tile, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; if (add_cost) { cost.AddCost(ret); } add_cost = !IsWaterTile(tile2); ret = DoCommand(tile2, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; if (add_cost) { cost.AddCost(ret); } if (flags & DC_EXEC) { Depot *depot = new Depot(tile); depot->build_date = _date; if (wc1 == WATER_CLASS_CANAL || wc2 == WATER_CLASS_CANAL) { /* Update infrastructure counts after the unconditional clear earlier. */ Company::Get(_current_company)->infrastructure.water += wc1 == WATER_CLASS_CANAL && wc2 == WATER_CLASS_CANAL ? 2 : 1; } Company::Get(_current_company)->infrastructure.water += 2 * LOCK_DEPOT_TILE_FACTOR; DirtyCompanyInfrastructureWindows(_current_company); MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1); MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2); MarkTileDirtyByTile(tile); MarkTileDirtyByTile(tile2); MakeDefaultName(depot); } return cost; }