Exemplo n.º 1
0
/**
 * 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);
}
Exemplo n.º 2
0
/**
 * 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;
}
Exemplo n.º 3
0
/**
 * 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();
}
Exemplo n.º 4
0
/**
 * 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();
}
Exemplo n.º 5
0
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]);
}
Exemplo n.º 6
0
/** 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));
}
Exemplo n.º 7
0
/**
 * 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]);
}
Exemplo n.º 10
0
/**
 * 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);
	}
}
Exemplo n.º 11
0
/**
 * 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;
}
Exemplo n.º 12
0
/**
 * 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();
}
Exemplo n.º 13
0
/**
 * 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();
}
Exemplo n.º 14
0
/**
 * 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();
}
Exemplo n.º 15
0
/**
 * 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;
}
Exemplo n.º 16
0
/**
 * 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;
}
Exemplo n.º 17
0
/**
 * 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);
}
Exemplo n.º 18
0
/**
 * 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();
}
Exemplo n.º 19
0
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();
}
Exemplo n.º 20
0
/**
 * 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();
}
Exemplo n.º 21
0
/**
 * 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();
}
Exemplo n.º 22
0
/**
 * 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();
}
Exemplo n.º 23
0
/**
 * 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();
}
Exemplo n.º 24
0
/**
 * 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]);
}
Exemplo n.º 25
0
/** 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();
}
Exemplo n.º 26
0
/** 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);
}
Exemplo n.º 27
0
/** 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);
}
Exemplo n.º 28
0
/**
 * 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();
}
Exemplo n.º 29
0
/**
 * 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();
}
Exemplo n.º 30
0
/**
 * 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;
}