Changeset - r15175:66e0817dc450
[Not reviewed]
master
0 30 0
rubidium - 14 years ago 2010-05-13 10:14:29
rubidium@openttd.org
(svn r19814) -Codechange: give some more unnamed enums a name, in case they consisted of unrelated values use static const (u)int
23 files changed:
0 comments (0 inline, 0 general)
src/ai/ai_instance.cpp
Show inline comments
 
@@ -271,387 +271,385 @@ bool AIInstance::LoadCompatibilityScript
 
		AILog::Error("Failed to load API compatibility script");
 
		DEBUG(ai, 0, "Error compiling / running API compatibility script: %s", buf);
 
		return false;
 
	}
 

	
 
	AILog::Warning("API compatibility script not found");
 
	return true;
 
}
 

	
 
void AIInstance::Continue()
 
{
 
	assert(this->suspend < 0);
 
	this->suspend = -this->suspend - 1;
 
}
 

	
 
void AIInstance::Died()
 
{
 
	DEBUG(ai, 0, "The AI died unexpectedly.");
 
	this->is_dead = true;
 

	
 
	if (this->instance != NULL) this->engine->ReleaseObject(this->instance);
 
	delete this->engine;
 
	this->instance = NULL;
 
	this->engine = NULL;
 

	
 
	ShowAIDebugWindow(_current_company);
 

	
 
	const AIInfo *info = AIConfig::GetConfig(_current_company)->GetInfo();
 
	if (info != NULL) {
 
		ShowErrorMessage(STR_ERROR_AI_PLEASE_REPORT_CRASH, INVALID_STRING_ID, WL_WARNING);
 

	
 
		if (info->GetURL() != NULL) {
 
			AILog::Info("Please report the error to the following URL:");
 
			AILog::Info(info->GetURL());
 
		}
 
	}
 
}
 

	
 
void AIInstance::GameLoop()
 
{
 
	if (this->IsDead()) return;
 
	if (this->engine->HasScriptCrashed()) {
 
		/* The script crashed during saving, kill it here. */
 
		this->Died();
 
		return;
 
	}
 
	this->controller->ticks++;
 

	
 
	if (this->suspend   < -1) this->suspend++; // Multiplayer suspend, increase up to -1.
 
	if (this->suspend   < 0)  return;          // Multiplayer suspend, wait for Continue().
 
	if (--this->suspend > 0)  return;          // Singleplayer suspend, decrease to 0.
 

	
 
	/* If there is a callback to call, call that first */
 
	if (this->callback != NULL) {
 
		if (this->is_save_data_on_stack) {
 
			sq_poptop(this->engine->GetVM());
 
			this->is_save_data_on_stack = false;
 
		}
 
		try {
 
			this->callback(this);
 
		} catch (AI_VMSuspend e) {
 
			this->suspend  = e.GetSuspendTime();
 
			this->callback = e.GetSuspendCallback();
 

	
 
			return;
 
		}
 
	}
 

	
 
	this->suspend  = 0;
 
	this->callback = NULL;
 

	
 
	if (!this->is_started) {
 
		try {
 
			AIObject::SetAllowDoCommand(false);
 
			/* Run the constructor if it exists. Don't allow any DoCommands in it. */
 
			if (this->engine->MethodExists(*this->instance, "constructor")) {
 
				if (!this->engine->CallMethod(*this->instance, "constructor", 100000) || this->engine->IsSuspended()) {
 
					if (this->engine->IsSuspended()) AILog::Error("This AI took too long to initialize. AI is not started.");
 
					this->Died();
 
					return;
 
				}
 
			}
 
			if (!this->CallLoad() || this->engine->IsSuspended()) {
 
				if (this->engine->IsSuspended()) AILog::Error("This AI took too long in the Load function. AI is not started.");
 
				this->Died();
 
				return;
 
			}
 
			AIObject::SetAllowDoCommand(true);
 
			/* Start the AI by calling Start() */
 
			if (!this->engine->CallMethod(*this->instance, "Start",  _settings_game.ai.ai_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
 
		} catch (AI_VMSuspend e) {
 
			this->suspend  = e.GetSuspendTime();
 
			this->callback = e.GetSuspendCallback();
 
		} catch (AI_FatalError e) {
 
			this->is_dead = true;
 
			this->engine->ThrowError(e.GetErrorMessage());
 
			this->engine->ResumeError();
 
			this->Died();
 
		}
 

	
 
		this->is_started = true;
 
		return;
 
	}
 
	if (this->is_save_data_on_stack) {
 
		sq_poptop(this->engine->GetVM());
 
		this->is_save_data_on_stack = false;
 
	}
 

	
 
	/* Continue the VM */
 
	try {
 
		if (!this->engine->Resume(_settings_game.ai.ai_max_opcode_till_suspend)) this->Died();
 
	} catch (AI_VMSuspend e) {
 
		this->suspend  = e.GetSuspendTime();
 
		this->callback = e.GetSuspendCallback();
 
	} catch (AI_FatalError e) {
 
		this->is_dead = true;
 
		this->engine->ThrowError(e.GetErrorMessage());
 
		this->engine->ResumeError();
 
		this->Died();
 
	}
 
}
 

	
 
void AIInstance::CollectGarbage() const
 
{
 
	if (this->is_started && !this->IsDead()) this->engine->CollectGarbage();
 
}
 

	
 
/* static */ void AIInstance::DoCommandReturn(AIInstance *instance)
 
{
 
	instance->engine->InsertResult(AIObject::GetLastCommandRes());
 
}
 

	
 
/* static */ void AIInstance::DoCommandReturnVehicleID(AIInstance *instance)
 
{
 
	instance->engine->InsertResult(AIObject::GetNewVehicleID());
 
}
 

	
 
/* static */ void AIInstance::DoCommandReturnSignID(AIInstance *instance)
 
{
 
	instance->engine->InsertResult(AIObject::GetNewSignID());
 
}
 

	
 
/* static */ void AIInstance::DoCommandReturnGroupID(AIInstance *instance)
 
{
 
	instance->engine->InsertResult(AIObject::GetNewGroupID());
 
}
 

	
 
/* static */ AIStorage *AIInstance::GetStorage()
 
{
 
	assert(Company::IsValidAiID(_current_company));
 
	return Company::Get(_current_company)->ai_instance->storage;
 
}
 

	
 
/*
 
 * All data is stored in the following format:
 
 * First 1 byte indicating if there is a data blob at all.
 
 * 1 byte indicating the type of data.
 
 * The data itself, this differs per type:
 
 *  - integer: a binary representation of the integer (int32).
 
 *  - string:  First one byte with the string length, then a 0-terminated char
 
 *             array. The string can't be longer than 255 bytes (including
 
 *             terminating '\0').
 
 *  - array:   All data-elements of the array are saved recursive in this
 
 *             format, and ended with an element of the type
 
 *             SQSL_ARRAY_TABLE_END.
 
 *  - table:   All key/value pairs are saved in this format (first key 1, then
 
 *             value 1, then key 2, etc.). All keys and values can have an
 
 *             arbitrary type (as long as it is supported by the save function
 
 *             of course). The table is ended with an element of the type
 
 *             SQSL_ARRAY_TABLE_END.
 
 *  - bool:    A single byte with value 1 representing true and 0 false.
 
 *  - null:    No data.
 
 */
 

	
 
/** The type of the data that follows in the savegame. */
 
enum SQSaveLoadType {
 
	SQSL_INT             = 0x00, ///< The following data is an integer.
 
	SQSL_STRING          = 0x01, ///< The following data is an string.
 
	SQSL_ARRAY           = 0x02, ///< The following data is an array.
 
	SQSL_TABLE           = 0x03, ///< The following data is an table.
 
	SQSL_BOOL            = 0x04, ///< The following data is a boolean.
 
	SQSL_NULL            = 0x05, ///< A null variable.
 
	SQSL_ARRAY_TABLE_END = 0xFF, ///< Marks the end of an array or table, no data follows.
 
};
 

	
 
static byte _ai_sl_byte;
 

	
 
static const SaveLoad _ai_byte[] = {
 
	SLEG_VAR(_ai_sl_byte, SLE_UINT8),
 
	SLE_END()
 
};
 

	
 
enum {
 
	AISAVE_MAX_DEPTH = 25, ///< The maximum recursive depth for items stored in the savegame.
 
};
 
static const uint AISAVE_MAX_DEPTH = 25; ///< The maximum recursive depth for items stored in the savegame.
 

	
 
/* static */ bool AIInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
 
{
 
	if (max_depth == 0) {
 
		AILog::Error("Savedata can only be nested to 25 deep. No data saved.");
 
		return false;
 
	}
 

	
 
	switch (sq_gettype(vm, index)) {
 
		case OT_INTEGER: {
 
			if (!test) {
 
				_ai_sl_byte = SQSL_INT;
 
				SlObject(NULL, _ai_byte);
 
			}
 
			SQInteger res;
 
			sq_getinteger(vm, index, &res);
 
			if (!test) {
 
				int value = (int)res;
 
				SlArray(&value, 1, SLE_INT32);
 
			}
 
			return true;
 
		}
 

	
 
		case OT_STRING: {
 
			if (!test) {
 
				_ai_sl_byte = SQSL_STRING;
 
				SlObject(NULL, _ai_byte);
 
			}
 
			const SQChar *res;
 
			sq_getstring(vm, index, &res);
 
			/* @bug if a string longer than 512 characters is given to SQ2OTTD, the
 
			 *  internal buffer overflows. */
 
			const char *buf = SQ2OTTD(res);
 
			size_t len = strlen(buf) + 1;
 
			if (len >= 255) {
 
				AILog::Error("Maximum string length is 254 chars. No data saved.");
 
				return false;
 
			}
 
			if (!test) {
 
				_ai_sl_byte = (byte)len;
 
				SlObject(NULL, _ai_byte);
 
				SlArray((void*)buf, len, SLE_CHAR);
 
			}
 
			return true;
 
		}
 

	
 
		case OT_ARRAY: {
 
			if (!test) {
 
				_ai_sl_byte = SQSL_ARRAY;
 
				SlObject(NULL, _ai_byte);
 
			}
 
			sq_pushnull(vm);
 
			while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
 
				/* Store the value */
 
				bool res = SaveObject(vm, -1, max_depth - 1, test);
 
				sq_pop(vm, 2);
 
				if (!res) {
 
					sq_pop(vm, 1);
 
					return false;
 
				}
 
			}
 
			sq_pop(vm, 1);
 
			if (!test) {
 
				_ai_sl_byte = SQSL_ARRAY_TABLE_END;
 
				SlObject(NULL, _ai_byte);
 
			}
 
			return true;
 
		}
 

	
 
		case OT_TABLE: {
 
			if (!test) {
 
				_ai_sl_byte = SQSL_TABLE;
 
				SlObject(NULL, _ai_byte);
 
			}
 
			sq_pushnull(vm);
 
			while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
 
				/* Store the key + value */
 
				bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test);
 
				sq_pop(vm, 2);
 
				if (!res) {
 
					sq_pop(vm, 1);
 
					return false;
 
				}
 
			}
 
			sq_pop(vm, 1);
 
			if (!test) {
 
				_ai_sl_byte = SQSL_ARRAY_TABLE_END;
 
				SlObject(NULL, _ai_byte);
 
			}
 
			return true;
 
		}
 

	
 
		case OT_BOOL: {
 
			if (!test) {
 
				_ai_sl_byte = SQSL_BOOL;
 
				SlObject(NULL, _ai_byte);
 
			}
 
			SQBool res;
 
			sq_getbool(vm, index, &res);
 
			if (!test) {
 
				_ai_sl_byte = res ? 1 : 0;
 
				SlObject(NULL, _ai_byte);
 
			}
 
			return true;
 
		}
 

	
 
		case OT_NULL: {
 
			if (!test) {
 
				_ai_sl_byte = SQSL_NULL;
 
				SlObject(NULL, _ai_byte);
 
			}
 
			return true;
 
		}
 

	
 
		default:
 
			AILog::Error("You tried to save an unsupported type. No data saved.");
 
			return false;
 
	}
 
}
 

	
 
/* static */ void AIInstance::SaveEmpty()
 
{
 
	_ai_sl_byte = 0;
 
	SlObject(NULL, _ai_byte);
 
}
 

	
 
void AIInstance::Save()
 
{
 
	/* Don't save data if the AI didn't start yet or if it crashed. */
 
	if (this->engine == NULL || this->engine->HasScriptCrashed()) {
 
		SaveEmpty();
 
		return;
 
	}
 

	
 
	HSQUIRRELVM vm = this->engine->GetVM();
 
	if (this->is_save_data_on_stack) {
 
		_ai_sl_byte = 1;
 
		SlObject(NULL, _ai_byte);
 
		/* Save the data that was just loaded. */
 
		SaveObject(vm, -1, AISAVE_MAX_DEPTH, false);
 
	} else if (!this->is_started) {
 
		SaveEmpty();
 
		return;
 
	} else if (this->engine->MethodExists(*this->instance, "Save")) {
 
		HSQOBJECT savedata;
 
		/* We don't want to be interrupted during the save function. */
 
		bool backup_allow = AIObject::GetAllowDoCommand();
 
		AIObject::SetAllowDoCommand(false);
 
		try {
 
			if (!this->engine->CallMethod(*this->instance, "Save", &savedata)) {
 
				/* The script crashed in the Save function. We can't kill
 
				 * it here, but do so in the next AI tick. */
 
				SaveEmpty();
 
				this->engine->CrashOccurred();
 
				return;
 
			}
 
		} catch (AI_FatalError e) {
 
			/* If we don't mark the AI as dead here cleaning up the squirrel
 
			 * stack could throw AI_FatalError again. */
 
			this->is_dead = true;
 
			this->engine->ThrowError(e.GetErrorMessage());
 
			this->engine->ResumeError();
 
			SaveEmpty();
 
			/* We can't kill the AI here, so mark it as crashed (not dead) and
 
			 * kill it in the next AI tick. */
 
			this->is_dead = false;
 
			this->engine->CrashOccurred();
 
			return;
 
		}
 
		AIObject::SetAllowDoCommand(backup_allow);
 

	
 
		if (!sq_istable(savedata)) {
 
			AILog::Error("Save function should return a table.");
 
			SaveEmpty();
 
			this->engine->CrashOccurred();
 
			return;
 
		}
 
		sq_pushobject(vm, savedata);
 
		if (SaveObject(vm, -1, AISAVE_MAX_DEPTH, true)) {
 
			_ai_sl_byte = 1;
 
			SlObject(NULL, _ai_byte);
 
			SaveObject(vm, -1, AISAVE_MAX_DEPTH, false);
 
			this->is_save_data_on_stack = true;
 
		} else {
 
			SaveEmpty();
 
			this->engine->CrashOccurred();
 
		}
 
	} else {
 
		AILog::Warning("Save function is not implemented");
 
		_ai_sl_byte = 0;
 
		SlObject(NULL, _ai_byte);
 
	}
src/airport.h
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file airport.h Various declarations for airports */
 

	
 
#ifndef AIRPORT_H
 
#define AIRPORT_H
 

	
 
#include "direction_type.h"
 
#include "tile_type.h"
 

	
 
/** Some airport-related constants */
 
enum {
 
	MAX_TERMINALS =  10,                    ///< maximum number of terminals per airport
 
	MAX_HELIPADS  =   4,                    ///< maximum number of helipads per airport
 
	MAX_ELEMENTS  = 255,                    ///< maximum number of aircraft positions at airport
 
	NUM_AIRPORTTILES = 256,                 ///< total number of airport tiles
 
	NEW_AIRPORTTILE_OFFSET = 74,            ///< offset of first newgrf airport tile
 
	INVALID_AIRPORTTILE = NUM_AIRPORTTILES, ///< id for an invalid airport tile
 
};
 
static const uint MAX_TERMINALS =  10;                       ///< maximum number of terminals per airport
 
static const uint MAX_HELIPADS  =   4;                       ///< maximum number of helipads per airport
 
static const uint MAX_ELEMENTS  = 255;                       ///< maximum number of aircraft positions at airport
 

	
 
static const uint NUM_AIRPORTTILES       = 256;              ///< total number of airport tiles
 
static const uint NEW_AIRPORTTILE_OFFSET = 74;               ///< offset of first newgrf airport tile
 
static const uint INVALID_AIRPORTTILE    = NUM_AIRPORTTILES; ///< id for an invalid airport tile
 

	
 
/** Airport types */
 
enum AirportTypes {
 
	AT_SMALL         =   0,
 
	AT_LARGE         =   1,
 
	AT_HELIPORT      =   2,
 
	AT_METROPOLITAN  =   3,
 
	AT_INTERNATIONAL =   4,
 
	AT_COMMUTER      =   5,
 
	AT_HELIDEPOT     =   6,
 
	AT_INTERCON      =   7,
 
	AT_HELISTATION   =   8,
 
	AT_OILRIG        =   9,
 
	NEW_AIRPORT_OFFSET = 10,
 
	NUM_AIRPORTS     =  128,
 
	AT_INVALID       = 254,
 
	AT_DUMMY         = 255
 
};
 

	
 
enum AirportMovingDataFlags {
 
	AMED_NOSPDCLAMP = 1 << 0,
 
	AMED_TAKEOFF    = 1 << 1,
 
	AMED_SLOWTURN   = 1 << 2,
 
	AMED_LAND       = 1 << 3,
 
	AMED_EXACTPOS   = 1 << 4,
 
	AMED_BRAKE      = 1 << 5,
 
	AMED_HELI_RAISE = 1 << 6,
 
	AMED_HELI_LOWER = 1 << 7,
 
	AMED_HOLD       = 1 << 8
 
};
 

	
 
/* Movement States on Airports (headings target) */
 
enum AirportMovementStates {
 
	TO_ALL         =  0,
 
	HANGAR         =  1,
 
	TERM1          =  2,
 
	TERM2          =  3,
 
	TERM3          =  4,
 
	TERM4          =  5,
 
	TERM5          =  6,
 
	TERM6          =  7,
 
	HELIPAD1       =  8,
 
	HELIPAD2       =  9,
 
	TAKEOFF        = 10,
 
	STARTTAKEOFF   = 11,
 
	ENDTAKEOFF     = 12,
 
	HELITAKEOFF    = 13,
 
	FLYING         = 14,
 
	LANDING        = 15,
 
	ENDLANDING     = 16,
 
	HELILANDING    = 17,
 
	HELIENDLANDING = 18,
 
	TERM7          = 19,
 
	TERM8          = 20,
 
	HELIPAD3       = 21,
 
	HELIPAD4       = 22,
 
	MAX_HEADINGS   = 22,
 
};
 

	
 
/* Movement Blocks on Airports
 
 * blocks (eg_airport_flags) */
 
static const uint64
 
	TERM1_block              = 1ULL <<  0,
 
	TERM2_block              = 1ULL <<  1,
 
	TERM3_block              = 1ULL <<  2,
 
	TERM4_block              = 1ULL <<  3,
 
	TERM5_block              = 1ULL <<  4,
 
	TERM6_block              = 1ULL <<  5,
 
	HELIPAD1_block           = 1ULL <<  6,
 
	HELIPAD2_block           = 1ULL <<  7,
 
	RUNWAY_IN_OUT_block      = 1ULL <<  8,
 
	RUNWAY_IN_block          = 1ULL <<  8,
 
	AIRPORT_BUSY_block       = 1ULL <<  8,
 
	RUNWAY_OUT_block         = 1ULL <<  9,
 
	TAXIWAY_BUSY_block       = 1ULL << 10,
 
	OUT_WAY_block            = 1ULL << 11,
 
	IN_WAY_block             = 1ULL << 12,
 
	AIRPORT_ENTRANCE_block   = 1ULL << 13,
 
	TERM_GROUP1_block        = 1ULL << 14,
 
	TERM_GROUP2_block        = 1ULL << 15,
 
	HANGAR2_AREA_block       = 1ULL << 16,
 
	TERM_GROUP2_ENTER1_block = 1ULL << 17,
 
	TERM_GROUP2_ENTER2_block = 1ULL << 18,
 
	TERM_GROUP2_EXIT1_block  = 1ULL << 19,
 
	TERM_GROUP2_EXIT2_block  = 1ULL << 20,
 
	PRE_HELIPAD_block        = 1ULL << 21,
 

	
 
	/* blocks for new airports */
 
	TERM7_block              = 1ULL << 22,
 
	TERM8_block              = 1ULL << 23,
 
	TERM9_block              = 1ULL << 24,
 
	HELIPAD3_block           = 1ULL << 24,
 
	TERM10_block             = 1ULL << 25,
 
	HELIPAD4_block           = 1ULL << 25,
 
	HANGAR1_AREA_block       = 1ULL << 26,
 
	OUT_WAY2_block           = 1ULL << 27,
 
	IN_WAY2_block            = 1ULL << 28,
 
	RUNWAY_IN2_block         = 1ULL << 29,
 
	RUNWAY_OUT2_block        = 1ULL << 10,   ///< note re-uses TAXIWAY_BUSY
 
	HELIPAD_GROUP_block      = 1ULL << 13,   ///< note re-uses AIRPORT_ENTRANCE
 
	OUT_WAY_block2           = 1ULL << 31,
 
	/* end of new blocks */
 

	
 
	NOTHING_block            = 1ULL << 30;
 

	
 
struct AirportMovingData {
 
	int16 x;
 
	int16 y;
 
	uint16 flag;
 
	DirectionByte direction;
 
};
 

	
 
struct AirportFTAbuildup;
 

	
 
/** Finite sTate mAchine --> FTA */
 
struct AirportFTAClass {
 
public:
 
	enum Flags {
 
		AIRPLANES   = 0x1,
 
		HELICOPTERS = 0x2,
 
		ALL         = AIRPLANES | HELICOPTERS,
 
		SHORT_STRIP = 0x4
 
	};
 

	
 
	AirportFTAClass(
 
		const AirportMovingData *moving_data,
 
		const byte *terminals,
 
		const byte *helipads,
 
		const byte *entry_points,
 
		Flags flags,
 
		const AirportFTAbuildup *apFA,
 
		byte delta_z
 
	);
 

	
 
	~AirportFTAClass();
 

	
 
	const AirportMovingData *MovingData(byte position) const
 
	{
 
		assert(position < nofelements);
 
		return &moving_data[position];
 
	}
 

	
 
	const AirportMovingData *moving_data;
 
	struct AirportFTA *layout;            ///< state machine for airport
 
	const byte *terminals;
 
	const byte *helipads;
 
	Flags flags;
 
	byte nofelements;                     ///< number of positions the airport consists of
 
	const byte *entry_points;             ///< when an airplane arrives at this airport, enter it at position entry_point, index depends on direction
 
	byte delta_z;                         ///< Z adjustment for helicopter pads
 
};
 

	
 
DECLARE_ENUM_AS_BIT_SET(AirportFTAClass::Flags)
 

	
 

	
 
/** Internal structure used in openttd - Finite sTate mAchine --> FTA */
 
struct AirportFTA {
 
	AirportFTA *next;        ///< possible extra movement choices from this position
 
	uint64 block;            ///< 64 bit blocks (st->airport.flags), should be enough for the most complex airports
 
	byte position;           ///< the position that an airplane is at
 
	byte next_position;      ///< next position from this position
 
	byte heading;            ///< heading (current orders), guiding an airplane to its target on an airport
 
};
 

	
 
const AirportFTAClass *GetAirport(const byte airport_type);
 
byte GetVehiclePosOnBuild(TileIndex hangar_tile);
 

	
 
#endif /* AIRPORT_H */
src/airport_gui.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file airport_gui.cpp The GUI for airports. */
 

	
 
#include "stdafx.h"
 
#include "window_gui.h"
 
#include "station_gui.h"
 
#include "terraform_gui.h"
 
#include "airport.h"
 
#include "sound_func.h"
 
#include "window_func.h"
 
#include "strings_func.h"
 
#include "viewport_func.h"
 
#include "gfx_func.h"
 
#include "company_func.h"
 
#include "tilehighlight_func.h"
 
#include "company_base.h"
 
#include "station_type.h"
 
#include "newgrf_airport.h"
 
#include "widgets/dropdown_type.h"
 
#include "core/geometry_func.hpp"
 

	
 
#include "table/sprites.h"
 
#include "table/strings.h"
 

	
 
static AirportClassID _selected_airport_class; ///< the currently visible airport class
 
static int _selected_airport_index;            ///< the index of the selected airport in the current class or -1
 

	
 
static void ShowBuildAirportPicker(Window *parent);
 

	
 

	
 
void CcBuildAirport(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (result.Failed()) return;
 

	
 
	SndPlayTileFx(SND_1F_SPLAT, tile);
 
	if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
 
}
 

	
 
static void PlaceAirport(TileIndex tile)
 
{
 
	if (_selected_airport_index == -1) return;
 
	uint32 p2 = _ctrl_pressed;
 
	SB(p2, 16, 16, INVALID_STATION); // no station to join
 

	
 
	uint32 p1 = GetAirportSpecFromClass(_selected_airport_class, _selected_airport_index)->GetIndex();
 
	CommandContainer cmdcont = { tile, p1, p2, CMD_BUILD_AIRPORT | CMD_MSG(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE), CcBuildAirport, "" };
 
	ShowSelectStationIfNeeded(cmdcont, TileArea(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE));
 
}
 

	
 
/** Widget number of the airport build window. */
 
enum {
 
enum AirportToolbarWidgets {
 
	ATW_AIRPORT,
 
	ATW_DEMOLISH,
 
};
 

	
 

	
 
static void BuildAirClick_Airport(Window *w)
 
{
 
	if (HandlePlacePushButton(w, ATW_AIRPORT, SPR_CURSOR_AIRPORT, HT_RECT, PlaceAirport)) ShowBuildAirportPicker(w);
 
}
 

	
 
static void BuildAirClick_Demolish(Window *w)
 
{
 
	HandlePlacePushButton(w, ATW_DEMOLISH, ANIMCURSOR_DEMOLISH, HT_RECT, PlaceProc_DemolishArea);
 
}
 

	
 

	
 
typedef void OnButtonClick(Window *w);
 
static OnButtonClick * const _build_air_button_proc[] = {
 
	BuildAirClick_Airport,
 
	BuildAirClick_Demolish,
 
};
 

	
 
struct BuildAirToolbarWindow : Window {
 
	BuildAirToolbarWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
 
	{
 
		this->InitNested(desc, window_number);
 
		if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this);
 
	}
 

	
 
	~BuildAirToolbarWindow()
 
	{
 
		if (_settings_client.gui.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0, false);
 
	}
 

	
 
	virtual void OnPaint()
 
	{
 
		this->DrawWidgets();
 
	}
 

	
 
	virtual void OnClick(Point pt, int widget, int click_count)
 
	{
 
		if (!IsInsideBS(widget, ATW_AIRPORT, lengthof(_build_air_button_proc))) return;
 

	
 
		_build_air_button_proc[widget - ATW_AIRPORT](this);
 
	}
 

	
 

	
 
	virtual EventState OnKeyPress(uint16 key, uint16 keycode)
 
	{
 
		switch (keycode) {
 
			case '1': BuildAirClick_Airport(this); break;
 
			case '2': BuildAirClick_Demolish(this); break;
 
			default: return ES_NOT_HANDLED;
 
		}
 
		return ES_HANDLED;
 
	}
 

	
 
	virtual void OnPlaceObject(Point pt, TileIndex tile)
 
	{
 
		_place_proc(tile);
 
	}
 

	
 
	virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt)
 
	{
 
		VpSelectTilesWithMethod(pt.x, pt.y, select_method);
 
	}
 

	
 
	virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile)
 
	{
 
		if (pt.x != -1 && select_proc == DDSP_DEMOLISH_AREA) {
 
			GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
 
		}
 
	}
 

	
 
	virtual void OnPlaceObjectAbort()
 
	{
 
		this->RaiseButtons();
 

	
 
		DeleteWindowById(WC_BUILD_STATION, TRANSPORT_AIR);
 
		DeleteWindowById(WC_SELECT_STATION, 0);
 
	}
 
};
 

	
 
static const NWidgetPart _nested_air_toolbar_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
 
		NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_TOOLBAR_AIRCRAFT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
 
		NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
 
	EndContainer(),
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, ATW_AIRPORT), SetFill(0, 1), SetMinimalSize(42, 22), SetDataTip(SPR_IMG_AIRPORT, STR_TOOLBAR_AIRCRAFT_BUILD_AIRPORT_TOOLTIP),
 
		NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(4, 22), SetFill(1, 1), EndContainer(),
 
		NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, ATW_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
 
	EndContainer(),
 
};
 

	
 
static const WindowDesc _air_toolbar_desc(
 
	WDP_ALIGN_TOOLBAR, 0, 0,
 
	WC_BUILD_TOOLBAR, WC_NONE,
 
	WDF_CONSTRUCTION,
 
	_nested_air_toolbar_widgets, lengthof(_nested_air_toolbar_widgets)
 
);
 

	
 
void ShowBuildAirToolbar()
 
{
 
	if (!Company::IsValidID(_local_company)) return;
 

	
 
	DeleteWindowByClass(WC_BUILD_TOOLBAR);
 
	AllocateWindowDescFront<BuildAirToolbarWindow>(&_air_toolbar_desc, TRANSPORT_AIR);
 
}
 

	
 
/** Airport widgets in the airport picker window. */
 
enum AirportPickerWidgets {
 
	BAIRW_CLASS_DROPDOWN,
 
	BAIRW_AIRPORT_LIST,
 
	BAIRW_SCROLLBAR,
 
	BAIRW_BOTTOMPANEL,
 
	BAIRW_COVERAGE_LABEL,
 
	BAIRW_BTN_DONTHILIGHT,
 
	BAIRW_BTN_DOHILIGHT,
 
};
 

	
 
class BuildAirportWindow : public PickerWindowBase {
 
	int line_height;
 

	
 
	/** Build a dropdown list of available airport classes */
 
	static DropDownList *BuildAirportClassDropDown()
 
	{
 
		DropDownList *list = new DropDownList();
 

	
 
		for (uint i = 0; i < GetNumAirportClasses(); i++) {
 
			list->push_back(new DropDownListStringItem(GetAirportClassName((AirportClassID)i), i, false));
 
		}
 

	
 
		return list;
 
	}
 

	
 
public:
 
	BuildAirportWindow(const WindowDesc *desc, Window *parent) : PickerWindowBase(parent)
 
	{
 
		this->vscroll.SetCapacity(5);
 
		this->vscroll.SetPosition(0);
 
		this->InitNested(desc, TRANSPORT_AIR);
 

	
 
		this->SetWidgetLoweredState(BAIRW_BTN_DONTHILIGHT, !_settings_client.gui.station_show_coverage);
 
		this->SetWidgetLoweredState(BAIRW_BTN_DOHILIGHT, _settings_client.gui.station_show_coverage);
 
		this->OnInvalidateData();
 

	
 
		this->vscroll.SetCount(GetNumAirportsInClass(_selected_airport_class));
 
		this->SelectFirstAvailableAirport(true);
 
	}
 

	
 
	virtual ~BuildAirportWindow()
 
	{
 
		DeleteWindowById(WC_SELECT_STATION, 0);
 
	}
 

	
 
	virtual void SetStringParameters(int widget) const
 
	{
 
		if (widget != BAIRW_CLASS_DROPDOWN) return;
 

	
 
		SetDParam(0, GetAirportClassName(_selected_airport_class));
 
	}
 

	
 
	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
 
	{
 
		switch (widget) {
 
			case BAIRW_CLASS_DROPDOWN: {
 
				Dimension d = {0, 0};
 
				for (uint i = 0; i < GetNumAirportClasses(); i++) {
 
					SetDParam(0, GetAirportClassName((AirportClassID)i));
 
					d = maxdim(d, GetStringBoundingBox(STR_BLACK_STRING));
 
				}
 
				d.width += padding.width;
 
				d.height += padding.height;
 
				*size = maxdim(*size, d);
 
			} break;
 

	
 
			case BAIRW_AIRPORT_LIST: {
 
				for (int i = 0; i < NUM_AIRPORTS; i++) {
 
					const AirportSpec *as = AirportSpec::Get(i);
 
					if (!as->enabled) continue;
 

	
 
					size->width = max(size->width, GetStringBoundingBox(as->name).width);
 
				}
 

	
 
				this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
 
				size->height = this->vscroll.GetCapacity() * this->line_height;
 
			} break;
 

	
 
			default: break;
 
		}
src/company_gui.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file company_gui.cpp Company related GUIs. */
 

	
 
#include "stdafx.h"
 
#include "gui.h"
 
#include "window_gui.h"
 
#include "textbuf_gui.h"
 
#include "viewport_func.h"
 
#include "company_func.h"
 
#include "command_func.h"
 
#include "network/network.h"
 
#include "network/network_gui.h"
 
#include "network/network_func.h"
 
#include "economy_func.h"
 
#include "vehicle_base.h"
 
#include "newgrf.h"
 
#include "company_manager_face.h"
 
#include "strings_func.h"
 
#include "date_func.h"
 
#include "widgets/dropdown_type.h"
 
#include "tilehighlight_func.h"
 
#include "sprite.h"
 
#include "company_base.h"
 
#include "core/geometry_func.hpp"
 

	
 
#include "table/strings.h"
 

	
 
/** Company GUI constants. */
 
enum {
 
	FIRST_GUI_CALL = INT_MAX,  ///< default value to specify this is the first call of the resizable gui
 
static const int FIRST_GUI_CALL = INT_MAX; ///< default value to specify this is the first call of the resizable gui
 

	
 
	EXP_LINESPACE  = 2,        ///< Amount of vertical space for a horizontal (sub-)total line.
 
	EXP_BLOCKSPACE = 10,       ///< Amount of vertical space between two blocks of numbers.
 
};
 
static const uint EXP_LINESPACE  = 2;      ///< Amount of vertical space for a horizontal (sub-)total line.
 
static const uint EXP_BLOCKSPACE = 10;     ///< Amount of vertical space between two blocks of numbers.
 

	
 
static void DoSelectCompanyManagerFace(Window *parent);
 

	
 
/** Standard unsorted list of expenses. */
 
static ExpensesType _expenses_list_1[] = {
 
	EXPENSES_CONSTRUCTION,
 
	EXPENSES_NEW_VEHICLES,
 
	EXPENSES_TRAIN_RUN,
 
	EXPENSES_ROADVEH_RUN,
 
	EXPENSES_AIRCRAFT_RUN,
 
	EXPENSES_SHIP_RUN,
 
	EXPENSES_PROPERTY,
 
	EXPENSES_TRAIN_INC,
 
	EXPENSES_ROADVEH_INC,
 
	EXPENSES_AIRCRAFT_INC,
 
	EXPENSES_SHIP_INC,
 
	EXPENSES_LOAN_INT,
 
	EXPENSES_OTHER,
 
};
 

	
 
/** Grouped list of expenses. */
 
static ExpensesType _expenses_list_2[] = {
 
	EXPENSES_TRAIN_INC,
 
	EXPENSES_ROADVEH_INC,
 
	EXPENSES_AIRCRAFT_INC,
 
	EXPENSES_SHIP_INC,
 
	INVALID_EXPENSES,
 
	EXPENSES_TRAIN_RUN,
 
	EXPENSES_ROADVEH_RUN,
 
	EXPENSES_AIRCRAFT_RUN,
 
	EXPENSES_SHIP_RUN,
 
	EXPENSES_PROPERTY,
 
	EXPENSES_LOAN_INT,
 
	INVALID_EXPENSES,
 
	EXPENSES_CONSTRUCTION,
 
	EXPENSES_NEW_VEHICLES,
 
	EXPENSES_OTHER,
 
	INVALID_EXPENSES,
 
};
 

	
 
/** Expense list container. */
 
struct ExpensesList {
 
	const ExpensesType *et;   ///< Expenses items.
 
	const uint length;        ///< Number of items in list.
 
	const uint num_subtotals; ///< Number of sub-totals in the list.
 

	
 
	ExpensesList(ExpensesType *et, int length, int num_subtotals) : et(et), length(length), num_subtotals(num_subtotals)
 
	{
 
	}
 

	
 
	uint GetHeight() const
 
	{
 
		/* heading + line + texts of expenses + sub-totals + total line + total text */
 
		return FONT_HEIGHT_NORMAL + EXP_LINESPACE + this->length * FONT_HEIGHT_NORMAL + num_subtotals * (EXP_BLOCKSPACE + EXP_LINESPACE) + EXP_LINESPACE + FONT_HEIGHT_NORMAL;
 
	}
 

	
 
	/** Compute width of the expenses categories in pixels. */
 
	uint GetCategoriesWidth() const
 
	{
 
		uint width = 0;
 
		bool invalid_expenses_measured = false; // Measure 'Total' width only once.
 
		for (uint i = 0; i < this->length; i++) {
 
			ExpensesType et = this->et[i];
 
			if (et == INVALID_EXPENSES) {
 
				if (!invalid_expenses_measured) {
 
					width = max(width, GetStringBoundingBox(STR_FINANCES_TOTAL_CAPTION).width);
 
					invalid_expenses_measured = true;
 
				}
 
			} else {
 
				width = max(width, GetStringBoundingBox(STR_FINANCES_SECTION_CONSTRUCTION + et).width);
 
			}
 
		}
 
		return width;
 
	}
 
};
 

	
 
static const ExpensesList _expenses_list_types[] = {
 
	ExpensesList(_expenses_list_1, lengthof(_expenses_list_1), 0),
 
	ExpensesList(_expenses_list_2, lengthof(_expenses_list_2), 3),
 
};
 

	
 
/** Widgets of the company finances windows. */
 
enum CompanyFinancesWindowWidgets {
 
	CFW_CAPTION,       ///< Caption of the window
 
	CFW_TOGGLE_SIZE,   ///< Toggle windows size
 
	CFW_SEL_PANEL,     ///< Select panel or nothing
 
	CFW_EXPS_CATEGORY, ///< Column for expenses category strings
 
	CFW_EXPS_PRICE1,   ///< Column for year Y-2 expenses
 
	CFW_EXPS_PRICE2,   ///< Column for year Y-1 expenses
 
	CFW_EXPS_PRICE3,   ///< Column for year Y expenses
 
	CFW_TOTAL_PANEL,   ///< Panel for totals
 
	CFW_SEL_MAXLOAN,   ///< Selection of maxloan column
 
	CFW_BALANCE_VALUE, ///< Bank balance value
 
	CFW_LOAN_VALUE,    ///< Loan
 
	CFW_LOAN_LINE,     ///< Line for summing bank balance and loan
 
	CFW_TOTAL_VALUE,   ///< Total
 
	CFW_MAXLOAN_GAP,   ///< Gap above max loan widget
 
	CFW_MAXLOAN_VALUE, ///< Max loan widget
 
	CFW_SEL_BUTTONS,   ///< Selection of buttons
 
	CFW_INCREASE_LOAN, ///< Increase loan
 
	CFW_REPAY_LOAN,    ///< Decrease loan
 
};
 

	
 
/** Draw the expenses categories.
 
 * @param r Available space for drawing.
 
 * @note The environment must provide padding at the left and right of \a r.
 
 */
 
static void DrawCategories(const Rect &r)
 
{
 
	int y = r.top;
 

	
 
	DrawString(r.left, r.right, y, STR_FINANCES_EXPENDITURE_INCOME_TITLE, TC_FROMSTRING, SA_CENTER, true);
 
	y += FONT_HEIGHT_NORMAL + EXP_LINESPACE;
 

	
 
	int type = _settings_client.gui.expenses_layout;
 
	for (uint i = 0; i < _expenses_list_types[type].length; i++) {
 
		const ExpensesType et = _expenses_list_types[type].et[i];
 
		if (et == INVALID_EXPENSES) {
 
			y += EXP_LINESPACE;
 
			DrawString(r.left, r.right, y, STR_FINANCES_TOTAL_CAPTION, TC_FROMSTRING, SA_RIGHT);
 
			y += FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
 
		} else {
 
			DrawString(r.left, r.right, y, STR_FINANCES_SECTION_CONSTRUCTION + et);
 
			y += FONT_HEIGHT_NORMAL;
 
		}
 
	}
 

	
 
	DrawString(r.left, r.right, y + EXP_LINESPACE, STR_FINANCES_TOTAL_CAPTION, TC_FROMSTRING, SA_RIGHT);
 
}
 

	
 
/** Draw an amount of money.
 
 * @param amount Amount of money to draw,
 
 * @param left   Left coordinate of the space to draw in.
 
 * @param right  Right coordinate of the space to draw in.
 
 * @param top    Top coordinate of the space to draw in.
 
 */
 
static void DrawPrice(Money amount, int left, int right, int top)
 
{
 
	StringID str = STR_FINANCES_NEGATIVE_INCOME;
 
	if (amount < 0) {
 
		amount = -amount;
 
		str++;
 
	}
 
	SetDParam(0, amount);
 
	DrawString(left, right, top, str, TC_FROMSTRING, SA_RIGHT);
 
}
 

	
 
/** Draw a column with prices.
 
 * @param r    Available space for drawing.
 
 * @param year Year being drawn.
 
 * @param tbl  Pointer to table of amounts for \a year.
 
 * @note The environment must provide padding at the left and right of \a r.
 
 */
 
static void DrawYearColumn(const Rect &r, int year, const Money (*tbl)[EXPENSES_END])
 
{
 
	int y = r.top;
 

	
 
	SetDParam(0, year);
 
	DrawString(r.left, r.right, y, STR_FINANCES_YEAR, TC_FROMSTRING, SA_RIGHT, true);
 
	y += FONT_HEIGHT_NORMAL + EXP_LINESPACE;
 

	
 
	Money sum = 0;
 
	Money subtotal = 0;
 
	int type = _settings_client.gui.expenses_layout;
 
	for (uint i = 0; i < _expenses_list_types[type].length; i++) {
 
		const ExpensesType et = _expenses_list_types[type].et[i];
 
		if (et == INVALID_EXPENSES) {
 
			Money cost = subtotal;
 
			subtotal = 0;
 
			GfxFillRect(r.left, y, r.right, y, 215);
 
			y += EXP_LINESPACE;
 
			DrawPrice(cost, r.left, r.right, y);
 
			y += FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
 
		} else {
 
			Money cost = (*tbl)[et];
 
			subtotal += cost;
 
			sum += cost;
 
			if (cost != 0) DrawPrice(cost, r.left, r.right, y);
 
			y += FONT_HEIGHT_NORMAL;
 
		}
 
	}
 

	
 
	GfxFillRect(r.left, y, r.right, y, 215);
 
	y += EXP_LINESPACE;
 
	DrawPrice(sum, r.left, r.right, y);
 
}
 

	
 
static const NWidgetPart _nested_company_finances_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_GREY),
 
