diff options
Diffstat (limited to 'water_cmd.c')
-rw-r--r-- | water_cmd.c | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/water_cmd.c b/water_cmd.c new file mode 100644 index 000000000..14d99d716 --- /dev/null +++ b/water_cmd.c @@ -0,0 +1,612 @@ +#include "stdafx.h" +#include "ttd.h" +#include "vehicle.h" +#include "viewport.h" +#include "command.h" +#include "town.h" + +bool IsShipDepotTile(TileIndex tile) +{ + return IS_TILETYPE(tile, MP_WATER) && (_map5[tile]&~3) == 0x80; +} + +bool IsClearWaterTile(uint tile) +{ + TileInfo ti; + FindLandscapeHeightByTile(&ti, tile); + return (ti.type == MP_WATER && ti.tileh == 0 && ti.map5 == 0); +} + +/* Build a ship depot + * p1 - direction + */ + +int32 CmdBuildShipDepot(int x, int y, uint32 flags, uint32 p1, uint32 p2) +{ + uint tile, tile2; + + int32 cost, ret; + Depot *dep; + + SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); + + tile = TILE_FROM_XY(x,y); + if (!EnsureNoVehicle(tile)) + return CMD_ERROR; + + tile2 = tile + (p1 ? TILE_XY(0,1) : TILE_XY(1,0)); + if (!EnsureNoVehicle(tile2)) + return CMD_ERROR; + + if (!IsClearWaterTile(tile) || !IsClearWaterTile(tile2)) + return_cmd_error(STR_3801_MUST_BE_BUILT_ON_WATER); + + ret = DoCommandByTile(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (ret == CMD_ERROR) return CMD_ERROR; + ret = DoCommandByTile(tile2, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (ret == CMD_ERROR) + return CMD_ERROR; + + // pretend that we're not making land from the water even though we actually are. + cost = 0; + + dep = AllocateDepot(); + if (dep == NULL) + return CMD_ERROR; + + if (flags & DC_EXEC) { + dep->xy = tile; + _last_built_ship_depot_tile = tile; + dep->town_index = ClosestTownFromTile(tile, (uint)-1)->index; + + ModifyTile(tile, + MP_SETTYPE(MP_WATER) | MP_MAPOWNER_CURRENT | MP_MAP5 | MP_MAP2_CLEAR | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR, + (0x80 + p1*2) + ); + + ModifyTile(tile2, + MP_SETTYPE(MP_WATER) | MP_MAPOWNER_CURRENT | MP_MAP5 | MP_MAP2_CLEAR | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR, + (0x81 + p1*2) + ); + } + + return cost + _price.build_ship_depot; +} + +static int32 RemoveShipDepot(uint tile, uint32 flags) +{ + uint tile2; + + if (!CheckTileOwnership(tile)) + return CMD_ERROR; + + if (!EnsureNoVehicle(tile)) + return CMD_ERROR; + + tile2 = tile + ((_map5[tile] & 2) ? TILE_XY(0,1) : TILE_XY(1,0)); + + if (!EnsureNoVehicle(tile2)) + return CMD_ERROR; + + if (flags & DC_EXEC) { + Depot *d; + + // convert the cleared tiles to water + ModifyTile(tile, MP_SETTYPE(MP_WATER) | MP_MAPOWNER | MP_MAP5 | MP_MAP2_CLEAR | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR, OWNER_WATER, 0); + ModifyTile(tile2, MP_SETTYPE(MP_WATER) | MP_MAPOWNER | MP_MAP5 | MP_MAP2_CLEAR | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR, OWNER_WATER, 0); + + // Kill the entry from the depot table + for(d=_depots; d->xy != tile; d++) {} + d->xy = 0; + + DeleteWindowById(WC_VEHICLE_DEPOT, tile); + } + + return _price.remove_ship_depot; +} + +// build a shiplift +static int32 DoBuildShiplift(uint tile, int dir, uint32 flags) +{ + int32 ret; + int delta; + + // middle tile + ret = DoCommandByTile(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (ret == CMD_ERROR) return CMD_ERROR; + + delta = _tileoffs_by_dir[dir]; + // lower tile + ret = DoCommandByTile(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (ret == CMD_ERROR) return CMD_ERROR; + if (GetTileSlope(tile - delta, NULL)) return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); + + // upper tile + ret = DoCommandByTile(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (ret == CMD_ERROR) return CMD_ERROR; + if (GetTileSlope(tile + delta, NULL)) return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); + + if (flags & DC_EXEC) { + ModifyTile(tile, MP_SETTYPE(MP_WATER) | MP_MAPOWNER | MP_MAP5 | MP_MAP2_CLEAR | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR, OWNER_WATER, 0x10 + dir); + ModifyTile(tile - delta, MP_SETTYPE(MP_WATER) | MP_MAPOWNER | MP_MAP5 | MP_MAP2_CLEAR | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR, OWNER_WATER, 0x14 + dir); + ModifyTile(tile + delta, MP_SETTYPE(MP_WATER) | MP_MAPOWNER | MP_MAP5 | MP_MAP2_CLEAR | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR, OWNER_WATER, 0x18 + dir); + } + + return _price.clear_water * 22 >> 3; +} + +static int32 RemoveShiplift(uint tile, uint32 flags) +{ + int delta = _tileoffs_by_dir[_map5[tile] & 3]; + + // make sure no vehicle is on the tile. + if (!EnsureNoVehicle(tile) || !EnsureNoVehicle(tile + delta) || !EnsureNoVehicle(tile - delta)) + return CMD_ERROR; + + if (flags & DC_EXEC) { + DoClearSquare(tile); + DoClearSquare(tile + delta); + DoClearSquare(tile - delta); + } + + return _price.clear_water * 2; +} + +static void MarkTilesAroundDirty(uint tile) +{ + MarkTileDirtyByTile(TILE_ADDXY(tile, 0, 1)); + MarkTileDirtyByTile(TILE_ADDXY(tile, 0, -1)); + MarkTileDirtyByTile(TILE_ADDXY(tile, 1, 0)); + MarkTileDirtyByTile(TILE_ADDXY(tile, -1, 0)); +} + +int32 CmdBuildLock(int x, int y, uint32 flags, uint32 p1, uint32 p2) +{ + uint tile = TILE_FROM_XY(x,y); + int32 ret; + uint th; + th = GetTileSlope(tile, NULL); + + if (th==3 || th==6 || th==9 || th==12) { + static const byte _shiplift_dirs[16] = {0,0,0,2,0,0,1,0,0,3,0,0,0}; + ret = DoBuildShiplift(tile, _shiplift_dirs[th], flags); + return ret; + } + else + return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); + + return 0; +} + +int32 CmdBuildCanal(int x, int y, uint32 flags, uint32 p1, uint32 p2) +{ + uint tile = TILE_FROM_XY(x,y); + int32 ret; + uint th; + uint endtile = (uint)p1; + int delta; + int32 cost; + SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); + + // move in which direction? + delta = (GET_TILE_X(tile) == GET_TILE_X(endtile)) ? TILE_XY(0,1) : TILE_XY(1,0); + if (endtile < tile) delta = -delta; + + cost = 0; + for(;;) { + ret = 0; + th = GetTileSlope(tile, NULL); + if(th!=0) + return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); + + // can't make water of water! + if (IS_TILETYPE(tile, MP_WATER)) { + _error_message = STR_1007_ALREADY_BUILT; + } else { + + /* is middle piece of a bridge? */ + if (IS_TILETYPE(tile, MP_TUNNELBRIDGE) && _map5[tile] & 0x40) { /* build under bridge */ + if(_map5[tile] & 0x20) { // transport route under bridge + _error_message = STR_5800_OBJECT_IN_THE_WAY; + ret = CMD_ERROR; + } + else if (_map5[tile] & 0x18) { // already water under bridge + _error_message = STR_1007_ALREADY_BUILT; + ret = CMD_ERROR; + } + + /* no bridge? then try to clear it. */ + } else { + ret = DoCommandByTile(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + } + if (ret == CMD_ERROR) return ret; + cost += ret; + + /* execute modifications */ + if (flags & DC_EXEC) { + if(IS_TILETYPE(tile, MP_TUNNELBRIDGE)) { + // change owner to OWNER_WATER and set land under bridge bit to water + ModifyTile(tile, MP_MAP5 | MP_MAPOWNER, OWNER_WATER, _map5[tile] | 0x08); + } else { + ModifyTile(tile, MP_SETTYPE(MP_WATER) | MP_MAPOWNER | MP_MAP5 | MP_MAP2_CLEAR | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR, OWNER_WATER, 0); + } + // mark the tiles around dirty too + MarkTilesAroundDirty(tile); + } + + cost += _price.clear_water; + } + if (tile == endtile) + break; + tile += delta; + } + if (cost == 0) return CMD_ERROR; + + return cost; +} + +static int32 ClearTile_Water(uint tile, byte flags) { + byte m5 = _map5[tile]; + uint slope; + + if (m5 <= 1) { // water and shore + // Allow building on water? It's ok to build on shores. + if (flags & DC_NO_WATER && m5 != 1) + return_cmd_error(STR_3807_CAN_T_BUILD_ON_WATER); + + // Make sure no vehicle is on the tile + if (!EnsureNoVehicle(tile)) + return CMD_ERROR; + + // Make sure it's not an edge tile. + if (!(IS_INT_INSIDE(GET_TILE_X(tile),1,TILE_X_MAX-1) && + IS_INT_INSIDE(GET_TILE_Y(tile),1,TILE_Y_MAX-1))) + return_cmd_error(STR_0002_TOO_CLOSE_TO_EDGE_OF_MAP); + + if (m5 == 0) { + if (flags & DC_EXEC) + DoClearSquare(tile); + return _price.clear_water; + } else if (m5 == 1) { + slope = GetTileSlope(tile,NULL); + if (slope == 8 || slope == 4 || slope == 2 || slope == 1) { + if (flags & DC_EXEC) + DoClearSquare(tile); + return _price.clear_water; + } + if (flags & DC_EXEC) + DoClearSquare(tile); + return _price.purchase_land; + } else + return CMD_ERROR; + } else if ((m5 & 0x10) == 0x10) { + // shiplift + + static const TileIndexDiff _shiplift_tomiddle_offs[12] = { + 0,0,0,0, // middle + TILE_XY(-1, 0),TILE_XY(0, 1),TILE_XY(1, 0),TILE_XY(0, -1), // lower + TILE_XY(1, 0),TILE_XY(0, -1),TILE_XY(-1, 0),TILE_XY(0, 1), // upper + }; + + if (flags & DC_AUTO) return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED); + // don't allow water to delete it. + if (_current_player == OWNER_WATER) return CMD_ERROR; + // move to the middle tile.. + return RemoveShiplift(tile + _shiplift_tomiddle_offs[m5 & 0xF], flags); + } else { + // ship depot + if (flags & DC_AUTO) + return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED); + + if (m5 == 0x80 || m5 == 0x82) {} + else if (m5 == 0x81) { tile -= TILE_XY(1,0); } + else if (m5 == 0x83) { tile -= TILE_XY(0,1); } + else + return CMD_ERROR; + + return RemoveShipDepot(tile,flags); + } +} + +// return true if a tile is a water tile. +static bool IsWateredTile(uint tile) +{ + byte m5 = _map5[tile]; + if (IS_TILETYPE(tile, MP_WATER)) { + return m5 != 1; + } else if (IS_TILETYPE(tile, MP_STATION)) { + // returns true if it is a dock-station (m5 inside values is m5<75 all stations, + // 83<=m5<=114 new airports + return !(m5 < 75 || (m5 >= 83 && m5 <= 114)); + } else if (IS_TILETYPE(tile, MP_TUNNELBRIDGE)) { + return (m5 & 0xF8) == 0xC8; + } else + return false; +} + +// draw a canal styled water tile with dikes around +void DrawCanalWater(uint tile) +{ + uint wa; + + // determine the edges around with water. + wa = IsWateredTile(TILE_ADDXY(tile, -1, 0)) << 0; + wa += IsWateredTile(TILE_ADDXY(tile, 0, 1)) << 1; + wa += IsWateredTile(TILE_ADDXY(tile, 1, 0)) << 2; + wa += IsWateredTile(TILE_ADDXY(tile, 0, -1)) << 3; + + if (!(wa & 1)) DrawGroundSprite(SPR_CANALS_BASE + 57); + if (!(wa & 2)) DrawGroundSprite(SPR_CANALS_BASE + 58); + if (!(wa & 4)) DrawGroundSprite(SPR_CANALS_BASE + 59); + if (!(wa & 8)) DrawGroundSprite(SPR_CANALS_BASE + 60); + + // right corner + if ((wa & 3) == 0) DrawGroundSprite(SPR_CANALS_BASE + 57 + 4); + else if ((wa & 3) == 3 && !IsWateredTile(TILE_ADDXY(tile, -1, 1))) DrawGroundSprite(SPR_CANALS_BASE + 57 + 8); + + // bottom corner + if ((wa & 6) == 0) DrawGroundSprite(SPR_CANALS_BASE + 57 + 5); + else if ((wa & 6) == 6 && !IsWateredTile(TILE_ADDXY(tile, 1, 1))) DrawGroundSprite(SPR_CANALS_BASE + 57 + 9); + + // left corner + if ((wa & 12) == 0) DrawGroundSprite(SPR_CANALS_BASE + 57 + 6); + else if ((wa & 12) == 12 && !IsWateredTile(TILE_ADDXY(tile, 1, -1))) DrawGroundSprite(SPR_CANALS_BASE + 57 + 10); + + // upper corner + if ((wa & 9) == 0) DrawGroundSprite(SPR_CANALS_BASE + 57 + 7); + else if ((wa & 9) == 9 && !IsWateredTile(TILE_ADDXY(tile, -1, -1))) DrawGroundSprite(SPR_CANALS_BASE + 57 + 11); +} + +typedef struct WaterDrawTileStruct { + int8 delta_x; + int8 delta_y; + int8 delta_z; + byte width; + byte height; + byte unk; + SpriteID image; +} WaterDrawTileStruct; + +typedef struct LocksDrawTileStruct { + int8 delta_x, delta_y, delta_z; + byte width, height, depth; + SpriteID image; +} LocksDrawTileStruct; + +#include "table/water_land.h" + +static void DrawWaterStuff(TileInfo *ti, const byte *t, uint32 palette, uint base) +{ + const WaterDrawTileStruct *wdts; + uint32 image; + + DrawGroundSprite(*(uint16*)t); + t += sizeof(uint16); + + for(wdts = (WaterDrawTileStruct *)t; (byte)wdts->delta_x != 0x80; wdts++) { + image = wdts->image + base; + if (_display_opt & DO_TRANS_BUILDINGS) { + image |= palette; + } else { + image = (image & 0x3FFF) | 0x03224000; + } + AddSortableSpriteToDraw(image, ti->x + wdts->delta_x, ti->y + wdts->delta_y, wdts->width, wdts->height, wdts->unk, ti->z + wdts->delta_z); + } +} + +static void DrawTile_Water(TileInfo *ti) +{ + // draw water tile + if (ti->map5 == 0) { + DrawGroundSprite(0xFDD); + if (ti->z != 0) DrawCanalWater(ti->tile); + return; + } + + // draw shore + if (ti->map5 == 1) { + assert(ti->tileh < 16); + DrawGroundSprite(_water_shore_sprites[ti->tileh]); + return; + } + + // draw shiplift + if ((ti->map5 & 0xF0) == 0x10) { + const byte *t = _shiplift_display_seq[ti->map5 & 0xF]; + DrawWaterStuff(ti, t, 0, ti->z > t[19] ? 24 : 0); + return; + } + + DrawWaterStuff(ti, _shipdepot_display_seq[ti->map5 & 0x7F], PLAYER_SPRITE_COLOR(_map_owner[ti->tile]), 0); +} + +void DrawShipDepotSprite(int x, int y, int image) +{ + const byte *t; + const WaterDrawTileStruct *wdts; + + t = _shipdepot_display_seq[image]; + DrawSprite(*(uint16*)t, x, y); + t += sizeof(uint16); + + for(wdts = (WaterDrawTileStruct *)t; (byte)wdts->delta_x != 0x80; wdts++) { + Point pt = RemapCoords(wdts->delta_x, wdts->delta_y, wdts->delta_z); + DrawSprite(wdts->image + PLAYER_SPRITE_COLOR(_local_player), x + pt.x, y + pt.y); + } +} + + +uint GetSlopeZ_Water(TileInfo *ti) +{ + return GetPartialZ(ti->x&0xF, ti->y&0xF, ti->tileh) + ti->z; +} + +static void GetAcceptedCargo_Water(uint tile, AcceptedCargo *ac) +{ + /* not used */ +} + +static void GetTileDesc_Water(uint tile, TileDesc *td) +{ + if (_map5[tile] == 0 && GET_TILEHEIGHT(tile) == 0) + td->str = STR_3804_WATER; + else if (_map5[tile] == 0) + td->str = STR_LANDINFO_CANAL; + else if (_map5[tile] == 1) + td->str = STR_3805_COAST_OR_RIVERBANK; + else if ((_map5[tile]&0xF0) == 0x10) + td->str = STR_LANDINFO_LOCK; + else + td->str = STR_3806_SHIP_DEPOT; + + td->owner = _map_owner[tile]; +} + +static void AnimateTile_Water(uint tile) +{ + /* not used */ +} + +static void TileLoopWaterHelper(uint tile, const int16 *offs) +{ + byte *p; + + p = &_map_type_and_height[tile]; + tile += offs[0]; + + // type of this tile mustn't be water already. + if (p[offs[0]] >> 4 == MP_WATER) + return; + + if ( (p[offs[1]] | p[offs[2]]) & 0xF ) + return; + + if ( (p[offs[3]] | p[offs[4]]) & 0xF ) { + // make coast.. + if (p[offs[0]] >> 4 == MP_CLEAR || p[offs[0]] >> 4 == MP_TREES) { + _current_player = OWNER_WATER; + if (DoCommandByTile(tile,0,0,DC_EXEC | DC_AUTO, CMD_LANDSCAPE_CLEAR) != CMD_ERROR) + ModifyTile(tile, MP_SETTYPE(MP_WATER) | MP_MAPOWNER | MP_MAP5 | MP_MAP2_CLEAR | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR,OWNER_WATER,1); + } + } else { + if (IS_TILETYPE(tile, MP_TUNNELBRIDGE)) { + byte m5 = _map5[tile]; + if ( (m5&0xF8) == 0xC8 || (m5&0xF8) == 0xF0) + return; + + if ( (m5&0xC0) == 0xC0) { + ModifyTile(tile, MP_MAPOWNER | MP_MAP5,OWNER_WATER,(m5 & ~0x38)|0x8); + return; + } + } + + _current_player = OWNER_WATER; + if (DoCommandByTile(tile,0,0,DC_EXEC, CMD_LANDSCAPE_CLEAR) != CMD_ERROR) + ModifyTile(tile, MP_SETTYPE(MP_WATER) | MP_MAPOWNER | MP_MAP5 | MP_MAP2_CLEAR | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR,OWNER_WATER,0); + } +} + +// called from tunnelbridge_cmd +void TileLoop_Water(uint tile) +{ + int i; + static const TileIndexDiff _tile_loop_offs_array[4][5] = { + // tile to mod shore? shore? + {TILE_XY(-1,0), TILE_XY(0,0), TILE_XY(0,1), TILE_XY(-1,0), TILE_XY(-1,1)}, + {TILE_XY(0,1), TILE_XY(0,1), TILE_XY(1,1), TILE_XY(0,2), TILE_XY(1,2)}, + {TILE_XY(1,0), TILE_XY(1,0), TILE_XY(1,1), TILE_XY(2,0), TILE_XY(2,1)}, + {TILE_XY(0,-1), TILE_XY(0,0), TILE_XY(1,0), TILE_XY(0,-1), TILE_XY(1,-1)}, + }; + + if ( IS_INT_INSIDE(GET_TILE_X(tile),1,TILES_X-3+1) && + IS_INT_INSIDE(GET_TILE_Y(tile),1,TILES_Y-3+1)) { + for(i=0; i!=4; i++) + TileLoopWaterHelper(tile, _tile_loop_offs_array[i]); + } +} + + +static const byte _coast_tracks[16] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0}; +static const byte _shipdepot_tracks[4] = {1,1,2,2}; +static const byte _shiplift_tracks[12] = {1,2,1,2,1,2,1,2,1,2,1,2}; +static uint32 GetTileTrackStatus_Water(uint tile, int mode) +{ + uint m5; + uint b; + + if (mode != 4) + return 0; + + m5 = _map5[tile]; + if (m5 == 0) + return 0x3F3F; + + if (m5 == 1) { + b = _coast_tracks[GetTileSlope(tile, NULL)&0xF]; + return b + (b<<8); + } + + if ( (m5 & 0x10) == 0x10) { + // + b = _shiplift_tracks[m5 & 0xF]; + return b + (b<<8); + } + + if (!(m5 & 0x80)) + return 0; + + b = _shipdepot_tracks[m5 & 0x7F]; + return b + (b<<8); +} + +extern void ShowShipDepotWindow(uint tile); + +static void ClickTile_Water(uint tile) +{ + byte m5 = _map5[tile] - 0x80; + + if (IS_BYTE_INSIDE(m5, 0, 3+1)) { + if (m5 & 1) + tile += (m5==1) ? TILE_XY(-1,0) : TILE_XY(0,-1); + ShowShipDepotWindow(tile); + } +} + +static void ChangeTileOwner_Water(uint tile, byte old_player, byte new_player) +{ + if (_map_owner[tile] != old_player) + return; + + if (new_player != 255) { + _map_owner[tile] = new_player; + } else { + DoCommandByTile(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); + } +} + +static uint32 VehicleEnter_Water(Vehicle *v, uint tile, int x, int y) +{ + return 0; +} + +void InitializeDock() +{ + _last_built_ship_depot_tile = 0; +} + +const TileTypeProcs _tile_type_water_procs = { + DrawTile_Water, /* draw_tile_proc */ + GetSlopeZ_Water, /* get_slope_z_proc */ + ClearTile_Water, /* clear_tile_proc */ + GetAcceptedCargo_Water, /* get_accepted_cargo_proc */ + GetTileDesc_Water, /* get_tile_desc_proc */ + GetTileTrackStatus_Water, /* get_tile_track_status_proc */ + ClickTile_Water, /* click_tile_proc */ + AnimateTile_Water, /* animate_tile_proc */ + TileLoop_Water, /* tile_loop_clear */ + ChangeTileOwner_Water, /* change_tile_owner_clear */ + NULL, /* get_produced_cargo_proc */ + VehicleEnter_Water, /* vehicle_enter_tile_proc */ + NULL, /* vehicle_leave_tile_proc */ +}; + |