Changeset - r7413:3ccdde9800e0
[Not reviewed]
master
0 15 0
rubidium - 17 years ago 2007-08-05 21:20:55
rubidium@openttd.org
(svn r10799) -Fix: only calling QuickFree and not the destructor on pool cleanups might cause memory leaks due to the way C++ works.
15 files changed with 48 insertions and 67 deletions:
0 comments (0 inline, 0 general)
src/depot.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/** @file depot.cpp */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "vehicle.h"
 
#include "depot.h"
 
#include "functions.h"
 
#include "landscape.h"
 
#include "tile.h"
 
#include "map.h"
 
#include "table/strings.h"
 
#include "saveload.h"
 
#include "order.h"
 

	
 
DEFINE_OLD_POOL_GENERIC(Depot, Depot)
 

	
 
/**
 
 * Gets a depot from a tile
 
 *
 
 * @return Returns the depot if the tile had a depot, else it returns NULL
 
 */
 
Depot *GetDepotByTile(TileIndex tile)
 
{
 
	Depot *depot;
 

	
 
	FOR_ALL_DEPOTS(depot) {
 
		if (depot->xy == tile) return depot;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/**
 
 * Clean up a depot
 
 */
 
Depot::~Depot()
 
{
 
	if (CleaningPool()) return;
 

	
 
	/* Clear the depot from all order-lists */
 
	RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->index);
 

	
 
	/* Delete the depot-window */
 
	DeleteWindowById(WC_VEHICLE_DEPOT, this->xy);
 
	this->xy = 0;
 
}
 

	
 
void InitializeDepots()
 
{
 
	_Depot_pool.CleanPool();
 
	_Depot_pool.AddBlockToPool();
 
}
 

	
 

	
 
static const SaveLoad _depot_desc[] = {
 
	SLE_CONDVAR(Depot, xy,         SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(Depot, xy,         SLE_UINT32,                 6, SL_MAX_VERSION),
 
	    SLE_VAR(Depot, town_index, SLE_UINT16),
 
	SLE_END()
 
};
 

	
 
static void Save_DEPT()
 
{
 
	Depot *depot;
 

	
 
	FOR_ALL_DEPOTS(depot) {
 
		SlSetArrayIndex(depot->index);
 
		SlObject(depot, _depot_desc);
 
	}
 
}
 

	
 
static void Load_DEPT()
 
{
 
	int index;
 

	
 
	while ((index = SlIterateArray()) != -1) {
 
		Depot *depot = new (index) Depot();
 
		SlObject(depot, _depot_desc);
 
	}
 
}
 

	
 
extern const ChunkHandler _depot_chunk_handlers[] = {
 
	{ 'DEPT', Save_DEPT, Load_DEPT, CH_ARRAY | CH_LAST},
 
};
src/group.h
Show inline comments
 
/* $Id$ */
 

	
 
/** @file group.h */
 

	
 
#ifndef GROUP_H
 
#define GROUP_H
 

	
 
#include "oldpool.h"
 

	
 
enum {
 
	ALL_GROUP     = 0xFFFD,
 
	DEFAULT_GROUP = 0xFFFE,
 
	INVALID_GROUP = 0xFFFF,
 
};
 

	
 
struct Group;
 
DECLARE_OLD_POOL(Group, Group, 5, 2047)
 

	
 
struct Group : PoolItem<Group, GroupID, &_Group_pool> {
 
	StringID string_id;                     ///< Group Name
 

	
 
	uint16 num_vehicle;                     ///< Number of vehicles wich belong to the group
 
	PlayerID owner;                         ///< Group Owner
 
	VehicleTypeByte vehicle_type;           ///< Vehicle type of the group
 

	
 
	bool replace_protection;                ///< If set to true, the global autoreplace have no effect on the group
 
	uint16 num_engines[TOTAL_NUM_ENGINES];  ///< Caches the number of engines of each type the player owns (no need to save this)
 

	
 
	Group(StringID str = STR_NULL);
 
	virtual ~Group();
 

	
 
	void QuickFree();
 

	
 
	bool IsValid() const;
 
};
 

	
 

	
 
static inline bool IsValidGroupID(GroupID index)
 
{
 
	return index < GetGroupPoolSize() && GetGroup(index)->IsValid();
 
}
 

	
 
static inline bool IsDefaultGroupID(GroupID index)
 
{
 
	return index == DEFAULT_GROUP;
 
}
 

	
 
/**
 
 * Checks if a GroupID stands for all vehicles of a player
 
 * @param id_g The GroupID to check
 
 * @return true is id_g is identical to ALL_GROUP
 
 */
 
static inline bool IsAllGroupID(GroupID id_g)
 
{
 
	return id_g == ALL_GROUP;
 
}
 

	
 
#define FOR_ALL_GROUPS_FROM(g, start) for (g = GetGroup(start); g != NULL; g = (g->index + 1U < GetGroupPoolSize()) ? GetGroup(g->index + 1) : NULL) if (g->IsValid())
 
#define FOR_ALL_GROUPS(g) FOR_ALL_GROUPS_FROM(g, 0)
 

	
 
/**
 
 * Get the current size of the GroupPool
 
 */
 
static inline uint GetGroupArraySize(void)
 
{
 
	const Group *g;
 
	uint num = 0;
 

	
 
	FOR_ALL_GROUPS(g) num++;
 

	
 
	return num;
 
}
 

	
 
/**
 
 * Get the number of engines with EngineID id_e in the group with GroupID
 
 * id_g
 
 * @param id_g The GroupID of the group used
 
 * @param id_e The EngineID of the engine to count
 
 * @return The number of engines with EngineID id_e in the group
 
 */
 
static inline uint GetGroupNumEngines(GroupID id_g, EngineID id_e)
 
{
 
	if (IsValidGroupID(id_g)) return GetGroup(id_g)->num_engines[id_e];
 

	
 
	uint num = GetPlayer(_local_player)->num_engines[id_e];
 
	if (!IsDefaultGroupID(id_g)) return num;
 

	
 
	const Group *g;
 
	FOR_ALL_GROUPS(g) num -= g->num_engines[id_e];
 
	return num;
 
}
 

	
 
static inline void IncreaseGroupNumVehicle(GroupID id_g)
 
{
 
	if (IsValidGroupID(id_g)) GetGroup(id_g)->num_vehicle++;
 
}
 

	
 
static inline void DecreaseGroupNumVehicle(GroupID id_g)
 
{
 
	if (IsValidGroupID(id_g)) GetGroup(id_g)->num_vehicle--;
 
}
 

	
 

	
 
void InitializeGroup();
 
void SetTrainGroupID(Vehicle *v, GroupID grp);
 
void UpdateTrainGroupID(Vehicle *v);
 
void RemoveVehicleFromGroup(const Vehicle *v);
 
void RemoveAllGroupsForPlayer(const Player *p);
 

	
 
#endif /* GROUP_H */
src/group_cmd.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/** @file group_cmd.cpp Handling of the engine groups */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "variables.h"
 
#include "functions.h"
 
#include "player.h"
 
#include "table/strings.h"
 
#include "command.h"
 
#include "vehicle.h"
 
#include "saveload.h"
 
#include "debug.h"
 
#include "group.h"
 
#include "train.h"
 
#include "aircraft.h"
 
#include "string.h"
 
#include "window.h"
 
#include "vehicle_gui.h"
 
#include "strings.h"
 
#include "misc/autoptr.hpp"
 

	
 
/**
 
 * Update the num engines of a groupID. Decrease the old one and increase the new one
 
 * @note called in SetTrainGroupID and UpdateTrainGroupID
 
 * @param i     EngineID we have to update
 
 * @param old_g index of the old group
 
 * @param new_g index of the new group
 
 */
 
static inline void UpdateNumEngineGroup(EngineID i, GroupID old_g, GroupID new_g)
 
{
 
	if (old_g != new_g) {
 
		/* Decrease the num engines of EngineID i of the old group if it's not the default one */
 
		if (!IsDefaultGroupID(old_g) && IsValidGroupID(old_g)) GetGroup(old_g)->num_engines[i]--;
 

	
 
		/* Increase the num engines of EngineID i of the new group if it's not the new one */
 
		if (!IsDefaultGroupID(new_g) && IsValidGroupID(new_g)) GetGroup(new_g)->num_engines[i]++;
 
	}
 
}
 

	
 

	
 
DEFINE_OLD_POOL_GENERIC(Group, Group)
 

	
 

	
 
Group::Group(StringID str)
 
{
 
	this->string_id = str;
 
}
 

	
 
Group::~Group()
 
{
 
	this->QuickFree();
 
	DeleteName(this->string_id);
 
	this->string_id = STR_NULL;
 
}
 

	
 
void Group::QuickFree()
 
{
 
	DeleteName(this->string_id);
 
}
 

	
 
bool Group::IsValid() const
 
{
 
	return this->string_id != STR_NULL;
 
}
 

	
 
void InitializeGroup(void)
 
{
 
	_Group_pool.CleanPool();
 
	_Group_pool.AddBlockToPool();
 
}
 

	
 

	
 
static WindowClass GetWCForVT(VehicleType vt)
 
{
 
	switch (vt) {
 
		default:
 
		case VEH_TRAIN:    return WC_TRAINS_LIST;
 
		case VEH_ROAD:     return WC_ROADVEH_LIST;
 
		case VEH_SHIP:     return WC_SHIPS_LIST;
 
		case VEH_AIRCRAFT: return WC_AIRCRAFT_LIST;
 
	}
 
}
 

	
 

	
 
/**
 
 * Create a new vehicle group.
 
 * @param tile unused
 
 * @param p1   vehicle type
 
 * @param p2   unused
 
 */
 
CommandCost CmdCreateGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	VehicleType vt = (VehicleType)p1;
 
	if (!IsPlayerBuildableVehicleType(vt)) return CMD_ERROR;
 

	
 
	AutoPtrT<Group> g_auto_delete;
 

	
 
	Group *g = new Group(STR_EMPTY);
 
	if (g == NULL) return CMD_ERROR;
 

	
 
	g_auto_delete = g;
 

	
 
	if (flags & DC_EXEC) {
 
		g->owner = _current_player;
 
		g->replace_protection = false;
 
		g->vehicle_type = vt;
 

	
 
		InvalidateWindowData(GetWCForVT(vt), (vt << 11) | VLW_GROUP_LIST | _current_player);
 

	
 
		g_auto_delete.Detach();
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 

	
 
/**
 
 * Add all vehicles in the given group to the default group and then deletes the group.
 
 * @param tile unused
 
 * @param p1   index of array group
 
 *      - p1 bit 0-15 : GroupID
 
 * @param p2   unused
 
 */
 
CommandCost CmdDeleteGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (!IsValidGroupID(p1)) return CMD_ERROR;
 

	
 
	Group *g = GetGroup(p1);
 
	if (g->owner != _current_player) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		Vehicle *v;
 

	
 
		/* Add all vehicles belong to the group to the default group */
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->group_id == g->index && v->type == g->vehicle_type) v->group_id = DEFAULT_GROUP;
 
		}
 

	
 
		/* If we set an autoreplace for the group we delete, remove it. */
 
		if (_current_player < MAX_PLAYERS) {
 
			Player *p;
 
			EngineRenew *er;
 

	
 
			p = GetPlayer(_current_player);
 
			FOR_ALL_ENGINE_RENEWS(er) {
 
				if (er->group_id == g->index) RemoveEngineReplacementForPlayer(p, er->from, g->index, flags);
 
			}
 
		}
 

	
 
		VehicleType vt = g->vehicle_type;
 

	
 
		/* Delete the Replace Vehicle Windows */
 
		DeleteWindowById(WC_REPLACE_VEHICLE, g->vehicle_type);
 
		delete g;
 

	
 
		InvalidateWindowData(GetWCForVT(vt), (vt << 11) | VLW_GROUP_LIST | _current_player);
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
static bool IsUniqueGroupName(const char *name)
 
{
 
	const Group *g;
 
	char buf[512];
 

	
 
	FOR_ALL_GROUPS(g) {
 
		SetDParam(0, g->index);
 
		GetString(buf, STR_GROUP_NAME, lastof(buf));
 
		if (strcmp(buf, name) == 0) return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Rename a group
 
 * @param tile unused
 
 * @param p1   index of array group
 
 *   - p1 bit 0-15 : GroupID
 
 * @param p2   unused
 
 */
 
CommandCost CmdRenameGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (!IsValidGroupID(p1) || StrEmpty(_cmd_text)) return CMD_ERROR;
 

	
 
	Group *g = GetGroup(p1);
 
	if (g->owner != _current_player) return CMD_ERROR;
 

	
 
	if (!IsUniqueGroupName(_cmd_text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
 

	
 
	/* Create the name */
 
	StringID str = AllocateName(_cmd_text, 0);
 
	if (str == STR_NULL) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		/* Delete the old name */
 
		DeleteName(g->string_id);
 
		/* Assign the new one */
 
		g->string_id = str;
 

	
 
		InvalidateWindowData(GetWCForVT(g->vehicle_type), (g->vehicle_type << 11) | VLW_GROUP_LIST | _current_player);
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 

	
 
/**
 
 * Add a vehicle to a group
 
 * @param tile unused
 
 * @param p1   index of array group
 
 *   - p1 bit 0-15 : GroupID
 
 * @param p2   vehicle to add to a group
 
 *   - p2 bit 0-15 : VehicleID
 
 */
 
CommandCost CmdAddVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	GroupID new_g = p1;
 

	
 
	if (!IsValidVehicleID(p2) || (!IsValidGroupID(new_g) && !IsDefaultGroupID(new_g))) return CMD_ERROR;
 

	
 
	if (IsValidGroupID(new_g)) {
 
		Group *g = GetGroup(new_g);
 
		if (g->owner != _current_player) return CMD_ERROR;
 
	}
 

	
 
	Vehicle *v = GetVehicle(p2);
 
	if (v->owner != _current_player || !v->IsPrimaryVehicle()) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		DecreaseGroupNumVehicle(v->group_id);
 
		IncreaseGroupNumVehicle(new_g);
 

	
 
		switch (v->type) {
 
			default: NOT_REACHED();
 
			case VEH_TRAIN:
 
				SetTrainGroupID(v, new_g);
 
				break;
 
			case VEH_ROAD:
 
			case VEH_SHIP:
 
			case VEH_AIRCRAFT:
 
				if (IsEngineCountable(v)) UpdateNumEngineGroup(v->engine_type, v->group_id, new_g);
 
				v->group_id = new_g;
 
				break;
 
		}
 

	
 
		/* Update the Replace Vehicle Windows */
 
		InvalidateWindow(WC_REPLACE_VEHICLE, v->type);
 
		InvalidateWindowData(GetWCForVT(v->type), (v->type << 11) | VLW_GROUP_LIST | _current_player);
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
 * Add all shared vehicles of all vehicles from a group
 
 * @param tile unused
 
 * @param p1   index of group array
 
 *  - p1 bit 0-15 : GroupID
 
 * @param p2   type of vehicles
 
 */
 
CommandCost CmdAddSharedVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	VehicleType type = (VehicleType)p2;
 
	if (!IsValidGroupID(p1) || !IsPlayerBuildableVehicleType(type)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		Vehicle *v;
 
		VehicleType type = (VehicleType)p2;
 
		GroupID id_g = p1;
 

	
 
		/* Find the first front engine which belong to the group id_g
 
		 * then add all shared vehicles of this front engine to the group id_g */
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->type == type && v->IsPrimaryVehicle()) {
 
				if (v->group_id != id_g) continue;
 

	
 
				/* For each shared vehicles add it to the group */
 
				for (Vehicle *v2 = GetFirstVehicleFromSharedList(v); v2 != NULL; v2 = v2->next_shared) {
 
					if (v2->group_id != id_g) CmdAddVehicleGroup(tile, flags, id_g, v2->index);
 
				}
 
			}
 
		}
 

	
 
		InvalidateWindowData(GetWCForVT(type), (type << 11) | VLW_GROUP_LIST | _current_player);
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 

	
 
/**
 
 * Remove all vehicles from a group
 
 * @param tile unused
 
 * @param p1   index of group array
 
 * - p1 bit 0-15 : GroupID
 
 * @param p2   type of vehicles
 
 */
 
CommandCost CmdRemoveAllVehiclesGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	VehicleType type = (VehicleType)p2;
 
	if (!IsValidGroupID(p1) || !IsPlayerBuildableVehicleType(type)) return CMD_ERROR;
 

	
 
	Group *g = GetGroup(p1);
 
	if (g->owner != _current_player) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		GroupID old_g = p1;
 
		Vehicle *v;
 

	
 
		/* Find each Vehicle that belongs to the group old_g and add it to the default group */
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->type == type && v->IsPrimaryVehicle()) {
 
				if (v->group_id != old_g) continue;
 

	
 
				/* Add The Vehicle to the default group */
 
				CmdAddVehicleGroup(tile, flags, DEFAULT_GROUP, v->index);
 
			}
 
		}
 

	
 
		InvalidateWindowData(GetWCForVT(type), (type << 11) | VLW_GROUP_LIST | _current_player);
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 

	
 
/**
 
 * (Un)set global replace protection from a group
 
 * @param tile unused
 
 * @param p1   index of group array
 
 * - p1 bit 0-15 : GroupID
 
 * @param p2
 
 * - p2 bit 0    : 1 to set or 0 to clear protection.
 
 */
 
CommandCost CmdSetGroupReplaceProtection(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (!IsValidGroupID(p1)) return CMD_ERROR;
 

	
 
	Group *g = GetGroup(p1);
 
	if (g->owner != _current_player) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		g->replace_protection = HASBIT(p2, 0);
 

	
 
		InvalidateWindowData(GetWCForVT(g->vehicle_type), (g->vehicle_type << 11) | VLW_GROUP_LIST | _current_player);
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
 * Decrease the num_vehicle variable before delete an front engine from a group
 
 * @note Called in CmdSellRailWagon and DeleteLasWagon,
 
 * @param v     FrontEngine of the train we want to remove.
 
 */
 
void RemoveVehicleFromGroup(const Vehicle *v)
 
{
 
	if (!v->IsValid() || !(v->HasFront() && v->IsPrimaryVehicle())) return;
 

	
 
	if (!IsDefaultGroupID(v->group_id)) DecreaseGroupNumVehicle(v->group_id);
 
}
 

	
 

	
 
/**
 
 * Affect the groupID of a train to new_g.
 
 * @note called in CmdAddVehicleGroup and CmdMoveRailVehicle
 
 * @param v     First vehicle of the chain.
 
 * @param new_g index of array group
 
 */
 
void SetTrainGroupID(Vehicle *v, GroupID new_g)
 
{
 
	if (!IsValidGroupID(new_g) && !IsDefaultGroupID(new_g)) return;
 

	
 
	assert(v->IsValid() && v->type == VEH_TRAIN && IsFrontEngine(v));
 

	
 
	for (Vehicle *u = v; u != NULL; u = u->next) {
 
		if (IsEngineCountable(u)) UpdateNumEngineGroup(u->engine_type, u->group_id, new_g);
 

	
 
		u->group_id = new_g;
 
	}
 

	
 
	/* Update the Replace Vehicle Windows */
 
	InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
 
}
 

	
 

	
 
/**
 
 * Recalculates the groupID of a train. Should be called each time a vehicle is added
 
 * to/removed from the chain,.
 
 * @note this needs to be called too for 'wagon chains' (in the depot, without an engine)
 
 * @note Called in CmdBuildRailVehicle, CmdBuildRailWagon, CmdMoveRailVehicle, CmdSellRailWagon
 
 * @param v First vehicle of the chain.
 
 */
 
void UpdateTrainGroupID(Vehicle *v)
 
{
 
	assert(v->IsValid() && v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v)));
 

	
 
	GroupID new_g = IsFrontEngine(v) ? v->group_id : (GroupID)DEFAULT_GROUP;
 
	for (Vehicle *u = v; u != NULL; u = u->next) {
 
		if (IsEngineCountable(u)) UpdateNumEngineGroup(u->engine_type, u->group_id, new_g);
 

	
 
		u->group_id = new_g;
 
	}
 

	
 
	/* Update the Replace Vehicle Windows */
 
	InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
 
}
 

	
 

	
 
void RemoveAllGroupsForPlayer(const Player *p)
 
{
 
	Group *g;
 

	
 
	FOR_ALL_GROUPS(g) {
 
		if (p->index == g->owner) delete g;
 
	}
 
}
 

	
 

	
 
static const SaveLoad _group_desc[] = {
 
  SLE_VAR(Group, string_id,          SLE_UINT16),
 
  SLE_VAR(Group, num_vehicle,        SLE_UINT16),
 
  SLE_VAR(Group, owner,              SLE_UINT8),
 
  SLE_VAR(Group, vehicle_type,       SLE_UINT8),
 
  SLE_VAR(Group, replace_protection, SLE_BOOL),
 
  SLE_END()
 
};
 

	
 

	
 
static void Save_GROUP(void)
 
{
 
	Group *g;
 

	
 
	FOR_ALL_GROUPS(g) {
 
		SlSetArrayIndex(g->index);
 
		SlObject(g, _group_desc);
 
	}
 
}
 

	
 

	
 
static void Load_GROUP(void)
 
{
 
	int index;
 

	
 
	while ((index = SlIterateArray()) != -1) {
 
		Group *g = new (index) Group();
 
		SlObject(g, _group_desc);
 
	}
 
}
 

	
 
extern const ChunkHandler _group_chunk_handlers[] = {
 
	{ 'GRPS', Save_GROUP, Load_GROUP, CH_ARRAY | CH_LAST},
 
};
src/oldpool.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/** @file oldpool.cpp */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "oldpool.h"
 
#include "helpers.hpp"
 

	
 
/**
 
 * Clean a pool in a safe way (does free all blocks)
 
 */
 
void OldMemoryPoolBase::CleanPool()
 
{
 
	uint i;
 

	
 
	DEBUG(misc, 4, "[Pool] (%s) cleaning pool..", this->name);
 

	
 
	this->cleaning_pool = true;
 
	/* Free all blocks */
 
	for (i = 0; i < this->current_blocks; i++) {
 
		if (this->clean_block_proc != NULL) {
 
			this->clean_block_proc(i * (1 << this->block_size_bits), (i + 1) * (1 << this->block_size_bits) - 1);
 
		}
 
		free(this->blocks[i]);
 
	}
 
	this->cleaning_pool = false;
 

	
 
	/* Free the block itself */
 
	free(this->blocks);
 

	
 
	/* Clear up some critical data */
 
	this->total_items = 0;
 
	this->current_blocks = 0;
 
	this->blocks = NULL;
 
	this->first_free_index = 0;
 
}
 

	
 
/**
 
 * This function tries to increase the size of array by adding
 
 *  1 block too it
 
 *
 
 * @return Returns false if the pool could not be increased
 
 */
 
bool OldMemoryPoolBase::AddBlockToPool()
 
{
 
	/* Is the pool at his max? */
 
	if (this->max_blocks == this->current_blocks) return false;
 

	
 
	this->total_items = (this->current_blocks + 1) * (1 << this->block_size_bits);
 

	
 
	DEBUG(misc, 4, "[Pool] (%s) increasing size of pool to %d items (%d bytes)", this->name, this->total_items, this->total_items * this->item_size);
 

	
 
	/* Increase the poolsize */
 
	this->blocks = ReallocT(this->blocks, this->current_blocks + 1);
 
	if (this->blocks == NULL) error("Pool: (%s) could not allocate memory for blocks", this->name);
 

	
 
	/* Allocate memory to the new block item */
 
	this->blocks[this->current_blocks] = MallocT<byte>(this->item_size * (1 << this->block_size_bits));
 
	if (this->blocks[this->current_blocks] == NULL)
 
		error("Pool: (%s) could not allocate memory for blocks", this->name);
 

	
 
	/* Clean the content of the new block */
 
	memset(this->blocks[this->current_blocks], 0, this->item_size * (1 << this->block_size_bits));
 

	
 
	/* Call a custom function if defined (e.g. to fill indexes) */
 
	if (this->new_block_proc != NULL) this->new_block_proc(this->current_blocks * (1 << this->block_size_bits));
 

	
 
	/* We have a new block */
 
	this->current_blocks++;
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Adds blocks to the pool if needed (and possible) till index fits inside the pool
 
 *
 
 * @return Returns false if adding failed
 
 */
 
bool OldMemoryPoolBase::AddBlockIfNeeded(uint index)
 
{
 
	while (index >= this->total_items) {
 
		if (!this->AddBlockToPool()) return false;
 
	}
 

	
 
	return true;
 
}
src/oldpool.h
Show inline comments
 
/* $Id$ */
 

	
 
/** @file oldpool.h */
 

	
 
#ifndef OLDPOOL_H
 
#define OLDPOOL_H
 

	
 
/* The function that is called after a new block is added
 
     start_item is the first item of the new made block */
 
typedef void OldMemoryPoolNewBlock(uint start_item);
 
/* The function that is called before a block is cleaned up */
 
typedef void OldMemoryPoolCleanBlock(uint start_item, uint end_item);
 

	
 
/**
 
 * Stuff for dynamic vehicles. Use the wrappers to access the OldMemoryPool
 
 *  please try to avoid manual calls!
 
 */
 
struct OldMemoryPoolBase {
 
	void CleanPool();
 
	bool AddBlockToPool();
 
	bool AddBlockIfNeeded(uint index);
 

	
 
protected:
 
	OldMemoryPoolBase(const char *name, uint max_blocks, uint block_size_bits, uint item_size,
 
				OldMemoryPoolNewBlock *new_block_proc, OldMemoryPoolCleanBlock *clean_block_proc) :
 
		name(name), max_blocks(max_blocks), block_size_bits(block_size_bits),
 
		new_block_proc(new_block_proc), clean_block_proc(clean_block_proc), current_blocks(0),
 
		total_items(0), item_size(item_size), first_free_index(0), blocks(NULL) {}
 
		total_items(0), cleaning_pool(false), item_size(item_size), first_free_index(0), blocks(NULL) {}
 

	
 
	const char* name;     ///< Name of the pool (just for debugging)
 

	
 
	const uint max_blocks;      ///< The max amount of blocks this pool can have
 
	const uint block_size_bits; ///< The size of each block in bits
 

	
 
	/// Pointer to a function that is called after a new block is added
 
	OldMemoryPoolNewBlock *new_block_proc;
 
	/// Pointer to a function that is called to clean a block
 
	OldMemoryPoolCleanBlock *clean_block_proc;
 

	
 
	uint current_blocks;        ///< How many blocks we have in our pool
 
	uint total_items;           ///< How many items we now have in this pool
 

	
 
	bool cleaning_pool;         ///< Are we currently cleaning the pool?
 
public:
 
	const uint item_size;       ///< How many bytes one block is
 
	uint first_free_index;      ///< The index of the first free pool item in this pool
 
	byte **blocks;              ///< An array of blocks (one block hold all the items)
 

	
 
	/**
 
	 * Get the size of this pool, i.e. the total number of items you
 
	 * can put into it at the current moment; the pool might still
 
	 * be able to increase the size of the pool.
 
	 * @return the size of the pool
 
	 */
 
	inline uint GetSize() const
 
	{
 
		return this->total_items;
 
	}
 

	
 
	/**
 
	 * Can this pool allocate more blocks, i.e. is the maximum amount
 
	 * of allocated blocks not yet reached?
 
	 * @return the if and only if the amount of allocable blocks is
 
	 *         less than the amount of allocated blocks.
 
	 */
 
	inline bool CanAllocateMoreBlocks() const
 
	{
 
		return this->current_blocks < this->max_blocks;
 
	}
 

	
 
	/**
 
	 * Get the maximum number of allocable blocks.
 
	 * @return the numebr of blocks
 
	 */
 
	inline uint GetBlockCount() const
 
	{
 
		return this->current_blocks;
 
	}
 

	
 
	/**
 
	 * Get the name of this pool.
 
	 * @return the name
 
	 */
 
	inline const char *GetName() const
 
	{
 
		return this->name;
 
	}
 

	
 
	/**
 
	 * Is the pool in the cleaning phase?
 
	 * @return true if it is
 
	 */
 
	inline bool CleaningPool() const
 
	{
 
		return this->cleaning_pool;
 
	}
 
};
 

	
 
template <typename T>
 
struct OldMemoryPool : public OldMemoryPoolBase {
 
	OldMemoryPool(const char *name, uint max_blocks, uint block_size_bits, uint item_size,
 
				OldMemoryPoolNewBlock *new_block_proc, OldMemoryPoolCleanBlock *clean_block_proc) :
 
		OldMemoryPoolBase(name, max_blocks, block_size_bits, item_size, new_block_proc, clean_block_proc) {}
 

	
 
	/**
 
	 * Get the pool entry at the given index.
 
	 * @param index the index into the pool
 
	 * @pre index < this->GetSize()
 
	 * @return the pool entry.
 
	 */
 
	inline T *Get(uint index) const
 
	{
 
		assert(index < this->GetSize());
 
		return (T*)(this->blocks[index >> this->block_size_bits] +
 
				(index & ((1 << this->block_size_bits) - 1)) * this->item_size);
 
	}
 
};
 

	
 
/**
 
 * Generic function to initialize a new block in a pool.
 
 * @param start_item the first item that needs to be initialized
 
 */
 
template <typename T, OldMemoryPool<T> *Tpool>
 
static void PoolNewBlock(uint start_item)
 
{
 
	for (T *t = Tpool->Get(start_item); t != NULL; t = (t->index + 1U < Tpool->GetSize()) ? Tpool->Get(t->index + 1U) : NULL) {
 
		t = new (t) T();
 
		t->index = start_item++;
 
	}
 
}
 

	
 
/**
 
 * Generic function to free a new block in a pool.
 
 * This function uses QuickFree that is intended to only free memory that would be lost if the pool is freed.
 
 * @param start_item the first item that needs to be cleaned
 
 * @param end_item   the last item that needs to be cleaned
 
 */
 
template <typename T, OldMemoryPool<T> *Tpool>
 
static void PoolCleanBlock(uint start_item, uint end_item)
 
{
 
	for (uint i = start_item; i <= end_item; i++) {
 
		T *t = Tpool->Get(i);
 
		if (t->IsValid()) {
 
			t->QuickFree();
 
		}
 
		delete t;
 
	}
 
}
 

	
 

	
 
/**
 
 * Generalization for all pool items that are saved in the savegame.
 
 * It specifies all the mechanics to access the pool easily.
 
 */
 
template <typename T, typename Tid, OldMemoryPool<T> *Tpool>
 
struct PoolItem {
 
	/**
 
	 * The pool-wide index of this object.
 
	 */
 
	Tid index;
 

	
 
	/**
 
	 * We like to have the correct class destructed.
 
	 */
 
	virtual ~PoolItem()
 
	{
 
		if (this->index < Tpool->first_free_index) Tpool->first_free_index = this->index;
 
	}
 

	
 
	/**
 
	 * Called on each object when the pool is being destroyed, so one
 
	 * can free allocated memory without the need for freeing for
 
	 * example orders.
 
	 */
 
	virtual void QuickFree()
 
	{
 
	}
 

	
 
	/**
 
	 * An overriden version of new that allocates memory on the pool.
 
	 * @param size the size of the variable (unused)
 
	 * @return the memory that is 'allocated'
 
	 */
 
	void *operator new(size_t size)
 
	{
 
		return AllocateRaw();
 
	}
 

	
 
	/**
 
	 * 'Free' the memory allocated by the overriden new.
 
	 * @param p the memory to 'free'
 
	 */
 
	void operator delete(void *p)
 
	{
 
	}
 

	
 
	/**
 
	 * An overriden version of new, so you can directly allocate a new object with
 
	 * the correct index when one is loading the savegame.
 
	 * @param size  the size of the variable (unused)
 
	 * @param index the index of the object
 
	 * @return the memory that is 'allocated'
 
	 */
 
	void *operator new(size_t size, int index)
 
	{
 
		if (!Tpool->AddBlockIfNeeded(index)) error("%s: failed loading savegame: too many %s", Tpool->GetName(), Tpool->GetName());
 

	
 
		return Tpool->Get(index);
 
	}
 

	
 
	/**
 
	 * 'Free' the memory allocated by the overriden new.
 
	 * @param p     the memory to 'free'
 
	 * @param index the original parameter given to create the memory
 
	 */
 
	void operator delete(void *p, int index)
 
	{
 
	}
 

	
 
	/**
 
	 * An overriden version of new, so you can use the vehicle instance
 
	 * instead of a newly allocated piece of memory.
 
	 * @param size the size of the variable (unused)
 
	 * @param pn   the already existing object to use as 'storage' backend
 
	 * @return the memory that is 'allocated'
 
	 */
 
	void *operator new(size_t size, T *pn)
 
	{
 
		return pn;
 
	}
 

	
 
	/**
 
	 * 'Free' the memory allocated by the overriden new.
 
	 * @param p  the memory to 'free'
 
	 * @param pn the pointer that was given to 'new' on creation.
 
	 */
 
	void operator delete(void *p, T *pn)
 
	{
 
	}
 

	
 
	/**
 
	 * Is this a valid object or not?
 
	 * @return true if and only if it is valid
 
	 */
 
	virtual bool IsValid() const
 
	{
 
		return false;
 
	}
 

	
 
protected:
 
	/**
 
	 * Allocate a pool item; possibly allocate a new block in the pool.
 
	 * @return the allocated pool item (or NULL when the pool is full).
 
	 */
 
	static T *AllocateRaw()
 
	static inline T *AllocateRaw()
 
	{
 
		return AllocateRaw(Tpool->first_free_index);
 
	}
 

	
 
	/**
 
	 * Allocate a pool item; possibly allocate a new block in the pool.
 
	 * @param first the first pool item to start searching
 
	 * @return the allocated pool item (or NULL when the pool is full).
 
	 */
 
	static T *AllocateRaw(uint &first)
 
	static inline T *AllocateRaw(uint &first)
 
	{
 
		uint last_minus_one = Tpool->GetSize() - 1;
 

	
 
		for (T *t = Tpool->Get(first); t != NULL; t = (t->index < last_minus_one) ? Tpool->Get(t->index + 1U) : NULL) {
 
			if (!t->IsValid()) {
 
				first = t->index;
 
				Tid index = t->index;
 

	
 
				memset(t, 0, Tpool->item_size);
 
				t->index = index;
 
				return t;
 
			}
 
		}
 

	
 
		/* Check if we can add a block to the pool */
 
		if (Tpool->AddBlockToPool()) return AllocateRaw(first);
 

	
 
		return NULL;
 
	}
 

	
 
	/**
 
	 * Are we cleaning this pool?
 
	 * @return true if we are
 
	 */
 
	static inline bool CleaningPool()
 
	{
 
		return Tpool->CleaningPool();
 
	}
 
};
 

	
 

	
 
#define OLD_POOL_ENUM(name, type, block_size_bits, max_blocks) \
 
	enum { \
 
		name##_POOL_BLOCK_SIZE_BITS = block_size_bits, \
 
		name##_POOL_MAX_BLOCKS      = max_blocks \
 
	};
 

	
 

	
 
#define OLD_POOL_ACCESSORS(name, type) \
 
	static inline type* Get##name(uint index) { return _##name##_pool.Get(index);  } \
 
	static inline uint Get##name##PoolSize()  { return _##name##_pool.GetSize(); }
 

	
 

	
 
#define DECLARE_OLD_POOL(name, type, block_size_bits, max_blocks) \
 
	OLD_POOL_ENUM(name, type, block_size_bits, max_blocks) \
 
	extern OldMemoryPool<type> _##name##_pool; \
 
	OLD_POOL_ACCESSORS(name, type)
 

	
 

	
 
#define DEFINE_OLD_POOL(name, type, new_block_proc, clean_block_proc) \
 
	OldMemoryPool<type> _##name##_pool( \
 
		#name, name##_POOL_MAX_BLOCKS, name##_POOL_BLOCK_SIZE_BITS, sizeof(type), \
 
		new_block_proc, clean_block_proc);
 

	
 
#define DEFINE_OLD_POOL_GENERIC(name, type) \
 
	OldMemoryPool<type> _##name##_pool( \
 
		#name, name##_POOL_MAX_BLOCKS, name##_POOL_BLOCK_SIZE_BITS, sizeof(type), \
 
		PoolNewBlock<type, &_##name##_pool>, PoolCleanBlock<type, &_##name##_pool>);
 

	
 

	
 
#define STATIC_OLD_POOL(name, type, block_size_bits, max_blocks, new_block_proc, clean_block_proc) \
 
	OLD_POOL_ENUM(name, type, block_size_bits, max_blocks) \
 
	static DEFINE_OLD_POOL(name, type, new_block_proc, clean_block_proc) \
 
	OLD_POOL_ACCESSORS(name, type)
 

	
 
#endif /* OLDPOOL_H */
src/signs.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/** @file signs.cpp */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/strings.h"
 
#include "strings.h"
 
#include "functions.h"
 
#include "landscape.h"
 
#include "player.h"
 
#include "signs.h"
 
#include "saveload.h"
 
#include "command.h"
 
#include "variables.h"
 
#include "misc/autoptr.hpp"
 

	
 
SignID _new_sign_id;
 
uint _total_signs;
 

	
 
/* Initialize the sign-pool */
 
DEFINE_OLD_POOL_GENERIC(Sign, Sign)
 

	
 
Sign::Sign(StringID string)
 
{
 
	this->str = string;
 
}
 

	
 
Sign::~Sign()
 
{
 
	this->QuickFree();
 
	DeleteName(this->str);
 
	this->str = STR_NULL;
 
}
 

	
 
void Sign::QuickFree()
 
{
 
	DeleteName(this->str);
 
}
 

	
 
/**
 
 *
 
 * Update the coordinate of one sign
 
 * @param si Pointer to the Sign
 
 *
 
 */
 
static void UpdateSignVirtCoords(Sign *si)
 
{
 
	Point pt = RemapCoords(si->x, si->y, si->z);
 
	SetDParam(0, si->index);
 
	UpdateViewportSignPos(&si->sign, pt.x, pt.y - 6, STR_2806);
 
}
 

	
 
/**
 
 *
 
 * Update the coordinates of all signs
 
 *
 
 */
 
void UpdateAllSignVirtCoords()
 
{
 
	Sign *si;
 

	
 
	FOR_ALL_SIGNS(si) UpdateSignVirtCoords(si);
 

	
 
}
 

	
 
/**
 
 *
 
 * Marks the region of a sign as dirty
 
 *
 
 * @param si Pointer to the Sign
 
 */
 
static void MarkSignDirty(Sign *si)
 
{
 
	/* We use ZOOM_LVL_MAX here, as every viewport can have an other zoom,
 
		*  and there is no way for us to know which is the biggest. So make the
 
		*  biggest area dirty, and we are safe for sure. */
 
	MarkAllViewportsDirty(
 
		si->sign.left - 6,
 
		si->sign.top  - 3,
 
		si->sign.left + ScaleByZoom(si->sign.width_1 + 12, ZOOM_LVL_MAX),
 
		si->sign.top  + ScaleByZoom(12, ZOOM_LVL_MAX));
 
}
 

	
 
/**
 
 * Place a sign at the given coordinates. Ownership of sign has
 
 * no effect whatsoever except for the colour the sign gets for easy recognition,
 
 * but everybody is able to rename/remove it.
 
 * @param tile tile to place sign at
 
 * @param flags type of operation
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
CommandCost CmdPlaceSign(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	/* Try to locate a new sign */
 
	Sign *si = new Sign(STR_280A_SIGN);
 
	if (si == NULL) return_cmd_error(STR_2808_TOO_MANY_SIGNS);
 
	AutoPtrT<Sign> s_auto_delete = si;
 

	
 
	/* When we execute, really make the sign */
 
	if (flags & DC_EXEC) {
 
		int x = TileX(tile) * TILE_SIZE;
 
		int y = TileY(tile) * TILE_SIZE;
 

	
 
		si->x = x;
 
		si->y = y;
 
		si->owner = _current_player; // owner of the sign; just eyecandy
 
		si->z = GetSlopeZ(x, y);
 
		UpdateSignVirtCoords(si);
 
		MarkSignDirty(si);
 
		InvalidateWindow(WC_SIGN_LIST, 0);
 
		_sign_sort_dirty = true;
 
		_new_sign_id = si->index;
 
		_total_signs++;
 
		s_auto_delete.Detach();
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/** Rename a sign. If the new name of the sign is empty, we assume
 
 * the user wanted to delete it. So delete it. Ownership of signs
 
 * has no meaning/effect whatsoever except for eyecandy
 
 * @param tile unused
 
 * @param flags type of operation
 
 * @param p1 index of the sign to be renamed/removed
 
 * @param p2 unused
 
 * @return 0 if succesfull, otherwise CMD_ERROR
 
 */
 
CommandCost CmdRenameSign(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (!IsValidSignID(p1)) return CMD_ERROR;
 

	
 
	/* If _cmd_text 0 means the new text for the sign is non-empty.
 
	 * So rename the sign. If it is empty, it has no name, so delete it */
 
	if (_cmd_text[0] != '\0') {
 
		/* Create the name */
 
		StringID str = AllocateName(_cmd_text, 0);
 
		if (str == 0) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) {
 
			Sign *si = GetSign(p1);
 

	
 
			/* Delete the old name */
 
			DeleteName(si->str);
 
			/* Assign the new one */
 
			si->str = str;
 
			si->owner = _current_player;
 

	
 
			/* Update; mark sign dirty twice, because it can either becom longer, or shorter */
 
			MarkSignDirty(si);
 
			UpdateSignVirtCoords(si);
 
			MarkSignDirty(si);
 
			InvalidateWindow(WC_SIGN_LIST, 0);
 
			_sign_sort_dirty = true;
 
		} else {
 
			/* Free the name, because we did not assign it yet */
 
			DeleteName(str);
 
		}
 
	} else { // Delete sign
 
		if (flags & DC_EXEC) {
 
			Sign *si = GetSign(p1);
 

	
 
			MarkSignDirty(si);
 
			delete si;
 

	
 
			InvalidateWindow(WC_SIGN_LIST, 0);
 
			_sign_sort_dirty = true;
 
			_total_signs--;
 
		}
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
 * Callback function that is called after a sign is placed
 
 * @param success of the operation
 
 * @param tile unused
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
void CcPlaceSign(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		ShowRenameSignWindow(GetSign(_new_sign_id));
 
		ResetObjectToPlace();
 
	}
 
}
 

	
 
/**
 
 *
 
 * PlaceProc function, called when someone pressed the button if the
 
 *  sign-tool is selected
 
 * @param tile on which to place the sign
 
 */
 
void PlaceProc_Sign(TileIndex tile)
 
{
 
	DoCommandP(tile, 0, 0, CcPlaceSign, CMD_PLACE_SIGN | CMD_MSG(STR_2809_CAN_T_PLACE_SIGN_HERE));
 
}
 

	
 
/**
 
 *
 
 * Initialize the signs
 
 *
 
 */
 
void InitializeSigns()
 
{
 
	_total_signs = 0;
 
	_Sign_pool.CleanPool();
 
	_Sign_pool.AddBlockToPool();
 
}
 

	
 
static const SaveLoad _sign_desc[] = {
 
      SLE_VAR(Sign, str,   SLE_UINT16),
 
  SLE_CONDVAR(Sign, x,     SLE_FILE_I16 | SLE_VAR_I32, 0, 4),
 
  SLE_CONDVAR(Sign, y,     SLE_FILE_I16 | SLE_VAR_I32, 0, 4),
 
  SLE_CONDVAR(Sign, x,     SLE_INT32,                  5, SL_MAX_VERSION),
 
  SLE_CONDVAR(Sign, y,     SLE_INT32,                  5, SL_MAX_VERSION),
 
  SLE_CONDVAR(Sign, owner, SLE_UINT8,                  6, SL_MAX_VERSION),
 
      SLE_VAR(Sign, z,     SLE_UINT8),
 
	SLE_END()
 
};
 

	
 
/**
 
 *
 
 * Save all signs
 
 *
 
 */
 
static void Save_SIGN()
 
{
 
	Sign *si;
 

	
 
	FOR_ALL_SIGNS(si) {
 
		SlSetArrayIndex(si->index);
 
		SlObject(si, _sign_desc);
 
	}
 
}
 

	
 
/**
 
 *
 
 * Load all signs
 
 *
 
 */
 
static void Load_SIGN()
 
{
 
	_total_signs = 0;
 
	int index;
 
	while ((index = SlIterateArray()) != -1) {
 
		Sign *si = new (index) Sign();
 
		SlObject(si, _sign_desc);
 

	
 
		_total_signs++;
 
	}
 

	
 
	_sign_sort_dirty = true;
 
}
 

	
 
extern const ChunkHandler _sign_chunk_handlers[] = {
 
	{ 'SIGN', Save_SIGN, Load_SIGN, CH_ARRAY | CH_LAST},
 
};
src/signs.h
Show inline comments
 
/* $Id$ */
 

	
 
/** @file signs.h */
 

	
 
#ifndef SIGNS_H
 
#define SIGNS_H
 

	
 
#include "oldpool.h"
 

	
 
struct Sign;
 
DECLARE_OLD_POOL(Sign, Sign, 2, 16000)
 

	
 
struct Sign : PoolItem<Sign, SignID, &_Sign_pool> {
 
	StringID     str;
 
	ViewportSign sign;
 
	int32        x;
 
	int32        y;
 
	byte         z;
 
	PlayerByte   owner; // placed by this player. Anyone can delete them though. OWNER_NONE for gray signs from old games.
 

	
 
	/**
 
	 * Creates a new sign
 
	 */
 
	Sign(StringID string = STR_NULL);
 

	
 
	/** Destroy the sign */
 
	~Sign();
 

	
 
	bool IsValid() const { return this->str != STR_NULL; }
 

	
 
	void QuickFree();
 
};
 

	
 
enum {
 
	INVALID_SIGN = 0xFFFF,
 
};
 

	
 
extern SignID _new_sign_id;
 

	
 

	
 
static inline SignID GetMaxSignIndex()
 
{
 
	/* TODO - This isn't the real content of the function, but
 
	 *  with the new pool-system this will be replaced with one that
 
	 *  _really_ returns the highest index. Now it just returns
 
	 *  the next safe value we are sure about everything is below.
 
	 */
 
	return GetSignPoolSize() - 1;
 
}
 

	
 
static inline uint GetNumSigns()
 
{
 
	extern uint _total_signs;
 
	return _total_signs;
 
}
 

	
 
static inline bool IsValidSignID(uint index)
 
{
 
	return index < GetSignPoolSize() && GetSign(index)->IsValid();
 
}
 

	
 
#define FOR_ALL_SIGNS_FROM(ss, start) for (ss = GetSign(start); ss != NULL; ss = (ss->index + 1U < GetSignPoolSize()) ? GetSign(ss->index + 1U) : NULL) if (ss->IsValid())
 
#define FOR_ALL_SIGNS(ss) FOR_ALL_SIGNS_FROM(ss, 0)
 

	
 
VARDEF bool _sign_sort_dirty;
 

	
 
void UpdateAllSignVirtCoords();
 
void PlaceProc_Sign(TileIndex tile);
 

	
 
/* signs_gui.cpp */
 
void ShowRenameSignWindow(const Sign *si);
 

	
 
void ShowSignList();
 

	
 
#endif /* SIGNS_H */
src/station.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/** @file station.cpp */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "station_map.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "station.h"
 
#include "gfx.h"
 
#include "window.h"
 
#include "viewport.h"
 
#include "command.h"
 
#include "town.h"
 
#include "vehicle.h"
 
#include "news.h"
 
#include "saveload.h"
 
#include "economy.h"
 
#include "player.h"
 
#include "airport.h"
 
#include "sprite.h"
 
#include "depot.h"
 
#include "train.h"
 
#include "water_map.h"
 
#include "industry_map.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_station.h"
 
#include "yapf/yapf.h"
 
#include "date.h"
 
#include "helpers.hpp"
 

	
 
Station::Station(TileIndex tile)
 
{
 
	DEBUG(station, cDebugCtorLevel, "I+%3d", index);
 

	
 
	xy = tile;
 
	airport_tile = dock_tile = train_tile = 0;
 
	bus_stops = truck_stops = NULL;
 
	had_vehicle_of_type = 0;
 
	time_since_load = 255;
 
	time_since_unload = 255;
 
	delete_ctr = 0;
 
	facilities = 0;
 

	
 
	last_vehicle_type = VEH_INVALID;
 

	
 
	random_bits = 0; // Random() must be called when station is really built (DC_EXEC)
 
	waiting_triggers = 0;
 
}
 

	
 
/**
 
 * Clean up a station by clearing vehicle orders and invalidating windows.
 
 * Aircraft-Hangar orders need special treatment here, as the hangars are
 
 * actually part of a station (tiletype is STATION), but the order type
 
 * is OT_GOTO_DEPOT.
 
 */
 
Station::~Station()
 
{
 
	DEBUG(station, cDebugCtorLevel, "I-%3d", index);
 

	
 
	DeleteName(this->string_id);
 
	free(this->speclist);
 

	
 
	if (CleaningPool()) return;
 

	
 
	MarkDirty();
 
	RebuildStationLists();
 
	InvalidateWindowClasses(WC_STATION_LIST);
 

	
 
	DeleteWindowById(WC_STATION_VIEW, index);
 

	
 
	/* Now delete all orders that go to the station */
 
	RemoveOrderFromAllVehicles(OT_GOTO_STATION, index);
 

	
 
	/* Subsidies need removal as well */
 
	DeleteSubsidyWithStation(index);
 

	
 
	xy = 0;
 

	
 
	for (CargoID c = 0; c < NUM_CARGO; c++) {
 
		goods[c].cargo.Truncate(0);
 
	}
 

	
 
	this->QuickFree();
 
}
 

	
 
void Station::QuickFree()
 
{
 
	DeleteName(this->string_id);
 
	free(this->speclist);
 
}
 

	
 
/** Called when new facility is built on the station. If it is the first facility
 
 * it initializes also 'xy' and 'random_bits' members */
 
void Station::AddFacility(byte new_facility_bit, TileIndex facil_xy)
 
{
 
	if (facilities == 0) {
 
		xy = facil_xy;
 
		random_bits = Random();
 
	}
 
	facilities |= new_facility_bit;
 
	owner = _current_player;
 
	build_date = _date;
 
}
 

	
 
void Station::MarkDirty() const
 
{
 
	if (sign.width_1 != 0) {
 
		InvalidateWindowWidget(WC_STATION_VIEW, index, 1);
 

	
 
		/* We use ZOOM_LVL_MAX here, as every viewport can have an other zoom,
 
		 *  and there is no way for us to know which is the biggest. So make the
 
		 *  biggest area dirty, and we are safe for sure. */
 
		MarkAllViewportsDirty(
 
			sign.left - 6,
 
			sign.top,
 
			sign.left + ScaleByZoom(sign.width_1 + 12, ZOOM_LVL_MAX),
 
			sign.top + ScaleByZoom(12, ZOOM_LVL_MAX));
 
	}
 
}
 

	
 
void Station::MarkTilesDirty(bool cargo_change) const
 
{
 
	TileIndex tile = train_tile;
 
	int w, h;
 

	
 
	/* XXX No station is recorded as 0, not INVALID_TILE... */
 
	if (tile == 0) return;
 

	
 
	/* cargo_change is set if we're refreshing the tiles due to cargo moving
 
	 * around. */
 
	if (cargo_change) {
 
		/* Don't waste time updating if there are no custom station graphics
 
		 * that might change. Even if there are custom graphics, they might
 
		 * not change. Unfortunately we have no way of telling. */
 
		if (this->num_specs == 0) return;
 
	}
 

	
 
	for (h = 0; h < trainst_h; h++) {
 
		for (w = 0; w < trainst_w; w++) {
 
			if (TileBelongsToRailStation(tile)) {
 
				MarkTileDirtyByTile(tile);
 
			}
 
			tile += TileDiffXY(1, 0);
 
		}
 
		tile += TileDiffXY(-w, 1);
 
	}
 
}
 

	
 
bool Station::TileBelongsToRailStation(TileIndex tile) const
 
{
 
	return IsTileType(tile, MP_STATION) && GetStationIndex(tile) == index && IsRailwayStation(tile);
 
}
 

	
 
/** Obtain the length of a platform
 
 * @pre tile must be a railway station tile
 
 * @param tile A tile that contains the platform in question
 
 * @return The length of the platform
 
 */
 
uint Station::GetPlatformLength(TileIndex tile) const
 
{
 
	TileIndex t;
 
	TileIndexDiff delta;
 
	uint len = 0;
 
	assert(TileBelongsToRailStation(tile));
 

	
 
	delta = (GetRailStationAxis(tile) == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
 

	
 
	t = tile;
 
	do {
 
		t -= delta;
 
		len++;
 
	} while (IsCompatibleTrainStationTile(t, tile));
 

	
 
	t = tile;
 
	do {
 
		t += delta;
 
		len++;
 
	} while (IsCompatibleTrainStationTile(t, tile));
 

	
 
	return len - 1;
 
}
 

	
 
/** Determines the REMAINING length of a platform, starting at (and including)
 
 * the given tile.
 
 * @param tile the tile from which to start searching. Must be a railway station tile
 
 * @param dir The direction in which to search.
 
 * @return The platform length
 
 */
 
uint Station::GetPlatformLength(TileIndex tile, DiagDirection dir) const
 
{
 
	TileIndex start_tile = tile;
 
	uint length = 0;
 
	assert(IsRailwayStationTile(tile));
 
	assert(dir < DIAGDIR_END);
 

	
 
	do {
 
		length ++;
 
		tile += TileOffsByDiagDir(dir);
 
	} while (IsCompatibleTrainStationTile(tile, start_tile));
 

	
 
	return length;
 
}
 

	
 
/** Determines whether a station is a buoy only.
 
 * @todo Ditch this encoding of buoys
 
 */
 
bool Station::IsBuoy() const
 
{
 
	return (had_vehicle_of_type & HVOT_BUOY) != 0;
 
}
 

	
 
/** Determines whether a station exists
 
 * @todo replace 0 by INVALID_TILE
 
 */
 
bool Station::IsValid() const
 
{
 
	return xy != 0;
 
}
 

	
 

	
 
/************************************************************************/
 
/*                     StationRect implementation                       */
 
/************************************************************************/
 

	
 
StationRect::StationRect()
 
{
 
	MakeEmpty();
 
}
 

	
 
void StationRect::MakeEmpty()
 
{
 
	left = top = right = bottom = 0;
 
}
 

	
 
/**
 
 * Determines whether a given point (x, y) is within a certain distance of
 
 * the station rectangle.
 
 * @note x and y are in Tile coordinates
 
 * @param x X coordinate
 
 * @param y Y coordinate
 
 * @param distance The maxmium distance a point may have (L1 norm)
 
 * @return true if the point is within distance tiles of the station rectangle
 
 */
 
bool StationRect::PtInExtendedRect(int x, int y, int distance) const
 
{
 
	return (left - distance <= x && x <= right + distance && top - distance <= y && y <= bottom + distance);
 
}
 

	
 
bool StationRect::IsEmpty() const
 
{
 
	return (left == 0 || left > right || top > bottom);
 
}
 

	
 
bool StationRect::BeforeAddTile(TileIndex tile, StationRectMode mode)
 
{
 
	int x = TileX(tile);
 
	int y = TileY(tile);
 
	if (IsEmpty()) {
 
		/* we are adding the first station tile */
 
		left = right = x;
 
		top = bottom = y;
 

	
 
	} else if (!PtInExtendedRect(x, y)) {
 
		/* current rect is not empty and new point is outside this rect */
 
		/* make new spread-out rectangle */
 
		Rect new_rect = {min(x, left), min(y, top), max(x, right), max(y, bottom)};
 

	
 
		/* check new rect dimensions against preset max */
 
		int w = new_rect.right - new_rect.left + 1;
 
		int h = new_rect.bottom - new_rect.top + 1;
 
		if (mode != ADD_FORCE && (w > _patches.station_spread || h > _patches.station_spread)) {
 
			assert(mode != ADD_TRY);
 
			_error_message = STR_306C_STATION_TOO_SPREAD_OUT;
 
			return false;
 
		}
 

	
 
		/* spread-out ok, return true */
 
		if (mode != ADD_TEST) {
 
			/* we should update the station rect */
 
			*this = new_rect;
 
		}
 
	} else {
 
		; // new point is inside the rect, we don't need to do anything
 
	}
 
	return true;
 
}
 

	
 
bool StationRect::BeforeAddRect(TileIndex tile, int w, int h, StationRectMode mode)
 
{
 
	return BeforeAddTile(tile, mode) && BeforeAddTile(TILE_ADDXY(tile, w - 1, h - 1), mode);
 
}
 

	
 
/*static*/ bool StationRect::ScanForStationTiles(StationID st_id, int left_a, int top_a, int right_a, int bottom_a)
 
{
 
	TileIndex top_left = TileXY(left_a, top_a);
 
	int width = right_a - left_a + 1;
 
	int height = bottom_a - top_a + 1;
 

	
 
	BEGIN_TILE_LOOP(tile, width, height, top_left)
 
		if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st_id) return true;
 
	END_TILE_LOOP(tile, width, height, top_left);
 

	
 
	return false;
 
}
 

	
 
bool StationRect::AfterRemoveTile(Station *st, TileIndex tile)
 
{
 
	int x = TileX(tile);
 
	int y = TileY(tile);
 

	
 
	/* look if removed tile was on the bounding rect edge
 
	 * and try to reduce the rect by this edge
 
	 * do it until we have empty rect or nothing to do */
 
	for (;;) {
 
		/* check if removed tile is on rect edge */
 
		bool left_edge = (x == left);
 
		bool right_edge = (x == right);
 
		bool top_edge = (y == top);
 
		bool bottom_edge = (y == bottom);
 

	
 
		/* can we reduce the rect in either direction? */
 
		bool reduce_x = ((left_edge || right_edge) && !ScanForStationTiles(st->index, x, top, x, bottom));
 
		bool reduce_y = ((top_edge || bottom_edge) && !ScanForStationTiles(st->index, left, y, right, y));
 
		if (!(reduce_x || reduce_y)) break; // nothing to do (can't reduce)
 

	
 
		if (reduce_x) {
 
			/* reduce horizontally */
 
			if (left_edge) {
 
				/* move left edge right */
 
				left = x = x + 1;
 
			} else {
 
				/* move right edge left */
 
				right = x = x - 1;
 
			}
 
		}
 
		if (reduce_y) {
 
			/* reduce vertically */
 
			if (top_edge) {
 
				/* move top edge down */
 
				top = y = y + 1;
 
			} else {
 
				/* move bottom edge up */
 
				bottom = y = y - 1;
 
			}
 
		}
 

	
 
		if (left > right || top > bottom) {
 
			/* can't continue, if the remaining rectangle is empty */
 
			MakeEmpty();
 
			return true; // empty remaining rect
 
		}
 
	}
 
	return false; // non-empty remaining rect
 
}
 

	
 
bool StationRect::AfterRemoveRect(Station *st, TileIndex tile, int w, int h)
 
{
 
	assert(PtInExtendedRect(TileX(tile), TileY(tile)));
 
	assert(PtInExtendedRect(TileX(tile) + w - 1, TileY(tile) + h - 1));
 

	
 
	bool empty = AfterRemoveTile(st, tile);
 
	if (w != 1 || h != 1) empty = empty || AfterRemoveTile(st, TILE_ADDXY(tile, w - 1, h - 1));
 
	return empty;
 
}
 

	
 
StationRect& StationRect::operator = (Rect src)
 
{
 
	left = src.left;
 
	top = src.top;
 
	right = src.right;
 
	bottom = src.bottom;
 
	return *this;
 
}
 

	
 

	
 
/************************************************************************/
 
/*                     RoadStop implementation                          */
 
/************************************************************************/
 

	
 
/** Initializes a RoadStop */
 
RoadStop::RoadStop(TileIndex tile) :
 
	xy(tile),
 
	status(3), // stop is free
 
	num_vehicles(0),
 
	next(NULL)
 
{
 
	DEBUG(ms, cDebugCtorLevel,  "I+ at %d[0x%x]", tile, tile);
 
}
 

	
 
/** De-Initializes a RoadStops. This includes clearing all slots that vehicles might
 
  * have and unlinks it from the linked list of road stops at the given station
 
  */
 
RoadStop::~RoadStop()
 
{
 
	/* Clear the slot assignment of all vehicles heading for this road stop */
 
	if (num_vehicles != 0) {
 
		Vehicle *v;
 

	
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->type == VEH_ROAD && v->u.road.slot == this) ClearSlot(v);
 
		}
 
	}
 
	assert(num_vehicles == 0);
 

	
 
	DEBUG(ms, cDebugCtorLevel , "I- at %d[0x%x]", xy, xy);
 

	
 
	xy = 0;
 
}
 

	
 
