|
@@ -277,32 +277,32 @@ byte VehicleRandomBits()
|
|
|
/* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
|
|
|
* lookup times at the expense of memory usage. */
|
|
|
const int HASH_BITS = 7;
|
|
|
const int HASH_SIZE = 1 << HASH_BITS;
|
|
|
const int HASH_MASK = HASH_SIZE - 1;
|
|
|
const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
|
|
|
const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
|
|
|
|
|
|
/* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
|
|
|
* Profiling results show that 0 is fastest. */
|
|
|
const int HASH_RES = 0;
|
|
|
|
|
|
static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
|
|
|
static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
|
|
|
|
|
|
static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
|
|
|
static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
|
|
|
{
|
|
|
for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
|
|
|
for (int x = xl; ; x = (x + 1) & HASH_MASK) {
|
|
|
Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
|
|
|
for (; v != NULL; v = v->next_new_hash) {
|
|
|
Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
|
|
|
for (; v != NULL; v = v->hash_tile_next) {
|
|
|
Vehicle *a = proc(v, data);
|
|
|
if (find_first && a != NULL) return a;
|
|
|
}
|
|
|
if (x == xu) break;
|
|
|
}
|
|
|
if (y == yu) break;
|
|
|
}
|
|
|
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -318,25 +318,25 @@ static Vehicle *VehicleFromHash(int xl,
|
|
|
* @return the best matching or first vehicle (depending on find_first).
|
|
|
*/
|
|
|
static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
|
|
|
{
|
|
|
const int COLL_DIST = 6;
|
|
|
|
|
|
/* Hash area to scan is from xl,yl to xu,yu */
|
|
|
int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
|
|
|
int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
|
|
|
int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
|
|
|
int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
|
|
|
|
|
|
return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
|
|
|
return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Find a vehicle from a specific location. It will call proc for ALL vehicles
|
|
|
* on the tile and YOU must make SURE that the "best one" is stored in the
|
|
|
* data value and is ALWAYS the same regardless of the order of the vehicles
|
|
|
* where proc was called on!
|
|
|
* When you fail to do this properly you create an almost untraceable DESYNC!
|
|
|
* @note The return value of proc will be ignored.
|
|
|
* @note Use this when you have the intention that all vehicles
|
|
|
* should be iterated over.
|
|
|
* @param x The X location on the map
|
|
@@ -371,26 +371,26 @@ bool HasVehicleOnPosXY(int x, int y, voi
|
|
|
* @param tile The location on the map
|
|
|
* @param data Arbitrary data passed to \a proc.
|
|
|
* @param proc The proc that determines whether a vehicle will be "found".
|
|
|
* @param find_first Whether to return on the first found or iterate over
|
|
|
* all vehicles
|
|
|
* @return the best matching or first vehicle (depending on find_first).
|
|
|
*/
|
|
|
static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
|
|
|
{
|
|
|
int x = GB(TileX(tile), HASH_RES, HASH_BITS);
|
|
|
int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
|
|
|
|
|
|
Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
|
|
|
for (; v != NULL; v = v->next_new_hash) {
|
|
|
Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
|
|
|
for (; v != NULL; v = v->hash_tile_next) {
|
|
|
if (v->tile != tile) continue;
|
|
|
|
|
|
Vehicle *a = proc(v, data);
|
|
|
if (find_first && a != NULL) return a;
|
|
|
}
|
|
|
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Find a vehicle from a specific location. It will call \a proc for ALL vehicles
|
|
|
* on the tile and YOU must make SURE that the "best one" is stored in the
|
|
@@ -509,110 +509,110 @@ static Vehicle *EnsureNoTrainOnTrackProc
|
|
|
*/
|
|
|
CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
|
|
|
{
|
|
|
/* Value v is not safe in MP games, however, it is used to generate a local
|
|
|
* error message only (which may be different for different machines).
|
|
|
* Such a message does not affect MP synchronisation.
|
|
|
*/
|
|
|
Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
|
|
|
if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
|
|
|
return CommandCost();
|
|
|
}
|
|
|
|
|
|
static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
|
|
|
static void UpdateVehicleTileHash(Vehicle *v, bool remove)
|
|
|
{
|
|
|
Vehicle **old_hash = v->old_new_hash;
|
|
|
Vehicle **old_hash = v->hash_tile_current;
|
|
|
Vehicle **new_hash;
|
|
|
|
|
|
if (remove) {
|
|
|
new_hash = NULL;
|
|
|
} else {
|
|
|
int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
|
|
|
int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
|
|
|
new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
|
|
|
new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
|
|
|
}
|
|
|
|
|
|
if (old_hash == new_hash) return;
|
|
|
|
|
|
/* Remove from the old position in the hash table */
|
|
|
if (old_hash != NULL) {
|
|
|
if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
|
|
|
*v->prev_new_hash = v->next_new_hash;
|
|
|
if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
|
|
|
*v->hash_tile_prev = v->hash_tile_next;
|
|
|
}
|
|
|
|
|
|
/* Insert vehicle at beginning of the new position in the hash table */
|
|
|
if (new_hash != NULL) {
|
|
|
v->next_new_hash = *new_hash;
|
|
|
if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
|
|
|
v->prev_new_hash = new_hash;
|
|
|
v->hash_tile_next = *new_hash;
|
|
|
if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
|
|
|
v->hash_tile_prev = new_hash;
|
|
|
*new_hash = v;
|
|
|
}
|
|
|
|
|
|
/* Remember current hash position */
|
|
|
v->old_new_hash = new_hash;
|
|
|
v->hash_tile_current = new_hash;
|
|
|
}
|
|
|
|
|
|
static Vehicle *_vehicle_position_hash[0x1000];
|
|
|
static Vehicle *_vehicle_viewport_hash[0x1000];
|
|
|
|
|
|
static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
|
|
|
static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
|
|
|
{
|
|
|
Vehicle **old_hash, **new_hash;
|
|
|
int old_x = v->coord.left;
|
|
|
int old_y = v->coord.top;
|
|
|
|
|
|
new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
|
|
|
old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
|
|
|
new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
|
|
|
old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
|
|
|
|
|
|
if (old_hash == new_hash) return;
|
|
|
|
|
|
/* remove from hash table? */
|
|
|
if (old_hash != NULL) {
|
|
|
if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
|
|
|
*v->prev_hash = v->next_hash;
|
|
|
if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
|
|
|
*v->hash_viewport_prev = v->hash_viewport_next;
|
|
|
}
|
|
|
|
|
|
/* insert into hash table? */
|
|
|
if (new_hash != NULL) {
|
|
|
v->next_hash = *new_hash;
|
|
|
if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
|
|
|
v->prev_hash = new_hash;
|
|
|
v->hash_viewport_next = *new_hash;
|
|
|
if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
|
|
|
v->hash_viewport_prev = new_hash;
|
|
|
*new_hash = v;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void ResetVehiclePosHash()
|
|
|
void ResetVehicleHash()
|
|
|
{
|
|
|
Vehicle *v;
|
|
|
FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
|
|
|
memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
|
|
|
memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
|
|
|
FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
|
|
|
memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
|
|
|
memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
|
|
|
}
|
|
|
|
|
|
void ResetVehicleColourMap()
|
|
|
{
|
|
|
Vehicle *v;
|
|
|
FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* List of vehicles that should check for autoreplace this tick.
|
|
|
* Mapping of vehicle -> leave depot immediately after autoreplace.
|
|
|
*/
|
|
|
typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
|
|
|
static AutoreplaceMap _vehicles_to_autoreplace;
|
|
|
|
|
|
void InitializeVehicles()
|
|
|
{
|
|
|
_vehicles_to_autoreplace.Reset();
|
|
|
ResetVehiclePosHash();
|
|
|
ResetVehicleHash();
|
|
|
}
|
|
|
|
|
|
uint CountVehiclesInChain(const Vehicle *v)
|
|
|
{
|
|
|
uint count = 0;
|
|
|
do count++; while ((v = v->Next()) != NULL);
|
|
|
return count;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Check if a vehicle is counted in num_engines in each company struct
|
|
|
* @return true if the vehicle is counted in num_engines
|
|
@@ -782,26 +782,26 @@ Vehicle::~Vehicle()
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
/* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
|
|
|
* it may happen that vehicle chain is deleted when visible */
|
|
|
if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
|
|
|
|
|
|
Vehicle *v = this->Next();
|
|
|
this->SetNext(NULL);
|
|
|
|
|
|
delete v;
|
|
|
|
|
|
UpdateVehiclePosHash(this, INVALID_COORD, 0);
|
|
|
UpdateNewVehiclePosHash(this, true);
|
|
|
UpdateVehicleTileHash(this, true);
|
|
|
UpdateVehicleViewportHash(this, INVALID_COORD, 0);
|
|
|
DeleteVehicleNews(this->index, INVALID_STRING_ID);
|
|
|
DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Adds a vehicle to the list of vehicles that visited a depot this tick
|
|
|
* @param *v vehicle to add
|
|
|
*/
|
|
|
void VehicleEnteredDepotThisTick(Vehicle *v)
|
|
|
{
|
|
|
/* Vehicle should stop in the depot if it was in 'stopping' state */
|
|
|
_vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
|
|
@@ -990,35 +990,35 @@ void ViewportAddVehicles(DrawPixelInfo *
|
|
|
|
|
|
if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
|
|
|
yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
|
|
|
yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
|
|
|
} else {
|
|
|
/* scan whole column */
|
|
|
yl = 0;
|
|
|
yu = 0x3F << 6;
|
|
|
}
|
|
|
|
|
|
for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
|
|
|
for (int x = xl;; x = (x + 1) & 0x3F) {
|
|
|
const Vehicle *v = _vehicle_position_hash[x + y]; // already masked & 0xFFF
|
|
|
const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
|
|
|
|
|
|
while (v != NULL) {
|
|
|
if (!(v->vehstatus & VS_HIDDEN) &&
|
|
|
l <= v->coord.right &&
|
|
|
t <= v->coord.bottom &&
|
|
|
r >= v->coord.left &&
|
|
|
b >= v->coord.top) {
|
|
|
DoDrawVehicle(v);
|
|
|
}
|
|
|
v = v->next_hash;
|
|
|
v = v->hash_viewport_next;
|
|
|
}
|
|
|
|
|
|
if (x == xu) break;
|
|
|
}
|
|
|
|
|
|
if (y == yu) break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Find the vehicle close to the clicked coordinates.
|
|
|
* @param vp Viewport clicked in.
|
|
@@ -1385,34 +1385,34 @@ void VehicleEnterDepot(Vehicle *v)
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* Move a vehicle in the game state; that is moving its position in
|
|
|
* the position hashes and marking its location in the viewport dirty
|
|
|
* if requested.
|
|
|
* @param v vehicle to move
|
|
|
* @param update_viewport whether to dirty the viewport
|
|
|
*/
|
|
|
void VehicleMove(Vehicle *v, bool update_viewport)
|
|
|
{
|
|
|
UpdateNewVehiclePosHash(v, false);
|
|
|
UpdateVehicleTileHash(v, false);
|
|
|
|
|
|
int img = v->cur_image;
|
|
|
Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
|
|
|
const Sprite *spr = GetSprite(img, ST_NORMAL);
|
|
|
|
|
|
pt.x += spr->x_offs;
|
|
|
pt.y += spr->y_offs;
|
|
|
|
|
|
UpdateVehiclePosHash(v, pt.x, pt.y);
|
|
|
UpdateVehicleViewportHash(v, pt.x, pt.y);
|
|
|
|
|
|
Rect old_coord = v->coord;
|
|
|
v->coord.left = pt.x;
|
|
|
v->coord.top = pt.y;
|
|
|
v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
|
|
|
v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
|
|
|
|
|
|
if (update_viewport) {
|
|
|
MarkAllViewportsDirty(
|
|
|
min(old_coord.left, v->coord.left),
|
|
|
min(old_coord.top, v->coord.top),
|
|
|
max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
|