コード例 #1
0
ファイル: npf.cpp プロジェクト: andrew889/OpenTTD
/**
 * Finds out if a given company's vehicles are allowed to enter a given tile.
 * @param owner    The owner of the vehicle.
 * @param tile     The tile that is about to be entered.
 * @param enterdir The direction in which the vehicle wants to enter the tile.
 * @return         true if the vehicle can enter the tile.
 * @todo           This function should be used in other places than just NPF,
 *                 maybe moved to another file too.
 */
static bool CanEnterTileOwnerCheck(Owner owner, TileIndex tile, DiagDirection enterdir)
{
	if (IsTileType(tile, MP_RAILWAY) || // Rail tile (also rail depot)
			HasStationTileRail(tile) ||     // Rail station tile/waypoint
			IsRoadDepotTile(tile) ||        // Road depot tile
			IsStandardRoadStopTile(tile)) { // Road station tile (but not drive-through stops)
		return IsTileOwner(tile, owner);  // You need to own these tiles entirely to use them
	}

	switch (GetTileType(tile)) {
		case MP_ROAD:
			/* rail-road crossing : are we looking at the railway part? */
			if (IsLevelCrossing(tile) &&
					DiagDirToAxis(enterdir) != GetCrossingRoadAxis(tile)) {
				return IsTileOwner(tile, owner); // Railway needs owner check, while the street is public
			}
			break;

		case MP_TUNNELBRIDGE:
			if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) {
				return IsTileOwner(tile, owner);
			}
			break;

		default:
			break;
	}

	return true; // no need to check
}
コード例 #2
0
ファイル: ship_cmd.cpp プロジェクト: IchiroWang/OpenTTD
static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
{
	/* Find the closest depot */
	const Depot *depot;
	const Depot *best_depot = NULL;
	/* If we don't have a maximum distance, i.e. distance = 0,
	 * we want to find any depot so the best distance of no
	 * depot must be more than any correct distance. On the
	 * other hand if we have set a maximum distance, any depot
	 * further away than max_distance can safely be ignored. */
	uint best_dist = max_distance == 0 ? UINT_MAX : max_distance + 1;

	FOR_ALL_DEPOTS(depot) {
		TileIndex tile = depot->xy;
		if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
			uint dist = DistanceManhattan(tile, v->tile);
			if (dist < best_dist) {
				best_dist = dist;
				best_depot = depot;
			}
		}
	}

	return best_depot;
}
コード例 #3
0
void CopyPastePlaceRailWaypoint(GenericTileIndex tile, StationID sid, Axis axis, RailType rt, StationGfx gfx, StationClassID stat_class, byte stat_type, int specindex, bool adjacent)
{
	if (IsMainMapTile(tile)) {
		TileIndex t = AsMainMapTile(tile);
		/* check if required track is already there, try to build one if not */
		if (!IsTileOwner(t, _current_company) ||
				(!IsRailWaypointTile(tile) && !IsPlainRailTile(tile)) ||
				GetRailType(t) != rt ||
				(IsTileType(t, MP_STATION) ? GetRailStationAxis(tile) != axis : !HasBit(GetTrackBits(t), AxisToTrack(axis)))) {
			CopyPastePlaceTracks(tile, rt, AxisToTrackBits(axis));
			if (_current_pasting->last_result.Failed()) return;
		}
		/* build the waypoint */
		_station_cmd_specindex_to_paste = specindex;
		uint32 p1 = 0;
		SB(p1, 0, 4, rt);
		SB(p1, 4, 1, axis);
		SB(p1, 8, 8, 1);  // width
		SB(p1, 16, 8, 1); // height
		SB(p1, 24, 1, adjacent);
		uint32 p2 = 0;
		SB(p2, 0, 8, stat_class);
		SB(p2, 8, 8, stat_type);
		SB(p2, 16, 16, sid);
		_current_pasting->DoCommand(t, p1, p2, CMD_BUILD_RAIL_WAYPOINT | CMD_MSG(STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT));
	} else {
		MakeRailWaypoint(tile, OWNER_NONE, sid, axis, gfx & ~1, rt);
		assert(IsInsideMM(specindex, 0, MAX_UVALUE(byte) + 1));
		SetCustomStationSpecIndex(tile, (byte)specindex);
		_clipboard_stations_builder.AddRailPart(sid, stat_class, stat_type, (byte)specindex);
	}
}
コード例 #4
0
/**
 * Build a piece of canal.
 * @param tile end tile of stretch-dragging
 * @param flags type of operation
 * @param p1 start tile of stretch-dragging
 * @param p2 waterclass to build. sea and river can only be built in scenario editor
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	WaterClass wc = Extract<WaterClass, 0, 2>(p2);
	if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR;

	/* Outside of the editor you can only build canals, not oceans */
	if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;

	TileArea ta(tile, p1);

	/* Outside the editor you can only drag canals, and not areas */
	if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR;

	CommandCost cost(EXPENSES_CONSTRUCTION);
	TILE_AREA_LOOP(tile, ta) {
		CommandCost ret;

		Slope slope = GetTileSlope(tile, NULL);
		if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) {
			return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
		}

		/* can't make water of water! */
		if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue;

		ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
		if (ret.Failed()) return ret;
		cost.AddCost(ret);

		if (flags & DC_EXEC) {
			switch (wc) {
				case WATER_CLASS_RIVER:
					MakeRiver(tile, Random());
					break;

				case WATER_CLASS_SEA:
					if (TileHeight(tile) == 0) {
						MakeSea(tile);
						break;
					}
					/* FALL THROUGH */

				default:
					MakeCanal(tile, _current_company, Random());
					break;
			}
			MarkTileDirtyByTile(tile);
			MarkCanalsAndRiversAroundDirty(tile);
		}

		cost.AddCost(_price[PR_BUILD_CANAL]);
	}
