/** * 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 }
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; }
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); } }
/** * 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]); }
/** 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; }
/** * 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); }
/** 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]); }
/** * 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; }
/** * 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; }
/** 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; }