/** Determines whether a RoadStop is a valid (i.e. existing) one */
 
bool RoadStop::IsValid() const
 
{
 
	return xy != 0;
 
}
 

	
 
/** Checks whether there is a free bay in this road stop */
 
bool RoadStop::HasFreeBay() const
 
{
 
	return GB(status, 0, MAX_BAY_COUNT) != 0;
 
}
 

	
 
/** Checks whether the given bay is free in this road stop */
 
bool RoadStop::IsFreeBay(uint nr) const
 
{
 
	assert(nr < MAX_BAY_COUNT);
 
	return HASBIT(status, nr);
 
}
 

	
 
/**
 
 * Allocates a bay
 
 * @return the allocated bay number
 
 * @pre this->HasFreeBay()
 
 */
 
uint RoadStop::AllocateBay()
 
{
 
	assert(HasFreeBay());
 

	
 
	/* Find the first free bay. If the bit is set, the bay is free. */
 
	uint bay_nr = 0;
 
	while (!HASBIT(status, bay_nr)) bay_nr++;
 

	
 
	CLRBIT(status, bay_nr);
 
	return bay_nr;
 
}
 

	
 
/**
 
 * Allocates a bay in a drive-through road stop
 
 * @param nr the number of the bay to allocate
 
 */
 
void RoadStop::AllocateDriveThroughBay(uint nr)
 
