Пример #1
0
/** Refits a ship to the specified cargo type.
 * @param tile unused
 * @param flags type of operation
 * @param p1 vehicle ID of the ship to refit
 * @param p2 various bitstuffed elements
 * - p2 = (bit 0-7) - the new cargo type to refit to (p2 & 0xFF)
 * - p2 = (bit 8-15) - the new cargo subtype to refit to
 * - p2 = (bit 16) - refit only this vehicle (ignored)
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdRefitShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	CargoID new_cid = GB(p2, 0, 8); // gets the cargo number
	byte new_subtype = GB(p2, 8, 8);

	Ship *v = Ship::GetIfValid(p1);

	if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;
	if (!v->IsStoppedInDepot()) return_cmd_error(STR_ERROR_SHIP_MUST_BE_STOPPED_IN_DEPOT);
	if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_CAN_T_REFIT_DESTROYED_VEHICLE);

	/* Check cargo */
	if (new_cid >= NUM_CARGO) return CMD_ERROR;

	CommandCost cost = RefitVehicle(v, true, new_cid, new_subtype, flags);

	if (flags & DC_EXEC) {
		v->colourmap = PAL_NONE; // invalidate vehicle colour map
		SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
		SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
		InvalidateWindowClassesData(WC_SHIPS_LIST, 0);
	}
	v->InvalidateNewGRFCacheOfChain(); // always invalidate; querycost might have filled it

	return cost;

}
Пример #2
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();
}
Пример #3
0
TInt CCallDummyBase::AcquireOwnership(const TTsyReqHandle aTsyReqHandle)
//
//	Acquire call Ownership
//
	{
	TInt ret(KErrNone);
	if (CheckOwnership(aTsyReqHandle)==CCallBase::EOwnedUnowned)
		{
		SetOwnership(aTsyReqHandle);
		ReqCompleted(aTsyReqHandle,KErrNone);
		}
	else
		{
		if(iList->iAcquireList.IsEmpty())
			// List is empty. Client is the first one to request ownership of the call.
			{
			CAcquireEntry* entry=NULL;
			TRAP(ret, entry=CAcquireEntry::NewL(aTsyReqHandle));
			if (ret==KErrNone)
				iList->iAcquireList.AddLast(*entry);
			}
		else
			// List is not empty. Another client has already requested to acquire ownership of the call.
			// Only one client can be waiting to acquire ownership at any one time.
			return KErrInUse;
		}
	return ret;
	}
Пример #4
0
TInt CCallDummyBase::TransferOwnership(const TTsyReqHandle aTsyReqHandle)
//
//	Transfer Call Ownership
//
	{
	if (CheckOwnership(aTsyReqHandle)!=CCallBase::EOwnedTrue)
		{
		ReqCompleted(aTsyReqHandle,KErrEtelNotCallOwner);
		return KErrNone;
		}

	if(iList->iAcquireList.IsEmpty()) // no one interested in this call !
		{
		ReqCompleted(aTsyReqHandle,KErrEtelNoClientInterestedInThisCall);
		return KErrNone;
		}

	CAcquireEntry* entry=iList->iAcquireList.First();
	if (entry) // someone interested in this call
		{
		SetOwnership(entry->iTsyReqHandle);
		ReqCompleted(entry->iTsyReqHandle,KErrNone);
		iList->Remove(entry);
		ReqCompleted(aTsyReqHandle,KErrNone);
		}
	return KErrNone;
	}
Пример #5
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();
}
Пример #6
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();
}
Пример #7
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();
}
Пример #8
0
TInt CCallDummyBase::Dial(const TTsyReqHandle aTsyReqHandle,const TDesC8* /*aParams*/,TDesC* aNumber)
	{
	
	if (CheckOwnership(aTsyReqHandle)==CCallBase::EOwnedUnowned)
		{
		SetOwnership(aTsyReqHandle);
		if(aNumber->Compare(DACQ_PHONE_NUMBER_TO_DIAL)!=KErrNone)
			TsyPanic(KTsyPanicDataCorrupted);
		iFac->QueueTimer(iDial,aTsyReqHandle,DACQ_ASYN_TIMEOUT,
						CCallDummyBase::DialHandler,this);
		}
	else if (CheckOwnership(aTsyReqHandle)==CCallBase::EOwnedFalse)
		ReqCompleted(aTsyReqHandle,KErrEtelNotCallOwner);
	else
		ReqCompleted(aTsyReqHandle,KErrEtelCallAlreadyActive);

	return KErrNone;
	}
Пример #9
0
TInt CCallDummyBase::HangUp(const TTsyReqHandle aTsyReqHandle)
	{
	if (CheckOwnership(aTsyReqHandle)==CCallBase::EOwnedTrue)
		{
		iFac->QueueTimer(iHangUp,aTsyReqHandle,DACQ_ASYN_TIMEOUT,
						CCallDummyBase::HangUpHandler,this);
		return KErrNone;
		}
	else
		return KErrEtelNotCallOwner;
	}