コード例 #5
0
ファイル: vehicle_cmd.cpp プロジェクト: Voxar/OpenTTD
/** Autoreplace all vehicles in the depot
 * Note: this command can make incorrect cost estimations
 * Luckily the final price can only drop, not increase. This is due to the fact that
 * estimation can't predict wagon removal so it presumes worst case which is no income from selling wagons.
 * @param tile Tile of the depot where the vehicles are
 * @param flags type of operation
 * @param p1 Type of vehicle
 * @param p2 If bit 0 is set, then either replace all or nothing (instead of replacing until money runs out)
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdDepotMassAutoReplace(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
    VehicleList list;
    CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES);
    VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
    bool all_or_nothing = HasBit(p2, 0);

    if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;

    /* Get the list of vehicles in the depot */
    BuildDepotVehicleList(vehicle_type, tile, &list, &list, true);

    bool did_something = false;

    for (uint i = 0; i < list.Length(); i++) {
        const Vehicle *v = list[i];

        /* Ensure that the vehicle completely in the depot */
        if (!v->IsInDepot()) continue;

        CommandCost ret = DoCommand(0, v->index, 0, flags, CMD_AUTOREPLACE_VEHICLE);

        if (CmdSucceeded(ret)) {
            did_something = true;
            cost.AddCost(ret);
        } else {
            if (ret.GetErrorMessage() != STR_ERROR_AUTOREPLACE_NOTHING_TO_DO && all_or_nothing) {
                /* We failed to replace a vehicle even though we set all or nothing.
                 * We should never reach this if DC_EXEC is set since then it should
                 * have failed the estimation guess. */
                assert(!(flags & DC_EXEC));
                /* Now we will have to return an error. */
                return CMD_ERROR;
            }
        }
    }

    if (!did_something) {
        /* Either we didn't replace anything or something went wrong.
         * Either way we want to return an error and not execute this command. */
        cost = CMD_ERROR;
    }

    return cost;
}
コード例 #6
0
/**
 * Actually build the object.
 * @param type  The type of object to build.
 * @param tile  The tile to build the northern tile of the object on.
 * @param owner The owner of the object.
 * @param town  Town the tile is related with.
 * @param view  The view for the object.
 * @pre All preconditions for building the object at that location
 *      are met, e.g. slope and clearness of tiles are checked.
 */