{
 
	assert(nr < MAX_BAY_COUNT);
 
	CLRBIT(status, nr);
 
}
 

	
 
/**
 
 * Frees the given bay
 
 * @param nr the number of the bay to free
 
 */
 
void RoadStop::FreeBay(uint nr)
 
{
 
	assert(nr < MAX_BAY_COUNT);
 
	SETBIT(status, nr);
 
}
 

	
 

	
 
/** Checks whether the entrance of the road stop is occupied by a vehicle */
 
bool RoadStop::IsEntranceBusy() const
 
{
 
	return HASBIT(status, 7);
 
}
 

	
 
/** Makes an entrance occupied or free */
 
void RoadStop::SetEntranceBusy(bool busy)
 
{
 
	SB(status, 7, 1, busy);
 
}
src/station.h
Show inline comments
 
/* $Id$ */
 

	
 
/** @file station.h */
 

	
 
#ifndef STATION_H
 
#define STATION_H
 

	
 
#include "airport.h"
 
#include "player.h"
 
#include "oldpool.h"
 
#include "sprite.h"
 
#include "tile.h"
 
#include "road.h"
 
#include "newgrf_station.h"
 
#include "cargopacket.h"
 
#include <list>
 

	
 
struct Station;
 
struct RoadStop;
 

	
 
DECLARE_OLD_POOL(Station, Station, 6, 1000)
 
DECLARE_OLD_POOL(RoadStop, RoadStop, 5, 2000)
 

	
 
static const byte INITIAL_STATION_RATING = 175;
 

	
 
struct GoodsEntry {
 
	GoodsEntry() :
 
		acceptance(false),
 
		days_since_pickup(255),
 
		rating(INITIAL_STATION_RATING),
 
		last_speed(0),
 
		last_age(255)
 
	{}
 

	
 
	bool acceptance;
 
	byte days_since_pickup;
 
	byte rating;
 
	byte last_speed;
 
	byte last_age;
 
	CargoList cargo; ///< The cargo packets of cargo waiting in this station
 
};
 

	
 
/** A Stop for a Road Vehicle */
 
struct RoadStop : PoolItem<RoadStop, RoadStopID, &_RoadStop_pool> {
 
	/** Types of RoadStops */
 
	enum Type {
 
		BUS,                                ///< A standard stop for buses
 
		TRUCK                               ///< A standard stop for trucks
 
	};
 

	
 
	static const int  cDebugCtorLevel =  5;  ///< Debug level on which Contructor / Destructor messages are printed
 
	static const uint LIMIT           = 16;  ///< The maximum amount of roadstops that are allowed at a single station
 
	static const uint MAX_BAY_COUNT   =  2;  ///< The maximum number of loading bays
 

	
 
	TileIndex        xy;                    ///< Position on the map
 
	byte             status;                ///< Current status of the Stop. Like which spot is taken. Access using *Bay and *Busy functions.
 
	byte             num_vehicles;          ///< Number of vehicles currently slotted to this stop
 
	struct RoadStop  *next;                 ///< Next stop of the given type at this station
 

	
 
	RoadStop(TileIndex tile = 0);
 
	virtual ~RoadStop();
 

	
 
	bool IsValid() const;
 

	
 
	/* For accessing status */
 
	bool HasFreeBay() const;
 
	bool IsFreeBay(uint nr) const;
 
	uint AllocateBay();
 
	void AllocateDriveThroughBay(uint nr);
 
	void FreeBay(uint nr);
 
	bool IsEntranceBusy() const;
 
	void SetEntranceBusy(bool busy);
 
};
 

	
 
struct StationSpecList {
 
	const StationSpec *spec;
 
	uint32 grfid;      ///< GRF ID of this custom station
 
	uint8  localidx;   ///< Station ID within GRF of station
 
};
 

	
 
/** StationRect - used to track station spread out rectangle - cheaper than scanning whole map */
 
struct StationRect : public Rect {
 
	enum StationRectMode
 
	{
 
		ADD_TEST = 0,
 
		ADD_TRY,
 
		ADD_FORCE
 
	};
 

	
 
	StationRect();
 
	void MakeEmpty();
 
	bool PtInExtendedRect(int x, int y, int distance = 0) const;
 
	bool IsEmpty() const;
 
	bool BeforeAddTile(TileIndex tile, StationRectMode mode);
 
	bool BeforeAddRect(TileIndex tile, int w, int h, StationRectMode mode);
 
	bool AfterRemoveTile(Station *st, TileIndex tile);
 
	bool AfterRemoveRect(Station *st, TileIndex tile, int w, int h);
 

	
 
	static bool ScanForStationTiles(StationID st_id, int left_a, int top_a, int right_a, int bottom_a);
 

	
 
	StationRect& operator = (Rect src);
 
};
 

	
 
struct Station : PoolItem<Station, StationID, &_Station_pool> {
 
	public:
 
		RoadStop *GetPrimaryRoadStop(RoadStop::Type type) const
 
		{
 
			return type == RoadStop::BUS ? bus_stops : truck_stops;
 
		}
 

	
 
		const AirportFTAClass *Airport() const
 
		{
 
			if (airport_tile == 0) return GetAirport(AT_DUMMY);
 
			return GetAirport(airport_type);
 
		}
 

	
 
	TileIndex xy;
 
	RoadStop *bus_stops;
 
	RoadStop *truck_stops;
 
	TileIndex train_tile;
 
	TileIndex airport_tile;
 
	TileIndex dock_tile;
 
	Town *town;
 
	uint16 string_id;
 

	
 
	ViewportSign sign;
 

	
 
	uint16 had_vehicle_of_type;
 

	
 
	byte time_since_load;
 
	byte time_since_unload;
 
	byte delete_ctr;
 
	PlayerByte owner;
 
	byte facilities;
 
	byte airport_type;
 

	
 
	/* trainstation width/height */
 
	byte trainst_w, trainst_h;
 

	
 
	/** List of custom stations (StationSpecs) allocated to the station */
 
	uint8 num_specs;
 
	StationSpecList *speclist;
 

	
 
	Date build_date;
 

	
 
	uint64 airport_flags;   ///< stores which blocks on the airport are taken. was 16 bit earlier on, then 32
 

	
 
	byte last_vehicle_type;
 
	std::list<Vehicle *> loading_vehicles;
 
	GoodsEntry goods[NUM_CARGO];
 

	
 
	uint16 random_bits;
 
	byte waiting_triggers;
 

	
 
	StationRect rect; ///< Station spread out rectangle (not saved) maintained by StationRect_xxx() functions
 

	
 
	static const int cDebugCtorLevel = 5;
 

	
 
	Station(TileIndex tile = 0);
 
	virtual ~Station();
 

	
 
	void QuickFree();
 

	
 
	void AddFacility(byte new_facility_bit, TileIndex facil_xy);
 
	void MarkDirty() const;
 
	void MarkTilesDirty(bool cargo_change) const;
 
	bool TileBelongsToRailStation(TileIndex tile) const;
 
	uint GetPlatformLength(TileIndex tile, DiagDirection dir) const;
 
	uint GetPlatformLength(TileIndex tile) const;
 
	bool IsBuoy() const;
 
	bool IsValid() const;
 
};
 

	
 
enum StationType {
 
	STATION_RAIL,
 
	STATION_AIRPORT,
 
	STATION_TRUCK,
 
	STATION_BUS,
 
	STATION_OILRIG,
 
	STATION_DOCK,
 
	STATION_BUOY
 
};
 

	
 
enum {
 
	FACIL_TRAIN      = 0x01,
 
	FACIL_TRUCK_STOP = 0x02,
 
	FACIL_BUS_STOP   = 0x04,
 
	FACIL_AIRPORT    = 0x08,
 
	FACIL_DOCK       = 0x10,
 
};
 

	
 
enum {
 
//	HVOT_PENDING_DELETE = 1 << 0, // not needed anymore
 
	HVOT_TRAIN    = 1 << 1,
 
	HVOT_BUS      = 1 << 2,
 
	HVOT_TRUCK    = 1 << 3,
 
	HVOT_AIRCRAFT = 1 << 4,
 
	HVOT_SHIP     = 1 << 5,
 
	/* This bit is used to mark stations. No, it does not belong here, but what
 
	 * can we do? ;-) */
 
	HVOT_BUOY     = 1 << 6
 
};
 

	
 
enum CatchmentArea {
 
	CA_NONE            =  0,
 
	CA_BUS             =  3,
 
	CA_TRUCK           =  3,
 
	CA_TRAIN           =  4,
 
	CA_DOCK            =  5
 
};
 

	
 
void ModifyStationRatingAround(TileIndex tile, PlayerID owner, int amount, uint radius);
 

	
 
void ShowStationViewWindow(StationID station);
 
void UpdateAllStationVirtCoord();
 

	
 
/* sorter stuff */
 
void RebuildStationLists();
 
void ResortStationLists();
 

	
 
static inline StationID GetMaxStationIndex()
 
{
 
	/* TODO - This isn't the real content of the function, but
 
	 *  with the new pool-system this will be replaced with one that
 
	 *  _really_ returns the highest index. Now it just returns
 
	 *  the next safe value we are sure about everything is below.
 
	 */
 
	return GetStationPoolSize() - 1;
 
}
 

	
 
static inline uint GetNumStations()
 
{
 
	return GetStationPoolSize();
 
}
 

	
 
static inline bool IsValidStationID(StationID index)
 
{
 
	return index < GetStationPoolSize() && GetStation(index)->IsValid();
 
}
 

	
 
#define FOR_ALL_STATIONS_FROM(st, start) for (st = GetStation(start); st != NULL; st = (st->index + 1U < GetStationPoolSize()) ? GetStation(st->index + 1U) : NULL) if (st->IsValid())
 
#define FOR_ALL_STATIONS(st) FOR_ALL_STATIONS_FROM(st, 0)
 

	
 

	
 
/* Stuff for ROADSTOPS */
 

	
 
#define FOR_ALL_ROADSTOPS_FROM(rs, start) for (rs = GetRoadStop(start); rs != NULL; rs = (rs->index + 1U < GetRoadStopPoolSize()) ? GetRoadStop(rs->index + 1U) : NULL) if (rs->IsValid())
 
#define FOR_ALL_ROADSTOPS(rs) FOR_ALL_ROADSTOPS_FROM(rs, 0)
 

	
 
/* End of stuff for ROADSTOPS */
 

	
 

	
 
void AfterLoadStations();
 
void GetProductionAroundTiles(AcceptedCargo produced, TileIndex tile, int w, int h, int rad);
 
void GetAcceptanceAroundTiles(AcceptedCargo accepts, TileIndex tile, int w, int h, int rad);
 

	
 

	
 
const DrawTileSprites *GetStationTileLayout(StationType st, byte gfx);
 
void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image);
 

	
 
RoadStop * GetRoadStopByTile(TileIndex tile, RoadStop::Type type);
 
uint GetNumRoadStops(const Station* st, RoadStop::Type type);
 
RoadStop * AllocateRoadStop();
 
void ClearSlot(Vehicle *v);
 

	
 
void DeleteOilRig(TileIndex t);
 

	
 
#endif /* STATION_H */
src/town.h
Show inline comments
 
/* $Id$ */
 

	
 
/** @file town.h */
 

	
 
#ifndef TOWN_H
 
#define TOWN_H
 

	
 
#include "oldpool.h"
 
#include "player.h"
 
#include "functions.h"
 
#include "helpers.hpp"
 

	
 
enum {
 
	HOUSE_NO_CLASS   = 0,
 
	NEW_HOUSE_OFFSET = 110,
 
	HOUSE_MAX        = 512,
 
	INVALID_TOWN     = 0xFFFF,
 
	INVALID_HOUSE_ID = 0xFFFF,
 

	
 
	/* There can only be as many classes as there are new houses, plus one for
 
	 * NO_CLASS, as the original houses don't have classes. */
 
	HOUSE_CLASS_MAX  = HOUSE_MAX - NEW_HOUSE_OFFSET + 1,
 
};
 

	
 
enum BuildingFlags {
 
	TILE_NO_FLAG         =       0,
 
	TILE_SIZE_1x1        = 1U << 0,
 
	TILE_NOT_SLOPED      = 1U << 1,
 
	TILE_SIZE_2x1        = 1U << 2,
 
	TILE_SIZE_1x2        = 1U << 3,
 
	TILE_SIZE_2x2        = 1U << 4,
 
	BUILDING_IS_ANIMATED = 1U << 5,
 
	BUILDING_IS_CHURCH   = 1U << 6,
 
	BUILDING_IS_STADIUM  = 1U << 7,
 
	BUILDING_HAS_1_TILE  = TILE_SIZE_1x1 | TILE_SIZE_2x1 | TILE_SIZE_1x2 | TILE_SIZE_2x2,
 
	BUILDING_2_TILES_X   = TILE_SIZE_2x1 | TILE_SIZE_2x2,
 
	BUILDING_2_TILES_Y   = TILE_SIZE_1x2 | TILE_SIZE_2x2,
 
	BUILDING_HAS_4_TILES = TILE_SIZE_2x2,
 
};
 

	
 
DECLARE_ENUM_AS_BIT_SET(BuildingFlags)
 

	
 
enum HouseZones {                  ///< Bit  Value       Meaning
 
	HZ_NOZNS             = 0x0000,  ///<       0          This is just to get rid of zeros, meaning none
 
	HZ_ZON1              = 0x0001,  ///< 0..4 1,2,4,8,10  which town zones the building can be built in, Zone1 been the further suburb
 
	HZ_ZON2              = 0x0002,
 
	HZ_ZON3              = 0x0004,
 
	HZ_ZON4              = 0x0008,
 
	HZ_ZON5              = 0x0010,  ///<                  center of town
 
	HZ_ZONALL            = 0x001F,  ///<       1F         This is just to englobe all above types at once
 
	HZ_SUBARTC_ABOVE     = 0x0800,  ///< 11    800        can appear in sub-arctic climate above the snow line
 
	HZ_TEMP              = 0x1000,  ///< 12   1000        can appear in temperate climate
 
	HZ_SUBARTC_BELOW     = 0x2000,  ///< 13   2000        can appear in sub-arctic climate below the snow line
 
	HZ_SUBTROPIC         = 0x4000,  ///< 14   4000        can appear in subtropical climate
 
	HZ_TOYLND            = 0x8000   ///< 15   8000        can appear in toyland climate
 
};
 

	
 
DECLARE_ENUM_AS_BIT_SET(HouseZones)
 

	
 
enum HouseExtraFlags {
 
	NO_EXTRA_FLAG            =       0,
 
	BUILDING_IS_HISTORICAL   = 1U << 0,  ///< this house will only appear during town generation in random games, thus the historical
 
	BUILDING_IS_PROTECTED    = 1U << 1,  ///< towns and AI will not remove this house, while human players will be able tp
 
	SYNCHRONISED_CALLBACK_1B = 1U << 2,  ///< synchronized callback 1B will be performed, on multi tile houses
 
	CALLBACK_1A_RANDOM_BITS  = 1U << 3,  ///< callback 1A needs random bits
 
};
 

	
 
DECLARE_ENUM_AS_BIT_SET(HouseExtraFlags)
 

	
 
typedef uint16 HouseID;
 
typedef uint16 HouseClassID;
 

	
 
struct BuildingCounts {
 
	uint8 id_count[HOUSE_MAX];
 
	uint8 class_count[HOUSE_CLASS_MAX];
 
};
 

	
 
struct Town;
 
DECLARE_OLD_POOL(Town, Town, 3, 8000)
 

	
 
struct Town : PoolItem<Town, TownID, &_Town_pool> {
 
	TileIndex xy;
 

	
 
	/* Current population of people and amount of houses. */
 
	uint16 num_houses;
 
	uint32 population;
 

	
 
	/* Town name */
 
	uint32 townnamegrfid;
 
	uint16 townnametype;
 
	uint32 townnameparts;
 

	
 
	/* NOSAVE: Location of name sign, UpdateTownVirtCoord updates this. */
 
	ViewportSign sign;
 

	
 
	/* Makes sure we don't build certain house types twice.
 
	 * bit 0 = Building funds received
 
	 * bit 1 = CHURCH
 
	 * bit 2 = STADIUM */
 
	byte flags12;
 

	
 
	/* Which players have a statue? */
 
	byte statues;
 

	
 
	/* Player ratings as well as a mask that determines which players have a rating. */
 
	byte have_ratings;
 
	uint8 unwanted[MAX_PLAYERS]; ///< how many months companies aren't wanted by towns (bribe)
 
	PlayerByte exclusivity;      ///< which player has exslusivity
 
	uint8 exclusive_counter;     ///< months till the exclusivity expires
 
	int16 ratings[MAX_PLAYERS];
 

	
 
	/* Maximum amount of passengers and mail that can be transported. */
 
	uint32 max_pass;
 
	uint32 max_mail;
 
	uint32 new_max_pass;
 
	uint32 new_max_mail;
 
	uint32 act_pass;
 
	uint32 act_mail;
 
	uint32 new_act_pass;
 
	uint32 new_act_mail;
 

	
 
	/* Amount of passengers that were transported. */
 
	byte pct_pass_transported;
 
	byte pct_mail_transported;
 

	
 
	/* Amount of food and paper that was transported. Actually a bit mask would be enough. */
 
	uint16 act_food;
 
	uint16 act_water;
 
	uint16 new_act_food;
 
	uint16 new_act_water;
 

	
 
	/* Time until we rebuild a house. */
 