Пример #10
0
/**
 * Add or remove waiting times from an order.
 * @param tile Not used.
 * @param flags Operation to perform.
 * @param p1 Various bitstuffed elements
 * - p1 = (bit  0-15) - Vehicle with the orders to change.
 * - p1 = (bit 16-23) - Order index to modify.
 * - p1 = (bit    24) - Whether to change the waiting time or the travelling
 *                      time.
 * - p1 = (bit    25) - Whether p2 contains waiting and travelling time.
 * @param p2 The amount of time to wait.
 * - p2 = (bit  0-15) - Waiting or travelling time as specified by p1 bit 24 if p1 bit 25 is not set,
 *                      Travelling time if p1 bit 25 is set.
 * - p2 = (bit 16-31) - Waiting time if p1 bit 25 is set
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	if (!_settings_game.order.timetabling) return CMD_ERROR;

	VehicleID veh = GB(p1, 0, 16);

	Vehicle *v = Vehicle::GetIfValid(veh);
	if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;

	CommandCost ret = CheckOwnership(v->owner);
	if (ret.Failed()) return ret;

	VehicleOrderID order_number = GB(p1, 16, 8);
	Order *order = v->GetOrder(order_number);
	if (order == NULL) return CMD_ERROR;

	bool packed_time = HasBit(p1, 25);
	bool is_journey = HasBit(p1, 24) || packed_time;

	int wait_time   = order->wait_time;
	int travel_time = order->travel_time;
	if (packed_time) {
		travel_time = GB(p2, 0, 16);
		wait_time   = GB(p2, 16, 16);
	} else if (is_journey) {
		travel_time = GB(p2, 0, 16);
	} else {
		wait_time   = GB(p2, 0, 16);
	}

	if (wait_time != order->wait_time) {
		switch (order->GetType()) {
			case OT_GOTO_STATION:
				if (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) return_cmd_error(STR_ERROR_TIMETABLE_NOT_STOPPING_HERE);
				break;

			case OT_CONDITIONAL:
				break;

			default: return_cmd_error(STR_ERROR_TIMETABLE_ONLY_WAIT_AT_STATIONS);
		}
	}

	if (travel_time != order->travel_time && order->IsType(OT_CONDITIONAL)) return CMD_ERROR;

	if (flags & DC_EXEC) {
		if (wait_time   != order->wait_time)   ChangeTimetable(v, order_number, wait_time,   false);
		if (travel_time != order->travel_time) ChangeTimetable(v, order_number, travel_time, true);
	}

	return CommandCost();
}
Пример #11
0
/**
 * Set the start date of the timetable.
 * @param tile Not used.
 * @param flags Operation to perform.
 * @param p2 Various bitstuffed elements
 * - p2 = (bit 0-19) - Vehicle ID.
 * - p2 = (bit 20)   - Set to 1 to set timetable start for all vehicles sharing this order
 * @param p2 The timetable start date.
 * @param text Not used.
 * @return The error or cost of the operation.
 */
CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
    bool timetable_all = HasBit(p1, 20);
    Vehicle *v = Vehicle::GetIfValid(GB(p1, 0, 20));
    if (v == NULL || !v->IsPrimaryVehicle() || v->orders.list == NULL) 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 (timetable_all && !v->orders.list->IsCompleteTimetable()) return CMD_ERROR;

    if (flags & DC_EXEC) {
        SmallVector<Vehicle *, 8> vehs;

        if (timetable_all) {
            for (Vehicle *w = v->orders.list->GetFirstSharedVehicle(); w != NULL; w = w->NextShared()) {
                *vehs.Append() = w;
            }
        } else {
            *vehs.Append() = v;
        }

        int total_duration = v->orders.list->GetTimetableTotalDuration();
        int num_vehs = vehs.Length();

        if (num_vehs >= 2) {
            QSortT(vehs.Begin(), vehs.Length(), &VehicleTimetableSorter);
        }

        int base = vehs.FindIndex(v);

        for (Vehicle **viter = vehs.Begin(); viter != vehs.End(); viter++) {
            int idx = (viter - vehs.Begin()) - base;
            Vehicle *w = *viter;

            w->lateness_counter = 0;
            ClrBit(w->vehicle_flags, VF_TIMETABLE_STARTED);
            /* Do multiplication, then division to reduce rounding errors. */
            w->timetable_start = start_date + idx * total_duration / num_vehs / DAY_TICKS;
            SetWindowDirty(WC_VEHICLE_TIMETABLE, w->index);
        }

    }

    return CommandCost();
}
Пример #12
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 new service interval
 * @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 || !CheckOwnership(v->owner)) return CMD_ERROR;

    uint16 serv_int = GetServiceIntervalClamped(p2, v->owner); // Double check the service interval from the user-input
    if (serv_int != p2) return CMD_ERROR;

    if (flags & DC_EXEC) {
        v->service_interval = serv_int;
        SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    }

    return CommandCost();
}
Пример #13
0
/** Start/Stop a vehicle
 * @param tile unused
 * @param flags type of operation
 * @param p1 vehicle to start/stop
 * @param p2 bit 0: Shall the start/stop newgrf callback be evaluated (only valid with DC_AUTOREPLACE for network safety)
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
    /* Disable the effect of p2 bit 0, when DC_AUTOREPLACE is not set */
    if ((flags & DC_AUTOREPLACE) == 0) SetBit(p2, 0);

    Vehicle *v = Vehicle::GetIfValid(p1);
    if (v == NULL || !CheckOwnership(v->owner) || !v->IsPrimaryVehicle()) return CMD_ERROR;

    switch (v->type) {
    case VEH_TRAIN:
        if ((v->vehstatus & VS_STOPPED) && Train::From(v)->tcache.cached_power == 0) return_cmd_error(STR_ERROR_TRAIN_START_NO_CATENARY);
        break;

    case VEH_SHIP:
    case VEH_ROAD:
        break;

    case VEH_AIRCRAFT: {
        Aircraft *a = Aircraft::From(v);
        /* cannot stop airplane when in flight, or when taking off / landing */
        if (a->state >= STARTTAKEOFF && a->state < TERM7) return_cmd_error(STR_ERROR_AIRCRAFT_IS_IN_FLIGHT);
    }
    break;

    default:
        return CMD_ERROR;
    }

    /* Check if this vehicle can be started/stopped. The callback will fail or
     * return 0xFF if it can. */
    uint16 callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
    if (callback != CALLBACK_FAILED && GB(callback, 0, 8) != 0xFF && HasBit(p2, 0)) {
        StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback);
        return_cmd_error(error);
    }

    if (flags & DC_EXEC) {
        if (v->IsStoppedInDepot() && (flags & DC_AUTOREPLACE) == 0) DeleteVehicleNews(p1, STR_NEWS_TRAIN_IS_WAITING + v->type);

        v->vehstatus ^= VS_STOPPED;
        if (v->type != VEH_TRAIN) v->cur_speed = 0; // trains can stop 'slowly'
        v->MarkDirty();
        SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
        SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
        SetWindowClassesDirty(GetWindowClassForVehicleType(v->type));
    }
    return CommandCost();
}
Пример #14
0
/**
 * Clear the lateness counter to make the vehicle on time.
 * @param tile Not used.
 * @param flags Operation to perform.
 * @param p1 Various bitstuffed elements
 * - p1 = (bit  0-19) - Vehicle with the orders to change.
 * @param p2 unused
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdSetVehicleOnTime(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	VehicleID veh = GB(p1, 0, 20);

	Vehicle *v = Vehicle::GetIfValid(veh);
	if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;

	CommandCost ret = CheckOwnership(v->owner);
	if (ret.Failed()) return ret;

	if (flags & DC_EXEC) {
		v->lateness_counter = 0;
		SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
	}

	return CommandCost();
}
Пример #15
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.
 * @param flags flags for the command
 */
static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint, DoCommandFlag flags)
{
	/* 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);
			}
		}
	}

	/* When pasting a waypoint, there may be no track yet (it will be placed when DC_EXEC'ing).
	 * Ignore that so we can calculate the cost more precisely. */
	bool ignore_lack_of_tracks = (flags & DC_PASTE) && !(flags & DC_EXEC);

	if (!ignore_lack_of_tracks || IsTileType(tile, MP_RAILWAY)) {
		if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
	}

	Owner owner = GetTileOwner(tile);
	if (!ignore_lack_of_tracks || owner != OWNER_NONE) {
		CommandCost ret = CheckOwnership(owner);
		if (ret.Failed()) return ret;
	}

	CommandCost 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 (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);

	return CommandCost();
}
Пример #16
0
/** Sell a ship.
 * @param tile unused
 * @param flags type of operation
 * @param p1 vehicle ID to be sold
 * @param p2 unused
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdSellShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	Ship *v = Ship::GetIfValid(p1);
	if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;

	if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_CAN_T_SELL_DESTROYED_VEHICLE);

	if (!v->IsStoppedInDepot()) {
		return_cmd_error(STR_ERROR_SHIP_MUST_BE_STOPPED_IN_DEPOT);
	}

	CommandCost ret(EXPENSES_NEW_VEHICLES, -v->value);

	if (flags & DC_EXEC) {
		delete v;
	}

	return ret;
}
Пример #17
0
/**
 * Start or stop filling the timetable automatically from the time the vehicle
 * actually takes to complete it. When starting to autofill the current times
 * are cleared and the timetable will start again from scratch.
 * @param tile Not used.
 * @param flags Operation to perform.
 * @param p1 Vehicle index.
 * @param p2 Various bitstuffed elements
 * - p2 = (bit 0) - Set to 1 to enable, 0 to disable autofill.
 * - p2 = (bit 1) - Set to 1 to preserve waiting times in non-destructive mode
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdAutofillTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	if (!_settings_game.order.timetabling) return CMD_ERROR;

	VehicleID veh = GB(p1, 0, 16);

	Vehicle *v = Vehicle::GetIfValid(veh);
	if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;

	CommandCost ret = CheckOwnership(v->owner);
	if (ret.Failed()) return ret;

	if (flags & DC_EXEC) {
		if (HasBit(p2, 0)) {
			/* Start autofilling the timetable, which clears the
			 * "timetable has started" bit. Times are not cleared anymore, but are
			 * overwritten when the order is reached now. */
			SetBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
			ClrBit(v->vehicle_flags, VF_TIMETABLE_STARTED);

			/* Overwrite waiting times only if they got longer */
			if (HasBit(p2, 1)) SetBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);

			v->timetable_start = 0;
			v->lateness_counter = 0;
		} else {
			ClrBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
			ClrBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
		}
	}

	for (Vehicle *v2 = v->FirstShared(); v2 != NULL; v2 = v2->NextShared()) {
		if (v2 != v) {
			/* Stop autofilling; only one vehicle at a time can perform autofill */
			ClrBit(v2->vehicle_flags, VF_AUTOFILL_TIMETABLE);
			ClrBit(v2->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
		}
		SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
	}

	return CommandCost();
}
    virtual
    void
    Unlock(
        __in KIRQL PreviousIrql
        )
    {
        CheckOwnership();

        if (m_RecursionCount > 0) {
            m_RecursionCount--;
        }
        else {
            m_OwnerThread = NULL;
            if (m_Verifier != NULL) {
                m_Verifier->Unlock(PreviousIrql, FALSE);
            }
            else {
                m_Lock.Release(PreviousIrql);
            }
        }
    }
