diff options
Diffstat (limited to 'clear_cmd.c')
-rw-r--r-- | clear_cmd.c | 806 |
1 files changed, 806 insertions, 0 deletions
diff --git a/clear_cmd.c b/clear_cmd.c new file mode 100644 index 000000000..6645b5f9c --- /dev/null +++ b/clear_cmd.c @@ -0,0 +1,806 @@ +#include "stdafx.h" +#include "ttd.h" +#include "viewport.h" +#include "command.h" + +typedef struct TerraformerHeightMod { + TileIndex tile; + byte height; +} TerraformerHeightMod; + +typedef struct TerraformerState { + int height[4]; + uint32 flags; + + int direction; + int modheight_count; + int tile_table_count; + + int32 cost; + + TileIndex *tile_table; + TerraformerHeightMod *modheight; + +} TerraformerState; + +static int TerraformAllowTileProcess(TerraformerState *ts, TileIndex tile) +{ + TileIndex *t; + int count; + + if ((GET_TILE_X(tile) == TILE_X_MAX) || (GET_TILE_Y(tile) == TILE_Y_MAX)) + return -1; + + t = ts->tile_table; + for(count = ts->tile_table_count; count != 0; count--,t++) { + if (*t == tile) + return 0; + } + + return 1; +} + +static int TerraformGetHeightOfTile(TerraformerState *ts, TileIndex tile) +{ + TerraformerHeightMod *mod = ts->modheight; + int count; + + for(count = ts->modheight_count; count != 0; count--, mod++) { + if (mod->tile == tile) + return mod->height; + } + + return _map_type_and_height[tile] & 0xF; +} + +static void TerraformAddDirtyTile(TerraformerState *ts, TileIndex tile) +{ + int count; + TileIndex *t; + + count = ts->tile_table_count; + + if (count >= 625) + return; + + for(t = ts->tile_table; count != 0; count--,t++) { + if (*t == tile) + return; + } + + ts->tile_table[ts->tile_table_count++] = tile; +} + +static void TerraformAddDirtyTileAround(TerraformerState *ts, TileIndex tile) +{ + TerraformAddDirtyTile(ts, tile+TILE_XY(0,-1)); + TerraformAddDirtyTile(ts, tile+TILE_XY(-1,-1)); + TerraformAddDirtyTile(ts, tile+TILE_XY(-1,0)); + TerraformAddDirtyTile(ts, tile); +} + +static int TerraformProc(TerraformerState *ts, uint tile, int mode) +{ + int r; + int32 ret; + + assert(tile < TILES_X * TILES_Y); + + if ((r=TerraformAllowTileProcess(ts, tile)) <= 0) + return r; + + if ((_map_type_and_height[tile] >> 4) == MP_RAILWAY) { + static const byte _railway_modes[4] = {8, 0x10, 4, 0x20}; + static const byte _railway_dangslopes[4] = {0xd, 0xe, 7, 0xb}; + + // Nothing could be built at the steep slope - this avoids a bug + // when you have a single diagonal track in one corner on a + // basement and then you raise the other corner. + if ((GetTileSlope(tile, NULL)&0xF) == _railway_dangslopes[mode]) { + _error_message = STR_1008_MUST_REMOVE_RAILROAD_TRACK; + return -1; + } + + // If we have a single diagonal track there, the other side of + // tile can be terraformed. + if ((_map5[tile]&~0x40) == _railway_modes[mode]) + return 0; + } + + ret = DoCommandByTile(tile, 0,0, ts->flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR); + + if (ret == CMD_ERROR) { + _terraform_err_tile = tile; + return -1; + } + + ts->cost += ret; + + if (ts->tile_table_count >= 625) + return -1; + ts->tile_table[ts->tile_table_count++] = tile; + + return 0; +} + +static bool TerraformTileHeight(TerraformerState *ts, uint tile, int height) +{ + int nh; + TerraformerHeightMod *mod; + int count; + + assert(tile < TILES_X * TILES_Y); + + if (height < 0) { + _error_message = STR_1003_ALREADY_AT_SEA_LEVEL; + return false; + } + + _error_message = STR_1004_TOO_HIGH; + + if (height > 0xF) + return false; + + nh = TerraformGetHeightOfTile(ts, tile); + if (nh < 0 || height == nh) + return false; + + if (TerraformProc(ts, tile, 0)<0) + return false; + + if (TerraformProc(ts, tile + TILE_XY(0,-1), 1)<0) + return false; + + if (TerraformProc(ts, tile + TILE_XY(-1,-1), 2)<0) + return false; + + if (TerraformProc(ts, tile + TILE_XY(-1,0), 3)<0) + return false; + + mod = ts->modheight; + count = ts->modheight_count; + + for(;;) { + if (count == 0) { + if (ts->modheight_count >= 576) + return false; + ts->modheight_count++; + break; + } + if (mod->tile == (TileIndex)tile) + break; + mod++; + count--; + } + + mod->tile = (TileIndex)tile; + mod->height = (byte)height; + + ts->cost += _price.terraform; + + { + int direction = ts->direction, r; + const TileIndexDiff *ttm; + + static const TileIndexDiff _terraform_tilepos[5] = {TILE_XY(1,0), TILE_XY(-2,0), TILE_XY(1,1), TILE_XY(0,-2), 0 }; + + for(ttm = _terraform_tilepos; *ttm != 0; ttm++) { + tile += *ttm; + + r = TerraformGetHeightOfTile(ts, tile); + if (r != height && r-direction != height && r+direction != height) { + if (!TerraformTileHeight(ts, tile, r+direction)) + return false; + } + } + } + + return true; +} + +/* Terraform land + * p1 - corners + * p2 - direction + */ + +int32 CmdTerraformLand(int x, int y, uint32 flags, uint32 p1, uint32 p2) +{ + TerraformerState ts; + uint tile; + int direction; + + TerraformerHeightMod modheight_data[576]; + TileIndex tile_table_data[625]; + + SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); + + _error_message = INVALID_STRING_ID; + _terraform_err_tile = 0; + + ts.direction = direction = p2 ? 1 : -1; + ts.flags = flags; + ts.modheight_count = ts.tile_table_count = 0; + ts.cost = 0; + ts.modheight = modheight_data; + ts.tile_table = tile_table_data; + + tile = TILE_FROM_XY(x,y); + + if (p1 & 1) { + if (!TerraformTileHeight(&ts, tile+TILE_XY(1,0), + (_map_type_and_height[tile+TILE_XY(1,0)]&0xF) + direction)) + return CMD_ERROR; + } + + if (p1 & 2) { + if (!TerraformTileHeight(&ts, tile+TILE_XY(1,1), + (_map_type_and_height[tile+TILE_XY(1,1)]&0xF) + direction)) + return CMD_ERROR; + } + + if (p1 & 4) { + if (!TerraformTileHeight(&ts, tile+TILE_XY(0,1), + (_map_type_and_height[tile+TILE_XY(0,1)]&0xF) + direction)) + return CMD_ERROR; + } + + if (p1 & 8) { + if (!TerraformTileHeight(&ts, tile+TILE_XY(0,0), + (_map_type_and_height[tile+TILE_XY(0,0)]&0xF) + direction)) + return CMD_ERROR; + } + + if (direction == -1) { + /* Check if tunnel would take damage */ + int count; + TileIndex *ti = ts.tile_table; + + for(count = ts.tile_table_count; count != 0; count--, ti++) { + uint z, t; + uint tile = *ti; + + z = TerraformGetHeightOfTile(&ts, tile + TILE_XY(0,0)); + t = TerraformGetHeightOfTile(&ts, tile + TILE_XY(1,0)); + if (t <= z) z = t; + t = TerraformGetHeightOfTile(&ts, tile + TILE_XY(1,1)); + if (t <= z) z = t; + t = TerraformGetHeightOfTile(&ts, tile + TILE_XY(0,1)); + if (t <= z) z = t; + + if (!CheckTunnelInWay(tile, z*8)) + return_cmd_error(STR_1002_EXCAVATION_WOULD_DAMAGE); + } + } + + if (flags & DC_EXEC) { + /* Clear the landscape at the tiles */ + { + int count; + TileIndex *ti = ts.tile_table; + for(count = ts.tile_table_count; count != 0; count--, ti++) { + DoCommandByTile(*ti, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + } + } + + /* change the height */ + { + int count; + TerraformerHeightMod *mod; + uint til; + + mod = ts.modheight; + for(count = ts.modheight_count; count != 0; count--, mod++) { + til = mod->tile; + + // Change tile height + _map_type_and_height[til] = (_map_type_and_height[til]&~0x0F)|mod->height; + + TerraformAddDirtyTileAround(&ts, til); + } + } + + /* finally mark the dirty tiles dirty */ + { + int count; + TileIndex *ti = ts.tile_table; + for(count = ts.tile_table_count; count != 0; count--, ti++) { + MarkTileDirtyByTile(*ti); + } + } + } + return ts.cost; +} + + +/* + * p1 - start + */ + +int32 CmdLevelLand(int ex, int ey, uint32 flags, uint32 p1, uint32 p2) +{ + int size_x, size_y; + int sx, sy; + uint h, curh; + uint tile; + int32 ret, cost, money; + + // remember level height + h = _map_type_and_height[p1]&0xF; + + ex >>= 4; ey >>= 4; + + // make sure sx,sy are smaller than ex,ey + sx = GET_TILE_X(p1); + sy = GET_TILE_Y(p1); + if (ex < sx) intswap(ex, sx); + if (ey < sy) intswap(ey, sy); + tile = TILE_XY(sx,sy); + + size_x = ex-sx+1; + size_y = ey-sy+1; + + money = GetAvailableMoneyForCommand(); + cost = 0; + + BEGIN_TILE_LOOP(tile2, size_x, size_y, tile) + curh = _map_type_and_height[tile2]&0xF; + while (curh != h) { + ret = DoCommandByTile(tile2, 8, (curh > h)?0:1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND); + if (ret == CMD_ERROR) break; + cost += ret; + + if (flags & DC_EXEC) { + if ((money -= ret) < 0) { + _additional_cash_required = ret; + return cost - ret; + } + DoCommandByTile(tile2, 8, (curh > h)?0:1, flags, CMD_TERRAFORM_LAND); + } + + curh += (curh > h) ? -1 : 1; + } + END_TILE_LOOP(tile2, size_x, size_y, tile) + + if (cost == 0) return CMD_ERROR; + return cost; +} + +/* Purchase a land area + * p1 = unused + * p2 = unused + */ + +int32 CmdPurchaseLandArea(int x, int y, uint32 flags, uint32 p1, uint32 p2) +{ + uint tile; + int32 cost; + + SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); + + tile = TILE_FROM_XY(x,y); + + if (!EnsureNoVehicle(tile)) + return CMD_ERROR; + + if (IS_TILETYPE(tile, MP_UNMOVABLE) && + _map5[tile] == 3 && + _map_owner[tile] == _current_player) + return_cmd_error(STR_5807_YOU_ALREADY_OWN_IT); + + cost = DoCommandByTile(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (cost == CMD_ERROR) + return CMD_ERROR; + + if (flags & DC_EXEC) { + ModifyTile(tile, + MP_SETTYPE(MP_UNMOVABLE) | MP_MAPOWNER_CURRENT | MP_MAP5, + 3 /* map5 */ + ); + } + + return cost + _price.purchase_land * 10; +} + + +int32 ClearTile_Clear(uint tile, byte flags) { + static const int32 * _clear_price_table[] = { + NULL, + &_price.clear_1, &_price.clear_1,&_price.clear_1, + &_price.purchase_land,&_price.purchase_land,&_price.purchase_land,&_price.purchase_land, + &_price.clear_2,&_price.clear_2,&_price.clear_2,&_price.clear_2, + &_price.clear_3,&_price.clear_3,&_price.clear_3,&_price.clear_3, + &_price.purchase_land,&_price.purchase_land,&_price.purchase_land,&_price.purchase_land, + &_price.purchase_land,&_price.purchase_land,&_price.purchase_land,&_price.purchase_land, + &_price.clear_2,&_price.clear_2,&_price.clear_2,&_price.clear_2, + }; + const int32 *price = _clear_price_table[_map5[tile] & 0x1F]; + + if (flags & DC_EXEC) + DoClearSquare(tile); + + if (price == NULL) + return 0; + return *price; +} + +int32 CmdSellLandArea(int x, int y, uint32 flags, uint32 p1, uint32 p2) +{ + uint tile; + + SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); + + tile = TILE_FROM_XY(x,y); + + if (!CheckTileOwnership(tile)) + return CMD_ERROR; + + if (!EnsureNoVehicle(tile)) + return CMD_ERROR; + + if (flags & DC_EXEC) + DoClearSquare(tile); + + return - _price.purchase_land*2; +} + + +#include "table/clear_land.h" + + +void DrawClearLandTile(TileInfo *ti, byte set) +{ + DrawGroundSprite(0xF54 + _tileh_to_sprite[ti->tileh] + set * 19); +} + +void DrawHillyLandTile(TileInfo *ti) +{ + if (ti->tileh != 0) { + DrawGroundSprite(0xFA0 + _tileh_to_sprite[ti->tileh]); + } else { + DrawGroundSprite(_landscape_clear_sprites[((ti->x^ti->y) >> 4) & 0x7]); + } +} + +void DrawClearLandFence(TileInfo *ti, byte img) +{ + byte z = ti->z; + + if (ti->tileh & 2) { + z += 8; + if (ti->tileh == 0x17) + z += 8; + } + + if (img & 0x38) { + DrawGroundSpriteAt(_clear_land_fence_sprites_1[((img >> 3) & 7) - 1] + _fence_mod_by_tileh[ti->tileh], ti->x, ti->y, z); + } + + if (img & 0x7) { + DrawGroundSpriteAt(_clear_land_fence_sprites_1[(img & 7) - 1] + _fence_mod_by_tileh_2[ti->tileh], ti->x, ti->y, z); + } +} + +static void DrawTile_Clear(TileInfo *ti) +{ + + switch((ti->map5 & (7<<2)) >> 2) { + case 0: + DrawClearLandTile(ti, (ti->map5 & 3)); + break; + + case 1: + DrawHillyLandTile(ti); + break; + + case 2: + DrawGroundSprite(0xFB7 + _tileh_to_sprite[ti->tileh]); + break; + + case 3: + DrawGroundSprite( _clear_land_sprites_1[_map3_lo[ti->tile]&0xF] + _tileh_to_sprite[ti->tileh]); + break; + + case 4: + DrawGroundSprite( _clear_land_sprites_2[ti->map5&3] + _tileh_to_sprite[ti->tileh]); + break; + + case 5: + DrawGroundSprite( _clear_land_sprites_3[ti->map5&3] + _tileh_to_sprite[ti->tileh]); + break; + } + + DrawClearLandFence(ti, _map3_hi[ti->tile] >> 2); +} + +uint GetSlopeZ_Clear(TileInfo *ti) { return GetPartialZ(ti->x&0xF, ti->y&0xF, ti->tileh) + ti->z; } + +static void GetAcceptedCargo_Clear(uint tile, AcceptedCargo *ac) +{ + /* unused */ +} + +static void AnimateTile_Clear(uint tile) +{ + /* unused */ +} + +void TileLoopClearHelper(uint tile) +{ + byte img_1, img_2; + static byte img_by_map5[8] = { 0,0,0,2, 1,1,0,0, }; + uint dirty = -1; + + img_1 = 0; + if (IS_TILETYPE(tile, MP_CLEAR)) { + img_1 = img_by_map5[(_map5[tile] & 0x1C) >> 2]; + } else if (IS_TILETYPE(tile, MP_TREES) && (_map2[tile] & 0x30) == 0x20) { + img_1 = 1; + } + + img_2 = 0; + if (IS_TILETYPE(TILE_ADDXY(tile, 1, 0), MP_CLEAR)) { + img_2 = img_by_map5[(_map5[TILE_ADDXY(tile, 1, 0)] & 0x1C) >> 2]; + } else if (IS_TILETYPE(TILE_ADDXY(tile, 1, 0), MP_TREES) && (_map2[TILE_ADDXY(tile, 1, 0)] & 0x30) == 0x20) { + img_2 = 1; + } + + if (!(_map3_hi[tile] & 0xE0)) { + if ( (img_1&2) != (img_2&2) ) { + _map3_hi[tile] |= 3 << 5; + dirty = tile; + } + } else { + if (img_1 == 1 && img_2 == 1) { + _map3_hi[tile] &= ~(3 << 5); + dirty = tile; + } + } + + img_2 = 0; + if (IS_TILETYPE(TILE_ADDXY(tile, 0, 1), MP_CLEAR)) { + img_2 = img_by_map5[(_map5[TILE_ADDXY(tile, 0, 1)] & 0x1C) >> 2]; + } else if (IS_TILETYPE(TILE_ADDXY(tile, 0, 1), MP_TREES) && (_map2[TILE_ADDXY(tile, 0, 1)] & 0x30) == 0x20) { + img_2 = 1; + } + + if (!(_map3_hi[tile] & 0x1C)) { + if ( (img_1&2) != (img_2&2) ) { + _map3_hi[tile] |= 3 << 2; + dirty = tile; + } + } else { + if (img_1 == 1 && img_2 == 1) { + _map3_hi[tile] &= ~(3 << 2); + dirty = tile; + } + } + + if (dirty != -1) + MarkTileDirtyByTile(dirty); +} + + +/* convert into snowy tiles */ +static void TileLoopClearAlps(uint tile) +{ + int k; + byte m5,tmp; + + /* distance from snow line, in steps of 8 */ + k = GetTileZ(tile) - _opt.snow_line; + + m5 = _map5[tile] & 0x1C; + tmp = _map5[tile] & 3; + + if (k < -8) { + /* snow_m2_down */ + if (m5 != 0x10) + return; + if (tmp == 0) + m5 = 3; + } else if (k == -8) { + /* snow_m1 */ + if (m5 != 0x10) { + m5 = 0x10; + } else if (tmp != 0) { + m5 = (tmp - 1) + 0x10; + } else + return; + } else if (k < 8) { + /* snow_0 */ + if (m5 != 0x10) { + m5 = 0x10; + } else if (tmp != 1) { + m5 = 1; + if (tmp != 0) + m5 = tmp - 1; + m5 += 0x10; + } else + return; + } else if (k == 8) { + /* snow_p1 */ + if (m5 != 0x10) { + m5 = 0x10; + } else if (tmp != 2) { + m5 = 2; + if (tmp <= 2) + m5 = tmp + 1; + m5 += 0x10; + } else + return; + } else { + /* snow_p2_up */ + if (m5 != 0x10) { + m5 = 0x10; + } else if (tmp != 3) { + m5 = tmp + 1 + 0x10; + } else + return; + } + + _map5[tile] = m5; + MarkTileDirtyByTile(tile); +} + +static void TileLoopClearDesert(uint tile) +{ + if ( (_map5[tile] & 0x1C) == 0x14) + return; + + if (GetMapExtraBits(tile) == 1) { + _map5[tile] = 0x17; + } else { + if (GetMapExtraBits(tile+TILE_XY(1,0)) != 1 && + GetMapExtraBits(tile+TILE_XY(-1,0)) != 1 && + GetMapExtraBits(tile+TILE_XY(0,1)) != 1 && + GetMapExtraBits(tile+TILE_XY(0,-1)) != 1) + return; + _map5[tile] = 0x15; + } + + MarkTileDirtyByTile(tile); +} + +static void TileLoop_Clear(uint tile) +{ + byte m5,m3; + + TileLoopClearHelper(tile); + + if (_opt.landscape == LT_DESERT) { + TileLoopClearDesert(tile); + } else if (_opt.landscape == LT_HILLY) { + TileLoopClearAlps(tile); + } + + m5 = _map5[tile]; + if ( (m5 & 0x1C) == 0x10 || (m5 & 0x1C) == 0x14) + return; + + if ( (m5 & 0x1C) != 0xC) { + if ( (m5 & 3) == 3) + return; + + if (_game_mode != GM_EDITOR) { + m5 += 0x20; + if (m5 >= 0x20) { + // Didn't overflow + _map5[tile] = m5; + return; + } + /* did overflow, so continue */ + } else { + m5 = ((byte)Random() > 21) ? (2) : (6); + } + m5++; + } else if (_game_mode != GM_EDITOR) { + /* handle farm field */ + m5 += 0x20; + if (m5 >= 0x20) { + // Didn't overflow + _map5[tile] = m5; + return; + } + /* overflowed */ + m3 = _map3_lo[tile] + 1; + assert( (m3 & 0xF) != 0); + if ( (m3 & 0xF) >= 9) /* NOTE: will not work properly if m3&0xF == 0xF */ + m3 &= ~0xF; + _map3_lo[tile] = m3; + } + + _map5[tile] = m5; + MarkTileDirtyByTile(tile); +} + +void GenerateClearTile() +{ + int i,j; + uint tile,tile_new; + uint32 r; + + /* add hills */ + i = (Random() & 0x3FF) | 0x400; + do { + tile = TILE_MASK(Random()); + if (IS_TILETYPE(tile, MP_CLEAR)) + _map5[tile] = (byte)((_map5[tile] & ~(3<<2)) | (1<<2)); + } while (--i); + + /* add grey squares */ + i = (Random() & 0x7F) | 0x80; + do { + r = Random(); + tile = TILE_MASK(r); + if (IS_TILETYPE(tile, MP_CLEAR)) { + j = ((r >> 16) & 0xF) + 5; + for(;;) { + _map5[tile] = (byte)((_map5[tile] & ~(3<<2)) | (2<<2)); + do { + if (--j == 0) goto get_out; + tile_new = tile + _tileoffs_by_dir[Random() & 3]; + } while (!IS_TILETYPE(tile_new, MP_CLEAR)); + tile = tile_new; + } +get_out:; + } + } while (--i); +} + +static void ClickTile_Clear(uint tile) +{ + /* not used */ +} + +uint32 GetTileTrackStatus_Clear(uint tile, int mode) +{ + return 0; +} + +static const StringID _clear_land_str[4+8-1] = { + STR_080B_ROUGH_LAND, + STR_080A_ROCKS, + STR_080E_FIELDS, + STR_080F_SNOW_COVERED_LAND, + STR_0810_DESERT, + 0, + 0, + STR_080C_BARE_LAND, + STR_080D_GRASS, + STR_080D_GRASS, + STR_080D_GRASS, +}; + +static void GetTileDesc_Clear(uint tile, TileDesc *td) +{ + int i = (_map5[tile]>>2) & 7; + if (i == 0) + i = (_map5[tile] & 3) + 8; + td->str = _clear_land_str[i - 1]; + td->owner = _map_owner[tile]; +} + +static void ChangeTileOwner_Clear(uint tile, byte old_player, byte new_player) +{ + return; +} + +void InitializeClearLand() { + _opt.snow_line = _patches.snow_line_height * 8; +} + +const TileTypeProcs _tile_type_clear_procs = { + DrawTile_Clear, /* draw_tile_proc */ + GetSlopeZ_Clear, /* get_slope_z_proc */ + ClearTile_Clear, /* clear_tile_proc */ + GetAcceptedCargo_Clear, /* get_accepted_cargo_proc */ + GetTileDesc_Clear, /* get_tile_desc_proc */ + GetTileTrackStatus_Clear, /* get_tile_track_status_proc */ + ClickTile_Clear, /* click_tile_proc */ + AnimateTile_Clear, /* animate_tile_proc */ + TileLoop_Clear, /* tile_loop_clear */ + ChangeTileOwner_Clear, /* change_tile_owner_clear */ + NULL, /* get_produced_cargo_proc */ + NULL, /* vehicle_enter_tile_proc */ + NULL, /* vehicle_leave_tile_proc */ +}; |