	uint16 time_until_rebuild;
 

	
 
	/* When to grow town next time. */
 
	uint16 grow_counter;
 
	int16 growth_rate;
 

	
 
	/* Fund buildings program in action? */
 
	byte fund_buildings_months;
 

	
 
	/* Fund road reconstruction in action? */
 
	byte road_build_months;
 

	
 
	/* If this is a larger town, and should grow more quickly. */
 
	bool larger_town;
 

	
 
	/* NOSAVE: UpdateTownRadius updates this given the house count. */
 
	uint16 radius[5];
 

	
 
	/* NOSAVE: The number of each type of building in the town. */
 
	BuildingCounts building_counts;
 

	
 
	/**
 
	 * Creates a new town
 
	 */
 
	Town(TileIndex tile = 0);
 

	
 
	/** Destroy the town */
 
	~Town();
 

	
 
	bool IsValid() const { return this->xy != 0; }
 

	
 
	void QuickFree();
 
};
 

	
 
struct HouseSpec {
 
	/* Standard properties */
 
	Year min_date;                     ///< introduction year of the house
 
	Year max_date;                     ///< last year it can be built
 
	byte population;                   ///< population (Zero on other tiles in multi tile house.)
 
	byte removal_cost;                 ///< cost multiplier for removing it
 
	StringID building_name;            ///< building name
 
	uint16 remove_rating_decrease;     ///< rating decrease if removed
 
	byte mail_generation;              ///< mail generation multiplier (tile based, as the acceptances below)
 
	byte cargo_acceptance[3];          ///< acceptance level for the cargo slots
 
	CargoID accepts_cargo[3];          ///< 3 input cargo slots
 
	BuildingFlags building_flags;      ///< some flags that describe the house (size, stadium etc...)
 
	HouseZones building_availability;  ///< where can it be built (climates, zones)
 
	bool enabled;                      ///< the house is available to build (true by default, but can be disabled by newgrf)
 

	
 
	/* NewHouses properties */
 
	HouseID substitute_id;             ///< which original house this one is based on
 
	struct SpriteGroup *spritegroup;   ///< pointer to the different sprites of the house
 
	HouseID override;                  ///< which house this one replaces
 
	uint16 callback_mask;              ///< House callback flags
 
	byte random_colour[4];             ///< 4 "random" colours
 
	byte probability;                  ///< Relative probability of appearing (16 is the standard value)
 
	HouseExtraFlags extra_flags;       ///< some more flags
 
	HouseClassID class_id;             ///< defines the class this house has (grf file based) @See HouseGetVariable, prop 0x44
 
	byte animation_frames;             ///< number of animation frames
 
	byte animation_speed;              ///< amount of time between each of those frames
 
	byte processing_time;              ///< Periodic refresh multiplier
 
	byte minimum_life;                 ///< The minimum number of years this house will survive before the town rebuilds it
 

	
 
	/* grf file related properties*/
 
	uint8 local_id;                    ///< id defined by the grf file for this house
 
	const struct GRFFile *grffile;     ///< grf file that introduced this house
 
};
 

	
 
enum TownSizeMode {
 
	TSM_RANDOM,
 
	TSM_FIXED,
 
	TSM_CITY
 
};
 

	
 
VARDEF HouseSpec _house_specs[HOUSE_MAX];
 

	
 
uint32 GetWorldPopulation();
 

	
 
void UpdateTownVirtCoord(Town *t);
 
void InitializeTown();
 
void ShowTownViewWindow(TownID town);
 
void ExpandTown(Town *t);
 
Town *CreateRandomTown(uint attempts, TownSizeMode mode, uint size);
 

	
 
enum {
 
	ROAD_REMOVE = 0,
 
	UNMOVEABLE_REMOVE = 1,
 
	TUNNELBRIDGE_REMOVE = 1,
 
	INDUSTRY_REMOVE = 2
 
};
 

	
 
enum {
 
	/* These refer to the maximums, so Appalling is -1000 to -400
 
	 * MAXIMUM RATINGS BOUNDARIES */
 
	RATING_MINIMUM     = -1000,
 
	RATING_APPALLING   =  -400,
 
	RATING_VERYPOOR    =  -200,
 
	RATING_POOR        =     0,
 
	RATING_MEDIOCRE    =   200,
 
	RATING_GOOD        =   400,
 
	RATING_VERYGOOD    =   600,
 
	RATING_EXCELLENT   =   800,
 
	RATING_OUTSTANDING =  1000,         ///< OUTSTANDING
 

	
 
	RATING_MAXIMUM = RATING_OUTSTANDING,
 

	
 
	/* RATINGS AFFECTING NUMBERS */
 
	RATING_TREE_DOWN_STEP = -35,
 
	RATING_TREE_MINIMUM   = RATING_MINIMUM,
 
	RATING_TREE_UP_STEP   = 7,
 
	RATING_TREE_MAXIMUM   = 220,
 

	
 
	RATING_TUNNEL_BRIDGE_DOWN_STEP = -250,
 
	RATING_TUNNEL_BRIDGE_MINIMUM   = 0,
 

	
 
	RATING_INDUSTRY_DOWN_STEP = -1500,
 
	RATING_INDUSTRY_MINIMUM   = RATING_MINIMUM,
 

	
 
	RATING_ROAD_DOWN_STEP = -50,
 
	RATING_ROAD_MINIMUM   = -100,
 
	RATING_HOUSE_MINIMUM  = RATING_MINIMUM,
 

	
 
	RATING_BRIBE_UP_STEP = 200,
 
	RATING_BRIBE_MAXIMUM = 800,
 
	RATING_BRIBE_DOWN_TO = -50        // XXX SHOULD BE SOMETHING LOWER?
 
};
 

	
 
/** This is the number of ticks between towns being processed for building new
 
 * houses or roads. This value originally came from the size of the town array
 
 * in TTD. */
 
static const byte TOWN_GROWTH_FREQUENCY = 70;
 

	
 
/** Simple value that indicates the house has reached the final stage of
 
 * construction. */
 
static const byte TOWN_HOUSE_COMPLETED = 3;
 

	
 
/** This enum is used in conjonction with town->flags12.
 
 * IT simply states what bit is used for.
 
 * It is pretty unrealistic (IMHO) to only have one church/stadium
 
 * per town, NO MATTER the population of it.
 
 * And there are 5 more bits available on flags12...
 
 */
 
enum {
 
	TOWN_IS_FUNDED      = 0,   ///< Town has received some funds for
 
	TOWN_HAS_CHURCH     = 1,   ///< There can be only one church by town.
 
	TOWN_HAS_STADIUM    = 2    ///< There can be only one stadium by town.
 
};
 

	
 
bool CheckforTownRating(uint32 flags, Town *t, byte type);
 

	
 
VARDEF const Town** _town_sort;
 

	
 
static inline HouseSpec *GetHouseSpecs(HouseID house_id)
 
{
 
	assert(house_id < HOUSE_MAX);
 
	return &_house_specs[house_id];
 
}
 

	
 
/**
 
 * Check if a TownID is valid.
 
 * @param index to inquiry in the pool of town
 
 * @return true if it exists
 
 */
 
static inline bool IsValidTownID(TownID index)
 
{
 
	return index < GetTownPoolSize() && GetTown(index)->IsValid();
 
}
 

	
 
VARDEF uint _total_towns;
 

	
 
static inline TownID GetMaxTownIndex()
 
{
 
	/* TODO - This isn't the real content of the function, but
 
	 *  with the new pool-system this will be replaced with one that
 
	 *  _really_ returns the highest index. Now it just returns
 
	 *  the next safe value we are sure about everything is below.
 
	 */
 
	return GetTownPoolSize() - 1;
 
}
 

	
 
static inline uint GetNumTowns()
 
{
 
	return _total_towns;
 
}
 

	
 
/**
 
 * Return a random valid town.
 
 */
 
static inline Town *GetRandomTown()
 
{
 
	int num = RandomRange(GetNumTowns());
 
	TownID index = INVALID_TOWN;
 

	
 
	while (num >= 0) {
 
		num--;
 

	
 
		index++;
 
		/* Make sure we have a valid town */
 
		while (!IsValidTownID(index)) {
 
			index++;
 
			assert(index <= GetMaxTownIndex());
 
		}
 
	}
 

	
 
	return GetTown(index);
 
}
 

	
 
Town* CalcClosestTownFromTile(TileIndex tile, uint threshold);
 

	
 
#define FOR_ALL_TOWNS_FROM(t, start) for (t = GetTown(start); t != NULL; t = (t->index + 1U < GetTownPoolSize()) ? GetTown(t->index + 1U) : NULL) if (t->IsValid())
 
#define FOR_ALL_TOWNS(t) FOR_ALL_TOWNS_FROM(t, 0)
 

	
 
VARDEF bool _town_sort_dirty;
 
VARDEF byte _town_sort_order;
 

	
 
VARDEF Town *_cleared_town;
 
VARDEF int _cleared_town_rating;
 

	
 
uint OriginalTileRandomiser(uint x, uint y);
 
void ResetHouses();
 

	
 
void ClearTownHouse(Town *t, TileIndex tile);
 

	
 
#endif /* TOWN_H */
src/town_cmd.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/** @file town_cmd.cpp */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "debug.h"
 
#include "strings.h"
 
#include "road_map.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "map.h"
 
#include "landscape.h"
 
#include "tile.h"
 
#include "town_map.h"
 
#include "tunnel_map.h"
 
#include "viewport.h"
 
#include "town.h"
 
#include "command.h"
 
#include "gfx.h"
 
#include "industry.h"
 
#include "station.h"
 
#include "vehicle.h"
 
#include "player.h"
 
#include "news.h"
 
#include "saveload.h"
 
#include "economy.h"
 
#include "gui.h"
 
#include "unmovable_map.h"
 
#include "water_map.h"
 
#include "variables.h"
 
#include "bridge.h"
 
#include "bridge_map.h"
 
#include "date.h"
 
#include "table/town_land.h"
 
#include "genworld.h"
 
#include "newgrf.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_house.h"
 
#include "newgrf_commons.h"
 
#include "newgrf_townname.h"
 
#include "misc/autoptr.hpp"
 

	
 
/* Initialize the town-pool */
 
DEFINE_OLD_POOL_GENERIC(Town, Town)
 

	
 
Town::Town(TileIndex tile)
 
{
 
	if (tile != 0) _total_towns++;
 
	this->xy = tile;
 
}
 

	
 
Town::~Town()
 
{
 
	DeleteName(this->townnametype);
 

	
 
	if (CleaningPool()) return;
 

	
 
	Industry *i;
 

	
 
	/* Delete town authority window
 
	 * and remove from list of sorted towns */
 
	DeleteWindowById(WC_TOWN_VIEW, this->index);
 
	_town_sort_dirty = true;
 
	_total_towns--;
 

	
 
	/* Delete all industries belonging to the town */
 
	FOR_ALL_INDUSTRIES(i) if (i->town == this) delete i;
 

	
 
	/* Go through all tiles and delete those belonging to the town */
 
	for (TileIndex tile = 0; tile < MapSize(); ++tile) {
 
		switch (GetTileType(tile)) {
 
			case MP_HOUSE:
 
				if (GetTownByTile(tile) == this) DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				break;
 

	
 
			case MP_ROAD:
 
			case MP_TUNNELBRIDGE:
 
				if (IsTileOwner(tile, OWNER_TOWN) &&
 
						ClosestTownFromTile(tile, (uint)-1) == this)
 
					DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				break;
 

	
 
			default:
 
				break;
 
		}
 
	}
 

	
 
	DeleteSubsidyWithTown(this->index);
 

	
 
	MarkWholeScreenDirty();
 

	
 
	this->QuickFree();
 
	this->xy = 0;
 
}
 

	
 
void Town::QuickFree()
 
{
 
	DeleteName(this->townnametype);
 
}
 

	
 
// Local
 
static int _grow_town_result;
 

	
 
static bool BuildTownHouse(Town *t, TileIndex tile);
 
static void DoBuildTownHouse(Town *t, TileIndex tile);
 

	
 
static void TownDrawHouseLift(const TileInfo *ti)
 
{
 
	AddChildSpriteScreen(SPR_LIFT, PAL_NONE, 14, 60 - GetLiftPosition(ti->tile));
 
}
 

	
 
typedef void TownDrawTileProc(const TileInfo *ti);
 
static TownDrawTileProc * const _town_draw_tile_procs[1] = {
 
	TownDrawHouseLift
 
};
 

	
 
uint OriginalTileRandomiser(uint x, uint y)
 
{
 
	uint variant;
 
	variant  = x >> 4;
 
	variant ^= x >> 6;
 
	variant ^= y >> 4;
 
	variant -= y >> 6;
 
	variant &= 3;
 
	return variant;
 
}
 

	
 
/**
 
 * House Tile drawing handler.
 
 * Part of the tile loop process
 
 * @param ti TileInfo of the tile to draw
 
 */
 
static void DrawTile_Town(TileInfo *ti)
 
{
 
	const DrawBuildingsTileStruct *dcts;
 
	SpriteID image;
 
	SpriteID pal;
 
	HouseID house_id = GetHouseType(ti->tile);
 

	
 
	if (house_id >= NEW_HOUSE_OFFSET) {
 
		/* Houses don't necessarily need new graphics. If they don't have a
 
		 * spritegroup associated with them, then the sprite for the substitute
 
		 * house id is drawn instead. */
 
		if (GetHouseSpecs(house_id)->spritegroup != NULL) {
 
			DrawNewHouseTile(ti, house_id);
 
			return;
 
		} else {
 
			house_id = GetHouseSpecs(house_id)->substitute_id;
 
		}
 
	}
 

	
 
	/* Retrieve pointer to the draw town tile struct */
 
	dcts = &_town_draw_tile_data[house_id << 4 | OriginalTileRandomiser(ti->x, ti->y) << 2 | GetHouseBuildingStage(ti->tile)];
 

	
 
	if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED);
 

	
 
	image = dcts->ground.sprite;
 
	pal   = dcts->ground.pal;
 
	DrawGroundSprite(image, pal);
 

	
 
	/* Add a house on top of the ground? */
 
	image = dcts->building.sprite;
 
	if (image != 0) {
 
		AddSortableSpriteToDraw(image, dcts->building.pal,
 
			ti->x + dcts->subtile_x,
 
			ti->y + dcts->subtile_y,
 
			dcts->width + 1,
 
			dcts->height + 1,
 
			dcts->dz,
 
			ti->z,
 
			HASBIT(_transparent_opt, TO_HOUSES)
 
		);
 

	
 
		if (HASBIT(_transparent_opt, TO_HOUSES)) return;
 
	}
 

	
 
	{
 
		int proc = dcts->draw_proc - 1;
 

	
 
		if (proc >= 0) _town_draw_tile_procs[proc](ti);
 
	}
 
}
 

	
 
static uint GetSlopeZ_Town(TileIndex tile, uint x, uint y)
 
{
 
	return GetTileMaxZ(tile);
 
}
 

	
 
static Foundation GetFoundation_Town(TileIndex tile, Slope tileh)
 
{
 
	return FlatteningFoundation(tileh);
 
}
 

	
 
/**
 
 * Animate a tile for a town
 
 * Only certain houses can be animated
 
 * The newhouses animation superseeds regular ones
 
 * @param tile TileIndex of the house to animate
 
 */
 
static void AnimateTile_Town(TileIndex tile)
 
{
 
	int pos, dest;
 

	
 
	if (GetHouseType(tile) >= NEW_HOUSE_OFFSET) {
 
		AnimateNewHouseTile(tile);
 
		return;
 
	}
 

	
 
	if (_tick_counter & 3) return;
 

	
 
	/* If the house is not one with a lift anymore, then stop this animating.
 
	 * Not exactly sure when this happens, but probably when a house changes.
 
	 * Before this was just a return...so it'd leak animated tiles..
 
	 * That bug seems to have been here since day 1?? */
 
	if (!(GetHouseSpecs(GetHouseType(tile))->building_flags & BUILDING_IS_ANIMATED)) {
 
		DeleteAnimatedTile(tile);
 
		return;
 
	}
 

	
 
	if (!LiftHasDestination(tile)) {
 
		int i;
 

	
 
		/*  Building has 6 floors, number 0 .. 6, where 1 is illegal.
 
		 *  This is due to the fact that the first floor is, in the graphics,
 
		 *  the height of 2 'normal' floors.
 
		 *  Furthermore, there are 6 lift positions from floor N (incl) to floor N + 1 (excl) */
 
		do {
 
			i = (Random() & 7) - 1;
 
		} while (i < 0 || i == 1 || i * 6 == GetLiftPosition(tile));
 

	
 
		SetLiftDestination(tile, i);
 
	}
 

	
 
	pos = GetLiftPosition(tile);
 
	dest = GetLiftDestination(tile) * 6;
 
	pos += (pos < dest) ? 1 : -1;
 
	SetLiftPosition(tile, pos);
 

	
 
	if (pos == dest) HaltLift(tile);
 

	
 
	MarkTileDirtyByTile(tile);
 
}
 

	
 
static void UpdateTownRadius(Town *t);
 

	
 
/**
 
 * Determines if a town is close to a tile
 
 * @param tile TileIndex of the tile to query
 
 * @param dist maximum distance to be accepted
 
 * @returns true if the tile correspond to the distance criteria
 
 */
 
static bool IsCloseToTown(TileIndex tile, uint dist)
 
{
 
	const Town* t;
 

	
 
	FOR_ALL_TOWNS(t) {
 
		if (DistanceManhattan(tile, t->xy) < dist) return true;
 
	}
 
	return false;
 
}
 

	
 
/**
 
 * Marks the town sign as needing a repaint
 
 * @param t Town requesting repaint
 
 */
 
static void MarkTownSignDirty(Town *t)
 
{
 
	MarkAllViewportsDirty(
 
		t->sign.left - 6,
 
		t->sign.top - 3,
 
		t->sign.left + t->sign.width_1 * 4 + 12,
 
		t->sign.top + 45
 
	);
 
}
 

	
 
/**
 
 * Resize the sign(label) of the town after changes in
 
 * population (creation or growth or else)
 
 * @param t Town to update
 
 */
 
void UpdateTownVirtCoord(Town *t)
 
{
 
	Point pt;
 

	
 
	MarkTownSignDirty(t);
 
	pt = RemapCoords2(TileX(t->xy) * TILE_SIZE, TileY(t->xy) * TILE_SIZE);
 
	SetDParam(0, t->index);
 
	SetDParam(1, t->population);
 
	UpdateViewportSignPos(&t->sign, pt.x, pt.y - 24,
 
		_patches.population_in_label ? STR_TOWN_LABEL_POP : STR_TOWN_LABEL);
 
	MarkTownSignDirty(t);
 
}
 

	
 
/**
 
 * Change the towns population
 
 * @param t Town which polulation has changed
 
 * @param mod polulation change (can be positive or negative)
 
 */
 
static void ChangePopulation(Town *t, int mod)
 
{
 
	t->population += mod;
 
	InvalidateWindow(WC_TOWN_VIEW, t->index);
 
	UpdateTownVirtCoord(t);
 

	
 
	if (_town_sort_order & 2) _town_sort_dirty = true;
 
}
 

	
 
/**
 
 * Determines the world population
 
 * Basically, count population of all towns, one by one
 
 * @return uint32 the calculated population of the world
 
 */
 
uint32 GetWorldPopulation()
 
{
 
	uint32 pop;
 
	const Town* t;
 

	
 
	pop = 0;
 
	FOR_ALL_TOWNS(t) pop += t->population;
 
	return pop;
 
}
 

	
 
/**
 
 * Helper function for house completion stages progression
 
 * @param tile TileIndex of the house (or parts of it) to "grow"
 
 */
 
static void MakeSingleHouseBigger(TileIndex tile)
 
{
 
	assert(IsTileType(tile, MP_HOUSE));
 

	
 
	/* means it is completed, get out. */
 
	if (LiftHasDestination(tile)) return;
 

	
 
	/* progress in construction stages */
 
	IncHouseConstructionTick(tile);
 
	if (GetHouseConstructionTick(tile) != 0) return;
 

	
 
	/* Check and/or  */
 
	if (HASBIT(GetHouseSpecs(GetHouseType(tile))->callback_mask, CBM_CONSTRUCTION_STATE_CHANGE)) {
 
		uint16 callback_res = GetHouseCallback(CBID_HOUSE_CONSTRUCTION_STATE_CHANGE, 0, 0, GetHouseType(tile), GetTownByTile(tile), tile);
 
		if (callback_res != CALLBACK_FAILED) ChangeHouseAnimationFrame(tile, callback_res);
 
	}
 

	
 
	if (IsHouseCompleted(tile)) {
 
		/* Now that construction is complete, we can add the population of the
 
		 * building to the town. */
 
		ChangePopulation(GetTownByTile(tile), GetHouseSpecs(GetHouseType(tile))->population);
 
	}
 
	MarkTileDirtyByTile(tile);
 
}
 

	
 
/** Make the house advances in its construction stages until completion
 
 * @param tile TileIndex of house
 
 */
 
static void MakeTownHouseBigger(TileIndex tile)
 
{
 
	uint flags = GetHouseSpecs(GetHouseType(tile))->building_flags;
 
	if (flags & BUILDING_HAS_1_TILE)  MakeSingleHouseBigger(TILE_ADDXY(tile, 0, 0));
 
	if (flags & BUILDING_2_TILES_Y)   MakeSingleHouseBigger(TILE_ADDXY(tile, 0, 1));
 
	if (flags & BUILDING_2_TILES_X)   MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 0));
 
	if (flags & BUILDING_HAS_4_TILES) MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 1));
 
}
 

	
 
/**
 
 * Periodic tic handler for houses and town
 
 * @param tile been asked to do its stuff
 
 */
 
static void TileLoop_Town(TileIndex tile)
 
{
 
	Town *t;
 
	uint32 r;
 
	HouseID house_id = GetHouseType(tile);
 
	HouseSpec *hs = GetHouseSpecs(house_id);
 

	
 
	/* NewHouseTileLoop returns false if Callback 21 succeeded, i.e. the house
 
	 * doesn't exist any more, so don't continue here. */
 
	if (house_id >= NEW_HOUSE_OFFSET && !NewHouseTileLoop(tile)) return;
 

	
 
	if (!IsHouseCompleted(tile)) {
 
		/* Construction is not completed. See if we can go further in construction*/
 
		MakeTownHouseBigger(tile);
 
		return;
 
	}
 

	
 
	/* If the lift has a destination, it is already an animated tile. */
 
	if ((hs->building_flags & BUILDING_IS_ANIMATED) &&
 
			house_id < NEW_HOUSE_OFFSET &&
 
			!LiftHasDestination(tile) &&
 
			CHANCE16(1, 2))
 
		AddAnimatedTile(tile);
 

	
 
	t = GetTownByTile(tile);
 

	
 
	r = Random();
 

	
 
	if (HASBIT(hs->callback_mask, CBM_HOUSE_PRODUCE_CARGO)) {
 
		for (uint i = 0; i < 256; i++) {
 
			uint16 callback = GetHouseCallback(CBID_HOUSE_PRODUCE_CARGO, i, r, house_id, t, tile);
 

	
 
			if (callback == CALLBACK_FAILED) break;
 
			if (callback == 0x20FF) break;
 

	
 
			CargoID cargo = GetCargoTranslation(GB(callback, 8, 7), hs->grffile);
 
			if (cargo == CT_INVALID) continue;
 

	
 
			uint amt = GB(callback, 0, 8);
 
			uint moved = MoveGoodsToStation(tile, 1, 1, cargo, amt);
 

	
 
			const CargoSpec *cs = GetCargo(cargo);
 
			switch (cs->town_effect) {
 
				case TE_PASSENGERS:
 
					t->new_max_pass += amt;
 
					t->new_act_pass += moved;
 
					break;
 

	
 
				case TE_MAIL:
 
					t->new_max_mail += amt;
 
					t->new_act_mail += moved;
 
					break;
 

	
 
				default:
 
					break;
 
			}
 
		}
 
	} else {
 
		if (GB(r, 0, 8) < hs->population) {
 
			uint amt = GB(r, 0, 8) / 8 + 1;
 
			uint moved;
 

	
 
			if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
 
			t->new_max_pass += amt;
 
			moved = MoveGoodsToStation(tile, 1, 1, CT_PASSENGERS, amt);
 
			t->new_act_pass += moved;
 
		}
 

	
 
		if (GB(r, 8, 8) < hs->mail_generation) {
 
			uint amt = GB(r, 8, 8) / 8 + 1;
 
			uint moved;
 

	
 
			if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
 
			t->new_max_mail += amt;
 
			moved = MoveGoodsToStation(tile, 1, 1, CT_MAIL, amt);
 
			t->new_act_mail += moved;
 
		}
 
	}
 

	
 
	_current_player = OWNER_TOWN;
 

	
 
	if (hs->building_flags & BUILDING_HAS_1_TILE &&
 
			HASBIT(t->flags12, TOWN_IS_FUNDED) &&
 
			CanDeleteHouse(tile) &&
 
			max(_cur_year - GetHouseConstructionYear(tile), 0) >= hs->minimum_life &&
 
			--t->time_until_rebuild == 0) {
 
		t->time_until_rebuild = GB(r, 16, 8) + 192;
 

	
 
		ClearTownHouse(t, tile);
 

	
 
		/* Rebuild with another house? */
 
		if (GB(r, 24, 8) >= 12) DoBuildTownHouse(t, tile);
 
	}
 

	
 
	_current_player = OWNER_NONE;
 
}
 

	
 
/**
 
 * Unused handler
 
 * @param tile unused
 
 */
 
static void ClickTile_Town(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 
static CommandCost ClearTile_Town(TileIndex tile, byte flags)
 
{
 
	int rating;
 
	CommandCost cost;
 
	Town *t;
 
	HouseSpec *hs = GetHouseSpecs(GetHouseType(tile));
 

	
 
	if (flags&DC_AUTO && !(flags&DC_AI_BUILDING)) return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED);
 
	if (!CanDeleteHouse(tile)) return CMD_ERROR;
 

	
 
	cost.AddCost(_price.remove_house * hs->removal_cost >> 8);
 

	
 
	rating = hs->remove_rating_decrease;
 
	_cleared_town_rating += rating;
 
	_cleared_town = t = GetTownByTile(tile);
 

	
 
	if (IsValidPlayer(_current_player)) {
 
		if (rating > t->ratings[_current_player] && !(flags & DC_NO_TOWN_RATING) && !_cheats.magic_bulldozer.value) {
 
			SetDParam(0, t->index);
 
			return_cmd_error(STR_2009_LOCAL_AUTHORITY_REFUSES);
 
		}
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		ChangeTownRating(t, -rating, RATING_HOUSE_MINIMUM);
 
		ClearTownHouse(t, tile);
 
	}
 

	
 
	return cost;
 
}
 

	
 