		NWidget(WWT_CAPTION, COLOUR_GREY, CFW_CAPTION), SetDataTip(STR_FINANCES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
 
		NWidget(WWT_IMGBTN, COLOUR_GREY, CFW_TOGGLE_SIZE), SetDataTip(SPR_LARGE_SMALL_WINDOW, STR_TOOLTIP_TOGGLE_LARGE_SMALL_WINDOW),
src/console_gui.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file console_gui.cpp Handling the GUI of the in-game console. */
 

	
 
#include "stdafx.h"
 
#include "textbuf_gui.h"
 
#include "window_gui.h"
 
#include "console_gui.h"
 
#include "console_internal.h"
 
#include "window_func.h"
 
#include "string_func.h"
 
#include "gfx_func.h"
 
#include "settings_type.h"
 
#include "console_func.h"
 
#include "rev.h"
 

	
 

	
 
enum {
 
	ICON_HISTORY_SIZE       = 20,
 
	ICON_LINE_SPACING       =  2,
 
	ICON_RIGHT_BORDERWIDTH  = 10,
 
	ICON_BOTTOM_BORDERWIDTH = 12,
 
};
 
static const uint ICON_HISTORY_SIZE       = 20;
 
static const uint ICON_LINE_SPACING       =  2;
 
static const uint ICON_RIGHT_BORDERWIDTH  = 10;
 
static const uint ICON_BOTTOM_BORDERWIDTH = 12;
 

	
 
/**
 
 * Container for a single line of console output
 
 */
 
struct IConsoleLine {
 
	static IConsoleLine *front; ///< The front of the console backlog buffer
 
	static int size;            ///< The amount of items in the backlog
 

	
 
	IConsoleLine *previous; ///< The previous console message.
 
	char *buffer;           ///< The data to store.
 
	TextColour colour;      ///< The colour of the line.
 
	uint16 time;            ///< The amount of time the line is in the backlog.
 

	
 
	/**
 
	 * Initialize the console line.
 
	 * @param buffer the data to print.
 
	 * @param colour the colour of the line.
 
	 */
 
	IConsoleLine(char *buffer, TextColour colour) :
 
			previous(IConsoleLine::front),
 
			buffer(buffer),
 
			colour(colour),
 
			time(0)
 
	{
 
		IConsoleLine::front = this;
 
		IConsoleLine::size++;
 
	}
 

	
 
	/**
 
	 * Clear this console line and any further ones.
 
	 */
 
	~IConsoleLine()
 
	{
 
		IConsoleLine::size--;
 
		free(buffer);
 

	
 
		delete previous;
 
	}
 

	
 
	/**
 
	 * Get the index-ed item in the list.
 
	 */
 
	static const IConsoleLine *Get(uint index)
 
	{
 
		const IConsoleLine *item = IConsoleLine::front;
 
		while (index != 0 && item != NULL) {
 
			index--;
 
			item = item->previous;
 
		}
 

	
 
		return item;
 
	}
 

	
 
	/**
 
	 * Truncate the list removing everything older than/more than the amount
 
	 * as specified in the config file.
 
	 * As a side effect also increase the time the other lines have been in
 
	 * the list.
 
	 * @return true if and only if items got removed.
 
	 */
 
	static bool Truncate()
 
	{
 
		IConsoleLine *cur = IConsoleLine::front;
 
		if (cur == NULL) return false;
 

	
 
		int count = 1;
 
		for (IConsoleLine *item = cur->previous; item != NULL; count++, cur = item, item = item->previous) {
 
			if (item->time > _settings_client.gui.console_backlog_timeout &&
 
					count > _settings_client.gui.console_backlog_length) {
 
				delete item;
 
				cur->previous = NULL;
 
				return true;
 
			}
 

	
 
			if (item->time != MAX_UVALUE(uint16)) item->time++;
 
		}
 

	
 
		return false;
 
	}
 

	
 
	/**
 
	 * Reset the complete console line backlog.
 
	 */
 
	static void Reset()
 
	{
 
		delete IConsoleLine::front;
 
		IConsoleLine::front = NULL;
 
		IConsoleLine::size = 0;
 
	}
 
};
 

	
 
/* static */ IConsoleLine *IConsoleLine::front = NULL;
 
/* static */ int IConsoleLine::size  = 0;
 

	
 

	
 
/* ** main console cmd buffer ** */
 
static Textbuf _iconsole_cmdline;
 
static char *_iconsole_history[ICON_HISTORY_SIZE];
 
static byte _iconsole_historypos;
 
IConsoleModes _iconsole_mode;
 

	
 
/* *************** *
 
 *  end of header  *
 
 * *************** */
 

	
 
static void IConsoleClearCommand()
 
{
 
	memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
 
	_iconsole_cmdline.size = 1; // only terminating zero
 
	_iconsole_cmdline.width = 0;
 
	_iconsole_cmdline.caretpos = 0;
 
	_iconsole_cmdline.caretxoffs = 0;
 
	SetWindowDirty(WC_CONSOLE, 0);
 
}
 

	
 
static inline void IConsoleResetHistoryPos() {_iconsole_historypos = ICON_HISTORY_SIZE - 1;}
 

	
 

	
 
static const char *IConsoleHistoryAdd(const char *cmd);
 
static void IConsoleHistoryNavigate(int direction);
 

	
 
/** Widgets of the console window. */
 
enum ConsoleWidgets {
 
	CW_BACKGROUND, ///< Background of the console
 
};
 

	
 
static const struct NWidgetPart _nested_console_window_widgets[] = {
 
	NWidget(WWT_EMPTY, INVALID_COLOUR, CW_BACKGROUND), SetResize(1, 1),
 
};
 

	
 
static const WindowDesc _console_window_desc(
 
	WDP_MANUAL, 0, 0,
 
	WC_CONSOLE, WC_NONE,
 
	0,
 
	_nested_console_window_widgets, lengthof(_nested_console_window_widgets)
 
);
 

	
 
struct IConsoleWindow : Window
 
{
 
	static int scroll;
 
	int line_height;
 
	int line_offset;
 

	
 
	IConsoleWindow() : Window()
 
	{
 
		_iconsole_mode = ICONSOLE_OPENED;
 
		this->line_height = FONT_HEIGHT_NORMAL + ICON_LINE_SPACING;
 
		this->line_offset = GetStringBoundingBox("] ").width + 5;
 

	
 
		this->InitNested(&_console_window_desc, 0);
 
		ResizeWindow(this, _screen.width, _screen.height / 3);
 
	}
 

	
 
	~IConsoleWindow()
 
	{
 
		_iconsole_mode = ICONSOLE_CLOSED;
 
	}
 

	
 
	virtual void OnPaint()
 
	{
 
		const int max = (this->height / this->line_height) - 1;
 
		const int right = this->width - 5;
 

	
 
		const IConsoleLine *print = IConsoleLine::Get(IConsoleWindow::scroll);
 
		GfxFillRect(this->left, this->top, this->width, this->height - 1, 0);
 
		for (int i = 0; i < max && print != NULL; i++, print = print->previous) {
 
			DrawString(5, right, this->height - (2 + i) * this->line_height, print->buffer, print->colour, SA_LEFT | SA_FORCE);
 
		}
 
		/* If the text is longer than the window, don't show the starting ']' */
 
		int delta = this->width - this->line_offset - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH;
 
		if (delta > 0) {
 
			DrawString(5, right, this->height - this->line_height, "]", (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
 
			delta = 0;
 
		}
 

	
 
		DrawString(this->line_offset + delta, right, this->height - this->line_height, _iconsole_cmdline.buf, (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
 

	
 
		if (_focused_window == this && _iconsole_cmdline.caret) {
 
			DrawString(this->line_offset + delta + _iconsole_cmdline.caretxoffs, right, this->height - this->line_height, "_", TC_WHITE, SA_LEFT | SA_FORCE);
 
		}
 
	}
 

	
 
	virtual void OnHundredthTick()
 
	{
 
		if (IConsoleLine::Truncate() &&
 
				(IConsoleWindow::scroll > IConsoleLine::size)) {
 
			IConsoleWindow::scroll = max(0, IConsoleLine::size - (this->height / this->line_height) + 1);
 
			this->SetDirty();
 
		}
 
	}
 

	
 
	virtual void OnMouseLoop()
 
@@ -257,224 +254,224 @@ struct IConsoleWindow : Window
 
				}
 
				this->SetDirty();
 
				break;
 

	
 
			case WKC_SHIFT | WKC_DOWN:
 
				if (IConsoleWindow::scroll <= 0) {
 
					IConsoleWindow::scroll = 0;
 
				} else {
 
					--IConsoleWindow::scroll;
 
				}
 
				this->SetDirty();
 
				break;
 

	
 
			case WKC_SHIFT | WKC_UP:
 
				if (IConsoleWindow::scroll >= IConsoleLine::size) {
 
					IConsoleWindow::scroll = IConsoleLine::size;
 
				} else {
 
					++IConsoleWindow::scroll;
 
				}
 
				this->SetDirty();
 
				break;
 

	
 
			case WKC_BACKQUOTE:
 
				IConsoleSwitch();
 
				break;
 

	
 
			case WKC_RETURN: case WKC_NUM_ENTER: {
 
				IConsolePrintF(CC_COMMAND, "] %s", _iconsole_cmdline.buf);
 
				const char *cmd = IConsoleHistoryAdd(_iconsole_cmdline.buf);
 
				IConsoleClearCommand();
 

	
 
				if (cmd != NULL) IConsoleCmdExec(cmd);
 
			} break;
 

	
 
			case WKC_CTRL | WKC_RETURN:
 
				_iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
 
				IConsoleResize(this);
 
				MarkWholeScreenDirty();
 
				break;
 

	
 
#ifdef WITH_COCOA
 
			case (WKC_META | 'V'):
 
#endif
 
			case (WKC_CTRL | 'V'):
 
				if (InsertTextBufferClipboard(&_iconsole_cmdline)) {
 
					IConsoleResetHistoryPos();
 
					this->SetDirty();
 
				}
 
				break;
 

	
 
			case (WKC_CTRL | 'L'):
 
				IConsoleCmdExec("clear");
 
				break;
 

	
 
#ifdef WITH_COCOA
 
			case (WKC_META | 'U'):
 
#endif
 
			case (WKC_CTRL | 'U'):
 
				DeleteTextBufferAll(&_iconsole_cmdline);
 
				this->SetDirty();
 
				break;
 

	
 
			case WKC_BACKSPACE: case WKC_DELETE:
 
				if (DeleteTextBufferChar(&_iconsole_cmdline, keycode)) {
 
					IConsoleResetHistoryPos();
 
					this->SetDirty();
 
				}
 
				break;
 

	
 
			case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
 
				if (MoveTextBufferPos(&_iconsole_cmdline, keycode)) {
 
					IConsoleResetHistoryPos();
 
					this->SetDirty();
 
				}
 
				break;
 

	
 
			default:
 
				if (IsValidChar(key, CS_ALPHANUMERAL)) {
 
					IConsoleWindow::scroll = 0;
 
					InsertTextBufferChar(&_iconsole_cmdline, key);
 
					IConsoleResetHistoryPos();
 
					this->SetDirty();
 
				} else {
 
					return ES_NOT_HANDLED;
 
				}
 
		}
 
		return ES_HANDLED;
 
	}
 
};
 

	
 
int IConsoleWindow::scroll = 0;
 

	
 
void IConsoleGUIInit()
 
{
 
	_iconsole_historypos = ICON_HISTORY_SIZE - 1;
 
	_iconsole_mode = ICONSOLE_CLOSED;
 

	
 
	IConsoleLine::Reset();
 
	memset(_iconsole_history, 0, sizeof(_iconsole_history));
 

	
 
	_iconsole_cmdline.buf = CallocT<char>(ICON_CMDLN_SIZE); // create buffer and zero it
 
	_iconsole_cmdline.maxsize = ICON_CMDLN_SIZE;
 

	
 
	IConsolePrintF(CC_WARNING, "OpenTTD Game Console Revision 7 - %s", _openttd_revision);
 
	IConsolePrint(CC_WHITE,  "------------------------------------");
 
	IConsolePrint(CC_WHITE,  "use \"help\" for more information");
 
	IConsolePrint(CC_WHITE,  "");
 
	IConsoleClearCommand();
 
}
 

	
 
void IConsoleClearBuffer()
 
{
 
	IConsoleLine::Reset();
 
}
 

	
 
void IConsoleGUIFree()
 
{
 
	free(_iconsole_cmdline.buf);
 
	IConsoleClearBuffer();
 
}
 

	
 
void IConsoleResize(Window *w)
 
{
 
	switch (_iconsole_mode) {
 
		case ICONSOLE_OPENED:
 
			w->height = _screen.height / 3;
 
			w->width = _screen.width;
 
			break;
 
		case ICONSOLE_FULL:
 
			w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
 
			w->width = _screen.width;
 
			break;
 
		default: return;
 
	}
 

	
 
	MarkWholeScreenDirty();
 
}
 

	
 
void IConsoleSwitch()
 
{
 
	switch (_iconsole_mode) {
 
		case ICONSOLE_CLOSED:
 
			new IConsoleWindow();
 
			break;
 

	
 
		case ICONSOLE_OPENED: case ICONSOLE_FULL:
 
			DeleteWindowById(WC_CONSOLE, 0);
 
			break;
 
	}
 

	
 
	MarkWholeScreenDirty();
 
}
 

	
 
void IConsoleClose() {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();}
 

	
 
/**
 
 * Add the entered line into the history so you can look it back
 
 * scroll, etc. Put it to the beginning as it is the latest text
 
 * @param cmd Text to be entered into the 'history'
 
 * @return the command to execute
 
 */
 
static const char *IConsoleHistoryAdd(const char *cmd)
 
{
 
	/* Strip all spaces at the begin */
 
	while (IsWhitespace(*cmd)) cmd++;
 

	
 
	/* Do not put empty command in history */
 
	if (StrEmpty(cmd)) return NULL;
 

	
 
	/* Do not put in history if command is same as previous */
 
	if (_iconsole_history[0] == NULL || strcmp(_iconsole_history[0], cmd) != 0) {
 
		free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
 
		memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1));
 
		_iconsole_history[0] = strdup(cmd);
 
	}
 

	
 
	/* Reset the history position */
 
	IConsoleResetHistoryPos();
 
	return _iconsole_history[0];
 
}
 

	
 
/**
 
 * Navigate Up/Down in the history of typed commands
 
 * @param direction Go further back in history (+1), go to recently typed commands (-1)
 
 */
 
static void IConsoleHistoryNavigate(int direction)
 
{
 
	if (_iconsole_history[0] == NULL) return; // Empty history
 
	int i = _iconsole_historypos + direction;
 

	
 
	/* watch out for overflows, just wrap around */
 
	if (i < 0) i = ICON_HISTORY_SIZE - 1;
 
	if (i >= ICON_HISTORY_SIZE) i = 0;
 
	if ((uint)i >= ICON_HISTORY_SIZE) i = 0;
 

	
 
	if (direction > 0) {
 
		if (_iconsole_history[i] == NULL) i = 0;
 
	}
 

	
 
	if (direction < 0) {
 
		while (i > 0 && _iconsole_history[i] == NULL) i--;
 
	}
 

	
 
	_iconsole_historypos = i;
 
	IConsoleClearCommand();
 
	/* copy history to 'command prompt / bash' */
 
	assert(_iconsole_history[i] != NULL && IsInsideMM(i, 0, ICON_HISTORY_SIZE));
 
	ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxsize);
 
	UpdateTextBufferSize(&_iconsole_cmdline);
 
}
 

	
 
/**
 
 * Handle the printing of text entered into the console or redirected there
 
 * by any other means. Text can be redirected to other clients in a network game
 
 * as well as to a logfile. If the network server is a dedicated server, all activities
 
 * are also logged. All lines to print are added to a temporary buffer which can be
 
 * used as a history to print them onscreen
 
 * @param colour_code the colour of the command. Red in case of errors, etc.
 
 * @param str the message entered or output on the console (notice, error, etc.)
 
 */
 
void IConsoleGUIPrint(ConsoleColour colour_code, char *str)
 
{
 
	new IConsoleLine(str, (TextColour)colour_code);
 
	SetWindowDirty(WC_CONSOLE, 0);
 
}
src/currency.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file currency.cpp Support for different currencies. */
 

	
 
#include "stdafx.h"
 
#include "currency.h"
 
#include "news_func.h"
 
#include "settings_type.h"
 
#include "date_func.h"
 

	
 
#include "table/strings.h"
 

	
 
	/*   exchange rate    prefix                      symbol_pos
 
	 *   |  separator        |           postfix          |
 
	 *   |   |   Euro year   |              |             | name
 
	 *   |   |    |          |              |             |  | */
 
static const CurrencySpec origin_currency_specs[NUM_CURRENCY] = {
 
	{    1, "", CF_NOEURO, "\xC2\xA3",     "",           0, STR_GAME_OPTIONS_CURRENCY_GBP    }, ///< british pounds
 
	{    2, "", CF_NOEURO, "$",            "",           0, STR_GAME_OPTIONS_CURRENCY_USD    }, ///< us dollars
 
	{    2, "", CF_ISEURO, "\xE2\x82\xAC", "",           0, STR_GAME_OPTIONS_CURRENCY_EUR    }, ///< Euro
 
	{  220, "", CF_NOEURO, "\xC2\xA5",     "",           0, STR_GAME_OPTIONS_CURRENCY_YEN    }, ///< yen
 
	{   20, "", 2002,      "",             " S.",        1, STR_GAME_OPTIONS_CURRENCY_ATS    }, ///< austrian schilling
 
	{   59, "", 2002,      "BEF ",         "",           0, STR_GAME_OPTIONS_CURRENCY_BEF    }, ///< belgian franc
 
	{    2, "", CF_NOEURO, "CHF ",         "",           0, STR_GAME_OPTIONS_CURRENCY_CHF    }, ///< swiss franc
 
	{   41, "", CF_NOEURO, "",             " K\xC4\x8D", 1, STR_GAME_OPTIONS_CURRENCY_CZK    }, ///< czech koruna
 
	{    3, "", 2002,      "DM ",          "",           0, STR_GAME_OPTIONS_CURRENCY_DEM    }, ///< deutsche mark
 
	{   11, "", CF_NOEURO, "",             " kr",        1, STR_GAME_OPTIONS_CURRENCY_DKK    }, ///< danish krone
 
	{  245, "", 2002,      "Pts ",         "",           0, STR_GAME_OPTIONS_CURRENCY_ESP    }, ///< spanish pesetas
 
	{    9, "", 2002,      "",             " mk",        1, STR_GAME_OPTIONS_CURRENCY_FIM    }, ///< finnish markka
 
	{   10, "", 2002,      "FF ",          "",           0, STR_GAME_OPTIONS_CURRENCY_FRF    }, ///< french francs
 
	{  500, "", 2002,      "",             "Dr.",        1, STR_GAME_OPTIONS_CURRENCY_GRD    }, ///< greek drachma
 
	{  378, "", CF_NOEURO, "",             " Ft",        1, STR_GAME_OPTIONS_CURRENCY_HUF    }, ///< hungarian forint
 
	{  130, "", CF_NOEURO, "",             " Kr",        1, STR_GAME_OPTIONS_CURRENCY_ISK    }, ///< icelandic krona
 
	{ 2850, "", 2002,      "",             " L.",        1, STR_GAME_OPTIONS_CURRENCY_ITL    }, ///< italian lira
 
	{    3, "", 2002,      "NLG ",         "",           0, STR_GAME_OPTIONS_CURRENCY_NLG    }, ///< dutch gulden
 
	{   12, "", CF_NOEURO, "",             " Kr",        1, STR_GAME_OPTIONS_CURRENCY_NOK    }, ///< norwegian krone
 
	{    6, "", CF_NOEURO, "",             " z\xC5\x82", 1, STR_GAME_OPTIONS_CURRENCY_PLN    }, ///< polish zloty
 
	{    5, "", CF_NOEURO, "",             " Lei",       1, STR_GAME_OPTIONS_CURRENCY_RON    }, ///< romanian Lei
 
	{   50, "", CF_NOEURO, "",             " p",         1, STR_GAME_OPTIONS_CURRENCY_RUR    }, ///< russian rouble
 
	{  352, "", 2007,      "",             " SIT",       1, STR_GAME_OPTIONS_CURRENCY_SIT    }, ///< slovenian tolar
 
	{   13, "", CF_NOEURO, "",             " Kr",        1, STR_GAME_OPTIONS_CURRENCY_SEK    }, ///< swedish krona
 
	{    3, "", CF_NOEURO, "",             " TL",        1, STR_GAME_OPTIONS_CURRENCY_TRY    }, ///< turkish lira
 
	{   52, "", 2009,      "",             " Sk",        1, STR_GAME_OPTIONS_CURRENCY_SKK    }, ///< slovak koruna
 
	{    4, "", CF_NOEURO, "R$ ",          "",           0, STR_GAME_OPTIONS_CURRENCY_BRL    }, ///< brazil real
 
	{   20, "", CF_NOEURO, "",             " EEK",       1, STR_GAME_OPTIONS_CURRENCY_EEK    }, ///< estonian krooni
 
	{    1, "", CF_NOEURO, "",             "",           2, STR_GAME_OPTIONS_CURRENCY_CUSTOM }, ///< custom currency
 
};
 

	
 
/* Array of currencies used by the system */
 
CurrencySpec _currency_specs[NUM_CURRENCY];
 

	
 
/**
 
 * These enums are only declared in order to make sens
 
 * out of the TTDPatch_To_OTTDIndex array that will follow
 
 * Every currency used by Ottd is there, just in case TTDPatch will
 
 * add those missing in its code
 
 **/
 
enum {
 
enum Currencies {
 
	CURR_GBP,
 
	CURR_USD,
 
	CURR_EUR,
 
	CURR_YEN,
 
	CURR_ATS,
 
	CURR_BEF,
 
	CURR_CHF,
 
	CURR_CZK,
 
	CURR_DEM,
 
	CURR_DKK,
 
	CURR_ESP,
 
	CURR_FIM,
 
	CURR_FRF,
 
	CURR_GRD,
 
	CURR_HUF,
 
	CURR_ISK,
 
	CURR_ITL,
 
	CURR_NLG,
 
	CURR_NOK,
 
	CURR_PLN,
 
	CURR_RON,
 
	CURR_RUR,
 
	CURR_SIT,
 
	CURR_SEK,
 
	CURR_YTL,
 
	CURR_SKK,
 
	CURR_BRL,
 
	CURR_EEK,
 
};
 

	
 
/**
 
 * This array represent the position of OpenTTD's currencies,
 
 * compared to TTDPatch's ones.
 
 * When a grf sends currencies, they are based on the order defined by TTDPatch.
 
 * So, we must reindex them to our own order.
 
 **/
 
const byte TTDPatch_To_OTTDIndex[] =
 
{
 
	CURR_GBP,
 
	CURR_USD,
 
	CURR_FRF,
 
	CURR_DEM,
 
	CURR_YEN,
 
	CURR_ESP,
 
	CURR_HUF,
 
	CURR_PLN,
 
	CURR_ATS,
 
	CURR_BEF,
 
	CURR_DKK,
 
	CURR_FIM,
 
	CURR_GRD,
 
	CURR_CHF,
 
	CURR_NLG,
 
	CURR_ITL,
 
	CURR_SEK,
 
	CURR_RUR,
 
	CURR_EUR,
 
};
 

	
 
/**
 
 * Will return the ottd's index correspondance to
 
 * the ttdpatch's id.  If the id is bigger than the array,
 
 * it is a grf written for ottd, thus returning the same id.
 
 * Only called from newgrf.cpp
 
 * @param grfcurr_id currency id coming from newgrf
 
 * @return the corrected index
 
 **/
 
byte GetNewgrfCurrencyIdConverted(byte grfcurr_id)
 
{
 
	return (grfcurr_id >= lengthof(TTDPatch_To_OTTDIndex)) ? grfcurr_id : TTDPatch_To_OTTDIndex[grfcurr_id];
 
}
 

	
 
/**
 
 * get a mask of the allowed currencies depending on the year
 
 * @return mask of currencies
 
 */
 
uint GetMaskOfAllowedCurrencies()
 
{
 
	uint mask = 0;
 
	uint i;
 

	
 
	for (i = 0; i < NUM_CURRENCY; i++) {
 
		Year to_euro = _currency_specs[i].to_euro;
 

	
 
		if (to_euro != CF_NOEURO && to_euro != CF_ISEURO && _cur_year >= to_euro) continue;
 
		if (to_euro == CF_ISEURO && _cur_year < 2000) continue;
 
		mask |= (1 << i);
 
	}
 
	mask |= (1 << CUSTOM_CURRENCY_ID); // always allow custom currency
 
	return mask;
 
}
 

	
 
/**
 
 * Verify if the currency chosen by the user is about to be converted to Euro
 
 **/
 
void CheckSwitchToEuro()
 
{
 
	if (_currency_specs[_settings_game.locale.currency].to_euro != CF_NOEURO &&
 
			_currency_specs[_settings_game.locale.currency].to_euro != CF_ISEURO &&
 
			_cur_year >= _currency_specs[_settings_game.locale.currency].to_euro) {
 
		_settings_game.locale.currency = 2; // this is the index of euro above.
 
		AddNewsItem(STR_NEWS_EURO_INTRODUCTION, NS_ECONOMY);
 
	}
 
}
 

	
 
/**
 
 * Will fill _currency_specs array with
 
 * default values from origin_currency_specs
 
 * Called only from newgrf.cpp and settings.cpp.
 
 * @param preserve_custom will not reset custom currency (the latest one on the list)
 
 *        if ever it is flagged to true. In which case, the total size of the memory to move
 
 *        will be one currency spec less, thus preserving the custom curreny from been
 
 *        overwritten.
 
 **/
 
void ResetCurrencies(bool preserve_custom)
 
{
 
	memcpy(&_currency_specs, &origin_currency_specs, sizeof(origin_currency_specs) - (preserve_custom ? sizeof(_custom_currency) : 0));
 
}
 

	
 
/**
 
 * Build a list of currency names StringIDs to use in a dropdown list
 
 * @return Pointer to a (static) array of StringIDs
 
 */
 
StringID *BuildCurrencyDropdown()
 
{
 
	/* Allow room for all currencies, plus a terminator entry */
 
	static StringID names[NUM_CURRENCY + 1];
 
	uint i;
 

	
 
	/* Add each name */
 
	for (i = 0; i < NUM_CURRENCY; i++) {
 
		names[i] = _currency_specs[i].name;
 
	}
 
	/* Terminate the list */
 
	names[i] = INVALID_STRING_ID;
 

	
 
	return names;
 
}
src/date.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file date.cpp Handling of dates in our native format and transforming them to something human readable. */
 

	
 
#include "stdafx.h"
 
#include "variables.h"
 
#include "network/network.h"
 
#include "network/network_func.h"
 
#include "currency.h"
 
#include "window_func.h"
 
#include "functions.h"
 
#include "date_func.h"
 
#include "vehicle_base.h"
 
#include "debug.h"
 
#include "rail_gui.h"
 
#include "saveload/saveload.h"
 

	
 
Year      _cur_year;   ///< Current year, starting at 0
 
Month     _cur_month;  ///< Current month (0..11)
 
Date      _date;       ///< Current date in days (day counter)
 
DateFract _date_fract;
 

	
 

	
 
void SetDate(Date date)
 
{
 
	YearMonthDay ymd;
 

	
 
	_date = date;
 
	ConvertDateToYMD(date, &ymd);
 
	_cur_year = ymd.year;
 
	_cur_month = ymd.month;
 
}
 

	
 
#define M(a, b) ((a << 5) | b)
 
static const uint16 _month_date_from_year_day[] = {
 
	M( 0, 1), M( 0, 2), M( 0, 3), M( 0, 4), M( 0, 5), M( 0, 6), M( 0, 7), M( 0, 8), M( 0, 9), M( 0, 10), M( 0, 11), M( 0, 12), M( 0, 13), M( 0, 14), M( 0, 15), M( 0, 16), M( 0, 17), M( 0, 18), M( 0, 19), M( 0, 20), M( 0, 21), M( 0, 22), M( 0, 23), M( 0, 24), M( 0, 25), M( 0, 26), M( 0, 27), M( 0, 28), M( 0, 29), M( 0, 30), M( 0, 31),
 
	M( 1, 1), M( 1, 2), M( 1, 3), M( 1, 4), M( 1, 5), M( 1, 6), M( 1, 7), M( 1, 8), M( 1, 9), M( 1, 10), M( 1, 11), M( 1, 12), M( 1, 13), M( 1, 14), M( 1, 15), M( 1, 16), M( 1, 17), M( 1, 18), M( 1, 19), M( 1, 20), M( 1, 21), M( 1, 22), M( 1, 23), M( 1, 24), M( 1, 25), M( 1, 26), M( 1, 27), M( 1, 28), M( 1, 29),
 
	M( 2, 1), M( 2, 2), M( 2, 3), M( 2, 4), M( 2, 5), M( 2, 6), M( 2, 7), M( 2, 8), M( 2, 9), M( 2, 10), M( 2, 11), M( 2, 12), M( 2, 13), M( 2, 14), M( 2, 15), M( 2, 16), M( 2, 17), M( 2, 18), M( 2, 19), M( 2, 20), M( 2, 21), M( 2, 22), M( 2, 23), M( 2, 24), M( 2, 25), M( 2, 26), M( 2, 27), M( 2, 28), M( 2, 29), M( 2, 30), M( 2, 31),
 
	M( 3, 1), M( 3, 2), M( 3, 3), M( 3, 4), M( 3, 5), M( 3, 6), M( 3, 7), M( 3, 8), M( 3, 9), M( 3, 10), M( 3, 11), M( 3, 12), M( 3, 13), M( 3, 14), M( 3, 15), M( 3, 16), M( 3, 17), M( 3, 18), M( 3, 19), M( 3, 20), M( 3, 21), M( 3, 22), M( 3, 23), M( 3, 24), M( 3, 25), M( 3, 26), M( 3, 27), M( 3, 28), M( 3, 29), M( 3, 30),
 
	M( 4, 1), M( 4, 2), M( 4, 3), M( 4, 4), M( 4, 5), M( 4, 6), M( 4, 7), M( 4, 8), M( 4, 9), M( 4, 10), M( 4, 11), M( 4, 12), M( 4, 13), M( 4, 14), M( 4, 15), M( 4, 16), M( 4, 17), M( 4, 18), M( 4, 19), M( 4, 20), M( 4, 21), M( 4, 22), M( 4, 23), M( 4, 24), M( 4, 25), M( 4, 26), M( 4, 27), M( 4, 28), M( 4, 29), M( 4, 30), M( 4, 31),
 
	M( 5, 1), M( 5, 2), M( 5, 3), M( 5, 4), M( 5, 5), M( 5, 6), M( 5, 7), M( 5, 8), M( 5, 9), M( 5, 10), M( 5, 11), M( 5, 12), M( 5, 13), M( 5, 14), M( 5, 15), M( 5, 16), M( 5, 17), M( 5, 18), M( 5, 19), M( 5, 20), M( 5, 21), M( 5, 22), M( 5, 23), M( 5, 24), M( 5, 25), M( 5, 26), M( 5, 27), M( 5, 28), M( 5, 29), M( 5, 30),
 
	M( 6, 1), M( 6, 2), M( 6, 3), M( 6, 4), M( 6, 5), M( 6, 6), M( 6, 7), M( 6, 8), M( 6, 9), M( 6, 10), M( 6, 11), M( 6, 12), M( 6, 13), M( 6, 14), M( 6, 15), M( 6, 16), M( 6, 17), M( 6, 18), M( 6, 19), M( 6, 20), M( 6, 21), M( 6, 22), M( 6, 23), M( 6, 24), M( 6, 25), M( 6, 26), M( 6, 27), M( 6, 28), M( 6, 29), M( 6, 30), M( 6, 31),
 
	M( 7, 1), M( 7, 2), M( 7, 3), M( 7, 4), M( 7, 5), M( 7, 6), M( 7, 7), M( 7, 8), M( 7, 9), M( 7, 10), M( 7, 11), M( 7, 12), M( 7, 13), M( 7, 14), M( 7, 15), M( 7, 16), M( 7, 17), M( 7, 18), M( 7, 19), M( 7, 20), M( 7, 21), M( 7, 22), M( 7, 23), M( 7, 24), M( 7, 25), M( 7, 26), M( 7, 27), M( 7, 28), M( 7, 29), M( 7, 30), M( 7, 31),
 
	M( 8, 1), M( 8, 2), M( 8, 3), M( 8, 4), M( 8, 5), M( 8, 6), M( 8, 7), M( 8, 8), M( 8, 9), M( 8, 10), M( 8, 11), M( 8, 12), M( 8, 13), M( 8, 14), M( 8, 15), M( 8, 16), M( 8, 17), M( 8, 18), M( 8, 19), M( 8, 20), M( 8, 21), M( 8, 22), M( 8, 23), M( 8, 24), M( 8, 25), M( 8, 26), M( 8, 27), M( 8, 28), M( 8, 29), M( 8, 30),
 
	M( 9, 1), M( 9, 2), M( 9, 3), M( 9, 4), M( 9, 5), M( 9, 6), M( 9, 7), M( 9, 8), M( 9, 9), M( 9, 10), M( 9, 11), M( 9, 12), M( 9, 13), M( 9, 14), M( 9, 15), M( 9, 16), M( 9, 17), M( 9, 18), M( 9, 19), M( 9, 20), M( 9, 21), M( 9, 22), M( 9, 23), M( 9, 24), M( 9, 25), M( 9, 26), M( 9, 27), M( 9, 28), M( 9, 29), M( 9, 30), M( 9, 31),
 
	M(10, 1), M(10, 2), M(10, 3), M(10, 4), M(10, 5), M(10, 6), M(10, 7), M(10, 8), M(10, 9), M(10, 10), M(10, 11), M(10, 12), M(10, 13), M(10, 14), M(10, 15), M(10, 16), M(10, 17), M(10, 18), M(10, 19), M(10, 20), M(10, 21), M(10, 22), M(10, 23), M(10, 24), M(10, 25), M(10, 26), M(10, 27), M(10, 28), M(10, 29), M(10, 30),
 
	M(11, 1), M(11, 2), M(11, 3), M(11, 4), M(11, 5), M(11, 6), M(11, 7), M(11, 8), M(11, 9), M(11, 10), M(11, 11), M(11, 12), M(11, 13), M(11, 14), M(11, 15), M(11, 16), M(11, 17), M(11, 18), M(11, 19), M(11, 20), M(11, 21), M(11, 22), M(11, 23), M(11, 24), M(11, 25), M(11, 26), M(11, 27), M(11, 28), M(11, 29), M(11, 30), M(11, 31),
 
};
 
#undef M
 

	
 
enum {
 
enum DaysTillMonth {
 
