31 #include "vehicle_func.h" |
31 #include "vehicle_func.h" |
32 #include "sound_func.h" |
32 #include "sound_func.h" |
33 #include "variables.h" |
33 #include "variables.h" |
34 #include "player_func.h" |
34 #include "player_func.h" |
35 #include "settings_type.h" |
35 #include "settings_type.h" |
|
36 #include "clear_map.h" |
36 |
37 |
37 #include "table/sprites.h" |
38 #include "table/sprites.h" |
38 #include "table/strings.h" |
39 #include "table/strings.h" |
39 |
40 |
40 static Vehicle *FindFloodableVehicleOnTile(TileIndex tile); |
41 /** |
41 static void FloodVehicle(Vehicle *v); |
42 * Describes the behaviour of a tile during flooding. |
|
43 */ |
|
44 enum FloodingBehaviour { |
|
45 FLOOD_NONE, ///< The tile does not flood neighboured tiles. |
|
46 FLOOD_ACTIVE, ///< The tile floods neighboured tiles. |
|
47 FLOOD_PASSIVE, ///< The tile does not actively flood neighboured tiles, but it prevents them from drying up. |
|
48 FLOOD_DRYUP, ///< The tile drys up if it is not constantly flooded from neighboured tiles. |
|
49 }; |
|
50 |
|
51 /** |
|
52 * Describes from which directions a specific slope can be flooded (if the tile is floodable at all). |
|
53 */ |
|
54 static const uint8 _flood_from_dirs[] = { |
|
55 (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE), // SLOPE_FLAT |
|
56 (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_W |
|
57 (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_S |
|
58 (1 << DIR_NE), // SLOPE_SW |
|
59 (1 << DIR_NW) | (1 << DIR_SW), // SLOPE_E |
|
60 0, // SLOPE_EW |
|
61 (1 << DIR_NW), // SLOPE_SE |
|
62 (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_WSE, SLOPE_STEEP_S |
|
63 (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_N |
|
64 (1 << DIR_SE), // SLOPE_NW |
|
65 0, // SLOPE_NS |
|
66 (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_NWS, SLOPE_STEEP_W |
|
67 (1 << DIR_SW), // SLOPE_NE |
|
68 (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_ENW, SLOPE_STEEP_N |
|
69 (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW), // SLOPE_SEN, SLOPE_STEEP_E |
|
70 }; |
|
71 |
|
72 /** |
|
73 * Slopes that contain flat water and not only shore. |
|
74 */ |
|
75 static const uint32 _active_water_slopes = (1 << SLOPE_FLAT) | (1 << SLOPE_W) | (1 << SLOPE_S) | (1 << SLOPE_E) | (1 << SLOPE_N); |
42 |
76 |
43 /** |
77 /** |
44 * Makes a tile canal or water depending on the surroundings. |
78 * Makes a tile canal or water depending on the surroundings. |
45 * This as for example docks and shipdepots do not store |
79 * This as for example docks and shipdepots do not store |
46 * whether the tile used to be canal or 'normal' water. |
80 * whether the tile used to be canal or 'normal' water. |
518 /* Draw canal dikes if there are no river edges to draw. */ |
552 /* Draw canal dikes if there are no river edges to draw. */ |
519 if (edges_base <= 48) edges_base = SPR_CANAL_DIKES_BASE; |
553 if (edges_base <= 48) edges_base = SPR_CANAL_DIKES_BASE; |
520 DrawWaterEdges(edges_base, ti->tile); |
554 DrawWaterEdges(edges_base, ti->tile); |
521 } |
555 } |
522 |
556 |
|
557 void DrawShoreTile(Slope tileh) |
|
558 { |
|
559 /* Converts the enum Slope into an offset based on SPR_SHORE_BASE. |
|
560 * This allows to calculate the proper sprite to display for this Slope */ |
|
561 static const byte tileh_to_shoresprite[32] = { |
|
562 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0, |
|
563 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0, |
|
564 }; |
|
565 |
|
566 assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier. |
|
567 assert(tileh != SLOPE_FLAT); // Shore is never flat |
|
568 |
|
569 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour |
|
570 |
|
571 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE); |
|
572 } |
|
573 |
523 static void DrawTile_Water(TileInfo *ti) |
574 static void DrawTile_Water(TileInfo *ti) |
524 { |
575 { |
525 switch (GetWaterTileType(ti->tile)) { |
576 switch (GetWaterTileType(ti->tile)) { |
526 case WATER_TILE_CLEAR: |
577 case WATER_TILE_CLEAR: |
527 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE); |
578 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE); |
528 if (IsCanal(ti->tile)) DrawCanalWater(ti->tile); |
579 if (IsCanal(ti->tile)) DrawCanalWater(ti->tile); |
529 DrawBridgeMiddle(ti); |
580 DrawBridgeMiddle(ti); |
530 break; |
581 break; |
531 |
582 |
532 case WATER_TILE_COAST: { |
583 case WATER_TILE_COAST: { |
533 /* Converts the enum Slope into an offset based on SPR_SHORE_BASE. |
584 DrawShoreTile(ti->tileh); |
534 * This allows to calculate the proper sprite to display for this Slope */ |
|
535 static const byte tileh_to_shoresprite[32] = { |
|
536 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0, |
|
537 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0, |
|
538 }; |
|
539 |
|
540 assert(!IsSteepSlope(ti->tileh)); |
|
541 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[ti->tileh], PAL_NONE); |
|
542 DrawBridgeMiddle(ti); |
585 DrawBridgeMiddle(ti); |
543 } break; |
586 } break; |
544 |
587 |
545 case WATER_TILE_LOCK: { |
588 case WATER_TILE_LOCK: { |
546 const WaterDrawTileStruct *t = _shiplift_display_seq[GetSection(ti->tile)]; |
589 const WaterDrawTileStruct *t = _shiplift_display_seq[GetSection(ti->tile)]; |
623 static inline void MarkTileDirtyIfCanal(TileIndex tile) |
666 static inline void MarkTileDirtyIfCanal(TileIndex tile) |
624 { |
667 { |
625 if (IsTileType(tile, MP_WATER) && IsCanal(tile)) MarkTileDirtyByTile(tile); |
668 if (IsTileType(tile, MP_WATER) && IsCanal(tile)) MarkTileDirtyByTile(tile); |
626 } |
669 } |
627 |
670 |
628 /** |
|
629 * Floods neighboured floodable tiles |
|
630 * |
|
631 * @param tile The water source tile that causes the flooding. |
|
632 * @param offs[0] Destination tile to flood. |
|
633 * @param offs[1] First corner of edge between source and dest tile. |
|
634 * @param offs[2] Second corder of edge between source and dest tile. |
|
635 * @param offs[3] Third corner of dest tile. |
|
636 * @param offs[4] Fourth corner of dest tile. |
|
637 */ |
|
638 static void TileLoopWaterHelper(TileIndex tile, const TileIndexDiffC *offs) |
|
639 { |
|
640 TileIndex target = TILE_ADD(tile, ToTileIndexDiff(offs[0])); |
|
641 |
|
642 /* type of this tile mustn't be water already. */ |
|
643 if (IsTileType(target, MP_WATER)) return; |
|
644 |
|
645 /* Are both corners of the edge between source and dest on height 0 ? */ |
|
646 if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[1]))) != 0 || |
|
647 TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[2]))) != 0) { |
|
648 return; |
|
649 } |
|
650 |
|
651 bool flooded = false; // Will be set to true, when something is flooded |
|
652 |
|
653 /* Is any corner of the dest tile raised? (First two corners already checked above. */ |
|
654 if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[3]))) != 0 || |
|
655 TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[4]))) != 0) { |
|
656 /* make coast.. */ |
|
657 switch (GetTileType(target)) { |
|
658 case MP_RAILWAY: { |
|
659 if (!IsPlainRailTile(target)) break; |
|
660 |
|
661 flooded = FloodHalftile(target); |
|
662 |
|
663 Vehicle *v = FindFloodableVehicleOnTile(target); |
|
664 if (v != NULL) FloodVehicle(v); |
|
665 |
|
666 break; |
|
667 } |
|
668 |
|
669 case MP_CLEAR: |
|
670 case MP_TREES: |
|
671 _current_player = OWNER_WATER; |
|
672 if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) { |
|
673 flooded = true; |
|
674 MakeShore(target); |
|
675 MarkTileDirtyByTile(target); |
|
676 } |
|
677 break; |
|
678 |
|
679 default: |
|
680 break; |
|
681 } |
|
682 } else { |
|
683 /* Flood vehicles */ |
|
684 _current_player = OWNER_WATER; |
|
685 |
|
686 Vehicle *v = FindFloodableVehicleOnTile(target); |
|
687 if (v != NULL) FloodVehicle(v); |
|
688 |
|
689 /* flood flat tile */ |
|
690 if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) { |
|
691 flooded = true; |
|
692 MakeWater(target); |
|
693 MarkTileDirtyByTile(target); |
|
694 } |
|
695 } |
|
696 |
|
697 if (flooded) { |
|
698 /* Mark surrounding canal tiles dirty too to avoid glitches */ |
|
699 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) { |
|
700 MarkTileDirtyIfCanal(target + TileOffsByDir(dir)); |
|
701 } |
|
702 /* update signals if needed */ |
|
703 UpdateSignalsInBuffer(); |
|
704 } |
|
705 } |
|
706 |
671 |
707 /** |
672 /** |
708 * Finds a vehicle to flood. |
673 * Finds a vehicle to flood. |
709 * It does not find vehicles that are already crashed on bridges, i.e. flooded. |
674 * It does not find vehicles that are already crashed on bridges, i.e. flooded. |
710 * @param tile the tile where to find a vehicle to flood |
675 * @param tile the tile where to find a vehicle to flood |
825 SndPlayVehicleFx(SND_12_EXPLOSION, v); |
790 SndPlayVehicleFx(SND_12_EXPLOSION, v); |
826 } |
791 } |
827 } |
792 } |
828 |
793 |
829 /** |
794 /** |
|
795 * Returns the behaviour of a tile during flooding. |
|
796 * |
|
797 * @return Behaviour of the tile |
|
798 */ |
|
799 static FloodingBehaviour GetFloodingBehaviour(TileIndex tile) |
|
800 { |
|
801 /* FLOOD_ACTIVE: 'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, rail with flooded halftile |
|
802 * FLOOD_DRYUP: coast with more than one corner raised |
|
803 * FLOOD_PASSIVE: oilrig, dock, water-industries |
|
804 * FLOOD_NONE: canals, rivers, everything else |
|
805 */ |
|
806 switch (GetTileType(tile)) { |
|
807 case MP_WATER: |
|
808 if (IsCoast(tile)) { |
|
809 Slope tileh = GetTileSlope(tile, NULL); |
|
810 return (HasBit(_active_water_slopes, tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP); |
|
811 } else { |
|
812 return ((IsSea(tile) || (IsShipDepot(tile) && (GetShipDepotWaterOwner(tile) == OWNER_WATER))) ? FLOOD_ACTIVE : FLOOD_NONE); |
|
813 } |
|
814 |
|
815 case MP_RAILWAY: |
|
816 return ((GetRailGroundType(tile) == RAIL_GROUND_WATER) ? FLOOD_ACTIVE : FLOOD_NONE); |
|
817 |
|
818 case MP_STATION: |
|
819 if (IsSeaBuoyTile(tile)) return FLOOD_ACTIVE; |
|
820 if (IsOilRig(tile) || IsDock(tile)) return FLOOD_PASSIVE; |
|
821 return FLOOD_NONE; |
|
822 |
|
823 case MP_INDUSTRY: |
|
824 return ((GetIndustrySpec(GetIndustryType(tile))->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0 ? FLOOD_PASSIVE : FLOOD_NONE); |
|
825 |
|
826 default: |
|
827 return FLOOD_NONE; |
|
828 } |
|
829 } |
|
830 |
|
831 /** |
|
832 * Floods a tile. |
|
833 */ |
|
834 static void DoFloodTile(TileIndex target) |
|
835 { |
|
836 if (IsTileType(target, MP_WATER)) return; |
|
837 |
|
838 bool flooded = false; // Will be set to true if something is changed. |
|
839 |
|
840 _current_player = OWNER_WATER; |
|
841 |
|
842 if (GetTileSlope(target, NULL) != SLOPE_FLAT) { |
|
843 /* make coast.. */ |
|
844 switch (GetTileType(target)) { |
|
845 case MP_RAILWAY: { |
|
846 if (!IsPlainRailTile(target)) break; |
|
847 |
|
848 flooded = FloodHalftile(target); |
|
849 |
|
850 Vehicle *v = FindFloodableVehicleOnTile(target); |
|
851 if (v != NULL) FloodVehicle(v); |
|
852 |
|
853 break; |
|
854 } |
|
855 |
|
856 case MP_CLEAR: |
|
857 case MP_TREES: |
|
858 if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) { |
|
859 MakeShore(target); |
|
860 MarkTileDirtyByTile(target); |
|
861 flooded = true; |
|
862 } |
|
863 break; |
|
864 |
|
865 default: |
|
866 break; |
|
867 } |
|
868 } else { |
|
869 /* Flood vehicles */ |
|
870 Vehicle *v = FindFloodableVehicleOnTile(target); |
|
871 if (v != NULL) FloodVehicle(v); |
|
872 |
|
873 /* flood flat tile */ |
|
874 if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) { |
|
875 MakeWater(target); |
|
876 MarkTileDirtyByTile(target); |
|
877 flooded = true; |
|
878 } |
|
879 } |
|
880 |
|
881 if (flooded) { |
|
882 /* Mark surrounding canal tiles dirty too to avoid glitches */ |
|
883 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) { |
|
884 MarkTileDirtyIfCanal(target + TileOffsByDir(dir)); |
|
885 } |
|
886 |
|
887 /* update signals if needed */ |
|
888 UpdateSignalsInBuffer(); |
|
889 } |
|
890 |
|
891 _current_player = OWNER_NONE; |
|
892 } |
|
893 |
|
894 /** |
|
895 * Drys a tile up. |
|
896 */ |
|
897 static void DoDryUp(TileIndex tile) |
|
898 { |
|
899 assert(IsTileType(tile, MP_WATER) && IsCoast(tile)); |
|
900 _current_player = OWNER_WATER; |
|
901 |
|
902 if (CmdSucceeded(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) { |
|
903 MakeClear(tile, CLEAR_GRASS, 3); |
|
904 MarkTileDirtyByTile(tile); |
|
905 } |
|
906 |
|
907 _current_player = OWNER_NONE; |
|
908 } |
|
909 |
|
910 /** |
830 * Let a water tile floods its diagonal adjoining tiles |
911 * Let a water tile floods its diagonal adjoining tiles |
831 * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track() |
912 * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track() |
832 * |
913 * |
833 * @param tile the water/shore tile that floods |
914 * @param tile the water/shore tile that floods |
834 */ |
915 */ |
835 void TileLoop_Water(TileIndex tile) |
916 void TileLoop_Water(TileIndex tile) |
836 { |
917 { |
837 static const TileIndexDiffC _tile_loop_offs_array[][5] = { |
918 switch (GetFloodingBehaviour(tile)) { |
838 // tile to mod shore? shore? |
919 case FLOOD_ACTIVE: |
839 {{-1, 0}, {0, 0}, {0, 1}, {-1, 0}, {-1, 1}}, |
920 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) { |
840 {{ 0, 1}, {0, 1}, {1, 1}, { 0, 2}, { 1, 2}}, |
921 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir)); |
841 {{ 1, 0}, {1, 0}, {1, 1}, { 2, 0}, { 2, 1}}, |
922 if (dest == INVALID_TILE) continue; |
842 {{ 0, -1}, {0, 0}, {1, 0}, { 0, -1}, { 1, -1}} |
923 |
843 }; |
924 uint z_dest; |
844 |
925 Slope slope_dest = (Slope)(GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP); |
845 /* Ensure buoys on canal borders do not flood */ |
926 if (z_dest > 0) continue; |
846 if (IsCanalBuoyTile(tile)) return; |
927 |
847 /* Ensure only sea and coast floods, not canals or rivers */ |
928 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue; |
848 if (IsTileType(tile, MP_WATER) && !(IsSea(tile) || IsCoast(tile))) return; |
929 |
849 |
930 DoFloodTile(dest); |
850 /* floods in all four diagonal directions with the exception of the edges */ |
931 } |
851 if (IsInsideMM(TileX(tile), 1, MapSizeX() - 3 + 1) && |
932 break; |
852 IsInsideMM(TileY(tile), 1, MapSizeY() - 3 + 1)) { |
933 |
853 uint i; |
934 case FLOOD_DRYUP: { |
854 |
935 Slope slope_here = (Slope)(GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP); |
855 for (i = 0; i != lengthof(_tile_loop_offs_array); i++) { |
936 uint check_dirs = _flood_from_dirs[slope_here]; |
856 TileLoopWaterHelper(tile, _tile_loop_offs_array[i]); |
937 uint dir; |
857 } |
938 FOR_EACH_SET_BIT(dir, check_dirs) { |
858 } |
939 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir)); |
859 |
940 if (dest == INVALID_TILE) continue; |
860 /* _current_player can be changed by TileLoopWaterHelper.. reset it back here */ |
941 |
861 _current_player = OWNER_NONE; |
942 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest); |
862 |
943 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return; |
863 /* edges */ |
944 } |
864 if (TileX(tile) == 0 && IsInsideMM(TileY(tile), 1, MapSizeY() - 3 + 1)) { //NE |
945 DoDryUp(tile); |
865 TileLoopWaterHelper(tile, _tile_loop_offs_array[2]); |
946 break; |
866 } |
947 } |
867 |
948 |
868 if (TileX(tile) == MapSizeX() - 2 && IsInsideMM(TileY(tile), 1, MapSizeY() - 3 + 1)) { //SW |
949 default: return; |
869 TileLoopWaterHelper(tile, _tile_loop_offs_array[0]); |
950 } |
870 } |
951 } |
871 |
952 |
872 if (TileY(tile) == 0 && IsInsideMM(TileX(tile), 1, MapSizeX() - 3 + 1)) { //NW |
953 void ConvertGroundTilesIntoWaterTiles() |
873 TileLoopWaterHelper(tile, _tile_loop_offs_array[1]); |
954 { |
874 } |
955 TileIndex tile; |
875 |
956 uint z; |
876 if (TileY(tile) == MapSizeY() - 2 && IsInsideMM(TileX(tile), 1, MapSizeX() - 3 + 1)) { //SE |
957 Slope slope; |
877 TileLoopWaterHelper(tile, _tile_loop_offs_array[3]); |
958 |
|
959 for (tile = 0; tile < MapSize(); ++tile) { |
|
960 slope = GetTileSlope(tile, &z); |
|
961 if (IsTileType(tile, MP_CLEAR) && z == 0) { |
|
962 /* Make both water for tiles at level 0 |
|
963 * and make shore, as that looks much better |
|
964 * during the generation. */ |
|
965 switch (slope) { |
|
966 case SLOPE_FLAT: |
|
967 MakeWater(tile); |
|
968 break; |
|
969 |
|
970 case SLOPE_N: |
|
971 case SLOPE_E: |
|
972 case SLOPE_S: |
|
973 case SLOPE_W: |
|
974 MakeShore(tile); |
|
975 break; |
|
976 |
|
977 default: |
|
978 uint check_dirs = _flood_from_dirs[slope & ~SLOPE_STEEP]; |
|
979 uint dir; |
|
980 FOR_EACH_SET_BIT(dir, check_dirs) { |
|
981 TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir)); |
|
982 Slope slope_dest = (Slope)(GetTileSlope(dest, NULL) & ~SLOPE_STEEP); |
|
983 if (HasBit(_active_water_slopes, slope_dest)) { |
|
984 MakeShore(tile); |
|
985 break; |
|
986 } |
|
987 } |
|
988 break; |
|
989 } |
|
990 } |
878 } |
991 } |
879 } |
992 } |
880 |
993 |
881 static uint32 GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode) |
994 static uint32 GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode) |
882 { |
995 { |