static void GetAcceptedCargo_Town(TileIndex tile, AcceptedCargo ac)
 
{
 
	HouseSpec *hs = GetHouseSpecs(GetHouseType(tile));
 
	CargoID accepts[3];
 

	
 
	/* Set the initial accepted cargo types */
 
	for (uint8 i = 0; i < lengthof(accepts); i++) {
 
		accepts[i] = hs->accepts_cargo[i];
 
	}
 

	
 
	/* Check for custom accepted cargo types */
 
	if (HASBIT(hs->callback_mask, CBM_HOUSE_ACCEPT_CARGO)) {
 
		uint16 callback = GetHouseCallback(CBID_HOUSE_ACCEPT_CARGO, 0, 0, GetHouseType(tile), GetTownByTile(tile), tile);
 
		if (callback != CALLBACK_FAILED) {
 
			/* Replace accepted cargo types with translated values from callback */
 
			accepts[0] = GetCargoTranslation(GB(callback,  0, 5), hs->grffile);
 
			accepts[1] = GetCargoTranslation(GB(callback,  5, 5), hs->grffile);
 
			accepts[2] = GetCargoTranslation(GB(callback, 10, 5), hs->grffile);
 
		}
 
	}
 

	
 
	/* Check for custom cargo acceptance */
 
	if (HASBIT(hs->callback_mask, CBM_CARGO_ACCEPTANCE)) {
 
		uint16 callback = GetHouseCallback(CBID_HOUSE_CARGO_ACCEPTANCE, 0, 0, GetHouseType(tile), GetTownByTile(tile), tile);
 
		if (callback != CALLBACK_FAILED) {
 
			if (accepts[0] != CT_INVALID) ac[accepts[0]] = GB(callback, 0, 4);
 
			if (accepts[1] != CT_INVALID) ac[accepts[1]] = GB(callback, 4, 4);
 
			if (_opt.landscape != LT_TEMPERATE && HASBIT(callback, 12)) {
 
				/* The 'S' bit indicates food instead of goods */
 
				ac[CT_FOOD] = GB(callback, 8, 4);
 
			} else {
 
				if (accepts[2] != CT_INVALID) ac[accepts[2]] = GB(callback, 8, 4);
 
			}
 
			return;
 
		}
 
	}
 

	
 
	/* No custom acceptance, so fill in with the default values */
 
	for (uint8 i = 0; i < lengthof(accepts); i++) {
 
		if (accepts[i] != CT_INVALID) ac[accepts[i]] = hs->cargo_acceptance[i];
 
	}
 
}
 

	
 
static void GetTileDesc_Town(TileIndex tile, TileDesc *td)
 
{
 
	td->str = GetHouseSpecs(GetHouseType(tile))->building_name;
 
	if (!IsHouseCompleted(tile)) {
 
		SetDParamX(td->dparam, 0, td->str);
 
		td->str = STR_2058_UNDER_CONSTRUCTION;
 
	}
 

	
 
	td->owner = OWNER_TOWN;
 
}
 

	
 
static uint32 GetTileTrackStatus_Town(TileIndex tile, TransportType mode, uint sub_mode)
 
{
 
	/* not used */
 
	return 0;
 
}
 

	
 
static void ChangeTileOwner_Town(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	/* not used */
 
}
 

	
 

	
 
static const TileIndexDiffC _roadblock_tileadd[] = {
 
	{ 0, -1},
 
	{ 1,  0},
 
	{ 0,  1},
 
	{-1,  0},
 

	
 
	/* Store the first 3 elements again.
 
	 * Lets us rotate without using &3. */
 
	{ 0, -1},
 
	{ 1,  0},
 
	{ 0,  1}
 
};
 

	
 
/**
 
 * Distance multiplyer
 
 * Defines the possible distances between 2 road tiles
 
 */
 
enum RoadBlockTitleDistance {
 
	RB_TILE_DIST1 = 1, ///< 1 tile between
 
	RB_TILE_DIST2,     ///< 2 tiles between
 
};
 

	
 
static bool GrowTown(Town *t);
 

	
 
static void TownTickHandler(Town *t)
 
{
 
	if (HASBIT(t->flags12, TOWN_IS_FUNDED)) {
 
		int i = t->grow_counter - 1;
 
		if (i < 0) {
 
			if (GrowTown(t)) {
 
				i = t->growth_rate;
 
			} else {
 
				i = 0;
 
			}
 
		}
 
		t->grow_counter = i;
 
	}
 

	
 
	UpdateTownRadius(t);
 
}
 

	
 
void OnTick_Town()
 
{
 
	if (_game_mode == GM_EDITOR) return;
 

	
 
	/* Make sure each town's tickhandler invocation frequency is about the
 
	 * same - TOWN_GROWTH_FREQUENCY - independent on the number of towns. */
 
	for (_cur_town_iter += GetMaxTownIndex() + 1;
 
	     _cur_town_iter >= TOWN_GROWTH_FREQUENCY;
 
	     _cur_town_iter -= TOWN_GROWTH_FREQUENCY) {
 
		uint32 i = _cur_town_ctr;
 

	
 
		if (++_cur_town_ctr > GetMaxTownIndex())
 
			_cur_town_ctr = 0;
 

	
 
		if (IsValidTownID(i)) TownTickHandler(GetTown(i));
 
	}
 
}
 

	
 
static RoadBits GetTownRoadMask(TileIndex tile)
 
{
 
	TrackBits b = GetAnyRoadTrackBits(tile, ROADTYPE_ROAD);
 
	RoadBits r = ROAD_NONE;
 

	
 
	if (b == TRACK_BIT_NONE) return r;
 
	if (b & TRACK_BIT_X)     r |= ROAD_X;
 
	if (b & TRACK_BIT_Y)     r |= ROAD_Y;
 
	if (b & TRACK_BIT_UPPER) r |= ROAD_NE | ROAD_NW;
 
	if (b & TRACK_BIT_LOWER) r |= ROAD_SE | ROAD_SW;
 
	if (b & TRACK_BIT_LEFT)  r |= ROAD_NW | ROAD_SW;
 
	if (b & TRACK_BIT_RIGHT) r |= ROAD_NE | ROAD_SE;
 
	return r;
 
}
 

	
 
/**
 
 * Check if a neighboring tile has a road
 
 *
 
 * @param tile curent tile
 
 * @param dir target direction
 
 * @param dist_multi distance multiplyer
 
 * @return true if one of the neighboring tiles at the
 
 *  given distance is a road tile else
 
 */
 
static bool NeighborIsRoadTile(TileIndex tile, int dir, RoadBlockTitleDistance dist_multi)
 
{
 
	return (HASBIT(GetTownRoadMask(TILE_ADD(tile, dist_multi * ToTileIndexDiff(_roadblock_tileadd[dir + 1]))), dir ^ 2) ||
 
			HASBIT(GetTownRoadMask(TILE_ADD(tile, dist_multi * ToTileIndexDiff(_roadblock_tileadd[dir + 3]))), dir ^ 2) ||
 
			HASBIT(GetTownRoadMask(TILE_ADD(tile, dist_multi * (ToTileIndexDiff(_roadblock_tileadd[dir + 1]) + ToTileIndexDiff(_roadblock_tileadd[dir + 2])))), dir) ||
 
			HASBIT(GetTownRoadMask(TILE_ADD(tile, dist_multi * (ToTileIndexDiff(_roadblock_tileadd[dir + 3]) + ToTileIndexDiff(_roadblock_tileadd[dir + 2])))), dir));
 
}
 

	
 
static bool IsRoadAllowedHere(TileIndex tile, int dir)
 
{
 
	if (TileX(tile) < 2 || TileY(tile) < 2 || MapMaxX() <= TileX(tile) || MapMaxY() <= TileY(tile)) return false;
 

	
 
	Slope k;
 
	Slope slope;
 

	
 
	/* If this assertion fails, it might be because the world contains
 
	 *  land at the edges. This is not ok. */
 
	TILE_ASSERT(tile);
 

	
 
	for (;;) {
 
		/* Check if there already is a road at this point? */
 
		if (GetAnyRoadTrackBits(tile, ROADTYPE_ROAD) == 0) {
 
			/* No, try to build one in the direction.
 
			 * if that fails clear the land, and if that fails exit.
 
			 * This is to make sure that we can build a road here later. */
 
			if (CmdFailed(DoCommand(tile, (dir & 1 ? ROAD_X : ROAD_Y), 0, DC_AUTO, CMD_BUILD_ROAD)) &&
 
					CmdFailed(DoCommand(tile, 0, 0, DC_AUTO, CMD_LANDSCAPE_CLEAR)))
 
				return false;
 
		}
 

	
 
		slope = GetTileSlope(tile, NULL);
 
		if (slope == SLOPE_FLAT) {
 
no_slope:
 
			/* Tile has no slope */
 
			switch (_patches.town_layout) {
 
				default: NOT_REACHED();
 

	
 
				case TL_ORIGINAL: /* Disallow the road if any neighboring tile has a road (distance: 1) */
 
					return !NeighborIsRoadTile(tile, dir, RB_TILE_DIST1);
 

	
 
				case TL_BETTER_ROADS: /* Disallow the road if any neighboring tile has a road (distance: 1 and 2). */
 
					return !(NeighborIsRoadTile(tile, dir, RB_TILE_DIST1) ||
 
							NeighborIsRoadTile(tile, dir, RB_TILE_DIST2));
 
			}
 
		}
 

	
 
		/* If the tile is not a slope in the right direction, then
 
		 * maybe terraform some. */
 
		k = (dir & 1) ? SLOPE_NE : SLOPE_NW;
 
		if (k != slope && ComplementSlope(k) != slope) {
 
			uint32 r = Random();
 

	
 
			if (CHANCE16I(1, 8, r) && !_generating_world) {
 
				CommandCost res;
 

	
 
				if (CHANCE16I(1, 16, r)) {
 
					res = DoCommand(tile, slope, 0, DC_EXEC | DC_AUTO | DC_NO_WATER,
 
					                      CMD_TERRAFORM_LAND);
 
				} else {
 
					res = DoCommand(tile, slope ^ 0xF, 1, DC_EXEC | DC_AUTO | DC_NO_WATER,
 
					                      CMD_TERRAFORM_LAND);
 
				}
 
				if (CmdFailed(res) && CHANCE16I(1, 3, r)) {
 
					/* We can consider building on the slope, though. */
 
					goto no_slope;
 
				}
 
			}
 
			return false;
 
		}
 
		return true;
 
	}
 
}
 

	
 
static bool TerraformTownTile(TileIndex tile, int edges, int dir)
 
{
 
	CommandCost r;
 

	
 
	TILE_ASSERT(tile);
 

	
 
	r = DoCommand(tile, edges, dir, DC_AUTO | DC_NO_WATER, CMD_TERRAFORM_LAND);
 
	if (CmdFailed(r) || r.GetCost() >= 126 * 16) return false;
 
	DoCommand(tile, edges, dir, DC_AUTO | DC_NO_WATER | DC_EXEC, CMD_TERRAFORM_LAND);
 
	return true;
 
}
 

	
 
static void LevelTownLand(TileIndex tile)
 
{
 
	Slope tileh;
 

	
 
	TILE_ASSERT(tile);
 

	
 
	/* Don't terraform if land is plain or if there's a house there. */
 
	if (IsTileType(tile, MP_HOUSE)) return;
 
	tileh = GetTileSlope(tile, NULL);
 
	if (tileh == SLOPE_FLAT) return;
 

	
 
	/* First try up, then down */
 
	if (!TerraformTownTile(tile, ~tileh & 0xF, 1)) {
 
		TerraformTownTile(tile, tileh & 0xF, 0);
 
	}
 
}
 

	
 
/**
 
 * Generate the RoadBits of a grid tile
 
 *
 
 * @param t current town
 
 * @param tile tile in reference to the town
 
 * @return the RoadBit of the current tile regarding
 
 *  the selected town layout
 
 */
 
static RoadBits GetTownRoadGridElement(Town* t, TileIndex tile)
 
{
 
	/* align the grid to the downtown */
 
	TileIndexDiffC grid_pos = TileIndexToTileIndexDiffC(t->xy, tile); ///< Vector from downtown to the tile
 

	
 
	/* lx, ly description:
 
	 * @li lx and ly are true  if the tile is a crossing tile.
 
	 * @li lx xor ly are true  if the tile is a straight road tile.
 
	 * @li lx and ly are false if the tile is a house tile.
 
	 */
 
	bool lx, ly;
 

	
 
	switch (_patches.town_layout) {
 
		default: NOT_REACHED();
 

	
 
		case TL_2X2_GRID:
 
			lx = ((grid_pos.x % 3) == 0);
 
			ly = ((grid_pos.y % 3) == 0);
 
			break;
 

	
 
		case TL_3X3_GRID:
 
			lx = ((grid_pos.x % 4) == 0);
 
			ly = ((grid_pos.y % 4) == 0);
 
			break;
 
	}
 

	
 
	/* generate the basic grid structure */
 
	if (!lx && !ly) {         ///< It is a house tile
 
		return ROAD_NONE;
 
	} else if (lx && !ly) {   ///< It is a Y-dir road tile
 
		return ROAD_Y;
 
	} else if (!lx && ly) {   ///< It is a X-dir road tile
 
		return ROAD_X;
 
	} else {                  ///< It is a crossing tile
 
		/* Presets for junctions on slopes
 
		 * not nice :( */
 
		switch (GetTileSlope(tile, NULL)) {
 
			case SLOPE_W:
 
				return ROAD_NW | ROAD_SW;
 
			case SLOPE_S:
 
				return ROAD_SE | ROAD_SW;
 
			case SLOPE_SW:
 
				return ROAD_Y | ROAD_SW;
 
			case SLOPE_E:
 
				return ROAD_NE | ROAD_SE;
 
			case SLOPE_SE:
 
				return ROAD_X | ROAD_SE;
 
			case SLOPE_N:
 
				return ROAD_NW | ROAD_NE;
 
			case SLOPE_NW:
 
				return ROAD_X | ROAD_NW;
 
			case SLOPE_NE:
 
				return ROAD_Y | ROAD_NE;
 
			case SLOPE_STEEP_W:
 
			case SLOPE_STEEP_N:
 
				return ROAD_X;
 
			case SLOPE_STEEP_S:
 
			case SLOPE_STEEP_E:
 
				return ROAD_Y;
 
			default:
 
				return ROAD_ALL;
 
		}
 
	}
 
}
 

	
 
/**
 
 * Check there are enougth neighbor house tiles next to the current tile
 
 *
 
 * @param tile current tile
 
 * @return true if there are more than 2 house tiles next
 
 *  to the current one
 
 */
 
static bool NeighborsAreHouseTiles(TileIndex tile)
 
{
 
	uint counter = 0; ///< counts the house neighbor tiles
 

	
 
	/* We can't look further than that. */
 
	if (TileX(tile) < 1 || TileY(tile) < 1) {
 
		return false;
 
	}
 

	
 
	/* Check the tiles E,N,W and S of the current tile. */
 
	for (uint i = 0; i < 4; i++) {
 
		if (IsTileType(TILE_ADD(tile, ToTileIndexDiff(_roadblock_tileadd[i])), MP_HOUSE)) {
 
			counter++;
 
		}
 

	
 
		/* If there are enougth neighbor's stop it here */
 
		if (counter >= 3) {
 
			return true;
 
		}
 
	}
 
	return false;
 
}
 

	
 
/**
 
 * Grows the given town.
 
 * There are at the moment 3 possible way's for
 
 * the town expansion:
 
 * @li Generate a random tile and check if there is a road allowed
 
 * 	@li TL_ORIGINAL
 
 * 	@li TL_BETTER_ROADS
 
 * @li Check if the town geometry allows a road and which one
 
 * 	@li TL_2X2_GRID
 
 * 	@li TL_3X3_GRID
src/vehicle.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/** @file vehicle.cpp */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "road_map.h"
 
#include "roadveh.h"
 
#include "ship.h"
 
#include "spritecache.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "landscape.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "vehicle.h"
 
#include "timetable.h"
 
#include "gfx.h"
 
#include "viewport.h"
 
#include "news.h"
 
#include "command.h"
 
#include "saveload.h"
 
#include "player.h"
 
#include "engine.h"
 
#include "sound.h"
 
#include "debug.h"
 
#include "vehicle_gui.h"
 
#include "depot.h"
 
#include "station.h"
 
#include "rail.h"
 
#include "train.h"
 
#include "aircraft.h"
 
#include "industry_map.h"
 
#include "station_map.h"
 
#include "water_map.h"
 
#include "network/network.h"
 
#include "yapf/yapf.h"
 
#include "date.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_engine.h"
 
#include "newgrf_sound.h"
 
#include "helpers.hpp"
 
#include "group.h"
 
#include "economy.h"
 
#include "strings.h"
 

	
 
#define INVALID_COORD (0x7fffffff)
 
#define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
 

	
 

	
 
/* Tables used in vehicle.h to find the right command for a certain vehicle type */
 
const uint32 _veh_build_proc_table[] = {
 
	CMD_BUILD_RAIL_VEHICLE,
 
	CMD_BUILD_ROAD_VEH,
 
	CMD_BUILD_SHIP,
 
	CMD_BUILD_AIRCRAFT,
 
};
 
const uint32 _veh_sell_proc_table[] = {
 
	CMD_SELL_RAIL_WAGON,
 
	CMD_SELL_ROAD_VEH,
 
	CMD_SELL_SHIP,
 
	CMD_SELL_AIRCRAFT,
 
};
 

	
 
const uint32 _veh_refit_proc_table[] = {
 
	CMD_REFIT_RAIL_VEHICLE,
 
	CMD_REFIT_ROAD_VEH,
 
	CMD_REFIT_SHIP,
 
	CMD_REFIT_AIRCRAFT,
 
};
 

	
 
const uint32 _send_to_depot_proc_table[] = {
 
	CMD_SEND_TRAIN_TO_DEPOT,
 
	CMD_SEND_ROADVEH_TO_DEPOT,
 
	CMD_SEND_SHIP_TO_DEPOT,
 
	CMD_SEND_AIRCRAFT_TO_HANGAR,
 
};
 

	
 

	
 
/* Initialize the vehicle-pool */
 
DEFINE_OLD_POOL_GENERIC(Vehicle, Vehicle)
 

	
 
void VehicleServiceInDepot(Vehicle *v)
 
{
 
	v->date_of_last_service = _date;
 
	v->breakdowns_since_last_service = 0;
 
	v->reliability = GetEngine(v->engine_type)->reliability;
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
 
}
 

	
 
bool VehicleNeedsService(const Vehicle *v)
 
{
 
	if (v->vehstatus & VS_CRASHED)
 
		return false; // Crashed vehicles don't need service anymore
 

	
 
	if (_patches.no_servicing_if_no_breakdowns && _opt.diff.vehicle_breakdowns == 0) {
 
		return EngineHasReplacementForPlayer(GetPlayer(v->owner), v->engine_type, v->group_id);  /* Vehicles set for autoreplacing needs to go to a depot even if breakdowns are turned off */
 
	}
 

	
 
	return _patches.servint_ispercent ?
 
		(v->reliability < GetEngine(v->engine_type)->reliability * (100 - v->service_interval) / 100) :
 
		(v->date_of_last_service + v->service_interval < _date);
 
}
 

	
 
StringID VehicleInTheWayErrMsg(const Vehicle* v)
 
{
 
	switch (v->type) {
 
		case VEH_TRAIN:    return STR_8803_TRAIN_IN_THE_WAY;
 
		case VEH_ROAD:     return STR_9000_ROAD_VEHICLE_IN_THE_WAY;
 
		case VEH_AIRCRAFT: return STR_A015_AIRCRAFT_IN_THE_WAY;
 
		default:           return STR_980E_SHIP_IN_THE_WAY;
 
	}
 
}
 

	
 
static void *EnsureNoVehicleProc(Vehicle *v, void *data)
 
{
 
	if (v->tile != *(const TileIndex*)data || v->type == VEH_DISASTER)
 
		return NULL;
 

	
 
	_error_message = VehicleInTheWayErrMsg(v);
 
	return v;
 
}
 

	
 
bool EnsureNoVehicle(TileIndex tile)
 
{
 
	return VehicleFromPos(tile, &tile, EnsureNoVehicleProc) == NULL;
 
}
 

	
 
static void *EnsureNoVehicleProcZ(Vehicle *v, void *data)
 
{
 
	const TileInfo *ti = (const TileInfo*)data;
 

	
 
	if (v->tile != ti->tile || v->type == VEH_DISASTER) return NULL;
 
	if (v->z_pos > ti->z) return NULL;
 

	
 
	_error_message = VehicleInTheWayErrMsg(v);
 
	return v;
 
}
 

	
 

	
 
bool EnsureNoVehicleOnGround(TileIndex tile)
 
{
 
	TileInfo ti;
 

	
 
	ti.tile = tile;
 
	ti.z = GetTileMaxZ(tile);
 
	return VehicleFromPos(tile, &ti, EnsureNoVehicleProcZ) == NULL;
 
}
 

	
 
Vehicle *FindVehicleOnTileZ(TileIndex tile, byte z)
 
{
 
	TileInfo ti;
 

	
 
	ti.tile = tile;
 
	ti.z = z;
 

	
 
	return (Vehicle*)VehicleFromPos(tile, &ti, EnsureNoVehicleProcZ);
 
}
 

	
 
Vehicle *FindVehicleBetween(TileIndex from, TileIndex to, byte z, bool without_crashed)
 
{
 
	int x1 = TileX(from);
 
	int y1 = TileY(from);
 
	int x2 = TileX(to);
 
	int y2 = TileY(to);
 
	Vehicle *veh;
 

	
 
	/* Make sure x1 < x2 or y1 < y2 */
 
	if (x1 > x2 || y1 > y2) {
 
		Swap(x1, x2);
 
		Swap(y1, y2);
 
	}
 
	FOR_ALL_VEHICLES(veh) {
 
		if (without_crashed && (veh->vehstatus & VS_CRASHED) != 0) continue;
 
		if ((veh->type == VEH_TRAIN || veh->type == VEH_ROAD) && (z == 0xFF || veh->z_pos == z)) {
 
			if ((veh->x_pos >> 4) >= x1 && (veh->x_pos >> 4) <= x2 &&
 
					(veh->y_pos >> 4) >= y1 && (veh->y_pos >> 4) <= y2) {
 
				return veh;
 
			}
 
		}
 
	}
 
	return NULL;
 
}
 

	
 

	
 
static void UpdateVehiclePosHash(Vehicle* v, int x, int y);
 

	
 
void VehiclePositionChanged(Vehicle *v)
 
{
 
	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);
 

	
 
	pt.x += spr->x_offs;
 
	pt.y += spr->y_offs;
 

	
 
	UpdateVehiclePosHash(v, pt.x, pt.y);
 

	
 
	v->left_coord = pt.x;
 
	v->top_coord = pt.y;
 
	v->right_coord = pt.x + spr->width + 2;
 
	v->bottom_coord = pt.y + spr->height + 2;
 
}
 

	
 
/** Called after load to update coordinates */
 
void AfterLoadVehicles()
 
{
 
	Vehicle *v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		v->UpdateDeltaXY(v->direction);
 

	
 
		v->fill_percent_te_id = INVALID_TE_ID;
 
		v->first = NULL;
 
		if (v->type == VEH_TRAIN) v->u.rail.first_engine = INVALID_ENGINE;
 
		if (v->type == VEH_ROAD)  v->u.road.first_engine = INVALID_ENGINE;
 

	
 
		v->cargo.InvalidateCache();
 
	}
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) {
 
			TrainConsistChanged(v);
 
		} else if (v->type == VEH_ROAD && IsRoadVehFront(v)) {
 
			RoadVehUpdateCache(v);
 
		}
 
	}
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		switch (v->type) {
 
			case VEH_ROAD:
 
				v->u.road.roadtype = HASBIT(EngInfo(v->engine_type)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
 
				v->u.road.compatible_roadtypes = RoadTypeToRoadTypes(v->u.road.roadtype);
 
				/* FALL THROUGH */
 
			case VEH_TRAIN:
 
			case VEH_SHIP:
 
				v->cur_image = v->GetImage(v->direction);
 
				break;
 

	
 
			case VEH_AIRCRAFT:
 
				if (IsNormalAircraft(v)) {
 
					v->cur_image = v->GetImage(v->direction);
 

	
 
					/* The plane's shadow will have the same image as the plane */
 
					Vehicle *shadow = v->next;
 
					shadow->cur_image = v->cur_image;
 

	
 
					/* In the case of a helicopter we will update the rotor sprites */
 
					if (v->subtype == AIR_HELICOPTER) {
 
						Vehicle *rotor = shadow->next;
 
						rotor->cur_image = GetRotorImage(v);
 
					}
 

	
 
					UpdateAircraftCache(v);
 
				}
 
				break;
 
			default: break;
 
		}
 

	
 
		v->left_coord = INVALID_COORD;
 
		VehiclePositionChanged(v);
 
	}
 
}
 

	
 
Vehicle::Vehicle()
 
{
 
	this->type               = VEH_INVALID;
 
	this->left_coord         = INVALID_COORD;
 
	this->group_id           = DEFAULT_GROUP;
 
	this->fill_percent_te_id = INVALID_TE_ID;
 
}
 

	
 
/**
 
 * Get a value for a vehicle's random_bits.
 
 * @return A random value from 0 to 255.
 
 */
 
byte VehicleRandomBits()
 
{
 
	return GB(Random(), 0, 8);
 
}
 

	
 

	
 
/* static */ bool Vehicle::AllocateList(Vehicle **vl, int num)
 
{
 
	uint counter = _Vehicle_pool.first_free_index;
 

	
 
	for (int i = 0; i != num; i++) {
 
		Vehicle *v = AllocateRaw(counter);
 

	
 
		if (v == NULL) return false;
 
		v = new (v) InvalidVehicle();
 

	
 
		if (vl != NULL) {
 
			vl[i] = v;
 
		}
 
		counter++;
 
	}
 

	
 
	return true;
 
}
 

	
 