	ACCUM_JAN = 0,
 
	ACCUM_FEB = ACCUM_JAN + 31,
 
	ACCUM_MAR = ACCUM_FEB + 29,
 
	ACCUM_APR = ACCUM_MAR + 31,
 
	ACCUM_MAY = ACCUM_APR + 30,
 
	ACCUM_JUN = ACCUM_MAY + 31,
 
	ACCUM_JUL = ACCUM_JUN + 30,
 
	ACCUM_AUG = ACCUM_JUL + 31,
 
	ACCUM_SEP = ACCUM_AUG + 31,
 
	ACCUM_OCT = ACCUM_SEP + 30,
 
	ACCUM_NOV = ACCUM_OCT + 31,
 
	ACCUM_DEC = ACCUM_NOV + 30,
 
};
 

	
 
static const uint16 _accum_days_for_month[] = {
 
	ACCUM_JAN, ACCUM_FEB, ACCUM_MAR, ACCUM_APR,
 
	ACCUM_MAY, ACCUM_JUN, ACCUM_JUL, ACCUM_AUG,
 
	ACCUM_SEP, ACCUM_OCT, ACCUM_NOV, ACCUM_DEC,
 
};
 

	
 
/**
 
 * Converts a Date to a Year, Month & Day.
 
 * @param date the date to convert from
 
 * @param ymd  the year, month and day to write to
 
 */
 
void ConvertDateToYMD(Date date, YearMonthDay *ymd)
 
{
 
	/*
 
	 * Year determination in multiple steps to account for leap
 
	 * years. First do the large steps, then the smaller ones.
 
	 */
 

	
 
	/* There are 97 leap years in 400 years */
 
	Year yr = 400 * (date / (DAYS_IN_YEAR * 400 + 97));
 
	int rem = date % (DAYS_IN_YEAR * 400 + 97);
 
	uint16 x;
 

	
 
	if (rem >= DAYS_IN_YEAR * 100 + 25) {
 
		/* There are 25 leap years in the first 100 years after
 
		 * every 400th year, as every 400th year is a leap year */
 
		yr  += 100;
 
		rem -= DAYS_IN_YEAR * 100 + 25;
 

	
 
		/* There are 24 leap years in the next couple of 100 years */
 
		yr += 100 * (rem / (DAYS_IN_YEAR * 100 + 24));
 
		rem = (rem % (DAYS_IN_YEAR * 100 + 24));
 
	}
 

	
 
	if (!IsLeapYear(yr) && rem >= DAYS_IN_YEAR * 4) {
 
		/* The first 4 year of the century are not always a leap year */
 
		yr  += 4;
 
		rem -= DAYS_IN_YEAR * 4;
 
	}
 

	
 
	/* There is 1 leap year every 4 years */
 
	yr += 4 * (rem / (DAYS_IN_YEAR * 4 + 1));
 
	rem = rem % (DAYS_IN_YEAR * 4 + 1);
 

	
 
	/* The last (max 3) years to account for; the first one
 
	 * can be, but is not necessarily a leap year */
 
	while (rem >= (IsLeapYear(yr) ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR)) {
 
		rem -= IsLeapYear(yr) ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR;
 
		yr++;
 
	}
 

	
 
	/* Skip the 29th of February in non-leap years */
 
	if (!IsLeapYear(yr) && rem >= ACCUM_MAR - 1) rem++;
 

	
 
	ymd->year = yr;
 

	
 
	x = _month_date_from_year_day[rem];
 
	ymd->month = x >> 5;
 
	ymd->day = x & 0x1F;
 
}
 

	
 
/**
 
 * Converts a tupe of Year, Month and Day to a Date.
 
 * @param year  is a number between 0..MAX_YEAR
 
 * @param month is a number between 0..11
 
 * @param day   is a number between 1..31
 
 */
 
Date ConvertYMDToDate(Year year, Month month, Day day)
 
{
 
	/* Day-offset in a leap year */
 
	int days = _accum_days_for_month[month] + day - 1;
 

	
 
	/* Account for the missing of the 29th of February in non-leap years */
 
	if (!IsLeapYear(year) && days >= ACCUM_MAR) days--;
 

	
 
	return DAYS_TILL(year) + days;
 
}
 

	
 
/** Functions used by the IncreaseDate function */
 

	
 
extern void EnginesDailyLoop();
 
extern void DisasterDailyLoop();
 
extern void IndustryDailyLoop();
 
extern void CompaniesMonthlyLoop();
 
extern void EnginesMonthlyLoop();
 
extern void TownsMonthlyLoop();
 
extern void IndustryMonthlyLoop();
 
extern void StationMonthlyLoop();
 
extern void SubsidyMonthlyLoop();
 

	
 
extern void CompaniesYearlyLoop();
 
extern void VehiclesYearlyLoop();
 
extern void TownsYearlyLoop();
 

	
 
extern void ShowEndGameChart();
 

	
 

	
 
static const Month _autosave_months[] = {
 
	 0, ///< never
 
	 1, ///< every month
 
	 3, ///< every 3 months
 
	 6, ///< every 6 months
 
	12, ///< every 12 months
 
};
 

	
 
/**
 
 * Runs various procedures that have to be done yearly
 
 */
 
static void OnNewYear()
 
{
 
	CompaniesYearlyLoop();
 
	VehiclesYearlyLoop();
 
	TownsYearlyLoop();
 
	InvalidateWindowClassesData(WC_BUILD_STATION);
 
#ifdef ENABLE_NETWORK
 
	if (_network_server) NetworkServerYearlyLoop();
 
#endif /* ENABLE_NETWORK */
 

	
 
	if (_cur_year == _settings_client.gui.semaphore_build_before) ResetSignalVariant();
 

	
 
	/* check if we reached end of the game */
 
	if (_cur_year == ORIGINAL_END_YEAR) {
 
		ShowEndGameChart();
 
	/* check if we reached the maximum year, decrement dates by a year */
 
	} else if (_cur_year == MAX_YEAR + 1) {
 
		Vehicle *v;
 
		uint days_this_year;
 

	
 
		_cur_year--;
 
		days_this_year = IsLeapYear(_cur_year) ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR;
 
		_date -= days_this_year;
 
		FOR_ALL_VEHICLES(v) v->date_of_last_service -= days_this_year;
 

	
 
#ifdef ENABLE_NETWORK
 
		/* Because the _date wraps here, and text-messages expire by game-days, we have to clean out
 
		 *  all of them if the date is set back, else those messages will hang for ever */
 
		NetworkInitChatMessage();
 
#endif /* ENABLE_NETWORK */
 
	}
 

	
 
	if (_settings_client.gui.auto_euro) CheckSwitchToEuro();
 
}
 

	
 
/**
 
 * Runs various procedures that have to be done monthly
 
 */
 
static void OnNewMonth()
 
{
 
	if (_settings_client.gui.autosave != 0 && (_cur_month % _autosave_months[_settings_client.gui.autosave]) == 0) {
 
		_do_autosave = true;
 
		RedrawAutosave();
 
	}
 

	
 
	SetWindowClassesDirty(WC_CHEATS);
 
	CompaniesMonthlyLoop();
 
	SubsidyMonthlyLoop();
 
	EnginesMonthlyLoop();
 
	TownsMonthlyLoop();
 
	IndustryMonthlyLoop();
 
	StationMonthlyLoop();
 
#ifdef ENABLE_NETWORK
 
	if (_network_server) NetworkServerMonthlyLoop();
 
#endif /* ENABLE_NETWORK */
 
}
 

	
 
/**
 
 * Runs various procedures that have to be done daily
 
 */
 
static void OnNewDay()
 
{
 
#ifdef ENABLE_NETWORK
 
	NetworkChatMessageDailyLoop();
 
#endif /* ENABLE_NETWORK */
 

	
 
	DisasterDailyLoop();
 
	IndustryDailyLoop();
 

	
 
	SetWindowWidgetDirty(WC_STATUS_BAR, 0, 0);
src/fontcache.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file fontcache.cpp Cache for characters from fonts. */
 

	
 
#include "stdafx.h"
 
#include "fontcache.h"
 
#include "blitter/factory.hpp"
 
#include "core/math_func.hpp"
 

	
 
#include "table/sprites.h"
 
#include "table/control_codes.h"
 

	
 
static const int ASCII_LETTERSTART = 32; ///< First printable ASCII letter.
 

	
 
/** Semi-constant for the height of the different sizes of fonts. */
 
int _font_height[FS_END];
 

	
 
#ifdef WITH_FREETYPE
 
#include <ft2build.h>
 
#include FT_FREETYPE_H
 
#include FT_GLYPH_H
 

	
 
#ifdef WITH_FONTCONFIG
 
#include <fontconfig/fontconfig.h>
 
#endif
 

	
 
static FT_Library _library = NULL;
 
static FT_Face _face_small = NULL;
 
static FT_Face _face_medium = NULL;
 
static FT_Face _face_large = NULL;
 
static int _ascender[FS_END];
 

	
 
FreeTypeSettings _freetype;
 

	
 
enum {
 
	FACE_COLOUR = 1,
 
	SHADOW_COLOUR = 2,
 
};
 
static const byte FACE_COLOUR   = 1;
 
static const byte SHADOW_COLOUR = 2;
 

	
 
/** Get the font loaded into a Freetype face by using a font-name.
 
 * If no appropiate font is found, the function returns an error */
 
#ifdef WIN32
 
#include <windows.h>
 
#include <shlobj.h> /* SHGetFolderPath */
 
#include "os/windows/win32.h"
 

	
 
/**
 
 * Get the short DOS 8.3 format for paths.
 
 * FreeType doesn't support Unicode filenames and Windows' fopen (as used
 
 * by FreeType) doesn't support UTF-8 filenames. So we have to convert the
 
 * filename into something that isn't UTF-8 but represents the Unicode file
 
 * name. This is the short DOS 8.3 format. This does not contain any
 
 * characters that fopen doesn't support.
 
 * @param long_path the path in UTF-8.
 
 * @return the short path in ANSI (ASCII).
 
 */
 
char *GetShortPath(const char *long_path)
 
{
 
	static char short_path[MAX_PATH];
 
#ifdef UNICODE
 
	/* The non-unicode GetShortPath doesn't support UTF-8...,
 
	 * so convert the path to wide chars, then get the short
 
	 * path and convert it back again. */
 
	wchar_t long_path_w[MAX_PATH];
 
	MultiByteToWideChar(CP_UTF8, 0, long_path, -1, long_path_w, MAX_PATH);
 

	
 
	wchar_t short_path_w[MAX_PATH];
 
	GetShortPathNameW(long_path_w, short_path_w, MAX_PATH);
 

	
 
	WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path, MAX_PATH, NULL, NULL);
 
#else
 
	/* Technically not needed, but do it for consistency. */
 
	GetShortPathNameA(long_path, short_path, MAX_PATH);
 
#endif
 
	return short_path;
 
}
 

	
 
/* Get the font file to be loaded into Freetype by looping the registry
 
 * location where windows lists all installed fonts. Not very nice, will
 
 * surely break if the registry path changes, but it works. Much better
 
 * solution would be to use CreateFont, and extract the font data from it
 
 * by GetFontData. The problem with this is that the font file needs to be
 
 * kept in memory then until the font is no longer needed. This could mean
 
 * an additional memory usage of 30MB (just for fonts!) when using an eastern
 
 * font for all font sizes */
 
#define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
 
#define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts"
 
static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
 
{
 
	FT_Error err = FT_Err_Cannot_Open_Resource;
 
	HKEY hKey;
 
	LONG ret;
 
	TCHAR vbuffer[MAX_PATH], dbuffer[256];
 
	TCHAR *font_namep;
 
	char *font_path;
 
	uint index;
 

	
 
	/* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
 
	 * "Windows NT" key, on Windows 9x in the Windows key. To save us having
 
	 * to retrieve the windows version, we'll just query both */
 
	ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
 
	if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
 

	
 
	if (ret != ERROR_SUCCESS) {
 
		DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
 
		return err;
 
	}
 

	
 
	/* For Unicode we need some conversion between widechar and
 
	 * normal char to match the data returned by RegEnumValue,
 
	 * otherwise just use parameter */
 
#if defined(UNICODE)
 
	font_namep = MallocT<TCHAR>(MAX_PATH);
 
	MB_TO_WIDE_BUFFER(font_name, font_namep, MAX_PATH * sizeof(TCHAR));
 
#else
 
	font_namep = const_cast<char *>(font_name); // only cast because in unicode pointer is not const
 
#endif
 

	
 
	for (index = 0;; index++) {
 
		TCHAR *s;
 
		DWORD vbuflen = lengthof(vbuffer);
 
		DWORD dbuflen = lengthof(dbuffer);
 

	
 
		ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, NULL, NULL, (byte*)dbuffer, &dbuflen);
 
		if (ret != ERROR_SUCCESS) goto registry_no_font_found;
 

	
 
		/* The font names in the registry are of the following 3 forms:
 
		 * - ADMUI3.fon
 
		 * - Book Antiqua Bold (TrueType)
 
		 * - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
 
		 * We will strip the font-type '()' if any and work with the font name
 
		 * itself, which must match exactly; if...
 
		 * TTC files, font files which contain more than one font are seperated
 
		 * byt '&'. Our best bet will be to do substr match for the fontname
 
		 * and then let FreeType figure out which index to load */
 
		s = _tcschr(vbuffer, _T('('));
 
		if (s != NULL) s[-1] = '\0';
 

	
 
		if (_tcschr(vbuffer, _T('&')) == NULL) {
 
			if (_tcsicmp(vbuffer, font_namep) == 0) break;
 
		} else {
 
			if (_tcsstr(vbuffer, font_namep) != NULL) break;
 
		}
 
	}
 

	
 
	if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, vbuffer))) {
 
		DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory");
 
		goto folder_error;
 
	}
 

	
 
	/* Some fonts are contained in .ttc files, TrueType Collection fonts. These
 
	 * contain multiple fonts inside this single file. GetFontData however
 
	 * returns the whole file, so we need to check each font inside to get the
 
	 * proper font.
 
	 * Also note that FreeType does not support UNICODE filesnames! */
 
#if defined(UNICODE)
 
	/* We need a cast here back from wide because FreeType doesn't support
 
	 * widechar filenames. Just use the buffer we allocated before for the
 
	 * font_name search */
 
	font_path = (char*)font_namep;
 
	WIDE_TO_MB_BUFFER(vbuffer, font_path, MAX_PATH * sizeof(TCHAR));
 
#else
 
	font_path = vbuffer;
 
#endif
 

	
 
	ttd_strlcat(font_path, "\\", MAX_PATH * sizeof(TCHAR));
 
	ttd_strlcat(font_path, WIDE_TO_MB(dbuffer), MAX_PATH * sizeof(TCHAR));
 

	
 
	/* Convert the path into something that FreeType understands */
 
	font_path = GetShortPath(font_path);
 

	
 
	index = 0;
 
	do {
 
		err = FT_New_Face(_library, font_path, index, face);
 
		if (err != FT_Err_Ok) break;
 

	
 
		if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
 
		/* Try english name if font name failed */
 
		if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
 
		err = FT_Err_Cannot_Open_Resource;
 

	
 
	} while ((FT_Long)++index != (*face)->num_faces);
 

	
 

	
 
folder_error:
 
registry_no_font_found:
 
#if defined(UNICODE)
 
	free(font_namep);
 
#endif
 
	RegCloseKey(hKey);
 
	return err;
 
}
 

	
 
/**
 
 * Fonts can have localised names and when the system locale is the same as
 
 * one of those localised names Windows will always return that localised name
 
 * instead of allowing to get the non-localised (English US) name of the font.
 
 * This will later on give problems as freetype uses the non-localised name of
 
 * the font and we need to compare based on that name.
 
 * Windows furthermore DOES NOT have an API to get the non-localised name nor
 
 * can we override the system locale. This means that we have to actually read
 
 * the font itself to gather the font name we want.
 
 * Based on: http://blogs.msdn.com/michkap/archive/2006/02/13/530814.aspx
 
 * @param logfont the font information to get the english name of.
 
 * @return the English name (if it could be found).
 
 */
 
static const char *GetEnglishFontName(const ENUMLOGFONTEX *logfont)
 
{
 
	static char font_name[MAX_PATH];
 
	const char *ret_font_name = NULL;
 
	uint pos = 0;
 
	HDC dc;
 
	HGDIOBJ oldfont;
 
	byte *buf;
 
	DWORD dw;
 
	uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset;
 

	
 
	HFONT font = CreateFontIndirect(&logfont->elfLogFont);
 
	if (font == NULL) goto err1;
 

	
 
	dc = GetDC(NULL);
 
	oldfont = SelectObject(dc, font);
 
	dw = GetFontData(dc, 'eman', 0, NULL, 0);
 
	if (dw == GDI_ERROR) goto err2;
 

	
 
	buf = MallocT<byte>(dw);
 
	dw = GetFontData(dc, 'eman', 0, buf, dw);
 
	if (dw == GDI_ERROR) goto err3;
 

	
 
	format = buf[pos++] << 8;
src/gamelog.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file gamelog.cpp Definition of functions used for logging of important changes in the game */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "saveload/saveload.h"
 
#include "core/alloc_func.hpp"
 
#include "variables.h"
 
#include "string_func.h"
 
#include "settings_type.h"
 
#include "gamelog_internal.h"
 
#include "console_func.h"
 
#include "debug.h"
 
#include "rev.h"
 

	
 
#include <stdarg.h>
 

	
 
extern const uint16 SAVEGAME_VERSION;  ///< current savegame version
 

	
 
extern SavegameType _savegame_type; ///< type of savegame we are loading
 

	
 
extern uint32 _ttdp_version;     ///< version of TTDP savegame (if applicable)
 
extern uint16 _sl_version;       ///< the major savegame version identifier
 
extern byte   _sl_minor_version; ///< the minor savegame version, DO NOT USE!
 

	
 

	
 
static GamelogActionType _gamelog_action_type = GLAT_NONE; ///< action to record if anything changes
 

	
 
LoggedAction *_gamelog_action = NULL;        ///< first logged action
 
uint _gamelog_actions         = 0;           ///< number of actions
 
static LoggedAction *_current_action = NULL; ///< current action we are logging, NULL when there is no action active
 

	
 

	
 
/** Stores information about new action, but doesn't allocate it
 
 * Action is allocated only when there is at least one change
 
 * @param at type of action
 
 */
 
void GamelogStartAction(GamelogActionType at)
 
{
 
	assert(_gamelog_action_type == GLAT_NONE); // do not allow starting new action without stopping the previous first
 
	_gamelog_action_type = at;
 
}
 

	
 
/** Stops logging of any changes
 
 */
 
void GamelogStopAction()
 
{
 
	assert(_gamelog_action_type != GLAT_NONE); // nobody should try to stop if there is no action in progress
 

	
 
	bool print = _current_action != NULL;
 

	
 
	_current_action = NULL;
 
	_gamelog_action_type = GLAT_NONE;
 

	
 
	if (print) GamelogPrintDebug(5);
 
}
 

	
 
/** Resets and frees all memory allocated - used before loading or starting a new game
 
 */
 
void GamelogReset()
 
{
 
	assert(_gamelog_action_type == GLAT_NONE);
 

	
 
	for (uint i = 0; i < _gamelog_actions; i++) {
 
		const LoggedAction *la = &_gamelog_action[i];
 
		for (uint j = 0; j < la->changes; j++) {
 
			const LoggedChange *lc = &la->change[j];
 
			if (lc->ct == GLCT_SETTING) free(lc->setting.name);
 
		}
 
		free(la->change);
 
	}
 

	
 
	free(_gamelog_action);
 

	
 
	_gamelog_action  = NULL;
 
	_gamelog_actions = 0;
 
	_current_action  = NULL;
 
}
 

	
 
enum {
 
	GAMELOG_BUF_LEN = 1024 ///< length of buffer for one line of text
 
};
 
static const uint GAMELOG_BUF_LEN = 1024; ///< length of buffer for one line of text
 

	
 
static int _dbgofs = 0; ///< offset in current output buffer
 
static uint _dbgofs = 0; ///< offset in current output buffer
 

	
 
static void AddDebugText(char *buf, const char *s, ...) WARN_FORMAT(2, 3);
 

	
 
static void AddDebugText(char *buf, const char *s, ...)
 
{
 
	if (GAMELOG_BUF_LEN <= _dbgofs) return;
 

	
 
	va_list va;
 

	
 
	va_start(va, s);
 
	_dbgofs += vsnprintf(buf + _dbgofs, GAMELOG_BUF_LEN - _dbgofs, s, va);
 
	va_end(va);
 
}
 

	
 

	
 
/** Prints GRF filename if found
 
 * @param buf The location in the _dbgofs buffer to draw
 
 * @param grfid GRF which filename to print
 
 */
 
static void PrintGrfFilename(char *buf, uint grfid)
 
{
 
	const GRFConfig *gc = FindGRFConfig(grfid);
 

	
 
	if (gc == NULL) return;
 

	
 
	AddDebugText(buf, ", filename: %s", gc->filename);
 
}
 

	
 
/** Prints GRF ID, checksum and filename if found
 
 * @param buf The location in the _dbgofs buffer to draw
 
 * @param grfid GRF ID
 
 * @param md5sum array of md5sum to print
 
 */
 
static void PrintGrfInfo(char *buf, uint grfid, const uint8 *md5sum)
 
{
 
	char txt[40];
 

	
 
	md5sumToString(txt, lastof(txt), md5sum);
 

	
 
	AddDebugText(buf, "GRF ID %08X, checksum %s", BSWAP32(grfid), txt);
 

	
 
	PrintGrfFilename(buf, grfid);
 

	
 
	return;
 
}
 

	
 

	
 
/** Text messages for various logged actions */
 
static const char * const la_text[] = {
 
	"new game started",
 
	"game loaded",
 
	"GRF config changed",
 
	"cheat was used",
 
	"settings changed",
 
	"GRF bug triggered",
 
	"emergency savegame",
 
};
 

	
 
assert_compile(lengthof(la_text) == GLAT_END);
 

	
 

	
 
/**
 
 * Prints active gamelog
 
 * @param proc the procedure to draw with
 
 */
 
void GamelogPrint(GamelogPrintProc *proc)
 
{
 
	char buf[GAMELOG_BUF_LEN];
 

	
 
	proc("---- gamelog start ----");
 

	
 
	const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
 

	
 
	for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
 
		assert((uint)la->at < GLAT_END);
 

	
 
		snprintf(buf, GAMELOG_BUF_LEN, "Tick %u: %s", (uint)la->tick, la_text[(uint)la->at]);
 
		proc(buf);
 

	
 
		const LoggedChange *lcend = &la->change[la->changes];
 

	
 
		for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
 
			_dbgofs = 0;
 
			AddDebugText(buf, "     ");
 

	
 
			switch (lc->ct) {
 
				default: NOT_REACHED();
 
				case GLCT_MODE:
 
					AddDebugText(buf, "New game mode: %u landscape: %u",
 
						(uint)lc->mode.mode, (uint)lc->mode.landscape);
 
					break;
 

	
 
				case GLCT_REVISION:
 
					AddDebugText(buf, "Revision text changed to %s, savegame version %u, ",
 
						lc->revision.text, lc->revision.slver);
 

	
 
					switch (lc->revision.modified) {
 
						case 0: AddDebugText(buf, "not "); break;
 
						case 1: AddDebugText(buf, "maybe "); break;
 
						default: break;
 
					}
 

	
 
					AddDebugText(buf, "modified, _openttd_newgrf_version = 0x%08x", lc->revision.newgrf);
 
					break;
 

	
 
				case GLCT_OLDVER:
 
					AddDebugText(buf, "Conversion from ");
 
					switch (lc->oldver.type) {
 
						default: NOT_REACHED();
 
						case SGT_OTTD:
 
							AddDebugText(buf, "OTTD savegame without gamelog: version %u, %u",
 
								GB(lc->oldver.version, 8, 16), GB(lc->oldver.version, 0, 8));
 
							break;
 

	
 
						case SGT_TTO:
 
							AddDebugText(buf, "TTO savegame");
 
							break;
 

	
 
						case SGT_TTD:
 
							AddDebugText(buf, "TTD savegame");
 
							break;
 

	
 
						case SGT_TTDP1:
 
						case SGT_TTDP2:
 
							AddDebugText(buf, "TTDP savegame, %s format",
 
								lc->oldver.type == SGT_TTDP1 ? "old" : "new");
 
							if (lc->oldver.version != 0) {
 
								AddDebugText(buf, ", TTDP version %u.%u.%u.%u",
 
									GB(lc->oldver.version, 24, 8), GB(lc->oldver.version, 20, 4),
 
									GB(lc->oldver.version, 16, 4), GB(lc->oldver.version, 0, 16));
 
							}
 
							break;
 
					}
 
					break;
 

	
 
				case GLCT_SETTING:
 
					AddDebugText(buf, "Setting changed: %s : %d -> %d", lc->setting.name, lc->setting.oldval, lc->setting.newval);
 
					break;
 

	
 
				case GLCT_GRFADD:
 
					AddDebugText(buf, "Added NewGRF: ");
 
					PrintGrfInfo(buf, lc->grfadd.grfid, lc->grfadd.md5sum);
 
					break;
 

	
 
				case GLCT_GRFREM:
 
					AddDebugText(buf, "Removed NewGRF: %08X", BSWAP32(lc->grfrem.grfid));
 
					PrintGrfFilename(buf, lc->grfrem.grfid);
 
					break;
 

	
 
				case GLCT_GRFCOMPAT:
 
					AddDebugText(buf, "Compatible NewGRF loaded: ");
 
					PrintGrfInfo(buf, lc->grfcompat.grfid, lc->grfcompat.md5sum);
 
					break;
 

	
 
				case GLCT_GRFPARAM:
 
					AddDebugText(buf, "GRF parameter changed: %08X", BSWAP32(lc->grfparam.grfid));
 
					PrintGrfFilename(buf, lc->grfparam.grfid);
 
					break;
 

	
 
				case GLCT_GRFMOVE:
 
					AddDebugText(buf, "GRF order changed: %08X moved %d places %s",
 
						BSWAP32(lc->grfmove.grfid), abs(lc->grfmove.offset), lc->grfmove.offset >= 0 ? "down" : "up" );
 
					PrintGrfFilename(buf, lc->grfmove.grfid);
 
					break;
 

	
 
				case GLCT_GRFBUG:
 
					switch (lc->grfbug.bug) {
 
						default: NOT_REACHED();
 
						case GBUG_VEH_LENGTH:
 
							AddDebugText(buf, "Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X", BSWAP32(lc->grfbug.grfid), (uint)lc->grfbug.data);
 
							PrintGrfFilename(buf, lc->grfbug.grfid);
 
							break;
 
					}
 

	
 
				case GLCT_EMERGENCY:
 
					break;
 
			}
 

	
 
			proc(buf);
 
		}
 
	}
 

	
 
	proc("---- gamelog end ----");
 
}
 

	
 

	
 
static void GamelogPrintConsoleProc(const char *s)
 
{
 
	IConsolePrint(CC_WARNING, s);
 
}
 

	
 
void GamelogPrintConsole()
src/gfx.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file gfx.cpp Handling of drawing text and other gfx related stuff. */
 

	
 
#include "stdafx.h"
 
#include "gfx_func.h"
 
#include "variables.h"
 
#include "fontcache.h"
 
#include "genworld.h"
 
#include "zoom_func.h"
 
#include "blitter/factory.hpp"
 
#include "video/video_driver.hpp"
 
#include "strings_func.h"
 
#include "settings_type.h"
 
#include "landscape_type.h"
 
#include "network/network.h"
 
#include "network/network_func.h"
 
#include "thread/thread.h"
 
#include "window_func.h"
 
#include "newgrf_debug.h"
 

	
 
#include "table/palettes.h"
 
#include "table/sprites.h"
 
#include "table/control_codes.h"
 

	
 
byte _dirkeys;        ///< 1 = left, 2 = up, 4 = right, 8 = down
 
bool _fullscreen;
 
CursorVars _cursor;
 
bool _ctrl_pressed;   ///< Is Ctrl pressed?
 
bool _shift_pressed;  ///< Is Shift pressed?
 
byte _fast_forward;
 
bool _left_button_down;     ///< Is left mouse button pressed?
 
bool _left_button_clicked;  ///< Is left mouse button clicked?
 
bool _right_button_down;    ///< Is right mouse button pressed?
 
bool _right_button_clicked; ///< Is right mouse button clicked?
 
DrawPixelInfo _screen;
 
bool _screen_disable_anim = false;   ///< Disable palette animation (important for 32bpp-anim blitter during giant screenshot)
 
bool _exit_game;
 
GameMode _game_mode;
 
SwitchMode _switch_mode;  ///< The next mainloop command.
 
PauseModeByte _pause_mode;
 
int _pal_first_dirty;
 
int _pal_count_dirty;
 

	
 
Colour _cur_palette[256];
 

	
 
static int _max_char_height; ///< Cache of the height of the largest font
 
static int _max_char_width;  ///< Cache of the width of the largest font
 
static byte _stringwidth_table[FS_END][224]; ///< Cache containing width of often used characters. @see GetCharacterWidth()
 
DrawPixelInfo *_cur_dpi;
 
byte _colour_gradient[COLOUR_END][8];
 

	
 
static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = NULL, SpriteID sprite_id = SPR_CURSOR_MOUSE);
 

	
 
FontSize _cur_fontsize;
 
static FontSize _last_fontsize;
 
static ReusableBuffer<uint8> _cursor_backup;
 

	
 
/**
 
 * The rect for repaint.
 
 *
 
 * This rectangle defines the area which should be repaint by the video driver.
 
 *
 
 * @ingroup dirty
 
 */
 
static Rect _invalid_rect;
 
static const byte *_colour_remap_ptr;
 
static byte _string_colourremap[3]; ///< Recoloursprite for stringdrawing. The grf loader ensures, that ST_FONT sprites only use colours 0 to 2.
 

	
 
enum {
 
	DIRTY_BLOCK_HEIGHT   = 8,
 
	DIRTY_BLOCK_WIDTH    = 64,
 
};
 
static const uint DIRTY_BLOCK_HEIGHT   = 8;
 
static const uint DIRTY_BLOCK_WIDTH    = 64;
 

	
 
static uint _dirty_bytes_per_line = 0;
 
static byte *_dirty_blocks = NULL;
 

	
 
void GfxScroll(int left, int top, int width, int height, int xo, int yo)
 
{
 
	Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
 

	
 
	if (xo == 0 && yo == 0) return;
 

	
 
	if (_cursor.visible) UndrawMouseCursor();
 

	
 
#ifdef ENABLE_NETWORK
 
	if (_networking) NetworkUndrawChatMessage();
 
#endif /* ENABLE_NETWORK */
 

	
 
	blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
 
	/* This part of the screen is now dirty. */
 
	_video_driver->MakeDirty(left, top, width, height);
 
}
 

	
 

	
 
/**
 
 * Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
 
 *
 
 * @pre dpi->zoom == ZOOM_LVL_NORMAL, right >= left, bottom >= top
 
 * @param left Minimum X (inclusive)
 
 * @param top Minimum Y (inclusive)
 
 * @param right Maximum X (inclusive)
 
 * @param bottom Maximum Y (inclusive)
 
 * @param colour A 8 bit palette index (FILLRECT_OPAQUE and FILLRECT_CHECKER) or a recolour spritenumber (FILLRECT_RECOLOUR)
 
 * @param mode
 
 *         FILLRECT_OPAQUE:   Fill the rectangle with the specified colour
 
 *         FILLRECT_CHECKER:  Like FILLRECT_OPAQUE, but only draw every second pixel (used to grey out things)
 
 *         FILLRECT_RECOLOUR:  Apply a recolour sprite to every pixel in the rectangle currently on screen
 
 */
 
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
 
{
 
	Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
 
	const DrawPixelInfo *dpi = _cur_dpi;
 
	void *dst;
 
	const int otop = top;
 
	const int oleft = left;
 

	
 
	if (dpi->zoom != ZOOM_LVL_NORMAL) return;
 
	if (left > right || top > bottom) return;
 
	if (right < dpi->left || left >= dpi->left + dpi->width) return;
 
	if (bottom < dpi->top || top >= dpi->top + dpi->height) return;
 

	
 
	if ( (left -= dpi->left) < 0) left = 0;
 
	right = right - dpi->left + 1;
 
	if (right > dpi->width) right = dpi->width;
 
	right -= left;
 
	assert(right > 0);
 

	
 
	if ( (top -= dpi->top) < 0) top = 0;
 
	bottom = bottom - dpi->top + 1;
 
	if (bottom > dpi->height) bottom = dpi->height;
 
	bottom -= top;
 
	assert(bottom > 0);
 

	
 
	dst = blitter->MoveTo(dpi->dst_ptr, left, top);
 

	
 
	switch (mode) {
 
		default: // FILLRECT_OPAQUE
 
			blitter->DrawRect(dst, right, bottom, (uint8)colour);
 
			break;
 

	
 
		case FILLRECT_RECOLOUR:
 
			blitter->DrawColourMappingRect(dst, right, bottom, GB(colour, 0, PALETTE_WIDTH));
 
			break;
 

	
 
		case FILLRECT_CHECKER: {
 
			byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
 
			do {
 
				for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8)colour);
 
				dst = blitter->MoveTo(dst, 0, 1);
 
			} while (--bottom > 0);
 
			break;
 
		}
 
	}
 
}
 

	
 
void GfxDrawLine(int x, int y, int x2, int y2, int colour)
 
{
 
	Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
 
	DrawPixelInfo *dpi = _cur_dpi;
 

	
 
	x -= dpi->left;
 
	x2 -= dpi->left;
 
	y -= dpi->top;
 
	y2 -= dpi->top;
 

	
 
	/* Check clipping */
 
	if (x < 0 && x2 < 0) return;
 
	if (y < 0 && y2 < 0) return;
 
	if (x > dpi->width  && x2 > dpi->width)  return;
 
	if (y > dpi->height && y2 > dpi->height) return;
 

	
 
	blitter->DrawLine(dpi->dst_ptr, x, y, x2, y2, dpi->width, dpi->height, colour);
 
}
 

	
 
void GfxDrawLineUnscaled(int x, int y, int x2, int y2, int colour)
 
{
 
	Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
 
	DrawPixelInfo *dpi = _cur_dpi;
 

	
 
	x -= dpi->left;
 
	x2 -= dpi->left;
 
	y -= dpi->top;
 
	y2 -= dpi->top;
 

	
 
	/* Check clipping */
 
	if (x < 0 && x2 < 0) return;
 
	if (y < 0 && y2 < 0) return;
 
	if (x > dpi->width  && x2 > dpi->width)  return;
 
	if (y > dpi->height && y2 > dpi->height) return;
 

	
 
	blitter->DrawLine(dpi->dst_ptr, UnScaleByZoom(x, dpi->zoom), UnScaleByZoom(y, dpi->zoom),
 
			UnScaleByZoom(x2, dpi->zoom), UnScaleByZoom(y2, dpi->zoom),
 
			UnScaleByZoom(dpi->width, dpi->zoom), UnScaleByZoom(dpi->height, dpi->zoom), colour);
 
}
 

	
 
/**
 
 * Draws the projection of a parallelepiped.
 
 * This can be used to draw boxes in world coordinates.
 
 *
 
 * @param x   Screen X-coordinate of top front corner.
 
 * @param y   Screen Y-coordinate of top front corner.
 
 * @param dx1 Screen X-length of first edge.
 
 * @param dy1 Screen Y-length of first edge.
 
 * @param dx2 Screen X-length of second edge.
 
 * @param dy2 Screen Y-length of second edge.
 
 * @param dx3 Screen X-length of third edge.
 
 * @param dy3 Screen Y-length of third edge.
 
 */
 
void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
 
