tron@2186: /* $Id$ */ tron@2186: truelight@1542: #include "stdafx.h" Darkvater@1891: #include "openttd.h" truelight@1542: truelight@1542: #include "command.h" tron@2163: #include "functions.h" truelight@1542: #include "gfx.h" truelight@1542: #include "map.h" truelight@1542: #include "order.h" tron@3101: #include "rail_map.h" celestar@5573: #include "bridge_map.h" truelight@1542: #include "saveload.h" truelight@1542: #include "station.h" truelight@1542: #include "tile.h" truelight@1542: #include "town.h" truelight@1542: #include "waypoint.h" tron@2159: #include "variables.h" truelight@1542: #include "table/strings.h" celestar@3578: #include "vehicle.h" KUDr@3900: #include "yapf/yapf.h" rubidium@4261: #include "date.h" truelight@1542: truelight@1542: enum { tron@4984: MAX_WAYPOINTS_PER_TOWN = 64, truelight@1542: }; truelight@1542: truelight@1542: /** truelight@1542: * Called if a new block is added to the waypoint-pool truelight@1542: */ truelight@1542: static void WaypointPoolNewBlock(uint start_item) truelight@1542: { truelight@1542: Waypoint *wp; truelight@1542: truelight@4346: /* We don't use FOR_ALL here, because FOR_ALL skips invalid items. truelight@4346: * TODO - This is just a temporary stage, this will be removed. */ tron@4984: for (wp = GetWaypoint(start_item); wp != NULL; wp = (wp->index + 1U < GetWaypointPoolSize()) ? GetWaypoint(wp->index + 1U) : NULL) wp->index = start_item++; truelight@1542: } truelight@1542: matthijs@5216: DEFINE_OLD_POOL(Waypoint, Waypoint, WaypointPoolNewBlock, NULL) truelight@1542: truelight@1542: /* Create a new waypoint */ tron@2752: static Waypoint* AllocateWaypoint(void) truelight@1542: { truelight@1542: Waypoint *wp; truelight@1542: truelight@4346: /* We don't use FOR_ALL here, because FOR_ALL skips invalid items. truelight@4346: * TODO - This is just a temporary stage, this will be removed. */ tron@4984: for (wp = GetWaypoint(0); wp != NULL; wp = (wp->index + 1U < GetWaypointPoolSize()) ? GetWaypoint(wp->index + 1U) : NULL) { truelight@4346: if (!IsValidWaypoint(wp)) { truelight@1542: uint index = wp->index; truelight@1542: tron@4077: memset(wp, 0, sizeof(*wp)); truelight@1542: wp->index = index; truelight@1542: truelight@1542: return wp; truelight@1542: } truelight@1542: } truelight@1542: truelight@1542: /* Check if we can add a block to the pool */ tron@4984: if (AddBlockToPool(&_Waypoint_pool)) return AllocateWaypoint(); truelight@1542: truelight@1542: return NULL; truelight@1542: } truelight@1542: truelight@1542: /* Update the sign for the waypoint */ tron@2817: static void UpdateWaypointSign(Waypoint* wp) truelight@1542: { celestar@3422: Point pt = RemapCoords2(TileX(wp->xy) * TILE_SIZE, TileY(wp->xy) * TILE_SIZE); truelight@1542: SetDParam(0, wp->index); truelight@1542: UpdateViewportSignPos(&wp->sign, pt.x, pt.y - 0x20, STR_WAYPOINT_VIEWPORT); truelight@1542: } truelight@1542: truelight@1542: /* Redraw the sign of a waypoint */ tron@2752: static void RedrawWaypointSign(const Waypoint* wp) truelight@1542: { truelight@1542: MarkAllViewportsDirty( truelight@1542: wp->sign.left - 6, truelight@1542: wp->sign.top, truelight@1542: wp->sign.left + (wp->sign.width_1 << 2) + 12, truelight@1542: wp->sign.top + 48); truelight@1542: } truelight@1542: truelight@1542: /* Update all signs */ truelight@1542: void UpdateAllWaypointSigns(void) truelight@1542: { truelight@1542: Waypoint *wp; truelight@1542: truelight@1542: FOR_ALL_WAYPOINTS(wp) { truelight@4346: UpdateWaypointSign(wp); truelight@1542: } truelight@1542: } truelight@1542: truelight@4390: /* Internal handler to delete a waypoint */ truelight@4390: void DestroyWaypoint(Waypoint *wp) truelight@4390: { tron@4527: RemoveOrderFromAllVehicles(OT_GOTO_WAYPOINT, wp->index); truelight@4390: truelight@4390: if (wp->string != STR_NULL) DeleteName(wp->string); truelight@4390: truelight@4390: RedrawWaypointSign(wp); truelight@4390: } truelight@4390: truelight@1542: /* Set the default name for a waypoint */ tron@2752: static void MakeDefaultWaypointName(Waypoint* wp) truelight@1542: { truelight@1542: Waypoint *local_wp; truelight@1542: bool used_waypoint[MAX_WAYPOINTS_PER_TOWN]; truelight@1542: int i; truelight@1542: truelight@1542: wp->town_index = ClosestTownFromTile(wp->xy, (uint)-1)->index; truelight@1542: truelight@1542: memset(used_waypoint, 0, sizeof(used_waypoint)); truelight@1542: truelight@1542: /* Find an unused waypoint number belonging to this town */ truelight@1542: FOR_ALL_WAYPOINTS(local_wp) { tron@4077: if (wp == local_wp) continue; truelight@1542: truelight@1542: if (local_wp->xy && local_wp->string == STR_NULL && local_wp->town_index == wp->town_index) truelight@1542: used_waypoint[local_wp->town_cn] = true; truelight@1542: } truelight@1542: truelight@1542: /* Find an empty spot */ truelight@1542: for (i = 0; used_waypoint[i] && i < MAX_WAYPOINTS_PER_TOWN; i++) {} truelight@1542: truelight@1542: wp->string = STR_NULL; truelight@1542: wp->town_cn = i; truelight@1542: } truelight@1542: truelight@1542: /* Find a deleted waypoint close to a tile. */ tron@1977: static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile) truelight@1542: { truelight@1542: Waypoint *wp, *best = NULL; tron@4077: uint thres = 8; truelight@1542: truelight@1542: FOR_ALL_WAYPOINTS(wp) { truelight@4346: if (wp->deleted) { tron@4077: uint cur_dist = DistanceManhattan(tile, wp->xy); tron@4077: truelight@1542: if (cur_dist < thres) { truelight@1542: thres = cur_dist; truelight@1542: best = wp; truelight@1542: } truelight@1542: } truelight@1542: } truelight@1542: truelight@1542: return best; truelight@1542: } truelight@1542: peter1138@2670: /** peter1138@2670: * Update waypoint graphics id against saved GRFID/localidx. peter1138@2670: * This is to ensure the chosen graphics are correct if GRF files are changed. peter1138@2670: */ Darkvater@5350: void AfterLoadWaypoints(void) peter1138@2670: { peter1138@2670: Waypoint *wp; peter1138@2670: peter1138@2670: FOR_ALL_WAYPOINTS(wp) { peter1138@2670: uint i; peter1138@2670: peter1138@2670: if (wp->grfid == 0) continue; peter1138@2670: peter1138@2670: for (i = 0; i < GetNumCustomStations(STAT_CLASS_WAYP); i++) { belugas@3676: const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, i); belugas@3676: if (statspec != NULL && statspec->grfid == wp->grfid && statspec->localidx == wp->localidx) { peter1138@2670: wp->stat_id = i; peter1138@2670: break; peter1138@2670: } peter1138@2670: } peter1138@2670: } peter1138@2670: } peter1138@2670: Darkvater@1782: /** Convert existing rail to waypoint. Eg build a waypoint station over Darkvater@1782: * piece of rail tron@3491: * @param tile tile where waypoint will be built peter1138@2625: * @param p1 graphics for waypoint type, 0 indicates standard graphics Darkvater@1782: * @param p2 unused celestar@2085: * celestar@2085: * @todo When checking for the tile slope, celestar@2085: * distingush between "Flat land required" and "land sloped in wrong direction" Darkvater@1782: */ tron@3491: int32 CmdBuildTrainWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@1542: { truelight@1542: Waypoint *wp; tron@3636: Slope tileh; tron@3101: Axis axis; truelight@1542: truelight@1542: SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); truelight@1542: Darkvater@1782: /* if custom gfx are used, make sure it is within bounds */ peter1138@2625: if (p1 >= GetNumCustomStations(STAT_CLASS_WAYP)) return CMD_ERROR; Darkvater@1782: tron@3267: if (!IsTileType(tile, MP_RAILWAY) || rubidium@3792: GetRailTileType(tile) != RAIL_TILE_NORMAL || ( tron@3267: (axis = AXIS_X, GetTrackBits(tile) != TRACK_BIT_X) && tron@3267: (axis = AXIS_Y, GetTrackBits(tile) != TRACK_BIT_Y) tron@3101: )) { truelight@1542: return_cmd_error(STR_1005_NO_SUITABLE_RAILROAD_TRACK); tron@3101: } truelight@1542: tron@4077: if (!CheckTileOwnership(tile)) return CMD_ERROR; truelight@1542: if (!EnsureNoVehicle(tile)) return CMD_ERROR; truelight@1542: truelight@1542: tileh = GetTileSlope(tile, NULL); tron@4077: if (tileh != SLOPE_FLAT && tron@4077: (!_patches.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) { tron@4077: return_cmd_error(STR_0007_FLAT_LAND_REQUIRED); truelight@1542: } truelight@1542: celestar@5573: if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST); celestar@5573: truelight@1542: /* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */ truelight@1542: wp = FindDeletedWaypointCloseTo(tile); truelight@1542: if (wp == NULL) { truelight@1542: wp = AllocateWaypoint(); Darkvater@1782: if (wp == NULL) return CMD_ERROR; truelight@1542: truelight@1542: wp->town_index = 0; truelight@1542: wp->string = STR_NULL; truelight@1542: wp->town_cn = 0; truelight@1542: } truelight@1542: truelight@1542: if (flags & DC_EXEC) { tron@4051: const StationSpec* statspec; tron@4051: tron@3242: MakeRailWaypoint(tile, GetTileOwner(tile), axis, GetRailType(tile), wp->index); tron@3101: MarkTileDirtyByTile(tile); peter1138@2670: tron@4051: statspec = GetCustomStationSpec(STAT_CLASS_WAYP, p1); peter1138@2670: belugas@3676: if (statspec != NULL) { tron@4051: wp->stat_id = p1; belugas@3676: wp->grfid = statspec->grfid; belugas@3676: wp->localidx = statspec->localidx; peter1138@2670: } else { peter1138@2670: // Specified custom graphics do not exist, so use default. peter1138@2670: wp->stat_id = 0; peter1138@2670: wp->grfid = 0; peter1138@2670: wp->localidx = 0; truelight@1542: } peter1138@2670: truelight@1542: wp->deleted = 0; truelight@1542: wp->xy = tile; truelight@1542: wp->build_date = _date; truelight@1542: tron@4000: if (wp->town_index == 0) MakeDefaultWaypointName(wp); truelight@1542: truelight@1542: UpdateWaypointSign(wp); truelight@1542: RedrawWaypointSign(wp); KUDr@5348: YapfNotifyTrackLayoutChange(tile, AxisToTrack(axis)); truelight@1542: } truelight@1542: truelight@1542: return _price.build_train_depot; truelight@1542: } truelight@1542: truelight@1542: /* Daily loop for waypoints */ truelight@1542: void WaypointsDailyLoop(void) truelight@1542: { truelight@1542: Waypoint *wp; truelight@1542: truelight@1542: /* Check if we need to delete a waypoint */ truelight@1542: FOR_ALL_WAYPOINTS(wp) { truelight@4500: if (wp->deleted != 0 && --wp->deleted == 0) DeleteWaypoint(wp); truelight@1542: } truelight@1542: } truelight@1542: truelight@1542: /* Remove a waypoint */ tron@1977: int32 RemoveTrainWaypoint(TileIndex tile, uint32 flags, bool justremove) truelight@1542: { truelight@1542: Waypoint *wp; truelight@1542: truelight@1542: /* Make sure it's a waypoint */ tron@4077: if (!IsTileType(tile, MP_RAILWAY) || tron@4077: !IsRailWaypoint(tile) || tron@4077: (!CheckTileOwnership(tile) && _current_player != OWNER_WATER) || tron@4077: !EnsureNoVehicle(tile)) { truelight@1542: return CMD_ERROR; tron@4077: } truelight@1542: truelight@1542: if (flags & DC_EXEC) { KUDr@5348: Track track = GetRailWaypointTrack(tile); truelight@1542: wp = GetWaypointByTile(tile); truelight@1542: truelight@1542: wp->deleted = 30; // let it live for this many days before we do the actual deletion. truelight@1542: RedrawWaypointSign(wp); truelight@1542: truelight@1542: if (justremove) { tron@3242: MakeRailNormal(tile, GetTileOwner(tile), GetRailWaypointBits(tile), GetRailType(tile)); tron@3101: MarkTileDirtyByTile(tile); truelight@1542: } else { truelight@1542: DoClearSquare(tile); KUDr@5348: SetSignalsOnBothDir(tile, track); truelight@1542: } KUDr@5348: YapfNotifyTrackLayoutChange(tile, track); truelight@1542: } truelight@1542: truelight@1542: return _price.remove_train_depot; truelight@1542: } truelight@1542: Darkvater@1782: /** Delete a waypoint tron@3491: * @param tile tile where waypoint is to be deleted Darkvater@1782: * @param p1 unused Darkvater@1782: * @param p2 unused Darkvater@1782: */ tron@3491: int32 CmdRemoveTrainWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@1542: { truelight@1542: SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); truelight@1542: return RemoveTrainWaypoint(tile, flags, true); truelight@1542: } truelight@1542: Darkvater@1782: /** Rename a waypoint. tron@3491: * @param tile unused Darkvater@1782: * @param p1 id of waypoint Darkvater@1782: * @param p2 unused Darkvater@1782: */ tron@3491: int32 CmdRenameWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@1542: { truelight@1542: Waypoint *wp; truelight@1542: truelight@4352: if (!IsValidWaypointID(p1)) return CMD_ERROR; Darkvater@1782: tron@1820: if (_cmd_text[0] != '\0') { tron@4077: StringID str = AllocateNameUnique(_cmd_text, 0); tron@4077: tron@4077: if (str == 0) return CMD_ERROR; truelight@1542: truelight@1542: if (flags & DC_EXEC) { truelight@1542: wp = GetWaypoint(p1); tron@4077: if (wp->string != STR_NULL) DeleteName(wp->string); truelight@1542: truelight@1542: wp->string = str; truelight@1542: wp->town_cn = 0; truelight@1542: truelight@1542: UpdateWaypointSign(wp); truelight@1542: MarkWholeScreenDirty(); truelight@1542: } else { truelight@1542: DeleteName(str); truelight@1542: } tron@2026: } else { truelight@1542: if (flags & DC_EXEC) { truelight@1542: wp = GetWaypoint(p1); tron@4077: if (wp->string != STR_NULL) DeleteName(wp->string); truelight@1542: truelight@1542: MakeDefaultWaypointName(wp); truelight@1542: UpdateWaypointSign(wp); truelight@1542: MarkWholeScreenDirty(); truelight@1542: } truelight@1542: } truelight@1542: return 0; truelight@1542: } truelight@1542: truelight@1542: /* This hacks together some dummy one-shot Station structure for a waypoint. */ tron@1977: Station *ComposeWaypointStation(TileIndex tile) truelight@1542: { truelight@1542: Waypoint *wp = GetWaypointByTile(tile); truelight@1542: static Station stat; truelight@1542: truelight@1542: stat.train_tile = stat.xy = wp->xy; truelight@1542: stat.town = GetTown(wp->town_index); truelight@1542: stat.string_id = wp->string == STR_NULL ? /* FIXME? */ 0 : wp->string; truelight@1542: stat.build_date = wp->build_date; truelight@1542: truelight@1542: return &stat; truelight@1542: } truelight@1542: truelight@1542: /* Draw a waypoint */ tron@2520: void DrawWaypointSprite(int x, int y, int stat_id, RailType railtype) truelight@1542: { truelight@1542: x += 33; truelight@1542: y += 17; truelight@1542: peter1138@3764: if (!DrawStationTile(x, y, railtype, AXIS_X, STAT_CLASS_WAYP, stat_id)) { ludde@2261: DrawDefaultWaypointSprite(x, y, railtype); truelight@1542: } truelight@1542: } truelight@1542: truelight@1542: /* Fix savegames which stored waypoints in their old format */ truelight@1542: void FixOldWaypoints(void) truelight@1542: { truelight@1542: Waypoint *wp; truelight@1542: truelight@1542: /* Convert the old 'town_or_string', to 'string' / 'town' / 'town_cn' */ truelight@1542: FOR_ALL_WAYPOINTS(wp) { truelight@1542: wp->town_index = ClosestTownFromTile(wp->xy, (uint)-1)->index; truelight@1542: wp->town_cn = 0; truelight@1542: if (wp->string & 0xC000) { truelight@1542: wp->town_cn = wp->string & 0x3F; truelight@1542: wp->string = STR_NULL; truelight@1542: } truelight@1542: } truelight@1542: } truelight@1542: truelight@1542: void InitializeWaypoints(void) truelight@1542: { tron@4984: CleanPool(&_Waypoint_pool); tron@4984: AddBlockToPool(&_Waypoint_pool); truelight@1542: } truelight@1542: Darkvater@1881: static const SaveLoad _waypoint_desc[] = { rubidium@4344: SLE_CONDVAR(Waypoint, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), rubidium@4344: SLE_CONDVAR(Waypoint, xy, SLE_UINT32, 6, SL_MAX_VERSION), rubidium@4344: SLE_CONDVAR(Waypoint, town_index, SLE_UINT16, 12, SL_MAX_VERSION), rubidium@4344: SLE_CONDVAR(Waypoint, town_cn, SLE_UINT8, 12, SL_MAX_VERSION), rubidium@4344: SLE_VAR(Waypoint, string, SLE_UINT16), rubidium@4344: SLE_VAR(Waypoint, deleted, SLE_UINT8), truelight@1542: rubidium@4344: SLE_CONDVAR(Waypoint, build_date, SLE_FILE_U16 | SLE_VAR_I32, 3, 30), rubidium@4344: SLE_CONDVAR(Waypoint, build_date, SLE_INT32, 31, SL_MAX_VERSION), rubidium@4344: SLE_CONDVAR(Waypoint, localidx, SLE_UINT8, 3, SL_MAX_VERSION), rubidium@4344: SLE_CONDVAR(Waypoint, grfid, SLE_UINT32, 17, SL_MAX_VERSION), truelight@1542: truelight@1542: SLE_END() truelight@1542: }; truelight@1542: truelight@1542: static void Save_WAYP(void) truelight@1542: { truelight@1542: Waypoint *wp; truelight@1542: truelight@1542: FOR_ALL_WAYPOINTS(wp) { truelight@4346: SlSetArrayIndex(wp->index); truelight@4346: SlObject(wp, _waypoint_desc); truelight@1542: } truelight@1542: } truelight@1542: truelight@1542: static void Load_WAYP(void) truelight@1542: { truelight@1542: int index; truelight@1542: truelight@1542: while ((index = SlIterateArray()) != -1) { truelight@1542: Waypoint *wp; truelight@1542: tron@4984: if (!AddBlockIfNeeded(&_Waypoint_pool, index)) truelight@1542: error("Waypoints: failed loading savegame: too many waypoints"); truelight@1542: truelight@1542: wp = GetWaypoint(index); truelight@1542: SlObject(wp, _waypoint_desc); truelight@1542: } truelight@1542: } truelight@1542: truelight@1542: const ChunkHandler _waypoint_chunk_handlers[] = { truelight@1542: { 'CHKP', Save_WAYP, Load_WAYP, CH_ARRAY | CH_LAST}, truelight@1542: };