/* 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 void *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc)
 
{
 
	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) {
 
				void *a = proc(v, data);
 
				if (a != NULL) return a;
 
			}
 
			if (x == xu) break;
 
		}
 
		if (y == yu) break;
 
	}
 

	
 
	return NULL;
 
}
 

	
 

	
 
void *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
 
{
 
	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);
 
}
 

	
 

	
 
void *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
 
{
 
	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) {
 
		if (v->tile != tile) continue;
 

	
 
		void *a = proc(v, data);
 
		if (a != NULL) return a;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
 
{
 
	Vehicle **old_hash = v->old_new_hash;
 
	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];
 
	}
 

	
 
	if (old_hash == new_hash) return;
 

	
 
	/* Remove from the old position in the hash table */
 
	if (old_hash != NULL) {
 
		Vehicle *last = NULL;
 
		Vehicle *u = *old_hash;
 
		while (u != v) {
 
			last = u;
 
			u = u->next_new_hash;
 
			assert(u != NULL);
 
		}
 

	
 
		if (last == NULL) {
 
			*old_hash = v->next_new_hash;
 
		} else {
 
			last->next_new_hash = v->next_new_hash;
 
		}
 
	}
 

	
 
	/* Insert vehicle at beginning of the new position in the hash table */
 
	if (new_hash != NULL) {
 
		v->next_new_hash = *new_hash;
 
		*new_hash = v;
 
		assert(v != v->next_new_hash);
 
	}
 

	
 
	/* Remember current hash position */
 
	v->old_new_hash = new_hash;
 
}
 

	
 
static Vehicle *_vehicle_position_hash[0x1000];
 

	
 
static void UpdateVehiclePosHash(Vehicle* v, int x, int y)
 
{
 
	UpdateNewVehiclePosHash(v, x == INVALID_COORD);
 

	
 
	Vehicle **old_hash, **new_hash;
 
	int old_x = v->left_coord;
 
	int old_y = v->top_coord;
 

	
 
	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)];
 

	
 
	if (old_hash == new_hash) return;
 

	
 
	/* remove from hash table? */
 
	if (old_hash != NULL) {
 
		Vehicle *last = NULL;
 
		Vehicle *u = *old_hash;
 
		while (u != v) {
 
			last = u;
 
			u = u->next_hash;
 
			assert(u != NULL);
 
		}
 

	
 
		if (last == NULL) {
 
			*old_hash = v->next_hash;
 
		} else {
 
			last->next_hash = v->next_hash;
 
		}
 
	}
 

	
 
	/* insert into hash table? */
 
	if (new_hash != NULL) {
 
		v->next_hash = *new_hash;
 
		*new_hash = v;
 
	}
 
}
 

	
 
void ResetVehiclePosHash()
 
{
 
	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));
 
}
 

	
 
void InitializeVehicles()
 
{
 
	_Vehicle_pool.CleanPool();
 
	_Vehicle_pool.AddBlockToPool();
 

	
 
	ResetVehiclePosHash();
 
}
 

	
 
Vehicle *GetLastVehicleInChain(Vehicle *v)
 
{
 
	while (v->next != NULL) v = v->next;
 
	return v;
 
}
 

	
 
/** Finds the previous vehicle in a chain, by a brute force search.
 
 * This old function is REALLY slow because it searches through all vehicles to
 
 * find the previous vehicle, but if v->first has not been set, then this function
 
 * will need to be used to find the previous one. This function should never be
 
 * called by anything but GetFirstVehicleInChain
 
 */
 
static Vehicle *GetPrevVehicleInChain_bruteforce(const Vehicle *v)
 
{
 
	Vehicle *u;
 

	
 
	FOR_ALL_VEHICLES(u) if (u->type == v->type && u->next == v) return u;
 

	
 
	return NULL;
 
}
 

	
 
/** Find the previous vehicle in a chain, by using the v->first cache.
 
 * While this function is fast, it cannot be used in the GetFirstVehicleInChain
 
 * function, otherwise you'll end up in an infinite loop call
 
 */
 
Vehicle *GetPrevVehicleInChain(const Vehicle *v)
 
{
 
	Vehicle *u;
 
	assert(v != NULL);
 

	
 
	u = GetFirstVehicleInChain(v);
 

	
 
	/* Check to see if this is the first */
 
	if (v == u) return NULL;
 

	
 
	for (; u->next != v; u = u->next) assert(u->next != NULL);
 

	
 
	return u;
 
}
 

	
 
/** Finds the first vehicle in a chain.
 
 * This function reads out the v->first cache. Should the cache be dirty,
 
 * it determines the first vehicle in a chain, and updates the cache.
 
 */
 
Vehicle *GetFirstVehicleInChain(const Vehicle *v)
 
{
 
	Vehicle* u;
 

	
 
	assert(v != NULL);
 
	assert(v->type == VEH_TRAIN || v->type == VEH_ROAD);
 

	
 
	if (v->first != NULL) {
 
		if (v->type == VEH_TRAIN) {
 
			if (IsFrontEngine(v->first) || IsFreeWagon(v->first)) return v->first;
 
		} else {
 
			if (IsRoadVehFront(v->first)) return v->first;
 
		}
 

	
 
		DEBUG(misc, 0, "v->first cache faulty. We shouldn't be here, rebuilding cache!");
 
	}
 

	
 
	/* It is the fact (currently) that newly built vehicles do not have
 
	 * their ->first pointer set. When this is the case, go up to the
 
	 * first engine and set the pointers correctly. Also the first pointer
 
	 * is not saved in a savegame, so this has to be fixed up after loading */
 

	
 
	/* Find the 'locomotive' or the first wagon in a chain */
 
	while ((u = GetPrevVehicleInChain_bruteforce(v)) != NULL) v = u;
 

	
 
	/* Set the first pointer of all vehicles in that chain to the first wagon */
 
	if ((v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) ||
 
			(v->type == VEH_ROAD && IsRoadVehFront(v))) {
 
		for (u = (Vehicle *)v; u != NULL; u = u->next) u->first = (Vehicle *)v;
 
	}
 

	
 
	return (Vehicle*)v;
 
}
 

	
 
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 player struct
 
 * @param *v Vehicle to test
 
 * @return true if the vehicle is counted in num_engines
 
 */
 
bool IsEngineCountable(const Vehicle *v)
 
{
 
	switch (v->type) {
 
		case VEH_AIRCRAFT: return IsNormalAircraft(v); // don't count plane shadows and helicopter rotors
 
		case VEH_TRAIN:
 
			return !IsArticulatedPart(v) && // tenders and other articulated parts
 
			(!IsMultiheaded(v) || IsTrainEngine(v)); // rear parts of multiheaded engines
 
		case VEH_ROAD: return IsRoadVehFront(v);
 
		case VEH_SHIP: return true;
 
		default: return false; // Only count player buildable vehicles
 
	}
 
}
 

	
 
void Vehicle::PreDestructor()
 
{
 
	if (CleaningPool()) return;
 

	
 
	if (IsValidStationID(this->last_station_visited)) {
 
		GetStation(this->last_station_visited)->loading_vehicles.remove(this);
 

	
 
		HideFillingPercent(this->fill_percent_te_id);
 
		this->fill_percent_te_id = INVALID_TE_ID;
 
	}
 

	
 
	if (IsEngineCountable(this)) {
 
		GetPlayer(this->owner)->num_engines[this->engine_type]--;
 
		if (this->owner == _local_player) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
 

	
 
		if (IsValidGroupID(this->group_id)) GetGroup(this->group_id)->num_engines[this->engine_type]--;
 
		if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
 
	}
 

	
 
	this->QuickFree();
 
	if (this->type == VEH_ROAD) ClearSlot(this);
 

	
 
	if (this->type != VEH_TRAIN || (this->type == VEH_TRAIN && (IsFrontEngine(this) || IsFreeWagon(this)))) {
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
 
	}
 

	
 
	this->cargo.Truncate(0);
 
	DeleteVehicleOrders(this);
 

	
 
	/* Now remove any artic part. This will trigger an other
 
	 *  destroy vehicle, which on his turn can remove any
 
	 *  other artic parts. */
 
	if ((this->type == VEH_TRAIN && EngineHasArticPart(this)) || (this->type == VEH_ROAD && RoadVehHasArticPart(this))) {
 
		delete this->next;
 
	}
 
}
 

	
 
Vehicle::~Vehicle()
 
{
 
	DeleteName(this->string_id);
 

	
 
	if (CleaningPool()) return;
 

	
 
	UpdateVehiclePosHash(this, INVALID_COORD, 0);
 
	this->next_hash = NULL;
 
	this->next_new_hash = NULL;
 

	
 
	DeleteVehicleNews(this->index, INVALID_STRING_ID);
 

	
 
	new (this) InvalidVehicle();
 
}
 

	
 
void Vehicle::QuickFree()
 
{
 
	DeleteName(this->string_id);
 
}
 

	
 
/**
 
 * Deletes all vehicles in a chain.
 
 * @param v The first vehicle in the chain.
 
 *
 
 * @warning This function is not valid for any vehicle containing articulated
 
 * parts.
 
 */
 
void DeleteVehicleChain(Vehicle *v)
 
{
 
	assert(v->type != VEH_TRAIN && v->type != VEH_ROAD);
 

	
 
	do {
 
		Vehicle *u = v;
 
		v = v->next;
 
		delete u;
 
	} while (v != NULL);
 
}
 

	
 
/** head of the linked list to tell what vehicles that visited a depot in a tick */
 
static Vehicle* _first_veh_in_depot_list;
 

	
 
/** Adds a vehicle to the list of vehicles, that visited a depot this tick
 
 * @param *v vehicle to add
 
 */
 
void VehicleEnteredDepotThisTick(Vehicle *v)
 
{
 
	/* we need to set v->leave_depot_instantly as we have no control of it's contents at this time */
 
	if (HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT) && !HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS) && v->current_order.type == OT_GOTO_DEPOT) {
 
		/* we keep the vehicle in the depot since the user ordered it to stay */
 
		v->leave_depot_instantly = false;
 
	} else {
 
		/* the vehicle do not plan on stopping in the depot, so we stop it to ensure that it will not reserve the path
 
		 * out of the depot before we might autoreplace it to a different engine. The new engine would not own the reserved path
 
		 * we store that we stopped the vehicle, so autoreplace can start it again */
 
		v->vehstatus |= VS_STOPPED;
 
		v->leave_depot_instantly = true;
 
	}
 

	
 
	if (_first_veh_in_depot_list == NULL) {
 
		_first_veh_in_depot_list = v;
 
	} else {
 
		Vehicle *w = _first_veh_in_depot_list;
 
		while (w->depot_list != NULL) w = w->depot_list;
 
		w->depot_list = v;
 
	}
 
}
 

	
 
void CallVehicleTicks()
 
{
 
	_first_veh_in_depot_list = NULL; // now we are sure it's initialized at the start of each tick
 

	
 
	Station *st;
 
	FOR_ALL_STATIONS(st) LoadUnloadStation(st);
 

	
 
	Vehicle *v;
 
	FOR_ALL_VEHICLES(v) {
 
		v->Tick();
 

	
 
		switch (v->type) {
 
			default: break;
 

	
 
			case VEH_TRAIN:
 
			case VEH_ROAD:
 
			case VEH_AIRCRAFT:
 
			case VEH_SHIP:
 
				if (v->type == VEH_TRAIN && IsTrainWagon(v)) continue;
 
				if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
 
				if (v->type == VEH_ROAD && !IsRoadVehFront(v)) continue;
 

	
 
				v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
 
				/* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
 
				if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
 

	
 
				/* Play an alterate running sound every 16 ticks */
 
				if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
 
		}
 
	}
 

	
 
	/* now we handle all the vehicles that entered a depot this tick */
 
	v = _first_veh_in_depot_list;
 
	while (v != NULL) {
 
		Vehicle *w = v->depot_list;
 
		v->depot_list = NULL; // it should always be NULL at the end of each tick
 
		MaybeReplaceVehicle(v, false, true);
 
		v = w;
 
	}
 
}
 

	
 
/** Check if a given engine type can be refitted to a given cargo
 
 * @param engine_type Engine type to check
 
 * @param cid_to check refit to this cargo-type
 
 * @return true if it is possible, false otherwise
 
 */
 
bool CanRefitTo(EngineID engine_type, CargoID cid_to)
 
{
 
	return HASBIT(EngInfo(engine_type)->refit_mask, cid_to);
 
}
 

	
 
/** Find the first cargo type that an engine can be refitted to.
 
 * @param engine_type Which engine to find cargo for.
 
 * @return A climate dependent cargo type. CT_INVALID is returned if not refittable.
 
 */
 
CargoID FindFirstRefittableCargo(EngineID engine_type)
 
{
 
	uint32 refit_mask = EngInfo(engine_type)->refit_mask;
 

	
 
	if (refit_mask != 0) {
 
		for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
 
			if (HASBIT(refit_mask, cid)) return cid;
 
		}
 
	}
 

	
 
	return CT_INVALID;
 
}
 

	
 
/** Learn the price of refitting a certain engine
 
* @param engine_type Which engine to refit
 
* @return Price for refitting
 
*/
 
CommandCost GetRefitCost(EngineID engine_type)
 
{
 
	CommandCost base_cost;
 

	
 
	switch (GetEngine(engine_type)->type) {
 
		case VEH_SHIP: base_cost.AddCost(_price.ship_base); break;
 
		case VEH_ROAD: base_cost.AddCost(_price.roadveh_base); break;
 
		case VEH_AIRCRAFT: base_cost.AddCost(_price.aircraft_base); break;
 
		case VEH_TRAIN:
 
			base_cost.AddCost(2 * ((RailVehInfo(engine_type)->railveh_type == RAILVEH_WAGON) ?
 
							 _price.build_railwagon : _price.build_railvehicle));
 
			break;
 
		default: NOT_REACHED(); break;
 
	}
 
	return CommandCost((EngInfo(engine_type)->refit_cost * base_cost.GetCost()) >> 10);
 
}
 

	
 
static void DoDrawVehicle(const Vehicle *v)
 
{
 
	SpriteID image = v->cur_image;
 
	SpriteID pal;
 

	
 
	if (v->vehstatus & VS_DEFPAL) {
 
		pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
 
	} else {
 
		pal = PAL_NONE;
 
	}
 

	
 
	AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
 
		v->sprite_width, v->sprite_height, v->z_height, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
 
}
 

	
 
void ViewportAddVehicles(DrawPixelInfo *dpi)
 
{
 
	/* The bounding rectangle */
 
	const int l = dpi->left;
 
	const int r = dpi->left + dpi->width;
 
	const int t = dpi->top;
 
	const int b = dpi->top + dpi->height;
 

	
 
	/* The hash area to scan */
 
	int xl, xu, yl, yu;
 

	
 
	if (dpi->width + 70 < (1 << (7 + 6))) {
 
		xl = GB(l - 70, 7, 6);
 
		xu = GB(r,      7, 6);
 
	} else {
 
		/* scan whole hash row */
 
		xl = 0;
 
		xu = 0x3F;
 
	}
 

	
 
	if (dpi->height + 70 < (1 << (6 + 6))) {
 
		yl = GB(t - 70, 6, 6) << 6;
 
		yu = GB(b,      6, 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
 

	
 
			while (v != NULL) {
 
				if (!(v->vehstatus & VS_HIDDEN) &&
 
						l <= v->right_coord &&
 
						t <= v->bottom_coord &&
 
						r >= v->left_coord &&
 
						b >= v->top_coord) {
 
					DoDrawVehicle(v);
 
				}
 
				v = v->next_hash;
 
			}
 

	
 
			if (x == xu) break;
 
		}
 

	
 
		if (y == yu) break;
 
	}
 
}
 

	
 
static void ChimneySmokeInit(Vehicle *v)
 
{
 
	uint32 r = Random();
 
	v->cur_image = SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3);
 
	v->progress = GB(r, 16, 3);
 
}
 

	
 
static void ChimneySmokeTick(Vehicle *v)
 
{
 
	if (v->progress > 0) {
 
		v->progress--;
 
	} else {
 
		TileIndex tile;
 

	
 
		BeginVehicleMove(v);
 

	
 
		tile = TileVirtXY(v->x_pos, v->y_pos);
 
		if (!IsTileType(tile, MP_INDUSTRY)) {
 
			EndVehicleMove(v);
 
			delete v;
 
			return;
 
		}
 

	
 
		if (v->cur_image != SPR_CHIMNEY_SMOKE_7) {
 
			v->cur_image++;
 
		} else {
 
			v->cur_image = SPR_CHIMNEY_SMOKE_0;
 
		}
 
		v->progress = 7;
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 
}
 

	
 
static void SteamSmokeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_STEAM_SMOKE_0;
 
	v->progress = 12;
 
}
 

	
 
static void SteamSmokeTick(Vehicle *v)
 
{
 
	bool moved = false;
 

	
 
	BeginVehicleMove(v);
 

	
 
	v->progress++;
 

	
 
	if ((v->progress & 7) == 0) {
 
		v->z_pos++;
 
		moved = true;
 
	}
 

	
 
	if ((v->progress & 0xF) == 4) {
 
		if (v->cur_image != SPR_STEAM_SMOKE_4) {
 
			v->cur_image++;
 
		} else {
 
			EndVehicleMove(v);
 
			delete v;
 
			return;
 
		}
 
		moved = true;
 
	}
 

	
 
	if (moved) {
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 
}
 

	
 
static void DieselSmokeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_DIESEL_SMOKE_0;
 
	v->progress = 0;
 
}
 

	
 
static void DieselSmokeTick(Vehicle *v)
 
{
 
	v->progress++;
 

	
 
	if ((v->progress & 3) == 0) {
 
		BeginVehicleMove(v);
 
		v->z_pos++;
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	} else if ((v->progress & 7) == 1) {
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_DIESEL_SMOKE_5) {
 
			v->cur_image++;
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
		} else {
 
			EndVehicleMove(v);
 
			delete v;
 
		}
 
	}
 
}
 

	
 
static void ElectricSparkInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_ELECTRIC_SPARK_0;
 
	v->progress = 1;
 
}
 

	
 
static void ElectricSparkTick(Vehicle *v)
 
{
 
	if (v->progress < 2) {
 
		v->progress++;
 
	} else {
 
		v->progress = 0;
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_ELECTRIC_SPARK_5) {
 
			v->cur_image++;
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
		} else {
 
			EndVehicleMove(v);
 
			delete v;
 
		}
 
	}
 
}
 

	
 
static void SmokeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_SMOKE_0;
 
	v->progress = 12;
 
}
 

	
 
static void SmokeTick(Vehicle *v)
 
{
 
	bool moved = false;
 

	
 
	BeginVehicleMove(v);
 

	
 
	v->progress++;
 

	
 
	if ((v->progress & 3) == 0) {
 
		v->z_pos++;
 
		moved = true;
 
	}
 

	
 
	if ((v->progress & 0xF) == 4) {
 
		if (v->cur_image != SPR_SMOKE_4) {
 
			v->cur_image++;
 
		} else {
 
			EndVehicleMove(v);
 
			delete v;
 
			return;
 
		}
 
		moved = true;
 
	}
 

	
 
	if (moved) {
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 
}
 

	
 
static void ExplosionLargeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_EXPLOSION_LARGE_0;
 
	v->progress = 0;
 
}
 

	
 
static void ExplosionLargeTick(Vehicle *v)
 
{
 
	v->progress++;
 
	if ((v->progress & 3) == 0) {
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_EXPLOSION_LARGE_F) {
 
			v->cur_image++;
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
		} else {
 
			EndVehicleMove(v);
 
			delete v;
 
		}
 
	}
 
}
 

	
 
static void BreakdownSmokeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_BREAKDOWN_SMOKE_0;
 
	v->progress = 0;
 
}
 

	
 
static void BreakdownSmokeTick(Vehicle *v)
 
{
 
	v->progress++;
 
	if ((v->progress & 7) == 0) {
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_BREAKDOWN_SMOKE_3) {
 
			v->cur_image++;
 
		} else {
 
			v->cur_image = SPR_BREAKDOWN_SMOKE_0;
 
		}
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 

	
 
	v->u.special.animation_state--;
 
	if (v->u.special.animation_state == 0) {
 
		BeginVehicleMove(v);
 
		EndVehicleMove(v);
 
		delete v;
 
	}
 
}
 

	
 
static void ExplosionSmallInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_EXPLOSION_SMALL_0;
 
	v->progress = 0;
 
}
 

	
 
static void ExplosionSmallTick(Vehicle *v)
 
{
 
	v->progress++;
 
	if ((v->progress & 3) == 0) {
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_EXPLOSION_SMALL_B) {
 
			v->cur_image++;
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
		} else {
 
			EndVehicleMove(v);
 
			delete v;
 
		}
 
	}
 
}
 

	
 
static void BulldozerInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_BULLDOZER_NE;
 
	v->progress = 0;
 
	v->u.special.animation_state = 0;
 
	v->u.special.animation_substate = 0;
 
}
 

	
 
struct BulldozerMovement {
 
	byte direction:2;
 
	byte image:2;
 
	byte duration:3;
 
};
 

	
 
static const BulldozerMovement _bulldozer_movement[] = {
 
	{ 0, 0, 4 },
 
	{ 3, 3, 4 },
 
	{ 2, 2, 7 },
 
	{ 0, 2, 7 },
 
	{ 1, 1, 3 },
 
	{ 2, 2, 7 },
 
	{ 0, 2, 7 },
 
	{ 1, 1, 3 },
 
	{ 2, 2, 7 },
 
	{ 0, 2, 7 },
 
	{ 3, 3, 6 },
 
	{ 2, 2, 6 },
 
	{ 1, 1, 7 },
 
	{ 3, 1, 7 },
 
	{ 0, 0, 3 },
 
	{ 1, 1, 7 },
 
	{ 3, 1, 7 },
 
	{ 0, 0, 3 },
 
	{ 1, 1, 7 },
 
	{ 3, 1, 7 }
 
};
 

	
 
static const struct {
 
	int8 x;
 
	int8 y;
 
} _inc_by_dir[] = {
 
	{ -1,  0 },
 
	{  0,  1 },
 
	{  1,  0 },
 
	{  0, -1 }
 
};
 

	
 
static void BulldozerTick(Vehicle *v)
 
{
 
	v->progress++;
 
	if ((v->progress & 7) == 0) {
 
		const BulldozerMovement* b = &_bulldozer_movement[v->u.special.animation_state];
 

	
 
		BeginVehicleMove(v);
 

	
 
		v->cur_image = SPR_BULLDOZER_NE + b->image;
 

	
 
		v->x_pos += _inc_by_dir[b->direction].x;
 
		v->y_pos += _inc_by_dir[b->direction].y;
 

	
 
		v->u.special.animation_substate++;
 
		if (v->u.special.animation_substate >= b->duration) {
 
			v->u.special.animation_substate = 0;
 
			v->u.special.animation_state++;
 
			if (v->u.special.animation_state == lengthof(_bulldozer_movement)) {
 
				EndVehicleMove(v);
 
				delete v;
 
				return;
 
			}
 
		}
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 
}
 

	
 
static void BubbleInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_BUBBLE_GENERATE_0;
 
	v->spritenum = 0;
 
	v->progress = 0;
 
}
 

	
 
struct BubbleMovement {
 
	int8 x:4;
 
	int8 y:4;
 
	int8 z:4;
 
	byte image:4;
 
};
 

	
 
#define MK(x, y, z, i) { x, y, z, i }
 
#define ME(i) { i, 4, 0, 0 }
 

	
 
static const BubbleMovement _bubble_float_sw[] = {
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 2),
 
	ME(1)
 
};
 

	
 

	
 
static const BubbleMovement _bubble_float_ne[] = {
 
	MK( 0, 0, 1, 0),
 
	MK(-1, 0, 1, 1),
 
	MK( 0, 0, 1, 0),
 
	MK(-1, 0, 1, 2),
 
	ME(1)
 
};
 

	
 
static const BubbleMovement _bubble_float_se[] = {
 
	MK(0, 0, 1, 0),
 
	MK(0, 1, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 1, 1, 2),
 
	ME(1)
 
};
 

	
 
static const BubbleMovement _bubble_float_nw[] = {
 
	MK(0,  0, 1, 0),
 
	MK(0, -1, 1, 1),
 
	MK(0,  0, 1, 0),
 
	MK(0, -1, 1, 2),
 
	ME(1)
 
};
 

	
 
static const BubbleMovement _bubble_burst[] = {
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 7),
 
	MK(0, 0, 1, 8),
 
	MK(0, 0, 1, 9),
 
	ME(0)
 
};
 

	
 
static const BubbleMovement _bubble_absorb[] = {
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(2, 1, 3, 0),
 
	MK(1, 1, 3, 1),
 
	MK(2, 1, 3, 0),
 
	MK(1, 1, 3, 2),
 
	MK(2, 1, 3, 0),
 
	MK(1, 1, 3, 1),
 
	MK(2, 1, 3, 0),
 
	MK(1, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 2),
 
	ME(2),
 
	MK(0, 0, 0, 0xA),
 
	MK(0, 0, 0, 0xB),
 
	MK(0, 0, 0, 0xC),
 
	MK(0, 0, 0, 0xD),
 
	MK(0, 0, 0, 0xE),
 
	ME(0)
 
};
 
#undef ME
 
#undef MK
 

	
 
static const BubbleMovement * const _bubble_movement[] = {
 
	_bubble_float_sw,
 
	_bubble_float_ne,
 
	_bubble_float_se,
 
	_bubble_float_nw,
 
	_bubble_burst,
 
	_bubble_absorb,
 
};
 

	
 
static void BubbleTick(Vehicle *v)
 
{
 
	/*
 
	 * Warning: those effects can NOT use Random(), and have to use
 
	 *  InteractiveRandom(), because somehow someone forgot to save
 
	 *  spritenum to the savegame, and so it will cause desyncs in
 
	 *  multiplayer!! (that is: in ToyLand)
 
	 */
 
	uint et;
 
	const BubbleMovement *b;
 

	
 
	v->progress++;
 
	if ((v->progress & 3) != 0)
 
		return;
 

	
 
	BeginVehicleMove(v);
 

	
 
	if (v->spritenum == 0) {
 
		v->cur_image++;
 
		if (v->cur_image < SPR_BUBBLE_GENERATE_3) {
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
			return;
 
		}
 
		if (v->u.special.animation_substate != 0) {
 
			v->spritenum = GB(InteractiveRandom(), 0, 2) + 1;
 
		} else {
 
			v->spritenum = 6;
 
		}
 
		et = 0;
 
	} else {
 
		et = v->engine_type + 1;
 
	}
 

	
 
	b = &_bubble_movement[v->spritenum - 1][et];
 

	
 
	if (b->y == 4 && b->x == 0) {
 
		EndVehicleMove(v);
 
		delete v;
 
		return;
 
	}
 

	
 
	if (b->y == 4 && b->x == 1) {
 
		if (v->z_pos > 180 || CHANCE16I(1, 96, InteractiveRandom())) {
 
			v->spritenum = 5;
 
			SndPlayVehicleFx(SND_2F_POP, v);
 
		}
 
		et = 0;
 
	}
 

	
 
	if (b->y == 4 && b->x == 2) {
 
		TileIndex tile;
 

	
 
		et++;
 
		SndPlayVehicleFx(SND_31_EXTRACT, v);
 

	
 
		tile = TileVirtXY(v->x_pos, v->y_pos);
 
		if (IsTileType(tile, MP_INDUSTRY) && GetIndustryGfx(tile) == 0xA2) AddAnimatedTile(tile);
 
	}
 

	
 
	v->engine_type = et;
 
	b = &_bubble_movement[v->spritenum - 1][et];
 

	
 
	v->x_pos += b->x;
 
	v->y_pos += b->y;
 
	v->z_pos += b->z;
 
	v->cur_image = SPR_BUBBLE_0 + b->image;
 

	
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 
}
 

	
 

	
 