Пример #19
0
/** Give a custom name to your vehicle
 * @param tile unused
 * @param flags type of operation
 * @param p1 vehicle ID to name
 * @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 CmdRenameVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
    Vehicle *v = Vehicle::GetIfValid(p1);
    if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;

    bool reset = StrEmpty(text);

    if (!reset) {
        if (strlen(text) >= MAX_LENGTH_VEHICLE_NAME_BYTES) return CMD_ERROR;
        if (!(flags & DC_AUTOREPLACE) && !IsUniqueVehicleName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
    }

    if (flags & DC_EXEC) {
        free(v->name);
        v->name = reset ? NULL : strdup(text);
        InvalidateWindowClassesData(WC_TRAINS_LIST, 1);
        MarkWholeScreenDirty();
    }

    return CommandCost();
}
Пример #20
0
/**
 * Sell a vehicle.
 * @param tile unused.
 * @param flags for command.
 * @param p1 various bitstuffed data.
 *  bits  0-19: vehicle ID being sold.
 *  bits 20-30: vehicle type specific bits passed on to the vehicle build functions.
 *  bit     31: make a backup of the vehicle's order (if an engine).
 * @param p2 User.
 * @param text unused.
 * @return the cost of this operation or an error.
 */
CommandCost CmdSellVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	Vehicle *v = Vehicle::GetIfValid(GB(p1, 0, 20));
	if (v == NULL) return CMD_ERROR;

	Vehicle *front = v->First();

	CommandCost ret = CheckOwnership(front->owner);
	if (ret.Failed()) return ret;

	if (front->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_VEHICLE_IS_DESTROYED);

	if (!front->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type);

	/* Can we actually make the order backup, i.e. are there enough orders? */
	if (p1 & MAKE_ORDER_BACKUP_FLAG &&
			front->orders.list != NULL &&
			!front->orders.list->IsShared() &&
			!Order::CanAllocateItem(front->orders.list->GetNumOrders())) {
		/* Only happens in exceptional cases when there aren't enough orders anyhow.
		 * Thus it should be safe to just drop the orders in that case. */
		p1 &= ~MAKE_ORDER_BACKUP_FLAG;
	}

	if (v->type == VEH_TRAIN) {
		ret = CmdSellRailWagon(flags, v, GB(p1, 20, 12), p2);
	} else {
		ret = CommandCost(EXPENSES_NEW_VEHICLES, -front->value);

		if (flags & DC_EXEC) {
			if (front->IsPrimaryVehicle() && p1 & MAKE_ORDER_BACKUP_FLAG) OrderBackup::Backup(front, p2);
			delete front;
		}
	}

	return ret;
}
Пример #21
0
/**
 * Give a custom name to your vehicle
 * @param tile unused
 * @param flags type of operation
 * @param p1 vehicle ID to name
 * @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 CmdRenameVehicle(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;

	bool reset = StrEmpty(text);

	if (!reset) {
		if (Utf8StringLength(text) >= MAX_LENGTH_VEHICLE_NAME_CHARS) return CMD_ERROR;
		if (!(flags & DC_AUTOREPLACE) && !IsUniqueVehicleName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
	}

	if (flags & DC_EXEC) {
		free(v->name);
		v->name = reset ? NULL : strdup(text);
		InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 1);
		MarkWholeScreenDirty();
	}

	return CommandCost();
}
Пример #22
0
/**
 * Autoreplaces a vehicle
 * Trains are replaced as a whole chain, free wagons in depot are replaced on their own
 * @param tile not used
 * @param flags type of operation
 * @param p1 Index of vehicle
 * @param p2 not used
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	Vehicle *v = Vehicle::GetIfValid(p1);
	if (v == NULL) return CMD_ERROR;

	CommandCost ret = CheckOwnership(v->owner);
	if (ret.Failed()) return ret;

	if (!v->IsInDepot()) return CMD_ERROR;
	if (v->vehstatus & VS_CRASHED) return CMD_ERROR;

	bool free_wagon = false;
	if (v->type == VEH_TRAIN) {
		Train *t = Train::From(v);
		if (t->IsArticulatedPart() || t->IsRearDualheaded()) return CMD_ERROR;
		free_wagon = !t->IsFrontEngine();
		if (free_wagon && t->First()->IsFrontEngine()) return CMD_ERROR;
	} else {
		if (!v->IsPrimaryVehicle()) return CMD_ERROR;
	}

	const Company *c = Company::Get(_current_company);
	bool wagon_removal = c->settings.renew_keep_length;

	/* Test whether any replacement is set, before issuing a whole lot of commands that would end in nothing changed */
	Vehicle *w = v;
	bool any_replacements = false;
	while (w != NULL) {
		EngineID e;
		CommandCost cost = GetNewEngineType(w, c, e);
		if (cost.Failed()) return cost;
		any_replacements |= (e != INVALID_ENGINE);
		w = (!free_wagon && w->type == VEH_TRAIN ? Train::From(w)->GetNextUnit() : NULL);
	}

	CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, 0);
	bool nothing_to_do = true;

	if (any_replacements) {
		bool was_stopped = free_wagon || ((v->vehstatus & VS_STOPPED) != 0);

		/* Stop the vehicle */
		if (!was_stopped) cost.AddCost(CmdStartStopVehicle(v, true));
		if (cost.Failed()) return cost;

		assert(v->IsStoppedInDepot());

		/* We have to construct the new vehicle chain to test whether it is valid.
		 * Vehicle construction needs random bits, so we have to save the random seeds
		 * to prevent desyncs and to replay newgrf callbacks during DC_EXEC */
		SavedRandomSeeds saved_seeds;
		SaveRandomSeeds(&saved_seeds);
		if (free_wagon) {
			cost.AddCost(ReplaceFreeUnit(&v, flags & ~DC_EXEC, &nothing_to_do));
		} else {
			cost.AddCost(ReplaceChain(&v, flags & ~DC_EXEC, wagon_removal, &nothing_to_do));
		}
		RestoreRandomSeeds(saved_seeds);

		if (cost.Succeeded() && (flags & DC_EXEC) != 0) {
			CommandCost ret;
			if (free_wagon) {
				ret = ReplaceFreeUnit(&v, flags, &nothing_to_do);
			} else {
				ret = ReplaceChain(&v, flags, wagon_removal, &nothing_to_do);
			}
			assert(ret.Succeeded() && ret.GetCost() == cost.GetCost());
		}

		/* Restart the vehicle */
		if (!was_stopped) cost.AddCost(CmdStartStopVehicle(v, false));
	}

	if (cost.Succeeded() && nothing_to_do) cost = CommandCost(STR_ERROR_AUTOREPLACE_NOTHING_TO_DO);
	return cost;
}
Пример #23
0
/**
 * Clone a vehicle. If it is a train, it will clone all the cars too
 * @param tile tile of the depot where the cloned vehicle is build
 * @param flags type of operation
 * @param p1 the original vehicle's index
 * @param p2 1 = shared orders, else copied orders
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdCloneVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	CommandCost total_cost(EXPENSES_NEW_VEHICLES);

	Vehicle *v = Vehicle::GetIfValid(p1);
	if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
	Vehicle *v_front = v;
	Vehicle *w = NULL;
	Vehicle *w_front = NULL;
	Vehicle *w_rear = NULL;

	/*
	 * v_front is the front engine in the original vehicle
	 * v is the car/vehicle of the original vehicle that is currently being copied
	 * w_front is the front engine of the cloned vehicle
	 * w is the car/vehicle currently being cloned
	 * w_rear is the rear end of the cloned train. It's used to add more cars and is only used by trains
	 */

	CommandCost ret = CheckOwnership(v->owner);
	if (ret.Failed()) return ret;

	if (v->type == VEH_TRAIN && (!v->IsFrontEngine() || Train::From(v)->crash_anim_pos >= 4400)) return CMD_ERROR;

	/* check that we can allocate enough vehicles */
	if (!(flags & DC_EXEC)) {
		int veh_counter = 0;
		do {
			veh_counter++;
		} while ((v = v->Next()) != NULL);

		if (!Vehicle::CanAllocateItem(veh_counter)) {
			return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
		}
	}

	v = v_front;

	do {
		if (v->type == VEH_TRAIN && Train::From(v)->IsRearDualheaded()) {
			/* we build the rear ends of multiheaded trains with the front ones */
			continue;
		}

		/* In case we're building a multi headed vehicle and the maximum number of
		 * vehicles is almost reached (e.g. max trains - 1) not all vehicles would
		 * be cloned. When the non-primary engines were build they were seen as
		 * 'new' vehicles whereas they would immediately be joined with a primary
		 * engine. This caused the vehicle to be not build as 'the limit' had been
		 * reached, resulting in partially build vehicles and such. */
		DoCommandFlag build_flags = flags;
		if ((flags & DC_EXEC) && !v->IsPrimaryVehicle()) build_flags |= DC_AUTOREPLACE;

		CommandCost cost = DoCommand(tile, v->engine_type | (1 << 16), 0, build_flags, GetCmdBuildVeh(v));

		if (cost.Failed()) {
			/* Can't build a part, then sell the stuff we already made; clear up the mess */
			if (w_front != NULL) DoCommand(w_front->tile, w_front->index | (1 << 20), 0, flags, GetCmdSellVeh(w_front));
			return cost;
		}

		total_cost.AddCost(cost);

		if (flags & DC_EXEC) {
			w = Vehicle::Get(_new_vehicle_id);

			if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
				SetBit(Train::From(w)->flags, VRF_REVERSE_DIRECTION);
			}

			if (v->type == VEH_TRAIN && !v->IsFrontEngine()) {
				/* this s a train car
				 * add this unit to the end of the train */
				CommandCost result = DoCommand(0, w->index | 1 << 20, w_rear->index, flags, CMD_MOVE_RAIL_VEHICLE);
				if (result.Failed()) {
					/* The train can't be joined to make the same consist as the original.
					 * Sell what we already made (clean up) and return an error.           */
					DoCommand(w_front->tile, w_front->index | 1 << 20, 0, flags, GetCmdSellVeh(w_front));
					DoCommand(w_front->tile, w->index       | 1 << 20, 0, flags, GetCmdSellVeh(w));
					return result; // return error and the message returned from CMD_MOVE_RAIL_VEHICLE
				}
			} else {
				/* this is a front engine or not a train. */
				w_front = w;
				w->service_interval = v->service_interval;
				w->SetServiceIntervalIsCustom(v->ServiceIntervalIsCustom());
				w->SetServiceIntervalIsPercent(v->ServiceIntervalIsPercent());
			}
			w_rear = w; // trains needs to know the last car in the train, so they can add more in next loop
		}
	} while (v->type == VEH_TRAIN && (v = v->GetNextVehicle()) != NULL);

	if ((flags & DC_EXEC) && v_front->type == VEH_TRAIN) {
		/* for trains this needs to be the front engine due to the callback function */
		_new_vehicle_id = w_front->index;
	}

	if (flags & DC_EXEC) {
		/* Cloned vehicles belong to the same group */
		DoCommand(0, v_front->group_id, w_front->index, flags, CMD_ADD_VEHICLE_GROUP);
	}


	/* Take care of refitting. */
	w = w_front;
	v = v_front;

	/* Both building and refitting are influenced by newgrf callbacks, which
	 * makes it impossible to accurately estimate the cloning costs. In
	 * particular, it is possible for engines of the same type to be built with
	 * different numbers of articulated parts, so when refitting we have to
	 * loop over real vehicles first, and then the articulated parts of those
	 * vehicles in a different loop. */
	do {
		do {
			if (flags & DC_EXEC) {
				assert(w != NULL);

				/* Find out what's the best sub type */
				byte subtype = GetBestFittingSubType(v, w, v->cargo_type);
				if (w->cargo_type != v->cargo_type || w->cargo_subtype != subtype) {
					CommandCost cost = DoCommand(0, w->index, v->cargo_type | 1U << 7 | (subtype << 8), flags, GetCmdRefitVeh(v));
					if (cost.Succeeded()) total_cost.AddCost(cost);
				}

				if (w->IsGroundVehicle() && w->HasArticulatedPart()) {
					w = w->GetNextArticulatedPart();
				} else {
					break;
				}
			} else {
				const Engine *e = v->GetEngine();
				CargoID initial_cargo = (e->CanCarryCargo() ? e->GetDefaultCargoType() : (CargoID)CT_INVALID);

				if (v->cargo_type != initial_cargo && initial_cargo != CT_INVALID) {
					bool dummy;
					total_cost.AddCost(GetRefitCost(NULL, v->engine_type, v->cargo_type, v->cargo_subtype, &dummy));
				}
			}

			if (v->IsGroundVehicle() && v->HasArticulatedPart()) {
				v = v->GetNextArticulatedPart();
			} else {
				break;
			}
		} while (v != NULL);

		if ((flags & DC_EXEC) && v->type == VEH_TRAIN) w = w->GetNextVehicle();
	} while (v->type == VEH_TRAIN && (v = v->GetNextVehicle()) != NULL);

	if (flags & DC_EXEC) {
		/*
		 * Set the orders of the vehicle. Cannot do it earlier as we need
		 * the vehicle refitted before doing this, otherwise the moved
		 * cargo types might not match (passenger vs non-passenger)
		 */
		DoCommand(0, w_front->index | (p2 & 1 ? CO_SHARE : CO_COPY) << 30, v_front->index, flags, CMD_CLONE_ORDER);

		/* Now clone the vehicle's name, if it has one. */
		if (v_front->name != NULL) CloneVehicleName(v_front, w_front);
	}

	/* Since we can't estimate the cost of cloning a vehicle accurately we must
	 * check whether the company has enough money manually. */
	if (!CheckCompanyHasMoney(total_cost)) {
		if (flags & DC_EXEC) {
			/* The vehicle has already been bought, so now it must be sold again. */
			DoCommand(w_front->tile, w_front->index | 1 << 20, 0, flags, GetCmdSellVeh(w_front));
		}
		return total_cost;
	}

	return total_cost;
}
Пример #24
0
/**
 * Start/Stop a vehicle
 * @param tile unused
 * @param flags type of operation
 * @param p1 vehicle to start/stop, don't forget to change CcStartStopVehicle if you modify this!
 * @param p2 bit 0: Shall the start/stop newgrf callback be evaluated (only valid with DC_AUTOREPLACE for network safety)
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	/* Disable the effect of p2 bit 0, when DC_AUTOREPLACE is not set */
	if ((flags & DC_AUTOREPLACE) == 0) SetBit(p2, 0);

	Vehicle *v = Vehicle::GetIfValid(p1);
	if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;

	CommandCost ret = CheckOwnership(v->owner);
	if (ret.Failed()) return ret;

	if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_VEHICLE_IS_DESTROYED);

	switch (v->type) {
		case VEH_TRAIN:
			if ((v->vehstatus & VS_STOPPED) && Train::From(v)->gcache.cached_power == 0) return_cmd_error(STR_ERROR_TRAIN_START_NO_POWER);
			break;

		case VEH_SHIP:
		case VEH_ROAD:
			break;

		case VEH_AIRCRAFT: {
			Aircraft *a = Aircraft::From(v);
			/* cannot stop airplane when in flight, or when taking off / landing */
			if (!(v->vehstatus & VS_CRASHED) && a->state >= STARTTAKEOFF && a->state < TERM7) return_cmd_error(STR_ERROR_AIRCRAFT_IS_IN_FLIGHT);
			break;
		}

		default: return CMD_ERROR;
	}

	if (HasBit(p2, 0)) {
		/* Check if this vehicle can be started/stopped. Failure means 'allow'. */
		uint16 callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
		StringID error = STR_NULL;
		if (callback != CALLBACK_FAILED) {
			if (v->GetGRF()->grf_version < 8) {
				/* 8 bit result 0xFF means 'allow' */
				if (callback < 0x400 && GB(callback, 0, 8) != 0xFF) error = GetGRFStringID(v->GetGRFID(), 0xD000 + callback);
			} else {
				if (callback < 0x400) {
					error = GetGRFStringID(v->GetGRFID(), 0xD000 + callback);
				} else {
					switch (callback) {
						case 0x400: // allow
							break;

						default: // unknown reason -> disallow
							error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
							break;
					}
				}
			}
		}
		if (error != STR_NULL) return_cmd_error(error);
	}

	if (flags & DC_EXEC) {
		if (v->IsStoppedInDepot() && (flags & DC_AUTOREPLACE) == 0) DeleteVehicleNews(p1, STR_NEWS_TRAIN_IS_WAITING + v->type);

		v->vehstatus ^= VS_STOPPED;
		if (v->type != VEH_TRAIN) v->cur_speed = 0; // trains can stop 'slowly'
		v->MarkDirty();
		SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
		SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
		SetWindowClassesDirty(GetWindowClassForVehicleType(v->type));
	}
	return CommandCost();
}
Пример #25
0
/**
 * Refits a vehicle to the specified cargo type.
 * @param tile unused
 * @param flags type of operation
 * @param p1 vehicle ID to refit
 * @param p2 various bitstuffed elements
 * - p2 = (bit 0-4)   - New cargo type to refit to.
 * - p2 = (bit 6)     - Automatic refitting.
 * - p2 = (bit 7)     - Refit only this vehicle. Used only for cloning vehicles.
 * - p2 = (bit 8-15)  - New cargo subtype to refit to. 0xFF means to try keeping the same subtype according to GetBestFittingSubType().
 * - p2 = (bit 16-23) - Number of vehicles to refit (not counting articulated parts). Zero means all vehicles.
 *                      Only used if "refit only this vehicle" is false.
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	Vehicle *v = Vehicle::GetIfValid(p1);
	if (v == NULL) return CMD_ERROR;

	/* Don't allow disasters and sparks and such to be refitted.
	 * We cannot check for IsPrimaryVehicle as autoreplace also refits in free wagon chains. */
	if (!IsCompanyBuildableVehicleType(v->type)) return CMD_ERROR;

	Vehicle *front = v->First();

	CommandCost ret = CheckOwnership(front->owner);
	if (ret.Failed()) return ret;

	bool auto_refit = HasBit(p2, 6);

	/* Don't allow shadows and such to be refitted. */
	if (v != front && (v->type == VEH_SHIP || v->type == VEH_AIRCRAFT)) return CMD_ERROR;
	/* Allow auto-refitting only during loading and normal refitting only in a depot. */
	if ((!auto_refit || !front->current_order.IsType(OT_LOADING)) && !front->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type);
	if (front->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_VEHICLE_IS_DESTROYED);

	/* Check cargo */
	CargoID new_cid = GB(p2, 0, 5);
	byte new_subtype = GB(p2, 8, 8);
	if (new_cid >= NUM_CARGO) return CMD_ERROR;

	/* For ships and aircrafts there is always only one. */
	bool only_this = HasBit(p2, 7) || front->type == VEH_SHIP || front->type == VEH_AIRCRAFT;
	uint8 num_vehicles = GB(p2, 16, 8);

	CommandCost cost = RefitVehicle(v, only_this, num_vehicles, new_cid, new_subtype, flags, auto_refit);

	if (flags & DC_EXEC) {
		/* Update the cached variables */
		switch (v->type) {
			case VEH_TRAIN:
				Train::From(front)->ConsistChanged(auto_refit);
				break;
			case VEH_ROAD:
				RoadVehUpdateCache(RoadVehicle::From(front), auto_refit);
				if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) RoadVehicle::From(front)->CargoChanged();
				break;

			case VEH_SHIP:
				v->InvalidateNewGRFCacheOfChain();
				v->colourmap = PAL_NONE; // invalidate vehicle colour map
				Ship::From(v)->UpdateCache();
				break;

			case VEH_AIRCRAFT:
				v->InvalidateNewGRFCacheOfChain();
				v->colourmap = PAL_NONE; // invalidate vehicle colour map
				UpdateAircraftCache(Aircraft::From(v), true);
				break;

			default: NOT_REACHED();
		}

		InvalidateWindowData(WC_VEHICLE_DETAILS, front->index);
		SetWindowDirty(WC_VEHICLE_DEPOT, front->tile);
		InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
	} else {
		/* Always invalidate the cache; querycost might have filled it. */
		v->InvalidateNewGRFCacheOfChain();
	}

	return cost;
}
Пример #26
0
/**
 * Change timetable data of an order.
 * @param tile Not used.
 * @param flags Operation to perform.
 * @param p1 Various bitstuffed elements
 * - p1 = (bit  0-19) - Vehicle with the orders to change.
 * - p1 = (bit 20-27) - Order index to modify.
 * - p1 = (bit 28-29) - Timetable data to change (@see ModifyTimetableFlags)
 * @param p2 The amount of time to wait.
 * - p2 = (bit  0-15) - The data to modify as specified by p1 bits 28-29.
 *                      0 to clear times, UINT16_MAX to clear speed limit.
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
    VehicleID veh = GB(p1, 0, 20);

    Vehicle *v = Vehicle::GetIfValid(veh);
    if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;

    CommandCost ret = CheckOwnership(v->owner);
    if (ret.Failed()) return ret;

    VehicleOrderID order_number = GB(p1, 20, 8);
    Order *order = v->GetOrder(order_number);
    if (order == NULL || order->IsType(OT_IMPLICIT)) return CMD_ERROR;

    ModifyTimetableFlags mtf = Extract<ModifyTimetableFlags, 28, 2>(p1);
    if (mtf >= MTF_END) return CMD_ERROR;

    int wait_time   = order->GetWaitTime();
    int travel_time = order->GetTravelTime();
    int max_speed   = order->GetMaxSpeed();
    switch (mtf) {
    case MTF_WAIT_TIME:
        wait_time = GB(p2, 0, 16);
        break;

    case MTF_TRAVEL_TIME:
        travel_time = GB(p2, 0, 16);
        break;

    case MTF_TRAVEL_SPEED:
        max_speed = GB(p2, 0, 16);
        if (max_speed == 0) max_speed = UINT16_MAX; // Disable speed limit.
        break;

    default:
        NOT_REACHED();
    }

    if (wait_time != order->GetWaitTime()) {
        switch (order->GetType()) {
        case OT_GOTO_STATION:
            if (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) return_cmd_error(STR_ERROR_TIMETABLE_NOT_STOPPING_HERE);
            break;

        case OT_CONDITIONAL:
            break;

        default:
            return_cmd_error(STR_ERROR_TIMETABLE_ONLY_WAIT_AT_STATIONS);
        }
    }

    if (travel_time != order->GetTravelTime() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR;
    if (max_speed != order->GetMaxSpeed() && (order->IsType(OT_CONDITIONAL) || v->type == VEH_AIRCRAFT)) return CMD_ERROR;

    if (flags & DC_EXEC) {
        switch (mtf) {
        case MTF_WAIT_TIME:
            /* Set time if changing the value or confirming an estimated time as timetabled. */
            if (wait_time != order->GetWaitTime() || (wait_time > 0 && !order->IsWaitTimetabled())) {
                ChangeTimetable(v, order_number, wait_time, MTF_WAIT_TIME, wait_time > 0);
            }
            break;

        case MTF_TRAVEL_TIME:
            /* Set time if changing the value or confirming an estimated time as timetabled. */
            if (travel_time != order->GetTravelTime() || (travel_time > 0 && !order->IsTravelTimetabled())) {
                ChangeTimetable(v, order_number, travel_time, MTF_TRAVEL_TIME, travel_time > 0);
            }
            break;

        case MTF_TRAVEL_SPEED:
            if (max_speed != order->GetMaxSpeed()) {
                ChangeTimetable(v, order_number, max_speed, MTF_TRAVEL_SPEED, max_speed != UINT16_MAX);
            }
            break;

        default:
            break;
        }
    }

    return CommandCost();
}