{
 
	/*           ....
 
	 *         ..    ....
 
	 *       ..          ....
 
	 *     ..                ^
 
	 *   <--__(dx1,dy1)    /(dx2,dy2)
 
	 *   :    --__       /   :
 
	 *   :        --__ /     :
 
	 *   :            *(x,y) :
 
	 *   :            |      :
 
	 *   :            |     ..
 
	 *    ....        |(dx3,dy3)
 
	 *        ....    | ..
 
	 *            ....V.
 
	 */
 

	
 
	static const byte colour = 255;
 

	
 
	GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
 
	GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
 
	GfxDrawLineUnscaled(x, y, x + dx3, y + dy3, colour);
 

	
 
	GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx2, y + dy1 + dy2, colour);
 
	GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx3, y + dy1 + dy3, colour);
 
	GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx1, y + dy2 + dy1, colour);
 
	GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx3, y + dy2 + dy3, colour);
 
	GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx1, y + dy3 + dy1, colour);
 
	GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx2, y + dy3 + dy2, colour);
 
}
 

	
 
/**
 
 * Set the colour remap to be for the given colour.
 
 * @param colour the new colour of the remap.
 
 */
 
static void SetColourRemap(TextColour colour)
 
{
 
	if (colour == TC_INVALID) return;
 

	
 
	if (colour & IS_PALETTE_COLOUR) {
 
		_string_colourremap[1] = colour & ~IS_PALETTE_COLOUR;
 
		_string_colourremap[2] = (_use_palette == PAL_DOS) ? 1 : 215;
 
	} else {
 
		_string_colourremap[1] = _string_colourmap[_use_palette][colour].text;
 
		_string_colourremap[2] = _string_colourmap[_use_palette][colour].shadow;
 
	}
 
	_colour_remap_ptr = _string_colourremap;
 
}
 

	
 
#if !defined(WITH_ICU)
 
typedef WChar UChar;
 
static UChar *HandleBiDiAndArabicShapes(UChar *text) { return text; }
 
#else
 
#include <unicode/ubidi.h>
 
#include <unicode/ushape.h>
 

	
 
/**
src/industry_cmd.cpp
Show inline comments
 
@@ -1998,388 +1998,386 @@ static void MaybeNewIndustry()
 

	
 
		/* If there is no Callback CBID_INDUSTRY_AVAILABLE or if this one did anot failed,
 
		 * and if appearing chance for this landscape is above 0, this industry can be chosen */
 
		if (CheckIfCallBackAllowsAvailability(j, IACT_RANDOMCREATION)) {
 
			probability_max += chance;
 
			/* adds the result for this industry */
 
			cumulative_probs[num].ind = j;
 
			cumulative_probs[num++].prob = probability_max;
 
		}
 
	}
 

	
 
	/* Abort if there is no industry buildable */
 
	if (probability_max == 0) return;
 

	
 
	/* Find a random type, with maximum being what has been evaluate above*/
 
	rndtype = RandomRange(probability_max);
 
	for (j = 0; j < NUM_INDUSTRYTYPES; j++) {
 
		/* and choose the index of the industry that matches as close as possible this random type */
 
		if (cumulative_probs[j].prob >= rndtype) break;
 
	}
 

	
 
	ind_spc = GetIndustrySpec(cumulative_probs[j].ind);
 
	/*  Check if it is allowed */
 
	if ((ind_spc->behaviour & INDUSTRYBEH_BEFORE_1950) && _cur_year > 1950) return;
 
	if ((ind_spc->behaviour & INDUSTRYBEH_AFTER_1960) && _cur_year < 1960) return;
 

	
 
	/* try to create 2000 times this industry */
 
	num = 2000;
 
	for (;;) {
 
		ind = CreateNewIndustry(RandomTile(), cumulative_probs[j].ind);
 
		if (ind != NULL) break;
 
		if (--num == 0) return;
 
	}
 

	
 
	SetDParam(0, ind_spc->name);
 
	if (ind_spc->new_industry_text > STR_LAST_STRINGID) {
 
		SetDParam(1, STR_TOWN_NAME);
 
		SetDParam(2, ind->town->index);
 
	} else {
 
		SetDParam(1, ind->town->index);
 
	}
 
	AddIndustryNewsItem(ind_spc->new_industry_text, NS_INDUSTRY_OPEN, ind->index);
 
	AI::BroadcastNewEvent(new AIEventIndustryOpen(ind->index));
 
}
 

	
 
/**
 
 * Protects an industry from closure if the appropriate flags and conditions are met
 
 * INDUSTRYBEH_CANCLOSE_LASTINSTANCE must be set (which, by default, it is not) and the
 
 * count of industries of this type must one (or lower) in order to be protected
 
 * against closure.
 
 * @param type IndustryType been queried
 
 * @result true if protection is on, false otherwise (except for oil wells)
 
 */
 
static bool CheckIndustryCloseDownProtection(IndustryType type)
 
{
 
	const IndustrySpec *indspec = GetIndustrySpec(type);
 

	
 
	/* oil wells (or the industries with that flag set) are always allowed to closedown */
 
	if ((indspec->behaviour & INDUSTRYBEH_DONT_INCR_PROD) && _settings_game.game_creation.landscape == LT_TEMPERATE) return false;
 
	return (indspec->behaviour & INDUSTRYBEH_CANCLOSE_LASTINSTANCE) == 0 && Industry::GetIndustryTypeCount(type) <= 1;
 
}
 

	
 
/**
 
 * Can given cargo type be accepted or produced by the industry?
 
 * @param cargo: Cargo type
 
 * @param ind: Industry
 
 * @param *c_accepts: Pointer to boolean for acceptance of cargo
 
 * @param *c_produces: Pointer to boolean for production of cargo
 
 * @return: \c *c_accepts is set when industry accepts the cargo type,
 
 *          \c *c_produces is set when the industry produces the cargo type
 
 */
 
static void CanCargoServiceIndustry(CargoID cargo, Industry *ind, bool *c_accepts, bool *c_produces)
 
{
 
	const IndustrySpec *indspec = GetIndustrySpec(ind->type);
 

	
 
	/* Check for acceptance of cargo */
 
	for (byte j = 0; j < lengthof(ind->accepts_cargo); j++) {
 
		if (ind->accepts_cargo[j] == CT_INVALID) continue;
 
		if (cargo == ind->accepts_cargo[j]) {
 
			if (HasBit(indspec->callback_mask, CBM_IND_REFUSE_CARGO)) {
 
				uint16 res = GetIndustryCallback(CBID_INDUSTRY_REFUSE_CARGO,
 
						0, GetReverseCargoTranslation(cargo, indspec->grf_prop.grffile),
 
						ind, ind->type, ind->location.tile);
 
				if (res == 0) continue;
 
			}
 
			*c_accepts = true;
 
			break;
 
		}
 
	}
 

	
 
	/* Check for produced cargo */
 
	for (byte j = 0; j < lengthof(ind->produced_cargo); j++) {
 
		if (ind->produced_cargo[j] == CT_INVALID) continue;
 
		if (cargo == ind->produced_cargo[j]) {
 
			*c_produces = true;
 
			break;
 
		}
 
	}
 
}
 

	
 
/**
 
 * Compute who can service the industry.
 
 *
 
 * Here, 'can service' means that he/she has trains and stations close enough
 
 * to the industry with the right cargo type and the right orders (ie has the
 
 * technical means).
 
 *
 
 * @param ind: Industry being investigated.
 
 *
 
 * @return: 0 if nobody can service the industry, 2 if the local company can
 
 * service the industry, and 1 otherwise (only competitors can service the
 
 * industry)
 
 */
 
static int WhoCanServiceIndustry(Industry *ind)
 
{
 
	/* Find all stations within reach of the industry */
 
	StationList stations;
 
	FindStationsAroundTiles(ind->location, &stations);
 

	
 
	if (stations.Length() == 0) return 0; // No stations found at all => nobody services
 

	
 
	const Vehicle *v;
 
	int result = 0;
 
	FOR_ALL_VEHICLES(v) {
 
		/* Is it worthwhile to try this vehicle? */
 
		if (v->owner != _local_company && result != 0) continue;
 

	
 
		/* Check whether it accepts the right kind of cargo */
 
		bool c_accepts = false;
 
		bool c_produces = false;
 
		if (v->type == VEH_TRAIN && Train::From(v)->IsFrontEngine()) {
 
			for (const Vehicle *u = v; u != NULL; u = u->Next()) {
 
				CanCargoServiceIndustry(u->cargo_type, ind, &c_accepts, &c_produces);
 
			}
 
		} else if (v->type == VEH_ROAD || v->type == VEH_SHIP || v->type == VEH_AIRCRAFT) {
 
			CanCargoServiceIndustry(v->cargo_type, ind, &c_accepts, &c_produces);
 
		} else {
 
			continue;
 
		}
 
		if (!c_accepts && !c_produces) continue; // Wrong cargo
 

	
 
		/* Check orders of the vehicle.
 
		 * We cannot check the first of shared orders only, since the first vehicle in such a chain
 
		 * may have a different cargo type.
 
		 */
 
		const Order *o;
 
		FOR_VEHICLE_ORDERS(v, o) {
 
			if (o->IsType(OT_GOTO_STATION) && !(o->GetUnloadType() & OUFB_TRANSFER)) {
 
				/* Vehicle visits a station to load or unload */
 
				Station *st = Station::Get(o->GetDestination());
 
				assert(st != NULL);
 

	
 
				/* Same cargo produced by industry is dropped here => not serviced by vehicle v */
 
				if ((o->GetUnloadType() & OUFB_UNLOAD) && !c_accepts) break;
 

	
 
				if (stations.Contains(st)) {
 
					if (v->owner == _local_company) return 2; // Company services industry
 
					result = 1; // Competitor services industry
 
				}
 
			}
 
		}
 
	}
 
	return result;
 
}
 

	
 
/**
 
 * Report news that industry production has changed significantly
 
 *
 
 * @param ind: Industry with changed production
 
 * @param type: Cargo type that has changed
 
 * @param percent: Percentage of change (>0 means increase, <0 means decrease)
 
 */
 
static void ReportNewsProductionChangeIndustry(Industry *ind, CargoID type, int percent)
 
{
 
	NewsSubtype ns;
 

	
 
	switch (WhoCanServiceIndustry(ind)) {
 
		case 0: ns = NS_INDUSTRY_NOBODY;  break;
 
		case 1: ns = NS_INDUSTRY_OTHER;   break;
 
		case 2: ns = NS_INDUSTRY_COMPANY; break;
 
		default: NOT_REACHED();
 
	}
 
	SetDParam(2, abs(percent));
 
	SetDParam(0, CargoSpec::Get(type)->name);
 
	SetDParam(1, ind->index);
 
	AddIndustryNewsItem(
 
		percent >= 0 ? STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_SMOOTH : STR_NEWS_INDUSTRY_PRODUCTION_DECREASE_SMOOTH,
 
		ns,
 
		ind->index
 
	);
 
}
 

	
 
enum {
 
	PERCENT_TRANSPORTED_60 = 153,
 
	PERCENT_TRANSPORTED_80 = 204,
 
};
 
static const uint PERCENT_TRANSPORTED_60 = 153;
 
static const uint PERCENT_TRANSPORTED_80 = 204;
 

	
 
/** Change industry production or do closure
 
 * @param i Industry for which changes are performed
 
 * @param monthly true if it's the monthly call, false if it's the random call
 
 */
 
static void ChangeIndustryProduction(Industry *i, bool monthly)
 
{
 
	StringID str = STR_NULL;
 
	bool closeit = false;
 
	const IndustrySpec *indspec = GetIndustrySpec(i->type);
 
	bool standard = false;
 
	bool suppress_message = false;
 
	bool recalculate_multipliers = false; ///< reinitialize production_rate to match prod_level
 
	/* don't use smooth economy for industries using production related callbacks */
 
	bool smooth_economy = _settings_game.economy.smooth_economy &&
 
	                      !(HasBit(indspec->callback_mask, CBM_IND_PRODUCTION_256_TICKS) || HasBit(indspec->callback_mask, CBM_IND_PRODUCTION_CARGO_ARRIVAL)) && // production callbacks
 
	                      !(HasBit(indspec->callback_mask, CBM_IND_MONTHLYPROD_CHANGE) || HasBit(indspec->callback_mask, CBM_IND_PRODUCTION_CHANGE));            // production change callbacks
 
	byte div = 0;
 
	byte mul = 0;
 
	int8 increment = 0;
 

	
 
	bool callback_enabled = HasBit(indspec->callback_mask, monthly ? CBM_IND_MONTHLYPROD_CHANGE : CBM_IND_PRODUCTION_CHANGE);
 
	if (callback_enabled) {
 
		uint16 res = GetIndustryCallback(monthly ? CBID_INDUSTRY_MONTHLYPROD_CHANGE : CBID_INDUSTRY_PRODUCTION_CHANGE, 0, Random(), i, i->type, i->location.tile);
 
		if (res != CALLBACK_FAILED) { // failed callback means "do nothing"
 
			suppress_message = HasBit(res, 7);
 
			/* Get the custom message if any */
 
			if (HasBit(res, 8)) str = MapGRFStringID(indspec->grf_prop.grffile->grfid, GB(GetRegister(0x100), 0, 16));
 
			res = GB(res, 0, 4);
 
			switch (res) {
 
				default: NOT_REACHED();
 
				case 0x0: break;                  // Do nothing, but show the custom message if any
 
				case 0x1: div = 1; break;         // Halve industry production. If production reaches the quarter of the default, the industry is closed instead.
 
				case 0x2: mul = 1; break;         // Double industry production if it hasn't reached eight times of the original yet.
 
				case 0x3: closeit = true; break;  // The industry announces imminent closure, and is physically removed from the map next month.
 
				case 0x4: standard = true; break; // Do the standard random production change as if this industry was a primary one.
 
				case 0x5: case 0x6: case 0x7:     // Divide production by 4, 8, 16
 
				case 0x8: div = res - 0x3; break; // Divide production by 32
 
				case 0x9: case 0xA: case 0xB:     // Multiply production by 4, 8, 16
 
				case 0xC: mul = res - 0x7; break; // Multiply production by 32
 
				case 0xD:                         // decrement production
 
				case 0xE:                         // increment production
 
					increment = res == 0x0D ? -1 : 1;
 
					break;
 
				case 0xF:                         // Set production to third byte of register 0x100
 
					i->prod_level = Clamp(GB(GetRegister(0x100), 16, 8), PRODLEVEL_MINIMUM, PRODLEVEL_MAXIMUM);
 
					recalculate_multipliers = true;
 
					break;
 
			}
 
		}
 
	} else {
 
		if (monthly != smooth_economy) return;
 
		if (indspec->life_type == INDUSTRYLIFE_BLACK_HOLE) return;
 
	}
 

	
 
	if (standard || (!callback_enabled && (indspec->life_type & (INDUSTRYLIFE_ORGANIC | INDUSTRYLIFE_EXTRACTIVE)) != 0)) {
 
		/* decrease or increase */
 
		bool only_decrease = (indspec->behaviour & INDUSTRYBEH_DONT_INCR_PROD) && _settings_game.game_creation.landscape == LT_TEMPERATE;
 

	
 
		if (smooth_economy) {
 
			closeit = true;
 
			for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
 
				if (i->produced_cargo[j] == CT_INVALID) continue;
 
				uint32 r = Random();
 
				int old_prod, new_prod, percent;
 
				/* If over 60% is transported, mult is 1, else mult is -1. */
 
				int mult = (i->last_month_pct_transported[j] > PERCENT_TRANSPORTED_60) ? 1 : -1;
 

	
 
				new_prod = old_prod = i->production_rate[j];
 

	
 
				/* For industries with only_decrease flags (temperate terrain Oil Wells),
 
				 * the multiplier will always be -1 so they will only decrease. */
 
				if (only_decrease) {
 
					mult = -1;
 
				/* For normal industries, if over 60% is transported, 33% chance for decrease.
 
				 * Bonus for very high station ratings (over 80%): 16% chance for decrease. */
 
				} else if (Chance16I(1, ((i->last_month_pct_transported[j] > PERCENT_TRANSPORTED_80) ? 6 : 3), r)) {
 
					mult *= -1;
 
				}
 

	
 
				/* 4.5% chance for 3-23% (or 1 unit for very low productions) production change,
 
				 * determined by mult value. If mult = 1 prod. increases, else (-1) it decreases. */
 
				if (Chance16I(1, 22, r >> 16)) {
 
					new_prod += mult * (max(((RandomRange(50) + 10) * old_prod) >> 8, 1U));
 
				}
 

	
 
				/* Prevent production to overflow or Oil Rig passengers to be over-"produced" */
 
				new_prod = Clamp(new_prod, 1, 255);
 

	
 
				if (((indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0) && j == 1)
 
					new_prod = Clamp(new_prod, 0, 16);
 

	
 
				/* Do not stop closing the industry when it has the lowest possible production rate */
 
				if (new_prod == old_prod && old_prod > 1) {
 
					closeit = false;
 
					continue;
 
				}
 

	
 
				percent = (old_prod == 0) ? 100 : (new_prod * 100 / old_prod - 100);
 
				i->production_rate[j] = new_prod;
 

	
 
				/* Close the industry when it has the lowest possible production rate */
 
				if (new_prod > 1) closeit = false;
 

	
 
				if (abs(percent) >= 10) {
 
					ReportNewsProductionChangeIndustry(i, i->produced_cargo[j], percent);
 
				}
 
			}
 
		} else {
 
			if (only_decrease || Chance16(1, 3)) {
 
				/* If more than 60% transported, 66% chance of increase, else 33% chance of increase */
 
				if (!only_decrease && (i->last_month_pct_transported[0] > PERCENT_TRANSPORTED_60) != Chance16(1, 3)) {
 
					mul = 1; // Increase production
 
				} else {
 
					div = 1; // Decrease production
 
				}
 
			}
 
		}
 
	}
 

	
 
	if (!callback_enabled && (indspec->life_type & INDUSTRYLIFE_PROCESSING)) {
 
		if ( (byte)(_cur_year - i->last_prod_year) >= 5 && Chance16(1, smooth_economy ? 180 : 2)) {
 
			closeit = true;
 
		}
 
	}
 

	
 
	/* Increase if needed */
 
	while (mul-- != 0 && i->prod_level < PRODLEVEL_MAXIMUM) {
 
		i->prod_level = min(i->prod_level * 2, PRODLEVEL_MAXIMUM);
 
		recalculate_multipliers = true;
 
		if (str == STR_NULL) str = indspec->production_up_text;
 
	}
 

	
 
	/* Decrease if needed */
 
	while (div-- != 0 && !closeit) {
 
		if (i->prod_level == PRODLEVEL_MINIMUM) {
 
			closeit = true;
 
		} else {
 
			i->prod_level = max(i->prod_level / 2, (int)PRODLEVEL_MINIMUM); // typecast to int required to please MSVC
 
			recalculate_multipliers = true;
 
			if (str == STR_NULL) str = indspec->production_down_text;
 
		}
 
	}
 

	
 
	/* Increase or Decreasing the production level if needed */
 
	if (increment != 0) {
 
		if (increment < 0 && i->prod_level == PRODLEVEL_MINIMUM) {
 
			closeit = true;
 
		} else {
 
			i->prod_level = ClampU(i->prod_level + increment, PRODLEVEL_MINIMUM, PRODLEVEL_MAXIMUM);
 
			recalculate_multipliers = true;
 
		}
 
	}
 

	
 
	/* Recalculate production_rate
 
	 * For non-smooth economy these should always be synchronized with prod_level */
 
	if (recalculate_multipliers) {
 
		/* Rates are rounded up, so e.g. oilrig always produces some passengers */
 
		i->production_rate[0] = min(CeilDiv(indspec->production_rate[0] * i->prod_level, PRODLEVEL_DEFAULT), 0xFF);
 
		i->production_rate[1] = min(CeilDiv(indspec->production_rate[1] * i->prod_level, PRODLEVEL_DEFAULT), 0xFF);
 
	}
 

	
 
	/* Close if needed and allowed */
 
	if (closeit && !CheckIndustryCloseDownProtection(i->type)) {
 
		i->prod_level = PRODLEVEL_CLOSURE;
 
		str = indspec->closure_text;
 
	}
 

	
 
	if (!suppress_message && str != STR_NULL) {
 
		NewsSubtype ns;
 
		/* Compute news category */
 
		if (closeit) {
 
			ns = NS_INDUSTRY_CLOSE;
 
			AI::BroadcastNewEvent(new AIEventIndustryClose(i->index));
 
		} else {
 
			switch (WhoCanServiceIndustry(i)) {
 
				case 0: ns = NS_INDUSTRY_NOBODY;  break;
 
				case 1: ns = NS_INDUSTRY_OTHER;   break;
 
				case 2: ns = NS_INDUSTRY_COMPANY; break;
 
				default: NOT_REACHED();
 
			}
 
		}
 
		/* Set parameters of news string */
 
		if (str > STR_LAST_STRINGID) {
 
			SetDParam(0, STR_TOWN_NAME);
 
			SetDParam(1, i->town->index);
 
			SetDParam(2, indspec->name);
 
		} else if (closeit) {
 
			SetDParam(0, STR_FORMAT_INDUSTRY_NAME);
 
			SetDParam(1, i->town->index);
 
			SetDParam(2, indspec->name);
 
		} else {
src/misc_gui.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file misc_gui.cpp GUIs for a number of misc windows. */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "landscape.h"
 
#include "newgrf_text.h"
 
#include "saveload/saveload.h"
 
#include "gui.h"
 
#include "viewport_func.h"
 
#include "gfx_func.h"
 
#include "command_func.h"
 
#include "company_func.h"
 
#include "town.h"
 
#include "network/network.h"
 
#include "network/network_content.h"
 
#include "company_base.h"
 
#include "texteff.hpp"
 
#include "cargotype.h"
 
#include "company_manager_face.h"
 
#include "strings_func.h"
 
#include "fileio_func.h"
 
#include "fios.h"
 
#include "zoom_func.h"
 
#include "window_func.h"
 
#include "tilehighlight_func.h"
 
#include "querystring_gui.h"
 
#include "console_func.h"
 
#include "core/geometry_func.hpp"
 
#include "newgrf_debug.h"
 

	
 
#include "table/strings.h"
 

	
 

	
 
/**
 
 * Try to retrive the current clipboard contents.
 
 *
 
 * @note OS-specific funtion.
 
 * @return True if some text could be retrived.
 
 */
 
bool GetClipboardContents(char *buffer, size_t buff_len);
 

	
 

	
 
/* Variables to display file lists */
 
SaveLoadDialogMode _saveload_mode;
 

	
 

	
 
static bool _fios_path_changed;
 
static bool _savegame_sort_dirty;
 
int _caret_timer;
 

	
 
/** Widgets for the land info window. */
 
enum LandInfoWidgets {
 
	LIW_BACKGROUND, ///< Background to draw on
 
};
 

	
 
static const NWidgetPart _nested_land_info_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_GREY),
 
		NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_LAND_AREA_INFORMATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
 
		NWidget(WWT_DEBUGBOX, COLOUR_GREY),
 
	EndContainer(),
 
	NWidget(WWT_PANEL, COLOUR_GREY, LIW_BACKGROUND), EndContainer(),
 
};
 

	
 
static const WindowDesc _land_info_desc(
 
	WDP_AUTO, 0, 0,
 
	WC_LAND_INFO, WC_NONE,
 
	0,
 
	_nested_land_info_widgets, lengthof(_nested_land_info_widgets)
 
);
 

	
 
class LandInfoWindow : public Window {
 
	enum {
 
	enum LandInfoLines {
 
		LAND_INFO_CENTERED_LINES   = 12,                       ///< Up to 12 centered lines
 
		LAND_INFO_MULTICENTER_LINE = LAND_INFO_CENTERED_LINES, ///< One multicenter line
 
		LAND_INFO_LINE_END,
 
	};
 

	
 
		LAND_INFO_LINE_BUFF_SIZE = 512,
 
	};
 
	static const uint LAND_INFO_LINE_BUFF_SIZE = 512;
 

	
 
public:
 
	char landinfo_data[LAND_INFO_LINE_END][LAND_INFO_LINE_BUFF_SIZE];
 
	TileIndex tile;
 

	
 
	virtual void OnPaint()
 
	{
 
		this->DrawWidgets();
 
	}
 

	
 
	virtual void DrawWidget(const Rect &r, int widget) const
 
	{
 
		if (widget != LIW_BACKGROUND) return;
 

	
 
		uint y = r.top + WD_TEXTPANEL_TOP;
 
		for (uint i = 0; i < LAND_INFO_CENTERED_LINES; i++) {
 
			if (StrEmpty(this->landinfo_data[i])) break;
 

	
 
			DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, this->landinfo_data[i], i == 0 ? TC_LIGHT_BLUE : TC_FROMSTRING, SA_CENTER);
 
			y += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
 
			if (i == 0) y += 4;
 
		}
 

	
 
		if (!StrEmpty(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])) {
 
			SetDParamStr(0, this->landinfo_data[LAND_INFO_MULTICENTER_LINE]);
 
			DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, r.bottom - WD_TEXTPANEL_BOTTOM, STR_JUST_RAW_STRING, TC_FROMSTRING, SA_CENTER);
 
		}
 
	}
 

	
 
	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
 
	{
 
		if (widget != LIW_BACKGROUND) return;
 

	
 
		size->height = WD_TEXTPANEL_TOP + WD_TEXTPANEL_BOTTOM;
 
		for (uint i = 0; i < LAND_INFO_CENTERED_LINES; i++) {
 
			if (StrEmpty(this->landinfo_data[i])) break;
 

	
 
			uint width = GetStringBoundingBox(this->landinfo_data[i]).width + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT;
 
			size->width = max(size->width, width);
 

	
 
			size->height += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
 
			if (i == 0) size->height += 4;
 
		}
 

	
 
		if (!StrEmpty(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])) {
 
			uint width = GetStringBoundingBox(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]).width + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT;
 
			size->width = max(size->width, min(300u, width));
 
			SetDParamStr(0, this->landinfo_data[LAND_INFO_MULTICENTER_LINE]);
 
			size->height += GetStringHeight(STR_JUST_RAW_STRING, size->width - WD_FRAMETEXT_LEFT - WD_FRAMETEXT_RIGHT);
 
		}
 
	}
 

	
 
	LandInfoWindow(TileIndex tile) : Window(), tile(tile) {
 
		Town *t = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority);
 

	
 
		/* Because build_date is not set yet in every TileDesc, we make sure it is empty */
 
		TileDesc td;
 

	
 
		td.build_date = INVALID_DATE;
 

	
 
		/* Most tiles have only one owner, but
 
		 *  - drivethrough roadstops can be build on town owned roads (up to 2 owners) and
 
		 *  - roads can have up to four owners (railroad, road, tram, 3rd-roadtype "highway").
 
		 */
 
		td.owner_type[0] = STR_LAND_AREA_INFORMATION_OWNER; // At least one owner is displayed, though it might be "N/A".
 
		td.owner_type[1] = STR_NULL;       // STR_NULL results in skipping the owner
 
		td.owner_type[2] = STR_NULL;
 
		td.owner_type[3] = STR_NULL;
 
		td.owner[0] = OWNER_NONE;
 
		td.owner[1] = OWNER_NONE;
 
		td.owner[2] = OWNER_NONE;
 
		td.owner[3] = OWNER_NONE;
 

	
 
		td.station_class = STR_NULL;
 
		td.station_name = STR_NULL;
 
		td.airport_tile_name = STR_NULL;
 
		td.rail_speed = 0;
 

	
 
		td.grf = NULL;
 

	
 
		CargoArray acceptance;
 
		AddAcceptedCargo(tile, acceptance, NULL);
 
		GetTileDesc(tile, &td);
 

	
 
		uint line_nr = 0;
 

	
 
		/* Tiletype */
 
		SetDParam(0, td.dparam[0]);
 
		GetString(this->landinfo_data[line_nr], td.str, lastof(this->landinfo_data[line_nr]));
 
		line_nr++;
 

	
 
		/* Up to four owners */
 
		for (uint i = 0; i < 4; i++) {
 
			if (td.owner_type[i] == STR_NULL) continue;
 

	
 
			SetDParam(0, STR_LAND_AREA_INFORMATION_OWNER_N_A);
 
			if (td.owner[i] != OWNER_NONE && td.owner[i] != OWNER_WATER) GetNameOfOwner(td.owner[i], tile);
 
			GetString(this->landinfo_data[line_nr], td.owner_type[i], lastof(this->landinfo_data[line_nr]));
 
			line_nr++;
 
		}
 

	
 
		/* Cost to clear/revenue when cleared */
 
		StringID str = STR_LAND_AREA_INFORMATION_COST_TO_CLEAR_N_A;
 
		Company *c = Company::GetIfValid(_local_company);
 
		if (c != NULL) {
 
			Money old_money = c->money;
 
			c->money = INT64_MAX;
 
			CommandCost costclear = DoCommand(tile, 0, 0, DC_NONE, CMD_LANDSCAPE_CLEAR);
 
			c->money = old_money;
 
			if (costclear.Succeeded()) {
 
				Money cost = costclear.GetCost();
 
				if (cost < 0) {
 
					cost = -cost; // Negate negative cost to a positive revenue
 
					str = STR_LAND_AREA_INFORMATION_REVENUE_WHEN_CLEARED;
 
				} else {
 
					str = STR_LAND_AREA_INFORMATION_COST_TO_CLEAR;
 
				}
 
				SetDParam(0, cost);
 
			}
 
		}
 
		GetString(this->landinfo_data[line_nr], str, lastof(this->landinfo_data[line_nr]));
 
		line_nr++;
 

	
 
		/* Location */
 
		char tmp[16];
 
		snprintf(tmp, lengthof(tmp), "0x%.4X", tile);
 
		SetDParam(0, TileX(tile));
 
		SetDParam(1, TileY(tile));
 
		SetDParam(2, TileHeight(tile));
 
		SetDParamStr(3, tmp);
 
		GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_LANDINFO_COORDS, lastof(this->landinfo_data[line_nr]));
 
		line_nr++;
 

	
 
		/* Local authority */
 
		SetDParam(0, STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE);
 
		if (t != NULL) {
 
			SetDParam(0, STR_TOWN_NAME);
 
			SetDParam(1, t->index);
 
		}
 
		GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY, lastof(this->landinfo_data[line_nr]));
 
		line_nr++;
 

	
 
		/* Build date */
 
		if (td.build_date != INVALID_DATE) {
 
			SetDParam(0, td.build_date);
 
			GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_BUILD_DATE, lastof(this->landinfo_data[line_nr]));
 
			line_nr++;
 
		}
 

	
 
		/* Station class */
 
		if (td.station_class != STR_NULL) {
 
			SetDParam(0, td.station_class);
 
			GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_STATION_CLASS, lastof(this->landinfo_data[line_nr]));
 
			line_nr++;
 
		}
 

	
 
		/* Station type name */
 
		if (td.station_name != STR_NULL) {
 
			SetDParam(0, td.station_name);
 
			GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_STATION_TYPE, lastof(this->landinfo_data[line_nr]));
 
			line_nr++;
 
		}
 

	
 
		/* Station type name */
 
		if (td.airport_tile_name != STR_NULL) {
 
			SetDParam(0, td.airport_tile_name);
 
			GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_AIRPORTTILE_NAME, lastof(this->landinfo_data[line_nr]));
 
			line_nr++;
 
		}
 

	
 
		/* Rail speed limit */
 
		if (td.rail_speed != 0) {
 
			SetDParam(0, td.rail_speed);
 
			GetString(this->landinfo_data[line_nr], STR_LANG_AREA_INFORMATION_RAIL_SPEED_LIMIT, lastof(this->landinfo_data[line_nr]));
 
			line_nr++;
 
		}
 

	
 
		/* NewGRF name */
 
		if (td.grf != NULL) {
 
			SetDParamStr(0, td.grf);
 
			GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_NEWGRF_NAME, lastof(this->landinfo_data[line_nr]));
 
			line_nr++;
 
		}
 

	
 
		assert(line_nr < LAND_INFO_CENTERED_LINES);
 

	
 
		/* Mark last line empty */
 
		this->landinfo_data[line_nr][0] = '\0';
 

	
 
		/* Cargo acceptance is displayed in a extra multiline */
 
		char *strp = GetString(this->landinfo_data[LAND_INFO_MULTICENTER_LINE], STR_LAND_AREA_INFORMATION_CARGO_ACCEPTED, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]));
 
		bool found = false;
src/music/qtmidi.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/**
 
 * @file qtmidi.cpp
 
 * @brief MIDI music player for MacOS X using QuickTime.
 
 *
 
 * This music player should work in all MacOS X releases starting from 10.0,
 
 * as QuickTime is an integral part of the system since the old days of the
 
 * Motorola 68k-based Macintoshes. The only extra dependency apart from
 
 * QuickTime itself is Carbon, which is included since 10.0 as well.
 
 *
 
 * QuickTime gets fooled with the MIDI files from Transport Tycoon Deluxe
 
 * because of the @c .gm suffix. To force QuickTime to load the MIDI files
 
 * without the need of dealing with the individual QuickTime components
 
 * needed to play music (data source, MIDI parser, note allocators,
 
 * synthesizers and the like) some Carbon functions are used to set the file
 
 * type as seen by QuickTime, using @c FSpSetFInfo() (which modifies the
 
 * file's resource fork).
 
 */
 

	
 

	
 
#ifndef NO_QUICKTIME
 

	
 
#include "../stdafx.h"
 
#include "qtmidi.h"
 
#include "../debug.h"
 

	
 
#define Rect  OTTDRect
 
#define Point OTTDPoint
 
#include <QuickTime/QuickTime.h>
 
#undef Rect
 
#undef Point
 

	
 
static FMusicDriver_QtMidi iFMusicDriver_QtMidi;
 

	
 

	
 
enum {
 
	midiType = 'Midi' ///< OSType code for MIDI songs.
 
};
 
static const uint MIDI_TYPE = 'Midi' ///< OSType code for MIDI songs.
 

	
 

	
 
/**
 
 * Sets the @c OSType of a given file to @c 'Midi', but only if it's not
 
 * already set.
 
 *
 
 * @param *ref A @c FSSpec structure referencing a file.
 
 */
 
static void SetMIDITypeIfNeeded(const FSRef *ref)
 
{
 
	FSCatalogInfo catalogInfo;
 

	
 
	assert(ref);
 

	
 
	if (noErr != FSGetCatalogInfo(ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL)) return;
 
	if (!(catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) {
 
		FileInfo * const info = (FileInfo *) catalogInfo.finderInfo;
 
		if (info->fileType != midiType && !(info->finderFlags & kIsAlias)) {
 
		if (info->fileType != MIDI_TYPE && !(info->finderFlags & kIsAlias)) {
 
			OSErr e;
 
			info->fileType = midiType;
 
			info->fileType = MIDI_TYPE;
 
			e = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo);
 
			if (e == noErr) {
 
				DEBUG(driver, 3, "qtmidi: changed filetype to 'Midi'");
 
			} else {
 
				DEBUG(driver, 0, "qtmidi: changing filetype to 'Midi' failed - error %d", e);
 
			}
 
		}
 
	}
 
}
 

	
 

	
 
/**
 
 * Loads a MIDI file and returns it as a QuickTime Movie structure.
 
 *
 
 * @param *path String with the path of an existing MIDI file.
 
 * @param *moov Pointer to a @c Movie where the result will be stored.
 
 * @return Wether the file was loaded and the @c Movie successfully created.
 
 */
 
static bool LoadMovieForMIDIFile(const char *path, Movie *moov)
 
{
 
	int fd;
 
	int ret;
 
	char magic[4];
 
	FSRef fsref;
 
	FSSpec fsspec;
 
	short refnum = 0;
 
	short resid  = 0;
 

	
 
	assert(path != NULL);
 
	assert(moov != NULL);
 

	
 
	DEBUG(driver, 2, "qtmidi: start loading '%s'...", path);
 

	
 
	/*
 
	 * XXX Manual check for MIDI header ('MThd'), as I don't know how to make
 
	 * QuickTime load MIDI files without a .mid suffix without knowing it's
 
	 * a MIDI file and setting the OSType of the file to the 'Midi' value.
 
	 * Perhahaps ugly, but it seems that it does the Right Thing(tm).
 
	 */
 
	fd = open(path, O_RDONLY, 0);
 
	if (fd == -1) return false;
 
	ret = read(fd, magic, 4);
 
	close(fd);
 
	if (ret < 4) return false;
 

	
 
	DEBUG(driver, 3, "qtmidi: header is '%.4s'", magic);
 
	if (magic[0] != 'M' || magic[1] != 'T' || magic[2] != 'h' || magic[3] != 'd') {
 
		return false;
 
	}
 

	
 
	if (noErr != FSPathMakeRef((const UInt8 *) path, &fsref, NULL)) return false;
 
	SetMIDITypeIfNeeded(&fsref);
 

	
 
	if (noErr != FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, &fsspec, NULL)) return false;
 
	if (OpenMovieFile(&fsspec, &refnum, fsRdPerm) != noErr) return false;
 
	DEBUG(driver, 3, "qtmidi: '%s' successfully opened", path);
 

	
 
	if (noErr != NewMovieFromFile(moov, refnum, &resid, NULL,
 
				newMovieActive | newMovieDontAskUnresolvedDataRefs, NULL)) {
 
		CloseMovieFile(refnum);
 
		return false;
 
	}
 
	DEBUG(driver, 3, "qtmidi: movie container created");
 

	
 
	CloseMovieFile(refnum);
 
	return true;
 
}
 

	
 

	
 
/**
 
 * Flag which has the @c true value when QuickTime is available and
 
 * initialized.
 
 */
 
static bool _quicktime_started = false;
 

	
 

	
 
/**
 
 * Initialize QuickTime if needed. This function sets the
 
 * #_quicktime_started flag to @c true if QuickTime is present in the system
 
 * and it was initialized properly.
 
 */
 