typedef void EffectInitProc(Vehicle *v);
 
typedef void EffectTickProc(Vehicle *v);
 

	
 
static EffectInitProc * const _effect_init_procs[] = {
 
	ChimneySmokeInit,
 
	SteamSmokeInit,
 
	DieselSmokeInit,
 
	ElectricSparkInit,
 
	SmokeInit,
 
	ExplosionLargeInit,
 
	BreakdownSmokeInit,
 
	ExplosionSmallInit,
 
	BulldozerInit,
 
	BubbleInit,
 
};
 

	
 
static EffectTickProc * const _effect_tick_procs[] = {
 
	ChimneySmokeTick,
 
	SteamSmokeTick,
 
	DieselSmokeTick,
 
	ElectricSparkTick,
 
	SmokeTick,
 
	ExplosionLargeTick,
 
	BreakdownSmokeTick,
 
	ExplosionSmallTick,
 
	BulldozerTick,
 
	BubbleTick,
 
};
 

	
 

	
 
Vehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicle type)
src/vehicle.h
Show inline comments
 
/* $Id$ */
 

	
 
/** @vehicle.h */
 

	
 
#ifndef VEHICLE_H
 
#define VEHICLE_H
 

	
 
#include "oldpool.h"
 
#include "order.h"
 
#include "rail.h"
 
#include "road.h"
 
#include "cargopacket.h"
 
#include "texteff.hpp"
 

	
 
/** The returned bits of VehicleEnterTile. */
 
enum VehicleEnterTileStatus {
 
	VETS_ENTERED_STATION  = 1, ///< The vehicle entered a station
 
	VETS_ENTERED_WORMHOLE = 2, ///< The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/tunnel)
 
	VETS_CANNOT_ENTER     = 3, ///< The vehicle cannot enter the tile
 

	
 
	/**
 
	 * Shift the VehicleEnterTileStatus this many bits
 
	 * to the right to get the station ID when
 
	 * VETS_ENTERED_STATION is set
 
	 */
 
	VETS_STATION_ID_OFFSET = 8,
 

	
 
	/** Bit sets of the above specified bits */
 
	VETSB_CONTINUE         = 0,                          ///< The vehicle can continue normally
 
	VETSB_ENTERED_STATION  = 1 << VETS_ENTERED_STATION,  ///< The vehicle entered a station
 
	VETSB_ENTERED_WORMHOLE = 1 << VETS_ENTERED_WORMHOLE, ///< The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/tunnel)
 
	VETSB_CANNOT_ENTER     = 1 << VETS_CANNOT_ENTER,     ///< The vehicle cannot enter the tile
 
};
 

	
 
/** Road vehicle states */
 
enum RoadVehicleStates {
 
	/*
 
	 * Lower 4 bits are used for vehicle track direction. (Trackdirs)
 
	 * When in a road stop (bit 5 or bit 6 set) these bits give the
 
	 * track direction of the entry to the road stop.
 
	 * As the entry direction will always be a diagonal
 
	 * direction (X_NE, Y_SE, X_SW or Y_NW) only bits 0 and 3
 
	 * are needed to hold this direction. Bit 1 is then used to show
 
	 * that the vehicle is using the second road stop bay.
 
	 * Bit 2 is then used for drive-through stops to show the vehicle
 
	 * is stopping at this road stop.
 
	 */
 

	
 
	/* Numeric values */
 
	RVSB_IN_DEPOT                = 0xFE,                      ///< The vehicle is in a depot
 
	RVSB_WORMHOLE                = 0xFF,                      ///< The vehicle is in a tunnel and/or bridge
 

	
 
	/* Bit numbers */
 
	RVS_USING_SECOND_BAY         =    1,                      ///< Only used while in a road stop
 
	RVS_IS_STOPPING              =    2,                      ///< Only used for drive-through stops. Vehicle will stop here
 
	RVS_DRIVE_SIDE               =    4,                      ///< Only used when retrieving move data
 
	RVS_IN_ROAD_STOP             =    5,                      ///< The vehicle is in a road stop
 
	RVS_IN_DT_ROAD_STOP          =    6,                      ///< The vehicle is in a drive-through road stop
 

	
 
	/* Bit sets of the above specified bits */
 
	RVSB_IN_ROAD_STOP            = 1 << RVS_IN_ROAD_STOP,     ///< The vehicle is in a road stop
 
	RVSB_IN_ROAD_STOP_END        = RVSB_IN_ROAD_STOP + TRACKDIR_END,
 
	RVSB_IN_DT_ROAD_STOP         = 1 << RVS_IN_DT_ROAD_STOP,  ///< The vehicle is in a drive-through road stop
 
	RVSB_IN_DT_ROAD_STOP_END     = RVSB_IN_DT_ROAD_STOP + TRACKDIR_END,
 

	
 
	RVSB_TRACKDIR_MASK           = 0x0F,                      ///< The mask used to extract track dirs
 
	RVSB_ROAD_STOP_TRACKDIR_MASK = 0x09                       ///< Only bits 0 and 3 are used to encode the trackdir for road stops
 
};
 

	
 
enum VehicleType {
 
	VEH_TRAIN,
 
	VEH_ROAD,
 
	VEH_SHIP,
 
	VEH_AIRCRAFT,
 
	VEH_SPECIAL,
 
	VEH_DISASTER,
 
	VEH_END,
 
	VEH_INVALID = 0xFF,
 
};
 
DECLARE_POSTFIX_INCREMENT(VehicleType);
 
template <> struct EnumPropsT<VehicleType> : MakeEnumPropsT<VehicleType, byte, VEH_TRAIN, VEH_END, VEH_INVALID> {};
 
typedef TinyEnumT<VehicleType> VehicleTypeByte;
 

	
 
enum VehStatus {
 
	VS_HIDDEN          = 0x01,
 
	VS_STOPPED         = 0x02,
 
	VS_UNCLICKABLE     = 0x04,
 
	VS_DEFPAL          = 0x08,
 
	VS_TRAIN_SLOWING   = 0x10,
 
	VS_SHADOW          = 0x20,
 
	VS_AIRCRAFT_BROKEN = 0x40,
 
	VS_CRASHED         = 0x80,
 
};
 

	
 
enum VehicleFlags {
 
	VF_LOADING_FINISHED,
 
	VF_CARGO_UNLOADING,
 
	VF_BUILT_AS_PROTOTYPE,
 
	VF_TIMETABLE_STARTED,  ///< Whether the vehicle has started running on the timetable yet.
 
	VF_AUTOFILL_TIMETABLE, ///< Whether the vehicle should fill in the timetable automatically.
 
};
 

	
 
/* Effect vehicle types */
 
enum EffectVehicle {
 
	EV_CHIMNEY_SMOKE   = 0,
 
	EV_STEAM_SMOKE     = 1,
 
	EV_DIESEL_SMOKE    = 2,
 
	EV_ELECTRIC_SPARK  = 3,
 
	EV_SMOKE           = 4,
 
	EV_EXPLOSION_LARGE = 5,
 
	EV_BREAKDOWN_SMOKE = 6,
 
	EV_EXPLOSION_SMALL = 7,
 
	EV_BULLDOZER       = 8,
 
	EV_BUBBLE          = 9
 
};
 

	
 
struct VehicleRail {
 
	uint16 last_speed; // NOSAVE: only used in UI
 
	uint16 crash_anim_pos;
 

	
 
	/* cached values, recalculated on load and each time a vehicle is added to/removed from the consist. */
 
	uint16 cached_max_speed;  // max speed of the consist. (minimum of the max speed of all vehicles in the consist)
 
	uint32 cached_power;      // total power of the consist.
 
	uint8 cached_veh_length;  // length of this vehicle in units of 1/8 of normal length, cached because this can be set by a callback
 
	uint16 cached_total_length; ///< Length of the whole train, valid only for first engine.
 

	
 
	/* cached values, recalculated when the cargo on a train changes (in addition to the conditions above) */
 
	uint32 cached_weight;     // total weight of the consist.
 
	uint32 cached_veh_weight; // weight of the vehicle.
 
	uint32 cached_max_te;     // max tractive effort of consist
 
	/**
 
	 * Position/type of visual effect.
 
	 * bit 0 - 3 = position of effect relative to vehicle. (0 = front, 8 = centre, 15 = rear)
 
	 * bit 4 - 5 = type of effect. (0 = default for engine class, 1 = steam, 2 = diesel, 3 = electric)
 
	 * bit     6 = disable visual effect.
 
	 * bit     7 = disable powered wagons.
 
	 */
 
	byte cached_vis_effect;
 

	
 
	/* NOSAVE: for wagon override - id of the first engine in train
 
	 * 0xffff == not in train */
 
	EngineID first_engine;
 

	
 
	TrackBitsByte track;
 
	byte force_proceed;
 
	RailTypeByte railtype;
 
	RailTypeMask compatible_railtypes;
 

	
 
	byte flags;
 

	
 
	/* Link between the two ends of a multiheaded engine */
 
	Vehicle *other_multiheaded_part;
 

	
 
	/* Cached wagon override spritegroup */
 
	const struct SpriteGroup *cached_override;
 
};
 

	
 
enum {
 
	VRF_REVERSING         = 0,
 

	
 
	/* used to calculate if train is going up or down */
 
	VRF_GOINGUP           = 1,
 
	VRF_GOINGDOWN         = 2,
 

	
 
	/* used to store if a wagon is powered or not */
 
	VRF_POWEREDWAGON      = 3,
 

	
 
	/* used to reverse the visible direction of the vehicle */
 
	VRF_REVERSE_DIRECTION = 4,
 

	
 
	/* used to mark train as lost because PF can't find the route */
 
	VRF_NO_PATH_TO_DESTINATION = 5,
 

	
 
	/* used to mark that electric train engine is allowed to run on normal rail */
 
	VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL = 6,
 
};
 

	
 
struct VehicleAir {
 
	uint16 crashed_counter;
 
	uint16 cached_max_speed;
 
	byte pos;
 
	byte previous_pos;
 
	StationID targetairport;
 
	byte state;
 
};
 

	
 
struct VehicleRoad {
 
	byte state;             ///< @see RoadVehicleStates
 
	byte frame;
 
	uint16 blocked_ctr;
 
	byte overtaking;
 
	byte overtaking_ctr;
 
	uint16 crashed_ctr;
 
	byte reverse_ctr;
 
	struct RoadStop *slot;
 
	byte slot_age;
 
	EngineID first_engine;
 
	byte cached_veh_length;
 

	
 
	RoadType roadtype;
 
	RoadTypes compatible_roadtypes;
 
};
 

	
 
struct VehicleSpecial {
 
	uint16 animation_state;
 
	byte animation_substate;
 
};
 

	
 
struct VehicleDisaster {
 
	uint16 image_override;
 
	VehicleID big_ufo_destroyer_target;
 
};
 

	
 
struct VehicleShip {
 
	TrackBitsByte state;
 
};
 

	
 
struct Vehicle;
 
DECLARE_OLD_POOL(Vehicle, Vehicle, 9, 125)
 

	
 
struct Vehicle : PoolItem<Vehicle, VehicleID, &_Vehicle_pool> {
 
	VehicleTypeByte type;    ///< Type of vehicle
 
	byte subtype;            // subtype (Filled with values from EffectVehicles/TrainSubTypes/AircraftSubTypes)
 

	
 
	Vehicle *next;           // next
 
	Vehicle *first;          // NOSAVE: pointer to the first vehicle in the chain
 
	Vehicle *depot_list;     //NOSAVE: linked list to tell what vehicles entered a depot during the last tick. Used by autoreplace
 

	
 
	StringID string_id;      // Displayed string
 

	
 
	UnitID unitnumber;       // unit number, for display purposes only
 
	PlayerByte owner;        // which player owns the vehicle?
 

	
 
	TileIndex tile;          // Current tile index
 
	TileIndex dest_tile;     // Heading for this tile
 

	
 
	int32 x_pos;             // coordinates
 
	int32 y_pos;
 
	byte z_pos;
 
	DirectionByte direction; // facing
 

	
 
	byte spritenum;          // currently displayed sprite index
 
	                         // 0xfd == custom sprite, 0xfe == custom second head sprite
 
	                         // 0xff == reserved for another custom sprite
 
	uint16 cur_image;        // sprite number for this vehicle
 
	byte sprite_width;       // width of vehicle sprite
 
	byte sprite_height;      // height of vehicle sprite
 
	byte z_height;           // z-height of vehicle sprite
 
	int8 x_offs;             // x offset for vehicle sprite
 
	int8 y_offs;             // y offset for vehicle sprite
 
	EngineID engine_type;
 

	
 
	TextEffectID fill_percent_te_id; // a text-effect id to a loading indicator object
 

	
 
	/* for randomized variational spritegroups
 
	 * bitmask used to resolve them; parts of it get reseeded when triggers
 
	 * of corresponding spritegroups get matched */
 
	byte random_bits;
 
	byte waiting_triggers;   // triggers to be yet matched
 

	
 
	uint16 max_speed;        // maximum speed
 
	uint16 cur_speed;        // current speed
 
	byte subspeed;           // fractional speed
 
	byte acceleration;       // used by train & aircraft
 
	byte progress;
 
	uint32 motion_counter;
 

	
 
	byte vehstatus;          // Status
 
	StationID last_station_visited;
 

	
 
	CargoID cargo_type;      // type of cargo this vehicle is carrying
 
	uint16 cargo_cap;        // total capacity
 
	byte cargo_subtype;      ///< Used for livery refits (NewGRF variations)
 
	CargoList cargo;         ///< The cargo this vehicle is carrying
 

	
 

	
 
	byte day_counter;        // increased by one for each day
 
	byte tick_counter;       // increased by one for each tick
 

	
 
	/* Begin Order-stuff */
 
	Order current_order;     ///< The current order (+ status, like: loading)
 
	VehicleOrderID cur_order_index; ///< The index to the current order
 

	
 
	Order *orders;           ///< Pointer to the first order for this vehicle
 
	VehicleOrderID num_orders;      ///< How many orders there are in the list
 

	
 
	Vehicle *next_shared;    ///< If not NULL, this points to the next vehicle that shared the order
 
	Vehicle *prev_shared;    ///< If not NULL, this points to the prev vehicle that shared the order
 
	/* End Order-stuff */
 

	
 
	/* Boundaries for the current position in the world and a next hash link.
 
	 * NOSAVE: All of those can be updated with VehiclePositionChanged() */
 
	int32 left_coord;
 
	int32 top_coord;
 
	int32 right_coord;
 
	int32 bottom_coord;
 
	Vehicle *next_hash;
 
	Vehicle *next_new_hash;
 
	Vehicle **old_new_hash;
 

	
 
	/* Related to age and service time */
 
	Date age;     // Age in days
 
	Date max_age; // Maximum age
 
	Date date_of_last_service;
 
	Date service_interval;
 
	uint16 reliability;
 
	uint16 reliability_spd_dec;
 
	byte breakdown_ctr;
 
	byte breakdown_delay;
 
	byte breakdowns_since_last_service;
 
	byte breakdown_chance;
 
	Year build_year;
 

	
 
	bool leave_depot_instantly; // NOSAVE: stores if the vehicle needs to leave the depot it just entered. Used by autoreplace
 

	
 
	uint16 load_unload_time_rem;
 
	byte vehicle_flags;         // Used for gradual loading and other miscellaneous things (@see VehicleFlags enum)
 

	
 
	Money profit_this_year;
 
	Money profit_last_year;
 
	Money value;
 

	
 
	GroupID group_id;              ///< Index of group Pool array
 

	
 
	/* Used for timetabling. */
 
	uint32 current_order_time;     ///< How many ticks have passed since this order started.
 
	int32 lateness_counter;        ///< How many ticks late (or early if negative) this vehicle is.
 

	
 
	union {
 
		VehicleRail rail;
 
		VehicleAir air;
 
		VehicleRoad road;
 
		VehicleSpecial special;
 
		VehicleDisaster disaster;
 
		VehicleShip ship;
 
	} u;
 

	
 

	
 
	/**
 
	 * Allocates a lot of vehicles.
 
	 * @param vl pointer to an array of vehicles to get allocated. Can be NULL if the vehicles aren't needed (makes it test only)
 
	 * @param num number of vehicles to allocate room for
 
	 * @return true if there is room to allocate all the vehicles
 
	 */
 
	static bool AllocateList(Vehicle **vl, int num);
 

	
 
	/** Create a new vehicle */
 
	Vehicle();
 

	
 
	/** Destroy all stuff that (still) needs the virtual functions to work properly */
 
	void PreDestructor();
 
	/** We want to 'destruct' the right class. */
 
	virtual ~Vehicle();
 

	
 
	void QuickFree();
 

	
 
	void BeginLoading();
 
	void LeaveStation();
 

	
 
	/**
 
	 * Handle the loading of the vehicle; when not it skips through dummy
 
	 * orders and does nothing in all other cases.
 
	 * @param mode is the non-first call for this vehicle in this tick?
 
	 */
 
	void HandleLoading(bool mode = false);
 

	
 
	/**
 
	 * Get a string 'representation' of the vehicle type.
 
	 * @return the string representation.
 
	 */
 
	virtual const char* GetTypeString() const { return "base vehicle"; }
 

	
 
	/**
 
	 * Marks the vehicles to be redrawn and updates cached variables
 
	 */
 
	virtual void MarkDirty() {}
 

	
 
	/**
 
	 * Updates the x and y offsets and the size of the sprite used
 
	 * for this vehicle.
 
	 * @param direction the direction the vehicle is facing
 
	 */
 
	virtual void UpdateDeltaXY(Direction direction) {}
 

	
 
	/**
 
	 * Sets the expense type associated to this vehicle type
 
	 * @param income whether this is income or (running) expenses of the vehicle
 
	 */
 
	virtual ExpensesType GetExpenseType(bool income) const { return EXPENSES_OTHER; }
 

	
 
	/**
 
	 * Invalidates the vehicle list window of this type of vehicle
 
	 */
 
	virtual WindowClass GetVehicleListWindowClass() const { return WC_NONE; }
 

	
 
	/**
 
	 * Play the sound associated with leaving the station
 
	 */
 
	virtual void PlayLeaveStationSound() const {}
 

	
 
	/**
 
	 * Whether this is the primary vehicle in the chain.
 
	 */
 
	virtual bool IsPrimaryVehicle() const { return false; }
 

	
 
	/**
 
	 * Whether this vehicle understands the concept of a front engine, so
 
	 * basically, if GetFirstVehicleInChain() can be called for it.
 
	 */
 
	virtual bool HasFront() const { return false; }
 

	
 
	/**
 
	 * Gets the sprite to show for the given direction
 
	 * @param direction the direction the vehicle is facing
 
	 * @return the sprite for the given vehicle in the given direction
 
	 */
 
	virtual int GetImage(Direction direction) const { return 0; }
 

	
 
	/**
 
	 * Calls the tick handler of the vehicle
 
	 */
 
	virtual void Tick() {};
 

	
 
	bool IsValid() const { return this->type != VEH_INVALID; }
 
};
 

	
 
/**
 
 * This class 'wraps' Vehicle; you do not actually instantiate this class.
 
 * You create a Vehicle using AllocateVehicle, so it is added to the pool
 
 * and you reinitialize that to a Train using:
 
 *   v = new (v) Train();
 
 *
 
 * As side-effect the vehicle type is set correctly.
 
 *
 
 * A special vehicle is one of the following:
 
 *  - smoke
 
 *  - electric sparks for trains
 
 *  - explosions
 
 *  - bulldozer (road works)
 
 *  - bubbles (industry)
 
 */
 
struct SpecialVehicle : public Vehicle {
 
	/** Initializes the Vehicle to a special vehicle */
 
	SpecialVehicle() { this->type = VEH_SPECIAL; }
 

	
 
	/** We want to 'destruct' the right class. */
 
	virtual ~SpecialVehicle() {}
 

	
 
	const char *GetTypeString() const { return "special vehicle"; }
 
	void UpdateDeltaXY(Direction direction);
 
	void Tick();
 
};
 

	
 
/**
 
 * This class 'wraps' Vehicle; you do not actually instantiate this class.
 
 * You create a Vehicle using AllocateVehicle, so it is added to the pool
 
 * and you reinitialize that to a Train using:
 
 *   v = new (v) Train();
 
 *
 
 * As side-effect the vehicle type is set correctly.
 
 */
 
struct DisasterVehicle : public Vehicle {
 
	/** Initializes the Vehicle to a disaster vehicle */
 
	DisasterVehicle() { this->type = VEH_DISASTER; }
 

	
 
	/** We want to 'destruct' the right class. */
 
	virtual ~DisasterVehicle() {}
 

	
 
	const char *GetTypeString() const { return "disaster vehicle"; }
 
	void UpdateDeltaXY(Direction direction);
 
	void Tick();
 
};
 

	
 
/**
 
 * This class 'wraps' Vehicle; you do not actually instantiate this class.
 
 * You create a Vehicle using AllocateVehicle, so it is added to the pool
 
 * and you reinitialize that to a Train using:
 
 *   v = new (v) Train();
 
 *
 
 * As side-effect the vehicle type is set correctly.
 
 */
 
struct InvalidVehicle : public Vehicle {
 
	/** Initializes the Vehicle to a invalid vehicle */
 
	InvalidVehicle() { this->type = VEH_INVALID; }
 

	
 
	/** We want to 'destruct' the right class. */
 
	virtual ~InvalidVehicle() {}
 

	
 
	const char *GetTypeString() const { return "invalid vehicle"; }
 
	void Tick() {}
 
};
 

	
 
#define is_custom_sprite(x) (x >= 0xFD)
 
#define IS_CUSTOM_FIRSTHEAD_SPRITE(x) (x == 0xFD)
 
#define IS_CUSTOM_SECONDHEAD_SPRITE(x) (x == 0xFE)
 

	
 
typedef void *VehicleFromPosProc(Vehicle *v, void *data);
 

	
 
void VehicleServiceInDepot(Vehicle *v);
 
void VehiclePositionChanged(Vehicle *v);
 
void AfterLoadVehicles();
 
Vehicle *GetLastVehicleInChain(Vehicle *v);
 
Vehicle *GetPrevVehicleInChain(const Vehicle *v);
 
Vehicle *GetFirstVehicleInChain(const Vehicle *v);
 
uint CountVehiclesInChain(const Vehicle *v);
 
bool IsEngineCountable(const Vehicle *v);
 
void DeleteVehicleChain(Vehicle *v);
 
void *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc);
 
void *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc);
 
void CallVehicleTicks();
 
Vehicle *FindVehicleOnTileZ(TileIndex tile, byte z);
 
uint8 CalcPercentVehicleFilled(Vehicle *v, StringID *color);
 

	
 
void InitializeTrains();
 
byte VehicleRandomBits();
 
void ResetVehiclePosHash();
 

	
 
bool CanRefitTo(EngineID engine_type, CargoID cid_to);
 
CargoID FindFirstRefittableCargo(EngineID engine_type);
 
CommandCost GetRefitCost(EngineID engine_type);
 

	
 
void ViewportAddVehicles(DrawPixelInfo *dpi);
 

	
 
SpriteID GetRotorImage(const Vehicle *v);
 

	
 
Vehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicle type);
 
Vehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicle type);
 
Vehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicle type);
 

	
 
uint32 VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y);
 

	
 
StringID VehicleInTheWayErrMsg(const Vehicle* v);
 
Vehicle *FindVehicleBetween(TileIndex from, TileIndex to, byte z, bool without_crashed = false);
 

	
 
bool UpdateSignalsOnSegment(TileIndex tile, DiagDirection direction);
 
void SetSignalsOnBothDir(TileIndex tile, byte track);
 

	
 
Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y);
 

	
 
void DecreaseVehicleValue(Vehicle *v);
 
void CheckVehicleBreakdown(Vehicle *v);
 
void AgeVehicle(Vehicle *v);
 
void VehicleEnteredDepotThisTick(Vehicle *v);
 

	
 
void BeginVehicleMove(Vehicle *v);
 
void EndVehicleMove(Vehicle *v);
 

	
 
void ShowAircraftViewWindow(const Vehicle* v);
 

	
 
UnitID GetFreeUnitNumber(VehicleType type);
 

	
 
void TrainConsistChanged(Vehicle *v);
 
void TrainPowerChanged(Vehicle *v);
 
Money GetTrainRunningCost(const Vehicle *v);
 

	
 
int CheckTrainStoppedInDepot(const Vehicle *v);
 

	
 
bool VehicleNeedsService(const Vehicle *v);
 

	
 
uint GenerateVehicleSortList(const Vehicle*** sort_list, uint16 *length_of_array, VehicleType type, PlayerID owner, uint32 index, uint16 window_type);
 
void BuildDepotVehicleList(VehicleType type, TileIndex tile, Vehicle ***engine_list, uint16 *engine_list_length, uint16 *engine_count, Vehicle ***wagon_list, uint16 *wagon_list_length, uint16 *wagon_count);
 
CommandCost SendAllVehiclesToDepot(VehicleType type, uint32 flags, bool service, PlayerID owner, uint16 vlw_flag, uint32 id);
 
bool IsVehicleInDepot(const Vehicle *v);
 
void VehicleEnterDepot(Vehicle *v);
 

	
 
void InvalidateAutoreplaceWindow(EngineID e, GroupID id_g);
 

	
 
CommandCost MaybeReplaceVehicle(Vehicle *v, bool check, bool display_costs);
 
bool CanBuildVehicleInfrastructure(VehicleType type);
 

	
 
/* Flags to add to p2 for goto depot commands */
 
/* Note: bits 8-10 are used for VLW flags */
 