void BuildObject(ObjectType type, TileIndex tile, CompanyID owner, Town *town, uint8 view)
{
	const ObjectSpec *spec = ObjectSpec::Get(type);

	TileArea ta(tile, GB(spec->size, HasBit(view, 0) ? 4 : 0, 4), GB(spec->size, HasBit(view, 0) ? 0 : 4, 4));
	Object *o = new Object();
	o->type          = type;
	o->location      = ta;
	o->town          = town == NULL ? CalcClosestTownFromTile(tile) : town;
	o->build_date    = _date;
	o->view          = view;

	/* If nothing owns the object, the colour will be random. Otherwise
	 * get the colour from the company's livery settings. */
	if (owner == OWNER_NONE) {
		o->colour = Random();
	} else {
		const Livery *l = Company::Get(owner)->livery;
		o->colour = l->colour1 + l->colour2 * 16;
	}

	/* If the object wants only one colour, then give it that colour. */
	if ((spec->flags & OBJECT_FLAG_2CC_COLOUR) == 0) o->colour &= 0xF;

	if (HasBit(spec->callback_mask, CBM_OBJ_COLOUR)) {
		uint16 res = GetObjectCallback(CBID_OBJECT_COLOUR, o->colour, 0, spec, o, tile);
		if (res != CALLBACK_FAILED) {
			if (res >= 0x100) ErrorUnknownCallbackResult(spec->grf_prop.grffile->grfid, CBID_OBJECT_COLOUR, res);
			o->colour = GB(res, 0, 8);
		}
	}

	assert(o->town != NULL);

	TILE_AREA_LOOP(t, ta) {
		WaterClass wc = (IsWaterTile(t) ? GetWaterClass(t) : WATER_CLASS_INVALID);
		/* Update company infrastructure counts for objects build on canals owned by nobody. */
		if (wc == WATER_CLASS_CANAL && owner != OWNER_NONE && (IsTileOwner(tile, OWNER_NONE) || IsTileOwner(tile, OWNER_WATER))) {
			Company::Get(owner)->infrastructure.water++;
			DirtyCompanyInfrastructureWindows(owner);
		}
		MakeObject(t, owner, o->index, wc, Random());
		MarkTileDirtyByTile(t);
	}
コード例 #7
0
ファイル: water_cmd.cpp プロジェクト: Voxar/OpenTTD
/** Build a piece of canal.
 * @param tile end tile of stretch-dragging
 * @param flags type of operation
 * @param p1 start tile of stretch-dragging
 * @param p2 specifies canal (0), water (1) or river (2); last two can only be built in scenario editor
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	CommandCost cost(EXPENSES_CONSTRUCTION);

	if (p1 >= MapSize()) return CMD_ERROR;

	/* Outside of the editor you can only build canals, not oceans */
	if (p2 != 0 && _game_mode != GM_EDITOR) return CMD_ERROR;

	TileArea ta(tile, p1);

	/* Outside the editor you can only drag canals, and not areas */
	if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR;

	TILE_AREA_LOOP(tile, ta) {
		CommandCost ret;

		Slope slope = GetTileSlope(tile, NULL);
		if (slope != SLOPE_FLAT && (p2 != 2 || !IsInclinedSlope(slope))) {
			return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
		}

		/* can't make water of water! */
		if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || p2 == 1)) continue;

		ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
		if (CmdFailed(ret)) return ret;
		cost.AddCost(ret);

		if (flags & DC_EXEC) {
			if (TileHeight(tile) == 0 && p2 == 1) {
				MakeSea(tile);
			} else if (p2 == 2) {
				MakeRiver(tile, Random());
			} else {
				MakeCanal(tile, _current_company, Random());
			}
			MarkTileDirtyByTile(tile);
			MarkCanalsAndRiversAroundDirty(tile);
		}

		cost.AddCost(_price[PR_CLEAR_WATER]);
	}
