mirror of
https://github.com/JGRennison/OpenTTD-patches.git
synced 2024-11-17 21:25:40 +00:00
6013b327f7
Added RandomTile/RandomTileSeed functions to generate a random tile. Changed landscape routines so they don't assume that the Y map side is a power of two. (support for this is not complete, though) Changed some frequently used map macros to not compute the values each time. Silence some warnings on MSVC.
863 lines
18 KiB
C
863 lines
18 KiB
C
#include "stdafx.h"
|
|
#include "openttd.h"
|
|
#include "table/strings.h"
|
|
#include "map.h"
|
|
#include "tile.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 (TileX(tile) == MapMaxX() || TileY(tile) == MapMaxY())
|
|
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 TileHeight(tile);
|
|
}
|
|
|
|
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 + TileDiffXY( 0, -1));
|
|
TerraformAddDirtyTile(ts, tile + TileDiffXY(-1, -1));
|
|
TerraformAddDirtyTile(ts, tile + TileDiffXY(-1, 0));
|
|
TerraformAddDirtyTile(ts, tile);
|
|
}
|
|
|
|
static int TerraformProc(TerraformerState *ts, TileIndex tile, int mode)
|
|
{
|
|
int r;
|
|
int32 ret;
|
|
|
|
assert(tile < MapSize());
|
|
|
|
if ((r=TerraformAllowTileProcess(ts, tile)) <= 0)
|
|
return r;
|
|
|
|
if (IsTileType(tile, MP_RAILWAY)) {
|
|
static const byte _railway_modes[4] = {8, 0x10, 4, 0x20};
|
|
static const byte _railway_dangslopes[4] = {0xd, 0xe, 7, 0xb};
|
|
static const byte _railway_dangslopes2[4] = {0x2, 0x1, 0x8, 0x4};
|
|
|
|
// 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/lower the other corner.
|
|
int tileh = GetTileSlope(tile, NULL) & 0xF;
|
|
if (tileh == _railway_dangslopes[mode] ||
|
|
tileh == _railway_dangslopes2[mode]) {
|
|
_terraform_err_tile = tile;
|
|
_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 ((_m[tile].m5&~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, TileIndex tile, int height)
|
|
{
|
|
int nh;
|
|
TerraformerHeightMod *mod;
|
|
int count;
|
|
|
|
assert(tile < MapSize());
|
|
|
|
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 + TileDiffXY( 0, -1), 1) < 0) return false;
|
|
if (TerraformProc(ts, tile + TileDiffXY(-1, -1), 2) < 0) return false;
|
|
if (TerraformProc(ts, tile + TileDiffXY(-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 == tile) break;
|
|
mod++;
|
|
count--;
|
|
}
|
|
|
|
mod->tile = tile;
|
|
mod->height = (byte)height;
|
|
|
|
ts->cost += _price.terraform;
|
|
|
|
{
|
|
int direction = ts->direction, r;
|
|
const TileIndexDiffC *ttm;
|
|
|
|
static const TileIndexDiffC _terraform_tilepos[] = {
|
|
{ 1, 0},
|
|
{-2, 0},
|
|
{ 1, 1},
|
|
{ 0, -2}
|
|
};
|
|
|
|
for(ttm = _terraform_tilepos; ttm != endof(_terraform_tilepos); ttm++) {
|
|
tile += ToTileIndexDiff(*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
|
|
* @param x,y coordinates to terraform
|
|
* @param p1 corners to terraform.
|
|
* @param p2 direction; eg up or down
|
|
*/
|
|
int32 CmdTerraformLand(int x, int y, uint32 flags, uint32 p1, uint32 p2)
|
|
{
|
|
TerraformerState ts;
|
|
TileIndex 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 = TileVirtXY(x, y);
|
|
|
|
/* Make an extra check for map-bounds cause we add tiles to the originating tile */
|
|
if (tile + TileDiffXY(1, 1) > MapSize()) return CMD_ERROR;
|
|
|
|
if (p1 & 1) {
|
|
if (!TerraformTileHeight(&ts, tile + TileDiffXY(1, 0),
|
|
TileHeight(tile + TileDiffXY(1, 0)) + direction))
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
if (p1 & 2) {
|
|
if (!TerraformTileHeight(&ts, tile + TileDiffXY(1, 1),
|
|
TileHeight(tile + TileDiffXY(1, 1)) + direction))
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
if (p1 & 4) {
|
|
if (!TerraformTileHeight(&ts, tile + TileDiffXY(0, 1),
|
|
TileHeight(tile + TileDiffXY(0, 1)) + direction))
|
|
return CMD_ERROR;
|
|
}
|
|
|
|
if (p1 & 8) {
|
|
if (!TerraformTileHeight(&ts, tile + TileDiffXY(0, 0),
|
|
TileHeight(tile + TileDiffXY(0, 0)) + 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;
|
|
TileIndex tile = *ti;
|
|
|
|
z = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0));
|
|
t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0));
|
|
if (t <= z) z = t;
|
|
t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 1));
|
|
if (t <= z) z = t;
|
|
t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(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;
|
|
|
|
mod = ts.modheight;
|
|
for (count = ts.modheight_count; count != 0; count--, mod++) {
|
|
TileIndex til = mod->tile;
|
|
|
|
SetTileHeight(til, 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;
|
|
}
|
|
|
|
|
|
/** Levels a selected (rectangle) area of land
|
|
* @param x,y end tile of area-drag
|
|
* @param p1 start tile of area drag
|
|
* @param p2 unused
|
|
*/
|
|
int32 CmdLevelLand(int ex, int ey, uint32 flags, uint32 p1, uint32 p2)
|
|
{
|
|
int size_x, size_y;
|
|
int sx, sy;
|
|
uint h, curh;
|
|
TileIndex tile;
|
|
int32 ret, cost, money;
|
|
|
|
if (p1 > MapSize()) return CMD_ERROR;
|
|
|
|
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
|
|
|
// remember level height
|
|
h = TileHeight(p1);
|
|
|
|
ex >>= 4; ey >>= 4;
|
|
|
|
// make sure sx,sy are smaller than ex,ey
|
|
sx = TileX(p1);
|
|
sy = TileY(p1);
|
|
if (ex < sx) intswap(ex, sx);
|
|
if (ey < sy) intswap(ey, sy);
|
|
tile = TileXY(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 = TileHeight(tile2);
|
|
while (curh != h) {
|
|
ret = DoCommandByTile(tile2, 8, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND);
|
|
if (CmdFailed(ret)) 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)
|
|
|
|
return (cost == 0) ? CMD_ERROR : cost;
|
|
}
|
|
|
|
/** Purchase a land area. Actually you only purchase one tile, so
|
|
* the name is a bit confusing ;p
|
|
* @param x,y the tile the player is purchasing
|
|
* @param p1 unused
|
|
* @param p2 unused
|
|
*/
|
|
int32 CmdPurchaseLandArea(int x, int y, uint32 flags, uint32 p1, uint32 p2)
|
|
{
|
|
TileIndex tile;
|
|
int32 cost;
|
|
|
|
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
|
|
|
tile = TileVirtXY(x, y);
|
|
|
|
if (!EnsureNoVehicle(tile)) return CMD_ERROR;
|
|
|
|
if (IsTileType(tile, MP_UNMOVABLE) && _m[tile].m5 == 3 &&
|
|
IsTileOwner(tile, _current_player))
|
|
return_cmd_error(STR_5807_YOU_ALREADY_OWN_IT);
|
|
|
|
cost = DoCommandByTile(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
|
|
if (CmdFailed(cost)) 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;
|
|
}
|
|
|
|
|
|
static int32 ClearTile_Clear(TileIndex 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[_m[tile].m5 & 0x1F];
|
|
|
|
if (flags & DC_EXEC)
|
|
DoClearSquare(tile);
|
|
|
|
if (price == NULL)
|
|
return 0;
|
|
return *price;
|
|
}
|
|
|
|
/** Sell a land area. Actually you only sell one tile, so
|
|
* the name is a bit confusing ;p
|
|
* @param x,y the tile the player is selling
|
|
* @param p1 unused
|
|
* @param p2 unused
|
|
*/
|
|
int32 CmdSellLandArea(int x, int y, uint32 flags, uint32 p1, uint32 p2)
|
|
{
|
|
TileIndex tile;
|
|
|
|
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
|
|
|
tile = TileVirtXY(x, y);
|
|
|
|
if (!IsTileType(tile, MP_UNMOVABLE) || _m[tile].m5 != 3) return CMD_ERROR;
|
|
if (!CheckTileOwnership(tile) && _current_player != OWNER_WATER) 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[_m[ti->tile].m3&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, _m[ti->tile].m4 >> 2);
|
|
}
|
|
|
|
static uint GetSlopeZ_Clear(TileInfo *ti)
|
|
{
|
|
return GetPartialZ(ti->x & 0xF, ti->y & 0xF, ti->tileh) + ti->z;
|
|
}
|
|
|
|
static uint GetSlopeTileh_Clear(TileInfo *ti)
|
|
{
|
|
return ti->tileh;
|
|
}
|
|
|
|
static void GetAcceptedCargo_Clear(TileIndex tile, AcceptedCargo ac)
|
|
{
|
|
/* unused */
|
|
}
|
|
|
|
static void AnimateTile_Clear(TileIndex tile)
|
|
{
|
|
/* unused */
|
|
}
|
|
|
|
void TileLoopClearHelper(TileIndex tile)
|
|
{
|
|
byte img_1;
|
|
byte img_2;
|
|
static const byte img_by_map5[] = { 0, 0, 0, 2, 1, 1, 0, 0 };
|
|
TileIndex dirty = INVALID_TILE;
|
|
|
|
switch (GetTileType(tile)) {
|
|
case MP_CLEAR:
|
|
img_1 = img_by_map5[(_m[tile].m5 & 0x1C) >> 2];
|
|
break;
|
|
|
|
case MP_TREES:
|
|
if ((_m[tile].m2 & 0x30) == 0x20)
|
|
img_1 = 1;
|
|
else
|
|
img_1 = 0;
|
|
break;
|
|
|
|
default:
|
|
img_1 = 0;
|
|
break;
|
|
}
|
|
|
|
switch (GetTileType(TILE_ADDXY(tile, 1, 0))) {
|
|
case MP_CLEAR:
|
|
img_2 = img_by_map5[(_m[TILE_ADDXY(tile, 1, 0)].m5 & 0x1C) >> 2];
|
|
break;
|
|
|
|
case MP_TREES:
|
|
if ((_m[TILE_ADDXY(tile, 1, 0)].m2 & 0x30) == 0x20)
|
|
img_2 = 1;
|
|
else
|
|
img_2 = 0;
|
|
break;
|
|
|
|
default:
|
|
img_2 = 0;
|
|
break;
|
|
}
|
|
|
|
if ((_m[tile].m4 & 0xE0) == 0) {
|
|
if ((img_1 & 2) != (img_2 & 2)) {
|
|
_m[tile].m4 |= 3 << 5;
|
|
dirty = tile;
|
|
}
|
|
} else {
|
|
if (img_1 == 1 && img_2 == 1) {
|
|
_m[tile].m4 &= ~(3 << 5);
|
|
dirty = tile;
|
|
}
|
|
}
|
|
|
|
switch (GetTileType(TILE_ADDXY(tile, 0, 1))) {
|
|
case MP_CLEAR:
|
|
img_2 = img_by_map5[(_m[TILE_ADDXY(tile, 0, 1)].m5 & 0x1C) >> 2];
|
|
break;
|
|
|
|
case MP_TREES:
|
|
if ((_m[TILE_ADDXY(tile, 0, 1)].m2 & 0x30) == 0x20)
|
|
img_2 = 1;
|
|
else
|
|
img_2 = 0;
|
|
break;
|
|
|
|
default:
|
|
img_2 = 0;
|
|
break;
|
|
}
|
|
|
|
if ((_m[tile].m4 & 0x1C) == 0) {
|
|
if ((img_1 & 2) != (img_2 & 2)) {
|
|
_m[tile].m4 |= 3 << 2;
|
|
dirty = tile;
|
|
}
|
|
} else {
|
|
if (img_1 == 1 && img_2 == 1) {
|
|
_m[tile].m4 &= ~(3 << 2);
|
|
dirty = tile;
|
|
}
|
|
}
|
|
|
|
if (dirty != INVALID_TILE)
|
|
MarkTileDirtyByTile(dirty);
|
|
}
|
|
|
|
|
|
/* convert into snowy tiles */
|
|
static void TileLoopClearAlps(TileIndex tile)
|
|
{
|
|
int k;
|
|
byte m5,tmp;
|
|
|
|
/* distance from snow line, in steps of 8 */
|
|
k = GetTileZ(tile) - _opt.snow_line;
|
|
|
|
m5 = _m[tile].m5 & 0x1C;
|
|
tmp = _m[tile].m5 & 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;
|
|
}
|
|
|
|
_m[tile].m5 = m5;
|
|
MarkTileDirtyByTile(tile);
|
|
}
|
|
|
|
static void TileLoopClearDesert(TileIndex tile)
|
|
{
|
|
if ( (_m[tile].m5 & 0x1C) == 0x14)
|
|
return;
|
|
|
|
if (GetMapExtraBits(tile) == 1) {
|
|
_m[tile].m5 = 0x17;
|
|
} else {
|
|
if (GetMapExtraBits(tile + TileDiffXY( 1, 0)) != 1 &&
|
|
GetMapExtraBits(tile + TileDiffXY(-1, 0)) != 1 &&
|
|
GetMapExtraBits(tile + TileDiffXY( 0, 1)) != 1 &&
|
|
GetMapExtraBits(tile + TileDiffXY( 0, -1)) != 1)
|
|
return;
|
|
_m[tile].m5 = 0x15;
|
|
}
|
|
|
|
MarkTileDirtyByTile(tile);
|
|
}
|
|
|
|
static void TileLoop_Clear(TileIndex tile)
|
|
{
|
|
byte m5,m3;
|
|
|
|
TileLoopClearHelper(tile);
|
|
|
|
if (_opt.landscape == LT_DESERT) {
|
|
TileLoopClearDesert(tile);
|
|
} else if (_opt.landscape == LT_HILLY) {
|
|
TileLoopClearAlps(tile);
|
|
}
|
|
|
|
m5 = _m[tile].m5;
|
|
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
|
|
_m[tile].m5 = 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
|
|
_m[tile].m5 = m5;
|
|
return;
|
|
}
|
|
/* overflowed */
|
|
m3 = _m[tile].m3 + 1;
|
|
assert( (m3 & 0xF) != 0);
|
|
if ( (m3 & 0xF) >= 9) /* NOTE: will not work properly if m3&0xF == 0xF */
|
|
m3 &= ~0xF;
|
|
_m[tile].m3 = m3;
|
|
}
|
|
|
|
_m[tile].m5 = m5;
|
|
MarkTileDirtyByTile(tile);
|
|
}
|
|
|
|
void GenerateClearTile(void)
|
|
{
|
|
int i,j;
|
|
TileIndex tile;
|
|
uint32 r;
|
|
|
|
/* add hills */
|
|
i = ScaleByMapSize((Random() & 0x3FF) + 0x400);
|
|
do {
|
|
tile = RandomTile();
|
|
if (IsTileType(tile, MP_CLEAR))
|
|
_m[tile].m5 = (byte)((_m[tile].m5 & ~(3<<2)) | (1<<2));
|
|
} while (--i);
|
|
|
|
/* add grey squares */
|
|
i = ScaleByMapSize((Random() & 0x7F) + 0x80);
|
|
do {
|
|
r = Random();
|
|
tile = RandomTileSeed(r);
|
|
if (IsTileType(tile, MP_CLEAR)) {
|
|
j = ((r >> 16) & 0xF) + 5;
|
|
for(;;) {
|
|
TileIndex tile_new;
|
|
|
|
_m[tile].m5 = (byte)((_m[tile].m5 & ~(3<<2)) | (2<<2));
|
|
do {
|
|
if (--j == 0) goto get_out;
|
|
tile_new = tile + TileOffsByDir(Random() & 3);
|
|
} while (!IsTileType(tile_new, MP_CLEAR));
|
|
tile = tile_new;
|
|
}
|
|
get_out:;
|
|
}
|
|
} while (--i);
|
|
}
|
|
|
|
static void ClickTile_Clear(TileIndex tile)
|
|
{
|
|
/* not used */
|
|
}
|
|
|
|
static uint32 GetTileTrackStatus_Clear(TileIndex tile, TransportType 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(TileIndex tile, TileDesc *td)
|
|
{
|
|
int i = (_m[tile].m5>>2) & 7;
|
|
if (i == 0)
|
|
i = (_m[tile].m5 & 3) + 8;
|
|
td->str = _clear_land_str[i - 1];
|
|
td->owner = GetTileOwner(tile);
|
|
}
|
|
|
|
static void ChangeTileOwner_Clear(TileIndex tile, byte old_player, byte new_player)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void InitializeClearLand(void)
|
|
{
|
|
_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 */
|
|
GetSlopeTileh_Clear, /* get_slope_tileh_proc */
|
|
};
|