static void InitQuickTimeIfNeeded()
 
{
 
	OSStatus dummy;
 

	
 
	if (_quicktime_started) return;
 

	
 
	DEBUG(driver, 2, "qtmidi: initializing Quicktime");
 
	/* Be polite: check wether QuickTime is available and initialize it. */
 
	_quicktime_started =
 
		(noErr == Gestalt(gestaltQuickTime, &dummy)) &&
 
		(noErr == EnterMovies());
 
	if (!_quicktime_started) DEBUG(driver, 0, "qtmidi: Quicktime initialization failed!");
 
}
 

	
 

	
 
/** Possible states of the QuickTime music driver. */
 
enum {
 
enum QTStates {
 
	QT_STATE_IDLE, ///< No file loaded.
 
	QT_STATE_PLAY, ///< File loaded, playing.
 
	QT_STATE_STOP, ///< File loaded, stopped.
 
};
 

	
 

	
 
static Movie _quicktime_movie;                  ///< Current QuickTime @c Movie.
 
static byte  _quicktime_volume = 127;           ///< Current volume.
 
static int   _quicktime_state  = QT_STATE_IDLE; ///< Current player state.
 

	
 

	
 
/**
 
 * Maps OpenTTD volume to QuickTime notion of volume.
 
 */
 
#define VOLUME  ((short)((0x00FF & _quicktime_volume) << 1))
 

	
 

	
 
/**
 
 * Initialized the MIDI player, including QuickTime initialization.
 
 *
 
 * @todo Give better error messages by inspecting error codes returned by
 
 * @c Gestalt() and @c EnterMovies(). Needs changes in
 
 * #InitQuickTimeIfNeeded.
 
 */
 
const char *MusicDriver_QtMidi::Start(const char * const *parm)
 
{
 
	InitQuickTimeIfNeeded();
 
	return (_quicktime_started) ? NULL : "can't initialize QuickTime";
 
}
 

	
 

	
 
/**
 
 * Checks wether the player is active.
 
 *
 
 * This function is called at regular intervals from OpenTTD's main loop, so
 
 * we call @c MoviesTask() from here to let QuickTime do its work.
 
 */
 
bool MusicDriver_QtMidi::IsSongPlaying()
 
{
 
	if (!_quicktime_started) return true;
 

	
 
	switch (_quicktime_state) {
 
		case QT_STATE_IDLE:
 
		case QT_STATE_STOP:
 
			/* Do nothing. */
 
			break;
 

	
 
		case QT_STATE_PLAY:
 
			MoviesTask(_quicktime_movie, 0);
 
			/* Check wether movie ended. */
 
			if (IsMovieDone(_quicktime_movie) ||
 
					(GetMovieTime(_quicktime_movie, NULL) >=
 
					 GetMovieDuration(_quicktime_movie))) {
 
				_quicktime_state = QT_STATE_STOP;
 
			}
 
	}
 

	
 
	return _quicktime_state == QT_STATE_PLAY;
 
}
 

	
 

	
 
/**
 
 * Stops the MIDI player.
 
 *
 
 * Stops playing and frees any used resources before returning. As it
 
 * deinitilizes QuickTime, the #_quicktime_started flag is set to @c false.
 
 */
 
void MusicDriver_QtMidi::Stop()
 
{
 
	if (!_quicktime_started) return;
 

	
 
	DEBUG(driver, 2, "qtmidi: stopping driver...");
 
	switch (_quicktime_state) {
 
		case QT_STATE_IDLE:
 
			DEBUG(driver, 3, "qtmidi: stopping not needed, already idle");
 
			/* Do nothing. */
 
			break;
 

	
 
		case QT_STATE_PLAY:
 
			StopSong();
 
			/* Fall-through */
 

	
 
		case QT_STATE_STOP:
 
			DisposeMovie(_quicktime_movie);
 
	}
 

	
 
	ExitMovies();
 
	_quicktime_started = false;
 
}
 

	
 

	
 
/**
 
 * Starts playing a new song.
 
 *
 
 * @param filename Path to a MIDI file.
 
 */
 
void MusicDriver_QtMidi::PlaySong(const char *filename)
 
{
 
	if (!_quicktime_started) return;
 

	
 
	DEBUG(driver, 2, "qtmidi: trying to play '%s'", filename);
 
	switch (_quicktime_state) {
 
		case QT_STATE_PLAY:
 
			StopSong();
 
			DEBUG(driver, 3, "qtmidi: previous tune stopped");
 
			/* Fall-through -- no break needed. */
 

	
 
		case QT_STATE_STOP:
 
			DisposeMovie(_quicktime_movie);
 
			DEBUG(driver, 3, "qtmidi: previous tune disposed");
 
			_quicktime_state = QT_STATE_IDLE;
 
			/* Fall-through -- no break needed. */
 

	
 
		case QT_STATE_IDLE:
 
			LoadMovieForMIDIFile(filename, &_quicktime_movie);
 
			SetMovieVolume(_quicktime_movie, VOLUME);
 
			StartMovie(_quicktime_movie);
 
			_quicktime_state = QT_STATE_PLAY;
 
	}
 
	DEBUG(driver, 3, "qtmidi: playing '%s'", filename);
 
}
 

	
 

	
 
/**
 
 * Stops playing the current song, if the player is active.
 
 */
 
void MusicDriver_QtMidi::StopSong()
 
{
 
	if (!_quicktime_started) return;
 

	
 
	switch (_quicktime_state) {
 
		case QT_STATE_IDLE:
 
			/* Fall-through -- no break needed. */
 

	
 
		case QT_STATE_STOP:
 
			DEBUG(driver, 3, "qtmidi: stop requested, but already idle");
 
			/* Do nothing. */
 
			break;
 

	
 
		case QT_STATE_PLAY:
 
			StopMovie(_quicktime_movie);
 
			_quicktime_state = QT_STATE_STOP;
 
			DEBUG(driver, 3, "qtmidi: player stopped");
 
	}
 
}
 

	
 

	
 
/**
 
 * Changes the playing volume of the MIDI player.
 
 *
 
 * As QuickTime controls volume in a per-movie basis, the desired volume is
 
 * stored in #_quicktime_volume, and the volume is set here using the
 
 * #VOLUME macro, @b and when loading new song in #PlaySong.
 
 *
 
 * @param vol The desired volume, range of the value is @c 0-127
 
 */
 
void MusicDriver_QtMidi::SetVolume(byte vol)
 
{
 
	if (!_quicktime_started) return;
 

	
 
	_quicktime_volume = vol;
 

	
 
	DEBUG(driver, 2, "qtmidi: set volume to %u (%hi)", vol, VOLUME);
 
	switch (_quicktime_state) {
 
		case QT_STATE_IDLE:
 
			/* Do nothing. */
 
			break;
 

	
 
		case QT_STATE_PLAY:
 
		case QT_STATE_STOP:
 
			SetMovieVolume(_quicktime_movie, VOLUME);
 
	}
 
}
 

	
 
#endif /* NO_QUICKTIME */
src/network/network_chat_gui.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file network_chat_gui.cpp GUI for handling chat messages. */
 

	
 
#include <stdarg.h> /* va_list */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../stdafx.h"
 
#include "../date_func.h"
 
#include "../gfx_func.h"
 
#include "../strings_func.h"
 
#include "../blitter/factory.hpp"
 
#include "../console_func.h"
 
#include "../video/video_driver.hpp"
 
#include "../table/sprites.h"
 
#include "../querystring_gui.h"
 
#include "../town.h"
 
#include "../window_func.h"
 
#include "../core/geometry_func.hpp"
 
#include "network.h"
 
#include "network_client.h"
 
#include "network_base.h"
 

	
 
#include "table/strings.h"
 

	
 
/* The draw buffer must be able to contain the chat message, client name and the "[All]" message,
 
 * some spaces and possible translations of [All] to other languages. */
 
assert_compile((int)DRAW_STRING_BUFFER >= (int)NETWORK_CHAT_LENGTH + NETWORK_NAME_LENGTH + 40);
 

	
 
enum {
 
	NETWORK_CHAT_LINE_SPACING = 3,
 
};
 
static const uint NETWORK_CHAT_LINE_SPACING = 3;
 

	
 
struct ChatMessage {
 
	char message[DRAW_STRING_BUFFER];
 
	TextColour colour;
 
	Date end_date;
 
};
 

	
 
/* used for chat window */
 
static ChatMessage *_chatmsg_list = NULL;
 
static bool _chatmessage_dirty = false;
 
static bool _chatmessage_visible = false;
 
static bool _chat_tab_completion_active;
 
static uint MAX_CHAT_MESSAGES = 0;
 

	
 
/* The chatbox grows from the bottom so the coordinates are pixels from
 
 * the left and pixels from the bottom. The height is the maximum height */
 
static PointDimension _chatmsg_box;
 
static uint8 *_chatmessage_backup = NULL;
 

	
 
static inline uint GetChatMessageCount()
 
{
 
	uint i = 0;
 
	for (; i < MAX_CHAT_MESSAGES; i++) {
 
		if (_chatmsg_list[i].message[0] == '\0') break;
 
	}
 

	
 
	return i;
 
}
 

	
 
/**
 
 * Add a text message to the 'chat window' to be shown
 
 * @param colour The colour this message is to be shown in
 
 * @param duration The duration of the chat message in game-days
 
 * @param message message itself in printf() style
 
 */
 
void CDECL NetworkAddChatMessage(TextColour colour, uint8 duration, const char *message, ...)
 
{
 
	char buf[DRAW_STRING_BUFFER];
 
	const char *bufp;
 
	va_list va;
 
	uint msg_count;
 
	uint16 lines;
 

	
 
	va_start(va, message);
 
	vsnprintf(buf, lengthof(buf), message, va);
 
	va_end(va);
 

	
 
	Utf8TrimString(buf, DRAW_STRING_BUFFER);
 

	
 
	/* Force linebreaks for strings that are too long */
 
	lines = GB(FormatStringLinebreaks(buf, lastof(buf), _chatmsg_box.width - 8), 0, 16) + 1;
 
	if (lines >= MAX_CHAT_MESSAGES) return;
 

	
 
	msg_count = GetChatMessageCount();
 
	/* We want to add more chat messages than there is free space for, remove 'old' */
 
	if (lines > MAX_CHAT_MESSAGES - msg_count) {
 
		int i = lines - (MAX_CHAT_MESSAGES - msg_count);
 
		memmove(&_chatmsg_list[0], &_chatmsg_list[i], sizeof(_chatmsg_list[0]) * (msg_count - i));
 
		msg_count = MAX_CHAT_MESSAGES - lines;
 
	}
 

	
 
	for (bufp = buf; lines != 0; lines--) {
 
		ChatMessage *cmsg = &_chatmsg_list[msg_count++];
 
		strecpy(cmsg->message, bufp, lastof(cmsg->message));
 

	
 
		/* The default colour for a message is company colour. Replace this with
 
		 * white for any additional lines */
 
		cmsg->colour = (bufp == buf && (colour & IS_PALETTE_COLOUR)) ? colour : TC_WHITE;
 
		cmsg->end_date = _date + duration;
 

	
 
		bufp += strlen(bufp) + 1; // jump to 'next line' in the formatted string
 
	}
 

	
 
	_chatmessage_dirty = true;
 
}
 

	
 
void NetworkInitChatMessage()
 
{
 
	MAX_CHAT_MESSAGES    = _settings_client.gui.network_chat_box_height;
 

	
 
	_chatmsg_list        = ReallocT(_chatmsg_list, _settings_client.gui.network_chat_box_height);
 
	_chatmsg_box.x       = 10;
 
	_chatmsg_box.y       = 3 * FONT_HEIGHT_NORMAL;
 
	_chatmsg_box.width   = _settings_client.gui.network_chat_box_width;
 
	_chatmsg_box.height  = _settings_client.gui.network_chat_box_height * (FONT_HEIGHT_NORMAL + NETWORK_CHAT_LINE_SPACING) + 2;
 
	_chatmessage_backup  = ReallocT(_chatmessage_backup, _chatmsg_box.width * _chatmsg_box.height * BlitterFactoryBase::GetCurrentBlitter()->GetBytesPerPixel());
 
	_chatmessage_visible = false;
 

	
 
	for (uint i = 0; i < MAX_CHAT_MESSAGES; i++) {
 
		_chatmsg_list[i].message[0] = '\0';
 
	}
 
}
 

	
 
/** Hide the chatbox */
 
void NetworkUndrawChatMessage()
 
{
 
	/* Sometimes we also need to hide the cursor
 
	 *   This is because both textmessage and the cursor take a shot of the
 
	 *   screen before drawing.
 
	 *   Now the textmessage takes his shot and paints his data before the cursor
 
	 *   does, so in the shot of the cursor is the screen-data of the textmessage
 
	 *   included when the cursor hangs somewhere over the textmessage. To
 
	 *   avoid wrong repaints, we undraw the cursor in that case, and everything
 
	 *   looks nicely ;)
 
	 * (and now hope this story above makes sense to you ;))
 
	 */
 
	if (_cursor.visible &&
 
			_cursor.draw_pos.x + _cursor.draw_size.x >= _chatmsg_box.x &&
 
			_cursor.draw_pos.x <= _chatmsg_box.x + _chatmsg_box.width &&
 
			_cursor.draw_pos.y + _cursor.draw_size.y >= _screen.height - _chatmsg_box.y - _chatmsg_box.height &&
 
			_cursor.draw_pos.y <= _screen.height - _chatmsg_box.y) {
 
		UndrawMouseCursor();
 
	}
 

	
 
	if (_chatmessage_visible) {
 
		Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
 
		int x      = _chatmsg_box.x;
 
		int y      = _screen.height - _chatmsg_box.y - _chatmsg_box.height;
 
		int width  = _chatmsg_box.width;
 
		int height = _chatmsg_box.height;
 
		if (y < 0) {
 
			height = max(height + y, min(_chatmsg_box.height, _screen.height));
 
			y = 0;
 
		}
 
		if (x + width >= _screen.width) {
 
			width = _screen.width - x;
 
		}
 
		if (width <= 0 || height <= 0) return;
 

	
 
		_chatmessage_visible = false;
 
		/* Put our 'shot' back to the screen */
 
		blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height);
 
		/* And make sure it is updated next time */
 
		_video_driver->MakeDirty(x, y, width, height);
 

	
 
		_chatmessage_dirty = true;
 
	}
 
}
 

	
 
/** Check if a message is expired every day */
 
void NetworkChatMessageDailyLoop()
 
{
 
	for (uint i = 0; i < MAX_CHAT_MESSAGES; i++) {
 
		ChatMessage *cmsg = &_chatmsg_list[i];
 
		if (cmsg->message[0] == '\0') continue;
 

	
 
		/* Message has expired, remove from the list */
 
		if (cmsg->end_date < _date) {
 
			/* Move the remaining messages over the current message */
 
			if (i != MAX_CHAT_MESSAGES - 1) memmove(cmsg, cmsg + 1, sizeof(*cmsg) * (MAX_CHAT_MESSAGES - i - 1));
 

	
 
			/* Mark the last item as empty */
 
			_chatmsg_list[MAX_CHAT_MESSAGES - 1].message[0] = '\0';
 
			_chatmessage_dirty = true;
 

	
 
			/* Go one item back, because we moved the array 1 to the left */
 
			i--;
 
		}
 
	}
 
}
 

	
 
/** Draw the chat message-box */
 
void NetworkDrawChatMessage()
 
{
 
	Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
 
	if (!_chatmessage_dirty) return;
 

	
 
	/* First undraw if needed */
 
	NetworkUndrawChatMessage();
 

	
 
	if (_iconsole_mode == ICONSOLE_FULL) return;
 

	
 
	/* Check if we have anything to draw at all */
 
	uint count = GetChatMessageCount();
 
	if (count == 0) return;
 

	
 
	int x      = _chatmsg_box.x;
 
	int y      = _screen.height - _chatmsg_box.y - _chatmsg_box.height;
 
	int width  = _chatmsg_box.width;
 
	int height = _chatmsg_box.height;
 
	if (y < 0) {
 
		height = max(height + y, min(_chatmsg_box.height, _screen.height));
 
		y = 0;
 
	}
 
	if (x + width >= _screen.width) {
 
		width = _screen.width - x;
 
	}
 
	if (width <= 0 || height <= 0) return;
 

	
 
	assert(blitter->BufferSize(width, height) <= (int)(_chatmsg_box.width * _chatmsg_box.height * blitter->GetBytesPerPixel()));
 

	
 
	/* Make a copy of the screen as it is before painting (for undraw) */
src/network/network_content_gui.cpp
Show inline comments
 
@@ -36,388 +36,386 @@ static const NWidgetPart _nested_network
 
	NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_CONTENT_DOWNLOAD_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
 
	NWidget(WWT_PANEL, COLOUR_GREY, NCDSWW_BACKGROUND),
 
		NWidget(NWID_SPACER), SetMinimalSize(350, 0), SetMinimalTextLines(3, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 30),
 
		NWidget(NWID_HORIZONTAL),
 
			NWidget(NWID_SPACER), SetMinimalSize(125, 0),
 
			NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NCDSWW_CANCELOK), SetMinimalSize(101, 12), SetDataTip(STR_BUTTON_CANCEL, STR_NULL),
 
			NWidget(NWID_SPACER), SetFill(1, 0),
 
		EndContainer(),
 
		NWidget(NWID_SPACER), SetMinimalSize(0, 4),
 
	EndContainer(),
 
};
 

	
 
/** Window description for the download window */
 
static const WindowDesc _network_content_download_status_window_desc(
 
	WDP_CENTER, 0, 0,
 
	WC_NETWORK_STATUS_WINDOW, WC_NONE,
 
	WDF_MODAL,
 
	_nested_network_content_download_status_window_widgets, lengthof(_nested_network_content_download_status_window_widgets)
 
);
 

	
 
/** Window for showing the download status of content */
 
struct NetworkContentDownloadStatusWindow : public Window, ContentCallback {
 
private:
 
	ClientNetworkContentSocketHandler *connection; ///< Our connection with the content server
 
	SmallVector<ContentType, 4> receivedTypes;     ///< Types we received so we can update their cache
 

	
 
	uint total_files;      ///< Number of files to download
 
	uint downloaded_files; ///< Number of files downloaded
 
	uint total_bytes;      ///< Number of bytes to download
 
	uint downloaded_bytes; ///< Number of bytes downloaded
 

	
 
	uint32 cur_id; ///< The current ID of the downloaded file
 
	char name[48]; ///< The current name of the downloaded file
 

	
 
public:
 
	/**
 
	 * Create a new download window based on a list of content information
 
	 * with flags whether to download them or not.
 
	 * @param infos the list to search in
 
	 */
 
	NetworkContentDownloadStatusWindow() :
 
		cur_id(UINT32_MAX)
 
	{
 
		this->parent = FindWindowById(WC_NETWORK_WINDOW, 1);
 

	
 
		_network_content_client.AddCallback(this);
 
		_network_content_client.DownloadSelectedContent(this->total_files, this->total_bytes);
 

	
 
		this->InitNested(&_network_content_download_status_window_desc, 0);
 
	}
 

	
 
	/** Free whatever we've allocated */
 
	~NetworkContentDownloadStatusWindow()
 
	{
 
		/* Tell all the backends about what we've downloaded */
 
		for (ContentType *iter = this->receivedTypes.Begin(); iter != this->receivedTypes.End(); iter++) {
 
			switch (*iter) {
 
				case CONTENT_TYPE_AI:
 
				case CONTENT_TYPE_AI_LIBRARY:
 
					AI::Rescan();
 
					SetWindowClassesDirty(WC_AI_DEBUG);
 
					break;
 

	
 
				case CONTENT_TYPE_BASE_GRAPHICS:
 
					BaseGraphics::FindSets();
 
					SetWindowDirty(WC_GAME_OPTIONS, 0);
 
					break;
 

	
 
				case CONTENT_TYPE_BASE_SOUNDS:
 
					BaseSounds::FindSets();
 
					SetWindowDirty(WC_GAME_OPTIONS, 0);
 
					break;
 

	
 
				case CONTENT_TYPE_BASE_MUSIC:
 
					BaseMusic::FindSets();
 
					SetWindowDirty(WC_GAME_OPTIONS, 0);
 
					break;
 

	
 
				case CONTENT_TYPE_NEWGRF:
 
					ScanNewGRFFiles();
 
					/* Yes... these are the NewGRF windows */
 
					InvalidateWindowClassesData(WC_SAVELOAD);
 
					InvalidateWindowData(WC_GAME_OPTIONS, 0, 1);
 
					break;
 

	
 
				case CONTENT_TYPE_SCENARIO:
 
				case CONTENT_TYPE_HEIGHTMAP:
 
					extern void ScanScenarios();
 
					ScanScenarios();
 
					InvalidateWindowData(WC_SAVELOAD, 0, 0);
 
					break;
 

	
 
				default:
 
					break;
 
			}
 
		}
 

	
 
		/* Always invalidate the download window; tell it we are going to be gone */
 
		InvalidateWindowData(WC_NETWORK_WINDOW, 1, 2);
 
		_network_content_client.RemoveCallback(this);
 
	}
 

	
 
	virtual void OnPaint()
 
	{
 
		this->DrawWidgets();
 
	}
 

	
 
	virtual void DrawWidget(const Rect &r, int widget) const
 
	{
 
		if (widget != NCDSWW_BACKGROUND) return;
 

	
 
		/* Draw nice progress bar :) */
 
		DrawFrameRect(r.left + 20, r.top + 4, r.left + 20 + (int)((this->width - 40LL) * this->downloaded_bytes / this->total_bytes), r.top + 14, COLOUR_MAUVE, FR_NONE);
 

	
 
		int y = r.top + 20;
 
		SetDParam(0, this->downloaded_bytes);
 
		SetDParam(1, this->total_bytes);
 
		SetDParam(2, this->downloaded_bytes * 100LL / this->total_bytes);
 
		DrawString(r.left + 2, r.right - 2, y, STR_CONTENT_DOWNLOAD_PROGRESS_SIZE, TC_FROMSTRING, SA_CENTER);
 

	
 
		StringID str;
 
		if (this->downloaded_bytes == this->total_bytes) {
 
			str = STR_CONTENT_DOWNLOAD_COMPLETE;
 
		} else if (!StrEmpty(this->name)) {
 
			SetDParamStr(0, this->name);
 
			SetDParam(1, this->downloaded_files);
 
			SetDParam(2, this->total_files);
 
			str = STR_CONTENT_DOWNLOAD_FILE;
 
		} else {
 
			str = STR_CONTENT_DOWNLOAD_INITIALISE;
 
		}
 

	
 
		y += FONT_HEIGHT_NORMAL + 5;
 
		DrawStringMultiLine(r.left + 2, r.right - 2, y, y + FONT_HEIGHT_NORMAL * 2, str, TC_FROMSTRING, SA_CENTER);
 
	}
 

	
 
	virtual void OnClick(Point pt, int widget, int click_count)
 
	{
 
		if (widget == NCDSWW_CANCELOK) {
 
			if (this->downloaded_bytes != this->total_bytes) _network_content_client.Close();
 
			delete this;
 
		}
 
	}
 

	
 
	virtual void OnDownloadProgress(const ContentInfo *ci, uint bytes)
 
	{
 
		if (ci->id != this->cur_id) {
 
			strecpy(this->name, ci->filename, lastof(this->name));
 
			this->cur_id = ci->id;
 
			this->downloaded_files++;
 
			this->receivedTypes.Include(ci->type);
 
		}
 
		this->downloaded_bytes += bytes;
 

	
 
		/* When downloading is finished change cancel in ok */
 
		if (this->downloaded_bytes == this->total_bytes) {
 
			this->GetWidget<NWidgetCore>(NCDSWW_CANCELOK)->widget_data = STR_BUTTON_OK;
 
		}
 

	
 
		this->SetDirty();
 
	}
 
};
 

	
 
/** Widgets of the content list window. */
 
enum NetworkContentListWindowWidgets {
 
	NCLWW_BACKGROUND,    ///< Resize button
 

	
 
	NCLWW_FILTER_CAPT,   ///< Caption for the filter editbox
 
	NCLWW_FILTER,        ///< Filter editbox
 

	
 
	NCLWW_CHECKBOX,      ///< Button above checkboxes
 
	NCLWW_TYPE,          ///< 'Type' button
 
	NCLWW_NAME,          ///< 'Name' button
 

	
 
	NCLWW_MATRIX,        ///< Panel with list of content
 
	NCLWW_SCROLLBAR,     ///< Scrollbar of matrix
 

	
 
	NCLWW_DETAILS,       ///< Panel with content details
 

	
 
	NCLWW_SELECT_ALL,    ///< 'Select all' button
 
	NCLWW_SELECT_UPDATE, ///< 'Select updates' button
 
	NCLWW_UNSELECT,      ///< 'Unselect all' button
 
	NCLWW_CANCEL,        ///< 'Cancel' button
 
	NCLWW_DOWNLOAD,      ///< 'Download' button
 

	
 
	NCLWW_SEL_ALL_UPDATE, ///< #NWID_SELECTION widget for select all/update buttons.
 
};
 

	
 
/** Window that lists the content that's at the content server */
 
class NetworkContentListWindow : public QueryStringBaseWindow, ContentCallback {
 
	typedef GUIList<const ContentInfo*> GUIContentList;
 

	
 
	enum {
 
		EDITBOX_MAX_SIZE = 50,
 
		EDITBOX_MAX_LENGTH = 300,
 
	};
 
	static const uint EDITBOX_MAX_SIZE   =  50;
 
	static const uint EDITBOX_MAX_LENGTH = 300;
 

	
 
	/** Runtime saved values */
 
	static Listing last_sorting;
 
	static Filtering last_filtering;
 
	/** The sorter functions */
 
	static GUIContentList::SortFunction * const sorter_funcs[];
 
	static GUIContentList::FilterFunction * const filter_funcs[];
 
	GUIContentList content;      ///< List with content
 

	
 
	const ContentInfo *selected; ///< The selected content info
 
	int list_pos;                ///< Our position in the list
 
	uint filesize_sum;           ///< The sum of all selected file sizes
 

	
 
	/**
 
	 * (Re)build the network game list as its amount has changed because
 
	 * an item has been added or deleted for example
 
	 */
 
	void BuildContentList()
 
	{
 
		if (!this->content.NeedRebuild()) return;
 

	
 
		/* Create temporary array of games to use for listing */
 
		this->content.Clear();
 

	
 
		for (ConstContentIterator iter = _network_content_client.Begin(); iter != _network_content_client.End(); iter++) {
 
			*this->content.Append() = *iter;
 
		}
 

	
 
		this->FilterContentList();
 
		this->content.Compact();
 
		this->content.RebuildDone();
 
		this->SortContentList();
 

	
 
		this->vscroll.SetCount(this->content.Length()); // Update the scrollbar
 
		this->ScrollToSelected();
 
	}
 

	
 
	/** Sort content by name. */
 
	static int CDECL NameSorter(const ContentInfo * const *a, const ContentInfo * const *b)
 
	{
 
		return strcasecmp((*a)->name, (*b)->name);
 
	}
 

	
 
	/** Sort content by type. */
 
	static int CDECL TypeSorter(const ContentInfo * const *a, const ContentInfo * const *b)
 
	{
 
		int r = 0;
 
		if ((*a)->type != (*b)->type) {
 
			char a_str[64];
 
			char b_str[64];
 
			GetString(a_str, STR_CONTENT_TYPE_BASE_GRAPHICS + (*a)->type - CONTENT_TYPE_BASE_GRAPHICS, lastof(a_str));
 
			GetString(b_str, STR_CONTENT_TYPE_BASE_GRAPHICS + (*b)->type - CONTENT_TYPE_BASE_GRAPHICS, lastof(b_str));
 
			r = strcasecmp(a_str, b_str);
 
		}
 
		if (r == 0) r = NameSorter(a, b);
 
		return r;
 
	}
 

	
 
	/** Sort content by state. */
 
	static int CDECL StateSorter(const ContentInfo * const *a, const ContentInfo * const *b)
 
	{
 
		int r = (*a)->state - (*b)->state;
 
		if (r == 0) r = TypeSorter(a, b);
 
		return r;
 
	}
 

	
 
	/** Sort the content list */
 
	void SortContentList()
 
	{
 
		if (!this->content.Sort()) return;
 

	
 
		for (ConstContentIterator iter = this->content.Begin(); iter != this->content.End(); iter++) {
 
			if (*iter == this->selected) {
 
				this->list_pos = iter - this->content.Begin();
 
				break;
 
			}
 
		}
 
	}
 

	
 
	/** Filter content by tags/name */
 
	static bool CDECL TagNameFilter(const ContentInfo * const *a, const char *filter_string)
 
	{
 
		for (int i = 0; i < (*a)->tag_count; i++) {
 
			if (strcasestr((*a)->tags[i], filter_string) != NULL) return true;
 
		}
 
		return strcasestr((*a)->name, filter_string) != NULL;
 
	}
 

	
 
	/** Filter the content list */
 
	void FilterContentList()
 
	{
 
		if (!this->content.Filter(this->edit_str_buf)) return;
 

	
 
		/* update list position */
 
		for (ConstContentIterator iter = this->content.Begin(); iter != this->content.End(); iter++) {
 
			if (*iter == this->selected) {
 
				this->list_pos = iter - this->content.Begin();
 
				return;
 
			}
 
		}
 

	
 
		/* previously selected item not in list anymore */
 
		this->selected = NULL;
 
		this->list_pos = 0;
 
	}
 

	
 
	/** Make sure that the currently selected content info is within the visible part of the matrix */
 
	void ScrollToSelected()
 
	{
 
		if (this->selected == NULL) return;
 

	
 
		this->vscroll.ScrollTowards(this->list_pos);
 
	}
 

	
 
public:
 
	/**
 
	 * Create the content list window.
 
	 * @param desc the window description to pass to Window's constructor.
 
	 */
 
	NetworkContentListWindow(const WindowDesc *desc, bool select_all) :
 
			QueryStringBaseWindow(EDITBOX_MAX_SIZE),
 
			selected(NULL),
 
			list_pos(0)
 
	{
 
		this->InitNested(desc, 1);
 

	
 
		this->GetWidget<NWidgetStacked>(NCLWW_SEL_ALL_UPDATE)->SetDisplayedPlane(select_all);
 

	
 
		this->afilter = CS_ALPHANUMERAL;
 
		InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, EDITBOX_MAX_LENGTH);
 
		this->SetFocusedWidget(NCLWW_FILTER);
 

	
 
		_network_content_client.AddCallback(this);
 
		this->content.SetListing(this->last_sorting);
 
		this->content.SetFiltering(this->last_filtering);
 
		this->content.SetSortFuncs(this->sorter_funcs);
 
		this->content.SetFilterFuncs(this->filter_funcs);
 
		this->content.ForceRebuild();
 
		this->FilterContentList();
 
		this->SortContentList();
 
		this->InvalidateData();
 
	}
 

	
 
	/** Free everything we allocated */
 
	~NetworkContentListWindow()
 
	{
 
		_network_content_client.RemoveCallback(this);
 
	}
 

	
 
	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
 
	{
 
		switch (widget) {
 
			case NCLWW_FILTER_CAPT:
 
				*size = maxdim(*size, GetStringBoundingBox(STR_CONTENT_FILTER_TITLE));
 
				break;
 

	
 
			case NCLWW_TYPE: {
 
				Dimension d = *size;
 
				for (int i = CONTENT_TYPE_BEGIN; i < CONTENT_TYPE_END; i++) {
 
					d = maxdim(d, GetStringBoundingBox(STR_CONTENT_TYPE_BASE_GRAPHICS + i - CONTENT_TYPE_BASE_GRAPHICS));
 
				}
 
				size->width = d.width + WD_MATRIX_RIGHT + WD_MATRIX_LEFT;
 
				break;
 
			}
 

	
 
			case NCLWW_MATRIX:
 
				resize->height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
 
				size->height = 10 * resize->height;
 
				break;
 
		}
 
	}
 

	
 

	
 
	virtual void DrawWidget(const Rect &r, int widget) const
 
	{
 
		switch (widget) {
 
			case NCLWW_FILTER_CAPT:
 
				DrawString(r.left, r.right, r.top, STR_CONTENT_FILTER_TITLE, TC_FROMSTRING, SA_RIGHT);
 
				break;
 

	
 
			case NCLWW_DETAILS:
 
				this->DrawDetails(r);
 
				break;
 

	
 
			case NCLWW_MATRIX:
 
				this->DrawMatrix(r);
 
				break;
 
		}
 
	}
 

	
 
	virtual void OnPaint()
 
	{
src/network/network_gamelist.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/**
 
 * @file network_gamelist.cpp This file handles the GameList
 
 * Also, it handles the request to a server for data about the server
 
 */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../stdafx.h"
 
#include "../debug.h"
 
#include "../thread/thread.h"
 
#include "network_internal.h"
 
#include "network_udp.h"
 
#include "network_gamelist.h"
 

	
 
NetworkGameList *_network_game_list = NULL;
 

	
 
static ThreadMutex *_network_game_list_mutex = ThreadMutex::New();
 
static NetworkGameList *_network_game_delayed_insertion_list = NULL;
 

	
 
/** Add a new item to the linked gamelist, but do it delayed in the next tick
 
 * or so to prevent race conditions.
 
 * @param item the item to add. Will be freed once added.
 
 */
 
void NetworkGameListAddItemDelayed(NetworkGameList *item)
 
{
 
	_network_game_list_mutex->BeginCritical();
 
	item->next = _network_game_delayed_insertion_list;
 
	_network_game_delayed_insertion_list = item;
 
	_network_game_list_mutex->EndCritical();
 
}
 

	
 
/** Perform the delayed (thread safe) insertion into the game list */
 
static void NetworkGameListHandleDelayedInsert()
 
{
 
	_network_game_list_mutex->BeginCritical();
 
	while (_network_game_delayed_insertion_list != NULL) {
 
		NetworkGameList *ins_item = _network_game_delayed_insertion_list;
 
		_network_game_delayed_insertion_list = ins_item->next;
 

	
 
		NetworkGameList *item = NetworkGameListAddItem(ins_item->address);
 

	
 
		if (item != NULL) {
 
			if (StrEmpty(item->info.server_name)) {
 
				ClearGRFConfigList(&item->info.grfconfig);
 
				memset(&item->info, 0, sizeof(item->info));
 
				strecpy(item->info.server_name, ins_item->info.server_name, lastof(item->info.server_name));
 
				strecpy(item->info.hostname, ins_item->info.hostname, lastof(item->info.hostname));
 
				item->online = false;
 
			}
 
			item->manually |= ins_item->manually;
 
			if (item->manually) NetworkRebuildHostList();
 
			UpdateNetworkGameWindow(false);
 
		}
 
		free(ins_item);
 
	}
 
	_network_game_list_mutex->EndCritical();
 
}
 

	
 
/** Add a new item to the linked gamelist. If the IP and Port match
 
 * return the existing item instead of adding it again
 
 * @param address the address of the to-be added item
 
 * @param port the port the server is running on
 
 * @return a point to the newly added or already existing item */
 
NetworkGameList *NetworkGameListAddItem(NetworkAddress address)
 
{
 
	const char *hostname = address.GetHostname();
 

	
 
	/* Do not query the 'any' address. */
 
	if (StrEmpty(hostname) ||
 
			strcmp(hostname, "0.0.0.0") == 0 ||
 
			strcmp(hostname, "::") == 0) {
 
		return NULL;
 
	}
 

	
 
	NetworkGameList *item, *prev_item;
 

	
 
	prev_item = NULL;
 
	for (item = _network_game_list; item != NULL; item = item->next) {
 
		if (item->address == address) return item;
 
		prev_item = item;
 
	}
 

	
 
	item = CallocT<NetworkGameList>(1);
 
	item->next = NULL;
 
	item->address = address;
 

	
 
	if (prev_item == NULL) {
 
		_network_game_list = item;
 
	} else {
 
		prev_item->next = item;
 
	}
 
	DEBUG(net, 4, "[gamelist] added server to list");
 

	
 
	UpdateNetworkGameWindow(false);
 

	
 
	return item;
 
}
 

	
 
/** Remove an item from the gamelist linked list
 
 * @param remove pointer to the item to be removed */
 
void NetworkGameListRemoveItem(NetworkGameList *remove)
 
{
 
	NetworkGameList *prev_item = NULL;
 
	for (NetworkGameList *item = _network_game_list; item != NULL; item = item->next) {
 
		if (remove == item) {
 
			if (prev_item == NULL) {
 
				_network_game_list = remove->next;
 
			} else {
 
				prev_item->next = remove->next;
 
			}
 

	
 
			/* Remove GRFConfig information */
 
			ClearGRFConfigList(&remove->info.grfconfig);
 
			free(remove);
 
			remove = NULL;
 

	
 
			DEBUG(net, 4, "[gamelist] removed server from list");
 
			NetworkRebuildHostList();
 
			UpdateNetworkGameWindow(false);
 
			return;
 
		}
 
		prev_item = item;
 
	}
 
}
 

	
 
enum {
 
	MAX_GAME_LIST_REQUERY_COUNT  = 10, ///< How often do we requery in number of times per server?
 
	REQUERY_EVERY_X_GAMELOOPS    = 60, ///< How often do we requery in time?
 
	REFRESH_GAMEINFO_X_REQUERIES = 50, ///< Refresh the game info itself after REFRESH_GAMEINFO_X_REQUERIES * REQUERY_EVERY_X_GAMELOOPS game loops
 
};
 
static const uint MAX_GAME_LIST_REQUERY_COUNT  = 10; ///< How often do we requery in number of times per server?
 
static const uint REQUERY_EVERY_X_GAMELOOPS    = 60; ///< How often do we requery in time?
 
static const uint REFRESH_GAMEINFO_X_REQUERIES = 50; ///< Refresh the game info itself after REFRESH_GAMEINFO_X_REQUERIES * REQUERY_EVERY_X_GAMELOOPS game loops
 

	
 
/** Requeries the (game) servers we have not gotten a reply from */
 
void NetworkGameListRequery()
 
{
 
	NetworkGameListHandleDelayedInsert();
 

	
 
	static uint8 requery_cnt = 0;
 

	
 
	if (++requery_cnt < REQUERY_EVERY_X_GAMELOOPS) return;
 
	requery_cnt = 0;
 

	
 
	for (NetworkGameList *item = _network_game_list; item != NULL; item = item->next) {
 
		item->retries++;
 
		if (item->retries < REFRESH_GAMEINFO_X_REQUERIES && (item->online || item->retries >= MAX_GAME_LIST_REQUERY_COUNT)) continue;
 

	
 
		/* item gets mostly zeroed by NetworkUDPQueryServer */
 
		uint8 retries = item->retries;
 
		NetworkUDPQueryServer(NetworkAddress(item->address));
 
		item->retries = (retries >= REFRESH_GAMEINFO_X_REQUERIES) ? 0 : retries;
 
	}
 
}
 

	
 
/**
 
 * Rebuild the GRFConfig's of the servers in the game list as we did
 
 * a rescan and might have found new NewGRFs.
 
 */
 
void NetworkAfterNewGRFScan()
 
{
 
	for (NetworkGameList *item = _network_game_list; item != NULL; item = item->next) {
 
		/* Reset compatability state */
 
		item->info.compatible = item->info.version_compatible;
 

	
 
		for (GRFConfig *c = item->info.grfconfig; c != NULL; c = c->next) {
 
			assert(HasBit(c->flags, GCF_COPY));
 

	
 
			const GRFConfig *f = FindGRFConfig(c->ident.grfid, c->ident.md5sum);
 
			if (f == NULL) {
 
				/* Don't know the GRF, so mark game incompatible and the (possibly)
 
				 * already resolved name for this GRF (another server has sent the
 
				 * name of the GRF already */
 
				c->name   = FindUnknownGRFName(c->ident.grfid, c->ident.md5sum, true);
 
				c->status = GCS_NOT_FOUND;
 

	
 
				/* If we miss a file, we're obviously incompatible */
 
				item->info.compatible = false;
 
			} else {
 
				c->filename  = f->filename;
 
				c->name      = f->name;
 
				c->info      = f->info;
 
				c->status    = GCS_UNKNOWN;
 
			}
 
		}
 
	}
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/network/network_udp.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/**
 
 * @file network_udp.cpp This file handles the UDP related communication.
 
 *
 
 * This is the GameServer <-> MasterServer and GameServer <-> GameClient
 
 * communication before the game is being joined.
 
 */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../stdafx.h"
 
#include "../date_func.h"
 
#include "../map_func.h"
 
#include "../debug.h"
 
#include "network_gamelist.h"
 
#include "network_internal.h"
 
#include "network_udp.h"
 
#include "network.h"
 
#include "../core/endian_func.hpp"
 
#include "../company_base.h"
 
#include "../thread/thread.h"
 
#include "../rev.h"
 

	
 
#include "core/udp.h"
 

	
 
static ThreadMutex *_network_udp_mutex = ThreadMutex::New();
 

	
 
/** Session key to register ourselves to the master server */
 
static uint64 _session_key = 0;
 

	
 
enum {
 
	ADVERTISE_NORMAL_INTERVAL = 30000, // interval between advertising in ticks (15 minutes)
 
	ADVERTISE_RETRY_INTERVAL  =   300, // readvertise when no response after this many ticks (9 seconds)
 
	ADVERTISE_RETRY_TIMES     =     3  // give up readvertising after this much failed retries
 
};
 
static const uint ADVERTISE_NORMAL_INTERVAL = 30000; ///< interval between advertising in ticks (15 minutes)
 
static const uint ADVERTISE_RETRY_INTERVAL  =   300; ///< readvertise when no response after this many ticks (9 seconds)
 
static const uint ADVERTISE_RETRY_TIMES     =     3; ///< give up readvertising after this much failed retries
 

	
 
NetworkUDPSocketHandler *_udp_client_socket = NULL; ///< udp client socket
 
NetworkUDPSocketHandler *_udp_server_socket = NULL; ///< udp server socket
 
NetworkUDPSocketHandler *_udp_master_socket = NULL; ///< udp master socket
 

	
 
///*** Communication with the masterserver ***/
 

	
 
class MasterNetworkUDPSocketHandler : public NetworkUDPSocketHandler {
 
protected:
 
	DECLARE_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_ACK_REGISTER);
 
	DECLARE_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_SESSION_KEY);
 
public:
 
	MasterNetworkUDPSocketHandler(NetworkAddressList *addresses) : NetworkUDPSocketHandler(addresses) {}
 
	virtual ~MasterNetworkUDPSocketHandler() {}
 
};
 

	
 