コード例 #8
0
/**
 * Autoreplace all vehicles in the depot
 * @param tile Tile of the depot where the vehicles are
 * @param flags type of operation
 * @param p1 Type of vehicle
 * @param p2 unused
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdDepotMassAutoReplace(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	VehicleList list;
	CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES);
	VehicleType vehicle_type = Extract<VehicleType, 0, 3>(p1);

	if (!IsCompanyBuildableVehicleType(vehicle_type)) return CMD_ERROR;
	if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;

	/* Get the list of vehicles in the depot */
	BuildDepotVehicleList(vehicle_type, tile, &list, &list, true);

	for (uint i = 0; i < list.Length(); i++) {
		const Vehicle *v = list[i];

		/* Ensure that the vehicle completely in the depot */
		if (!v->IsChainInDepot()) continue;

		CommandCost ret = DoCommand(0, v->index, 0, flags, CMD_AUTOREPLACE_VEHICLE);

		if (ret.Succeeded()) cost.AddCost(ret);
	}
	return cost;
}
コード例 #9
0
/**
 * Build a vehicle.
 * @param tile tile of depot where the vehicle is built
 * @param flags for command
 * @param p1 various bitstuffed data
 *  bits  0-15: vehicle type being built.
 *  bits 16-31: vehicle type specific bits passed on to the vehicle build functions.
 * @param p2 User
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdBuildVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	/* Elementary check for valid location. */
	if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;

	VehicleType type;
	switch (GetTileType(tile)) {
		case MP_RAILWAY: type = VEH_TRAIN;    break;
		case MP_ROAD:    type = VEH_ROAD;     break;
		case MP_WATER:   type = VEH_SHIP;     break;
		case MP_STATION: type = VEH_AIRCRAFT; break;
		default: NOT_REACHED(); // Safe due to IsDepotTile()
	}

	/* Validate the engine type. */
	EngineID eid = GB(p1, 0, 16);
	if (!IsEngineBuildable(eid, type, _current_company)) return_cmd_error(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + type);

	const Engine *e = Engine::Get(eid);
	CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());

	/* Engines without valid cargo should not be available */
	if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;

	/* Check whether the number of vehicles we need to build can be built according to pool space. */
	uint num_vehicles;
	switch (type) {
		case VEH_TRAIN:    num_vehicles = (e->u.rail.railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + CountArticulatedParts(eid, false); break;
		case VEH_ROAD:     num_vehicles = 1 + CountArticulatedParts(eid, false); break;
		case VEH_SHIP:     num_vehicles = 1; break;
		case VEH_AIRCRAFT: num_vehicles = e->u.air.subtype & AIR_CTOL ? 2 : 3; break;
		default: NOT_REACHED(); // Safe due to IsDepotTile()
	}
	if (!Vehicle::CanAllocateItem(num_vehicles)) return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);

	/* Check whether we can allocate a unit number. Autoreplace does not allocate
	 * an unit number as it will (always) reuse the one of the replaced vehicle
	 * and (train) wagons don't have an unit number in any scenario. */
	UnitID unit_num = (flags & DC_AUTOREPLACE || (type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON)) ? 0 : GetFreeUnitNumber(type);
	if (unit_num == UINT16_MAX) return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);

	Vehicle *v;
	switch (type) {
		case VEH_TRAIN:    value.AddCost(CmdBuildRailVehicle(tile, flags, e, GB(p1, 16, 16), &v)); break;
		case VEH_ROAD:     value.AddCost(CmdBuildRoadVehicle(tile, flags, e, GB(p1, 16, 16), &v)); break;
		case VEH_SHIP:     value.AddCost(CmdBuildShip       (tile, flags, e, GB(p1, 16, 16), &v)); break;
		case VEH_AIRCRAFT: value.AddCost(CmdBuildAircraft   (tile, flags, e, GB(p1, 16, 16), &v)); break;
		default: NOT_REACHED(); // Safe due to IsDepotTile()
	}

	if (value.Succeeded() && flags & DC_EXEC) {
		v->unitnumber = unit_num;
		v->value      = value.GetCost();

		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
		InvalidateWindowClassesData(GetWindowClassForVehicleType(type), 0);
		SetWindowDirty(WC_COMPANY, _current_company);
		if (IsLocalCompany()) {
			InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the auto replace window (must be called before incrementing num_engines)
		}

		GroupStatistics::CountEngine(v, 1);
		GroupStatistics::UpdateAutoreplace(_current_company);

		if (v->IsPrimaryVehicle()) {
			GroupStatistics::CountVehicle(v, 1);
			OrderBackup::Restore(v, p2);
		}
	}

	return value;
}
コード例 #10
0
ファイル: ship_cmd.cpp プロジェクト: andrew889/OpenTTD
/** Build a ship.
 * @param tile tile of depot where ship is built
 * @param flags type of operation
 * @param p1 ship type being built (engine)
 * @param p2 unused
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	UnitID unit_num;

	if (!IsEngineBuildable(p1, VEH_SHIP, _current_company)) return_cmd_error(STR_ERROR_SHIP_NOT_AVAILABLE);

	const Engine *e = Engine::Get(p1);
	CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());

	/* Engines without valid cargo should not be available */
	if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;

	if (flags & DC_QUERY_COST) return value;

	/* The ai_new queries the vehicle cost before building the route,
	 * so we must check against cheaters no sooner than now. --pasky */
	if (!IsShipDepotTile(tile)) return CMD_ERROR;
	if (!IsTileOwner(tile, _current_company)) return CMD_ERROR;

	unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_SHIP);

	if (!Vehicle::CanAllocateItem() || unit_num > _settings_game.vehicle.max_ships)
		return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);

	if (flags & DC_EXEC) {
		int x;
		int y;

		const ShipVehicleInfo *svi = &e->u.ship;

		Ship *v = new Ship();
		v->unitnumber = unit_num;

		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->value = value.GetCost();

		v->last_station_visited = INVALID_STATION;
		v->max_speed = svi->max_speed;
		v->engine_type = p1;

		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();

		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);

		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
		InvalidateWindowClassesData(WC_SHIPS_LIST, 0);
		SetWindowDirty(WC_COMPANY, v->owner);
		if (IsLocalCompany()) {
			InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Ship window
		}

		Company::Get(_current_company)->num_engines[p1]++;
	}

	return value;
}