enum {
 
	DEPOT_SERVICE       = (1 << 0), // The vehicle will leave the depot right after arrival (serivce only)
 
	DEPOT_MASS_SEND     = (1 << 1), // Tells that it's a mass send to depot command (type in VLW flag)
 
	DEPOT_DONT_CANCEL   = (1 << 2), // Don't cancel current goto depot command if any
 
	DEPOT_LOCATE_HANGAR = (1 << 3), // Find another airport if the target one lacks a hangar
 
};
 

	
 
struct GetNewVehiclePosResult {
 
	int x, y;
 
	TileIndex old_tile;
 
	TileIndex new_tile;
 
};
 

	
 
/**
 
 * Returns the Trackdir on which the vehicle is currently located.
 
 * Works for trains and ships.
 
 * Currently works only sortof for road vehicles, since they have a fuzzy
 
 * concept of being "on" a trackdir. Dunno really what it returns for a road
 
 * vehicle that is halfway a tile, never really understood that part. For road
 
 * vehicles that are at the beginning or end of the tile, should just return
 
 * the diagonal trackdir on which they are driving. I _think_.
 
 * For other vehicles types, or vehicles with no clear trackdir (such as those
 
 * in depots), returns 0xFF.
 
 */
 
Trackdir GetVehicleTrackdir(const Vehicle* v);
 

	
 
/* returns true if staying in the same tile */
 
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v);
 
Direction GetDirectionTowards(const Vehicle *v, int x, int y);
 

	
 
#define BEGIN_ENUM_WAGONS(v) do {
 
#define END_ENUM_WAGONS(v) } while ((v = v->next) != NULL);
 

	
 
static inline VehicleID GetMaxVehicleIndex()
 
{
 
	/* TODO - This isn't the real content of the function, but
 
	 *  with the new pool-system this will be replaced with one that
 
	 *  _really_ returns the highest index. Now it just returns
 
	 *  the next safe value we are sure about everything is below.
 
	 */
 
	return GetVehiclePoolSize() - 1;
 
}
 

	
 
static inline uint GetNumVehicles()
 
{
 
	return GetVehiclePoolSize();
 
}
 

	
 
static inline bool IsPlayerBuildableVehicleType(VehicleType type)
 
{
 
	switch (type) {
 
		case VEH_TRAIN:
 
		case VEH_ROAD:
 
		case VEH_SHIP:
 
		case VEH_AIRCRAFT:
 
			return true;
 

	
 
		default: return false;
 
	}
 
}
 

	
 
static inline bool IsPlayerBuildableVehicleType(const Vehicle *v)
 
{
 
	return IsPlayerBuildableVehicleType(v->type);
 
}
 

	
 
#define FOR_ALL_VEHICLES_FROM(v, start) for (v = GetVehicle(start); v != NULL; v = (v->index + 1U < GetVehiclePoolSize()) ? GetVehicle(v->index + 1) : NULL) if (v->IsValid())
 
#define FOR_ALL_VEHICLES(v) FOR_ALL_VEHICLES_FROM(v, 0)
 

	
 
/**
 
 * Check if an index is a vehicle-index (so between 0 and max-vehicles)
 
 * @param index of the vehicle to query
 
 * @return Returns true if the vehicle-id is in range
 
 */
 
static inline bool IsValidVehicleID(uint index)
 
{
 
	return index < GetVehiclePoolSize() && GetVehicle(index)->IsValid();
 
}
 

	
 
/* Returns order 'index' of a vehicle or NULL when it doesn't exists */
 
static inline Order *GetVehicleOrder(const Vehicle *v, int index)
 
{
 
	Order *order = v->orders;
 

	
 
	if (index < 0) return NULL;
 

	
 
	while (order != NULL && index-- > 0)
 
		order = order->next;
 

	
 
	return order;
 
}
 

	
 
/**
 
 * Returns the last order of a vehicle, or NULL if it doesn't exists
 
 * @param v Vehicle to query
 
 * @return last order of a vehicle, if available
 
 */
 
static inline Order *GetLastVehicleOrder(const Vehicle *v)
 
{
 
	Order *order = v->orders;
 

	
 
	if (order == NULL) return NULL;
 

	
 
	while (order->next != NULL)
 
		order = order->next;
 

	
 
	return order;
 
}
 

	
 
/** Get the first vehicle of a shared-list, so we only have to walk forwards
 
 * @param v Vehicle to query
 
 * @return first vehicle of a shared-list
 
 */
 
static inline Vehicle *GetFirstVehicleFromSharedList(const Vehicle *v)
 
{
 
	Vehicle *u = (Vehicle *)v;
 
	while (u->prev_shared != NULL) u = u->prev_shared;
 

	
 
	return u;
 
}
 

	
 
/* NOSAVE: Return values from various commands. */
 
VARDEF VehicleID _new_vehicle_id;
 
VARDEF uint16 _returned_refit_capacity;
 

	
 
static const VehicleID INVALID_VEHICLE = 0xFFFF;
 

	
 
const struct Livery *GetEngineLivery(EngineID engine_type, PlayerID player, EngineID parent_engine_type, const Vehicle *v);
 

	
 
/**
 
 * Get the colour map for an engine. This used for unbuilt engines in the user interface.
 
 * @param engine_type ID of engine
 
 * @param player ID of player
 
 * @return A ready-to-use palette modifier
 
 */
 
SpriteID GetEnginePalette(EngineID engine_type, PlayerID player);
 

	
 
/**
 
 * Get the colour map for a vehicle.
 
 * @param v Vehicle to get colour map for
 
 * @return A ready-to-use palette modifier
 
 */
 
SpriteID GetVehiclePalette(const Vehicle *v);
 

	
 
/* A lot of code calls for the invalidation of the status bar, which is widget 5.
 
 * Best is to have a virtual value for it when it needs to change again */
 
#define STATUS_BAR 5
 

	
 
extern const uint32 _veh_build_proc_table[];
 
extern const uint32 _veh_sell_proc_table[];
 
extern const uint32 _veh_refit_proc_table[];
 
extern const uint32 _send_to_depot_proc_table[];
 

	
 
/* Functions to find the right command for certain vehicle type */
 
static inline uint32 GetCmdBuildVeh(VehicleType type)
 
{
 
	return _veh_build_proc_table[type];
 
}
 

	
 
static inline uint32 GetCmdBuildVeh(const Vehicle *v)
 
{
 
	return GetCmdBuildVeh(v->type);
 
}
 

	
 
static inline uint32 GetCmdSellVeh(VehicleType type)
 
{
 
	return _veh_sell_proc_table[type];
 
}
 

	
 
static inline uint32 GetCmdSellVeh(const Vehicle *v)
 
{
 
	return GetCmdSellVeh(v->type);
 
}
 

	
 
static inline uint32 GetCmdRefitVeh(VehicleType type)
 
{
 
	return _veh_refit_proc_table[type];
 
}
 

	
 
static inline uint32 GetCmdRefitVeh(const Vehicle *v)
 
{
 
	return GetCmdRefitVeh(v->type);
 
}
 

	
 
static inline uint32 GetCmdSendToDepot(VehicleType type)
 
{
 
	return _send_to_depot_proc_table[type];
 
}
 

	
 
static inline uint32 GetCmdSendToDepot(const Vehicle *v)
 
{
 
	return GetCmdSendToDepot(v->type);
 
}
 

	
 
#endif /* VEHICLE_H */
src/waypoint.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/** @file waypoint.cpp */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 

	
 
#include "command.h"
 
#include "functions.h"
 
#include "gfx.h"
 
#include "landscape.h"
 
#include "map.h"
 
#include "order.h"
 
#include "rail_map.h"
 
#include "bridge_map.h"
 
#include "saveload.h"
 
#include "station.h"
 
#include "tile.h"
 
#include "town.h"
 
#include "waypoint.h"
 
#include "variables.h"
 
#include "table/strings.h"
 
#include "vehicle.h"
 
#include "yapf/yapf.h"
 
#include "date.h"
 
#include "newgrf.h"
 
#include "string.h"
 
#include "strings.h"
 
#include "misc/autoptr.hpp"
 

	
 
enum {
 
	MAX_WAYPOINTS_PER_TOWN = 64,
 
};
 

	
 
DEFINE_OLD_POOL_GENERIC(Waypoint, Waypoint)
 

	
 

	
 
/**
 
 * Update the sign for the waypoint
 
 * @param wp Waypoint to update sign */
 
static void UpdateWaypointSign(Waypoint* wp)
 
{
 
	Point pt = RemapCoords2(TileX(wp->xy) * TILE_SIZE, TileY(wp->xy) * TILE_SIZE);
 
	SetDParam(0, wp->index);
 
	UpdateViewportSignPos(&wp->sign, pt.x, pt.y - 0x20, STR_WAYPOINT_VIEWPORT);
 
}
 

	
 
/**
 
 * Redraw the sign of a waypoint
 
 * @param wp Waypoint to redraw sign */
 
static void RedrawWaypointSign(const Waypoint* wp)
 
{
 
	MarkAllViewportsDirty(
 
		wp->sign.left - 6,
 
		wp->sign.top,
 
		wp->sign.left + (wp->sign.width_1 << 2) + 12,
 
		wp->sign.top + 48);
 
}
 

	
 
/**
 
 * Update all signs
 
 */
 
void UpdateAllWaypointSigns()
 
{
 
	Waypoint *wp;
 

	
 
	FOR_ALL_WAYPOINTS(wp) {
 
		UpdateWaypointSign(wp);
 
	}
 
}
 

	
 
/**
 
 * Set the default name for a waypoint
 
 * @param wp Waypoint to work on
 
 */
 
static void MakeDefaultWaypointName(Waypoint* wp)
 
{
 
	Waypoint *local_wp;
 
	bool used_waypoint[MAX_WAYPOINTS_PER_TOWN];
 
	int i;
 

	
 
	wp->town_index = ClosestTownFromTile(wp->xy, (uint)-1)->index;
 

	
 
	memset(used_waypoint, 0, sizeof(used_waypoint));
 

	
 
	/* Find an unused waypoint number belonging to this town */
 
	FOR_ALL_WAYPOINTS(local_wp) {
 
		if (wp == local_wp) continue;
 

	
 
		if (local_wp->xy && local_wp->string == STR_NULL && local_wp->town_index == wp->town_index)
 
			used_waypoint[local_wp->town_cn] = true;
 
	}
 

	
 
	/* Find an empty spot */
 
	for (i = 0; used_waypoint[i] && i < MAX_WAYPOINTS_PER_TOWN; i++) {}
 

	
 
	wp->string = STR_NULL;
 
	wp->town_cn = i;
 
}
 

	
 
/**
 
 * Find a deleted waypoint close to a tile.
 
 * @param tile to search from
 
 */
 
static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile)
 
{
 
	Waypoint *wp, *best = NULL;
 
	uint thres = 8;
 

	
 
	FOR_ALL_WAYPOINTS(wp) {
 
		if (wp->deleted) {
 
			uint cur_dist = DistanceManhattan(tile, wp->xy);
 

	
 
			if (cur_dist < thres) {
 
				thres = cur_dist;
 
				best = wp;
 
			}
 
		}
 
	}
 

	
 
	return best;
 
}
 

	
 
/**
 
 * Update waypoint graphics id against saved GRFID/localidx.
 
 * This is to ensure the chosen graphics are correct if GRF files are changed.
 
 */
 
void AfterLoadWaypoints()
 
{
 
	Waypoint *wp;
 

	
 
	FOR_ALL_WAYPOINTS(wp) {
 
		uint i;
 

	
 
		if (wp->grfid == 0) continue;
 

	
 
		for (i = 0; i < GetNumCustomStations(STAT_CLASS_WAYP); i++) {
 
			const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, i);
 
			if (statspec != NULL && statspec->grffile->grfid == wp->grfid && statspec->localidx == wp->localidx) {
 
				wp->stat_id = i;
 
				break;
 
			}
 
		}
 
	}
 
}
 

	
 
/** Convert existing rail to waypoint. Eg build a waypoint station over
 
 * piece of rail
 
 * @param tile tile where waypoint will be built
 
 * @param flags type of operation
 
 * @param p1 graphics for waypoint type, 0 indicates standard graphics
 
 * @param p2 unused
 
 *
 
 * @todo When checking for the tile slope,
 
 * distingush between "Flat land required" and "land sloped in wrong direction"
 
 */
 
CommandCost CmdBuildTrainWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Waypoint *wp;
 
	AutoPtrT<Waypoint> wp_auto_delete;
 
	Slope tileh;
 
	Axis axis;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* if custom gfx are used, make sure it is within bounds */
 
	if (p1 >= GetNumCustomStations(STAT_CLASS_WAYP)) return CMD_ERROR;
 

	
 
	if (!IsTileType(tile, MP_RAILWAY) ||
 
			GetRailTileType(tile) != RAIL_TILE_NORMAL || (
 
				(axis = AXIS_X, GetTrackBits(tile) != TRACK_BIT_X) &&
 
				(axis = AXIS_Y, GetTrackBits(tile) != TRACK_BIT_Y)
 
			)) {
 
		return_cmd_error(STR_1005_NO_SUITABLE_RAILROAD_TRACK);
 
	}
 

	
 
	if (!CheckTileOwnership(tile)) return CMD_ERROR;
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	tileh = GetTileSlope(tile, NULL);
 
	if (tileh != SLOPE_FLAT &&
 
			(!_patches.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) {
 
		return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
 
	}
 

	
 
	if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 

	
 
	/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
 
	wp = FindDeletedWaypointCloseTo(tile);
 
	if (wp == NULL) {
 
		wp = new Waypoint(tile);
 
		if (wp == NULL) return CMD_ERROR;
 

	
 
		wp_auto_delete = wp;
 

	
 
		wp->town_index = 0;
 
		wp->string = STR_NULL;
 
		wp->town_cn = 0;
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		const StationSpec* statspec;
 

	
 
		MakeRailWaypoint(tile, GetTileOwner(tile), axis, GetRailType(tile), wp->index);
 
		MarkTileDirtyByTile(tile);
 

	
 
		statspec = GetCustomStationSpec(STAT_CLASS_WAYP, p1);
 

	
 
		if (statspec != NULL) {
 
			wp->stat_id = p1;
 
			wp->grfid = statspec->grffile->grfid;
 
			wp->localidx = statspec->localidx;
 
		} else {
 
			/* Specified custom graphics do not exist, so use default. */
 
			wp->stat_id = 0;
 
			wp->grfid = 0;
 
			wp->localidx = 0;
 
		}
 

	
 
		wp->deleted = 0;
 
		wp->build_date = _date;
 

	
 
		if (wp->town_index == 0) MakeDefaultWaypointName(wp);
 

	
 
		UpdateWaypointSign(wp);
 
		RedrawWaypointSign(wp);
 
		YapfNotifyTrackLayoutChange(tile, AxisToTrack(axis));
 
		wp_auto_delete.Detach();
 
	}
 

	
 
	return CommandCost(_price.build_train_depot);
 
}
 

	
 
/**
 
 * Daily loop for waypoints
 
 */
 
void WaypointsDailyLoop()
 
{
 
	Waypoint *wp;
 

	
 
	/* Check if we need to delete a waypoint */
 
	FOR_ALL_WAYPOINTS(wp) {
 
		if (wp->deleted != 0 && --wp->deleted == 0) DeleteWaypoint(wp);
 
	}
 
}
 

	
 
/**
 
 * Remove a waypoint
 
 * @param tile from which to remove waypoint
 
 * @param flags type of operation
 
 * @param justremove will indicate if it is removed from rail or if rails are removed too
 
 * @return cost of operation or error
 
 */
 
CommandCost RemoveTrainWaypoint(TileIndex tile, uint32 flags, bool justremove)
 
{
 
	Waypoint *wp;
 

	
 
	/* Make sure it's a waypoint */
 
	if (!IsTileType(tile, MP_RAILWAY) ||
 
			!IsRailWaypoint(tile) ||
 
			(!CheckTileOwnership(tile) && _current_player != OWNER_WATER) ||
 
			!EnsureNoVehicle(tile)) {
 
		return CMD_ERROR;
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		Track track = GetRailWaypointTrack(tile);
 
		wp = GetWaypointByTile(tile);
 

	
 
		wp->deleted = 30; // let it live for this many days before we do the actual deletion.
 
		RedrawWaypointSign(wp);
 

	
 
		if (justremove) {
 
			MakeRailNormal(tile, GetTileOwner(tile), GetRailWaypointBits(tile), GetRailType(tile));
 
			MarkTileDirtyByTile(tile);
 
		} else {
 
			DoClearSquare(tile);
 
			SetSignalsOnBothDir(tile, track);
 
		}
 
		YapfNotifyTrackLayoutChange(tile, track);
 
	}
 

	
 
	return CommandCost(_price.remove_train_depot);
 
}
 

	
 
/**
 
 * Delete a waypoint
 
 * @param tile tile where waypoint is to be deleted
 
 * @param flags type of operation
 
 * @param p1 unused
 
 * @param p2 unused
 
 * @return cost of operation or error
 
 */
 
CommandCost CmdRemoveTrainWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 
	return RemoveTrainWaypoint(tile, flags, true);
 
}
 

	
 
static bool IsUniqueWaypointName(const char *name)
 
{
 
	const Waypoint *wp;
 
	char buf[512];
 

	
 
	FOR_ALL_WAYPOINTS(wp) {
 
		SetDParam(0, wp->index);
 
		GetString(buf, STR_WAYPOINT_RAW, lastof(buf));
 
		if (strcmp(buf, name) == 0) return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Rename a waypoint.
 
 * @param tile unused
 
 * @param flags type of operation
 
 * @param p1 id of waypoint
 
 * @param p2 unused
 
 * @return cost of operation or error
 
 */
 
CommandCost CmdRenameWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Waypoint *wp;
 

	
 
	if (!IsValidWaypointID(p1)) return CMD_ERROR;
 

	
 
	wp = GetWaypoint(p1);
 
	if (!CheckTileOwnership(wp->xy)) return CMD_ERROR;
 

	
 
	if (!StrEmpty(_cmd_text)) {
 
		if (!IsUniqueWaypointName(_cmd_text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
 

	
 
		StringID str = AllocateName(_cmd_text, 0);
 

	
 
		if (str == 0) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) {
 
			if (wp->string != STR_NULL) DeleteName(wp->string);
 

	
 
			wp->string = str;
 
			wp->town_cn = 0;
 

	
 
			UpdateWaypointSign(wp);
 
			MarkWholeScreenDirty();
 
		} else {
 
			DeleteName(str);
 
		}
 
	} else {
 
		if (flags & DC_EXEC) {
 
			if (wp->string != STR_NULL) DeleteName(wp->string);
 

	
 
			MakeDefaultWaypointName(wp);
 
			UpdateWaypointSign(wp);
 
			MarkWholeScreenDirty();
 
		}
 
	}
 
	return CommandCost();
 
}
 

	
 
/**
 
 * This hacks together some dummy one-shot Station structure for a waypoint.
 
 * @param tile on which to work
 
 * @return pointer to a Station
 
 */
 
Station *ComposeWaypointStation(TileIndex tile)
 
{
 
	Waypoint *wp = GetWaypointByTile(tile);
 

	
 
	/* instead of 'static Station stat' use byte array to avoid Station's destructor call upon exit. As
 
	 * a side effect, the station is not constructed now. */
 
	static byte stat_raw[sizeof(Station)];
 
	static Station &stat = *(Station*)stat_raw;
 

	
 
	stat.train_tile = stat.xy = wp->xy;
 
	stat.town = GetTown(wp->town_index);
 
	stat.string_id = wp->string;
 
	stat.build_date = wp->build_date;
 

	
 
	return &stat;
 
}
 

	
 
/**
 
 * Draw a waypoint
 
 * @param x coordinate
 
 * @param y coordinate
 
 * @param stat_id station id
 
 * @param railtype RailType to use for
 
 */
 
void DrawWaypointSprite(int x, int y, int stat_id, RailType railtype)
 
{
 
	x += 33;
 
	y += 17;
 

	
 
	if (!DrawStationTile(x, y, railtype, AXIS_X, STAT_CLASS_WAYP, stat_id)) {
 
		DrawDefaultWaypointSprite(x, y, railtype);
 
	}
 
}
 

	
 
Waypoint::Waypoint(TileIndex tile)
 
{
 
	this->xy = tile;
 
}
 

	
 
Waypoint::~Waypoint()
 
{
 
	if (this->string != STR_NULL) DeleteName(this->string);
 

	
 
	if (CleaningPool()) return;
 

	
 
	RemoveOrderFromAllVehicles(OT_GOTO_WAYPOINT, this->index);
 

	
 
	RedrawWaypointSign(this);
 
	this->xy = 0;
 

	
 
	this->QuickFree();
 
}
 

	
 
void Waypoint::QuickFree()
 
{
 
	if (this->string != STR_NULL) DeleteName(this->string);
 
}
 

	
 
bool Waypoint::IsValid() const
 
{
 
	return this->xy != 0;
 
}
 

	
 

	
 
/**
 
 * Fix savegames which stored waypoints in their old format
 
 */
 
void FixOldWaypoints()
 
{
 
	Waypoint *wp;
 

	
 
	/* Convert the old 'town_or_string', to 'string' / 'town' / 'town_cn' */
 
	FOR_ALL_WAYPOINTS(wp) {
 
		wp->town_index = ClosestTownFromTile(wp->xy, (uint)-1)->index;
 
		wp->town_cn = 0;
 
		if (wp->string & 0xC000) {
 
			wp->town_cn = wp->string & 0x3F;
 
			wp->string = STR_NULL;
 
		}
 
	}
 
}
 

	
 
void InitializeWaypoints()
 
{
 
	_Waypoint_pool.CleanPool();
 
	_Waypoint_pool.AddBlockToPool();
 
}
 

	
 
static const SaveLoad _waypoint_desc[] = {
 
	SLE_CONDVAR(Waypoint, xy,         SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Waypoint, xy,         SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Waypoint, town_index, SLE_UINT16,                 12, SL_MAX_VERSION),
 
	SLE_CONDVAR(Waypoint, town_cn,    SLE_UINT8,                  12, SL_MAX_VERSION),
 
	    SLE_VAR(Waypoint, string,     SLE_UINT16),
 
	    SLE_VAR(Waypoint, deleted,    SLE_UINT8),
 

	
 
	SLE_CONDVAR(Waypoint, build_date, SLE_FILE_U16 | SLE_VAR_I32,  3, 30),
 
	SLE_CONDVAR(Waypoint, build_date, SLE_INT32,                  31, SL_MAX_VERSION),
 
	SLE_CONDVAR(Waypoint, localidx,   SLE_UINT8,                   3, SL_MAX_VERSION),
 
	SLE_CONDVAR(Waypoint, grfid,      SLE_UINT32,                 17, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static void Save_WAYP()
 
{
 
	Waypoint *wp;
 

	
 
	FOR_ALL_WAYPOINTS(wp) {
 
		SlSetArrayIndex(wp->index);
 
		SlObject(wp, _waypoint_desc);
 
	}
 
}
 

	
 
static void Load_WAYP()
 
{
 
	int index;
 

	
 
	while ((index = SlIterateArray()) != -1) {
 
		Waypoint *wp = new (index) Waypoint();
 
		SlObject(wp, _waypoint_desc);
 
	}
 
}
 

	
 
extern const ChunkHandler _waypoint_chunk_handlers[] = {
 
	{ 'CHKP', Save_WAYP, Load_WAYP, CH_ARRAY | CH_LAST},
 
};
src/waypoint.h
Show inline comments
 
/* $Id$ */
 

	
 
/** @file waypoint.h */
 

	
 
#ifndef WAYPOINT_H
 
#define WAYPOINT_H
 

	
 
#include "oldpool.h"
 
#include "rail_map.h"
 

	
 
struct Waypoint;
 
DECLARE_OLD_POOL(Waypoint, Waypoint, 3, 8000)
 

	
 
struct Waypoint : PoolItem<Waypoint, WaypointID, &_Waypoint_pool> {
 
	TileIndex xy;      ///< Tile of waypoint
 

	
 
	TownID town_index; ///< Town associated with the waypoint
 
	byte town_cn;      ///< The Nth waypoint for this town (consecutive number)
 
	StringID string;   ///< If this is zero (i.e. no custom name), town + town_cn is used for naming
 

	
 
	ViewportSign sign; ///< Dimensions of sign (not saved)
 
	Date build_date;   ///< Date of construction
 

	
 
	byte stat_id;      ///< ID of waypoint within the waypoint class (not saved)
 
	uint32 grfid;      ///< ID of GRF file
 
	byte localidx;     ///< Index of station within GRF file
 

	
 
	byte deleted;      ///< Delete counter. If greater than 0 then it is decremented until it reaches 0; the waypoint is then is deleted.
 

	
 
	Waypoint(TileIndex tile = 0);
 
	~Waypoint();
 

	
 
	void QuickFree();
 

	
 
	bool IsValid() const;
 
};
 

	
 
static inline bool IsValidWaypointID(WaypointID index)
 
{
 
	return index < GetWaypointPoolSize() && GetWaypoint(index)->IsValid();
 
}
 

	
 
static inline void DeleteWaypoint(Waypoint *wp)
 
{
 
	wp->~Waypoint();
 
}
 

	
 
#define FOR_ALL_WAYPOINTS_FROM(wp, start) for (wp = GetWaypoint(start); wp != NULL; wp = (wp->index + 1U < GetWaypointPoolSize()) ? GetWaypoint(wp->index + 1U) : NULL) if (wp->IsValid())
 
#define FOR_ALL_WAYPOINTS(wp) FOR_ALL_WAYPOINTS_FROM(wp, 0)
 

	
 

	
 
/**
 
 * Fetch a waypoint by tile
 
 * @param tile Tile of waypoint
 
 * @return Waypoint
 
 */
 
static inline Waypoint *GetWaypointByTile(TileIndex tile)
 
{
 
	assert(IsTileType(tile, MP_RAILWAY) && IsRailWaypoint(tile));
 
	return GetWaypoint(GetWaypointIndex(tile));
 
}
 

	
 
CommandCost RemoveTrainWaypoint(TileIndex tile, uint32 flags, bool justremove);
 
Station *ComposeWaypointStation(TileIndex tile);
 
void ShowRenameWaypointWindow(const Waypoint *cp);
 
void DrawWaypointSprite(int x, int y, int stat_id, RailType railtype);
 
void FixOldWaypoints();
 
void UpdateAllWaypointSigns();
 
void AfterLoadWaypoints();
 

	
 
#endif /* WAYPOINT_H */
0 comments (0 inline, 0 general)