DEF_UDP_RECEIVE_COMMAND(Master, PACKET_UDP_MASTER_ACK_REGISTER)
 
{
 
	_network_advertise_retries = 0;
 
	DEBUG(net, 2, "[udp] advertising on master server successful (%s)", NetworkAddress::AddressFamilyAsString(client_addr->GetAddress()->ss_family));
 

	
 
	/* We are advertised, but we don't want to! */
 
	if (!_settings_client.network.server_advertise) NetworkUDPRemoveAdvertise(false);
 
}
 

	
 
DEF_UDP_RECEIVE_COMMAND(Master, PACKET_UDP_MASTER_SESSION_KEY)
 
{
 
	_session_key = p->Recv_uint64();
 
	DEBUG(net, 2, "[udp] received new session key from master server (%s)", NetworkAddress::AddressFamilyAsString(client_addr->GetAddress()->ss_family));
 
}
 

	
 
///*** Communication with clients (we are server) ***/
 

	
 
class ServerNetworkUDPSocketHandler : public NetworkUDPSocketHandler {
 
protected:
 
	DECLARE_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER);
 
	DECLARE_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO);
 
	DECLARE_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_GET_NEWGRFS);
 
public:
 
	ServerNetworkUDPSocketHandler(NetworkAddressList *addresses) : NetworkUDPSocketHandler(addresses) {}
 
	virtual ~ServerNetworkUDPSocketHandler() {}
 
};
 

	
 
DEF_UDP_RECEIVE_COMMAND(Server, PACKET_UDP_CLIENT_FIND_SERVER)
 
{
 
	/* Just a fail-safe.. should never happen */
 
	if (!_network_udp_server) {
 
		return;
 
	}
 

	
 
	NetworkGameInfo ngi;
 

	
 
	/* Update some game_info */
 
	ngi.clients_on     = _network_game_info.clients_on;
 
	ngi.start_date     = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
 

	
 
	ngi.server_lang    = _settings_client.network.server_lang;
 
	ngi.use_password   = !StrEmpty(_settings_client.network.server_password);
 
	ngi.clients_max    = _settings_client.network.max_clients;
 
	ngi.companies_on   = (byte)Company::GetNumItems();
 
	ngi.companies_max  = _settings_client.network.max_companies;
 
	ngi.spectators_on  = NetworkSpectatorCount();
 
	ngi.spectators_max = _settings_client.network.max_spectators;
 
	ngi.game_date      = _date;
 
	ngi.map_width      = MapSizeX();
 
	ngi.map_height     = MapSizeY();
 
	ngi.map_set        = _settings_game.game_creation.landscape;
 
	ngi.dedicated      = _network_dedicated;
 
	ngi.grfconfig      = _grfconfig;
 

	
 
	strecpy(ngi.map_name, _network_game_info.map_name, lastof(ngi.map_name));
 
	strecpy(ngi.server_name, _settings_client.network.server_name, lastof(ngi.server_name));
 
	strecpy(ngi.server_revision, _openttd_revision, lastof(ngi.server_revision));
 

	
 
	Packet packet(PACKET_UDP_SERVER_RESPONSE);
 
	this->Send_NetworkGameInfo(&packet, &ngi);
 

	
 
	/* Let the client know that we are here */
 
	this->SendPacket(&packet, client_addr);
 

	
 
	DEBUG(net, 2, "[udp] queried from %s", client_addr->GetHostname());
 
}
 

	
 
DEF_UDP_RECEIVE_COMMAND(Server, PACKET_UDP_CLIENT_DETAIL_INFO)
 
{
 
	/* Just a fail-safe.. should never happen */
 
	if (!_network_udp_server) return;
 

	
 
	Packet packet(PACKET_UDP_SERVER_DETAIL_INFO);
 

	
 
	/* Send the amount of active companies */
 
	packet.Send_uint8 (NETWORK_COMPANY_INFO_VERSION);
 
	packet.Send_uint8 ((uint8)Company::GetNumItems());
 

	
 
	/* Fetch the latest version of the stats */
 
	NetworkCompanyStats company_stats[MAX_COMPANIES];
 
	NetworkPopulateCompanyStats(company_stats);
 

	
 
	Company *company;
 
	/* Go through all the companies */
 
	FOR_ALL_COMPANIES(company) {
 
		/* Send the information */
 
		this->Send_CompanyInformation(&packet, company, &company_stats[company->index]);
 
	}
 

	
 
	this->SendPacket(&packet, client_addr);
 
}
 

	
 
/**
 
 * A client has requested the names of some NewGRFs.
 
 *
 
 * Replying this can be tricky as we have a limit of SEND_MTU bytes
 
 * in the reply packet and we can send up to 100 bytes per NewGRF
 
 * (GRF ID, MD5sum and NETWORK_GRF_NAME_LENGTH bytes for the name).
 
 * As SEND_MTU is _much_ less than 100 * NETWORK_MAX_GRF_COUNT, it
 
 * could be that a packet overflows. To stop this we only reply
 
 * with the first N NewGRFs so that if the first N + 1 NewGRFs
 
 * would be sent, the packet overflows.
 
 * in_reply and in_reply_count are used to keep a list of GRFs to
 
 * send in the reply.
 
 */
 
DEF_UDP_RECEIVE_COMMAND(Server, PACKET_UDP_CLIENT_GET_NEWGRFS)
 
{
 
	uint8 num_grfs;
 
	uint i;
 

	
 
	const GRFConfig *in_reply[NETWORK_MAX_GRF_COUNT];
 
	uint8 in_reply_count = 0;
 
	size_t packet_len = 0;
 

	
 
	DEBUG(net, 6, "[udp] newgrf data request from %s", client_addr->GetAddressAsString());
 

	
 
	num_grfs = p->Recv_uint8 ();
 
	if (num_grfs > NETWORK_MAX_GRF_COUNT) return;
 

	
 
	for (i = 0; i < num_grfs; i++) {
 
		GRFIdentifier c;
 
		const GRFConfig *f;
 

	
 
		this->Recv_GRFIdentifier(p, &c);
 

	
 
		/* Find the matching GRF file */
 
		f = FindGRFConfig(c.grfid, c.md5sum);
 
		if (f == NULL) continue; // The GRF is unknown to this server
 

	
 
		/* If the reply might exceed the size of the packet, only reply
 
		 * the current list and do not send the other data.
 
		 * The name could be an empty string, if so take the filename. */
 
		packet_len += sizeof(c.grfid) + sizeof(c.md5sum) +
 
				min(strlen(f->GetName()) + 1, (size_t)NETWORK_GRF_NAME_LENGTH);
 
		if (packet_len > SEND_MTU - 4) { // 4 is 3 byte header + grf count in reply
 
			break;
 
		}
 
		in_reply[in_reply_count] = f;
 
		in_reply_count++;
 
	}
 

	
 
	if (in_reply_count == 0) return;
 

	
 
	Packet packet(PACKET_UDP_SERVER_NEWGRFS);
 
	packet.Send_uint8(in_reply_count);
 
	for (i = 0; i < in_reply_count; i++) {
 
		char name[NETWORK_GRF_NAME_LENGTH];
 

	
 
		/* The name could be an empty string, if so take the filename */
 
		strecpy(name, in_reply[i]->GetName(), lastof(name));
 
		this->Send_GRFIdentifier(&packet, &in_reply[i]->ident);
 
		packet.Send_string(name);
 
	}
 

	
 
	this->SendPacket(&packet, client_addr);
 
}
 

	
 
///*** Communication with servers (we are client) ***/
 

	
 
class ClientNetworkUDPSocketHandler : public NetworkUDPSocketHandler {
 
protected:
 
	DECLARE_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE);
 
	DECLARE_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_RESPONSE_LIST);
 
	DECLARE_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_NEWGRFS);
 
	virtual void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config);
 
public:
 
	virtual ~ClientNetworkUDPSocketHandler() {}
 
};
 

	
 
DEF_UDP_RECEIVE_COMMAND(Client, PACKET_UDP_SERVER_RESPONSE)
 
{
 
	NetworkGameList *item;
 

	
 
	/* Just a fail-safe.. should never happen */
 
	if (_network_udp_server) return;
 

	
src/newgrf.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file newgrf.cpp Base of all NewGRF support. */
 

	
 
#include "stdafx.h"
 

	
 
#include <stdarg.h>
 

	
 
#include "debug.h"
 
#include "fileio_func.h"
 
#include "engine_func.h"
 
#include "engine_base.h"
 
#include "variables.h"
 
#include "bridge.h"
 
#include "town.h"
 
#include "newgrf_engine.h"
 
#include "newgrf_text.h"
 
#include "fontcache.h"
 
#include "currency.h"
 
#include "landscape.h"
 
#include "newgrf.h"
 
#include "newgrf_cargo.h"
 
#include "newgrf_house.h"
 
#include "newgrf_sound.h"
 
#include "newgrf_station.h"
 
#include "industry.h"
 
#include "newgrf_canal.h"
 
#include "newgrf_commons.h"
 
#include "newgrf_townname.h"
 
#include "newgrf_industries.h"
 
#include "newgrf_airporttiles.h"
 
#include "newgrf_airport.h"
 
#include "rev.h"
 
#include "fios.h"
 
#include "rail.h"
 
#include "strings_func.h"
 
#include "date_func.h"
 
#include "string_func.h"
 
#include "network/network.h"
 
#include <map>
 
#include "core/alloc_type.hpp"
 
#include "core/mem_func.hpp"
 
#include "smallmap_gui.h"
 

	
 
#include "table/strings.h"
 
#include "table/build_industry.h"
 

	
 
/* TTDPatch extended GRF format codec
 
 * (c) Petr Baudis 2004 (GPL'd)
 
 * Changes by Florian octo Forster are (c) by the OpenTTD development team.
 
 *
 
 * Contains portions of documentation by TTDPatch team.
 
 * Thanks especially to Josef Drexler for the documentation as well as a lot
 
 * of help at #tycoon. Also thanks to Michael Blunck for is GRF files which
 
 * served as subject to the initial testing of this codec. */
 

	
 

	
 
static int _skip_sprites; // XXX
 
static uint _file_index; // XXX
 

	
 
static SmallVector<GRFFile *, 16> _grf_files;
 

	
 
static GRFFile *_cur_grffile;
 
static SpriteID _cur_spriteid;
 
static GrfLoadingStage _cur_stage;
 
static uint32 _nfo_line;
 

	
 
static GRFConfig *_cur_grfconfig;
 

	
 
/* Miscellaneous GRF features, set by Action 0x0D, parameter 0x9E */
 
static byte _misc_grf_features = 0;
 

	
 
/* 32 * 8 = 256 flags. Apparently TTDPatch uses this many.. */
 
static uint32 _ttdpatch_flags[8];
 

	
 
/* Indicates which are the newgrf features currently loaded ingame */
 
GRFLoadedFeatures _loaded_newgrf_features;
 

	
 
enum GrfDataType {
 
	GDT_SOUND,
 
};
 

	
 
static byte _grf_data_blocks;
 
static GrfDataType _grf_data_type;
 

	
 
class OTTDByteReaderSignal { };
 

	
 
class ByteReader {
 
protected:
 
	byte *data;
 
	byte *end;
 

	
 
public:
 
	ByteReader(byte *data, byte *end) : data(data), end(end) { }
 

	
 
	FORCEINLINE byte ReadByte()
 
	{
 
		if (data < end) return *(data)++;
 
		throw OTTDByteReaderSignal();
 
	}
 

	
 
	uint16 ReadWord()
 
	{
 
		uint16 val = ReadByte();
 
		return val | (ReadByte() << 8);
 
	}
 

	
 
	uint16 ReadExtendedByte()
 
	{
 
		uint16 val = ReadByte();
 
		return val == 0xFF ? ReadWord() : val;
 
	}
 

	
 
	uint32 ReadDWord()
 
	{
 
		uint32 val = ReadWord();
 
		return val | (ReadWord() << 16);
 
	}
 

	
 
	uint32 ReadVarSize(byte size)
 
	{
 
		switch (size) {
 
			case 1: return ReadByte();
 
			case 2: return ReadWord();
 
			case 4: return ReadDWord();
 
			default:
 
				NOT_REACHED();
 
				return 0;
 
		}
 
	}
 

	
 
	const char *ReadString()
 
	{
 
		char *string = reinterpret_cast<char *>(data);
 
		size_t string_length = ttd_strnlen(string, Remaining());
 

	
 
		if (string_length == Remaining()) {
 
			/* String was not NUL terminated, so make sure it is now. */
 
			string[string_length - 1] = '\0';
 
			grfmsg(7, "String was not terminated with a zero byte.");
 
		} else {
 
			/* Increase the string length to include the NUL byte. */
 
			string_length++;
 
		}
 
		Skip(string_length);
 

	
 
		return string;
 
	}
 

	
 
	FORCEINLINE size_t Remaining() const
 
	{
 
		return end - data;
 
	}
 

	
 
	FORCEINLINE bool HasData() const
 
	{
 
		return data < end;
 
	}
 

	
 
	FORCEINLINE byte *Data()
 
	{
 
		return data;
 
	}
 

	
 
	FORCEINLINE void Skip(size_t len)
 
	{
 
		data += len;
 
		/* It is valid to move the buffer to exactly the end of the data,
 
		 * as there may not be any more data read. */
 
		if (data > end) throw OTTDByteReaderSignal();
 
	}
 
};
 

	
 
typedef void (*SpecialSpriteHandler)(ByteReader *buf);
 

	
 
enum {
 
	MAX_STATIONS = 256,
 
};
 
static const uint MAX_STATIONS = 256;
 

	
 
/* Temporary data used when loading only */
 
struct GRFTempEngineData {
 
	uint16 cargo_allowed;
 
	uint16 cargo_disallowed;
 
	RailTypeLabel railtypelabel;
 
	bool refitmask_valid;    ///< Did the newgrf set any refittability property? If not, default refittability will be applied.
 
	uint8 rv_max_speed;      ///< Temporary storage of RV prop 15, maximum speed in mph/0.8
 
};
 

	
 
static GRFTempEngineData *_gted;
 

	
 
/* Contains the GRF ID of the owner of a vehicle if it has been reserved.
 
 * GRM for vehicles is only used if dynamic engine allocation is disabled,
 
 * so 256 is the number of original engines. */
 
static uint32 _grm_engines[256];
 

	
 
/* Contains the GRF ID of the owner of a cargo if it has been reserved */
 
static uint32 _grm_cargos[NUM_CARGO * 2];
 

	
 
struct GRFLocation {
 
	uint32 grfid;
 
	uint32 nfoline;
 

	
 
	GRFLocation(uint32 grfid, uint32 nfoline) : grfid(grfid), nfoline(nfoline) { }
 

	
 
	bool operator<(const GRFLocation &other) const
 
	{
 
		return this->grfid < other.grfid || (this->grfid == other.grfid && this->nfoline < other.nfoline);
 
	}
 

	
 
	bool operator == (const GRFLocation &other) const
 
	{
 
		return this->grfid == other.grfid && this->nfoline == other.nfoline;
 
	}
 
};
 

	
 
static std::map<GRFLocation, SpriteID> _grm_sprites;
 
typedef std::map<GRFLocation, byte*> GRFLineToSpriteOverride;
 
static GRFLineToSpriteOverride _grf_line_to_action6_sprite_override;
 

	
 
/** DEBUG() function dedicated to newGRF debugging messages
 
 * Function is essentialy the same as DEBUG(grf, severity, ...) with the
 
 * addition of file:line information when parsing grf files.
 
 * NOTE: for the above reason(s) grfmsg() should ONLY be used for
 
 * loading/parsing grf files, not for runtime debug messages as there
 
 * is no file information available during that time.
 
 * @param severity debugging severity level, see debug.h
 
 * @param str message in printf() format */
 
void CDECL grfmsg(int severity, const char *str, ...)
 
{
 
	char buf[1024];
 
	va_list va;
 

	
 
	va_start(va, str);
 
	vsnprintf(buf, sizeof(buf), str, va);
 
	va_end(va);
 

	
 
	DEBUG(grf, severity, "[%s:%d] %s", _cur_grfconfig->filename, _nfo_line, buf);
 
}
 

	
 
static GRFFile *GetFileByGRFID(uint32 grfid)
 
{
 
	const GRFFile * const *end = _grf_files.End();
 
	for (GRFFile * const *file = _grf_files.Begin(); file != end; file++) {
 
		if ((*file)->grfid == grfid) return *file;
 
	}
 
	return NULL;
 
}
 

	
 
static GRFFile *GetFileByFilename(const char *filename)
 
{
 
	const GRFFile * const *end = _grf_files.End();
 
	for (GRFFile * const *file = _grf_files.Begin(); file != end; file++) {
 
		if (strcmp((*file)->filename, filename) == 0) return *file;
 
	}
 
	return NULL;
 
}
 

	
 
/** Reset all NewGRFData that was used only while processing data */
 
static void ClearTemporaryNewGRFData(GRFFile *gf)
 
{
 
	/* Clear the GOTO labels used for GRF processing */
 
	for (GRFLabel *l = gf->label; l != NULL;) {
 
		GRFLabel *l2 = l->next;
 
		free(l);
 
		l = l2;
 
	}
 
	gf->label = NULL;
 

	
 
	/* Clear the list of spritegroups */
 
	free(gf->spritegroups);
 
	gf->spritegroups = NULL;
 
	gf->spritegroups_count = 0;
 
}
 

	
 

	
 
typedef std::map<StringID *, uint32> StringIDToGRFIDMapping;
 
static StringIDToGRFIDMapping _string_to_grf_mapping;
 

	
 
/** Used when setting an object's property to map to the GRF's strings
 
 * while taking in consideration the "drift" between TTDPatch string system and OpenTTD's one
 
 * @param grfid Id of the grf file
 
 * @param str StringID that we want to have the equivalent in OoenTTD
 
 * @return the properly adjusted StringID
 
 */
 
StringID MapGRFStringID(uint32 grfid, StringID str)
 
{
 
	/* 0xD0 and 0xDC stand for all the TextIDs in the range
 
	 * of 0xD000 (misc graphics texts) and 0xDC00 (misc persistent texts).
 
	 * These strings are unique to each grf file, and thus require to be used with the
 
	 * grfid in which they are declared */
 
	switch (GB(str, 8, 8)) {
 
		case 0xD0: case 0xD1: case 0xD2: case 0xD3:
 
		case 0xDC:
 
			return GetGRFStringID(grfid, str);
 

	
 
		case 0xD4: case 0xD5: case 0xD6: case 0xD7:
 
			/* Strings embedded via 0x81 have 0x400 added to them (no real
 
			 * explanation why...) */
 
			return GetGRFStringID(grfid, str - 0x400);
 

	
 
		default: break;
 
	}
 

	
 
	return TTDPStringIDToOTTDStringIDMapping(str);
 
}
 

	
 
static inline uint8 MapDOSColour(uint8 colour)
 
{
 
	extern const byte _palmap_d2w[];
 
	return (_use_palette == PAL_DOS ? colour : _palmap_d2w[colour]);
 
}
 

	
 
static std::map<uint32, uint32> _grf_id_overrides;
 

	
 
static void SetNewGRFOverride(uint32 source_grfid, uint32 target_grfid)
 
{
 
	_grf_id_overrides[source_grfid] = target_grfid;
 
	grfmsg(5, "SetNewGRFOverride: Added override of 0x%X to 0x%X", BSWAP32(source_grfid), BSWAP32(target_grfid));
 
}
 

	
 
/**
 
 * Returns the engine associated to a certain internal_id, resp. allocates it.
 
 * @param file NewGRF that wants to change the engine
 
 * @param type Vehicle type
 
 * @param internal_id Engine ID inside the NewGRF
 
 * @param static_access If the engine is not present, return NULL instead of allocating a new engine. (Used for static Action 0x04)
 
 * @return The requested engine
 
 */
 
static Engine *GetNewEngine(const GRFFile *file, VehicleType type, uint16 internal_id, bool static_access = false)
 
{
 
	/* Hack for add-on GRFs that need to modify another GRF's engines. This lets
 
	 * them use the same engine slots. */
 
	uint32 scope_grfid = INVALID_GRFID; // If not using dynamic_engines, all newgrfs share their ID range
 
	if (_settings_game.vehicle.dynamic_engines) {
 
		/* If dynamic_engies is enabled, there can be multiple independent ID ranges. */
 
		scope_grfid = file->grfid;
 
		uint32 override = _grf_id_overrides[file->grfid];
 
		if (override != 0) {
 
			scope_grfid = override;
 
			const GRFFile *grf_match = GetFileByGRFID(override);
 
			if (grf_match == NULL) {
 
				grfmsg(5, "Tried mapping from GRFID %x to %x but target is not loaded", BSWAP32(file->grfid), BSWAP32(override));
 
			} else {
 
				grfmsg(5, "Mapping from GRFID %x to %x", BSWAP32(file->grfid), BSWAP32(override));
 
			}
 
		}
 

	
 
		/* Check if the engine is registered in the override manager */
 
		EngineID engine = _engine_mngr.GetID(type, internal_id, scope_grfid);
 
		if (engine != INVALID_ENGINE) {
 
			Engine *e = Engine::Get(engine);
 
			if (e->grffile == NULL) e->grffile = file;
 
			return e;
 
		}
 
	}
 

	
 
	/* Check if there is an unreserved slot */
 
	EngineID engine = _engine_mngr.GetID(type, internal_id, INVALID_GRFID);
 
	if (engine != INVALID_ENGINE) {
 
		Engine *e = Engine::Get(engine);
 

	
 
		if (e->grffile == NULL) {
 
			e->grffile = file;
 
			grfmsg(5, "Replaced engine at index %d for GRFID %x, type %d, index %d", e->index, BSWAP32(file->grfid), type, internal_id);
 
		}
 

	
 
		/* Reserve the engine slot */
 
		if (!static_access) {
 
			EngineIDMapping *eid = _engine_mngr.Get(engine);
 
			eid->grfid           = scope_grfid; // Note: this is INVALID_GRFID if dynamic_engines is disabled, so no reservation
 
@@ -6420,385 +6418,385 @@ static void CalculateRefitMasks()
 
					FOR_ALL_CARGOSPECS(cs) {
 
						if (HasBit(ei->refit_mask, cs->bitnum)) SetBit(xor_mask, cs->Index());
 
					}
 
				}
 
			}
 

	
 
			if (_gted[engine].cargo_allowed != 0) {
 
				/* Build up the list of cargo types from the set cargo classes. */
 
				const CargoSpec *cs;
 
				FOR_ALL_CARGOSPECS(cs) {
 
					if (_gted[engine].cargo_allowed    & cs->classes) SetBit(mask,     cs->Index());
 
					if (_gted[engine].cargo_disallowed & cs->classes) SetBit(not_mask, cs->Index());
 
				}
 
			}
 
		} else {
 
			/* Don't apply default refit mask to wagons nor engines with no capacity */
 
			if (e->type != VEH_TRAIN || (e->u.rail.capacity != 0 && e->u.rail.railveh_type != RAILVEH_WAGON)) {
 
				const CargoLabel *cl = _default_refitmasks[e->type];
 
				for (uint i = 0;; i++) {
 
					if (cl[i] == 0) break;
 

	
 
					CargoID cargo = GetCargoIDByLabel(cl[i]);
 
					if (cargo == CT_INVALID) continue;
 

	
 
					SetBit(xor_mask, cargo);
 
				}
 
			}
 
		}
 

	
 
		ei->refit_mask = ((mask & ~not_mask) ^ xor_mask) & _cargo_mask;
 

	
 
		/* Check if this engine's cargo type is valid. If not, set to the first refittable
 
		 * cargo type. Finally disable the vehicle, if there is still no cargo. */
 
		if (ei->cargo_type == CT_INVALID && ei->refit_mask != 0) ei->cargo_type = (CargoID)FindFirstBit(ei->refit_mask);
 
		if (ei->cargo_type == CT_INVALID) ei->climates = 0x80;
 

	
 
		/* Clear refit_mask for not refittable ships */
 
		if (e->type == VEH_SHIP && !e->u.ship.old_refittable) ei->refit_mask = 0;
 
	}
 
}
 

	
 
/** Add all new houses to the house array. House properties can be set at any
 
 * time in the GRF file, so we can only add a house spec to the house array
 
 * after the file has finished loading. We also need to check the dates, due to
 
 * the TTDPatch behaviour described below that we need to emulate. */
 
static void FinaliseHouseArray()
 
{
 
	/* If there are no houses with start dates before 1930, then all houses
 
	 * with start dates of 1930 have them reset to 0. This is in order to be
 
	 * compatible with TTDPatch, where if no houses have start dates before
 
	 * 1930 and the date is before 1930, the game pretends that this is 1930.
 
	 * If there have been any houses defined with start dates before 1930 then
 
	 * the dates are left alone.
 
	 * On the other hand, why 1930? Just 'fix' the houses with the lowest
 
	 * minimum introduction date to 0.
 
	 */
 
	Year min_year = MAX_YEAR;
 

	
 
	const GRFFile * const *end = _grf_files.End();
 
	for (GRFFile **file = _grf_files.Begin(); file != end; file++) {
 
		HouseSpec **&housespec = (*file)->housespec;
 
		if (housespec == NULL) continue;
 

	
 
		for (int i = 0; i < HOUSE_MAX; i++) {
 
			HouseSpec *hs = housespec[i];
 

	
 
			if (hs == NULL) continue;
 

	
 
			const HouseSpec *next1 = (i + 1 < HOUSE_MAX ? housespec[i + 1] : NULL);
 
			const HouseSpec *next2 = (i + 2 < HOUSE_MAX ? housespec[i + 2] : NULL);
 
			const HouseSpec *next3 = (i + 3 < HOUSE_MAX ? housespec[i + 3] : NULL);
 

	
 
			if (((hs->building_flags & BUILDING_HAS_2_TILES) != 0 &&
 
						(next1 == NULL || !next1->enabled || (next1->building_flags & BUILDING_HAS_1_TILE) != 0)) ||
 
					((hs->building_flags & BUILDING_HAS_4_TILES) != 0 &&
 
						(next2 == NULL || !next2->enabled || (next2->building_flags & BUILDING_HAS_1_TILE) != 0 ||
 
						next3 == NULL || !next3->enabled || (next3->building_flags & BUILDING_HAS_1_TILE) != 0))) {
 
				hs->enabled = false;
 
				DEBUG(grf, 1, "FinaliseHouseArray: %s defines house %d as multitile, but no suitable tiles follow. Disabling house.", (*file)->filename, hs->local_id);
 
				continue;
 
			}
 

	
 
			/* Some places sum population by only counting north tiles. Other places use all tiles causing desyncs.
 
			 * As the newgrf specs define population to be zero for non-north tiles, we just disable the offending house.
 
			 * If you want to allow non-zero populations somewhen, make sure to sum the population of all tiles in all places. */
 
			if (((hs->building_flags & BUILDING_HAS_2_TILES) != 0 && next1->population != 0) ||
 
					((hs->building_flags & BUILDING_HAS_4_TILES) != 0 && (next2->population != 0 || next3->population != 0))) {
 
				hs->enabled = false;
 
				DEBUG(grf, 1, "FinaliseHouseArray: %s defines multitile house %d with non-zero population on additional tiles. Disabling house.", (*file)->filename, hs->local_id);
 
				continue;
 
			}
 

	
 
			_house_mngr.SetEntitySpec(hs);
 
			if (hs->min_year < min_year) min_year = hs->min_year;
 
		}
 
	}
 

	
 
	if (min_year != 0) {
 
		for (int i = 0; i < HOUSE_MAX; i++) {
 
			HouseSpec *hs = HouseSpec::Get(i);
 

	
 
			if (hs->enabled && hs->min_year == min_year) hs->min_year = 0;
 
		}
 
	}
 
}
 

	
 
/** Add all new industries to the industry array. Industry properties can be set at any
 
 * time in the GRF file, so we can only add a industry spec to the industry array
 
 * after the file has finished loading. */
 
static void FinaliseIndustriesArray()
 
{
 
	const GRFFile * const *end = _grf_files.End();
 
	for (GRFFile **file = _grf_files.Begin(); file != end; file++) {
 
		IndustrySpec **&industryspec = (*file)->industryspec;
 
		IndustryTileSpec **&indtspec = (*file)->indtspec;
 
		if (industryspec != NULL) {
 
			for (int i = 0; i < NUM_INDUSTRYTYPES; i++) {
 
				IndustrySpec *indsp = industryspec[i];
 

	
 
				if (indsp != NULL && indsp->enabled) {
 
					StringID strid;
 
					/* process the conversion of text at the end, so to be sure everything will be fine
 
					 * and available.  Check if it does not return undefind marker, which is a very good sign of a
 
					 * substitute industry who has not changed the string been examined, thus using it as such */
 
					strid = GetGRFStringID(indsp->grf_prop.grffile->grfid, indsp->name);
 
					if (strid != STR_UNDEFINED) indsp->name = strid;
 

	
 
					strid = GetGRFStringID(indsp->grf_prop.grffile->grfid, indsp->closure_text);
 
					if (strid != STR_UNDEFINED) indsp->closure_text = strid;
 

	
 
					strid = GetGRFStringID(indsp->grf_prop.grffile->grfid, indsp->production_up_text);
 
					if (strid != STR_UNDEFINED) indsp->production_up_text = strid;
 

	
 
					strid = GetGRFStringID(indsp->grf_prop.grffile->grfid, indsp->production_down_text);
 
					if (strid != STR_UNDEFINED) indsp->production_down_text = strid;
 

	
 
					strid = GetGRFStringID(indsp->grf_prop.grffile->grfid, indsp->new_industry_text);
 
					if (strid != STR_UNDEFINED) indsp->new_industry_text = strid;
 

	
 
					if (indsp->station_name != STR_NULL) {
 
						/* STR_NULL (0) can be set by grf.  It has a meaning regarding assignation of the
 
						 * station's name. Don't want to lose the value, therefore, do not process. */
 
						strid = GetGRFStringID(indsp->grf_prop.grffile->grfid, indsp->station_name);
 
						if (strid != STR_UNDEFINED) indsp->station_name = strid;
 
					}
 

	
 
					_industry_mngr.SetEntitySpec(indsp);
 
					_loaded_newgrf_features.has_newindustries = true;
 
				}
 
			}
 
		}
 

	
 
		if (indtspec != NULL) {
 
			for (int i = 0; i < NUM_INDUSTRYTILES; i++) {
 
				IndustryTileSpec *indtsp = indtspec[i];
 
				if (indtsp != NULL) {
 
					_industile_mngr.SetEntitySpec(indtsp);
 
				}
 
			}
 
		}
 
	}
 

	
 
	for (uint j = 0; j < NUM_INDUSTRYTYPES; j++) {
 
		IndustrySpec *indsp = &_industry_specs[j];
 
		if (indsp->enabled && indsp->grf_prop.grffile != NULL) {
 
			for (uint i = 0; i < 3; i++) {
 
				indsp->conflicting[i] = MapNewGRFIndustryType(indsp->conflicting[i], indsp->grf_prop.grffile->grfid);
 
			}
 
		}
 
	}
 
}
 

	
 
/**
 
 * Add all new airports to the airport array. Airport properties can be set at any
 
 * time in the GRF file, so we can only add a airport spec to the airport array
 
 * after the file has finished loading.
 
 */
 
static void FinaliseAirportsArray()
 
{
 
	const GRFFile * const *end = _grf_files.End();
 
	for (GRFFile **file = _grf_files.Begin(); file != end; file++) {
 
		AirportSpec **&airportspec = (*file)->airportspec;
 
		if (airportspec != NULL) {
 
			for (int i = 0; i < NUM_AIRPORTS; i++) {
 
				if (airportspec[i] != NULL && airportspec[i]->enabled) {
 
					_airport_mngr.SetEntitySpec(airportspec[i]);
 
				}
 
			}
 
		}
 

	
 
		AirportTileSpec **&airporttilespec = (*file)->airtspec;
 
		if (airporttilespec != NULL) {
 
			for (int i = 0; i < NUM_AIRPORTTILES; i++) {
 
			for (uint i = 0; i < NUM_AIRPORTTILES; i++) {
 
				if (airporttilespec[i] != NULL && airporttilespec[i]->enabled) {
 
					_airporttile_mngr.SetEntitySpec(airporttilespec[i]);
 
				}
 
			}
 
		}
 
	}
 
}
 

	
 
/* Here we perform initial decoding of some special sprites (as are they
 
 * described at http://www.ttdpatch.net/src/newgrf.txt, but this is only a very
 
 * partial implementation yet).
 
 * XXX: We consider GRF files trusted. It would be trivial to exploit OTTD by
 
 * a crafted invalid GRF file. We should tell that to the user somehow, or
 
 * better make this more robust in the future. */
 
static void DecodeSpecialSprite(byte *buf, uint num, GrfLoadingStage stage)
 
{
 
	/* XXX: There is a difference between staged loading in TTDPatch and
 
	 * here.  In TTDPatch, for some reason actions 1 and 2 are carried out
 
	 * during stage 1, whilst action 3 is carried out during stage 2 (to
 
	 * "resolve" cargo IDs... wtf). This is a little problem, because cargo
 
	 * IDs are valid only within a given set (action 1) block, and may be
 
	 * overwritten after action 3 associates them. But overwriting happens
 
	 * in an earlier stage than associating, so...  We just process actions
 
	 * 1 and 2 in stage 2 now, let's hope that won't get us into problems.
 
	 * --pasky
 
	 * We need a pre-stage to set up GOTO labels of Action 0x10 because the grf
 
	 * is not in memory and scanning the file every time would be too expensive.
 
	 * In other stages we skip action 0x10 since it's already dealt with. */
 
	static const SpecialSpriteHandler handlers[][GLS_END] = {
 
		/* 0x00 */ { NULL,     SafeChangeInfo, NULL,       NULL,           ReserveChangeInfo, FeatureChangeInfo, },
 
		/* 0x01 */ { SkipAct1, SkipAct1,  SkipAct1,        SkipAct1,       SkipAct1,          NewSpriteSet, },
 
		/* 0x02 */ { NULL,     NULL,      NULL,            NULL,           NULL,              NewSpriteGroup, },
 
		/* 0x03 */ { NULL,     GRFUnsafe, NULL,            NULL,           NULL,              FeatureMapSpriteGroup, },
 
		/* 0x04 */ { NULL,     NULL,      NULL,            NULL,           NULL,              FeatureNewName, },
 
		/* 0x05 */ { SkipAct5, SkipAct5,  SkipAct5,        SkipAct5,       SkipAct5,          GraphicsNew, },
 
		/* 0x06 */ { NULL,     NULL,      NULL,            CfgApply,       CfgApply,          CfgApply, },
 
		/* 0x07 */ { NULL,     NULL,      NULL,            NULL,           SkipIf,            SkipIf, },
 
		/* 0x08 */ { ScanInfo, NULL,      NULL,            GRFInfo,        GRFInfo,           GRFInfo, },
 
		/* 0x09 */ { NULL,     NULL,      NULL,            SkipIf,         SkipIf,            SkipIf, },
 
		/* 0x0A */ { SkipActA, SkipActA,  SkipActA,        SkipActA,       SkipActA,          SpriteReplace, },
 
		/* 0x0B */ { NULL,     NULL,      NULL,            GRFLoadError,   GRFLoadError,      GRFLoadError, },
 
		/* 0x0C */ { NULL,     NULL,      NULL,            GRFComment,     NULL,              GRFComment, },
 
		/* 0x0D */ { NULL,     SafeParamSet, NULL,         ParamSet,       ParamSet,          ParamSet, },
 
		/* 0x0E */ { NULL,     SafeGRFInhibit, NULL,       GRFInhibit,     GRFInhibit,        GRFInhibit, },
 
		/* 0x0F */ { NULL,     GRFUnsafe, NULL,            FeatureTownName, NULL,             NULL, },
 
		/* 0x10 */ { NULL,     NULL,      DefineGotoLabel, NULL,           NULL,              NULL, },
 
		/* 0x11 */ { SkipAct11,GRFUnsafe, SkipAct11,       SkipAct11,      SkipAct11,         GRFSound, },
 
		/* 0x12 */ { SkipAct12, SkipAct12, SkipAct12,      SkipAct12,      SkipAct12,         LoadFontGlyph, },
 
		/* 0x13 */ { NULL,     NULL,      NULL,            NULL,           NULL,              TranslateGRFStrings, },
 
	};
 

	
 
	GRFLocation location(_cur_grfconfig->ident.grfid, _nfo_line);
 

	
 
	GRFLineToSpriteOverride::iterator it = _grf_line_to_action6_sprite_override.find(location);
 
	if (it == _grf_line_to_action6_sprite_override.end()) {
 
		/* No preloaded sprite to work with; read the
 
		 * pseudo sprite content. */
 
		FioReadBlock(buf, num);
 
	} else {
 
		/* Use the preloaded sprite data. */
 
		buf = _grf_line_to_action6_sprite_override[location];
 
		grfmsg(7, "DecodeSpecialSprite: Using preloaded pseudo sprite data");
 

	
 
		/* Skip the real (original) content of this action. */
 
		FioSeekTo(num, SEEK_CUR);
 
	}
 

	
 
	ByteReader br(buf, buf + num);
 
	ByteReader *bufp = &br;
 

	
 
	try {
 
		byte action = bufp->ReadByte();
 

	
 
		if (action == 0xFF) {
 
			grfmsg(7, "DecodeSpecialSprite: Handling data block in stage %d", stage);
 
			GRFDataBlock(bufp);
 
		} else if (action == 0xFE) {
 
			grfmsg(7, "DecodeSpecialSprite: Handling import block in stage %d", stage);
 
			GRFImportBlock(bufp);
 
		} else if (action >= lengthof(handlers)) {
 
			grfmsg(7, "DecodeSpecialSprite: Skipping unknown action 0x%02X", action);
 
		} else if (handlers[action][stage] == NULL) {
 
			grfmsg(7, "DecodeSpecialSprite: Skipping action 0x%02X in stage %d", action, stage);
 
		} else {
 
			grfmsg(7, "DecodeSpecialSprite: Handling action 0x%02X in stage %d", action, stage);
 
			handlers[action][stage](bufp);
 
		}
 
	} catch (...) {
 
		grfmsg(1, "DecodeSpecialSprite: Tried to read past end of pseudo-sprite data");
 

	
 
		_skip_sprites = -1;
 
		_cur_grfconfig->status = GCS_DISABLED;
 
		delete _cur_grfconfig->error;
 
		_cur_grfconfig->error  = new GRFError(STR_NEWGRF_ERROR_MSG_FATAL, STR_NEWGRF_ERROR_READ_BOUNDS);
 
	}
 
}
 

	
 

	
 
void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage)
 
{
 
	const char *filename = config->filename;
 
	uint16 num;
 

	
 
	/* A .grf file is activated only if it was active when the game was
 
	 * started.  If a game is loaded, only its active .grfs will be
 
	 * reactivated, unless "loadallgraphics on" is used.  A .grf file is
 
	 * considered active if its action 8 has been processed, i.e. its
 
	 * action 8 hasn't been skipped using an action 7.
 
	 *
 
	 * During activation, only actions 0, 1, 2, 3, 4, 5, 7, 8, 9, 0A and 0B are
 
	 * carried out.  All others are ignored, because they only need to be
 
	 * processed once at initialization.  */
 
	if (stage != GLS_FILESCAN && stage != GLS_SAFETYSCAN && stage != GLS_LABELSCAN) {
 
		_cur_grffile = GetFileByFilename(filename);
 
		if (_cur_grffile == NULL) usererror("File '%s' lost in cache.\n", filename);
 
		if (stage == GLS_RESERVE && config->status != GCS_INITIALISED) return;
 
		if (stage == GLS_ACTIVATION && !HasBit(config->flags, GCF_RESERVED)) return;
 
		_cur_grffile->is_ottdfile = config->IsOpenTTDBaseGRF();
 
	}
 

	
 
	if (file_index > LAST_GRF_SLOT) {
 
		DEBUG(grf, 0, "'%s' is not loaded as the maximum number of GRFs has been reached", filename);
 
		config->status = GCS_DISABLED;
 
		config->error  = new GRFError(STR_NEWGRF_ERROR_MSG_FATAL, STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED);
 
		return;
 
	}
 

	
 
	FioOpenFile(file_index, filename);
 
	_file_index = file_index; // XXX
 
	_palette_remap_grf[_file_index] = (config->windows_paletted != (_use_palette == PAL_WINDOWS));
 

	
 
	_cur_grfconfig = config;
 

	
 
	DEBUG(grf, 2, "LoadNewGRFFile: Reading NewGRF-file '%s'", filename);
 

	
 
	/* Skip the first sprite; we don't care about how many sprites this
 
	 * does contain; newest TTDPatches and George's longvehicles don't
 
	 * neither, apparently. */
 
	if (FioReadWord() == 4 && FioReadByte() == 0xFF) {
 
		FioReadDword();
 
	} else {
 
		DEBUG(grf, 7, "LoadNewGRFFile: Custom .grf has invalid format");
 
		return;
 
	}
 

	
 
	_skip_sprites = 0; // XXX
 
	_nfo_line = 0;
 

	
 
	ReusableBuffer<byte> buf;
 

	
 
	while ((num = FioReadWord()) != 0) {
 
		byte type = FioReadByte();
 
		_nfo_line++;
 

	
 
		if (type == 0xFF) {
 
			if (_skip_sprites == 0) {
 
				DecodeSpecialSprite(buf.Allocate(num), num, stage);
 

	
 
				/* Stop all processing if we are to skip the remaining sprites */
 
				if (_skip_sprites == -1) break;
 

	
 
				continue;
 
			} else {
 
				FioSkipBytes(num);
 
			}
 
		} else {
 
			if (_skip_sprites == 0) {
 
				grfmsg(0, "LoadNewGRFFile: Unexpected sprite, disabling");
 
				config->status = GCS_DISABLED;
 
				delete config->error;
 
				config->error  = new GRFError(STR_NEWGRF_ERROR_MSG_FATAL, STR_NEWGRF_ERROR_UNEXPECTED_SPRITE);
 
				break;
 
			}
 

	
 
			FioSkipBytes(7);
 
			SkipSpriteData(type, num - 8);
 
		}
 

	
 
		if (_skip_sprites > 0) _skip_sprites--;
 
	}
 
}
 

	
 
/**
 
 * Relocates the old shore sprites at new positions.
 
 *
 
 * 1. If shore sprites are neither loaded by Action5 nor ActionA, the extra sprites from openttd(w/d).grf are used. (SHORE_REPLACE_ONLY_NEW)
 
 * 2. If a newgrf replaces some shore sprites by ActionA. The (maybe also replaced) grass tiles are used for corner shores. (SHORE_REPLACE_ACTION_A)
 
 * 3. If a newgrf replaces shore sprites by Action5 any shore replacement by ActionA has no effect. (SHORE_REPLACE_ACTION_5)
 
 */
 
static void ActivateOldShore()
 
{
 
	/* Use default graphics, if no shore sprites were loaded.
src/newgrf_engine.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file newgrf_engine.cpp NewGRF handling of engines. */
 

	
 
#include "stdafx.h"
 
#include "debug.h"
 
#include "train.h"
 
#include "roadveh.h"
 
#include "company_func.h"
 
#include "newgrf.h"
 
#include "newgrf_cargo.h"
 
#include "newgrf_engine.h"
 
#include "newgrf_spritegroup.h"
 
#include "date_func.h"
 
#include "vehicle_func.h"
 
#include "core/random_func.hpp"
 
#include "aircraft.h"
 
#include "core/smallmap_type.hpp"
 
#include "station_base.h"
 
#include "engine_base.h"
 
#include "company_base.h"
 

	
 
struct WagonOverride {
 
	EngineID *train_id;
 
	uint trains;
 
	CargoID cargo;
 
	const SpriteGroup *group;
 
};
 

	
 
void SetWagonOverrideSprites(EngineID engine, CargoID cargo, const SpriteGroup *group, EngineID *train_id, uint trains)
 
{
 
	Engine *e = Engine::Get(engine);
 
	WagonOverride *wo;
 

	
 
	assert(cargo < NUM_CARGO + 2); // Include CT_DEFAULT and CT_PURCHASE pseudo cargos.
 

	
 
	e->overrides_count++;
 
	e->overrides = ReallocT(e->overrides, e->overrides_count);
 

	
 
	wo = &e->overrides[e->overrides_count - 1];
 
	wo->group = group;
 
	wo->cargo = cargo;
 
	wo->trains = trains;
 
	wo->train_id = MallocT<EngineID>(trains);
 
	memcpy(wo->train_id, train_id, trains * sizeof *train_id);
 
}
 

	
 
const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, CargoID cargo, EngineID overriding_engine)
 
{
 
	const Engine *e = Engine::Get(engine);
 

	
 
	/* XXX: This could turn out to be a timesink on profiles. We could
 
	 * always just dedicate 65535 bytes for an [engine][train] trampoline
 
	 * for O(1). Or O(logMlogN) and searching binary tree or smt. like
 
	 * that. --pasky */
 

	
 
	for (uint i = 0; i < e->overrides_count; i++) {
 
		const WagonOverride *wo = &e->overrides[i];
 

	
 
		if (wo->cargo != cargo && wo->cargo != CT_DEFAULT) continue;
 

	
 
		for (uint j = 0; j < wo->trains; j++) {
 
			if (wo->train_id[j] == overriding_engine) return wo->group;
 
		}
 
	}
 
	return NULL;
 
}
 

	
 
/**
 
 * Unload all wagon override sprite groups.
 
 */
 
void UnloadWagonOverrides(Engine *e)
 
{
 
	for (uint i = 0; i < e->overrides_count; i++) {
 
		WagonOverride *wo = &e->overrides[i];
 
		free(wo->train_id);
 
	}
 
	free(e->overrides);
 
	e->overrides_count = 0;
 
	e->overrides = NULL;
 
}
 

	
 

	
 
void SetCustomEngineSprites(EngineID engine, byte cargo, const SpriteGroup *group)
 
{
 
	Engine *e = Engine::Get(engine);
 
	assert(cargo < lengthof(e->group));
 

	
 
	if (e->group[cargo] != NULL) {
 
		grfmsg(6, "SetCustomEngineSprites: engine %d cargo %d already has group -- replacing", engine, cargo);
 
	}
 
	e->group[cargo] = group;
 
}
 

	
 

	
 
/**
 
 * Tie a GRFFile entry to an engine, to allow us to retrieve GRF parameters
 
 * etc during a game.
 
 * @param engine Engine ID to tie the GRFFile to.
 
 * @param file   Pointer of GRFFile to tie.
 
 */
 
void SetEngineGRF(EngineID engine, const GRFFile *file)
 
{
 
	Engine *e = Engine::Get(engine);
 
	e->grffile = file;
 
}
 

	
 

	
 
/**
 
 * Retrieve the GRFFile tied to an engine
 
 * @param engine Engine ID to retrieve.
 
 * @return Pointer to GRFFile.
 
 */
 
const GRFFile *GetEngineGRF(EngineID engine)
 
{
 
	return Engine::Get(engine)->grffile;
 
}
 

	
 

	
 
/**
 
 * Retrieve the GRF ID of the GRFFile tied to an engine
 
 * @param engine Engine ID to retrieve.
 
 * @return 32 bit GRFID value.
 
 */
 
uint32 GetEngineGRFID(EngineID engine)
 
{
 
	const GRFFile *file = GetEngineGRF(engine);
 
	return file == NULL ? 0 : file->grfid;
 
}
 

	
 

	
 
static int MapOldSubType(const Vehicle *v)
 
{
 
	switch (v->type) {
 
		case VEH_TRAIN:
 
			if (Train::From(v)->IsEngine()) return 0;
 
			if (Train::From(v)->IsFreeWagon()) return 4;
 
			return 2;
 
		case VEH_ROAD:
 
		case VEH_SHIP:     return 0;
 
		case VEH_AIRCRAFT:
 
		case VEH_DISASTER: return v->subtype;
 
		case VEH_EFFECT:   return v->subtype << 1;
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 

	
 
/* TTDP style aircraft movement states for GRF Action 2 Var 0xE2 */
 
enum {
 
enum TTDPAircraftMovementStates {
 
	AMS_TTDP_HANGAR,
 
	AMS_TTDP_TO_HANGAR,
 
	AMS_TTDP_TO_PAD1,
 
	AMS_TTDP_TO_PAD2,
 
	AMS_TTDP_TO_PAD3,
 
	AMS_TTDP_TO_ENTRY_2_AND_3,
 
	AMS_TTDP_TO_ENTRY_2_AND_3_AND_H,
 
	AMS_TTDP_TO_JUNCTION,
 
	AMS_TTDP_LEAVE_RUNWAY,
 
	AMS_TTDP_TO_INWAY,
 
	AMS_TTDP_TO_RUNWAY,
 
	AMS_TTDP_TO_OUTWAY,
 
	AMS_TTDP_WAITING,
 
	AMS_TTDP_TAKEOFF,
 
	AMS_TTDP_TO_TAKEOFF,
 
	AMS_TTDP_CLIMBING,
 
	AMS_TTDP_FLIGHT_APPROACH,
 
	AMS_TTDP_UNUSED_0x11,
 
	AMS_TTDP_FLIGHT_TO_TOWER,
 
	AMS_TTDP_UNUSED_0x13,
 
	AMS_TTDP_FLIGHT_FINAL,
 
	AMS_TTDP_FLIGHT_DESCENT,
 
	AMS_TTDP_BRAKING,
 
	AMS_TTDP_HELI_TAKEOFF_AIRPORT,
 
	AMS_TTDP_HELI_TO_TAKEOFF_AIRPORT,
 
	AMS_TTDP_HELI_LAND_AIRPORT,
 
	AMS_TTDP_HELI_TAKEOFF_HELIPORT,
 
	AMS_TTDP_HELI_TO_TAKEOFF_HELIPORT,
 
	AMS_TTDP_HELI_LAND_HELIPORT,
 
};
 

	
 

	
 
/**
 
 * Map OTTD aircraft movement states to TTDPatch style movement states
 
 * (VarAction 2 Variable 0xE2)
 
 */
 
static byte MapAircraftMovementState(const Aircraft *v)
 
{
 
	const Station *st = GetTargetAirportIfValid(v);
 
	if (st == NULL) return AMS_TTDP_FLIGHT_TO_TOWER;
 

	
 
	const AirportFTAClass *afc = st->airport.GetFTA();
 
	uint16 amdflag = afc->MovingData(v->pos)->flag;
 

	
 
	switch (v->state) {
 
		case HANGAR:
 
			/* The international airport is a special case as helicopters can land in
 
			 * front of the hanger. Helicopters also change their air.state to
 
			 * AMED_HELI_LOWER some time before actually descending. */
 

	
 
			/* This condition only occurs for helicopters, during descent,
 
			 * to a landing by the hanger of an international airport. */
 
			if (amdflag & AMED_HELI_LOWER) return AMS_TTDP_HELI_LAND_AIRPORT;
 

	
 
			/* This condition only occurs for helicopters, before starting descent,
 
			 * to a landing by the hanger of an international airport. */
 
			if (amdflag & AMED_SLOWTURN) return AMS_TTDP_FLIGHT_TO_TOWER;
 

	
 
			/* The final two conditions apply to helicopters or aircraft.
 
			 * Has reached hanger? */
 
			if (amdflag & AMED_EXACTPOS) return AMS_TTDP_HANGAR;
 

	
 
			/* Still moving towards hanger. */
 
			return AMS_TTDP_TO_HANGAR;
 

	
 
		case TERM1:
 
			if (amdflag & AMED_EXACTPOS) return AMS_TTDP_TO_PAD1;
 
			return AMS_TTDP_TO_JUNCTION;
 

	
 
		case TERM2:
 
			if (amdflag & AMED_EXACTPOS) return AMS_TTDP_TO_PAD2;
 
			return AMS_TTDP_TO_ENTRY_2_AND_3_AND_H;
 

	
 
		case TERM3:
 
		case TERM4:
 
		case TERM5:
 
		case TERM6:
 
		case TERM7:
 
		case TERM8:
 
			/* TTDPatch only has 3 terminals, so treat these states the same */
 
			if (amdflag & AMED_EXACTPOS) return AMS_TTDP_TO_PAD3;
 
			return AMS_TTDP_TO_ENTRY_2_AND_3_AND_H;
 

	
 
		case HELIPAD1:
 
		case HELIPAD2:
 
		case HELIPAD3:
 
		case HELIPAD4: // Will only occur for helicopters.
 
			if (amdflag & AMED_HELI_LOWER) return AMS_TTDP_HELI_LAND_AIRPORT; // Descending.
 
			if (amdflag & AMED_SLOWTURN)   return AMS_TTDP_FLIGHT_TO_TOWER;   // Still hasn't started descent.
 
			return AMS_TTDP_TO_JUNCTION; // On the ground.
 

	
 
		case TAKEOFF: // Moving to takeoff position.
 
			return AMS_TTDP_TO_OUTWAY;
 

	
 
		case STARTTAKEOFF: // Accelerating down runway.
 
			return AMS_TTDP_TAKEOFF;
 

	
 
		case ENDTAKEOFF: // Ascent
 
			return AMS_TTDP_CLIMBING;
 

	
 
		case HELITAKEOFF: // Helicopter is moving to take off position.
 
			if (afc->delta_z == 0) {
 
				return amdflag & AMED_HELI_RAISE ?
 
					AMS_TTDP_HELI_TAKEOFF_AIRPORT : AMS_TTDP_TO_JUNCTION;
 
			} else {
 
				return AMS_TTDP_HELI_TAKEOFF_HELIPORT;
 
			}
 

	
 
		case FLYING:
 
			return amdflag & AMED_HOLD ? AMS_TTDP_FLIGHT_APPROACH : AMS_TTDP_FLIGHT_TO_TOWER;
 

	
 
		case LANDING: // Descent
 
			return AMS_TTDP_FLIGHT_DESCENT;
 

	
 
		case ENDLANDING: // On the runway braking
 
			if (amdflag & AMED_BRAKE) return AMS_TTDP_BRAKING;
 
			/* Landed - moving off runway */
 
			return AMS_TTDP_TO_INWAY;
 

	
 
		case HELILANDING:
 
		case HELIENDLANDING: // Helicoptor is decending.
 
			if (amdflag & AMED_HELI_LOWER) {
 
				return afc->delta_z == 0 ?
 
					AMS_TTDP_HELI_LAND_AIRPORT : AMS_TTDP_HELI_LAND_HELIPORT;
 
			} else {
 
				return AMS_TTDP_FLIGHT_TO_TOWER;
 
			}
 

	
 
		default:
 
			return AMS_TTDP_HANGAR;
 
	}
 
}
 

	
 

	
 
/* TTDP style aircraft movement action for GRF Action 2 Var 0xE6 */
 
enum {
 
enum TTDPAircraftMovementActions {
 
	AMA_TTDP_IN_HANGAR,
 
	AMA_TTDP_ON_PAD1,
 
	AMA_TTDP_ON_PAD2,
 
	AMA_TTDP_ON_PAD3,
 
	AMA_TTDP_HANGAR_TO_PAD1,
 
	AMA_TTDP_HANGAR_TO_PAD2,
 
	AMA_TTDP_HANGAR_TO_PAD3,
 
	AMA_TTDP_LANDING_TO_PAD1,
 
	AMA_TTDP_LANDING_TO_PAD2,
 
	AMA_TTDP_LANDING_TO_PAD3,
 
	AMA_TTDP_PAD1_TO_HANGAR,
 
	AMA_TTDP_PAD2_TO_HANGAR,
 
	AMA_TTDP_PAD3_TO_HANGAR,
 
	AMA_TTDP_PAD1_TO_TAKEOFF,
 
	AMA_TTDP_PAD2_TO_TAKEOFF,
 
	AMA_TTDP_PAD3_TO_TAKEOFF,
 
	AMA_TTDP_HANGAR_TO_TAKOFF,
 
	AMA_TTDP_LANDING_TO_HANGAR,
 
	AMA_TTDP_IN_FLIGHT,
 
};
 

	
 

	
 
/**
 
 * Map OTTD aircraft movement states to TTDPatch style movement actions
 
 * (VarAction 2 Variable 0xE6)
 
 * This is not fully supported yet but it's enough for Planeset.
 
 */
 
static byte MapAircraftMovementAction(const Aircraft *v)
 
{
 
	switch (v->state) {
 
		case HANGAR:
 
			return (v->cur_speed > 0) ? AMA_TTDP_LANDING_TO_HANGAR : AMA_TTDP_IN_HANGAR;
 

	
 
		case TERM1:
 
		case HELIPAD1:
 
			return (v->current_order.IsType(OT_LOADING)) ? AMA_TTDP_ON_PAD1 : AMA_TTDP_LANDING_TO_PAD1;
 

	
 
		case TERM2:
 
		case HELIPAD2:
 
			return (v->current_order.IsType(OT_LOADING)) ? AMA_TTDP_ON_PAD2 : AMA_TTDP_LANDING_TO_PAD2;
 

	
 
		case TERM3:
 
		case TERM4:
 
		case TERM5:
 
		case TERM6:
 
		case TERM7:
 
		case TERM8:
 
		case HELIPAD3:
 
		case HELIPAD4:
 
			return (v->current_order.IsType(OT_LOADING)) ? AMA_TTDP_ON_PAD3 : AMA_TTDP_LANDING_TO_PAD3;
 

	
 
		case TAKEOFF:      // Moving to takeoff position
 
		case STARTTAKEOFF: // Accelerating down runway
 
		case ENDTAKEOFF:   // Ascent
 
		case HELITAKEOFF:
 
			/* @todo Need to find which terminal (or hanger) we've come from. How? */
 
			return AMA_TTDP_PAD1_TO_TAKEOFF;
 

	
 
		case FLYING:
 
			return AMA_TTDP_IN_FLIGHT;
 

	
 
		case LANDING:    // Descent
 
		case ENDLANDING: // On the runway braking
 
		case HELILANDING:
 
		case HELIENDLANDING:
 
			/* @todo Need to check terminal we're landing to. Is it known yet? */
 
			return (v->current_order.IsType(OT_GOTO_DEPOT)) ?
 
				AMA_TTDP_LANDING_TO_HANGAR : AMA_TTDP_LANDING_TO_PAD1;
 

	
 
		default:
 
			return AMA_TTDP_IN_HANGAR;
 
	}
 
}
 

	
 

	
 
/* Vehicle Resolver Functions */
 
static inline const Vehicle *GRV(const ResolverObject *object)
 
{
 
	switch (object->scope) {
 
		default: NOT_REACHED();
 
		case VSG_SCOPE_SELF: return object->u.vehicle.self;
 
		case VSG_SCOPE_PARENT: return object->u.vehicle.parent;
 
		case VSG_SCOPE_RELATIVE: {
 
			if (object->u.vehicle.self == NULL) return NULL;
 
			const Vehicle *v = NULL;
 
			switch (GB(object->count, 6, 2)) {
 
				default: NOT_REACHED();
 
				case 0x00: // count back (away from the engine), starting at this vehicle
 
				case 0x01: // count forward (toward the engine), starting at this vehicle
 
					v = object->u.vehicle.self;
 
					break;
 
				case 0x02: // count back, starting at the engine
 
					v = object->u.vehicle.parent;
 
					break;
 
				case 0x03: { // count back, starting at the first vehicle in this chain of vehicles with the same ID, as for vehicle variable 41
 
					const Vehicle *self = object->u.vehicle.self;
 
					for (const Vehicle *u = self->First(); u != self; u = u->Next()) {
 
						if (u->engine_type != self->engine_type) {
 
							v = NULL;
 
						} else {
 
							if (v == NULL) v = u;
 
						}
 
					}
 
					if (v == NULL) v = self;
 
				} break;
 
			}
 
			uint32 count = GB(object->count, 0, 4);
 
			if (count == 0) count = GetRegister(0x100);
 
			while (v != NULL && count-- != 0) v = (GB(object->count, 6, 2) == 0x01) ? v->Previous() : v->Next();
 
			return v;
 
		}
 
	}
 
}
 

	
 

	
 
static uint32 VehicleGetRandomBits(const ResolverObject *object)
 
{
 
	return GRV(object) == NULL ? 0 : GRV(object)->random_bits;
 
}
 

	
 

	
 
static uint32 VehicleGetTriggers(const ResolverObject *object)
 
{
 
	return GRV(object) == NULL ? 0 : GRV(object)->waiting_triggers;
 
}
 

	
 

	
 
static void VehicleSetTriggers(const ResolverObject *object, int triggers)
 
{
 
	/* Evil cast to get around const-ness. This used to be achieved by an
 
	 * innocent looking function pointer cast... Currently I cannot see a
 
	 * way of avoiding this without removing consts deep within gui code.
 
	 */
 
	Vehicle *v = const_cast<Vehicle *>(GRV(object));
 

	
 
	/* This function must only be called when processing triggers -- any
 
	 * other time is an error. */
 
	assert(object->trigger != 0);
 

	
 
	if (v != NULL) v->waiting_triggers = triggers;
 
}
 

	
 

	
 
static uint8 LiveryHelper(EngineID engine, const Vehicle *v)
 
{
 
	const Livery *l;
 

	
 
	if (v == NULL) {
 
		if (!Company::IsValidID(_current_company)) return 0;
 
		l = GetEngineLivery(engine, _current_company, INVALID_ENGINE, NULL);
 
	} else if (v->type == VEH_TRAIN) {
 
		l = GetEngineLivery(v->engine_type, v->owner, Train::From(v)->tcache.first_engine, v);
 
	} else if (v->type == VEH_ROAD) {
 
		l = GetEngineLivery(v->engine_type, v->owner, RoadVehicle::From(v)->rcache.first_engine, v);
 
	} else {
 
		l = GetEngineLivery(v->engine_type, v->owner, INVALID_ENGINE, v);
 
	}
 

	
 
	return l->colour1 + l->colour2 * 16;
 
}
 

	
 
/**
 
 * Helper to get the position of a vehicle within a chain of vehicles.
 
 * @param v the vehicle to get the position of.
 
 * @param consecutive whether to look at the whole chain or the vehicles
 
 *                    with the same 'engine type'.
 
 * @return the position in the chain from front and tail and chain length.
 
 */
 
static uint32 PositionHelper(const Vehicle *v, bool consecutive)
 
{
 
	const Vehicle *u;
 
	byte chain_before = 0;
 
	byte chain_after  = 0;
 

	
 
	for (u = v->First(); u != v; u = u->Next()) {
 
		chain_before++;
 
		if (consecutive && u->engine_type != v->engine_type) chain_before = 0;
 
	}
 

	
 
	while (u->Next() != NULL && (!consecutive || u->Next()->engine_type == v->engine_type)) {
 
		chain_after++;
 
		u = u->Next();
 
	}
 

	
 
	return chain_before | chain_after << 8 | (chain_before + chain_after + consecutive) << 16;
 
}
 

	
 
static uint32 VehicleGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available)
 
{
 
	Vehicle *v = const_cast<Vehicle*>(GRV(object));
 

	
 
	if (v == NULL) {
src/newgrf_gui.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file newgrf_gui.cpp GUI to change NewGRF settings. */
 

	
 
#include "stdafx.h"
 
#include "gui.h"
 
#include "newgrf.h"
 
#include "strings_func.h"
 
#include "window_func.h"
 
#include "gamelog.h"
 
#include "settings_func.h"
 
#include "widgets/dropdown_type.h"
 
#include "network/network.h"
 
#include "network/network_content.h"
 
#include "sortlist_type.h"
 
#include "querystring_gui.h"
 
#include "core/geometry_func.hpp"
 

	
 
#include "table/strings.h"
 
#include "table/sprites.h"
 

	
 
/**
 
 * Show the first NewGRF error we can find.
 
 */
 
void ShowNewGRFError()
 
{
 
	for (const GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
 
		/* We only want to show fatal errors */
 
		if (c->error == NULL || c->error->severity != STR_NEWGRF_ERROR_MSG_FATAL) continue;
 

	
 
		SetDParam   (0, c->error->custom_message == NULL ? c->error->message : STR_JUST_RAW_STRING);
 
		SetDParamStr(1, c->error->custom_message);
 
		SetDParam   (2, STR_JUST_RAW_STRING);
 
		SetDParamStr(3, c->filename);
 
		SetDParam   (4, STR_JUST_RAW_STRING);
 
		SetDParamStr(5, c->error->data);
 
		for (uint i = 0; i < c->error->num_params; i++) {
 
			SetDParam(6 + i, c->error->param_value[i]);
 
		}
 
		ShowErrorMessage(STR_NEWGRF_ERROR_FATAL_POPUP, INVALID_STRING_ID, WL_CRITICAL);
 
		break;
 
	}
 
}
 

	
 
static void ShowNewGRFInfo(const GRFConfig *c, uint x, uint y, uint right, uint bottom, bool show_params)
 
{
 
	char buff[256];
 

	
 
	if (c->error != NULL) {
 
		char message[512];
 
		SetDParamStr(0, c->error->custom_message); // is skipped by built-in messages
 
		SetDParam   (1, STR_JUST_RAW_STRING);
 
		SetDParamStr(2, c->filename);
 
		SetDParam   (3, STR_JUST_RAW_STRING);
 
		SetDParamStr(4, c->error->data);
 
		for (uint i = 0; i < c->error->num_params; i++) {
 
			SetDParam(5 + i, c->error->param_value[i]);
 
		}
 
		GetString(message, c->error->custom_message == NULL ? c->error->message : STR_JUST_RAW_STRING, lastof(message));
 

	
 
		SetDParamStr(0, message);
 
		y = DrawStringMultiLine(x, right, y, bottom, c->error->severity);
 
	}
 

	
 
	/* Draw filename or not if it is not known (GRF sent over internet) */
 
	if (c->filename != NULL) {
 
		SetDParamStr(0, c->filename);
 
		y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_FILENAME);
 
	}
 

	
 
	/* Prepare and draw GRF ID */
 
	snprintf(buff, lengthof(buff), "%08X", BSWAP32(c->ident.grfid));
 
	SetDParamStr(0, buff);
 
	y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_GRF_ID);
 

	
 
	/* Prepare and draw MD5 sum */
 
	md5sumToString(buff, lastof(buff), c->ident.md5sum);
 
	SetDParamStr(0, buff);
 
	y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_MD5SUM);
 

	
 
	/* Show GRF parameter list */
 
	if (show_params) {
 
		if (c->num_params > 0) {
 
			GRFBuildParamList(buff, c, lastof(buff));
 
			SetDParam(0, STR_JUST_RAW_STRING);
 
			SetDParamStr(1, buff);
 
		} else {
 
			SetDParam(0, STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE);
 
		}
 
		y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_PARAMETER);
 

	
 
		/* Draw the palette of the NewGRF */
 
		SetDParamStr(0, c->windows_paletted ? "Windows" : "DOS");
 
		y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_PALETTE);
 
	}
 

	
 
	/* Show flags */
 
	if (c->status == GCS_NOT_FOUND)       y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_NOT_FOUND);
 
	if (c->status == GCS_DISABLED)        y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_DISABLED);
 
	if (HasBit(c->flags, GCF_COMPATIBLE)) y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_COMPATIBLE_LOADED);
 

	
 
	/* Draw GRF info if it exists */
 
	if (!StrEmpty(c->GetDescription())) {
 
		SetDParam(0, STR_JUST_RAW_STRING);
 
		SetDParamStr(1, c->GetDescription());
 
		y = DrawStringMultiLine(x, right, y, bottom, STR_BLACK_STRING);
 
	} else {
 
		y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_NO_INFO);
 
	}
 
}
 

	
 

	
 
/** Names of the add a newgrf window widgets. */
 
enum AddNewGRFWindowWidgets {
 
	ANGRFW_FILTER,
 
	ANGRFW_GRF_LIST,
 
	ANGRFW_SCROLLBAR,
 
	ANGRFW_GRF_INFO,
 
	ANGRFW_ADD,
 
	ANGRFW_RESCAN,
 
};
 

	
 
/**
 
 * Window for adding NewGRF files
 
 */
 
struct NewGRFAddWindow : public QueryStringBaseWindow {
 
private:
 
	typedef GUIList<const GRFConfig *> GUIGRFConfigList;
 

	
 
	GRFConfig **list;
 

	
 
	/** Runtime saved values */
 
	static Listing last_sorting;
 
	static Filtering last_filtering;
 

	
 
	static GUIGRFConfigList::SortFunction * const sorter_funcs[];
 
	static GUIGRFConfigList::FilterFunction * const filter_funcs[];
 
	GUIGRFConfigList grfs;
 

	
 
	const GRFConfig *sel;
 
	int sel_pos;
 

	
 
	enum {
 
		EDITBOX_MAX_SIZE = 50,
 
		EDITBOX_MAX_LENGTH = 300,
 
	};
 
	static const uint EDITBOX_MAX_SIZE   =  50;
 
	static const uint EDITBOX_MAX_LENGTH = 300;
 

	
 
	/**
 
	 * (Re)build the grf as its amount has changed because
 
	 * an item has been added or deleted for example
 
	 */
 
	void BuildGrfList()
 
	{
 
		if (!this->grfs.NeedRebuild()) return;
 

	
 
		/* Create temporary array of grfs to use for listing */
 
		this->grfs.Clear();
 

	
 
		for (const GRFConfig *c = _all_grfs; c != NULL; c = c->next) {
 
			*this->grfs.Append() = c;
 
		}
 

	
 
		this->FilterGrfList();
 
		this->grfs.Compact();
 
		this->grfs.RebuildDone();
 
		this->SortGrfList();
 

	
 
		this->vscroll.SetCount(this->grfs.Length()); // Update the scrollbar
 
		this->ScrollToSelected();
 
	}
 

	
 
	/** Sort grfs by name. */
 
	static int CDECL NameSorter(const GRFConfig * const *a, const GRFConfig * const *b)
 
	{
 
		return strcasecmp((*a)->GetName(), (*b)->GetName());
 
	}
 

	
 
	/** Sort the grf list */
 
	void SortGrfList()
 
	{
 
		if (!this->grfs.Sort()) return;
 
		this->UpdateListPosition();
 
	}
 

	
 
	/** Update selection position. */
 
	void UpdateListPosition()
 
	{
 
		/* update list position */
 
		if (this->sel != NULL) {
 
			this->sel_pos = this->grfs.FindIndex(this->sel);
 
			if (this->sel_pos < 0) {
 
				this->sel = NULL;
 
			}
 
		}
 
	}
 

	
 
	/** Filter grfs by tags/name */
 
	static bool CDECL TagNameFilter(const GRFConfig * const *a, const char *filter_string)
 
	{
 
		if (strcasestr((*a)->GetName(), filter_string) != NULL) return true;
 
		if ((*a)->filename != NULL && strcasestr((*a)->filename, filter_string) != NULL) return true;
 
		if ((*a)->GetDescription() != NULL && strcasestr((*a)->GetDescription(), filter_string) != NULL) return true;
 
		return false;
 
	}
 

	
 
	/** Filter the grf list */
 
	void FilterGrfList()
 
	{
 
		if (!this->grfs.Filter(this->edit_str_buf)) return;
 
		this->UpdateListPosition();
 
	}
 

	
 
	/** Make sure that the currently selected grf is within the visible part of the list */
 
	void ScrollToSelected()
 
	{
 
		if (this->sel_pos >= 0) {
 
			this->vscroll.ScrollTowards(this->sel_pos);
 
		}
 
	}
 

	
 
public:
 
	NewGRFAddWindow(const WindowDesc *desc, Window *parent, GRFConfig **list) : QueryStringBaseWindow(EDITBOX_MAX_SIZE)
 
	{
 
		this->parent = parent;
 
		this->InitNested(desc, 0);
 

	
 
		InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, EDITBOX_MAX_LENGTH);
 
		this->SetFocusedWidget(ANGRFW_FILTER);
 

	
 
		this->list = list;
 
		this->grfs.SetListing(this->last_sorting);
 
		this->grfs.SetFiltering(this->last_filtering);
 
		this->grfs.SetSortFuncs(this->sorter_funcs);
 
		this->grfs.SetFilterFuncs(this->filter_funcs);
 

	
 
		this->OnInvalidateData(0);
 
	}
 

	
 
	virtual void OnInvalidateData(int data)
 
	{
 
		/* data == 0: NewGRFS were rescanned. All pointers to GrfConfigs are invalid.
 
		 * data == 1: User interaction with this window: Filter or selection change. */
 
		if (data == 0) {
 
			this->sel = NULL;
 
			this->sel_pos = -1;
 
			this->grfs.ForceRebuild();
 
		}
 

	
 
		this->BuildGrfList();
 
		this->SetWidgetDisabledState(ANGRFW_ADD, this->sel == NULL || this->sel->IsOpenTTDBaseGRF());
 
	}
 

	
 
	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
 
	{
 
		switch (widget) {
 
			case ANGRFW_GRF_LIST:
 
				resize->height = FONT_HEIGHT_NORMAL;
 
				size->height = max(size->height, WD_FRAMERECT_TOP + 10 * resize->height + WD_FRAMERECT_BOTTOM + padding.height + 2);
 
				break;
 

	
 
			case ANGRFW_GRF_INFO:
 
				size->height = max(size->height, WD_FRAMERECT_TOP + 10 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM + padding.height + 2);
 
				break;
 
		}
 
	}
 

	
 
	virtual void OnResize()
 
	{
 
		this->vscroll.SetCapacityFromWidget(this, ANGRFW_GRF_LIST);
 
	}
 

	
 
	virtual void OnPaint()
 
	{
 
		this->DrawWidgets();
 
		this->DrawEditBox(ANGRFW_FILTER);
 
	}
 

	
 
	virtual void DrawWidget(const Rect &r, int widget) const
 
	{
 
		switch (widget) {
 
			case ANGRFW_GRF_LIST: {
 
				GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, 0xD7);
 

	
 
				uint y = r.top + WD_FRAMERECT_TOP;
 
				uint min_index = this->vscroll.GetPosition();
 
				uint max_index = min(min_index + this->vscroll.GetCapacity(), this->grfs.Length());
 

	
 
				for (uint i = min_index; i < max_index; i++)
 
				{
 
					const GRFConfig *c = this->grfs[i];
 
					bool h = c == this->sel;
 
					const char *text = c->GetName();
 

	
 
					/* Draw selection background */
 
					if (h) GfxFillRect(r.left + 1, y, r.right - 1, y + this->resize.step_height - 1, 156);
 
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, text, h ? TC_WHITE : TC_ORANGE);
 
					y += this->resize.step_height;
 
				}
 
				break;
 
			}
 

	
 
			case ANGRFW_GRF_INFO:
 
				if (this->sel != NULL) {
 
					ShowNewGRFInfo(this->sel, r.left + WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP, r.right - WD_FRAMERECT_RIGHT, r.bottom - WD_FRAMERECT_BOTTOM, false);
 
				}
 
				break;
 
		}
 
	}
 

	
 
	virtual void OnClick(Point pt, int widget, int click_count)
 
	{
 
		switch (widget) {
 
			case ANGRFW_GRF_LIST: {
 
				/* Get row... */
 
				uint i = (pt.y - this->GetWidget<NWidgetBase>(ANGRFW_GRF_LIST)->pos_y - WD_FRAMERECT_TOP) / this->resize.step_height + this->vscroll.GetPosition();
 

	
 
				if (i < this->grfs.Length()) {
 
					this->sel = this->grfs[i];
 
					this->sel_pos = i;
 
				} else {
 
					this->sel = NULL;
 
					this->sel_pos = -1;
 
				}
 
				this->InvalidateData(1);
 
				if (click_count == 1) break;
 
			}
 
			/* FALL THROUGH */
 
			case ANGRFW_ADD: // Add selection to list
 
				if (this->sel != NULL) {
 
					const GRFConfig *src = this->sel;
 
					GRFConfig **list;
 

	
 
					/* Find last entry in the list, checking for duplicate grfid on the way */
 
					for (list = this->list; *list != NULL; list = &(*list)->next) {
 
						if ((*list)->ident.grfid == src->ident.grfid) {
 
							ShowErrorMessage(STR_NEWGRF_DUPLICATE_GRFID, INVALID_STRING_ID, WL_INFO);
 
							return;
 
						}
src/newgrf_station.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file newgrf_station.cpp Functions for dealing with station classes and custom stations. */
 

	
 
#include "stdafx.h"
 
#include "variables.h"
 
#include "debug.h"
 
#include "station_base.h"
 
#include "waypoint_base.h"
 
#include "roadstop_base.h"
 
#include "newgrf_cargo.h"
 
#include "newgrf_commons.h"
 
#include "newgrf_station.h"
 
#include "newgrf_spritegroup.h"
 
#include "newgrf_sound.h"
 
#include "newgrf_railtype.h"
 
#include "town.h"
 
#include "newgrf_town.h"
 
#include "date_func.h"
 
#include "company_func.h"
 
#include "animated_tile_func.h"
 
#include "functions.h"
 
#include "tunnelbridge_map.h"
 
#include "newgrf.h"
 
#include "core/random_func.hpp"
 

	
 
#include "table/strings.h"
 

	
 
static StationClass _station_classes[STAT_CLASS_MAX];
 

	
 
enum {
 
	MAX_SPECLIST = 255,
 
};
 
static const uint MAX_SPECLIST = 255;
 

	
 
enum TriggerArea {
 
	TA_TILE,
 
	TA_PLATFORM,
 
	TA_WHOLE,
 
};
 

	
 
struct ETileArea : TileArea {
 
	ETileArea(const BaseStation *st, TileIndex tile, TriggerArea ta)
 
	{
 
		switch (ta) {
 
			default: NOT_REACHED();
 

	
 
			case TA_TILE:
 
				this->tile = tile;
 
				this->w    = 1;
 
				this->h    = 1;
 
				break;
 

	
 
			case TA_PLATFORM: {
 
				TileIndex start, end;
 
				Axis axis = GetRailStationAxis(tile);
 
				TileIndexDiff delta = TileOffsByDiagDir(AxisToDiagDir(axis));
 

	
 
				for (end = tile; IsRailStationTile(end + delta) && IsCompatibleTrainStationTile(tile, end + delta); end += delta) { /* Nothing */ }
 
				for (start = tile; IsRailStationTile(start - delta) && IsCompatibleTrainStationTile(tile, start - delta); start -= delta) { /* Nothing */ }
 

	
 
				this->tile = start;
 
				this->w = TileX(end) - TileX(start) + 1;
 
				this->h = TileY(end) - TileY(start) + 1;
 
				break;
 
			}
 

	
 
			case TA_WHOLE:
 
				st->GetTileArea(this, Station::IsExpected(st) ? STATION_RAIL : STATION_WAYPOINT);
 
				break;
 
		}
 
	}
 
};
 

	
 

	
 
/**
 
 * Reset station classes to their default state.
 
 * This includes initialising the Default and Waypoint classes with an empty
 
 * entry, for standard stations and waypoints.
 
 */
 
void ResetStationClasses()
 
{
 
	for (StationClassID i = STAT_CLASS_BEGIN; i < STAT_CLASS_MAX; i++) {
 
		_station_classes[i].id = 0;
 
		_station_classes[i].name = STR_EMPTY;
 
		_station_classes[i].stations = 0;
 

	
 
		free(_station_classes[i].spec);
 
		_station_classes[i].spec = NULL;
 
	}
 

	
 
	/* Set up initial data */
 
	_station_classes[0].id = 'DFLT';
 
	_station_classes[0].name = STR_STATION_CLASS_DFLT;
 
	_station_classes[0].stations = 1;
 
	_station_classes[0].spec = MallocT<StationSpec*>(1);
 
	_station_classes[0].spec[0] = NULL;
 

	
 
	_station_classes[1].id = 'WAYP';
 
	_station_classes[1].name = STR_STATION_CLASS_WAYP;
 
	_station_classes[1].stations = 1;
 
	_station_classes[1].spec = MallocT<StationSpec*>(1);
 
	_station_classes[1].spec[0] = NULL;
 
}
 

	
 
/**
 
 * Allocate a station class for the given class id.
 
 * @param cls A 32 bit value identifying the class.
 
 * @return Index into _station_classes of allocated class.
 
 */
 
StationClassID AllocateStationClass(uint32 cls)
 
{
 
	for (StationClassID i = STAT_CLASS_BEGIN; i < STAT_CLASS_MAX; i++) {
 
		if (_station_classes[i].id == cls) {
 
			/* ClassID is already allocated, so reuse it. */
 
			return i;
 
		} else if (_station_classes[i].id == 0) {
 
			/* This class is empty, so allocate it to the ClassID. */
 
			_station_classes[i].id = cls;
 
			return i;
 
		}
 
	}
 

	
 
	grfmsg(2, "StationClassAllocate: already allocated %d classes, using default", STAT_CLASS_MAX);
 
	return STAT_CLASS_DFLT;
 
}
 

	
 
/** Set the name of a custom station class */
 
void SetStationClassName(StationClassID sclass, StringID name)
 
{
 
	assert(sclass < STAT_CLASS_MAX);
 
	_station_classes[sclass].name = name;
 
}
 

	
 
/** Retrieve the name of a custom station class */
 
StringID GetStationClassName(StationClassID sclass)
 
{
 
	assert(sclass < STAT_CLASS_MAX);
 
	return _station_classes[sclass].name;
 
}
 

	
 
/**
 
 * Get the number of station classes in use.
 
 * @return Number of station classes.
 
 */
 
uint GetNumStationClasses()
 
{
 
	uint i;
 
	for (i = 0; i < STAT_CLASS_MAX && _station_classes[i].id != 0; i++) {}
 
	return i;
 
}
 

	
 
/**
 
 * Return the number of stations for the given station class.
 
 * @param sclass Index of the station class.
 
 * @return Number of stations in the class.
 
 */
 
uint GetNumCustomStations(StationClassID sclass)
 
{
 
	assert(sclass < STAT_CLASS_MAX);
 
	return _station_classes[sclass].stations;
 
}
 

	
 
/**
 
 * Tie a station spec to its station class.
 
 * @param statspec The station spec.
 
 */
 
void SetCustomStationSpec(StationSpec *statspec)
 
{
 
	StationClass *station_class;
 
	int i;
 

	
 
	/* If the station has already been allocated, don't reallocate it. */
 
	if (statspec->allocated) return;
 

	
 
	assert(statspec->sclass < STAT_CLASS_MAX);
 
	station_class = &_station_classes[statspec->sclass];
 

	
 
	i = station_class->stations++;
 
	station_class->spec = ReallocT(station_class->spec, station_class->stations);
 

	
 
	station_class->spec[i] = statspec;
 
	statspec->allocated = true;
 
}
 

	
 
/**
 
 * Retrieve a station spec from a class.
 
 * @param sclass Index of the station class.
 
 * @param station The station index with the class.
 
 * @return The station spec.
 
 */
 
const StationSpec *GetCustomStationSpec(StationClassID sclass, uint station)
 
{
 
	assert(sclass < STAT_CLASS_MAX);
 
	if (station < _station_classes[sclass].stations)
 
		return _station_classes[sclass].spec[station];
 

	
 
	/* If the custom station isn't defined any more, then the GRF file
 
	 * probably was not loaded. */
 
	return NULL;
 
}
 

	
 
/**
 
 * Retrieve a station spec by GRF location.
 
 * @param grfid    GRF ID of station spec.
 
 * @param localidx Index within GRF file of station spec.
 
 * @param index    Pointer to return the index of the station spec in its station class. If NULL then not used.
 
 * @return The station spec.
 
 */
 
const StationSpec *GetCustomStationSpecByGrf(uint32 grfid, byte localidx, int *index)
 
{
 
	uint j;
 

	
 
	for (StationClassID i = STAT_CLASS_BEGIN; i < STAT_CLASS_MAX; i++) {
 
		for (j = 0; j < _station_classes[i].stations; j++) {
 
			const StationSpec *statspec = _station_classes[i].spec[j];
 
			if (statspec == NULL) continue;
 
			if (statspec->grffile->grfid == grfid && statspec->localidx == localidx) {
 
				if (index != NULL) *index = j;
 
				return statspec;
 
			}
 
		}
 
	}
 

	
 
	return NULL;
 
}
src/osk_gui.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file osk_gui.cpp The On Screen Keyboard GUI */
 

	
 
#include "stdafx.h"
 
#include "string_func.h"
 
#include "strings_func.h"
 
#include "debug.h"
 
#include "window_func.h"
 
#include "gfx_func.h"
 
#include "querystring_gui.h"
 

	
 
#include "table/sprites.h"
 
#include "table/strings.h"
 

	
 
/** Widget numbers of the on-screen keyboard (OSK) window. */
 
enum OskWidgets {
 
	OSK_WIDGET_CAPTION,         ///< Title bar.
 
	OSK_WIDGET_TEXT,            ///< Edit box.
 
	OSK_WIDGET_CANCEL,          ///< Cancel key.
 
	OSK_WIDGET_OK,              ///< Ok key.
 
	OSK_WIDGET_BACKSPACE,       ///< Backspace key.
 
	OSK_WIDGET_SPECIAL,         ///< Special key (at keyborads often used for tab key).
 
	OSK_WIDGET_CAPS,            ///< Capslock key.
 
	OSK_WIDGET_SHIFT,           ///< Shift(lock) key.
 
	OSK_WIDGET_SPACE,           ///< Space bar.
 
	OSK_WIDGET_LEFT,            ///< Cursor left key.
 
	OSK_WIDGET_RIGHT,           ///< Cursor right key.
 
	OSK_WIDGET_LETTERS,         ///< First widget of the 'normal' keys.
 

	
 
	OSK_WIDGET_NUMBERS_FIRST = OSK_WIDGET_LETTERS,           ///< First widget of the numbers row.
 
	OSK_WIDGET_NUMBERS_LAST = OSK_WIDGET_NUMBERS_FIRST + 13, ///< Last widget of the numbers row.
 

	
 
	OSK_WIDGET_QWERTY_FIRST,                                 ///< First widget of the qwerty row.
 
	OSK_WIDGET_QWERTY_LAST = OSK_WIDGET_QWERTY_FIRST + 11,   ///< Last widget of the qwerty row.
 

	
 
	OSK_WIDGET_ASDFG_FIRST,                                  ///< First widget of the asdfg row.
 
	OSK_WIDGET_ASDFG_LAST = OSK_WIDGET_ASDFG_FIRST + 11,     ///< Last widget of the asdfg row.
 

	
 
	OSK_WIDGET_ZXCVB_FIRST,                                  ///< First widget of the zxcvb row.
 
	OSK_WIDGET_ZXCVB_LAST = OSK_WIDGET_ZXCVB_FIRST + 11,     ///< Last widget of the zxcvb row.
 
};
 

	
 
char _keyboard_opt[2][OSK_KEYBOARD_ENTRIES * 4 + 1];
 
static WChar _keyboard[2][OSK_KEYBOARD_ENTRIES];
 

	
 
enum {
 
enum KeyStateBits {
 
	KEYS_NONE,
 
	KEYS_SHIFT,
 
	KEYS_CAPS
 
};
 
static byte _keystate = KEYS_NONE;
 

	
 
struct OskWindow : public Window {
 
	StringID caption;      ///< the caption for this window.
 
	QueryString *qs;       ///< text-input
 
	int text_btn;          ///< widget number of parent's text field
 
	int ok_btn;            ///< widget number of parent's ok button (=0 when ok shouldn't be passed on)
 
	int cancel_btn;        ///< widget number of parent's cancel button (=0 when cancel shouldn't be passed on; text will be reverted to original)
 
	Textbuf *text;         ///< pointer to parent's textbuffer (to update caret position)
 
	char *orig_str_buf;    ///< Original string.
 
	bool shift;            ///< Is the shift effectively pressed?
 

	
 
	OskWindow(const WindowDesc *desc, QueryStringBaseWindow *parent, int button, int cancel, int ok) : Window()
 
	{
 
		this->parent = parent;
 
		assert(parent != NULL);
 

	
 
		NWidgetCore *par_wid = parent->GetWidget<NWidgetCore>(button);
 
		assert(par_wid != NULL);
 
		this->caption = (par_wid->widget_data != STR_NULL) ? par_wid->widget_data : parent->caption;
 

	
 
		this->qs         = parent;
 
		this->text_btn   = button;
 
		this->cancel_btn = cancel;
 
		this->ok_btn     = ok;
 
		this->text       = &parent->text;
 

	
 
		/* make a copy in case we need to reset later */
 
		this->orig_str_buf = strdup(this->qs->text.buf);
 

	
 
		this->InitNested(desc, 0);
 

	
 
		/* Not needed by default. */
 
		this->DisableWidget(OSK_WIDGET_SPECIAL);
 

	
 
		this->UpdateOskState();
 
	}
 

	
 
	~OskWindow()
 
	{
 
		free(this->orig_str_buf);
 
	}
 

	
 
	/**
 
	 * Only show valid characters; do not show characters that would
 
	 * only insert a space when we have a spacebar to do that or
 
	 * characters that are not allowed to be entered.
 
	 */
 
	void UpdateOskState()
 
	{
 
		this->shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT);
 

	
 
		for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
 
			this->SetWidgetDisabledState(OSK_WIDGET_LETTERS + i,
 
					!IsValidChar(_keyboard[this->shift][i], this->qs->afilter) || _keyboard[this->shift][i] == ' ');
 
		}
 
		this->SetWidgetDisabledState(OSK_WIDGET_SPACE, !IsValidChar(' ', this->qs->afilter));
 

	
 
		this->LowerWidget(OSK_WIDGET_TEXT);
 
		this->SetWidgetLoweredState(OSK_WIDGET_SHIFT, HasBit(_keystate, KEYS_SHIFT));
 
		this->SetWidgetLoweredState(OSK_WIDGET_CAPS, HasBit(_keystate, KEYS_CAPS));
 
	}
 

	
 
	virtual void SetStringParameters(int widget) const
 
	{
 
		if (widget == OSK_WIDGET_CAPTION) SetDParam(0, this->caption);
 
	}
 

	
 
	virtual void DrawWidget(const Rect &r, int widget) const
 
	{
 
		if (widget < OSK_WIDGET_LETTERS) return;
 

	
 
		widget -= OSK_WIDGET_LETTERS;
 
		DrawCharCentered(_keyboard[this->shift][widget],
 
			r.left + 8,
 
			r.top + 3,
 
			TC_BLACK);
 
	}
 

	
 
	virtual void OnPaint()
 
	{
 
		this->DrawWidgets();
 

	
 
		this->qs->DrawEditBox(this, OSK_WIDGET_TEXT);
 
	}
 

	
 
	virtual void OnClick(Point pt, int widget, int click_count)
 
	{
 
		/* clicked a letter */
 
		if (widget >= OSK_WIDGET_LETTERS) {
 
			WChar c = _keyboard[this->shift][widget - OSK_WIDGET_LETTERS];
 

	
 
			if (!IsValidChar(c, this->qs->afilter)) return;
 

	
 
			if (InsertTextBufferChar(&this->qs->text, c)) this->InvalidateParent();
 

	
 
			if (HasBit(_keystate, KEYS_SHIFT)) {
 
				ToggleBit(_keystate, KEYS_SHIFT);
 
				this->GetWidget<NWidgetCore>(OSK_WIDGET_SHIFT)->colour = HasBit(_keystate, KEYS_SHIFT) ? COLOUR_WHITE : COLOUR_GREY;
 
				this->SetDirty();
 
			}
 
			return;
 
		}
 

	
 
		switch (widget) {
 
			case OSK_WIDGET_TEXT:
 
				/* Return focus to the parent widget and window. */
 
				this->parent->SetFocusedWidget(this->text_btn);
 
				SetFocusedWindow(this->parent);
 
				break;
 

	
 
			case OSK_WIDGET_BACKSPACE:
 
				if (DeleteTextBufferChar(&this->qs->text, WKC_BACKSPACE)) this->InvalidateParent();
 
				break;
 

	
 
			case OSK_WIDGET_SPECIAL:
 
				/*
 
				 * Anything device specific can go here.
 
				 * The button itself is hidden by default, and when you need it you
 
				 * can not hide it in the create event.
 
				 */
 
				break;
 

	
 
			case OSK_WIDGET_CAPS:
 
				ToggleBit(_keystate, KEYS_CAPS);
 
				this->UpdateOskState();
 
				this->SetDirty();
 
				break;
 

	
 
			case OSK_WIDGET_SHIFT:
 
				ToggleBit(_keystate, KEYS_SHIFT);
 
				this->UpdateOskState();
 
				this->SetDirty();
 
				break;
 

	
 
			case OSK_WIDGET_SPACE:
 
				if (InsertTextBufferChar(&this->qs->text, ' ')) this->InvalidateParent();
 
				break;
 

	
 
			case OSK_WIDGET_LEFT:
 
				if (MoveTextBufferPos(&this->qs->text, WKC_LEFT)) this->InvalidateParent();
 
				break;
 

	
 
			case OSK_WIDGET_RIGHT:
 
				if (MoveTextBufferPos(&this->qs->text, WKC_RIGHT)) this->InvalidateParent();
 
				break;
 

	
 
			case OSK_WIDGET_OK:
 
				if (this->qs->orig == NULL || strcmp(this->qs->text.buf, this->qs->orig) != 0) {
 
					/* pass information by simulating a button press on parent window */
 
					if (this->ok_btn != 0) {
 
						this->parent->OnClick(pt, this->ok_btn, 1);
 
						/* Window gets deleted when the parent window removes itself. */
 
						return;
 
					}
 
				}
 
				delete this;
 
				break;
 

	
 
			case OSK_WIDGET_CANCEL:
 
				if (this->cancel_btn != 0) { // pass a cancel event to the parent window
 
					this->parent->OnClick(pt, this->cancel_btn, 1);
 
					/* Window gets deleted when the parent window removes itself. */
 
					return;
 
				} else { // or reset to original string
 
					strcpy(qs->text.buf, this->orig_str_buf);
 
					UpdateTextBufferSize(&qs->text);
 
					MoveTextBufferPos(&qs->text, WKC_END);
 
					this->InvalidateParent();
 
					delete this;
 
				}
 
				break;
 
		}
 
	}
 

	
 
	void InvalidateParent()
 
	{
 
		QueryStringBaseWindow *w = dynamic_cast<QueryStringBaseWindow*>(this->parent);
 
		if (w != NULL) w->OnOSKInput(this->text_btn);
 

	
 
		this->SetWidgetDirty(OSK_WIDGET_TEXT);
 
		if (this->parent != NULL) this->parent->SetWidgetDirty(this->text_btn);
 
	}
 

	
 
	virtual void OnMouseLoop()
 
	{
 
		this->qs->HandleEditBox(this, OSK_WIDGET_TEXT);
 
		/* make the caret of the parent window also blink */
src/pathfinder/npf/npf.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file npf.cpp Implementation of the NPF pathfinder. */
 

	
 
#include "../../stdafx.h"
 
#include "../../debug.h"
 
#include "../../landscape.h"
 
#include "../../network/network.h"
 
#include "../../functions.h"
 
#include "../../ship.h"
 
#include "../../roadstop_base.h"
 
#include "../pathfinder_func.h"
 
#include "../pathfinder_type.h"
 
#include "../follow_track.hpp"
 
#include "aystar.h"
 

	
 
enum {
 
	NPF_HASH_BITS = 12, ///< The size of the hash used in pathfinding. Just changing this value should be sufficient to change the hash size. Should be an even value.
 
	/* Do no change below values */
 
	NPF_HASH_SIZE = 1 << NPF_HASH_BITS,
 
	NPF_HASH_HALFBITS = NPF_HASH_BITS / 2,
 
	NPF_HASH_HALFMASK = (1 << NPF_HASH_HALFBITS) - 1
 
};
 
static const uint NPF_HASH_BITS = 12; ///< The size of the hash used in pathfinding. Just changing this value should be sufficient to change the hash size. Should be an even value.
 
/* Do no change below values */
 
static const uint NPF_HASH_SIZE = 1 << NPF_HASH_BITS;
 
static const uint NPF_HASH_HALFBITS = NPF_HASH_BITS / 2;
 
static const uint NPF_HASH_HALFMASK = (1 << NPF_HASH_HALFBITS) - 1;
 

	
 
/* Meant to be stored in AyStar.targetdata */
 
/** Meant to be stored in AyStar.targetdata */
 
struct NPFFindStationOrTileData {
 
	TileIndex dest_coords;    ///< An indication of where the station is, for heuristic purposes, or the target tile
 
	StationID station_index;  ///< station index we're heading for, or INVALID_STATION when we're heading for a tile
 
	bool reserve_path;        ///< Indicates whether the found path should be reserved
 
	StationType station_type; ///< The type of station we're heading for
 
	bool not_articulated;     ///< The (road) vehicle is not articulated
 
	const Vehicle *v;         ///< The vehicle we are pathfinding for
 
};
 

	
 
/* Indices into AyStar.userdata[] */
 
enum {
 
/** Indices into AyStar.userdata[] */
 
enum AyStarUserDataType {
 
	NPF_TYPE = 0,  ///< Contains a TransportTypes value
 
	NPF_SUB_TYPE,  ///< Contains the sub transport type
 
	NPF_OWNER,     ///< Contains an Owner value
 
	NPF_RAILTYPES, ///< Contains a bitmask the compatible RailTypes of the engine when NPF_TYPE == TRANSPORT_RAIL. Unused otherwise.
 
};
 

	
 
/* Indices into AyStarNode.userdata[] */
 
enum {
 
/** Indices into AyStarNode.userdata[] */
 
enum AyStarNodeUserDataType {
 
	NPF_TRACKDIR_CHOICE = 0, ///< The trackdir chosen to get here
 
	NPF_NODE_FLAGS,
 
};
 

	
 
/* Flags for AyStarNode.userdata[NPF_NODE_FLAGS]. Use NPFSetFlag() and NPFGetFlag() to use them. */
 
/** Flags for AyStarNode.userdata[NPF_NODE_FLAGS]. Use NPFSetFlag() and NPFGetFlag() to use them. */
 
enum NPFNodeFlag {
 
	NPF_FLAG_SEEN_SIGNAL,       ///< Used to mark that a signal was seen on the way, for rail only
 
	NPF_FLAG_2ND_SIGNAL,        ///< Used to mark that two signals were seen, rail only
 
	NPF_FLAG_3RD_SIGNAL,        ///< Used to mark that three signals were seen, rail only
 
	NPF_FLAG_REVERSE,           ///< Used to mark that this node was reached from the second start node, if applicable
 
	NPF_FLAG_LAST_SIGNAL_RED,   ///< Used to mark that the last signal on this path was red
 
	NPF_FLAG_LAST_SIGNAL_BLOCK, ///< Used to mark that the last signal on this path was a block signal
 
	NPF_FLAG_IGNORE_START_TILE, ///< Used to mark that the start tile is invalid, and searching should start from the second tile on
 
	NPF_FLAG_TARGET_RESERVED,   ///< Used to mark that the possible reservation target is already reserved
 
	NPF_FLAG_IGNORE_RESERVED,   ///< Used to mark that reserved tiles should be considered impassable
 
};
 

	
 
/* Meant to be stored in AyStar.userpath */
 
/** Meant to be stored in AyStar.userpath */
 
struct NPFFoundTargetData {
 
	uint best_bird_dist;    ///< The best heuristic found. Is 0 if the target was found
 
	uint best_path_dist;    ///< The shortest path. Is UINT_MAX if no path is found
 
	Trackdir best_trackdir; ///< The trackdir that leads to the shortest path/closest birds dist
 
	AyStarNode node;        ///< The node within the target the search led us to
 
	bool res_okay;          ///< True if a path reservation could be made
 
};
 

	
 
static AyStar _npf_aystar;
 

	
 
/* The cost of each trackdir. A diagonal piece is the full NPF_TILE_LENGTH,
 
 * the shorter piece is sqrt(2)/2*NPF_TILE_LENGTH =~ 0.7071
 
 */
 
#define NPF_STRAIGHT_LENGTH (uint)(NPF_TILE_LENGTH * STRAIGHT_TRACK_LENGTH)
 
static const uint _trackdir_length[TRACKDIR_END] = {
 
	NPF_TILE_LENGTH, NPF_TILE_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH,
 
	0, 0,
 
	NPF_TILE_LENGTH, NPF_TILE_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH
 
};
 

	
 
/**
 
 * Returns the current value of the given flag on the given AyStarNode.
 
 */
 
static inline bool NPFGetFlag(const AyStarNode *node, NPFNodeFlag flag)
 
{
 
	return HasBit(node->user_data[NPF_NODE_FLAGS], flag);
 
}
 

	
 
/**
 
 * Sets the given flag on the given AyStarNode to the given value.
 
 */
 
static inline void NPFSetFlag(AyStarNode *node, NPFNodeFlag flag, bool value)
 
{
 
	SB(node->user_data[NPF_NODE_FLAGS], flag, 1, value);
 
}
 

	
 
/**
 
 * Calculates the minimum distance traveled to get from t0 to t1 when only
 
 * using tracks (ie, only making 45 degree turns). Returns the distance in the
 
 * NPF scale, ie the number of full tiles multiplied by NPF_TILE_LENGTH to
 
 * prevent rounding.
 
 */
 
static uint NPFDistanceTrack(TileIndex t0, TileIndex t1)
 
{
 
	const uint dx = Delta(TileX(t0), TileX(t1));
 
	const uint dy = Delta(TileY(t0), TileY(t1));
 

	
 
	const uint straightTracks = 2 * min(dx, dy); // The number of straight (not full length) tracks
 
	/* OPTIMISATION:
 
	 * Original: diagTracks = max(dx, dy) - min(dx,dy);
 
	 * Proof:
 
	 * (dx+dy) - straightTracks  == (min + max) - straightTracks = min + max - 2 * min = max - min */
 
	const uint diagTracks = dx + dy - straightTracks; // The number of diagonal (full tile length) tracks.
 

	
 
	/* Don't factor out NPF_TILE_LENGTH below, this will round values and lose
 
	 * precision */
 
	return diagTracks * NPF_TILE_LENGTH + straightTracks * NPF_TILE_LENGTH * STRAIGHT_TRACK_LENGTH;
 
}
 

	
 

	
 
#if 0
 
static uint NTPHash(uint key1, uint key2)
 
{
 
	/* This function uses the old hash, which is fixed on 10 bits (1024 buckets) */
 
	return PATHFIND_HASH_TILE(key1);
 
}
 
#endif
 

	
 
/**
 
 * Calculates a hash value for use in the NPF.
 
 * @param key1 The TileIndex of the tile to hash
 
 * @param key2 The Trackdir of the track on the tile.
 
 *
 
 * @todo Think of a better hash.
 
 */
 
static uint NPFHash(uint key1, uint key2)
 
{
 
	/* TODO: think of a better hash? */
 
	uint part1 = TileX(key1) & NPF_HASH_HALFMASK;
 
	uint part2 = TileY(key1) & NPF_HASH_HALFMASK;
 

	
 
	assert(IsValidTrackdir((Trackdir)key2));
 
	assert(IsValidTile(key1));
 
	return ((part1 << NPF_HASH_HALFBITS | part2) + (NPF_HASH_SIZE * key2 / TRACKDIR_END)) % NPF_HASH_SIZE;
 
}
 

	
 
static int32 NPFCalcZero(AyStar *as, AyStarNode *current, OpenListNode *parent)
 
{
 
	return 0;
 
}
 

	
 
/* Calcs the heuristic to the target station or tile. For train stations, it
 
 * takes into account the direction of approach.
 
 */
 
static int32 NPFCalcStationOrTileHeuristic(AyStar *as, AyStarNode *current, OpenListNode *parent)
 
{
 
	NPFFindStationOrTileData *fstd = (NPFFindStationOrTileData*)as->user_target;
 
	NPFFoundTargetData *ftd = (NPFFoundTargetData*)as->user_path;
 
	TileIndex from = current->tile;
 
	TileIndex to = fstd->dest_coords;
 
	uint dist;
 

	
 
	/* for train-stations, we are going to aim for the closest station tile */
 
	if (as->user_data[NPF_TYPE] != TRANSPORT_WATER && fstd->station_index != INVALID_STATION)
 
		to = CalcClosestStationTile(fstd->station_index, from, fstd->station_type);
 

	
 
	if (as->user_data[NPF_TYPE] == TRANSPORT_ROAD) {
 
		/* Since roads only have diagonal pieces, we use manhattan distance here */
 
		dist = DistanceManhattan(from, to) * NPF_TILE_LENGTH;
 
	} else {
 
		/* Ships and trains can also go diagonal, so the minimum distance is shorter */
 
		dist = NPFDistanceTrack(from, to);
 
	}
 

	
 
	DEBUG(npf, 4, "Calculating H for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), dist);
 

	
 
	if (dist < ftd->best_bird_dist) {
 
		ftd->best_bird_dist = dist;
 
		ftd->best_trackdir = (Trackdir)current->user_data[NPF_TRACKDIR_CHOICE];
 
	}
 
	return dist;
 
}
 

	
 

	
 
/* Fills AyStarNode.user_data[NPF_TRACKDIRCHOICE] with the chosen direction to
 
 * get here, either getting it from the current choice or from the parent's
 
 * choice */
 
static void NPFFillTrackdirChoice(AyStarNode *current, OpenListNode *parent)
 
{
 
	if (parent->path.parent == NULL) {
 
		Trackdir trackdir = current->direction;
 
		/* This is a first order decision, so we'd better save the
 
		 * direction we chose */
 
		current->user_data[NPF_TRACKDIR_CHOICE] = trackdir;
 
		DEBUG(npf, 6, "Saving trackdir: 0x%X", trackdir);
 
	} else {
 
		/* We've already made the decision, so just save our parent's decision */
 
		current->user_data[NPF_TRACKDIR_CHOICE] = parent->path.node.user_data[NPF_TRACKDIR_CHOICE];
 
	}
 
}
 

	
 
/* Will return the cost of the tunnel. If it is an entry, it will return the
 
 * cost of that tile. If the tile is an exit, it will return the tunnel length
 
 * including the exit tile. Requires that this is a Tunnel tile */
 
static uint NPFTunnelCost(AyStarNode *current)
 
{
 
	DiagDirection exitdir = TrackdirToExitdir(current->direction);
 
	TileIndex tile = current->tile;
 
	if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(exitdir)) {
 
		/* We just popped out if this tunnel, since were
 
		 * facing the tunnel exit */
 
		return NPF_TILE_LENGTH * (GetTunnelBridgeLength(current->tile, GetOtherTunnelEnd(current->tile)) + 1);
 
		/* @todo: Penalty for tunnels? */
 
	} else {
 
		/* We are entering the tunnel, the enter tile is just a
 
		 * straight track */
 
		return NPF_TILE_LENGTH;
 
	}
 
}
 

	
 
static inline uint NPFBridgeCost(AyStarNode *current)
 
{
 
	return NPF_TILE_LENGTH * GetTunnelBridgeLength(current->tile, GetOtherBridgeEnd(current->tile));
 
}
 

	
 
static uint NPFSlopeCost(AyStarNode *current)
 
{
 
	TileIndex next = current->tile + TileOffsByDiagDir(TrackdirToExitdir(current->direction));
 

	
 
	/* Get center of tiles */
 
	int x1 = TileX(current->tile) * TILE_SIZE + TILE_SIZE / 2;
 
	int y1 = TileY(current->tile) * TILE_SIZE + TILE_SIZE / 2;
 
	int x2 = TileX(next) * TILE_SIZE + TILE_SIZE / 2;
 
	int y2 = TileY(next) * TILE_SIZE + TILE_SIZE / 2;
 

	
 
	int dx4 = (x2 - x1) / 4;
 
	int dy4 = (y2 - y1) / 4;
 

	
 
	/* Get the height on both sides of the tile edge.
 
	 * Avoid testing the height on the tile-center. This will fail for halftile-foundations.
 
	 */
 
	int z1 = GetSlopeZ(x1 + dx4, y1 + dy4);
 
	int z2 = GetSlopeZ(x2 - dx4, y2 - dy4);
 

	
 
	if (z2 - z1 > 1) {
 
		/* Slope up */
 
		return _settings_game.pf.npf.npf_rail_slope_penalty;
 
	}
 
	return 0;
 
	/* Should we give a bonus for slope down? Probably not, we
 
	 * could just substract that bonus from the penalty, because
 
	 * there is only one level of steepness... */

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)