(svn r7759) -Merge: makefile rewrite. This merge features:
- A proper ./configure, so everything needs to be configured only once, not for every make. - Usage of makedepend when available. This greatly reduces the time needed for generating the dependencies. - A generator for all project files. There is a single file with sources, which is used to generate Makefiles and the project files for MSVC. - Proper support for OSX universal binaries. - Object files for non-MSVC compiles are also placed in separate directories, making is faster to switch between debug and release compiles and it does not touch the directory with the source files. - Functionality to make a bundle of all needed files for for example a nightly or distribution of a binary with all needed GRFs and language files. Note: as this merge moves almost all files, it is recommended to make a backup of your working copy before updating your working copy.
This commit is contained in:
247
src/ai/ai.c
Normal file
247
src/ai/ai.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../openttd.h"
|
||||
#include "../variables.h"
|
||||
#include "../command.h"
|
||||
#include "../network/network.h"
|
||||
#include "ai.h"
|
||||
#include "default/default.h"
|
||||
|
||||
/**
|
||||
* Dequeues commands put in the queue via AI_PutCommandInQueue.
|
||||
*/
|
||||
static void AI_DequeueCommands(PlayerID player)
|
||||
{
|
||||
AICommand *com, *entry_com;
|
||||
|
||||
entry_com = _ai_player[player].queue;
|
||||
|
||||
/* It happens that DoCommandP issues a new DoCommandAI which adds a new command
|
||||
* to this very same queue (don't argue about this, if it currently doesn't
|
||||
* happen I can tell you it will happen with AIScript -- TrueLight). If we
|
||||
* do not make the queue NULL, that commands will be dequeued immediatly.
|
||||
* Therefor we safe the entry-point to entry_com, and make the queue NULL, so
|
||||
* the new queue can be safely built up. */
|
||||
_ai_player[player].queue = NULL;
|
||||
_ai_player[player].queue_tail = NULL;
|
||||
|
||||
/* Dequeue all commands */
|
||||
while ((com = entry_com) != NULL) {
|
||||
_current_player = player;
|
||||
|
||||
_cmd_text = com->text;
|
||||
DoCommandP(com->tile, com->p1, com->p2, com->callback, com->procc);
|
||||
|
||||
/* Free item */
|
||||
entry_com = com->next;
|
||||
free(com->text);
|
||||
free(com);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed for SP; we need to delay DoCommand with 1 tick, because else events
|
||||
* will make infinite loops (AIScript).
|
||||
*/
|
||||
static void AI_PutCommandInQueue(PlayerID player, TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCallback* callback)
|
||||
{
|
||||
AICommand *com;
|
||||
|
||||
if (_ai_player[player].queue_tail == NULL) {
|
||||
/* There is no item in the queue yet, create the queue */
|
||||
_ai_player[player].queue = malloc(sizeof(AICommand));
|
||||
_ai_player[player].queue_tail = _ai_player[player].queue;
|
||||
} else {
|
||||
/* Add an item at the end */
|
||||
_ai_player[player].queue_tail->next = malloc(sizeof(AICommand));
|
||||
_ai_player[player].queue_tail = _ai_player[player].queue_tail->next;
|
||||
}
|
||||
|
||||
/* This is our new item */
|
||||
com = _ai_player[player].queue_tail;
|
||||
|
||||
/* Assign the info */
|
||||
com->tile = tile;
|
||||
com->p1 = p1;
|
||||
com->p2 = p2;
|
||||
com->procc = procc;
|
||||
com->callback = callback;
|
||||
com->next = NULL;
|
||||
com->text = NULL;
|
||||
|
||||
/* Copy the cmd_text, if needed */
|
||||
if (_cmd_text != NULL) {
|
||||
com->text = strdup(_cmd_text);
|
||||
_cmd_text = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a raw DoCommand for the AI.
|
||||
*/
|
||||
int32 AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc, CommandCallback* callback)
|
||||
{
|
||||
PlayerID old_lp;
|
||||
int32 res = 0;
|
||||
const char* tmp_cmdtext;
|
||||
|
||||
/* If you enable DC_EXEC with DC_QUERY_COST you are a really strange
|
||||
* person.. should we check for those funny jokes?
|
||||
*/
|
||||
|
||||
/* The test already resets _cmd_text, so backup the pointer */
|
||||
tmp_cmdtext = _cmd_text;
|
||||
|
||||
/* First, do a test-run to see if we can do this */
|
||||
res = DoCommand(tile, p1, p2, flags & ~DC_EXEC, procc);
|
||||
/* The command failed, or you didn't want to execute, or you are quering, return */
|
||||
if (CmdFailed(res) || !(flags & DC_EXEC) || (flags & DC_QUERY_COST)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Restore _cmd_text */
|
||||
_cmd_text = tmp_cmdtext;
|
||||
|
||||
/* If we did a DC_EXEC, and the command did not return an error, execute it
|
||||
* over the network */
|
||||
if (flags & DC_AUTO) procc |= CMD_AUTO;
|
||||
if (flags & DC_NO_WATER) procc |= CMD_NO_WATER;
|
||||
|
||||
/* NetworkSend_Command needs _local_player to be set correctly, so
|
||||
* adjust it, and put it back right after the function */
|
||||
old_lp = _local_player;
|
||||
_local_player = _current_player;
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
/* Send the command */
|
||||
if (_networking) {
|
||||
/* Network is easy, send it to his handler */
|
||||
NetworkSend_Command(tile, p1, p2, procc, callback);
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
/* If we execute BuildCommands directly in SP, we have a big problem with events
|
||||
* so we need to delay is for 1 tick */
|
||||
AI_PutCommandInQueue(_current_player, tile, p1, p2, procc, callback);
|
||||
}
|
||||
|
||||
/* Set _local_player back */
|
||||
_local_player = old_lp;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int32 AI_DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
|
||||
{
|
||||
return AI_DoCommandCc(tile, p1, p2, flags, procc, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run 1 tick of the AI. Don't overdo it, keep it realistic.
|
||||
*/
|
||||
static void AI_RunTick(PlayerID player)
|
||||
{
|
||||
extern void AiNewDoGameLoop(Player *p);
|
||||
|
||||
Player *p = GetPlayer(player);
|
||||
_current_player = player;
|
||||
|
||||
if (_patches.ainew_active) {
|
||||
AiNewDoGameLoop(p);
|
||||
} else {
|
||||
/* Enable all kind of cheats the old AI needs in order to operate correctly... */
|
||||
_is_old_ai_player = true;
|
||||
AiDoGameLoop(p);
|
||||
_is_old_ai_player = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The gameloop for AIs.
|
||||
* Handles one tick for all the AIs.
|
||||
*/
|
||||
void AI_RunGameLoop(void)
|
||||
{
|
||||
/* Don't do anything if ai is disabled */
|
||||
if (!_ai.enabled) return;
|
||||
|
||||
/* Don't do anything if we are a network-client, or the AI has been disabled */
|
||||
if (_networking && (!_network_server || !_patches.ai_in_multiplayer)) return;
|
||||
|
||||
/* New tick */
|
||||
_ai.tick++;
|
||||
|
||||
/* Make sure the AI follows the difficulty rule.. */
|
||||
assert(_opt.diff.competitor_speed <= 4);
|
||||
if ((_ai.tick & ((1 << (4 - _opt.diff.competitor_speed)) - 1)) != 0) return;
|
||||
|
||||
/* Check for AI-client (so joining a network with an AI) */
|
||||
if (!_networking || _network_server) {
|
||||
/* Check if we want to run AIs (server or SP only) */
|
||||
const Player* p;
|
||||
|
||||
FOR_ALL_PLAYERS(p) {
|
||||
if (p->is_active && p->is_ai) {
|
||||
/* This should always be true, else something went wrong... */
|
||||
assert(_ai_player[p->index].active);
|
||||
|
||||
/* Run the script */
|
||||
AI_DequeueCommands(p->index);
|
||||
AI_RunTick(p->index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_current_player = OWNER_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* A new AI sees the day of light. You can do here what ever you think is needed.
|
||||
*/
|
||||
void AI_StartNewAI(PlayerID player)
|
||||
{
|
||||
assert(IsValidPlayer(player));
|
||||
|
||||
/* Called if a new AI is booted */
|
||||
_ai_player[player].active = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This AI player died. Give it some chance to make a final puf.
|
||||
*/
|
||||
void AI_PlayerDied(PlayerID player)
|
||||
{
|
||||
/* Called if this AI died */
|
||||
_ai_player[player].active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize some AI-related stuff.
|
||||
*/
|
||||
void AI_Initialize(void)
|
||||
{
|
||||
/* First, make sure all AIs are DEAD! */
|
||||
AI_Uninitialize();
|
||||
|
||||
memset(&_ai, 0, sizeof(_ai));
|
||||
memset(&_ai_player, 0, sizeof(_ai_player));
|
||||
|
||||
_ai.enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinitializer for AI-related stuff.
|
||||
*/
|
||||
void AI_Uninitialize(void)
|
||||
{
|
||||
const Player* p;
|
||||
|
||||
FOR_ALL_PLAYERS(p) {
|
||||
if (p->is_active && p->is_ai) AI_PlayerDied(p->index);
|
||||
}
|
||||
}
|
||||
111
src/ai/ai.h
Normal file
111
src/ai/ai.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#ifndef AI_H
|
||||
#define AI_H
|
||||
|
||||
#include "../functions.h"
|
||||
#include "../network/network.h"
|
||||
#include "../player.h"
|
||||
#include "../command.h"
|
||||
|
||||
/* How DoCommands look like for an AI */
|
||||
typedef struct AICommand {
|
||||
uint32 tile;
|
||||
uint32 p1;
|
||||
uint32 p2;
|
||||
uint32 procc;
|
||||
CommandCallback* callback;
|
||||
|
||||
char *text;
|
||||
uint uid;
|
||||
|
||||
struct AICommand *next;
|
||||
} AICommand;
|
||||
|
||||
/* The struct for an AIScript Player */
|
||||
typedef struct AIPlayer {
|
||||
bool active; ///< Is this AI active?
|
||||
AICommand *queue; ///< The commands that he has in his queue
|
||||
AICommand *queue_tail; ///< The tail of this queue
|
||||
} AIPlayer;
|
||||
|
||||
/* The struct to keep some data about the AI in general */
|
||||
typedef struct AIStruct {
|
||||
/* General */
|
||||
bool enabled; ///< Is AI enabled?
|
||||
uint tick; ///< The current tick (something like _frame_counter, only for AIs)
|
||||
} AIStruct;
|
||||
|
||||
VARDEF AIStruct _ai;
|
||||
VARDEF AIPlayer _ai_player[MAX_PLAYERS];
|
||||
|
||||
// ai.c
|
||||
void AI_StartNewAI(PlayerID player);
|
||||
void AI_PlayerDied(PlayerID player);
|
||||
void AI_RunGameLoop(void);
|
||||
void AI_Initialize(void);
|
||||
void AI_Uninitialize(void);
|
||||
int32 AI_DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc);
|
||||
int32 AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc, CommandCallback* callback);
|
||||
|
||||
/** Is it allowed to start a new AI.
|
||||
* This function checks some boundries to see if we should launch a new AI.
|
||||
* @return True if we can start a new AI.
|
||||
*/
|
||||
static inline bool AI_AllowNewAI(void)
|
||||
{
|
||||
/* If disabled, no AI */
|
||||
if (!_ai.enabled)
|
||||
return false;
|
||||
|
||||
/* If in network, but no server, no AI */
|
||||
if (_networking && !_network_server)
|
||||
return false;
|
||||
|
||||
/* If in network, and server, possible AI */
|
||||
if (_networking && _network_server) {
|
||||
/* Do we want AIs in multiplayer? */
|
||||
if (!_patches.ai_in_multiplayer)
|
||||
return false;
|
||||
|
||||
/* Only the NewAI is allowed... sadly enough the old AI just doesn't support this
|
||||
* system, because all commands are delayed by at least 1 tick, which causes
|
||||
* a big problem, because it uses variables that are only set AFTER the command
|
||||
* is really executed... */
|
||||
if (!_patches.ainew_active)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define AI_CHANCE16(a,b) ((uint16) AI_Random() <= (uint16)((65536 * a) / b))
|
||||
#define AI_CHANCE16R(a,b,r) ((uint16)(r = AI_Random()) <= (uint16)((65536 * a) / b))
|
||||
|
||||
/**
|
||||
* The random-function that should be used by ALL AIs.
|
||||
*/
|
||||
static inline uint AI_RandomRange(uint max)
|
||||
{
|
||||
/* We pick RandomRange if we are in SP (so when saved, we do the same over and over)
|
||||
* but we pick InteractiveRandomRange if we are a network_server or network-client.
|
||||
*/
|
||||
if (_networking)
|
||||
return InteractiveRandomRange(max);
|
||||
else
|
||||
return RandomRange(max);
|
||||
}
|
||||
|
||||
/**
|
||||
* The random-function that should be used by ALL AIs.
|
||||
*/
|
||||
static inline uint32 AI_Random(void)
|
||||
{
|
||||
/* We pick RandomRange if we are in SP (so when saved, we do the same over and over)
|
||||
* but we pick InteractiveRandomRange if we are a network_server or network-client.
|
||||
*/
|
||||
if (_networking)
|
||||
return InteractiveRandom();
|
||||
else
|
||||
return Random();
|
||||
}
|
||||
|
||||
#endif /* AI_H */
|
||||
3934
src/ai/default/default.c
Normal file
3934
src/ai/default/default.c
Normal file
File diff suppressed because it is too large
Load Diff
8
src/ai/default/default.h
Normal file
8
src/ai/default/default.h
Normal file
@@ -0,0 +1,8 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef DEFAULT_H
|
||||
#define DEFAULT_H
|
||||
|
||||
void AiDoGameLoop(Player*);
|
||||
|
||||
#endif
|
||||
324
src/ai/trolly/build.c
Normal file
324
src/ai/trolly/build.c
Normal file
@@ -0,0 +1,324 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../openttd.h"
|
||||
#include "../../debug.h"
|
||||
#include "../../functions.h"
|
||||
#include "../../map.h"
|
||||
#include "../../road_map.h"
|
||||
#include "../../tile.h"
|
||||
#include "../../vehicle.h"
|
||||
#include "../../command.h"
|
||||
#include "trolly.h"
|
||||
#include "../../engine.h"
|
||||
#include "../../station.h"
|
||||
#include "../../variables.h"
|
||||
#include "../../bridge.h"
|
||||
#include "../ai.h"
|
||||
|
||||
// Build HQ
|
||||
// Params:
|
||||
// tile : tile where HQ is going to be build
|
||||
bool AiNew_Build_CompanyHQ(Player *p, TileIndex tile)
|
||||
{
|
||||
if (CmdFailed(AI_DoCommand(tile, 0, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ)))
|
||||
return false;
|
||||
AI_DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Build station
|
||||
// Params:
|
||||
// type : AI_TRAIN/AI_BUS/AI_TRUCK : indicates the type of station
|
||||
// tile : tile where station is going to be build
|
||||
// length : in case of AI_TRAIN: length of station
|
||||
// numtracks : in case of AI_TRAIN: tracks of station
|
||||
// direction : the direction of the station
|
||||
// flag : flag passed to DoCommand (normally 0 to get the cost or DC_EXEC to build it)
|
||||
int AiNew_Build_Station(Player *p, byte type, TileIndex tile, byte length, byte numtracks, byte direction, byte flag)
|
||||
{
|
||||
if (type == AI_TRAIN)
|
||||
return AI_DoCommand(tile, direction + (numtracks << 8) + (length << 16), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_STATION);
|
||||
|
||||
if (type == AI_BUS)
|
||||
return AI_DoCommand(tile, direction, RS_BUS, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP);
|
||||
|
||||
return AI_DoCommand(tile, direction, RS_TRUCK, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP);
|
||||
}
|
||||
|
||||
|
||||
// Builds a brdige. The second best out of the ones available for this player
|
||||
// Params:
|
||||
// tile_a : starting point
|
||||
// tile_b : end point
|
||||
// flag : flag passed to DoCommand
|
||||
int AiNew_Build_Bridge(Player *p, TileIndex tile_a, TileIndex tile_b, byte flag)
|
||||
{
|
||||
int bridge_type, bridge_len, type, type2;
|
||||
|
||||
// Find a good bridgetype (the best money can buy)
|
||||
bridge_len = GetBridgeLength(tile_a, tile_b);
|
||||
type = type2 = 0;
|
||||
for (bridge_type = MAX_BRIDGES-1; bridge_type >= 0; bridge_type--) {
|
||||
if (CheckBridge_Stuff(bridge_type, bridge_len)) {
|
||||
type2 = type;
|
||||
type = bridge_type;
|
||||
// We found two bridges, exit
|
||||
if (type2 != 0) break;
|
||||
}
|
||||
}
|
||||
// There is only one bridge that can be built
|
||||
if (type2 == 0 && type != 0) type2 = type;
|
||||
|
||||
// Now, simply, build the bridge!
|
||||
if (p->ainew.tbt == AI_TRAIN) {
|
||||
return AI_DoCommand(tile_a, tile_b, (0x00 << 8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE);
|
||||
} else {
|
||||
return AI_DoCommand(tile_a, tile_b, (0x80 << 8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Build the route part by part
|
||||
// Basicly what this function do, is build that amount of parts of the route
|
||||
// that go in the same direction. It sets 'part' to the last part of the route builded.
|
||||
// The return value is the cost for the builded parts
|
||||
//
|
||||
// Params:
|
||||
// PathFinderInfo : Pointer to the PathFinderInfo used for AiPathFinder
|
||||
// part : Which part we need to build
|
||||
//
|
||||
// TODO: skip already builded road-pieces (e.g.: cityroad)
|
||||
int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte flag)
|
||||
{
|
||||
int part = PathFinderInfo->position;
|
||||
byte *route_extra = PathFinderInfo->route_extra;
|
||||
TileIndex *route = PathFinderInfo->route;
|
||||
int dir;
|
||||
int old_dir = -1;
|
||||
int cost = 0;
|
||||
int res;
|
||||
// We need to calculate the direction with the parent of the parent.. so we skip
|
||||
// the first pieces and the last piece
|
||||
if (part < 1) part = 1;
|
||||
// When we are done, stop it
|
||||
if (part >= PathFinderInfo->route_length - 1) {
|
||||
PathFinderInfo->position = -2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (PathFinderInfo->rail_or_road) {
|
||||
// Tunnel code
|
||||
if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) {
|
||||
cost += AI_DoCommand(route[part], 0, 0, flag, CMD_BUILD_TUNNEL);
|
||||
PathFinderInfo->position++;
|
||||
// TODO: problems!
|
||||
if (CmdFailed(cost)) {
|
||||
DEBUG(ai, 0, "[BuildPath] tunnel could not be built (0x%X)", route[part]);
|
||||
return 0;
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
// Bridge code
|
||||
if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) {
|
||||
cost += AiNew_Build_Bridge(p, route[part], route[part-1], flag);
|
||||
PathFinderInfo->position++;
|
||||
// TODO: problems!
|
||||
if (CmdFailed(cost)) {
|
||||
DEBUG(ai, 0, "[BuildPath] bridge could not be built (0x%X, 0x%X)", route[part], route[part - 1]);
|
||||
return 0;
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
// Build normal rail
|
||||
// Keep it doing till we go an other way
|
||||
if (route_extra[part - 1] == 0 && route_extra[part] == 0) {
|
||||
while (route_extra[part] == 0) {
|
||||
// Get the current direction
|
||||
dir = AiNew_GetRailDirection(route[part-1], route[part], route[part+1]);
|
||||
// Is it the same as the last one?
|
||||
if (old_dir != -1 && old_dir != dir) break;
|
||||
old_dir = dir;
|
||||
// Build the tile
|
||||
res = AI_DoCommand(route[part], 0, dir, flag, CMD_BUILD_SINGLE_RAIL);
|
||||
if (CmdFailed(res)) {
|
||||
// Problem.. let's just abort it all!
|
||||
p->ainew.state = AI_STATE_NOTHING;
|
||||
return 0;
|
||||
}
|
||||
cost += res;
|
||||
// Go to the next tile
|
||||
part++;
|
||||
// Check if it is still in range..
|
||||
if (part >= PathFinderInfo->route_length - 1) break;
|
||||
}
|
||||
part--;
|
||||
}
|
||||
// We want to return the last position, so we go back one
|
||||
PathFinderInfo->position = part;
|
||||
} else {
|
||||
// Tunnel code
|
||||
if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) {
|
||||
cost += AI_DoCommand(route[part], 0x200, 0, flag, CMD_BUILD_TUNNEL);
|
||||
PathFinderInfo->position++;
|
||||
// TODO: problems!
|
||||
if (CmdFailed(cost)) {
|
||||
DEBUG(ai, 0, "[BuildPath] tunnel could not be built (0x%X)", route[part]);
|
||||
return 0;
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
// Bridge code
|
||||
if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) {
|
||||
cost += AiNew_Build_Bridge(p, route[part], route[part+1], flag);
|
||||
PathFinderInfo->position++;
|
||||
// TODO: problems!
|
||||
if (CmdFailed(cost)) {
|
||||
DEBUG(ai, 0, "[BuildPath] bridge could not be built (0x%X, 0x%X)", route[part], route[part + 1]);
|
||||
return 0;
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
// Build normal road
|
||||
// Keep it doing till we go an other way
|
||||
// EnsureNoVehicle makes sure we don't build on a tile where a vehicle is. This way
|
||||
// it will wait till the vehicle is gone..
|
||||
if (route_extra[part-1] == 0 && route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicle(route[part]))) {
|
||||
while (route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicle(route[part]))) {
|
||||
// Get the current direction
|
||||
dir = AiNew_GetRoadDirection(route[part-1], route[part], route[part+1]);
|
||||
// Is it the same as the last one?
|
||||
if (old_dir != -1 && old_dir != dir) break;
|
||||
old_dir = dir;
|
||||
// There is already some road, and it is a bridge.. don't build!!!
|
||||
if (!IsTileType(route[part], MP_TUNNELBRIDGE)) {
|
||||
// Build the tile
|
||||
res = AI_DoCommand(route[part], dir, 0, flag | DC_NO_WATER, CMD_BUILD_ROAD);
|
||||
// Currently, we ignore CMD_ERRORs!
|
||||
if (CmdFailed(res) && flag == DC_EXEC && !IsTileType(route[part], MP_STREET) && !EnsureNoVehicle(route[part])) {
|
||||
// Problem.. let's just abort it all!
|
||||
DEBUG(ai, 0, "[BuidPath] route building failed at tile 0x%X, aborting", route[part]);
|
||||
p->ainew.state = AI_STATE_NOTHING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!CmdFailed(res)) cost += res;
|
||||
}
|
||||
// Go to the next tile
|
||||
part++;
|
||||
// Check if it is still in range..
|
||||
if (part >= PathFinderInfo->route_length - 1) break;
|
||||
}
|
||||
part--;
|
||||
// We want to return the last position, so we go back one
|
||||
}
|
||||
if (!EnsureNoVehicle(route[part]) && flag == DC_EXEC) part--;
|
||||
PathFinderInfo->position = part;
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
|
||||
// This functions tries to find the best vehicle for this type of cargo
|
||||
// It returns INVALID_ENGINE if not suitable engine is found
|
||||
EngineID AiNew_PickVehicle(Player *p)
|
||||
{
|
||||
if (p->ainew.tbt == AI_TRAIN) {
|
||||
// Not supported yet
|
||||
return INVALID_ENGINE;
|
||||
} else {
|
||||
EngineID best_veh_index = INVALID_ENGINE;
|
||||
int32 best_veh_rating = 0;
|
||||
EngineID start = ROAD_ENGINES_INDEX;
|
||||
EngineID end = ROAD_ENGINES_INDEX + NUM_ROAD_ENGINES;
|
||||
EngineID i;
|
||||
|
||||
/* Loop through all road vehicles */
|
||||
for (i = start; i != end; i++) {
|
||||
const RoadVehicleInfo *rvi = RoadVehInfo(i);
|
||||
const Engine* e = GetEngine(i);
|
||||
int32 rating;
|
||||
int32 ret;
|
||||
|
||||
/* Skip vehicles which can't take our cargo type */
|
||||
if (rvi->cargo_type != p->ainew.cargo && !CanRefitTo(i, p->ainew.cargo)) continue;
|
||||
|
||||
// Is it availiable?
|
||||
// Also, check if the reliability of the vehicle is above the AI_VEHICLE_MIN_RELIABILTY
|
||||
if (!HASBIT(e->player_avail, _current_player) || e->reliability * 100 < AI_VEHICLE_MIN_RELIABILTY << 16) continue;
|
||||
|
||||
/* Rate and compare the engine by speed & capacity */
|
||||
rating = rvi->max_speed * rvi->capacity;
|
||||
if (rating <= best_veh_rating) continue;
|
||||
|
||||
// Can we build it?
|
||||
ret = AI_DoCommand(0, i, 0, DC_QUERY_COST, CMD_BUILD_ROAD_VEH);
|
||||
if (CmdFailed(ret)) continue;
|
||||
|
||||
best_veh_rating = rating;
|
||||
best_veh_index = i;
|
||||
}
|
||||
|
||||
return best_veh_index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2)
|
||||
{
|
||||
Player* p = GetPlayer(_current_player);
|
||||
|
||||
if (success) {
|
||||
p->ainew.state = AI_STATE_GIVE_ORDERS;
|
||||
p->ainew.veh_id = _new_vehicle_id;
|
||||
|
||||
if (GetVehicle(p->ainew.veh_id)->cargo_type != p->ainew.cargo) {
|
||||
/* Cargo type doesn't match, so refit it */
|
||||
if (CmdFailed(DoCommand(tile, p->ainew.veh_id, p->ainew.cargo, DC_EXEC, CMD_REFIT_ROAD_VEH))) {
|
||||
/* Refit failed, so sell the vehicle */
|
||||
DoCommand(tile, p->ainew.veh_id, 0, DC_EXEC, CMD_SELL_ROAD_VEH);
|
||||
p->ainew.state = AI_STATE_NOTHING;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* XXX this should be handled more gracefully */
|
||||
p->ainew.state = AI_STATE_NOTHING;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Builds the best vehicle possible
|
||||
int AiNew_Build_Vehicle(Player *p, TileIndex tile, byte flag)
|
||||
{
|
||||
EngineID i = AiNew_PickVehicle(p);
|
||||
|
||||
if (i == INVALID_ENGINE) return CMD_ERROR;
|
||||
if (p->ainew.tbt == AI_TRAIN) return CMD_ERROR;
|
||||
|
||||
if (flag & DC_EXEC) {
|
||||
return AI_DoCommandCc(tile, i, 0, flag, CMD_BUILD_ROAD_VEH, CcAI);
|
||||
} else {
|
||||
return AI_DoCommand(tile, i, 0, flag, CMD_BUILD_ROAD_VEH);
|
||||
}
|
||||
}
|
||||
|
||||
int AiNew_Build_Depot(Player* p, TileIndex tile, DiagDirection direction, byte flag)
|
||||
{
|
||||
int ret, ret2;
|
||||
if (p->ainew.tbt == AI_TRAIN) {
|
||||
return AI_DoCommand(tile, 0, direction, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_TRAIN_DEPOT);
|
||||
} else {
|
||||
ret = AI_DoCommand(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_DEPOT);
|
||||
if (CmdFailed(ret)) return ret;
|
||||
// Try to build the road from the depot
|
||||
ret2 = AI_DoCommand(tile + TileOffsByDiagDir(direction), DiagDirToRoadBits(ReverseDiagDir(direction)), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
|
||||
// If it fails, ignore it..
|
||||
if (CmdFailed(ret2)) return ret;
|
||||
return ret + ret2;
|
||||
}
|
||||
}
|
||||
510
src/ai/trolly/pathfinder.c
Normal file
510
src/ai/trolly/pathfinder.c
Normal file
@@ -0,0 +1,510 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../openttd.h"
|
||||
#include "../../bridge_map.h"
|
||||
#include "../../debug.h"
|
||||
#include "../../functions.h"
|
||||
#include "../../map.h"
|
||||
#include "../../tile.h"
|
||||
#include "../../command.h"
|
||||
#include "trolly.h"
|
||||
#include "../../depot.h"
|
||||
#include "../../tunnel_map.h"
|
||||
#include "../../bridge.h"
|
||||
#include "../ai.h"
|
||||
|
||||
#define TEST_STATION_NO_DIR 0xFF
|
||||
|
||||
// Tests if a station can be build on the given spot
|
||||
// TODO: make it train compatible
|
||||
static bool TestCanBuildStationHere(TileIndex tile, byte dir)
|
||||
{
|
||||
Player *p = GetPlayer(_current_player);
|
||||
|
||||
if (dir == TEST_STATION_NO_DIR) {
|
||||
int32 ret;
|
||||
// TODO: currently we only allow spots that can be access from al 4 directions...
|
||||
// should be fixed!!!
|
||||
for (dir = 0; dir < 4; dir++) {
|
||||
ret = AiNew_Build_Station(p, p->ainew.tbt, tile, 1, 1, dir, DC_QUERY_COST);
|
||||
if (!CmdFailed(ret)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if command succeeded, so the inverse of CmdFailed()
|
||||
return !CmdFailed(AiNew_Build_Station(p, p->ainew.tbt, tile, 1, 1, dir, DC_QUERY_COST));
|
||||
}
|
||||
|
||||
|
||||
static bool IsRoad(TileIndex tile)
|
||||
{
|
||||
return
|
||||
// MP_STREET, but not a road depot?
|
||||
(IsTileType(tile, MP_STREET) && !IsTileDepotType(tile, TRANSPORT_ROAD)) ||
|
||||
(IsTileType(tile, MP_TUNNELBRIDGE) && (
|
||||
(IsTunnel(tile) && GetTunnelTransportType(tile) == TRANSPORT_ROAD) ||
|
||||
(IsBridge(tile) && GetBridgeTransportType(tile) == TRANSPORT_ROAD)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
// Checks if a tile 'a' is between the tiles 'b' and 'c'
|
||||
#define TILES_BETWEEN(a, b, c) (TileX(a) >= TileX(b) && TileX(a) <= TileX(c) && TileY(a) >= TileY(b) && TileY(a) <= TileY(c))
|
||||
|
||||
|
||||
// Check if the current tile is in our end-area
|
||||
static int32 AyStar_AiPathFinder_EndNodeCheck(AyStar *aystar, OpenListNode *current)
|
||||
{
|
||||
const Ai_PathFinderInfo* PathFinderInfo = aystar->user_target;
|
||||
|
||||
// It is not allowed to have a station on the end of a bridge or tunnel ;)
|
||||
if (current->path.node.user_data[0] != 0) return AYSTAR_DONE;
|
||||
if (TILES_BETWEEN(current->path.node.tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br))
|
||||
if (IsTileType(current->path.node.tile, MP_CLEAR) || IsTileType(current->path.node.tile, MP_TREES))
|
||||
if (current->path.parent == NULL || TestCanBuildStationHere(current->path.node.tile, AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile)))
|
||||
return AYSTAR_FOUND_END_NODE;
|
||||
|
||||
return AYSTAR_DONE;
|
||||
}
|
||||
|
||||
|
||||
// Calculates the hash
|
||||
// Currently it is a 10 bit hash, so the hash array has a max depth of 6 bits (so 64)
|
||||
static uint AiPathFinder_Hash(uint key1, uint key2)
|
||||
{
|
||||
return (TileX(key1) & 0x1F) + ((TileY(key1) & 0x1F) << 5);
|
||||
}
|
||||
|
||||
|
||||
// Clear the memory of all the things
|
||||
static void AyStar_AiPathFinder_Free(AyStar *aystar)
|
||||
{
|
||||
AyStarMain_Free(aystar);
|
||||
free(aystar);
|
||||
}
|
||||
|
||||
|
||||
static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
|
||||
static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
|
||||
static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current);
|
||||
static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current);
|
||||
|
||||
|
||||
// This creates the AiPathFinder
|
||||
AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo)
|
||||
{
|
||||
PathNode start_node;
|
||||
uint x;
|
||||
uint y;
|
||||
// Create AyStar
|
||||
AyStar *result = malloc(sizeof(AyStar));
|
||||
init_AyStar(result, AiPathFinder_Hash, 1 << 10);
|
||||
// Set the function pointers
|
||||
result->CalculateG = AyStar_AiPathFinder_CalculateG;
|
||||
result->CalculateH = AyStar_AiPathFinder_CalculateH;
|
||||
result->EndNodeCheck = AyStar_AiPathFinder_EndNodeCheck;
|
||||
result->FoundEndNode = AyStar_AiPathFinder_FoundEndNode;
|
||||
result->GetNeighbours = AyStar_AiPathFinder_GetNeighbours;
|
||||
|
||||
result->free = AyStar_AiPathFinder_Free;
|
||||
|
||||
// Set some information
|
||||
result->loops_per_tick = AI_PATHFINDER_LOOPS_PER_TICK;
|
||||
result->max_path_cost = 0;
|
||||
result->max_search_nodes = AI_PATHFINDER_MAX_SEARCH_NODES;
|
||||
|
||||
// Set the user_data to the PathFinderInfo
|
||||
result->user_target = PathFinderInfo;
|
||||
|
||||
// Set the start node
|
||||
start_node.parent = NULL;
|
||||
start_node.node.direction = 0;
|
||||
start_node.node.user_data[0] = 0;
|
||||
|
||||
// Now we add all the starting tiles
|
||||
for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) {
|
||||
for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) {
|
||||
start_node.node.tile = TileXY(x, y);
|
||||
result->addstart(result, &start_node.node, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// To reuse AyStar we sometimes have to clean all the memory
|
||||
void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo)
|
||||
{
|
||||
PathNode start_node;
|
||||
uint x;
|
||||
uint y;
|
||||
|
||||
aystar->clear(aystar);
|
||||
|
||||
// Set the user_data to the PathFinderInfo
|
||||
aystar->user_target = PathFinderInfo;
|
||||
|
||||
// Set the start node
|
||||
start_node.parent = NULL;
|
||||
start_node.node.direction = 0;
|
||||
start_node.node.user_data[0] = 0;
|
||||
start_node.node.tile = PathFinderInfo->start_tile_tl;
|
||||
|
||||
// Now we add all the starting tiles
|
||||
for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) {
|
||||
for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) {
|
||||
TileIndex tile = TileXY(x, y);
|
||||
|
||||
if (!IsTileType(tile, MP_CLEAR) && !IsTileType(tile, MP_TREES)) continue;
|
||||
if (!TestCanBuildStationHere(tile, TEST_STATION_NO_DIR)) continue;
|
||||
start_node.node.tile = tile;
|
||||
aystar->addstart(aystar, &start_node.node, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The h-value, simple calculation
|
||||
static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent)
|
||||
{
|
||||
const Ai_PathFinderInfo* PathFinderInfo = aystar->user_target;
|
||||
int r, r2;
|
||||
|
||||
if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION) {
|
||||
// The station is pointing to a direction, add a tile towards that direction, so the H-value is more accurate
|
||||
r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl + TileOffsByDiagDir(PathFinderInfo->end_direction));
|
||||
r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br + TileOffsByDiagDir(PathFinderInfo->end_direction));
|
||||
} else {
|
||||
// No direction, so just get the fastest route to the station
|
||||
r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl);
|
||||
r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br);
|
||||
}
|
||||
// See if the bottomright is faster than the topleft..
|
||||
if (r2 < r) r = r2;
|
||||
return r * AI_PATHFINDER_H_MULTIPLER;
|
||||
}
|
||||
|
||||
|
||||
// We found the end.. let's get the route back and put it in an array
|
||||
static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current)
|
||||
{
|
||||
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
|
||||
uint i = 0;
|
||||
PathNode *parent = ¤t->path;
|
||||
|
||||
do {
|
||||
PathFinderInfo->route_extra[i] = parent->node.user_data[0];
|
||||
PathFinderInfo->route[i++] = parent->node.tile;
|
||||
if (i > lengthof(PathFinderInfo->route)) {
|
||||
// We ran out of space for the PathFinder
|
||||
DEBUG(ai, 0, "No more space in pathfinder route[] array");
|
||||
PathFinderInfo->route_length = -1; // -1 indicates out of space
|
||||
return;
|
||||
}
|
||||
parent = parent->parent;
|
||||
} while (parent != NULL);
|
||||
PathFinderInfo->route_length = i;
|
||||
DEBUG(ai, 1, "Found route of %d nodes long in %d nodes of searching", i, Hash_Size(&aystar->ClosedListHash));
|
||||
}
|
||||
|
||||
|
||||
// What tiles are around us.
|
||||
static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current)
|
||||
{
|
||||
uint i;
|
||||
int ret;
|
||||
int dir;
|
||||
|
||||
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
|
||||
|
||||
aystar->num_neighbours = 0;
|
||||
|
||||
// Go through all surrounding tiles and check if they are within the limits
|
||||
for (i = 0; i < 4; i++) {
|
||||
TileIndex ctile = current->path.node.tile; // Current tile
|
||||
TileIndex atile = ctile + TileOffsByDiagDir(i); // Adjacent tile
|
||||
|
||||
if (TileX(atile) > 1 && TileX(atile) < MapMaxX() - 1 &&
|
||||
TileY(atile) > 1 && TileY(atile) < MapMaxY() - 1) {
|
||||
// We also directly test if the current tile can connect to this tile..
|
||||
// We do this simply by just building the tile!
|
||||
|
||||
// If the next step is a bridge, we have to enter it the right way
|
||||
if (!PathFinderInfo->rail_or_road && IsRoad(atile)) {
|
||||
if (IsTileType(atile, MP_TUNNELBRIDGE)) {
|
||||
if (IsTunnel(atile)) {
|
||||
if (GetTunnelDirection(atile) != i) continue;
|
||||
} else {
|
||||
if ((_m[atile].m5 & 1U) != DiagDirToAxis(i)) continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// But also if we are on a bridge, we can only move a certain direction
|
||||
if (!PathFinderInfo->rail_or_road && IsRoad(ctile)) {
|
||||
if (IsTileType(ctile, MP_TUNNELBRIDGE)) {
|
||||
// An existing bridge/tunnel... let's test the direction ;)
|
||||
if ((_m[ctile].m5 & 1U) != (i & 1)) continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ((AI_PATHFINDER_FLAG_BRIDGE & current->path.node.user_data[0]) != 0 ||
|
||||
(AI_PATHFINDER_FLAG_TUNNEL & current->path.node.user_data[0]) != 0) {
|
||||
// We are a bridge/tunnel, how cool!!
|
||||
// This means we can only point forward.. get the direction from the user_data
|
||||
if (i != (current->path.node.user_data[0] >> 8)) continue;
|
||||
}
|
||||
dir = 0;
|
||||
|
||||
// First, check if we have a parent
|
||||
if (current->path.parent == NULL && current->path.node.user_data[0] == 0) {
|
||||
// If not, this means we are at the starting station
|
||||
if (PathFinderInfo->start_direction != AI_PATHFINDER_NO_DIRECTION) {
|
||||
// We do need a direction?
|
||||
if (AiNew_GetDirection(ctile, atile) != PathFinderInfo->start_direction) {
|
||||
// We are not pointing the right way, invalid tile
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (current->path.node.user_data[0] == 0) {
|
||||
if (PathFinderInfo->rail_or_road) {
|
||||
// Rail check
|
||||
dir = AiNew_GetRailDirection(current->path.parent->node.tile, ctile, atile);
|
||||
ret = AI_DoCommand(ctile, 0, dir, DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL);
|
||||
if (CmdFailed(ret)) continue;
|
||||
#ifdef AI_PATHFINDER_NO_90DEGREES_TURN
|
||||
if (current->path.parent->parent != NULL) {
|
||||
// Check if we don't make a 90degree curve
|
||||
int dir1 = AiNew_GetRailDirection(current->path.parent->parent->node.tile, current->path.parent->node.tile, ctile);
|
||||
if (_illegal_curves[dir1] == dir || _illegal_curves[dir] == dir1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
// Road check
|
||||
dir = AiNew_GetRoadDirection(current->path.parent->node.tile, ctile, atile);
|
||||
if (IsRoad(ctile)) {
|
||||
if (IsTileType(ctile, MP_TUNNELBRIDGE)) {
|
||||
// We have a bridge, how nicely! We should mark it...
|
||||
dir = 0;
|
||||
} else {
|
||||
// It already has road.. check if we miss any bits!
|
||||
if ((_m[ctile].m5 & dir) != dir) {
|
||||
// We do miss some pieces :(
|
||||
dir &= ~_m[ctile].m5;
|
||||
} else {
|
||||
dir = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only destruct things if it is MP_CLEAR of MP_TREES
|
||||
if (dir != 0) {
|
||||
ret = AI_DoCommand(ctile, dir, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
|
||||
if (CmdFailed(ret)) continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The tile can be connected
|
||||
aystar->neighbours[aystar->num_neighbours].tile = atile;
|
||||
aystar->neighbours[aystar->num_neighbours].user_data[0] = 0;
|
||||
aystar->neighbours[aystar->num_neighbours++].direction = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Next step, check for bridges and tunnels
|
||||
if (current->path.parent != NULL && current->path.node.user_data[0] == 0) {
|
||||
// First we get the dir from this tile and his parent
|
||||
DiagDirection dir = AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile);
|
||||
// It means we can only walk with the track, so the bridge has to be in the same direction
|
||||
TileIndex tile = current->path.node.tile;
|
||||
TileIndex new_tile = tile;
|
||||
Slope tileh = GetTileSlope(tile, NULL);
|
||||
|
||||
// Bridges can only be build on land that is not flat
|
||||
// And if there is a road or rail blocking
|
||||
if (tileh != SLOPE_FLAT ||
|
||||
(PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDiagDir(dir), MP_STREET)) ||
|
||||
(!PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDiagDir(dir), MP_RAILWAY))) {
|
||||
for (;;) {
|
||||
new_tile += TileOffsByDiagDir(dir);
|
||||
|
||||
// Precheck, is the length allowed?
|
||||
if (!CheckBridge_Stuff(0, GetBridgeLength(tile, new_tile))) break;
|
||||
|
||||
// Check if we hit the station-tile.. we don't like that!
|
||||
if (TILES_BETWEEN(new_tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) break;
|
||||
|
||||
// Try building the bridge..
|
||||
ret = AI_DoCommand(tile, new_tile, (0 << 8) + (MAX_BRIDGES / 2), DC_AUTO, CMD_BUILD_BRIDGE);
|
||||
if (CmdFailed(ret)) continue;
|
||||
// We can build a bridge here.. add him to the neighbours
|
||||
aystar->neighbours[aystar->num_neighbours].tile = new_tile;
|
||||
aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_BRIDGE + (dir << 8);
|
||||
aystar->neighbours[aystar->num_neighbours++].direction = 0;
|
||||
// We can only have 12 neighbours, and we need 1 left for tunnels
|
||||
if (aystar->num_neighbours == 11) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Next, check for tunnels!
|
||||
// Tunnels can only be built on slopes corresponding to the direction
|
||||
// For now, we check both sides for this tile.. terraforming gives fuzzy result
|
||||
if ((dir == DIAGDIR_NE && tileh == SLOPE_NE) ||
|
||||
(dir == DIAGDIR_SE && tileh == SLOPE_SE) ||
|
||||
(dir == DIAGDIR_SW && tileh == SLOPE_SW) ||
|
||||
(dir == DIAGDIR_NW && tileh == SLOPE_NW)) {
|
||||
// Now simply check if a tunnel can be build
|
||||
ret = AI_DoCommand(tile, (PathFinderInfo->rail_or_road?0:0x200), 0, DC_AUTO, CMD_BUILD_TUNNEL);
|
||||
tileh = GetTileSlope(_build_tunnel_endtile, NULL);
|
||||
if (!CmdFailed(ret) && (tileh == SLOPE_SW || tileh == SLOPE_SE || tileh == SLOPE_NW || tileh == SLOPE_NE)) {
|
||||
aystar->neighbours[aystar->num_neighbours].tile = _build_tunnel_endtile;
|
||||
aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_TUNNEL + (dir << 8);
|
||||
aystar->neighbours[aystar->num_neighbours++].direction = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern uint GetRailFoundation(Slope tileh, TrackBits bits); // XXX function declaration in .c
|
||||
extern uint GetRoadFoundation(Slope tileh, uint bits); // XXX function declaration in .c
|
||||
extern uint GetBridgeFoundation(Slope tileh, Axis); // XXX function declaration in .c
|
||||
enum {
|
||||
BRIDGE_NO_FOUNDATION = 1 << 0 | 1 << 3 | 1 << 6 | 1 << 9 | 1 << 12,
|
||||
};
|
||||
|
||||
// The most important function: it calculates the g-value
|
||||
static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent)
|
||||
{
|
||||
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
|
||||
int r, res = 0;
|
||||
Slope tileh = GetTileSlope(current->tile, NULL);
|
||||
Slope parent_tileh = GetTileSlope(parent->path.node.tile, NULL);
|
||||
|
||||
// Check if we hit the end-tile
|
||||
if (TILES_BETWEEN(current->tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) {
|
||||
// We are at the end-tile, check if we had a direction or something...
|
||||
if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION && AiNew_GetDirection(current->tile, parent->path.node.tile) != PathFinderInfo->end_direction) {
|
||||
// We are not pointing the right way, invalid tile
|
||||
return AYSTAR_INVALID_NODE;
|
||||
}
|
||||
// If it was valid, drop out.. we don't build on the endtile
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Give everything a small penalty
|
||||
res += AI_PATHFINDER_PENALTY;
|
||||
|
||||
if (!PathFinderInfo->rail_or_road) {
|
||||
// Road has the lovely advantage it can use other road... check if
|
||||
// the current tile is road, and if so, give a good bonus
|
||||
if (IsRoad(current->tile)) {
|
||||
res -= AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS;
|
||||
}
|
||||
}
|
||||
|
||||
// We should give a penalty when the tile is going up or down.. this is one way to do so!
|
||||
// Too bad we have to count it from the parent.. but that is not so bad.
|
||||
// We also dislike long routes on slopes, since they do not look too realistic
|
||||
// when there is a flat land all around, they are more expensive to build, and
|
||||
// especially they essentially block the ability to connect or cross the road
|
||||
// from one side.
|
||||
if (parent_tileh != SLOPE_FLAT && parent->path.parent != NULL) {
|
||||
// Skip if the tile was from a bridge or tunnel
|
||||
if (parent->path.node.user_data[0] == 0 && current->user_data[0] == 0) {
|
||||
if (PathFinderInfo->rail_or_road) {
|
||||
r = GetRailFoundation(parent_tileh, 1 << AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
|
||||
// Maybe is BRIDGE_NO_FOUNDATION a bit strange here, but it contains just the right information..
|
||||
if (r >= 15 || (r == 0 && HASBIT(BRIDGE_NO_FOUNDATION, tileh))) {
|
||||
res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
|
||||
} else {
|
||||
res += AI_PATHFINDER_FOUNDATION_PENALTY;
|
||||
}
|
||||
} else {
|
||||
if (!IsRoad(parent->path.node.tile) || !IsTileType(parent->path.node.tile, MP_TUNNELBRIDGE)) {
|
||||
r = GetRoadFoundation(parent_tileh, AiNew_GetRoadDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
|
||||
if (r >= 15 || r == 0) {
|
||||
res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
|
||||
} else {
|
||||
res += AI_PATHFINDER_FOUNDATION_PENALTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Are we part of a tunnel?
|
||||
if ((AI_PATHFINDER_FLAG_TUNNEL & current->user_data[0]) != 0) {
|
||||
// Tunnels are very expensive when build on long routes..
|
||||
// Ironicly, we are using BridgeCode here ;)
|
||||
r = AI_PATHFINDER_TUNNEL_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile);
|
||||
res += r + (r >> 8);
|
||||
}
|
||||
|
||||
// Are we part of a bridge?
|
||||
if ((AI_PATHFINDER_FLAG_BRIDGE & current->user_data[0]) != 0) {
|
||||
// That means for every length a penalty
|
||||
res += AI_PATHFINDER_BRIDGE_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile);
|
||||
// Check if we are going up or down, first for the starting point
|
||||
// In user_data[0] is at the 8th bit the direction
|
||||
if (!HASBIT(BRIDGE_NO_FOUNDATION, parent_tileh)) {
|
||||
if (GetBridgeFoundation(parent_tileh, (current->user_data[0] >> 8) & 1) < 15) {
|
||||
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
|
||||
}
|
||||
}
|
||||
// Second for the end point
|
||||
if (!HASBIT(BRIDGE_NO_FOUNDATION, tileh)) {
|
||||
if (GetBridgeFoundation(tileh, (current->user_data[0] >> 8) & 1) < 15) {
|
||||
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
|
||||
}
|
||||
}
|
||||
if (parent_tileh == SLOPE_FLAT) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
|
||||
if (tileh == SLOPE_FLAT) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
|
||||
}
|
||||
|
||||
// To prevent the AI from taking the fastest way in tiles, but not the fastest way
|
||||
// in speed, we have to give a good penalty to direction changing
|
||||
// This way, we get almost the fastest way in tiles, and a very good speed on the track
|
||||
if (!PathFinderInfo->rail_or_road) {
|
||||
if (parent->path.parent != NULL &&
|
||||
AiNew_GetDirection(current->tile, parent->path.node.tile) != AiNew_GetDirection(parent->path.node.tile, parent->path.parent->node.tile)) {
|
||||
// When road exists, we don't like turning, but its free, so don't be to piggy about it
|
||||
if (IsRoad(parent->path.node.tile)) {
|
||||
res += AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY;
|
||||
} else {
|
||||
res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For rail we have 1 exeption: diagonal rail..
|
||||
// So we fetch 2 raildirection. That of the current one, and of the one before that
|
||||
if (parent->path.parent != NULL && parent->path.parent->parent != NULL) {
|
||||
int dir1 = AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile);
|
||||
int dir2 = AiNew_GetRailDirection(parent->path.parent->parent->node.tile, parent->path.parent->node.tile, parent->path.node.tile);
|
||||
// First, see if we are on diagonal path, that is better than straight path
|
||||
if (dir1 > 1) res -= AI_PATHFINDER_DIAGONAL_BONUS;
|
||||
|
||||
// First see if they are different
|
||||
if (dir1 != dir2) {
|
||||
// dir 2 and 3 are 1 diagonal track, and 4 and 5.
|
||||
if (!(((dir1 == 2 || dir1 == 3) && (dir2 == 2 || dir2 == 3)) || ((dir1 == 4 || dir1 == 5) && (dir2 == 4 || dir2 == 5)))) {
|
||||
// It is not, so we changed of direction
|
||||
res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
|
||||
}
|
||||
if (parent->path.parent->parent->parent != NULL) {
|
||||
int dir3 = AiNew_GetRailDirection(parent->path.parent->parent->parent->node.tile, parent->path.parent->parent->node.tile, parent->path.parent->node.tile);
|
||||
// Check if we changed 3 tiles of direction in 3 tiles.. bad!!!
|
||||
if ((dir1 == 0 || dir1 == 1) && dir2 > 1 && (dir3 == 0 || dir3 == 1)) {
|
||||
res += AI_PATHFINDER_CURVE_PENALTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (res < 0) ? 0 : res;
|
||||
}
|
||||
118
src/ai/trolly/shared.c
Normal file
118
src/ai/trolly/shared.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../openttd.h"
|
||||
#include "../../debug.h"
|
||||
#include "../../map.h"
|
||||
#include "trolly.h"
|
||||
#include "../../vehicle.h"
|
||||
|
||||
int AiNew_GetRailDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c)
|
||||
{
|
||||
// 0 = vert
|
||||
// 1 = horz
|
||||
// 2 = dig up-left
|
||||
// 3 = dig down-right
|
||||
// 4 = dig down-left
|
||||
// 5 = dig up-right
|
||||
|
||||
uint x1 = TileX(tile_a);
|
||||
uint x2 = TileX(tile_b);
|
||||
uint x3 = TileX(tile_c);
|
||||
|
||||
uint y1 = TileY(tile_a);
|
||||
uint y2 = TileY(tile_b);
|
||||
uint y3 = TileY(tile_c);
|
||||
|
||||
if (y1 == y2 && y2 == y3) return 0;
|
||||
if (x1 == x2 && x2 == x3) return 1;
|
||||
if (y2 > y1) return x2 > x3 ? 2 : 4;
|
||||
if (x2 > x1) return y2 > y3 ? 2 : 5;
|
||||
if (y1 > y2) return x2 > x3 ? 5 : 3;
|
||||
if (x1 > x2) return y2 > y3 ? 4 : 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AiNew_GetRoadDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c)
|
||||
{
|
||||
int x1, x2, x3;
|
||||
int y1, y2, y3;
|
||||
int r;
|
||||
|
||||
x1 = TileX(tile_a);
|
||||
x2 = TileX(tile_b);
|
||||
x3 = TileX(tile_c);
|
||||
|
||||
y1 = TileY(tile_a);
|
||||
y2 = TileY(tile_b);
|
||||
y3 = TileY(tile_c);
|
||||
|
||||
r = 0;
|
||||
|
||||
if (x1 < x2) r += 8;
|
||||
if (y1 < y2) r += 1;
|
||||
if (x1 > x2) r += 2;
|
||||
if (y1 > y2) r += 4;
|
||||
|
||||
if (x2 < x3) r += 2;
|
||||
if (y2 < y3) r += 4;
|
||||
if (x2 > x3) r += 8;
|
||||
if (y2 > y3) r += 1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// Get's the direction between 2 tiles seen from tile_a
|
||||
DiagDirection AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b)
|
||||
{
|
||||
if (TileY(tile_a) < TileY(tile_b)) return DIAGDIR_SE;
|
||||
if (TileY(tile_a) > TileY(tile_b)) return DIAGDIR_NW;
|
||||
if (TileX(tile_a) < TileX(tile_b)) return DIAGDIR_SW;
|
||||
return DIAGDIR_NE;
|
||||
}
|
||||
|
||||
|
||||
// This functions looks up if this vehicle is special for this AI
|
||||
// and returns his flag
|
||||
uint AiNew_GetSpecialVehicleFlag(Player* p, Vehicle* v)
|
||||
{
|
||||
uint i;
|
||||
|
||||
for (i = 0; i < AI_MAX_SPECIAL_VEHICLES; i++) {
|
||||
if (p->ainew.special_vehicles[i].veh_id == v->index) {
|
||||
return p->ainew.special_vehicles[i].flag;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found :(
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool AiNew_SetSpecialVehicleFlag(Player* p, Vehicle* v, uint flag)
|
||||
{
|
||||
int new_id = -1;
|
||||
uint i;
|
||||
|
||||
for (i = 0; i < AI_MAX_SPECIAL_VEHICLES; i++) {
|
||||
if (p->ainew.special_vehicles[i].veh_id == v->index) {
|
||||
p->ainew.special_vehicles[i].flag |= flag;
|
||||
return true;
|
||||
}
|
||||
if (new_id == -1 &&
|
||||
p->ainew.special_vehicles[i].veh_id == 0 &&
|
||||
p->ainew.special_vehicles[i].flag == 0) {
|
||||
new_id = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Out of special_vehicle spots :s
|
||||
if (new_id == -1) {
|
||||
DEBUG(ai, 1, "special_vehicles list is too small");
|
||||
return false;
|
||||
}
|
||||
p->ainew.special_vehicles[new_id].veh_id = v->index;
|
||||
p->ainew.special_vehicles[new_id].flag = flag;
|
||||
return true;
|
||||
}
|
||||
1353
src/ai/trolly/trolly.c
Normal file
1353
src/ai/trolly/trolly.c
Normal file
File diff suppressed because it is too large
Load Diff
262
src/ai/trolly/trolly.h
Normal file
262
src/ai/trolly/trolly.h
Normal file
@@ -0,0 +1,262 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef AI_TROLLY_H
|
||||
#define AI_TROLLY_H
|
||||
|
||||
#include "../../aystar.h"
|
||||
#include "../../player.h"
|
||||
|
||||
/*
|
||||
* These defines can be altered to change the behavoir of the AI
|
||||
*
|
||||
* WARNING:
|
||||
* This can also alter the AI in a negative way. I will never claim these settings
|
||||
* are perfect, but don't change them if you don't know what the effect is.
|
||||
*/
|
||||
|
||||
// How many times it the H multiplied. The higher, the more it will go straight to the
|
||||
// end point. The lower, how more it will find the route with the lowest cost.
|
||||
// also: the lower, the longer it takes before route is calculated..
|
||||
#define AI_PATHFINDER_H_MULTIPLER 100
|
||||
|
||||
// How many loops may AyStar do before it stops
|
||||
// 0 = infinite
|
||||
#define AI_PATHFINDER_LOOPS_PER_TICK 5
|
||||
|
||||
// How long may the AI search for one route?
|
||||
// 0 = infinite
|
||||
// This number is the number of tiles tested.
|
||||
// It takes (AI_PATHFINDER_MAX_SEARCH_NODES / AI_PATHFINDER_LOOPS_PER_TICK) ticks
|
||||
// to get here.. with 5000 / 10 = 500. 500 / 74 (one day) = 8 days till it aborts
|
||||
// (that is: if the AI is on VERY FAST! :p
|
||||
#define AI_PATHFINDER_MAX_SEARCH_NODES 5000
|
||||
|
||||
// If you enable this, the AI is not allowed to make 90degree turns
|
||||
#define AI_PATHFINDER_NO_90DEGREES_TURN
|
||||
|
||||
// Below are defines for the g-calculation
|
||||
|
||||
// Standard penalty given to a tile
|
||||
#define AI_PATHFINDER_PENALTY 150
|
||||
// The penalty given to a tile that is going up
|
||||
#define AI_PATHFINDER_TILE_GOES_UP_PENALTY 450
|
||||
// The penalty given to a tile which would have to use fundation
|
||||
#define AI_PATHFINDER_FOUNDATION_PENALTY 100
|
||||
// Changing direction is a penalty, to prevent curved ways (with that: slow ways)
|
||||
#define AI_PATHFINDER_DIRECTION_CHANGE_PENALTY 200
|
||||
// Same penalty, only for when road already exists
|
||||
#define AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY 50
|
||||
// A diagonal track cost the same as a straigh, but a diagonal is faster... so give
|
||||
// a bonus for using diagonal track
|
||||
#ifdef AI_PATHFINDER_NO_90DEGREES_TURN
|
||||
#define AI_PATHFINDER_DIAGONAL_BONUS 95
|
||||
#else
|
||||
#define AI_PATHFINDER_DIAGONAL_BONUS 75
|
||||
#endif
|
||||
// If a roadblock already exists, it gets a bonus
|
||||
#define AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS 140
|
||||
// To prevent 3 direction changes in 3 tiles, this penalty is given in such situation
|
||||
#define AI_PATHFINDER_CURVE_PENALTY 200
|
||||
|
||||
// Penalty a bridge gets per length
|
||||
#define AI_PATHFINDER_BRIDGE_PENALTY 180
|
||||
// The penalty for a bridge going up
|
||||
#define AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY 1000
|
||||
|
||||
// Tunnels are expensive...
|
||||
// Because of that, every tile the cost is increased with 1/8th of his value
|
||||
// This is also true if you are building a tunnel yourself
|
||||
#define AI_PATHFINDER_TUNNEL_PENALTY 350
|
||||
|
||||
/*
|
||||
* Ai_New defines
|
||||
*/
|
||||
|
||||
// How long may we search cities and industry for a new route?
|
||||
#define AI_LOCATE_ROUTE_MAX_COUNTER 200
|
||||
|
||||
// How many days must there be between building the first station and the second station
|
||||
// within one city. This number is in days and should be more than 4 months.
|
||||
#define AI_CHECKCITY_DATE_BETWEEN 180
|
||||
|
||||
// How many cargo is needed for one station in a city?
|
||||
#define AI_CHECKCITY_CARGO_PER_STATION 60
|
||||
// How much cargo must there not be used in a city before we can build a new station?
|
||||
#define AI_CHECKCITY_NEEDED_CARGO 50
|
||||
// When there is already a station which takes the same good and the rating of that
|
||||
// city is higher then this numer, we are not going to attempt to build anything
|
||||
// there
|
||||
#define AI_CHECKCITY_CARGO_RATING 50
|
||||
// But, there is a chance of 1 out of this number, that we do ;)
|
||||
#define AI_CHECKCITY_CARGO_RATING_CHANCE 5
|
||||
// If a city is too small to contain a station, there is a small chance
|
||||
// that we still do so.. just to make the city bigger!
|
||||
#define AI_CHECKCITY_CITY_CHANCE 5
|
||||
|
||||
// This number indicates for every unit of cargo, how many tiles two stations maybe be away
|
||||
// from eachother. In other words: if we have 120 units of cargo in one station, and 120 units
|
||||
// of the cargo in the other station, both stations can be 96 units away from eachother, if the
|
||||
// next number is 0.4.
|
||||
#define AI_LOCATEROUTE_BUS_CARGO_DISTANCE 0.4
|
||||
#define AI_LOCATEROUTE_TRUCK_CARGO_DISTANCE 0.7
|
||||
// In whole tiles, the minimum distance for a truck route
|
||||
#define AI_LOCATEROUTE_TRUCK_MIN_DISTANCE 30
|
||||
|
||||
// The amount of tiles in a square from -X to +X that is scanned for a station spot
|
||||
// (so if this number is 10, 20x20 = 400 tiles are scanned for _the_ perfect spot
|
||||
// Safe values are between 15 and 5
|
||||
#define AI_FINDSTATION_TILE_RANGE 10
|
||||
|
||||
// Building on normal speed goes very fast. Idle this amount of ticks between every
|
||||
// building part. It is calculated like this: (4 - competitor_speed) * num + 1
|
||||
// where competitor_speed is between 0 (very slow) to 4 (very fast)
|
||||
#define AI_BUILDPATH_PAUSE 10
|
||||
|
||||
// Minimum % of reliabilty a vehicle has to have before the AI buys it
|
||||
#define AI_VEHICLE_MIN_RELIABILTY 60
|
||||
|
||||
// The minimum amount of money a player should always have
|
||||
#define AI_MINIMUM_MONEY 15000
|
||||
|
||||
// If the most cheap route is build, how much is it going to cost..
|
||||
// This is to prevent the AI from trying to build a route which can not be paid for
|
||||
#define AI_MINIMUM_BUS_ROUTE_MONEY 25000
|
||||
#define AI_MINIMUM_TRUCK_ROUTE_MONEY 35000
|
||||
|
||||
// The minimum amount of money before we are going to repay any money
|
||||
#define AI_MINIMUM_LOAN_REPAY_MONEY 40000
|
||||
// How many repays do we do if we have enough money to do so?
|
||||
// Every repay is 10000
|
||||
#define AI_LOAN_REPAY 2
|
||||
// How much income must we have before paying back a loan? Month-based (and looked at the last month)
|
||||
#define AI_MINIMUM_INCOME_FOR_LOAN 7000
|
||||
|
||||
// If there is <num> time as much cargo in the station then the vehicle can handle
|
||||
// reuse the station instead of building a new one!
|
||||
#define AI_STATION_REUSE_MULTIPLER 2
|
||||
|
||||
// No more than this amount of vehicles per station..
|
||||
#define AI_CHECK_MAX_VEHICLE_PER_STATION 10
|
||||
|
||||
// How many thick between building 2 vehicles
|
||||
#define AI_BUILD_VEHICLE_TIME_BETWEEN DAY_TICKS
|
||||
|
||||
// How many days must there between vehicle checks
|
||||
// The more often, the less non-money-making lines there will be
|
||||
// but the unfair it may seem to a human player
|
||||
#define AI_DAYS_BETWEEN_VEHICLE_CHECKS 30
|
||||
|
||||
// How money profit does a vehicle needs to make to stay in order
|
||||
// This is the profit of this year + profit of last year
|
||||
// But also for vehicles that are just one year old. In other words:
|
||||
// Vehicles of 2 years do easier meet this setting then vehicles
|
||||
// of one year. This is a very good thing. New vehicles are filtered,
|
||||
// while old vehicles stay longer, because we do get less in return.
|
||||
#define AI_MINIMUM_ROUTE_PROFIT 1000
|
||||
|
||||
// A vehicle is considered lost when he his cargo is more than 180 days old
|
||||
#define AI_VEHICLE_LOST_DAYS 180
|
||||
|
||||
// How many times may the AI try to find a route before it gives up
|
||||
#define AI_MAX_TRIES_FOR_SAME_ROUTE 8
|
||||
|
||||
/*
|
||||
* End of defines
|
||||
*/
|
||||
|
||||
// This stops 90degrees curves
|
||||
static const byte _illegal_curves[6] = {
|
||||
255, 255, // Horz and vert, don't have the effect
|
||||
5, // upleft and upright are not valid
|
||||
4, // downright and downleft are not valid
|
||||
2, // downleft and upleft are not valid
|
||||
3, // upright and downright are not valid
|
||||
};
|
||||
|
||||
enum {
|
||||
AI_STATE_STARTUP = 0,
|
||||
AI_STATE_FIRST_TIME,
|
||||
AI_STATE_NOTHING,
|
||||
AI_STATE_WAKE_UP,
|
||||
AI_STATE_LOCATE_ROUTE,
|
||||
AI_STATE_FIND_STATION,
|
||||
AI_STATE_FIND_PATH,
|
||||
AI_STATE_FIND_DEPOT,
|
||||
AI_STATE_VERIFY_ROUTE,
|
||||
AI_STATE_BUILD_STATION,
|
||||
AI_STATE_BUILD_PATH,
|
||||
AI_STATE_BUILD_DEPOT,
|
||||
AI_STATE_BUILD_VEHICLE,
|
||||
AI_STATE_WAIT_FOR_BUILD,
|
||||
AI_STATE_GIVE_ORDERS,
|
||||
AI_STATE_START_VEHICLE,
|
||||
AI_STATE_REPAY_MONEY,
|
||||
AI_STATE_CHECK_ALL_VEHICLES,
|
||||
AI_STATE_ACTION_DONE,
|
||||
AI_STATE_STOP, // Temporary function to stop the AI
|
||||
};
|
||||
|
||||
// Used for tbt (train/bus/truck)
|
||||
enum {
|
||||
AI_TRAIN = 0,
|
||||
AI_BUS,
|
||||
AI_TRUCK,
|
||||
};
|
||||
|
||||
enum {
|
||||
AI_ACTION_NONE = 0,
|
||||
AI_ACTION_BUS_ROUTE,
|
||||
AI_ACTION_TRUCK_ROUTE,
|
||||
AI_ACTION_REPAY_LOAN,
|
||||
AI_ACTION_CHECK_ALL_VEHICLES,
|
||||
};
|
||||
|
||||
// Used for from_type/to_type
|
||||
enum {
|
||||
AI_NO_TYPE = 0,
|
||||
AI_CITY,
|
||||
AI_INDUSTRY,
|
||||
};
|
||||
|
||||
// Flags for in the vehicle
|
||||
enum {
|
||||
AI_VEHICLEFLAG_SELL = 1,
|
||||
// Remember, flags must be in power of 2
|
||||
};
|
||||
|
||||
#define AI_NO_CARGO 0xFF // Means that there is no cargo defined yet (used for industry)
|
||||
#define AI_NEED_CARGO 0xFE // Used when the AI needs to find out a cargo for the route
|
||||
#define AI_STATION_RANGE TileXY(MapMaxX(), MapMaxY())
|
||||
|
||||
#define AI_PATHFINDER_NO_DIRECTION (byte)-1
|
||||
|
||||
// Flags used in user_data
|
||||
#define AI_PATHFINDER_FLAG_BRIDGE 1
|
||||
#define AI_PATHFINDER_FLAG_TUNNEL 2
|
||||
|
||||
typedef void AiNew_StateFunction(Player *p);
|
||||
|
||||
// ai_new.c
|
||||
void AiNewDoGameLoop(Player *p);
|
||||
|
||||
// ai_pathfinder.c
|
||||
AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo);
|
||||
void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo);
|
||||
|
||||
// ai_shared.c
|
||||
int AiNew_GetRailDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c);
|
||||
int AiNew_GetRoadDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c);
|
||||
DiagDirection AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b);
|
||||
bool AiNew_SetSpecialVehicleFlag(Player *p, Vehicle *v, uint flag);
|
||||
uint AiNew_GetSpecialVehicleFlag(Player *p, Vehicle *v);
|
||||
|
||||
// ai_build.c
|
||||
bool AiNew_Build_CompanyHQ(Player *p, TileIndex tile);
|
||||
int AiNew_Build_Station(Player *p, byte type, TileIndex tile, byte length, byte numtracks, byte direction, byte flag);
|
||||
int AiNew_Build_Bridge(Player *p, TileIndex tile_a, TileIndex tile_b, byte flag);
|
||||
int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte flag);
|
||||
EngineID AiNew_PickVehicle(Player *p);
|
||||
int AiNew_Build_Vehicle(Player *p, TileIndex tile, byte flag);
|
||||
int AiNew_Build_Depot(Player* p, TileIndex tile, DiagDirection direction, byte flag);
|
||||
|
||||
#endif /* AI_TROLLY_H */
|
||||
26
src/aircraft.h
Normal file
26
src/aircraft.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef AIRCRAFT_H
|
||||
#define AIRCRAFT_H
|
||||
|
||||
#include "station_map.h"
|
||||
#include "vehicle.h"
|
||||
|
||||
|
||||
static inline bool IsAircraftInHangar(const Vehicle* v)
|
||||
{
|
||||
assert(v->type == VEH_Aircraft);
|
||||
return v->vehstatus & VS_HIDDEN && IsHangarTile(v->tile);
|
||||
}
|
||||
|
||||
static inline bool IsAircraftInHangarStopped(const Vehicle* v)
|
||||
{
|
||||
return IsAircraftInHangar(v) && v->vehstatus & VS_STOPPED;
|
||||
}
|
||||
|
||||
uint16 AircraftDefaultCargoCapacity(CargoID cid, EngineID engine_type);
|
||||
|
||||
void CcCloneAircraft(bool success, TileIndex tile, uint32 p1, uint32 p2);
|
||||
void HandleAircraftEnterHangar(Vehicle *v);
|
||||
|
||||
#endif /* AIRCRAFT_H */
|
||||
2120
src/aircraft_cmd.c
Normal file
2120
src/aircraft_cmd.c
Normal file
File diff suppressed because it is too large
Load Diff
350
src/aircraft_gui.c
Normal file
350
src/aircraft_gui.c
Normal file
@@ -0,0 +1,350 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "aircraft.h"
|
||||
#include "debug.h"
|
||||
#include "functions.h"
|
||||
#include "table/sprites.h"
|
||||
#include "table/strings.h"
|
||||
#include "map.h"
|
||||
#include "window.h"
|
||||
#include "gui.h"
|
||||
#include "vehicle.h"
|
||||
#include "gfx.h"
|
||||
#include "command.h"
|
||||
#include "engine.h"
|
||||
#include "viewport.h"
|
||||
#include "player.h"
|
||||
#include "depot.h"
|
||||
#include "vehicle_gui.h"
|
||||
#include "newgrf_engine.h"
|
||||
|
||||
|
||||
void CcCloneAircraft(bool success, TileIndex tile, uint32 p1, uint32 p2)
|
||||
{
|
||||
if (success) ShowAircraftViewWindow(GetVehicle(_new_vehicle_id));
|
||||
}
|
||||
|
||||
static void AircraftDetailsWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_PAINT: {
|
||||
const Vehicle *v = GetVehicle(w->window_number);
|
||||
|
||||
SetWindowWidgetDisabledState(w, 2, v->owner != _local_player);
|
||||
|
||||
/* Disable service-scroller when interval is set to disabled */
|
||||
SetWindowWidgetDisabledState(w, 5, !_patches.servint_aircraft);
|
||||
SetWindowWidgetDisabledState(w, 6, !_patches.servint_aircraft);
|
||||
|
||||
SetDParam(0, v->string_id);
|
||||
SetDParam(1, v->unitnumber);
|
||||
DrawWindowWidgets(w);
|
||||
|
||||
/* Draw running cost */
|
||||
{
|
||||
int year = v->age / 366;
|
||||
|
||||
SetDParam(1, year);
|
||||
|
||||
SetDParam(0, (v->age + 365 < v->max_age) ? STR_AGE : STR_AGE_RED);
|
||||
SetDParam(2, v->max_age / 366);
|
||||
SetDParam(3, _price.aircraft_running * AircraftVehInfo(v->engine_type)->running_cost >> 8);
|
||||
DrawString(2, 15, STR_A00D_AGE_RUNNING_COST_YR, 0);
|
||||
}
|
||||
|
||||
/* Draw max speed */
|
||||
{
|
||||
SetDParam(0, v->max_speed * 128 / 10);
|
||||
DrawString(2, 25, STR_A00E_MAX_SPEED, 0);
|
||||
}
|
||||
|
||||
/* Draw profit */
|
||||
{
|
||||
SetDParam(0, v->profit_this_year);
|
||||
SetDParam(1, v->profit_last_year);
|
||||
DrawString(2, 35, STR_A00F_PROFIT_THIS_YEAR_LAST_YEAR, 0);
|
||||
}
|
||||
|
||||
/* Draw breakdown & reliability */
|
||||
{
|
||||
SetDParam(0, v->reliability * 100 >> 16);
|
||||
SetDParam(1, v->breakdowns_since_last_service);
|
||||
DrawString(2, 45, STR_A010_RELIABILITY_BREAKDOWNS, 0);
|
||||
}
|
||||
|
||||
/* Draw service interval text */
|
||||
{
|
||||
SetDParam(0, v->service_interval);
|
||||
SetDParam(1, v->date_of_last_service);
|
||||
DrawString(13, 103, _patches.servint_ispercent?STR_SERVICING_INTERVAL_PERCENT:STR_883C_SERVICING_INTERVAL_DAYS, 0);
|
||||
}
|
||||
|
||||
DrawAircraftImage(v, 3, 57, INVALID_VEHICLE);
|
||||
|
||||
{
|
||||
const Vehicle *u;
|
||||
int y = 57;
|
||||
|
||||
do {
|
||||
if (v->subtype <= 2) {
|
||||
SetDParam(0, GetCustomEngineName(v->engine_type));
|
||||
SetDParam(1, v->build_year);
|
||||
SetDParam(2, v->value);
|
||||
DrawString(60, y, STR_A011_BUILT_VALUE, 0);
|
||||
y += 10;
|
||||
|
||||
SetDParam(0, v->cargo_type);
|
||||
SetDParam(1, v->cargo_cap);
|
||||
u = v->next;
|
||||
SetDParam(2, u->cargo_type);
|
||||
SetDParam(3, u->cargo_cap);
|
||||
DrawString(60, y, (u->cargo_cap != 0) ? STR_A019_CAPACITY : STR_A01A_CAPACITY, 0);
|
||||
y += 14;
|
||||
}
|
||||
|
||||
if (v->cargo_count != 0) {
|
||||
|
||||
/* Cargo names (fix pluralness) */
|
||||
SetDParam(0, v->cargo_type);
|
||||
SetDParam(1, v->cargo_count);
|
||||
SetDParam(2, v->cargo_source);
|
||||
DrawString(60, y, STR_8813_FROM, 0);
|
||||
|
||||
y += 10;
|
||||
}
|
||||
} while ( (v=v->next) != NULL);
|
||||
}
|
||||
} break;
|
||||
|
||||
case WE_CLICK: {
|
||||
int mod;
|
||||
const Vehicle *v;
|
||||
switch (e->we.click.widget) {
|
||||
case 2: /* rename */
|
||||
v = GetVehicle(w->window_number);
|
||||
SetDParam(0, v->unitnumber);
|
||||
ShowQueryString(v->string_id, STR_A030_NAME_AIRCRAFT, 31, 150, w, CS_ALPHANUMERAL);
|
||||
break;
|
||||
case 5: /* increase int */
|
||||
mod = _ctrl_pressed? 5 : 10;
|
||||
goto do_change_service_int;
|
||||
case 6: /* decrease int */
|
||||
mod = _ctrl_pressed?- 5 : -10;
|
||||
do_change_service_int:
|
||||
v = GetVehicle(w->window_number);
|
||||
|
||||
mod = GetServiceIntervalClamped(mod + v->service_interval);
|
||||
if (mod == v->service_interval) return;
|
||||
|
||||
DoCommandP(v->tile, v->index, mod, NULL, CMD_CHANGE_SERVICE_INT | CMD_MSG(STR_018A_CAN_T_CHANGE_SERVICING));
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case WE_ON_EDIT_TEXT:
|
||||
if (e->we.edittext.str[0] != '\0') {
|
||||
_cmd_text = e->we.edittext.str;
|
||||
DoCommandP(0, w->window_number, 0, NULL,
|
||||
CMD_NAME_VEHICLE | CMD_MSG(STR_A031_CAN_T_NAME_AIRCRAFT));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const Widget _aircraft_details_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW },
|
||||
{ WWT_CAPTION, RESIZE_NONE, 14, 11, 349, 0, 13, STR_A00C_DETAILS, STR_018C_WINDOW_TITLE_DRAG_THIS },
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 350, 389, 0, 13, STR_01AA_NAME, STR_A032_NAME_AIRCRAFT },
|
||||
{ WWT_PANEL, RESIZE_NONE, 14, 0, 389, 14, 55, 0x0, STR_NULL },
|
||||
{ WWT_PANEL, RESIZE_NONE, 14, 0, 389, 56, 101, 0x0, STR_NULL },
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 10, 102, 107, STR_0188, STR_884D_INCREASE_SERVICING_INTERVAL },
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 10, 108, 113, STR_0189, STR_884E_DECREASE_SERVICING_INTERVAL },
|
||||
{ WWT_PANEL, RESIZE_NONE, 14, 11, 389, 102, 113, 0x0, STR_NULL },
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const WindowDesc _aircraft_details_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 390, 114,
|
||||
WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
|
||||
_aircraft_details_widgets,
|
||||
AircraftDetailsWndProc
|
||||
};
|
||||
|
||||
|
||||
static void ShowAircraftDetailsWindow(const Vehicle *v)
|
||||
{
|
||||
Window *w;
|
||||
VehicleID veh = v->index;
|
||||
|
||||
DeleteWindowById(WC_VEHICLE_ORDERS, veh);
|
||||
DeleteWindowById(WC_VEHICLE_DETAILS, veh);
|
||||
|
||||
w = AllocateWindowDescFront(&_aircraft_details_desc, veh);
|
||||
w->caption_color = v->owner;
|
||||
// w->vscroll.cap = 6;
|
||||
// w->traindetails_d.tab = 0;
|
||||
}
|
||||
|
||||
|
||||
static const Widget _aircraft_view_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW },
|
||||
{ WWT_CAPTION, RESIZE_RIGHT, 14, 11, 237, 0, 13, STR_A00A, STR_018C_WINDOW_TITLE_DRAG_THIS },
|
||||
{ WWT_STICKYBOX, RESIZE_LR, 14, 238, 249, 0, 13, 0x0, STR_STICKY_BUTTON },
|
||||
{ WWT_PANEL, RESIZE_RB, 14, 0, 231, 14, 103, 0x0, STR_NULL },
|
||||
{ WWT_INSET, RESIZE_RB, 14, 2, 229, 16, 101, 0x0, STR_NULL },
|
||||
{ WWT_PUSHBTN, RESIZE_RTB, 14, 0, 237, 104, 115, 0x0, STR_A027_CURRENT_AIRCRAFT_ACTION },
|
||||
{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 14, 31, SPR_CENTRE_VIEW_VEHICLE, STR_A029_CENTER_MAIN_VIEW_ON_AIRCRAFT },
|
||||
{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 32, 49, SPR_SEND_AIRCRAFT_TODEPOT,STR_A02A_SEND_AIRCRAFT_TO_HANGAR },
|
||||
{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 50, 67, SPR_REFIT_VEHICLE, STR_A03B_REFIT_AIRCRAFT_TO_CARRY },
|
||||
{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 68, 85, SPR_SHOW_ORDERS, STR_A028_SHOW_AIRCRAFT_S_ORDERS },
|
||||
{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 86, 103, SPR_SHOW_VEHICLE_DETAILS, STR_A02B_SHOW_AIRCRAFT_DETAILS },
|
||||
{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 32, 49, SPR_CLONE_AIRCRAFT, STR_CLONE_AIRCRAFT_INFO },
|
||||
{ WWT_PANEL, RESIZE_LRB, 14, 232, 249, 104, 103, 0x0, STR_NULL },
|
||||
{ WWT_RESIZEBOX, RESIZE_LRTB, 14, 238, 249, 104, 115, 0x0, STR_NULL },
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
|
||||
static void AircraftViewWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_PAINT: {
|
||||
const Vehicle *v = GetVehicle(w->window_number);
|
||||
StringID str;
|
||||
bool is_localplayer = v->owner == _local_player;
|
||||
|
||||
SetWindowWidgetDisabledState(w, 7, !is_localplayer);
|
||||
SetWindowWidgetDisabledState(w, 8, !IsAircraftInHangarStopped(v) || !is_localplayer);
|
||||
SetWindowWidgetDisabledState(w, 11, !is_localplayer);
|
||||
|
||||
|
||||
/* draw widgets & caption */
|
||||
SetDParam(0, v->string_id);
|
||||
SetDParam(1, v->unitnumber);
|
||||
DrawWindowWidgets(w);
|
||||
|
||||
if (v->vehstatus & VS_CRASHED) {
|
||||
str = STR_8863_CRASHED;
|
||||
} else if (v->vehstatus & VS_STOPPED) {
|
||||
str = STR_8861_STOPPED;
|
||||
} else {
|
||||
switch (v->current_order.type) {
|
||||
case OT_GOTO_STATION: {
|
||||
SetDParam(0, v->current_order.dest);
|
||||
SetDParam(1, v->cur_speed * 128 / 10);
|
||||
str = STR_HEADING_FOR_STATION + _patches.vehicle_speed;
|
||||
} break;
|
||||
|
||||
case OT_GOTO_DEPOT: {
|
||||
/* Aircrafts always go to a station, even if you say depot */
|
||||
SetDParam(0, v->current_order.dest);
|
||||
SetDParam(1, v->cur_speed * 128 / 10);
|
||||
if (HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT) && !HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) {
|
||||
str = STR_HEADING_FOR_HANGAR + _patches.vehicle_speed;
|
||||
} else {
|
||||
str = STR_HEADING_FOR_HANGAR_SERVICE + _patches.vehicle_speed;
|
||||
}
|
||||
} break;
|
||||
|
||||
case OT_LOADING:
|
||||
str = STR_882F_LOADING_UNLOADING;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (v->num_orders == 0) {
|
||||
str = STR_NO_ORDERS + _patches.vehicle_speed;
|
||||
SetDParam(0, v->cur_speed * 128 / 10);
|
||||
} else {
|
||||
str = STR_EMPTY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* draw the flag plus orders */
|
||||
DrawSprite(v->vehstatus & VS_STOPPED ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, 2, w->widget[5].top + 1);
|
||||
DrawStringCenteredTruncated(w->widget[5].left + 8, w->widget[5].right, w->widget[5].top + 1, str, 0);
|
||||
DrawWindowViewport(w);
|
||||
} break;
|
||||
|
||||
case WE_CLICK: {
|
||||
const Vehicle *v = GetVehicle(w->window_number);
|
||||
|
||||
switch (e->we.click.widget) {
|
||||
case 5: /* start stop */
|
||||
DoCommandP(v->tile, v->index, 0, NULL, CMD_START_STOP_AIRCRAFT | CMD_MSG(STR_A016_CAN_T_STOP_START_AIRCRAFT));
|
||||
break;
|
||||
case 6: /* center main view */
|
||||
ScrollMainWindowTo(v->x_pos, v->y_pos);
|
||||
break;
|
||||
case 7: /* goto hangar */
|
||||
DoCommandP(v->tile, v->index, _ctrl_pressed ? DEPOT_SERVICE : 0, NULL, CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_MSG(STR_A012_CAN_T_SEND_AIRCRAFT_TO));
|
||||
break;
|
||||
case 8: /* refit */
|
||||
ShowVehicleRefitWindow(v, INVALID_VEH_ORDER_ID);
|
||||
break;
|
||||
case 9: /* show orders */
|
||||
ShowOrdersWindow(v);
|
||||
break;
|
||||
case 10: /* show details */
|
||||
ShowAircraftDetailsWindow(v);
|
||||
break;
|
||||
case 11:
|
||||
/* clone vehicle */
|
||||
DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneAircraft, CMD_CLONE_VEHICLE | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT));
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case WE_RESIZE:
|
||||
w->viewport->width += e->we.sizing.diff.x;
|
||||
w->viewport->height += e->we.sizing.diff.y;
|
||||
w->viewport->virtual_width += e->we.sizing.diff.x;
|
||||
w->viewport->virtual_height += e->we.sizing.diff.y;
|
||||
break;
|
||||
|
||||
case WE_DESTROY:
|
||||
DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number);
|
||||
DeleteWindowById(WC_VEHICLE_REFIT, w->window_number);
|
||||
DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number);
|
||||
break;
|
||||
|
||||
case WE_MOUSELOOP: {
|
||||
const Vehicle *v = GetVehicle(w->window_number);
|
||||
bool plane_stopped = IsAircraftInHangarStopped(v);
|
||||
|
||||
/* Widget 7 (send to hangar) must be hidden if the plane is already stopped in hangar.
|
||||
* Widget 11 (clone) should then be shown, since cloning is allowed only while in hangar and stopped.
|
||||
* This sytem allows to have two buttons, on top of each other*/
|
||||
if (plane_stopped != IsWindowWidgetHidden(w, 7) || plane_stopped == IsWindowWidgetHidden(w, 11)) {
|
||||
SetWindowWidgetHiddenState(w, 7, plane_stopped); // send to hangar
|
||||
SetWindowWidgetHiddenState(w, 11, !plane_stopped); // clone
|
||||
SetWindowDirty(w);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const WindowDesc _aircraft_view_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 250, 116,
|
||||
WC_VEHICLE_VIEW ,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
|
||||
_aircraft_view_widgets,
|
||||
AircraftViewWndProc
|
||||
};
|
||||
|
||||
|
||||
void ShowAircraftViewWindow(const Vehicle *v)
|
||||
{
|
||||
Window *w = AllocateWindowDescFront(&_aircraft_view_desc, v->index);
|
||||
|
||||
if (w != NULL) {
|
||||
w->caption_color = v->owner;
|
||||
AssignWindowViewport(w, 3, 17, 0xE2, 0x54, w->window_number | (1 << 31), 0);
|
||||
}
|
||||
}
|
||||
489
src/airport.c
Normal file
489
src/airport.c
Normal file
@@ -0,0 +1,489 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "debug.h"
|
||||
#include "map.h"
|
||||
#include "airport.h"
|
||||
#include "macros.h"
|
||||
#include "variables.h"
|
||||
#include "airport_movement.h"
|
||||
#include "date.h"
|
||||
|
||||
/* Uncomment this to print out a full report of the airport-structure
|
||||
* You should either use
|
||||
* - true: full-report, print out every state and choice with string-names
|
||||
* OR
|
||||
* - false: give a summarized report which only shows current and next position */
|
||||
//#define DEBUG_AIRPORT false
|
||||
|
||||
static AirportFTAClass *CountryAirport;
|
||||
static AirportFTAClass *CityAirport;
|
||||
static AirportFTAClass *Oilrig;
|
||||
static AirportFTAClass *Heliport;
|
||||
static AirportFTAClass *MetropolitanAirport;
|
||||
static AirportFTAClass *InternationalAirport;
|
||||
static AirportFTAClass *CommuterAirport;
|
||||
static AirportFTAClass *HeliDepot;
|
||||
static AirportFTAClass *IntercontinentalAirport;
|
||||
static AirportFTAClass *HeliStation;
|
||||
|
||||
static void AirportFTAClass_Constructor(AirportFTAClass *apc,
|
||||
const byte *terminals, const byte *helipads,
|
||||
const byte entry_point, const byte acc_planes,
|
||||
const AirportFTAbuildup *apFA,
|
||||
const TileIndexDiffC *depots, const byte nof_depots,
|
||||
uint size_x, uint size_y
|
||||
);
|
||||
static void AirportFTAClass_Destructor(AirportFTAClass *apc);
|
||||
|
||||
static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA);
|
||||
static void AirportBuildAutomata(AirportFTAClass *apc, const AirportFTAbuildup *apFA);
|
||||
static byte AirportGetTerminalCount(const byte *terminals, byte *groups);
|
||||
static byte AirportTestFTA(const AirportFTAClass *apc);
|
||||
|
||||
#ifdef DEBUG_AIRPORT
|
||||
static void AirportPrintOut(const AirportFTAClass *apc, bool full_report);
|
||||
#endif /* DEBUG_AIRPORT */
|
||||
|
||||
void InitializeAirports(void)
|
||||
{
|
||||
// country airport
|
||||
CountryAirport = malloc(sizeof(AirportFTAClass));
|
||||
|
||||
AirportFTAClass_Constructor(
|
||||
CountryAirport,
|
||||
_airport_terminal_country,
|
||||
NULL,
|
||||
16,
|
||||
ALL,
|
||||
_airport_fta_country,
|
||||
_airport_depots_country,
|
||||
lengthof(_airport_depots_country),
|
||||
4, 3
|
||||
);
|
||||
|
||||
// city airport
|
||||
CityAirport = malloc(sizeof(AirportFTAClass));
|
||||
|
||||
AirportFTAClass_Constructor(
|
||||
CityAirport,
|
||||
_airport_terminal_city,
|
||||
NULL,
|
||||
19,
|
||||
ALL,
|
||||
_airport_fta_city,
|
||||
_airport_depots_city,
|
||||
lengthof(_airport_depots_city),
|
||||
6, 6
|
||||
);
|
||||
|
||||
// metropolitan airport
|
||||
MetropolitanAirport = malloc(sizeof(AirportFTAClass));
|
||||
|
||||
AirportFTAClass_Constructor(
|
||||
MetropolitanAirport,
|
||||
_airport_terminal_metropolitan,
|
||||
NULL,
|
||||
20,
|
||||
ALL,
|
||||
_airport_fta_metropolitan,
|
||||
_airport_depots_metropolitan,
|
||||
lengthof(_airport_depots_metropolitan),
|
||||
6, 6
|
||||
);
|
||||
|
||||
// international airport
|
||||
InternationalAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));
|
||||
|
||||
AirportFTAClass_Constructor(
|
||||
InternationalAirport,
|
||||
_airport_terminal_international,
|
||||
_airport_helipad_international,
|
||||
37,
|
||||
ALL,
|
||||
_airport_fta_international,
|
||||
_airport_depots_international,
|
||||
lengthof(_airport_depots_international),
|
||||
7, 7
|
||||
);
|
||||
|
||||
// intercontintental airport
|
||||
IntercontinentalAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));
|
||||
|
||||
AirportFTAClass_Constructor(
|
||||
IntercontinentalAirport,
|
||||
_airport_terminal_intercontinental,
|
||||
_airport_helipad_intercontinental,
|
||||
43,
|
||||
ALL,
|
||||
_airport_fta_intercontinental,
|
||||
_airport_depots_intercontinental,
|
||||
lengthof(_airport_depots_intercontinental),
|
||||
9,11
|
||||
);
|
||||
|
||||
// heliport, oilrig
|
||||
Heliport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));
|
||||
|
||||
AirportFTAClass_Constructor(
|
||||
Heliport,
|
||||
NULL,
|
||||
_airport_helipad_heliport_oilrig,
|
||||
7,
|
||||
HELICOPTERS_ONLY,
|
||||
_airport_fta_heliport_oilrig,
|
||||
NULL,
|
||||
0,
|
||||
1, 1
|
||||
);
|
||||
|
||||
Oilrig = Heliport; // exactly the same structure for heliport/oilrig, so share state machine
|
||||
|
||||
// commuter airport
|
||||
CommuterAirport = malloc(sizeof(AirportFTAClass));
|
||||
|
||||
AirportFTAClass_Constructor(
|
||||
CommuterAirport,
|
||||
_airport_terminal_commuter,
|
||||
_airport_helipad_commuter,
|
||||
22,
|
||||
ALL,
|
||||
_airport_fta_commuter,
|
||||
_airport_depots_commuter,
|
||||
lengthof(_airport_depots_commuter),
|
||||
5,4
|
||||
);
|
||||
|
||||
// helidepot airport
|
||||
HeliDepot = malloc(sizeof(AirportFTAClass));
|
||||
|
||||
AirportFTAClass_Constructor(
|
||||
HeliDepot,
|
||||
NULL,
|
||||
_airport_helipad_helidepot,
|
||||
4,
|
||||
HELICOPTERS_ONLY,
|
||||
_airport_fta_helidepot,
|
||||
_airport_depots_helidepot,
|
||||
lengthof(_airport_depots_helidepot),
|
||||
2,2
|
||||
);
|
||||
|
||||
// helistation airport
|
||||
HeliStation = malloc(sizeof(AirportFTAClass));
|
||||
|
||||
AirportFTAClass_Constructor(
|
||||
HeliStation,
|
||||
NULL,
|
||||
_airport_helipad_helistation,
|
||||
25,
|
||||
HELICOPTERS_ONLY,
|
||||
_airport_fta_helistation,
|
||||
_airport_depots_helistation,
|
||||
lengthof(_airport_depots_helistation),
|
||||
4,2
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void UnInitializeAirports(void)
|
||||
{
|
||||
AirportFTAClass_Destructor(CountryAirport);
|
||||
AirportFTAClass_Destructor(CityAirport);
|
||||
AirportFTAClass_Destructor(Heliport);
|
||||
AirportFTAClass_Destructor(MetropolitanAirport);
|
||||
AirportFTAClass_Destructor(InternationalAirport);
|
||||
AirportFTAClass_Destructor(CommuterAirport);
|
||||
AirportFTAClass_Destructor(HeliDepot);
|
||||
AirportFTAClass_Destructor(IntercontinentalAirport);
|
||||
AirportFTAClass_Destructor(HeliStation);
|
||||
}
|
||||
|
||||
static void AirportFTAClass_Constructor(AirportFTAClass *apc,
|
||||
const byte *terminals, const byte *helipads,
|
||||
const byte entry_point, const byte acc_planes,
|
||||
const AirportFTAbuildup *apFA,
|
||||
const TileIndexDiffC *depots, const byte nof_depots,
|
||||
uint size_x, uint size_y
|
||||
)
|
||||
{
|
||||
byte nofterminals, nofhelipads;
|
||||
byte nofterminalgroups, nofhelipadgroups;
|
||||
|
||||
apc->size_x = size_x;
|
||||
apc->size_y = size_y;
|
||||
|
||||
/* Set up the terminal and helipad count for an airport.
|
||||
* TODO: If there are more than 10 terminals or 4 helipads, internal variables
|
||||
* need to be changed, so don't allow that for now */
|
||||
nofterminals = AirportGetTerminalCount(terminals, &nofterminalgroups);
|
||||
if (nofterminals > MAX_TERMINALS) {
|
||||
DEBUG(misc, 0, "[Ap] only a maximum of %d terminals are supported (requested %d)", MAX_TERMINALS, nofterminals);
|
||||
assert(nofterminals <= MAX_TERMINALS);
|
||||
}
|
||||
apc->terminals = terminals;
|
||||
|
||||
nofhelipads = AirportGetTerminalCount(helipads, &nofhelipadgroups);
|
||||
if (nofhelipads > MAX_HELIPADS) {
|
||||
DEBUG(misc, 0, "[Ap] only a maximum of %d helipads are supported (requested %d)", MAX_HELIPADS, nofhelipads);
|
||||
assert(nofhelipads <= MAX_HELIPADS);
|
||||
}
|
||||
apc->helipads = helipads;
|
||||
|
||||
/* Get the number of elements from the source table. We also double check this
|
||||
* with the entry point which must be within bounds and use this information
|
||||
* later on to build and validate the state machine */
|
||||
apc->nofelements = AirportGetNofElements(apFA);
|
||||
if (entry_point >= apc->nofelements) {
|
||||
DEBUG(misc, 0, "[Ap] entry (%d) must be within the airport (maximum %d)", entry_point, apc->nofelements);
|
||||
assert(entry_point < apc->nofelements);
|
||||
}
|
||||
|
||||
apc->acc_planes = acc_planes;
|
||||
apc->entry_point = entry_point;
|
||||
apc->airport_depots = depots;
|
||||
apc->nof_depots = nof_depots;
|
||||
|
||||
/* Build the state machine itself */
|
||||
AirportBuildAutomata(apc, apFA);
|
||||
DEBUG(misc, 2, "[Ap] #count %3d; #term %2d (%dgrp); #helipad %2d (%dgrp); entry %3d",
|
||||
apc->nofelements, nofterminals, nofterminalgroups, nofhelipads, nofhelipadgroups, apc->entry_point);
|
||||
|
||||
/* Test if everything went allright. This is only a rude static test checking
|
||||
* the symantic correctness. By no means does passing the test mean that the
|
||||
* airport is working correctly or will not deadlock for example */
|
||||
{ byte ret = AirportTestFTA(apc);
|
||||
if (ret != MAX_ELEMENTS) DEBUG(misc, 0, "[Ap] problem with element: %d", ret - 1);
|
||||
assert(ret == MAX_ELEMENTS);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_AIRPORT
|
||||
AirportPrintOut(apc, DEBUG_AIRPORT);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void AirportFTAClass_Destructor(AirportFTAClass *apc)
|
||||
{
|
||||
int i;
|
||||
AirportFTA *current, *next;
|
||||
|
||||
for (i = 0; i < apc->nofelements; i++) {
|
||||
current = apc->layout[i].next;
|
||||
while (current != NULL) {
|
||||
next = current->next;
|
||||
free(current);
|
||||
current = next;
|
||||
};
|
||||
}
|
||||
free(apc->layout);
|
||||
free(apc);
|
||||
}
|
||||
|
||||
/** Get the number of elements of a source Airport state automata
|
||||
* Since it is actually just a big array of AirportFTA types, we only
|
||||
* know one element from the other by differing 'position' identifiers */
|
||||
static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA)
|
||||
{
|
||||
int i;
|
||||
uint16 nofelements = 0;
|
||||
int temp = apFA[0].position;
|
||||
|
||||
for (i = 0; i < MAX_ELEMENTS; i++) {
|
||||
if (temp != apFA[i].position) {
|
||||
nofelements++;
|
||||
temp = apFA[i].position;
|
||||
}
|
||||
if (apFA[i].position == MAX_ELEMENTS) break;
|
||||
}
|
||||
return nofelements;
|
||||
}
|
||||
|
||||
/* We calculate the terminal/helipod count based on the data passed to us
|
||||
* This data (terminals) contains an index as a first element as to how many
|
||||
* groups there are, and then the number of terminals for each group */
|
||||
static byte AirportGetTerminalCount(const byte *terminals, byte *groups)
|
||||
{
|
||||
byte i;
|
||||
byte nof_terminals = 0;
|
||||
*groups = 0;
|
||||
|
||||
if (terminals != NULL) {
|
||||
i = terminals[0];
|
||||
*groups = i;
|
||||
while (i-- > 0) {
|
||||
terminals++;
|
||||
assert(*terminals != 0); // no empty groups please
|
||||
nof_terminals += *terminals;
|
||||
}
|
||||
}
|
||||
return nof_terminals;
|
||||
}
|
||||
|
||||
static void AirportBuildAutomata(AirportFTAClass *apc, const AirportFTAbuildup *apFA)
|
||||
{
|
||||
AirportFTA *current;
|
||||
AirportFTA *FAutomata = malloc(sizeof(AirportFTA) * apc->nofelements);
|
||||
uint16 internalcounter = 0;
|
||||
uint16 i;
|
||||
|
||||
apc->layout = FAutomata;
|
||||
for (i = 0; i < apc->nofelements; i++) {
|
||||
current = &apc->layout[i];
|
||||
current->position = apFA[internalcounter].position;
|
||||
current->heading = apFA[internalcounter].heading;
|
||||
current->block = apFA[internalcounter].block;
|
||||
current->next_position = apFA[internalcounter].next;
|
||||
|
||||
// outgoing nodes from the same position, create linked list
|
||||
while (current->position == apFA[internalcounter + 1].position) {
|
||||
AirportFTA *newNode = malloc(sizeof(AirportFTA));
|
||||
|
||||
newNode->position = apFA[internalcounter + 1].position;
|
||||
newNode->heading = apFA[internalcounter + 1].heading;
|
||||
newNode->block = apFA[internalcounter + 1].block;
|
||||
newNode->next_position = apFA[internalcounter + 1].next;
|
||||
// create link
|
||||
current->next = newNode;
|
||||
current = current->next;
|
||||
internalcounter++;
|
||||
} // while
|
||||
current->next = NULL;
|
||||
internalcounter++;
|
||||
}
|
||||
}
|
||||
|
||||
static byte AirportTestFTA(const AirportFTAClass *apc)
|
||||
{
|
||||
byte position, i, next_position;
|
||||
AirportFTA *current, *first;
|
||||
next_position = 0;
|
||||
|
||||
for (i = 0; i < apc->nofelements; i++) {
|
||||
position = apc->layout[i].position;
|
||||
if (position != next_position) return i;
|
||||
current = first = &apc->layout[i];
|
||||
|
||||
for (; current != NULL; current = current->next) {
|
||||
/* A heading must always be valid. The only exceptions are
|
||||
* - multiple choices as start, identified by a special value of 255
|
||||
* - terminal group which is identified by a special value of 255 */
|
||||
if (current->heading > MAX_HEADINGS) {
|
||||
if (current->heading != 255) return i;
|
||||
if (current == first && current->next == NULL) return i;
|
||||
if (current != first && current->next_position > apc->terminals[0]) return i;
|
||||
}
|
||||
|
||||
/* If there is only one choice, it must be at the end */
|
||||
if (current->heading == 0 && current->next != NULL) return i;
|
||||
/* Obviously the elements of the linked list must have the same identifier */
|
||||
if (position != current->position) return i;
|
||||
/* A next position must be within bounds */
|
||||
if (current->next_position >= apc->nofelements) return i;
|
||||
}
|
||||
next_position++;
|
||||
}
|
||||
return MAX_ELEMENTS;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_AIRPORT
|
||||
static const char* const _airport_heading_strings[] = {
|
||||
"TO_ALL",
|
||||
"HANGAR",
|
||||
"TERM1",
|
||||
"TERM2",
|
||||
"TERM3",
|
||||
"TERM4",
|
||||
"TERM5",
|
||||
"TERM6",
|
||||
"HELIPAD1",
|
||||
"HELIPAD2",
|
||||
"TAKEOFF",
|
||||
"STARTTAKEOFF",
|
||||
"ENDTAKEOFF",
|
||||
"HELITAKEOFF",
|
||||
"FLYING",
|
||||
"LANDING",
|
||||
"ENDLANDING",
|
||||
"HELILANDING",
|
||||
"HELIENDLANDING",
|
||||
"TERM7",
|
||||
"TERM8",
|
||||
"HELIPAD3",
|
||||
"HELIPAD4",
|
||||
"DUMMY" // extra heading for 255
|
||||
};
|
||||
|
||||
static uint AirportBlockToString(uint32 block)
|
||||
{
|
||||
uint i = 0;
|
||||
if (block & 0xffff0000) { block >>= 16; i += 16; }
|
||||
if (block & 0x0000ff00) { block >>= 8; i += 8; }
|
||||
if (block & 0x000000f0) { block >>= 4; i += 4; }
|
||||
if (block & 0x0000000c) { block >>= 2; i += 2; }
|
||||
if (block & 0x00000002) { i += 1; }
|
||||
return i;
|
||||
}
|
||||
|
||||
static void AirportPrintOut(const AirportFTAClass *apc, bool full_report)
|
||||
{
|
||||
uint16 i;
|
||||
|
||||
if (!full_report) printf("(P = Current Position; NP = Next Position)\n");
|
||||
|
||||
for (i = 0; i < apc->nofelements; i++) {
|
||||
AirportFTA *current = &apc->layout[i];
|
||||
|
||||
for (; current != NULL; current = current->next) {
|
||||
if (full_report) {
|
||||
byte heading = (current->heading == 255) ? MAX_HEADINGS + 1 : current->heading;
|
||||
printf("\tPos:%2d NPos:%2d Heading:%15s Block:%2d\n", current->position,
|
||||
current->next_position, _airport_heading_strings[heading],
|
||||
AirportBlockToString(current->block));
|
||||
} else {
|
||||
printf("P:%2d NP:%2d", current->position, current->next_position);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const AirportFTAClass *GetAirport(const byte airport_type)
|
||||
{
|
||||
//FIXME -- AircraftNextAirportPos_and_Order -> Needs something nicer, don't like this code
|
||||
// needs constant change if more airports are added
|
||||
switch (airport_type) {
|
||||
default: NOT_REACHED();
|
||||
case AT_SMALL: return CountryAirport;
|
||||
case AT_LARGE: return CityAirport;
|
||||
case AT_METROPOLITAN: return MetropolitanAirport;
|
||||
case AT_HELIPORT: return Heliport;
|
||||
case AT_OILRIG: return Oilrig;
|
||||
case AT_INTERNATIONAL: return InternationalAirport;
|
||||
case AT_COMMUTER: return CommuterAirport;
|
||||
case AT_HELIDEPOT: return HeliDepot;
|
||||
case AT_INTERCON: return IntercontinentalAirport;
|
||||
case AT_HELISTATION: return HeliStation;
|
||||
}
|
||||
}
|
||||
|
||||
const AirportMovingData *GetAirportMovingData(byte airport_type, byte position)
|
||||
{
|
||||
assert(airport_type < lengthof(_airport_moving_datas));
|
||||
assert(position < GetAirport(airport_type)->nofelements);
|
||||
return &_airport_moving_datas[airport_type][position];
|
||||
}
|
||||
|
||||
uint32 GetValidAirports(void)
|
||||
{
|
||||
uint32 bytemask = _avail_aircraft; /// sets the first 3 bytes, 0 - 2, @see AdjustAvailAircraft()
|
||||
|
||||
if (_cur_year >= 1980) SETBIT(bytemask, 3); // metropolitan airport
|
||||
if (_cur_year >= 1990) SETBIT(bytemask, 4); // international airport
|
||||
if (_cur_year >= 1983) SETBIT(bytemask, 5); // commuter airport
|
||||
if (_cur_year >= 1976) SETBIT(bytemask, 6); // helidepot
|
||||
if (_cur_year >= 2002) SETBIT(bytemask, 7); // intercontinental airport
|
||||
if (_cur_year >= 1980) SETBIT(bytemask, 8); // helistation
|
||||
return bytemask;
|
||||
}
|
||||
163
src/airport.h
Normal file
163
src/airport.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef AIRPORT_H
|
||||
#define AIRPORT_H
|
||||
|
||||
enum {MAX_TERMINALS = 10};
|
||||
enum {MAX_HELIPADS = 4};
|
||||
enum {MAX_ELEMENTS = 255};
|
||||
enum {MAX_HEADINGS = 22};
|
||||
|
||||
// Airport types
|
||||
enum {
|
||||
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 = 15
|
||||
};
|
||||
|
||||
// do not change unless you change v->subtype too. This aligns perfectly with its current setting
|
||||
enum {
|
||||
AIRCRAFT_ONLY = 0,
|
||||
ALL = 1,
|
||||
HELICOPTERS_ONLY = 2,
|
||||
};
|
||||
|
||||
enum {
|
||||
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,
|
||||
};
|
||||
|
||||
/* Movement States on Airports (headings target) */
|
||||
enum {
|
||||
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
|
||||
};
|
||||
|
||||
// this maps the terminal to its corresponding state and block flag
|
||||
// currently set for 10 terms, 4 helipads
|
||||
static const byte _airport_terminal_state[] = {2, 3, 4, 5, 6, 7, 19, 20, 0, 0, 8, 9, 21, 22};
|
||||
static const byte _airport_terminal_flag[] = {0, 1, 2, 3, 4, 5, 22, 23, 0, 0, 6, 7, 24, 25};
|
||||
|
||||
/* Movement Blocks on Airports */
|
||||
// blocks (eg_airport_flags)
|
||||
enum {
|
||||
TERM1_block = 1 << 0,
|
||||
TERM2_block = 1 << 1,
|
||||
TERM3_block = 1 << 2,
|
||||
TERM4_block = 1 << 3,
|
||||
TERM5_block = 1 << 4,
|
||||
TERM6_block = 1 << 5,
|
||||
HELIPAD1_block = 1 << 6,
|
||||
HELIPAD2_block = 1 << 7,
|
||||
RUNWAY_IN_OUT_block = 1 << 8,
|
||||
RUNWAY_IN_block = 1 << 8,
|
||||
AIRPORT_BUSY_block = 1 << 8,
|
||||
RUNWAY_OUT_block = 1 << 9,
|
||||
TAXIWAY_BUSY_block = 1 << 10,
|
||||
OUT_WAY_block = 1 << 11,
|
||||
IN_WAY_block = 1 << 12,
|
||||
AIRPORT_ENTRANCE_block = 1 << 13,
|
||||
TERM_GROUP1_block = 1 << 14,
|
||||
TERM_GROUP2_block = 1 << 15,
|
||||
HANGAR2_AREA_block = 1 << 16,
|
||||
TERM_GROUP2_ENTER1_block = 1 << 17,
|
||||
TERM_GROUP2_ENTER2_block = 1 << 18,
|
||||
TERM_GROUP2_EXIT1_block = 1 << 19,
|
||||
TERM_GROUP2_EXIT2_block = 1 << 20,
|
||||
PRE_HELIPAD_block = 1 << 21,
|
||||
|
||||
// blocks for new airports
|
||||
TERM7_block = 1 << 22,
|
||||
TERM8_block = 1 << 23,
|
||||
TERM9_block = 1 << 24,
|
||||
HELIPAD3_block = 1 << 24,
|
||||
TERM10_block = 1 << 25,
|
||||
HELIPAD4_block = 1 << 25,
|
||||
HANGAR1_AREA_block = 1 << 26,
|
||||
OUT_WAY2_block = 1 << 27,
|
||||
IN_WAY2_block = 1 << 28,
|
||||
RUNWAY_IN2_block = 1 << 29,
|
||||
RUNWAY_OUT2_block = 1 << 10, // note re-uses TAXIWAY_BUSY
|
||||
HELIPAD_GROUP_block = 1 << 13, // note re-uses AIRPORT_ENTRANCE
|
||||
OUT_WAY_block2 = 1 << 31,
|
||||
// end of new blocks
|
||||
|
||||
NOTHING_block = 1 << 30
|
||||
};
|
||||
|
||||
typedef struct AirportMovingData {
|
||||
int x,y;
|
||||
byte flag;
|
||||
byte direction;
|
||||
} AirportMovingData;
|
||||
|
||||
// Finite sTate mAchine --> FTA
|
||||
typedef struct AirportFTAClass {
|
||||
byte nofelements; // number of positions the airport consists of
|
||||
const byte *terminals;
|
||||
const byte *helipads;
|
||||
byte entry_point; // when an airplane arrives at this airport, enter it at position entry_point
|
||||
byte acc_planes; // accept airplanes or helicopters or both
|
||||
const TileIndexDiffC *airport_depots; // gives the position of the depots on the airports
|
||||
byte nof_depots; // number of depots this airport has
|
||||
struct AirportFTA *layout; // state machine for airport
|
||||
byte size_x;
|
||||
byte size_y;
|
||||
} AirportFTAClass;
|
||||
|
||||
// internal structure used in openttd - Finite sTate mAchine --> FTA
|
||||
typedef struct AirportFTA {
|
||||
byte position; // the position that an airplane is at
|
||||
byte next_position; // next position from this position
|
||||
uint32 block; // 32 bit blocks (st->airport_flags), should be enough for the most complex airports
|
||||
byte heading; // heading (current orders), guiding an airplane to its target on an airport
|
||||
struct AirportFTA *next; // possible extra movement choices from this position
|
||||
} AirportFTA;
|
||||
|
||||
void InitializeAirports(void);
|
||||
void UnInitializeAirports(void);
|
||||
const AirportFTAClass *GetAirport(const byte airport_type);
|
||||
const AirportMovingData *GetAirportMovingData(byte airport_type, byte position);
|
||||
|
||||
/** Get buildable airport bitmask.
|
||||
* @return get all buildable airports at this given time, bitmasked.
|
||||
* Bit 0 means the small airport is buildable, etc.
|
||||
* @todo set availability of airports by year, instead of airplane
|
||||
*/
|
||||
uint32 GetValidAirports(void);
|
||||
|
||||
#endif /* AIRPORT_H */
|
||||
286
src/airport_gui.c
Normal file
286
src/airport_gui.c
Normal file
@@ -0,0 +1,286 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "table/sprites.h"
|
||||
#include "table/strings.h"
|
||||
#include "functions.h"
|
||||
#include "map.h"
|
||||
#include "window.h"
|
||||
#include "gui.h"
|
||||
#include "viewport.h"
|
||||
#include "gfx.h"
|
||||
#include "sound.h"
|
||||
#include "command.h"
|
||||
#include "vehicle.h"
|
||||
#include "station.h"
|
||||
#include "airport.h"
|
||||
#include "depot.h"
|
||||
|
||||
static byte _selected_airport_type;
|
||||
|
||||
static void ShowBuildAirportPicker(void);
|
||||
|
||||
|
||||
void CcBuildAirport(bool success, TileIndex tile, uint32 p1, uint32 p2)
|
||||
{
|
||||
if (success) {
|
||||
SndPlayTileFx(SND_1F_SPLAT, tile);
|
||||
ResetObjectToPlace();
|
||||
}
|
||||
}
|
||||
|
||||
static void PlaceAirport(TileIndex tile)
|
||||
{
|
||||
DoCommandP(tile, _selected_airport_type, 0, CcBuildAirport, CMD_BUILD_AIRPORT | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_A001_CAN_T_BUILD_AIRPORT_HERE));
|
||||
}
|
||||
|
||||
static void PlaceAir_DemolishArea(TileIndex tile)
|
||||
{
|
||||
VpStartPlaceSizing(tile, 4);
|
||||
}
|
||||
|
||||
|
||||
enum {
|
||||
ATW_AIRPORT = 3,
|
||||
ATW_DEMOLISH = 4
|
||||
};
|
||||
|
||||
|
||||
static void BuildAirClick_Airport(Window *w)
|
||||
{
|
||||
if (HandlePlacePushButton(w, ATW_AIRPORT, SPR_CURSOR_AIRPORT, 1, PlaceAirport)) ShowBuildAirportPicker();
|
||||
}
|
||||
|
||||
static void BuildAirClick_Demolish(Window *w)
|
||||
{
|
||||
HandlePlacePushButton(w, ATW_DEMOLISH, ANIMCURSOR_DEMOLISH, 1, PlaceAir_DemolishArea);
|
||||
}
|
||||
|
||||
static void BuildAirClick_Landscaping(Window *w)
|
||||
{
|
||||
ShowTerraformToolbar();
|
||||
}
|
||||
|
||||
typedef void OnButtonClick(Window *w);
|
||||
static OnButtonClick * const _build_air_button_proc[] = {
|
||||
BuildAirClick_Airport,
|
||||
BuildAirClick_Demolish,
|
||||
BuildAirClick_Landscaping,
|
||||
};
|
||||
|
||||
static void BuildAirToolbWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_PAINT:
|
||||
DrawWindowWidgets(w);
|
||||
break;
|
||||
|
||||
case WE_CLICK:
|
||||
if (e->we.click.widget - 3 >= 0)
|
||||
_build_air_button_proc[e->we.click.widget - 3](w);
|
||||
break;
|
||||
|
||||
case WE_KEYPRESS: {
|
||||
switch (e->we.keypress.keycode) {
|
||||
case '1': BuildAirClick_Airport(w); break;
|
||||
case '2': BuildAirClick_Demolish(w); break;
|
||||
case 'l': BuildAirClick_Landscaping(w); break;
|
||||
default: return;
|
||||
}
|
||||
} break;
|
||||
|
||||
case WE_PLACE_OBJ:
|
||||
_place_proc(e->we.place.tile);
|
||||
break;
|
||||
|
||||
case WE_PLACE_DRAG:
|
||||
VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.userdata);
|
||||
break;
|
||||
|
||||
case WE_PLACE_MOUSEUP:
|
||||
if (e->we.place.pt.x != -1) {
|
||||
DoCommandP(e->we.place.tile, e->we.place.starttile, 0, CcPlaySound10, CMD_CLEAR_AREA | CMD_MSG(STR_00B5_CAN_T_CLEAR_THIS_AREA));
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_ABORT_PLACE_OBJ:
|
||||
RaiseWindowButtons(w);
|
||||
|
||||
w = FindWindowById(WC_BUILD_STATION, 0);
|
||||
if (w != 0)
|
||||
WP(w,def_d).close = true;
|
||||
break;
|
||||
|
||||
case WE_DESTROY:
|
||||
if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const Widget _air_toolbar_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW },
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 73, 0, 13, STR_A000_AIRPORTS, STR_018C_WINDOW_TITLE_DRAG_THIS },
|
||||
{ WWT_STICKYBOX, RESIZE_NONE, 7, 74, 85, 0, 13, 0x0, STR_STICKY_BUTTON },
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 7, 0, 41, 14, 35, SPR_IMG_AIRPORT, STR_A01E_BUILD_AIRPORT },
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 7, 42, 63, 14, 35, SPR_IMG_DYNAMITE, STR_018D_DEMOLISH_BUILDINGS_ETC },
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 7, 64, 85, 14, 35, SPR_IMG_LANDSCAPING, STR_LANDSCAPING_TOOLBAR_TIP },
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
|
||||
static const WindowDesc _air_toolbar_desc = {
|
||||
WDP_ALIGN_TBR, 22, 86, 36,
|
||||
WC_BUILD_TOOLBAR, 0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
|
||||
_air_toolbar_widgets,
|
||||
BuildAirToolbWndProc
|
||||
};
|
||||
|
||||
void ShowBuildAirToolbar(void)
|
||||
{
|
||||
if (!IsValidPlayer(_current_player)) return;
|
||||
|
||||
DeleteWindowById(WC_BUILD_TOOLBAR, 0);
|
||||
AllocateWindowDescFront(&_air_toolbar_desc, 0);
|
||||
if (_patches.link_terraform_toolbar) ShowTerraformToolbar();
|
||||
}
|
||||
|
||||
static void BuildAirportPickerWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_CREATE:
|
||||
SetWindowWidgetLoweredState(w, 16, !_station_show_coverage);
|
||||
SetWindowWidgetLoweredState(w, 17, _station_show_coverage);
|
||||
LowerWindowWidget(w, _selected_airport_type + 7);
|
||||
break;
|
||||
|
||||
case WE_PAINT: {
|
||||
int i; // airport enabling loop
|
||||
int rad = 4; // default catchment radious
|
||||
uint32 avail_airports;
|
||||
const AirportFTAClass *airport;
|
||||
|
||||
if (WP(w,def_d).close) return;
|
||||
|
||||
avail_airports = GetValidAirports();
|
||||
|
||||
RaiseWindowWidget(w, _selected_airport_type + 7);
|
||||
if (!HASBIT(avail_airports, 0) && _selected_airport_type == AT_SMALL) _selected_airport_type = AT_LARGE;
|
||||
if (!HASBIT(avail_airports, 1) && _selected_airport_type == AT_LARGE) _selected_airport_type = AT_SMALL;
|
||||
LowerWindowWidget(w, _selected_airport_type + 7);
|
||||
|
||||
/* 'Country Airport' starts at widget 7, and if its bit is set, it is
|
||||
* available, so take its opposite value to set the disabled state.
|
||||
* There are 9 buildable airports
|
||||
* XXX TODO : all airports should be held in arrays, with all relevant data.
|
||||
* This should be part of newgrf-airports, i suppose
|
||||
*/
|
||||
for (i = 0; i < 9; i++) SetWindowWidgetDisabledState(w, i + 7, !HASBIT(avail_airports, i));
|
||||
|
||||
// select default the coverage area to 'Off' (16)
|
||||
airport = GetAirport(_selected_airport_type);
|
||||
SetTileSelectSize(airport->size_x, airport->size_y);
|
||||
|
||||
if (_patches.modified_catchment) {
|
||||
switch (_selected_airport_type) {
|
||||
case AT_OILRIG: rad = CA_AIR_OILPAD; break;
|
||||
case AT_HELIPORT: rad = CA_AIR_HELIPORT; break;
|
||||
case AT_SMALL: rad = CA_AIR_SMALL; break;
|
||||
case AT_LARGE: rad = CA_AIR_LARGE; break;
|
||||
case AT_METROPOLITAN: rad = CA_AIR_METRO; break;
|
||||
case AT_INTERNATIONAL: rad = CA_AIR_INTER; break;
|
||||
case AT_COMMUTER: rad = CA_AIR_COMMUTER; break;
|
||||
case AT_HELIDEPOT: rad = CA_AIR_HELIDEPOT; break;
|
||||
case AT_INTERCON: rad = CA_AIR_INTERCON; break;
|
||||
case AT_HELISTATION: rad = CA_AIR_HELISTATION; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
|
||||
|
||||
DrawWindowWidgets(w);
|
||||
// strings such as 'Size' and 'Coverage Area'
|
||||
// 'Coverage Area'
|
||||
DrawStationCoverageAreaText(2, 206, (uint)-1, rad);
|
||||
break;
|
||||
}
|
||||
|
||||
case WE_CLICK: {
|
||||
switch (e->we.click.widget) {
|
||||
case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15:
|
||||
RaiseWindowWidget(w, _selected_airport_type + 7);
|
||||
_selected_airport_type = e->we.click.widget - 7;
|
||||
LowerWindowWidget(w, _selected_airport_type + 7);
|
||||
SndPlayFx(SND_15_BEEP);
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
case 16: case 17:
|
||||
_station_show_coverage = e->we.click.widget - 16;
|
||||
SetWindowWidgetLoweredState(w, 16, !_station_show_coverage);
|
||||
SetWindowWidgetLoweredState(w, 17, _station_show_coverage);
|
||||
SndPlayFx(SND_15_BEEP);
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case WE_MOUSELOOP: {
|
||||
if (WP(w,def_d).close) {
|
||||
DeleteWindow(w);
|
||||
return;
|
||||
}
|
||||
|
||||
CheckRedrawStationCoverage(w);
|
||||
} break;
|
||||
|
||||
case WE_DESTROY:
|
||||
if (!WP(w,def_d).close) ResetObjectToPlace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const Widget _build_airport_picker_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 147, 0, 13, STR_3001_AIRPORT_SELECTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 14, 52, 0x0, STR_NULL},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 53, 89, 0x0, STR_NULL},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 90, 127, 0x0, STR_NULL},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 128, 177, 0x0, STR_NULL},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 178, 239, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 27, 38, STR_SMALL_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 65, 76, STR_CITY_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 141, 152, STR_HELIPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 77, 88, STR_METRO_AIRPORT , STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 103, 114, STR_INTERNATIONAL_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 39, 50, STR_COMMUTER_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 165, 176, STR_HELIDEPOT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 115, 126, STR_INTERCONTINENTAL_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 153, 164, STR_HELISTATION, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 14, 73, 191, 202, STR_02DB_OFF, STR_3065_DON_T_HIGHLIGHT_COVERAGE},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 74, 133, 191, 202, STR_02DA_ON, STR_3064_HIGHLIGHT_COVERAGE_AREA},
|
||||
{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 14, 27, STR_SMALL_AIRPORTS, STR_NULL},
|
||||
{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 52, 65, STR_LARGE_AIRPORTS, STR_NULL},
|
||||
{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 90, 103, STR_HUB_AIRPORTS, STR_NULL},
|
||||
{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 128, 141, STR_HELIPORTS, STR_NULL},
|
||||
{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 178, 191, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const WindowDesc _build_airport_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 148, 240,
|
||||
WC_BUILD_STATION, WC_BUILD_TOOLBAR,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_airport_picker_widgets,
|
||||
BuildAirportPickerWndProc
|
||||
};
|
||||
|
||||
static void ShowBuildAirportPicker(void)
|
||||
{
|
||||
AllocateWindowDesc(&_build_airport_desc);
|
||||
}
|
||||
|
||||
void InitializeAirportGui(void)
|
||||
{
|
||||
_selected_airport_type = AT_SMALL;
|
||||
}
|
||||
794
src/airport_movement.h
Normal file
794
src/airport_movement.h
Normal file
@@ -0,0 +1,794 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef AIRPORT_MOVEMENT_H
|
||||
#define AIRPORT_MOVEMENT_H
|
||||
|
||||
|
||||
// state machine input struct (from external file, etc.)
|
||||
// Finite sTate mAchine --> FTA
|
||||
typedef struct AirportFTAbuildup {
|
||||
byte position; // the position that an airplane is at
|
||||
byte heading; // the current orders (eg. TAKEOFF, HANGAR, ENDLANDING, etc.)
|
||||
uint32 block; // the block this position is on on the airport (st->airport_flags)
|
||||
byte next; // next position from this position
|
||||
} AirportFTAbuildup;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
/////*********Movement Positions on Airports********************///////
|
||||
// Country Airfield (small) 4x3
|
||||
static const AirportMovingData _airport_moving_data_country[22] = {
|
||||
{ 53, 3, AMED_EXACTPOS, 3 }, // 00 In Hangar
|
||||
{ 53, 27, 0, 0 }, // 01 Taxi to right outside depot
|
||||
{ 32, 23, AMED_EXACTPOS, 7 }, // 02 Terminal 1
|
||||
{ 10, 23, AMED_EXACTPOS, 7 }, // 03 Terminal 2
|
||||
{ 43, 37, 0, 0 }, // 04 Going towards terminal 2
|
||||
{ 24, 37, 0, 0 }, // 05 Going towards terminal 2
|
||||
{ 53, 37, 0, 0 }, // 06 Going for takeoff
|
||||
{ 61, 40, AMED_EXACTPOS, 1 }, // 07 Taxi to start of runway (takeoff)
|
||||
{ 3, 40, AMED_NOSPDCLAMP, 0 }, // 08 Accelerate to end of runway
|
||||
{ -79, 40, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 09 Take off
|
||||
{ 177, 40, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 10 Fly to landing position in air
|
||||
{ 56, 40, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 11 Going down for land
|
||||
{ 3, 40, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 12 Just landed, brake until end of runway
|
||||
{ 7, 40, 0, 0 }, // 13 Just landed, turn around and taxi 1 square
|
||||
{ 53, 40, 0, 0 }, // 14 Taxi from runway to crossing
|
||||
{ -31, 193, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 15 Fly around waiting for a landing spot (north-east)
|
||||
{ 1, 1, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 16 Fly around waiting for a landing spot (north-west)
|
||||
{ 257, 1, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 17 Fly around waiting for a landing spot (south-west)
|
||||
{ 273, 49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 18 Fly around waiting for a landing spot (south)
|
||||
{ 44, 37, AMED_HELI_RAISE, 0 }, // 19 Helicopter takeoff
|
||||
{ 44, 40, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 20 In position above landing spot helicopter
|
||||
{ 44, 40, AMED_HELI_LOWER, 0 }, // 21 Helicopter landing
|
||||
};
|
||||
|
||||
// Commuter Airfield (small) 5x4
|
||||
static const AirportMovingData _airport_moving_data_commuter[37] = {
|
||||
{ 69, 3, AMED_EXACTPOS, 3 }, // 00 In Hangar
|
||||
{ 72, 22, 0, 0 }, // 01 Taxi to right outside depot
|
||||
{ 8, 22, AMED_EXACTPOS, 5 }, // 01 Taxi to right outside depot
|
||||
{ 24, 36, AMED_EXACTPOS, 3 }, // 03 Terminal 1
|
||||
{ 40, 36, AMED_EXACTPOS, 3 }, // 04 Terminal 2
|
||||
{ 56, 36, AMED_EXACTPOS, 3 }, // 05 Terminal 3
|
||||
{ 40, 8, AMED_EXACTPOS, 1 }, // 06 Helipad 1
|
||||
{ 56, 8, AMED_EXACTPOS, 1 }, // 07 Helipad 2
|
||||
{ 24, 22, 0, 5 }, // 08 Taxiing
|
||||
{ 40, 22, 0, 5 }, // 09 Taxiing
|
||||
{ 56, 22, 0, 5 }, // 10 Taxiing
|
||||
{ 72, 40, 0, 3 }, // 11 Airport OUTWAY
|
||||
{ 72, 54, AMED_EXACTPOS, 1 }, // 12 Accelerate to end of runway
|
||||
{ 7, 54, AMED_NOSPDCLAMP, 0 }, // 13 Release control of runway, for smoother movement
|
||||
{ 5, 54, AMED_NOSPDCLAMP, 0 }, // 14 End of runway
|
||||
{ -79, 54, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 15 Take off
|
||||
{ 145, 54, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 16 Fly to landing position in air
|
||||
{ 73, 54, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 17 Going down for land
|
||||
{ 3, 54, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 18 Just landed, brake until end of runway
|
||||
{ 12, 54, 0, 7 }, // 19 Just landed, turn around and taxi
|
||||
{ 8, 32, 0, 7 }, // 20 Taxi from runway to crossing
|
||||
{ -31, 149, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 21 Fly around waiting for a landing spot (north-east)
|
||||
{ 1, 6, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 22 Fly around waiting for a landing spot (north-west)
|
||||
{ 193, 6, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 23 Fly around waiting for a landing spot (south-west)
|
||||
{ 225, 81, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 24 Fly around waiting for a landing spot (south)
|
||||
// Helicopter
|
||||
{ 80, 0, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 25 Bufferspace before helipad
|
||||
{ 80, 0, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 26 Bufferspace before helipad
|
||||
{ 32, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 27 Get in position for Helipad1
|
||||
{ 48, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 28 Get in position for Helipad2
|
||||
{ 32, 8, AMED_HELI_LOWER, 0 }, // 29 Land at Helipad1
|
||||
{ 48, 8, AMED_HELI_LOWER, 0 }, // 30 Land at Helipad2
|
||||
{ 32, 8, AMED_HELI_RAISE, 0 }, // 31 Takeoff Helipad1
|
||||
{ 48, 8, AMED_HELI_RAISE, 0 }, // 32 Takeoff Helipad2
|
||||
{ 64, 22, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 33 Go to position for Hangarentrance in air
|
||||
{ 64, 22, AMED_HELI_LOWER, 0 }, // 34 Land in front of hangar
|
||||
{ 40, 8, AMED_EXACTPOS, 0 }, // pre-helitakeoff helipad 1
|
||||
{ 56, 8, AMED_EXACTPOS, 0 }, // pre-helitakeoff helipad 2
|
||||
};
|
||||
|
||||
// City Airport (large) 6x6
|
||||
static const AirportMovingData _airport_moving_data_town[25] = {
|
||||
{ 85, 3, AMED_EXACTPOS, 3 }, // 00 In Hangar
|
||||
{ 85, 27, 0, 0 }, // 01 Taxi to right outside depot
|
||||
{ 26, 41, AMED_EXACTPOS, 5 }, // 02 Terminal 1
|
||||
{ 56, 20, AMED_EXACTPOS, 3 }, // 03 Terminal 2
|
||||
{ 38, 8, AMED_EXACTPOS, 5 }, // 04 Terminal 3
|
||||
{ 65, 6, 0, 0 }, // 05 Taxi to right in infront of terminal 2/3
|
||||
{ 80, 27, 0, 0 }, // 06 Taxiway terminals 2-3
|
||||
{ 44, 63, 0, 0 }, // 07 Taxi to Airport center
|
||||
{ 58, 71, 0, 0 }, // 08 Towards takeoff
|
||||
{ 72, 85, 0, 0 }, // 09 Taxi to runway (takeoff)
|
||||
{ 89, 85, AMED_EXACTPOS, 1 }, // 10 Taxi to start of runway (takeoff)
|
||||
{ 3, 85, AMED_NOSPDCLAMP, 0 }, // 11 Accelerate to end of runway
|
||||
{ -79, 85, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 12 Take off
|
||||
{ 177, 85, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 13 Fly to landing position in air
|
||||
{ 89, 85, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 14 Going down for land
|
||||
{ 3, 85, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 15 Just landed, brake until end of runway
|
||||
{ 20, 87, 0, 0 }, // 16 Just landed, turn around and taxi 1 square
|
||||
{ 36, 71, 0, 0 }, // 17 Taxi from runway to crossing
|
||||
{ -31, 193, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 18 Fly around waiting for a landing spot (north-east)
|
||||
{ 1, 1, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 19 Fly around waiting for a landing spot (north-west)
|
||||
{ 257, 1, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 20 Fly around waiting for a landing spot (south-west)
|
||||
{ 273, 49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 21 Fly around waiting for a landing spot (south)
|
||||
{ 44, 63, AMED_HELI_RAISE, 0 }, // 22 Helicopter takeoff
|
||||
{ 28, 74, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 23 In position above landing spot helicopter
|
||||
{ 28, 74, AMED_HELI_LOWER, 0 }, // 24 Helicopter landing
|
||||
};
|
||||
|
||||
// Metropolitan Airport (metropolitan) - 2 runways
|
||||
static const AirportMovingData _airport_moving_data_metropolitan[27] = {
|
||||
{ 85, 3, AMED_EXACTPOS, 3 }, // 00 In Hangar
|
||||
{ 85, 27, 0, 0 }, // 01 Taxi to right outside depot
|
||||
{ 26, 41, AMED_EXACTPOS, 5 }, // 02 Terminal 1
|
||||
{ 56, 20, AMED_EXACTPOS, 3 }, // 03 Terminal 2
|
||||
{ 38, 8, AMED_EXACTPOS, 5 }, // 04 Terminal 3
|
||||
{ 65, 6, 0, 0 }, // 05 Taxi to right in infront of terminal 2/3
|
||||
{ 70, 33, 0, 0 }, // 06 Taxiway terminals 2-3
|
||||
{ 44, 58, 0, 0 }, // 07 Taxi to Airport center
|
||||
{ 72, 58, 0, 0 }, // 08 Towards takeoff
|
||||
{ 72, 69, 0, 0 }, // 09 Taxi to runway (takeoff)
|
||||
{ 89, 69, AMED_EXACTPOS, 1 }, // 10 Taxi to start of runway (takeoff)
|
||||
{ 3, 69, AMED_NOSPDCLAMP, 0 }, // 11 Accelerate to end of runway
|
||||
{ -79, 69, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 12 Take off
|
||||
{ 177, 85, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 13 Fly to landing position in air
|
||||
{ 89, 85, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 14 Going down for land
|
||||
{ 3, 85, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 15 Just landed, brake until end of runway
|
||||
{ 21, 85, 0, 0 }, // 16 Just landed, turn around and taxi 1 square
|
||||
{ 21, 69, 0, 0 }, // 17 On Runway-out taxiing to In-Way
|
||||
{ 21, 54, AMED_EXACTPOS, 5 }, // 18 Taxi from runway to crossing
|
||||
{ -31, 193, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 19 Fly around waiting for a landing spot (north-east)
|
||||
{ 1, 1, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 20 Fly around waiting for a landing spot (north-west)
|
||||
{ 257, 1, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 21 Fly around waiting for a landing spot (south-west)
|
||||
{ 273, 49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 22 Fly around waiting for a landing spot (south)
|
||||
{ 44, 58, 0, 0 }, // 23 Helicopter takeoff spot on ground (to clear airport sooner)
|
||||
{ 44, 63, AMED_HELI_RAISE, 0 }, // 24 Helicopter takeoff
|
||||
{ 15, 54, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 25 Get in position above landing spot helicopter
|
||||
{ 15, 54, AMED_HELI_LOWER, 0 }, // 26 Helicopter landing
|
||||
};
|
||||
|
||||
// International Airport (international) - 2 runways, 6 terminals, dedicated helipod
|
||||
static const AirportMovingData _airport_moving_data_international[51] = {
|
||||
{ 7, 55, AMED_EXACTPOS, 3 }, // 00 In Hangar 1
|
||||
{ 100, 21, AMED_EXACTPOS, 3 }, // 01 In Hangar 2
|
||||
{ 7, 70, 0, 0 }, // 02 Taxi to right outside depot
|
||||
{ 100, 36, 0, 0 }, // 03 Taxi to right outside depot
|
||||
{ 38, 70, AMED_EXACTPOS, 5 }, // 04 Terminal 1
|
||||
{ 38, 54, AMED_EXACTPOS, 5 }, // 05 Terminal 2
|
||||
{ 38, 38, AMED_EXACTPOS, 5 }, // 06 Terminal 3
|
||||
{ 70, 70, AMED_EXACTPOS, 1 }, // 07 Terminal 4
|
||||
{ 70, 54, AMED_EXACTPOS, 1 }, // 08 Terminal 5
|
||||
{ 70, 38, AMED_EXACTPOS, 1 }, // 09 Terminal 6
|
||||
{ 104, 71, AMED_EXACTPOS, 1 }, // 10 Helipad 1
|
||||
{ 104, 55, AMED_EXACTPOS, 1 }, // 11 Helipad 2
|
||||
{ 22, 87, 0, 0 }, // 12 Towards Terminals 4/5/6, Helipad 1/2
|
||||
{ 60, 87, 0, 0 }, // 13 Towards Terminals 4/5/6, Helipad 1/2
|
||||
{ 66, 87, 0, 0 }, // 14 Towards Terminals 4/5/6, Helipad 1/2
|
||||
{ 86, 87, AMED_EXACTPOS, 7 }, // 15 Towards Terminals 4/5/6, Helipad 1/2
|
||||
{ 86, 70, 0, 0 }, // 16 In Front of Terminal 4 / Helipad 1
|
||||
{ 86, 54, 0, 0 }, // 17 In Front of Terminal 5 / Helipad 2
|
||||
{ 86, 38, 0, 0 }, // 18 In Front of Terminal 6
|
||||
{ 86, 22, 0, 0 }, // 19 Towards Terminals Takeoff (Taxiway)
|
||||
{ 66, 22, 0, 0 }, // 20 Towards Terminals Takeoff (Taxiway)
|
||||
{ 60, 22, 0, 0 }, // 21 Towards Terminals Takeoff (Taxiway)
|
||||
{ 38, 22, 0, 0 }, // 22 Towards Terminals Takeoff (Taxiway)
|
||||
{ 22, 70, 0, 0 }, // 23 In Front of Terminal 1
|
||||
{ 22, 58, 0, 0 }, // 24 In Front of Terminal 2
|
||||
{ 22, 38, 0, 0 }, // 25 In Front of Terminal 3
|
||||
{ 22, 22, AMED_EXACTPOS, 7 }, // 26 Going for Takeoff
|
||||
{ 22, 6, 0, 0 }, // 27 On Runway-out, prepare for takeoff
|
||||
{ 3, 6, AMED_EXACTPOS, 5 }, // 28 Accelerate to end of runway
|
||||
{ 60, 6, AMED_NOSPDCLAMP, 0 }, // 29 Release control of runway, for smoother movement
|
||||
{ 105, 6, AMED_NOSPDCLAMP, 0 }, // 30 End of runway
|
||||
{ 190, 6, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 31 Take off
|
||||
{ 193, 104, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 32 Fly to landing position in air
|
||||
{ 105, 104, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 33 Going down for land
|
||||
{ 3, 104, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 34 Just landed, brake until end of runway
|
||||
{ 12, 104, 0, 0 }, // 35 Just landed, turn around and taxi 1 square
|
||||
{ 7, 84, 0, 0 }, // 36 Taxi from runway to crossing
|
||||
{ -31, 209, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 37 Fly around waiting for a landing spot (north-east)
|
||||
{ 1, 6, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 38 Fly around waiting for a landing spot (north-west)
|
||||
{ 273, 6, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 39 Fly around waiting for a landing spot (south-west)
|
||||
{ 305, 81, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 40 Fly around waiting for a landing spot (south)
|
||||
// Helicopter
|
||||
{ 128, 80, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 41 Bufferspace before helipad
|
||||
{ 128, 80, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 42 Bufferspace before helipad
|
||||
{ 96, 71, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 43 Get in position for Helipad1
|
||||
{ 96, 55, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 44 Get in position for Helipad2
|
||||
{ 96, 71, AMED_HELI_LOWER, 0 }, // 45 Land at Helipad1
|
||||
{ 96, 55, AMED_HELI_LOWER, 0 }, // 46 Land at Helipad2
|
||||
{ 104, 71, AMED_HELI_RAISE, 0 }, // 47 Takeoff Helipad1
|
||||
{ 104, 55, AMED_HELI_RAISE, 0 }, // 48 Takeoff Helipad2
|
||||
{ 104, 32, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 49 Go to position for Hangarentrance in air
|
||||
{ 104, 32, AMED_HELI_LOWER, 0} // 50 Land in HANGAR2_AREA to go to hangar
|
||||
};
|
||||
|
||||
// Intercontinental Airport - 4 runways, 8 terminals, 2 dedicated helipads
|
||||
static const AirportMovingData _airport_moving_data_intercontinental[77] = {
|
||||
{ 7, 87, AMED_EXACTPOS, 3 }, // 00 In Hangar 1
|
||||
{ 135, 72, AMED_EXACTPOS, 3 }, // 01 In Hangar 2
|
||||
{ 7, 104, 0, 0 }, // 02 Taxi to right outside depot 1
|
||||
{ 135, 88, 0, 0 }, // 03 Taxi to right outside depot 2
|
||||
{ 56, 120, AMED_EXACTPOS, 6 }, // 04 Terminal 1
|
||||
{ 56, 104, AMED_EXACTPOS, 5 }, // 05 Terminal 2
|
||||
{ 56, 88, AMED_EXACTPOS, 5 }, // 06 Terminal 3
|
||||
{ 56, 72, AMED_EXACTPOS, 5 }, // 07 Terminal 4
|
||||
{ 88, 120, AMED_EXACTPOS, 0 }, // 08 Terminal 5
|
||||
{ 88, 104, AMED_EXACTPOS, 1 }, // 09 Terminal 6
|
||||
{ 88, 88, AMED_EXACTPOS, 1 }, // 10 Terminal 7
|
||||
{ 88, 72, AMED_EXACTPOS, 1 }, // 11 Terminal 8
|
||||
{ 88, 56, AMED_EXACTPOS, 3 }, // 12 Helipad 1
|
||||
{ 72, 56, AMED_EXACTPOS, 1 }, // 13 Helipad 2
|
||||
{ 40, 136, 0, 0 }, // 14 Term group 2 enter 1 a
|
||||
{ 56, 136, 0, 0 }, // 15 Term group 2 enter 1 b
|
||||
{ 88, 136, 0, 0 }, // 16 Term group 2 enter 2 a
|
||||
{ 104, 136, 0, 0 }, // 17 Term group 2 enter 2 b
|
||||
{ 104, 120, 0, 0 }, // 18 Term group 2 - opp term 5
|
||||
{ 104, 104, 0, 0 }, // 19 Term group 2 - opp term 6 & exit2
|
||||
{ 104, 88, 0, 0 }, // 20 Term group 2 - opp term 7 & hangar area 2
|
||||
{ 104, 72, 0, 0 }, // 21 Term group 2 - opp term 8
|
||||
{ 104, 56, 0, 0 }, // 22 Taxi Term group 2 exit a
|
||||
{ 104, 40, 0, 0 }, // 23 Taxi Term group 2 exit b
|
||||
{ 56, 40, 0, 0 }, // 24 Term group 2 exit 2a
|
||||
{ 40, 40, 0, 0 }, // 25 Term group 2 exit 2b
|
||||
{ 40, 120, 0, 0 }, // 26 Term group 1 - opp term 1
|
||||
{ 40, 104, 0, 0 }, // 27 Term group 1 - opp term 2 & hangar area 1
|
||||
{ 40, 88, 0, 0 }, // 28 Term group 1 - opp term 3
|
||||
{ 40, 72, 0, 0 }, // 29 Term group 1 - opp term 4
|
||||
{ 18, 72, 0, 7 }, // 30 Outway 1
|
||||
{ 8, 40, 0, 7 }, // 31 Airport OUTWAY
|
||||
{ 8, 24, AMED_EXACTPOS, 5 }, // 32 Accelerate to end of runway
|
||||
{ 119, 24, AMED_NOSPDCLAMP, 0 }, // 33 Release control of runway, for smoother movement
|
||||
{ 117, 24, AMED_NOSPDCLAMP, 0 }, // 34 End of runway
|
||||
{ 197, 24, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 35 Take off
|
||||
{ 254, 84, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 36 Flying to landing position in air
|
||||
{ 117, 168, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 37 Going down for land
|
||||
{ 3, 168, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 38 Just landed, brake until end of runway
|
||||
{ 8, 168, 0, 0 }, // 39 Just landed, turn around and taxi
|
||||
{ 8, 144, 0, 7 }, // 40 Taxi from runway
|
||||
{ 8, 128, 0, 7 }, // 41 Taxi from runway
|
||||
{ 8, 120, AMED_EXACTPOS, 5 }, // 42 Airport entrance
|
||||
{ 56, 344, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 43 Fly around waiting for a landing spot (north-east)
|
||||
{ -200, 88, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 44 Fly around waiting for a landing spot (north-west)
|
||||
{ 56, -168, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 45 Fly around waiting for a landing spot (south-west)
|
||||
{ 312, 88, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 46 Fly around waiting for a landing spot (south)
|
||||
// Helicopter
|
||||
{ 96, 40, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 47 Bufferspace before helipad
|
||||
{ 96, 40, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 48 Bufferspace before helipad
|
||||
{ 82, 54, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 49 Get in position for Helipad1
|
||||
{ 64, 56, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 50 Get in position for Helipad2
|
||||
{ 81, 55, AMED_HELI_LOWER, 0 }, // 51 Land at Helipad1
|
||||
{ 64, 56, AMED_HELI_LOWER, 0 }, // 52 Land at Helipad2
|
||||
{ 80, 56, AMED_HELI_RAISE, 0 }, // 53 Takeoff Helipad1
|
||||
{ 64, 56, AMED_HELI_RAISE, 0 }, // 54 Takeoff Helipad2
|
||||
{ 136, 96, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 55 Go to position for Hangarentrance in air
|
||||
{ 136, 96, AMED_HELI_LOWER, 0 }, // 56 Land in front of hangar2
|
||||
{ 126, 104, 0, 3 }, // 57 Outway 2
|
||||
{ 136, 136, 0, 1 }, // 58 Airport OUTWAY 2
|
||||
{ 136, 152, AMED_EXACTPOS, 5 }, // 59 Accelerate to end of runway2
|
||||
{ 16, 152, AMED_NOSPDCLAMP, 0 }, // 60 Release control of runway2, for smoother movement
|
||||
{ 20, 152, AMED_NOSPDCLAMP, 0 }, // 61 End of runway2
|
||||
{ -56, 152, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 62 Take off2
|
||||
{ 24, 8, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 63 Going down for land2
|
||||
{ 136, 8, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 64 Just landed, brake until end of runway2in
|
||||
{ 136, 8, 0, 0 }, // 65 Just landed, turn around and taxi
|
||||
{ 136, 24, 0, 3 }, // 66 Taxi from runway 2in
|
||||
{ 136, 40, 0, 3 }, // 67 Taxi from runway 2in
|
||||
{ 136, 56, AMED_EXACTPOS, 1 }, // 68 Airport entrance2
|
||||
{ -56, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 69 Fly to landing position in air2
|
||||
{ 88, 40, 0, 0 }, // 70 Taxi Term group 2 exit - opp heli1
|
||||
{ 72, 40, 0, 0 }, // 71 Taxi Term group 2 exit - opp heli2
|
||||
{ 88, 57, AMED_EXACTPOS, 3 }, // 72 pre-helitakeoff helipad 1
|
||||
{ 71, 56, AMED_EXACTPOS, 1 }, // 73 pre-helitakeoff helipad 2
|
||||
{ 8, 120, AMED_HELI_RAISE, 0 }, // 74 Helitakeoff outside depot 1
|
||||
{ 136, 104, AMED_HELI_RAISE, 0 }, // 75 Helitakeoff outside depot 2
|
||||
{ 197, 168, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0} // 76 Fly to landing position in air1
|
||||
};
|
||||
|
||||
|
||||
// Heliport (heliport)
|
||||
static const AirportMovingData _airport_moving_data_heliport[9] = {
|
||||
{ 5, 9, AMED_EXACTPOS, 1 }, // 0 - At heliport terminal
|
||||
{ 2, 9, AMED_HELI_RAISE, 0 }, // 1 - Take off (play sound)
|
||||
{ -3, 9, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 2 - In position above landing spot helicopter
|
||||
{ -3, 9, AMED_HELI_LOWER, 0 }, // 3 - Land
|
||||
{ 2, 9, 0, 0 }, // 4 - Goto terminal on ground
|
||||
{ -31, 59, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 5 - Circle #1 (north-east)
|
||||
{ -31, -49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 6 - Circle #2 (north-west)
|
||||
{ 49, -49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 7 - Circle #3 (south-west)
|
||||
{ 70, 9, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 8 - Circle #4 (south)
|
||||
};
|
||||
|
||||
// HeliDepot 2x2 (heliport)
|
||||
static const AirportMovingData _airport_moving_data_helidepot[18] = {
|
||||
{ 24, 4, AMED_EXACTPOS, 1 }, // 0 - At depot
|
||||
{ 24, 28, 0, 0 }, // 1 Taxi to right outside depot
|
||||
{ 5, 38, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 2 Flying
|
||||
{ -15, -15, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 3 - Circle #1 (north-east)
|
||||
{ -15, -49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 4 - Circle #2 (north-west)
|
||||
{ 49, -49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 5 - Circle #3 (south-west)
|
||||
{ 49, -15, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 6 - Circle #4 (south-east)
|
||||
{ 8, 32, AMED_NOSPDCLAMP | AMED_SLOWTURN, 7 }, // 7 - PreHelipad
|
||||
{ 8, 32, AMED_NOSPDCLAMP | AMED_SLOWTURN, 7 }, // 8 - Helipad
|
||||
{ 8, 16, AMED_NOSPDCLAMP | AMED_SLOWTURN, 7 }, // 9 - Land
|
||||
{ 8, 16, AMED_HELI_LOWER, 7 }, // 10 - Land
|
||||
{ 8, 24, AMED_HELI_RAISE, 0 }, // 11 - Take off (play sound)
|
||||
{ 32, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 7 }, // 12 Air to above hangar area
|
||||
{ 32, 24, AMED_HELI_LOWER, 7 }, // 13 Taxi to right outside depot
|
||||
{ 8, 24, AMED_EXACTPOS, 7 }, // 14 - on helipad1
|
||||
{ 24, 28, AMED_HELI_RAISE, 0 }, // 15 Takeoff right outside depot
|
||||
{ 8, 24, AMED_HELI_RAISE, 5 }, // 16 - Take off (play sound)
|
||||
{ 8, 24, AMED_SLOWTURN | AMED_EXACTPOS, 2 }, // 17 - turn on helipad1 for takeoff
|
||||
};
|
||||
|
||||
// HeliDepot 2x2 (heliport)
|
||||
static const AirportMovingData _airport_moving_data_helistation[33] = {
|
||||
{ 8, 3, AMED_EXACTPOS, 3 }, // 00 In Hangar2
|
||||
{ 8, 22, 0, 0 }, // 01 outside hangar 2
|
||||
{ 116, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 02 Fly to landing position in air
|
||||
{ 14, 22, AMED_HELI_RAISE, 0 }, // 03 Helitakeoff outside hangar1(play sound)
|
||||
{ 24, 22, 0, 0 }, // 04 taxiing
|
||||
{ 40, 22, 0, 0 }, // 05 taxiing
|
||||
{ 40, 8, AMED_EXACTPOS, 1 }, // 06 Helipad 1
|
||||
{ 56, 8, AMED_EXACTPOS, 1 }, // 07 Helipad 2
|
||||
{ 56, 24, AMED_EXACTPOS, 1 }, // 08 Helipad 3
|
||||
{ 40, 8, AMED_EXACTPOS, 0 }, // 09 pre-helitakeoff helipad 1
|
||||
{ 56, 8, AMED_EXACTPOS, 0 }, // 10 pre-helitakeoff helipad 2
|
||||
{ 56, 24, AMED_EXACTPOS, 0 }, // 11 pre-helitakeoff helipad 3
|
||||
{ 32, 8, AMED_HELI_RAISE, 0 }, // 12 Takeoff Helipad1
|
||||
{ 48, 8, AMED_HELI_RAISE, 0 }, // 13 Takeoff Helipad2
|
||||
{ 48, 24, AMED_HELI_RAISE, 0 }, // 14 Takeoff Helipad3
|
||||
{ 84, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 15 Bufferspace before helipad
|
||||
{ 68, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 16 Bufferspace before helipad
|
||||
{ 32, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 17 Get in position for Helipad1
|
||||
{ 48, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 18 Get in position for Helipad2
|
||||
{ 48, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 1 }, // 19 Get in position for Helipad3
|
||||
{ 40, 8, AMED_HELI_LOWER, 0 }, // 20 Land at Helipad1
|
||||
{ 48, 8, AMED_HELI_LOWER, 0 }, // 21 Land at Helipad2
|
||||
{ 48, 24, AMED_HELI_LOWER, 0 }, // 22 Land at Helipad3
|
||||
{ 0, 22, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 23 Go to position for Hangarentrance in air
|
||||
{ 0, 22, AMED_HELI_LOWER, 0 }, // 24 Land in front of hangar
|
||||
{ 148, -8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 25 Fly around waiting for a landing spot (south-east)
|
||||
{ 148, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 26 Fly around waiting for a landing spot (south-west)
|
||||
{ 132, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 27 Fly around waiting for a landing spot (south-west)
|
||||
{ 100, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 28 Fly around waiting for a landing spot (north-east)
|
||||
{ 84, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 29 Fly around waiting for a landing spot (south-east)
|
||||
{ 84, -8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 30 Fly around waiting for a landing spot (south-west)
|
||||
{ 100, -24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 31 Fly around waiting for a landing spot (north-west)
|
||||
{ 132, -24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 32 Fly around waiting for a landing spot (north-east)
|
||||
};
|
||||
|
||||
// Oilrig
|
||||
static const AirportMovingData _airport_moving_data_oilrig[9] = {
|
||||
{ 31, 9, AMED_EXACTPOS, 1 }, // 0 - At oilrig terminal
|
||||
{ 28, 9, AMED_HELI_RAISE, 0 }, // 1 - Take off (play sound)
|
||||
{ 23, 9, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 2 - In position above landing spot helicopter
|
||||
{ 23, 9, AMED_HELI_LOWER, 0 }, // 3 - Land
|
||||
{ 28, 9, 0, 0 }, // 4 - Goto terminal on ground
|
||||
{ -31, 69, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 5 - circle #1 (north-east)
|
||||
{ -31, -49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 6 - circle #2 (north-west)
|
||||
{ 69, -49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 7 - circle #3 (south-west)
|
||||
{ 70, 9, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 8 - circle #4 (south)
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
/////**********Movement Machine on Airports*********************///////
|
||||
/* First element of terminals array tells us how many depots there are (to know size of array)
|
||||
* this may be changed later when airports are moved to external file */
|
||||
static const TileIndexDiffC _airport_depots_country[] = {{3, 0}};
|
||||
static const byte _airport_terminal_country[] = {1, 2};
|
||||
static const AirportFTAbuildup _airport_fta_country[] = {
|
||||
{ 0, HANGAR, NOTHING_block, 1 },
|
||||
{ 1, 255, AIRPORT_BUSY_block, 0 }, { 1, HANGAR, 0, 0 }, { 1, TERM1, TERM1_block, 2 }, { 1, TERM2, 0, 4 }, { 1, HELITAKEOFF, 0, 19 }, { 1, 0, 0, 6 },
|
||||
{ 2, TERM1, TERM1_block, 1 },
|
||||
{ 3, TERM2, TERM2_block, 5 },
|
||||
{ 4, 255, AIRPORT_BUSY_block, 0 }, { 4, TERM2, 0, 5 }, { 4, HANGAR, 0, 1 }, { 4, TAKEOFF, 0, 6 }, { 4, HELITAKEOFF, 0, 1 },
|
||||
{ 5, 255, AIRPORT_BUSY_block, 0 }, { 5, TERM2, TERM2_block, 3 }, { 5, 0, 0, 4 },
|
||||
{ 6, 0, AIRPORT_BUSY_block, 7 },
|
||||
// takeoff
|
||||
{ 7, TAKEOFF, AIRPORT_BUSY_block, 8 },
|
||||
{ 8, STARTTAKEOFF, NOTHING_block, 9 },
|
||||
{ 9, ENDTAKEOFF, NOTHING_block, 0 },
|
||||
// landing
|
||||
{ 10, FLYING, NOTHING_block, 15 }, { 10, LANDING, 0, 11 }, { 10, HELILANDING, 0, 20 },
|
||||
{ 11, LANDING, AIRPORT_BUSY_block, 12 },
|
||||
{ 12, 0, AIRPORT_BUSY_block, 13 },
|
||||
{ 13, ENDLANDING, AIRPORT_BUSY_block, 14 }, { 13, TERM2, 0, 5 }, { 13, 0, 0, 14 },
|
||||
{ 14, 0, AIRPORT_BUSY_block, 1 },
|
||||
// In air
|
||||
{ 15, 0, NOTHING_block, 16 },
|
||||
{ 16, 0, NOTHING_block, 17 },
|
||||
{ 17, 0, NOTHING_block, 18 },
|
||||
{ 18, 0, NOTHING_block, 10 },
|
||||
{ 19, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 20, HELILANDING, AIRPORT_BUSY_block, 21 },
|
||||
{ 21, HELIENDLANDING, AIRPORT_BUSY_block, 1 },
|
||||
{ MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE
|
||||
};
|
||||
|
||||
static const TileIndexDiffC _airport_depots_commuter[] = { { 4, 0 } };
|
||||
static const byte _airport_terminal_commuter[] = { 1, 3 };
|
||||
static const byte _airport_helipad_commuter[] = { 1, 2 };
|
||||
static const AirportFTAbuildup _airport_fta_commuter[] = {
|
||||
{ 0, HANGAR, NOTHING_block, 1 }, { 0, HELITAKEOFF, HELIPAD2_block, 1 }, { 0, 0, 0, 1 },
|
||||
{ 1, 255, TAXIWAY_BUSY_block, 0 }, { 1, HANGAR, 0, 0 }, { 1, TAKEOFF, 0, 11 }, { 1, TERM1, TAXIWAY_BUSY_block, 10 }, { 1, TERM2, TAXIWAY_BUSY_block, 10 }, { 1, TERM3, TAXIWAY_BUSY_block, 10 }, { 1, HELIPAD1, TAXIWAY_BUSY_block, 10 }, { 1, HELIPAD2, TAXIWAY_BUSY_block, 10 }, { 1, HELITAKEOFF, TAXIWAY_BUSY_block, 10 }, { 1, 0, 0, 0 },
|
||||
{ 2, 255, AIRPORT_ENTRANCE_block, 2 }, { 2, HANGAR, 0, 8 }, { 2, TERM1, 0, 8 }, { 2, TERM2, 0, 8 }, { 2, TERM3, 0, 8 }, { 2, HELIPAD1, 0, 8 }, { 2, HELIPAD2, 0, 8 }, { 2, HELITAKEOFF, 0, 8 }, { 2, 0, 0, 2 },
|
||||
{ 3, TERM1, TERM1_block, 8 }, { 3, HANGAR, 0, 8 }, { 3, TAKEOFF, 0, 8 }, { 3, 0, 0, 3 },
|
||||
{ 4, TERM2, TERM2_block, 9 }, { 4, HANGAR, 0, 9 }, { 4, TAKEOFF, 0, 9 }, { 4, 0, 0, 4 },
|
||||
{ 5, TERM3, TERM3_block, 10 }, { 5, HANGAR, 0, 10 }, { 5, TAKEOFF, 0, 10 }, { 5, 0, 0, 5 },
|
||||
{ 6, HELIPAD1, HELIPAD1_block, 6 }, { 6, HANGAR, TAXIWAY_BUSY_block, 9 }, { 6, HELITAKEOFF, 0, 35 },
|
||||
{ 7, HELIPAD2, HELIPAD2_block, 7 }, { 7, HANGAR, TAXIWAY_BUSY_block, 10 }, { 7, HELITAKEOFF, 0, 36 },
|
||||
{ 8, 255, TAXIWAY_BUSY_block, 8 }, { 8, TAKEOFF, TAXIWAY_BUSY_block, 9 }, { 8, HANGAR, TAXIWAY_BUSY_block, 9 }, { 8, TERM1, TERM1_block, 3 }, { 8, 0, TAXIWAY_BUSY_block, 9 },
|
||||
{ 9, 255, TAXIWAY_BUSY_block, 9 }, { 9, TAKEOFF, TAXIWAY_BUSY_block, 10 }, { 9, HANGAR, TAXIWAY_BUSY_block, 10 }, { 9, TERM2, TERM2_block, 4 }, { 9, HELIPAD1, HELIPAD1_block, 6 }, { 9, HELITAKEOFF, HELIPAD1_block, 6 }, { 9, TERM1, TAXIWAY_BUSY_block, 8 }, { 9, 0, TAXIWAY_BUSY_block, 10 },
|
||||
{ 10, 255, TAXIWAY_BUSY_block, 10 }, { 10, TERM3, TERM3_block, 5 }, { 10, HELIPAD1, 0, 9 }, { 10, HELIPAD2, HELIPAD2_block, 7 }, { 10, HELITAKEOFF, HELIPAD2_block, 7 }, { 10, TAKEOFF, TAXIWAY_BUSY_block, 1 }, { 10, HANGAR, TAXIWAY_BUSY_block, 1 }, { 10, 0, TAXIWAY_BUSY_block, 9 },
|
||||
{ 11, 0, OUT_WAY_block, 12 },
|
||||
// takeoff
|
||||
{ 12, TAKEOFF, RUNWAY_IN_OUT_block, 13 },
|
||||
{ 13, 0, RUNWAY_IN_OUT_block, 14 },
|
||||
{ 14, STARTTAKEOFF, RUNWAY_IN_OUT_block, 15 },
|
||||
{ 15, ENDTAKEOFF, NOTHING_block, 0 },
|
||||
// landing
|
||||
{ 16, FLYING, NOTHING_block, 21 }, { 16, LANDING, IN_WAY_block, 17 }, { 16, HELILANDING, 0, 25 },
|
||||
{ 17, LANDING, RUNWAY_IN_OUT_block, 18 },
|
||||
{ 18, 0, RUNWAY_IN_OUT_block, 19 },
|
||||
{ 19, 0, RUNWAY_IN_OUT_block, 20 },
|
||||
{ 20, ENDLANDING, IN_WAY_block, 2 },
|
||||
// In Air
|
||||
{ 21, 0, NOTHING_block, 22 },
|
||||
{ 22, 0, NOTHING_block, 23 },
|
||||
{ 23, 0, NOTHING_block, 24 },
|
||||
{ 24, 0, NOTHING_block, 16 },
|
||||
// Helicopter -- stay in air in special place as a buffer to choose from helipads
|
||||
{ 25, HELILANDING, PRE_HELIPAD_block, 26 },
|
||||
{ 26, HELIENDLANDING, PRE_HELIPAD_block, 26 }, { 26, HELIPAD1, 0, 27 }, { 26, HELIPAD2, 0, 28 }, { 26, HANGAR, 0, 33 },
|
||||
{ 27, 0, NOTHING_block, 29 }, //helipad1 approach
|
||||
{ 28, 0, NOTHING_block, 30 },
|
||||
// landing
|
||||
{ 29, 255, NOTHING_block, 0 }, { 29, HELIPAD1, HELIPAD1_block, 6 },
|
||||
{ 30, 255, NOTHING_block, 0 }, { 30, HELIPAD2, HELIPAD2_block, 7 },
|
||||
// Helicopter -- takeoff
|
||||
{ 31, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 32, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 33, 0, TAXIWAY_BUSY_block, 34 }, // need to go to hangar when waiting in air
|
||||
{ 34, 0, TAXIWAY_BUSY_block, 1 },
|
||||
{ 35, 0, HELIPAD1_block, 31 },
|
||||
{ 36, 0, HELIPAD2_block, 32 },
|
||||
{ MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE
|
||||
};
|
||||
|
||||
static const TileIndexDiffC _airport_depots_city[] = { { 5, 0 } };
|
||||
static const byte _airport_terminal_city[] = { 1, 3 };
|
||||
static const AirportFTAbuildup _airport_fta_city[] = {
|
||||
{ 0, HANGAR, NOTHING_block, 1 }, { 0, TAKEOFF, OUT_WAY_block, 1 }, { 0, 0, 0, 1 },
|
||||
{ 1, 255, TAXIWAY_BUSY_block, 0 }, { 1, HANGAR, 0, 0 }, { 1, TERM2, 0, 6 }, { 1, TERM3, 0, 6 }, { 1, 0, 0, 7 }, // for all else, go to 7
|
||||
{ 2, TERM1, TERM1_block, 7 }, { 2, TAKEOFF, OUT_WAY_block, 7 }, { 2, 0, 0, 7 },
|
||||
{ 3, TERM2, TERM2_block, 5 }, { 3, TAKEOFF, OUT_WAY_block, 5 }, { 3, 0, 0, 5 },
|
||||
{ 4, TERM3, TERM3_block, 5 }, { 4, TAKEOFF, OUT_WAY_block, 5 }, { 4, 0, 0, 5 },
|
||||
{ 5, 255, TAXIWAY_BUSY_block, 0 }, { 5, TERM2, TERM2_block, 3 }, { 5, TERM3, TERM3_block, 4 }, { 5, 0, 0, 6 },
|
||||
{ 6, 255, TAXIWAY_BUSY_block, 0 }, { 6, TERM2, 0, 5 }, { 6, TERM3, 0, 5 }, { 6, HANGAR, 0, 1 }, { 6, 0, 0, 7 },
|
||||
{ 7, 255, TAXIWAY_BUSY_block, 0 }, { 7, TERM1, TERM1_block, 2 }, { 7, TAKEOFF, OUT_WAY_block, 8 }, { 7, HELITAKEOFF, 0, 22 }, { 7, HANGAR, 0, 1 }, { 7, 0, 0, 6 },
|
||||
{ 8, 0, OUT_WAY_block, 9 },
|
||||
{ 9, 0, RUNWAY_IN_OUT_block, 10 },
|
||||
// takeoff
|
||||
{ 10, TAKEOFF, RUNWAY_IN_OUT_block, 11 },
|
||||
{ 11, STARTTAKEOFF, NOTHING_block, 12 },
|
||||
{ 12, ENDTAKEOFF, NOTHING_block, 0 },
|
||||
// landing
|
||||
{ 13, FLYING, NOTHING_block, 18 }, { 13, LANDING, 0, 14 }, { 13, HELILANDING, 0, 23 },
|
||||
{ 14, LANDING, RUNWAY_IN_OUT_block, 15 },
|
||||
{ 15, 0, RUNWAY_IN_OUT_block, 16 },
|
||||
{ 16, 0, RUNWAY_IN_OUT_block, 17 },
|
||||
{ 17, ENDLANDING, IN_WAY_block, 7 },
|
||||
// In Air
|
||||
{ 18, 0, NOTHING_block, 19 },
|
||||
{ 19, 0, NOTHING_block, 20 },
|
||||
{ 20, 0, NOTHING_block, 21 },
|
||||
{ 21, 0, NOTHING_block, 13 },
|
||||
// helicopter
|
||||
{ 22, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 23, HELILANDING, IN_WAY_block, 24 },
|
||||
{ 24, HELIENDLANDING, IN_WAY_block, 17 },
|
||||
{ MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE
|
||||
};
|
||||
|
||||
static const TileIndexDiffC _airport_depots_metropolitan[] = { { 5, 0 } };
|
||||
static const byte _airport_terminal_metropolitan[] = { 1, 3 };
|
||||
static const AirportFTAbuildup _airport_fta_metropolitan[] = {
|
||||
{ 0, HANGAR, NOTHING_block, 1 },
|
||||
{ 1, 255, TAXIWAY_BUSY_block, 0 }, { 1, HANGAR, 0, 0 }, { 1, TERM2, 0, 6 }, { 1, TERM3, 0, 6 }, { 1, 0, 0, 7 }, // for all else, go to 7
|
||||
{ 2, TERM1, TERM1_block, 7 },
|
||||
{ 3, TERM2, TERM2_block, 5 },
|
||||
{ 4, TERM3, TERM3_block, 5 },
|
||||
{ 5, 255, TAXIWAY_BUSY_block, 0 }, { 5, TERM2, TERM2_block, 3 }, { 5, TERM3, TERM3_block, 4 }, { 5, 0, 0, 6 },
|
||||
{ 6, 255, TAXIWAY_BUSY_block, 0 }, { 6, TERM2, 0, 5 }, { 6, TERM3, 0, 5 }, { 6, HANGAR, 0, 1 }, { 6, 0, 0, 7 },
|
||||
{ 7, 255, TAXIWAY_BUSY_block, 0 }, { 7, TERM1, TERM1_block, 2 }, { 7, TAKEOFF, 0, 8 }, { 7, HELITAKEOFF, 0, 23 }, { 7, HANGAR, 0, 1 }, { 7, 0, 0, 6 },
|
||||
{ 8, 0, OUT_WAY_block, 9 },
|
||||
{ 9, 0, RUNWAY_OUT_block, 10 },
|
||||
// takeoff
|
||||
{ 10, TAKEOFF, RUNWAY_OUT_block, 11 },
|
||||
{ 11, STARTTAKEOFF, NOTHING_block, 12 },
|
||||
{ 12, ENDTAKEOFF, NOTHING_block, 0 },
|
||||
// landing
|
||||
{ 13, FLYING, NOTHING_block, 19 }, { 13, LANDING, 0, 14 }, { 13, HELILANDING, 0, 25 },
|
||||
{ 14, LANDING, RUNWAY_IN_block, 15 },
|
||||
{ 15, 0, RUNWAY_IN_block, 16 },
|
||||
{ 16, 255, RUNWAY_IN_block, 0 }, { 16, ENDLANDING, IN_WAY_block, 17 },
|
||||
{ 17, 255, RUNWAY_OUT_block, 0 }, { 17, ENDLANDING, IN_WAY_block, 18 },
|
||||
{ 18, ENDLANDING, IN_WAY_block, 7 },
|
||||
// In Air
|
||||
{ 19, 0, NOTHING_block, 20 },
|
||||
{ 20, 0, NOTHING_block, 21 },
|
||||
{ 21, 0, NOTHING_block, 22 },
|
||||
{ 22, 0, NOTHING_block, 13 },
|
||||
// helicopter
|
||||
{ 23, 0, NOTHING_block, 24 },
|
||||
{ 24, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 25, HELILANDING, IN_WAY_block, 26 },
|
||||
{ 26, HELIENDLANDING, IN_WAY_block, 18 },
|
||||
{ MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE
|
||||
};
|
||||
|
||||
static const TileIndexDiffC _airport_depots_international[] = { { 0, 3 }, { 6, 1 } };
|
||||
static const byte _airport_terminal_international[] = { 2, 3, 3 };
|
||||
static const byte _airport_helipad_international[] = { 1, 2 };
|
||||
static const AirportFTAbuildup _airport_fta_international[] = {
|
||||
{ 0, HANGAR, NOTHING_block, 2 }, { 0, 255, TERM_GROUP1_block, 0 }, { 0, 255, TERM_GROUP2_ENTER1_block, 1 }, { 0, HELITAKEOFF, HELIPAD1_block, 2 }, { 0, 0, 0, 2 },
|
||||
{ 1, HANGAR, NOTHING_block, 3 }, { 1, 255, HANGAR2_AREA_block, 1 }, { 1, HELITAKEOFF, HELIPAD2_block, 3 }, { 1, 0, 0, 3 },
|
||||
{ 2, 255, AIRPORT_ENTRANCE_block, 0 }, { 2, HANGAR, 0, 0 }, { 2, TERM4, 0, 12 }, { 2, TERM5, 0, 12 }, { 2, TERM6, 0, 12 }, { 2, HELIPAD1, 0, 12 }, { 2, HELIPAD2, 0, 12 }, { 2, HELITAKEOFF, 0, 12 }, { 2, 0, 0, 23 },
|
||||
{ 3, 255, HANGAR2_AREA_block, 0 }, { 3, HANGAR, 0, 1 }, { 3, 0, 0, 18 },
|
||||
{ 4, TERM1, TERM1_block, 23 }, { 4, HANGAR, AIRPORT_ENTRANCE_block, 23 }, { 4, 0, 0, 23 },
|
||||
{ 5, TERM2, TERM2_block, 24 }, { 5, HANGAR, AIRPORT_ENTRANCE_block, 24 }, { 5, 0, 0, 24 },
|
||||
{ 6, TERM3, TERM3_block, 25 }, { 6, HANGAR, AIRPORT_ENTRANCE_block, 25 }, { 6, 0, 0, 25 },
|
||||
{ 7, TERM4, TERM4_block, 16 }, { 7, HANGAR, HANGAR2_AREA_block, 16 }, { 7, 0, 0, 16 },
|
||||
{ 8, TERM5, TERM5_block, 17 }, { 8, HANGAR, HANGAR2_AREA_block, 17 }, { 8, 0, 0, 17 },
|
||||
{ 9, TERM6, TERM6_block, 18 }, { 9, HANGAR, HANGAR2_AREA_block, 18 }, { 9, 0, 0, 18 },
|
||||
{ 10, HELIPAD1, HELIPAD1_block, 10 }, { 10, HANGAR, HANGAR2_AREA_block, 16 }, { 10, HELITAKEOFF, 0, 47 },
|
||||
{ 11, HELIPAD2, HELIPAD2_block, 11 }, { 11, HANGAR, HANGAR2_AREA_block, 17 }, { 11, HELITAKEOFF, 0, 48 },
|
||||
{ 12, 0, TERM_GROUP2_ENTER1_block, 13 },
|
||||
{ 13, 0, TERM_GROUP2_ENTER1_block, 14 },
|
||||
{ 14, 0, TERM_GROUP2_ENTER2_block, 15 },
|
||||
{ 15, 0, TERM_GROUP2_ENTER2_block, 16 },
|
||||
{ 16, 255, TERM_GROUP2_block, 0 }, { 16, TERM4, TERM4_block, 7 }, { 16, HELIPAD1, HELIPAD1_block, 10 }, { 16, HELITAKEOFF, HELIPAD1_block, 10 }, { 16, 0, 0, 17 },
|
||||
{ 17, 255, TERM_GROUP2_block, 0 }, { 17, TERM5, TERM5_block, 8 }, { 17, TERM4, 0, 16 }, { 17, HELIPAD1, 0, 16 }, { 17, HELIPAD2, HELIPAD2_block, 11 }, { 17, HELITAKEOFF, HELIPAD2_block, 11 }, { 17, 0, 0, 18 },
|
||||
{ 18, 255, TERM_GROUP2_block, 0 }, { 18, TERM6, TERM6_block, 9 }, { 18, TAKEOFF, 0, 19 }, { 18, HANGAR, HANGAR2_AREA_block, 3 }, { 18, 0, 0, 17 },
|
||||
{ 19, 0, TERM_GROUP2_EXIT1_block, 20 },
|
||||
{ 20, 0, TERM_GROUP2_EXIT1_block, 21 },
|
||||
{ 21, 0, TERM_GROUP2_EXIT2_block, 22 },
|
||||
{ 22, 0, TERM_GROUP2_EXIT2_block, 26 },
|
||||
{ 23, 255, TERM_GROUP1_block, 0 }, { 23, TERM1, TERM1_block, 4 }, { 23, HANGAR, AIRPORT_ENTRANCE_block, 2 }, { 23, 0, 0, 24 },
|
||||
{ 24, 255, TERM_GROUP1_block, 0 }, { 24, TERM2, TERM2_block, 5 }, { 24, TERM1, 0, 23 }, { 24, HANGAR, 0, 23 }, { 24, 0, 0, 25 },
|
||||
{ 25, 255, TERM_GROUP1_block, 0 }, { 25, TERM3, TERM3_block, 6 }, { 25, TAKEOFF, 0, 26 }, { 25, 0, 0, 24 },
|
||||
{ 26, 255, TAXIWAY_BUSY_block, 0 }, { 26, TAKEOFF, 0, 27 }, { 26, 0, 0, 25 },
|
||||
{ 27, 0, OUT_WAY_block, 28 },
|
||||
// takeoff
|
||||
{ 28, TAKEOFF, OUT_WAY_block, 29 },
|
||||
{ 29, 0, RUNWAY_OUT_block, 30 },
|
||||
{ 30, STARTTAKEOFF, NOTHING_block, 31 },
|
||||
{ 31, ENDTAKEOFF, NOTHING_block, 0 },
|
||||
// landing
|
||||
{ 32, FLYING, NOTHING_block, 37 }, { 32, LANDING, 0, 33 }, { 32, HELILANDING, 0, 41 },
|
||||
{ 33, LANDING, RUNWAY_IN_block, 34 },
|
||||
{ 34, 0, RUNWAY_IN_block, 35 },
|
||||
{ 35, 0, RUNWAY_IN_block, 36 },
|
||||
{ 36, ENDLANDING, IN_WAY_block, 36 }, { 36, 255, TERM_GROUP1_block, 0 }, { 36, 255, TERM_GROUP2_ENTER1_block, 1 }, { 36, TERM4, 0, 12 }, { 36, TERM5, 0, 12 }, { 36, TERM6, 0, 12 }, { 36, 0, 0, 2 },
|
||||
// In Air
|
||||
{ 37, 0, NOTHING_block, 38 },
|
||||
{ 38, 0, NOTHING_block, 39 },
|
||||
{ 39, 0, NOTHING_block, 40 },
|
||||
{ 40, 0, NOTHING_block, 32 },
|
||||
// Helicopter -- stay in air in special place as a buffer to choose from helipads
|
||||
{ 41, HELILANDING, PRE_HELIPAD_block, 42 },
|
||||
{ 42, HELIENDLANDING, PRE_HELIPAD_block, 42 }, { 42, HELIPAD1, 0, 43 }, { 42, HELIPAD2, 0, 44 }, { 42, HANGAR, 0, 49 },
|
||||
{ 43, 0, NOTHING_block, 45 },
|
||||
{ 44, 0, NOTHING_block, 46 },
|
||||
// landing
|
||||
{ 45, 255, NOTHING_block, 0 }, { 45, HELIPAD1, HELIPAD1_block, 10 },
|
||||
{ 46, 255, NOTHING_block, 0 }, { 46, HELIPAD2, HELIPAD2_block, 11 },
|
||||
// Helicopter -- takeoff
|
||||
{ 47, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 48, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 49, 0, HANGAR2_AREA_block, 50 }, // need to go to hangar when waiting in air
|
||||
{ 50, 0, HANGAR2_AREA_block, 3 },
|
||||
{ MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE
|
||||
};
|
||||
|
||||
// intercontinental
|
||||
static const TileIndexDiffC _airport_depots_intercontinental[] = { { 0, 5 }, { 8, 4 } };
|
||||
static const byte _airport_terminal_intercontinental[] = { 2, 4, 4 };
|
||||
static const byte _airport_helipad_intercontinental[] = { 1, 2 };
|
||||
static const AirportFTAbuildup _airport_fta_intercontinental[] = {
|
||||
{ 0, HANGAR, NOTHING_block, 2 }, { 0, 255, HANGAR1_AREA_block | TERM_GROUP1_block, 0 }, { 0, 255, HANGAR1_AREA_block | TERM_GROUP1_block, 1 }, { 0, TAKEOFF, HANGAR1_AREA_block | TERM_GROUP1_block, 2 }, { 0, 0, 0, 2 },
|
||||
{ 1, HANGAR, NOTHING_block, 3 }, { 1, 255, HANGAR2_AREA_block, 1 }, { 1, 255, HANGAR2_AREA_block, 0 }, { 1, 0, 0, 3 },
|
||||
{ 2, 255, HANGAR1_AREA_block, 0 }, { 2, 255, TERM_GROUP1_block, 0 }, { 2, 255, TERM_GROUP1_block, 1 }, { 2, HANGAR, 0, 0 }, { 2, TAKEOFF, TERM_GROUP1_block, 27 }, { 2, TERM5, 0, 26 }, { 2, TERM6, 0, 26 }, { 2, TERM7, 0, 26 }, { 2, TERM8, 0, 26 }, { 2, HELIPAD1, 0, 26 }, { 2, HELIPAD2, 0, 26 }, { 2, HELITAKEOFF, 0, 74 }, { 2, 0, 0, 27 },
|
||||
{ 3, 255, HANGAR2_AREA_block, 0 }, { 3, HANGAR, 0, 1 }, { 3, HELITAKEOFF, 0, 75 }, { 3, 0, 0, 20 },
|
||||
{ 4, TERM1, TERM1_block, 26 }, { 4, HANGAR, HANGAR1_AREA_block | TERM_GROUP1_block, 26 }, { 4, 0, 0, 26 },
|
||||
{ 5, TERM2, TERM2_block, 27 }, { 5, HANGAR, HANGAR1_AREA_block | TERM_GROUP1_block, 27 }, { 5, 0, 0, 27 },
|
||||
{ 6, TERM3, TERM3_block, 28 }, { 6, HANGAR, HANGAR1_AREA_block | TERM_GROUP1_block, 28 }, { 6, 0, 0, 28 },
|
||||
{ 7, TERM4, TERM4_block, 29 }, { 7, HANGAR, HANGAR1_AREA_block | TERM_GROUP1_block, 29 }, { 7, 0, 0, 29 },
|
||||
{ 8, TERM5, TERM5_block, 18 }, { 8, HANGAR, HANGAR2_AREA_block, 18 }, { 8, 0, 0, 18 },
|
||||
{ 9, TERM6, TERM6_block, 19 }, { 9, HANGAR, HANGAR2_AREA_block, 19 }, { 9, 0, 0, 19 },
|
||||
{ 10, TERM7, TERM7_block, 20 }, { 10, HANGAR, HANGAR2_AREA_block, 20 }, { 10, 0, 0, 20 },
|
||||
{ 11, TERM8, TERM8_block, 21 }, { 11, HANGAR, HANGAR2_AREA_block, 21 }, { 11, 0, 0, 21 },
|
||||
{ 12, HELIPAD1, HELIPAD1_block, 12 }, { 12, HANGAR, 0, 70 }, { 12, HELITAKEOFF, 0, 72 },
|
||||
{ 13, HELIPAD2, HELIPAD2_block, 13 }, { 13, HANGAR, 0, 71 }, { 13, HELITAKEOFF, 0, 73 },
|
||||
{ 14, 0, TERM_GROUP2_ENTER1_block, 15 },
|
||||
{ 15, 0, TERM_GROUP2_ENTER1_block, 16 },
|
||||
{ 16, 0, TERM_GROUP2_ENTER2_block, 17 },
|
||||
{ 17, 0, TERM_GROUP2_ENTER2_block, 18 },
|
||||
{ 18, 255, TERM_GROUP2_block, 0 }, { 18, TERM5, TERM5_block, 8 }, { 18, TAKEOFF, 0, 19 }, { 18, HELITAKEOFF, HELIPAD1_block, 19 }, { 18, 0, TERM_GROUP2_EXIT1_block, 19 },
|
||||
{ 19, 255, TERM_GROUP2_block, 0 }, { 19, TERM6, TERM6_block, 9 }, { 19, TERM5, 0, 18 }, { 19, TAKEOFF, 0, 57 }, { 19, HELITAKEOFF, HELIPAD1_block, 20 }, { 19, 0, TERM_GROUP2_EXIT1_block, 20 }, // add exit to runway out 2
|
||||
{ 20, 255, TERM_GROUP2_block, 0 }, { 20, TERM7, TERM7_block, 10 }, { 20, TERM5, 0, 19 }, { 20, TERM6, 0, 19 }, { 20, HANGAR, HANGAR2_AREA_block, 3 }, { 20, TAKEOFF, 0, 19 }, { 20, 0, TERM_GROUP2_EXIT1_block, 21 },
|
||||
{ 21, 255, TERM_GROUP2_block, 0 }, { 21, TERM8, TERM8_block, 11 }, { 21, HANGAR, HANGAR2_AREA_block, 20 }, { 21, TERM5, 0, 20 }, { 21, TERM6, 0, 20 }, { 21, TERM7, 0, 20 }, { 21, TAKEOFF, 0, 20 }, { 21, 0, TERM_GROUP2_EXIT1_block, 22 },
|
||||
{ 22, 255, TERM_GROUP2_block, 0 }, { 22, HANGAR, 0, 21 }, { 22, TERM5, 0, 21 }, { 22, TERM6, 0, 21 }, { 22, TERM7, 0, 21 }, { 22, TERM8, 0, 21 }, { 22, TAKEOFF, 0, 21 }, { 22, 0, 0, 23 },
|
||||
{ 23, 0, TERM_GROUP2_EXIT1_block, 70 },
|
||||
{ 24, 0, TERM_GROUP2_EXIT2_block, 25 },
|
||||
{ 25, 255, TERM_GROUP2_EXIT2_block, 0 }, { 25, HANGAR, HANGAR1_AREA_block | TERM_GROUP1_block, 29 }, { 25, 0, 0, 29 },
|
||||
{ 26, 255, TERM_GROUP1_block, 0 }, { 26, TERM1, TERM1_block, 4 }, { 26, HANGAR, HANGAR1_AREA_block, 27 }, { 26, TERM5, TERM_GROUP2_ENTER1_block, 14 }, { 26, TERM6, TERM_GROUP2_ENTER1_block, 14 }, { 26, TERM7, TERM_GROUP2_ENTER1_block, 14 }, { 26, TERM8, TERM_GROUP2_ENTER1_block, 14 }, { 26, HELIPAD1, TERM_GROUP2_ENTER1_block, 14 }, { 26, HELIPAD2, TERM_GROUP2_ENTER1_block, 14 }, { 26, HELITAKEOFF, TERM_GROUP2_ENTER1_block, 14 }, { 26, 0, 0, 27 },
|
||||
{ 27, 255, TERM_GROUP1_block, 0 }, { 27, TERM2, TERM2_block, 5 }, { 27, HANGAR, HANGAR1_AREA_block, 2 }, { 27, TERM1, 0, 26 }, { 27, TERM5, 0, 26 }, { 27, TERM6, 0, 26 }, { 27, TERM7, 0, 26 }, { 27, TERM8, 0, 26 }, { 27, HELIPAD1, 0, 14 }, { 27, HELIPAD2, 0, 14 }, { 27, 0, 0, 28 },
|
||||
{ 28, 255, TERM_GROUP1_block, 0 }, { 28, TERM3, TERM3_block, 6 }, { 28, HANGAR, HANGAR1_AREA_block, 27 }, { 28, TERM1, 0, 27 }, { 28, TERM2, 0, 27 }, { 28, TERM4, 0, 29 }, { 28, TERM5, 0, 14 }, { 28, TERM6, 0, 14 }, { 28, TERM7, 0, 14 }, { 28, TERM8, 0, 14 }, { 28, HELIPAD1, 0, 14 }, { 28, HELIPAD2, 0, 14 }, { 28, 0, 0, 29 },
|
||||
{ 29, 255, TERM_GROUP1_block, 0 }, { 29, TERM4, TERM4_block, 7 }, { 29, HANGAR, HANGAR1_AREA_block, 27 }, { 29, TAKEOFF, 0, 30 }, { 29, 0, 0, 28 },
|
||||
{ 30, 0, OUT_WAY_block2, 31 },
|
||||
{ 31, 0, OUT_WAY_block, 32 },
|
||||
// takeoff
|
||||
{ 32, TAKEOFF, RUNWAY_OUT_block, 33 },
|
||||
{ 33, 0, RUNWAY_OUT_block, 34 },
|
||||
{ 34, STARTTAKEOFF, NOTHING_block, 35 },
|
||||
{ 35, ENDTAKEOFF, NOTHING_block, 0 },
|
||||
// landing
|
||||
{ 36, 0, 0, 0 },
|
||||
{ 37, LANDING, RUNWAY_IN_block, 38 },
|
||||
{ 38, 0, RUNWAY_IN_block, 39 },
|
||||
{ 39, 0, RUNWAY_IN_block, 40 },
|
||||
{ 40, ENDLANDING, RUNWAY_IN_block, 41 },
|
||||
{ 41, 0, IN_WAY_block, 42 },
|
||||
{ 42, 255, IN_WAY_block, 0 }, { 42, 255, TERM_GROUP1_block, 0 }, { 42, 255, TERM_GROUP1_block, 1 }, { 42, HANGAR, 0, 2 }, { 42, 0, 0, 26 },
|
||||
// In Air
|
||||
{ 43, 0, 0, 44 },
|
||||
{ 44, FLYING, 0, 45 }, { 44, HELILANDING, 0, 47 }, { 44, LANDING, 0, 69 }, { 44, 0, 0, 45 },
|
||||
{ 45, 0, 0, 46 },
|
||||
{ 46, FLYING, 0, 43 }, { 46, LANDING, 0, 76 }, { 46, 0, 0, 43 },
|
||||
// Helicopter -- stay in air in special place as a buffer to choose from helipads
|
||||
{ 47, HELILANDING, PRE_HELIPAD_block, 48 },
|
||||
{ 48, HELIENDLANDING, PRE_HELIPAD_block, 48 }, { 48, HELIPAD1, 0, 49 }, { 48, HELIPAD2, 0, 50 }, { 48, HANGAR, 0, 55 },
|
||||
{ 49, 0, NOTHING_block, 51 },
|
||||
{ 50, 0, NOTHING_block, 52 },
|
||||
// landing
|
||||
{ 51, 255, NOTHING_block, 0 }, { 51, HELIPAD1, HELIPAD1_block, 12 }, { 51, HANGAR, 0, 55 }, { 51, 0, 0, 12 },
|
||||
{ 52, 255, NOTHING_block, 0 }, { 52, HELIPAD2, HELIPAD2_block, 13 }, { 52, HANGAR, 0, 55 }, { 52, 0, 0, 13 },
|
||||
// Helicopter -- takeoff
|
||||
{ 53, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 54, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 55, 0, HANGAR2_AREA_block, 56 }, // need to go to hangar when waiting in air
|
||||
{ 56, 0, HANGAR2_AREA_block, 3 },
|
||||
// runway 2 out support
|
||||
{ 57, 255, OUT_WAY2_block, 0 }, { 57, TAKEOFF, 0, 58 }, { 57, 0, 0, 58 },
|
||||
{ 58, 0, OUT_WAY2_block, 59 },
|
||||
{ 59, TAKEOFF, RUNWAY_OUT2_block, 60 }, // takeoff
|
||||
{ 60, 0, RUNWAY_OUT2_block, 61 },
|
||||
{ 61, STARTTAKEOFF, NOTHING_block, 62 },
|
||||
{ 62, ENDTAKEOFF, NOTHING_block, 0 },
|
||||
// runway 2 in support
|
||||
{ 63, LANDING, RUNWAY_IN2_block, 64 },
|
||||
{ 64, 0, RUNWAY_IN2_block, 65 },
|
||||
{ 65, 0, RUNWAY_IN2_block, 66 },
|
||||
{ 66, ENDLANDING, RUNWAY_IN2_block, 0 }, { 66, 255, 0, 1 }, { 66, 255, 0, 0 }, { 66, 0, 0, 67 },
|
||||
{ 67, 0, IN_WAY2_block, 68 },
|
||||
{ 68, 255, IN_WAY2_block, 0 }, { 68, 255, TERM_GROUP2_block, 1 }, { 68, 255, TERM_GROUP1_block, 0 }, { 68, HANGAR, HANGAR2_AREA_block, 22 }, { 68, 0, 0, 22 },
|
||||
{ 69, 255, RUNWAY_IN2_block, 0 }, { 69, 0, RUNWAY_IN2_block, 63 },
|
||||
{ 70, 255, TERM_GROUP2_EXIT1_block, 0 }, { 70, HELIPAD1, HELIPAD1_block, 12 }, { 70, HELITAKEOFF, HELIPAD1_block, 12 }, { 70, 0, 0, 71 },
|
||||
{ 71, 255, TERM_GROUP2_EXIT1_block, 0 }, { 71, HELIPAD2, HELIPAD2_block, 13 }, { 71, HELITAKEOFF, HELIPAD1_block, 12 }, { 71, 0, 0, 24 },
|
||||
{ 72, 0, HELIPAD1_block, 53 },
|
||||
{ 73, 0, HELIPAD2_block, 54 },
|
||||
{ 74, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 75, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 76, 255, RUNWAY_IN_block, 0 }, { 76, 0, RUNWAY_IN_block, 37 },
|
||||
{ MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE
|
||||
};
|
||||
|
||||
|
||||
// heliports, oilrigs don't have depots
|
||||
static const byte _airport_helipad_heliport_oilrig[] = { 1, 1 };
|
||||
static const AirportFTAbuildup _airport_fta_heliport_oilrig[] = {
|
||||
{ 0, HELIPAD1, HELIPAD1_block, 1 },
|
||||
{ 1, HELITAKEOFF, NOTHING_block, 0 }, // takeoff
|
||||
{ 2, 255, AIRPORT_BUSY_block, 0 }, { 2, HELILANDING, 0, 3 }, { 2, HELITAKEOFF, 0, 1 },
|
||||
{ 3, HELILANDING, AIRPORT_BUSY_block, 4 },
|
||||
{ 4, HELIENDLANDING, AIRPORT_BUSY_block, 4 }, { 4, HELIPAD1, HELIPAD1_block, 0 }, { 4, HELITAKEOFF, 0, 2 },
|
||||
// In Air
|
||||
{ 5, 0, NOTHING_block, 6 },
|
||||
{ 6, 0, NOTHING_block, 7 },
|
||||
{ 7, 0, NOTHING_block, 8 },
|
||||
{ 8, FLYING, NOTHING_block, 5 }, { 8, HELILANDING, HELIPAD1_block, 2 }, // landing
|
||||
{ MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE
|
||||
};
|
||||
|
||||
// helidepots
|
||||
static const TileIndexDiffC _airport_depots_helidepot[] = { { 1, 0 } };
|
||||
static const byte _airport_helipad_helidepot[] = { 1, 1 };
|
||||
static const AirportFTAbuildup _airport_fta_helidepot[] = {
|
||||
{ 0, HANGAR, NOTHING_block, 1 },
|
||||
{ 1, 255, HANGAR2_AREA_block, 0 }, { 1, HANGAR, 0, 0 }, { 1, HELIPAD1, HELIPAD1_block, 14 }, { 1, HELITAKEOFF, 0, 15 }, { 1, 0, 0, 0 },
|
||||
{ 2, FLYING, NOTHING_block, 3 }, { 2, HELILANDING, PRE_HELIPAD_block, 7 }, { 2, HANGAR, 0, 12 }, { 2, HELITAKEOFF, NOTHING_block, 16 },
|
||||
// In Air
|
||||
{ 3, 0, NOTHING_block, 4 },
|
||||
{ 4, 0, NOTHING_block, 5 },
|
||||
{ 5, 0, NOTHING_block, 6 },
|
||||
{ 6, 0, NOTHING_block, 2 },
|
||||
// Helicopter -- stay in air in special place as a buffer to choose from helipads
|
||||
{ 7, HELILANDING, PRE_HELIPAD_block, 8 },
|
||||
{ 8, HELIENDLANDING, PRE_HELIPAD_block, 8 }, { 8, HELIPAD1, 0, 9 }, { 8, HANGAR, 0, 12 }, { 8, 0, 0, 2 },
|
||||
{ 9, 0, NOTHING_block, 10 },
|
||||
// landing
|
||||
{ 10, 255, NOTHING_block, 10 }, { 10, HELIPAD1, HELIPAD1_block, 14 }, { 10, HANGAR, 0, 1 }, { 10, 0, 0, 14 },
|
||||
// Helicopter -- takeoff
|
||||
{ 11, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 12, 0, HANGAR2_AREA_block, 13 }, // need to go to hangar when waiting in air
|
||||
{ 13, 0, HANGAR2_AREA_block, 1 },
|
||||
{ 14, HELIPAD1, HELIPAD1_block, 14 }, { 14, HANGAR, 0, 1 }, { 14, HELITAKEOFF, 0, 17 },
|
||||
{ 15, HELITAKEOFF, NOTHING_block, 0 }, // takeoff outside depot
|
||||
{ 16, HELITAKEOFF, 0, 14 },
|
||||
{ 17, 0, NOTHING_block, 11 },
|
||||
{ MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE
|
||||
};
|
||||
|
||||
// helistation
|
||||
static const TileIndexDiffC _airport_depots_helistation[] = { { 0, 0 } };
|
||||
static const byte _airport_helipad_helistation[] = { 1, 3 };
|
||||
static const AirportFTAbuildup _airport_fta_helistation[] = {
|
||||
{ 0, HANGAR, NOTHING_block, 8 }, { 0, HELIPAD1, 0, 1 }, { 0, HELIPAD2, 0, 1 }, { 0, HELIPAD3, 0, 1 }, { 0, HELITAKEOFF, 0, 1 }, { 0, 0, 0, 0 },
|
||||
{ 1, 255, HANGAR2_AREA_block, 0 }, { 1, HANGAR, 0, 0 }, { 1, HELITAKEOFF, 0, 3 }, { 1, 0, 0, 4 },
|
||||
// landing
|
||||
{ 2, FLYING, NOTHING_block, 28 }, { 2, HELILANDING, 0, 15 }, { 2, 0, 0, 28 },
|
||||
// helicopter side
|
||||
{ 3, HELITAKEOFF, NOTHING_block, 0 }, // helitakeoff outside hangar2
|
||||
{ 4, 255, TAXIWAY_BUSY_block, 0 }, { 4, HANGAR, HANGAR2_AREA_block, 1 }, { 4, HELITAKEOFF, 0, 1 }, { 4, 0, 0, 5 },
|
||||
{ 5, 255, TAXIWAY_BUSY_block, 0 }, { 5, HELIPAD1, HELIPAD1_block, 6 }, { 5, HELIPAD2, HELIPAD2_block, 7 }, { 5, HELIPAD3, HELIPAD3_block, 8 }, { 5, 0, 0, 4 },
|
||||
{ 6, HELIPAD1, HELIPAD1_block, 5 }, { 6, HANGAR, HANGAR2_AREA_block, 5 }, { 6, HELITAKEOFF, 0, 9 }, { 6, 0, 0, 6 },
|
||||
{ 7, HELIPAD2, HELIPAD2_block, 5 }, { 7, HANGAR, HANGAR2_AREA_block, 5 }, { 7, HELITAKEOFF, 0, 10 }, { 7, 0, 0, 7 },
|
||||
{ 8, HELIPAD3, HELIPAD3_block, 5 }, { 8, HANGAR, HANGAR2_AREA_block, 5 }, { 8, HELITAKEOFF, 0, 11 }, { 8, 0, 0, 8 },
|
||||
{ 9, 0, HELIPAD1_block, 12 },
|
||||
{ 10, 0, HELIPAD2_block, 13 },
|
||||
{ 11, 0, HELIPAD3_block, 14 },
|
||||
{ 12, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 13, HELITAKEOFF, NOTHING_block, 0 },
|
||||
{ 14, HELITAKEOFF, NOTHING_block, 0 },
|
||||
// heli - in flight moves
|
||||
{ 15, HELILANDING, PRE_HELIPAD_block, 16 },
|
||||
{ 16, HELIENDLANDING, PRE_HELIPAD_block, 16 }, { 16, HELIPAD1, 0, 17 }, { 16, HELIPAD2, 0, 18 }, { 16, HELIPAD3, 0, 19 }, { 16, HANGAR, 0, 23 },
|
||||
{ 17, 0, NOTHING_block, 20 },
|
||||
{ 18, 0, NOTHING_block, 21 },
|
||||
{ 19, 0, NOTHING_block, 22 },
|
||||
// heli landing
|
||||
{ 20, 255, NOTHING_block, 0 }, { 20, HELIPAD1, HELIPAD1_block, 6 }, { 20, HANGAR, 0, 23 }, { 20, 0, 0, 6 },
|
||||
{ 21, 255, NOTHING_block, 0 }, { 21, HELIPAD2, HELIPAD2_block, 7 }, { 21, HANGAR, 0, 23 }, { 21, 0, 0, 7 },
|
||||
{ 22, 255, NOTHING_block, 0 }, { 22, HELIPAD3, HELIPAD3_block, 8 }, { 22, HANGAR, 0, 23 }, { 22, 0, 0, 8 },
|
||||
{ 23, 0, HANGAR2_AREA_block, 24 }, // need to go to helihangar when waiting in air
|
||||
{ 24, 0, HANGAR2_AREA_block, 1 },
|
||||
{ 25, 0, NOTHING_block, 26 },
|
||||
{ 26, 0, NOTHING_block, 27 },
|
||||
{ 27, 0, NOTHING_block, 2 },
|
||||
{ 28, 0, NOTHING_block, 29 },
|
||||
{ 29, 0, NOTHING_block, 30 },
|
||||
{ 30, 0, NOTHING_block, 31 },
|
||||
{ 31, 0, NOTHING_block, 32 },
|
||||
{ 32, 0, NOTHING_block, 25 },
|
||||
{ MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE
|
||||
};
|
||||
|
||||
|
||||
static const AirportMovingData * const _airport_moving_datas[] = {
|
||||
_airport_moving_data_country, // Country Airfield (small) 4x3
|
||||
_airport_moving_data_town, // City Airport (large) 6x6
|
||||
_airport_moving_data_heliport, // Heliport
|
||||
_airport_moving_data_metropolitan, // Metropolitain Airport (large) - 2 runways
|
||||
_airport_moving_data_international, // International Airport (xlarge) - 2 runways
|
||||
_airport_moving_data_commuter, // Commuter Airfield (small) 5x4
|
||||
_airport_moving_data_helidepot, // Helidepot
|
||||
_airport_moving_data_intercontinental, // Intercontinental Airport (xxlarge) - 4 runways
|
||||
_airport_moving_data_helistation, // Helistation
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
_airport_moving_data_oilrig // Oilrig
|
||||
};
|
||||
|
||||
#endif /* AIRPORT_MOVEMENT_H */
|
||||
296
src/aystar.c
Normal file
296
src/aystar.c
Normal file
@@ -0,0 +1,296 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file has the core function for AyStar
|
||||
* AyStar is a fast pathfinding routine and is used for things like
|
||||
* AI_pathfinding and Train_pathfinding.
|
||||
* For more information about AyStar (A* Algorithm), you can look at
|
||||
* http://en.wikipedia.org/wiki/A-star_search_algorithm
|
||||
*/
|
||||
|
||||
/*
|
||||
* Friendly reminder:
|
||||
* Call (AyStar).free() when you are done with Aystar. It reserves a lot of memory
|
||||
* And when not free'd, it can cause system-crashes.
|
||||
* Also remember that when you stop an algorithm before it is finished, your
|
||||
* should call clear() yourself!
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "aystar.h"
|
||||
|
||||
int _aystar_stats_open_size;
|
||||
int _aystar_stats_closed_size;
|
||||
|
||||
// This looks in the Hash if a node exists in ClosedList
|
||||
// If so, it returns the PathNode, else NULL
|
||||
static PathNode* AyStarMain_ClosedList_IsInList(AyStar *aystar, const AyStarNode *node)
|
||||
{
|
||||
return (PathNode*)Hash_Get(&aystar->ClosedListHash, node->tile, node->direction);
|
||||
}
|
||||
|
||||
// This adds a node to the ClosedList
|
||||
// It makes a copy of the data
|
||||
static void AyStarMain_ClosedList_Add(AyStar *aystar, const PathNode *node)
|
||||
{
|
||||
// Add a node to the ClosedList
|
||||
PathNode *new_node = malloc(sizeof(*new_node));
|
||||
*new_node = *node;
|
||||
Hash_Set(&aystar->ClosedListHash, node->node.tile, node->node.direction, new_node);
|
||||
}
|
||||
|
||||
// Checks if a node is in the OpenList
|
||||
// If so, it returns the OpenListNode, else NULL
|
||||
static OpenListNode *AyStarMain_OpenList_IsInList(AyStar *aystar, const AyStarNode *node)
|
||||
{
|
||||
return (OpenListNode*)Hash_Get(&aystar->OpenListHash, node->tile, node->direction);
|
||||
}
|
||||
|
||||
// Gets the best node from OpenList
|
||||
// returns the best node, or NULL of none is found
|
||||
// Also it deletes the node from the OpenList
|
||||
static OpenListNode *AyStarMain_OpenList_Pop(AyStar *aystar)
|
||||
{
|
||||
// Return the item the Queue returns.. the best next OpenList item.
|
||||
OpenListNode *res = (OpenListNode*)aystar->OpenListQueue.pop(&aystar->OpenListQueue);
|
||||
if (res != NULL) {
|
||||
Hash_Delete(&aystar->OpenListHash, res->path.node.tile, res->path.node.direction);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Adds a node to the OpenList
|
||||
// It makes a copy of node, and puts the pointer of parent in the struct
|
||||
static void AyStarMain_OpenList_Add(AyStar *aystar, PathNode *parent, const AyStarNode *node, int f, int g)
|
||||
{
|
||||
// Add a new Node to the OpenList
|
||||
OpenListNode *new_node = malloc(sizeof(*new_node));
|
||||
new_node->g = g;
|
||||
new_node->path.parent = parent;
|
||||
new_node->path.node = *node;
|
||||
Hash_Set(&aystar->OpenListHash, node->tile, node->direction, new_node);
|
||||
|
||||
// Add it to the queue
|
||||
aystar->OpenListQueue.push(&aystar->OpenListQueue, new_node, f);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks one tile and calculate his f-value
|
||||
* return values:
|
||||
* AYSTAR_DONE : indicates we are done
|
||||
*/
|
||||
int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent)
|
||||
{
|
||||
int new_f, new_g, new_h;
|
||||
PathNode *closedlist_parent;
|
||||
OpenListNode *check;
|
||||
|
||||
// Check the new node against the ClosedList
|
||||
if (AyStarMain_ClosedList_IsInList(aystar, current) != NULL) return AYSTAR_DONE;
|
||||
|
||||
// Calculate the G-value for this node
|
||||
new_g = aystar->CalculateG(aystar, current, parent);
|
||||
// If the value was INVALID_NODE, we don't do anything with this node
|
||||
if (new_g == AYSTAR_INVALID_NODE) return AYSTAR_DONE;
|
||||
|
||||
// There should not be given any other error-code..
|
||||
assert(new_g >= 0);
|
||||
// Add the parent g-value to the new g-value
|
||||
new_g += parent->g;
|
||||
if (aystar->max_path_cost != 0 && (uint)new_g > aystar->max_path_cost) return AYSTAR_DONE;
|
||||
|
||||
// Calculate the h-value
|
||||
new_h = aystar->CalculateH(aystar, current, parent);
|
||||
// There should not be given any error-code..
|
||||
assert(new_h >= 0);
|
||||
|
||||
// The f-value if g + h
|
||||
new_f = new_g + new_h;
|
||||
|
||||
// Get the pointer to the parent in the ClosedList (the currentone is to a copy of the one in the OpenList)
|
||||
closedlist_parent = AyStarMain_ClosedList_IsInList(aystar, &parent->path.node);
|
||||
|
||||
// Check if this item is already in the OpenList
|
||||
check = AyStarMain_OpenList_IsInList(aystar, current);
|
||||
if (check != NULL) {
|
||||
uint i;
|
||||
// Yes, check if this g value is lower..
|
||||
if (new_g > check->g) return AYSTAR_DONE;
|
||||
aystar->OpenListQueue.del(&aystar->OpenListQueue, check, 0);
|
||||
// It is lower, so change it to this item
|
||||
check->g = new_g;
|
||||
check->path.parent = closedlist_parent;
|
||||
/* Copy user data, will probably have changed */
|
||||
for (i = 0; i < lengthof(current->user_data); i++) {
|
||||
check->path.node.user_data[i] = current->user_data[i];
|
||||
}
|
||||
// Readd him in the OpenListQueue
|
||||
aystar->OpenListQueue.push(&aystar->OpenListQueue, check, new_f);
|
||||
} else {
|
||||
// A new node, add him to the OpenList
|
||||
AyStarMain_OpenList_Add(aystar, closedlist_parent, current, new_f, new_g);
|
||||
}
|
||||
|
||||
return AYSTAR_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is the core of AyStar. It handles one item and checks
|
||||
* his neighbour items. If they are valid, they are added to be checked too.
|
||||
* return values:
|
||||
* AYSTAR_EMPTY_OPENLIST : indicates all items are tested, and no path
|
||||
* has been found.
|
||||
* AYSTAR_LIMIT_REACHED : Indicates that the max_nodes limit has been
|
||||
* reached.
|
||||
* AYSTAR_FOUND_END_NODE : indicates we found the end. Path_found now is true, and in path is the path found.
|
||||
* AYSTAR_STILL_BUSY : indicates we have done this tile, did not found the path yet, and have items left to try.
|
||||
*/
|
||||
int AyStarMain_Loop(AyStar *aystar)
|
||||
{
|
||||
int i, r;
|
||||
|
||||
// Get the best node from OpenList
|
||||
OpenListNode *current = AyStarMain_OpenList_Pop(aystar);
|
||||
// If empty, drop an error
|
||||
if (current == NULL) return AYSTAR_EMPTY_OPENLIST;
|
||||
|
||||
// Check for end node and if found, return that code
|
||||
if (aystar->EndNodeCheck(aystar, current) == AYSTAR_FOUND_END_NODE) {
|
||||
if (aystar->FoundEndNode != NULL)
|
||||
aystar->FoundEndNode(aystar, current);
|
||||
free(current);
|
||||
return AYSTAR_FOUND_END_NODE;
|
||||
}
|
||||
|
||||
// Add the node to the ClosedList
|
||||
AyStarMain_ClosedList_Add(aystar, ¤t->path);
|
||||
|
||||
// Load the neighbours
|
||||
aystar->GetNeighbours(aystar, current);
|
||||
|
||||
// Go through all neighbours
|
||||
for (i = 0; i < aystar->num_neighbours; i++) {
|
||||
// Check and add them to the OpenList if needed
|
||||
r = aystar->checktile(aystar, &aystar->neighbours[i], current);
|
||||
}
|
||||
|
||||
// Free the node
|
||||
free(current);
|
||||
|
||||
if (aystar->max_search_nodes != 0 && Hash_Size(&aystar->ClosedListHash) >= aystar->max_search_nodes) {
|
||||
/* We've expanded enough nodes */
|
||||
return AYSTAR_LIMIT_REACHED;
|
||||
} else {
|
||||
// Return that we are still busy
|
||||
return AYSTAR_STILL_BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function frees the memory it allocated
|
||||
*/
|
||||
void AyStarMain_Free(AyStar *aystar)
|
||||
{
|
||||
aystar->OpenListQueue.free(&aystar->OpenListQueue, false);
|
||||
/* 2nd argument above is false, below is true, to free the values only
|
||||
* once */
|
||||
delete_Hash(&aystar->OpenListHash, true);
|
||||
delete_Hash(&aystar->ClosedListHash, true);
|
||||
#ifdef AYSTAR_DEBUG
|
||||
printf("[AyStar] Memory free'd\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* This function make the memory go back to zero
|
||||
* This function should be called when you are using the same instance again.
|
||||
*/
|
||||
void AyStarMain_Clear(AyStar *aystar)
|
||||
{
|
||||
// Clean the Queue, but not the elements within. That will be done by
|
||||
// the hash.
|
||||
aystar->OpenListQueue.clear(&aystar->OpenListQueue, false);
|
||||
// Clean the hashes
|
||||
clear_Hash(&aystar->OpenListHash, true);
|
||||
clear_Hash(&aystar->ClosedListHash, true);
|
||||
|
||||
#ifdef AYSTAR_DEBUG
|
||||
printf("[AyStar] Cleared AyStar\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the function you call to run AyStar.
|
||||
* return values:
|
||||
* AYSTAR_FOUND_END_NODE : indicates we found an end node.
|
||||
* AYSTAR_NO_PATH : indicates that there was no path found.
|
||||
* AYSTAR_STILL_BUSY : indicates we have done some checked, that we did not found the path yet, and that we still have items left to try.
|
||||
* When the algorithm is done (when the return value is not AYSTAR_STILL_BUSY)
|
||||
* aystar->clear() is called. Note that when you stop the algorithm halfway,
|
||||
* you should still call clear() yourself!
|
||||
*/
|
||||
int AyStarMain_Main(AyStar *aystar) {
|
||||
int r, i = 0;
|
||||
// Loop through the OpenList
|
||||
// Quit if result is no AYSTAR_STILL_BUSY or is more than loops_per_tick
|
||||
while ((r = aystar->loop(aystar)) == AYSTAR_STILL_BUSY && (aystar->loops_per_tick == 0 || ++i < aystar->loops_per_tick)) { }
|
||||
#ifdef AYSTAR_DEBUG
|
||||
switch (r) {
|
||||
case AYSTAR_FOUND_END_NODE: printf("[AyStar] Found path!\n"); break;
|
||||
case AYSTAR_EMPTY_OPENLIST: printf("[AyStar] OpenList run dry, no path found\n"); break;
|
||||
case AYSTAR_LIMIT_REACHED: printf("[AyStar] Exceeded search_nodes, no path found\n"); break;
|
||||
default: break;
|
||||
}
|
||||
#endif
|
||||
if (r != AYSTAR_STILL_BUSY) {
|
||||
/* We're done, clean up */
|
||||
_aystar_stats_open_size = aystar->OpenListHash.size;
|
||||
_aystar_stats_closed_size = aystar->ClosedListHash.size;
|
||||
aystar->clear(aystar);
|
||||
}
|
||||
|
||||
switch (r) {
|
||||
case AYSTAR_FOUND_END_NODE: return AYSTAR_FOUND_END_NODE;
|
||||
case AYSTAR_EMPTY_OPENLIST:
|
||||
case AYSTAR_LIMIT_REACHED: return AYSTAR_NO_PATH;
|
||||
default: return AYSTAR_STILL_BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds a node from where to start an algorithm. Multiple nodes can be added
|
||||
* if wanted. You should make sure that clear() is called before adding nodes
|
||||
* if the AyStar has been used before (though the normal main loop calls
|
||||
* clear() automatically when the algorithm finishes
|
||||
* g is the cost for starting with this node.
|
||||
*/
|
||||
void AyStarMain_AddStartNode(AyStar *aystar, AyStarNode *start_node, uint g)
|
||||
{
|
||||
#ifdef AYSTAR_DEBUG
|
||||
printf("[AyStar] Starting A* Algorithm from node (%d, %d, %d)\n",
|
||||
TileX(start_node->tile), TileY(start_node->tile), start_node->direction);
|
||||
#endif
|
||||
AyStarMain_OpenList_Add(aystar, NULL, start_node, 0, g);
|
||||
}
|
||||
|
||||
void init_AyStar(AyStar *aystar, Hash_HashProc hash, uint num_buckets)
|
||||
{
|
||||
// Allocated the Hash for the OpenList and ClosedList
|
||||
init_Hash(&aystar->OpenListHash, hash, num_buckets);
|
||||
init_Hash(&aystar->ClosedListHash, hash, num_buckets);
|
||||
|
||||
// Set up our sorting queue
|
||||
// BinaryHeap allocates a block of 1024 nodes
|
||||
// When thatone gets full it reserves an otherone, till this number
|
||||
// That is why it can stay this high
|
||||
init_BinaryHeap(&aystar->OpenListQueue, 102400);
|
||||
|
||||
aystar->addstart = AyStarMain_AddStartNode;
|
||||
aystar->main = AyStarMain_Main;
|
||||
aystar->loop = AyStarMain_Loop;
|
||||
aystar->free = AyStarMain_Free;
|
||||
aystar->clear = AyStarMain_Clear;
|
||||
aystar->checktile = AyStarMain_CheckTile;
|
||||
}
|
||||
179
src/aystar.h
Normal file
179
src/aystar.h
Normal file
@@ -0,0 +1,179 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file has the header for AyStar
|
||||
* AyStar is a fast pathfinding routine and is used for things like
|
||||
* AI_pathfinding and Train_pathfinding.
|
||||
* For more information about AyStar (A* Algorithm), you can look at
|
||||
* http://en.wikipedia.org/wiki/A-star_search_algorithm
|
||||
*/
|
||||
|
||||
#ifndef AYSTAR_H
|
||||
#define AYSTAR_H
|
||||
|
||||
#include "queue.h"
|
||||
|
||||
//#define AYSTAR_DEBUG
|
||||
enum {
|
||||
AYSTAR_FOUND_END_NODE,
|
||||
AYSTAR_EMPTY_OPENLIST,
|
||||
AYSTAR_STILL_BUSY,
|
||||
AYSTAR_NO_PATH,
|
||||
AYSTAR_LIMIT_REACHED,
|
||||
AYSTAR_DONE
|
||||
};
|
||||
|
||||
enum{
|
||||
AYSTAR_INVALID_NODE = -1,
|
||||
};
|
||||
|
||||
typedef struct AyStarNode AyStarNode;
|
||||
struct AyStarNode {
|
||||
TileIndex tile;
|
||||
uint direction;
|
||||
uint user_data[2];
|
||||
};
|
||||
|
||||
// The resulting path has nodes looking like this.
|
||||
typedef struct PathNode PathNode;
|
||||
struct PathNode {
|
||||
AyStarNode node;
|
||||
// The parent of this item
|
||||
PathNode *parent;
|
||||
};
|
||||
|
||||
// For internal use only
|
||||
// We do not save the h-value, because it is only needed to calculate the f-value.
|
||||
// h-value should _always_ be the distance left to the end-tile.
|
||||
typedef struct OpenListNode OpenListNode;
|
||||
struct OpenListNode {
|
||||
int g;
|
||||
PathNode path;
|
||||
};
|
||||
|
||||
typedef struct AyStar AyStar;
|
||||
/*
|
||||
* This function is called to check if the end-tile is found
|
||||
* return values can be:
|
||||
* AYSTAR_FOUND_END_NODE : indicates this is the end tile
|
||||
* AYSTAR_DONE : indicates this is not the end tile (or direction was wrong)
|
||||
*/
|
||||
/*
|
||||
* The 2nd parameter should be OpenListNode, and NOT AyStarNode. AyStarNode is
|
||||
* part of OpenListNode and so it could be accessed without any problems.
|
||||
* The good part about OpenListNode is, and how AIs use it, that you can
|
||||
* access the parent of the current node, and so check if you, for example
|
||||
* don't try to enter the file tile with a 90-degree curve. So please, leave
|
||||
* this an OpenListNode, it works just fine -- TrueLight
|
||||
*/
|
||||
typedef int32 AyStar_EndNodeCheck(AyStar *aystar, OpenListNode *current);
|
||||
|
||||
/*
|
||||
* This function is called to calculate the G-value for AyStar Algorithm.
|
||||
* return values can be:
|
||||
* AYSTAR_INVALID_NODE : indicates an item is not valid (e.g.: unwalkable)
|
||||
* Any value >= 0 : the g-value for this tile
|
||||
*/
|
||||
typedef int32 AyStar_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
|
||||
|
||||
/*
|
||||
* This function is called to calculate the H-value for AyStar Algorithm.
|
||||
* Mostly, this must result the distance (Manhattan way) between the
|
||||
* current point and the end point
|
||||
* return values can be:
|
||||
* Any value >= 0 : the h-value for this tile
|
||||
*/
|
||||
typedef int32 AyStar_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
|
||||
|
||||
/*
|
||||
* This function request the tiles around the current tile and put them in tiles_around
|
||||
* tiles_around is never resetted, so if you are not using directions, just leave it alone.
|
||||
* Warning: never add more tiles_around than memory allocated for it.
|
||||
*/
|
||||
typedef void AyStar_GetNeighbours(AyStar *aystar, OpenListNode *current);
|
||||
|
||||
/*
|
||||
* If the End Node is found, this function is called.
|
||||
* It can do, for example, calculate the route and put that in an array
|
||||
*/
|
||||
typedef void AyStar_FoundEndNode(AyStar *aystar, OpenListNode *current);
|
||||
|
||||
// For internal use, see aystar.c
|
||||
typedef void AyStar_AddStartNode(AyStar *aystar, AyStarNode *start_node, uint g);
|
||||
typedef int AyStar_Main(AyStar *aystar);
|
||||
typedef int AyStar_Loop(AyStar *aystar);
|
||||
typedef int AyStar_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
|
||||
typedef void AyStar_Free(AyStar *aystar);
|
||||
typedef void AyStar_Clear(AyStar *aystar);
|
||||
|
||||
struct AyStar {
|
||||
/* These fields should be filled before initting the AyStar, but not changed
|
||||
* afterwards (except for user_data and user_path)! (free and init again to change them) */
|
||||
|
||||
/* These should point to the application specific routines that do the
|
||||
* actual work */
|
||||
AyStar_CalculateG *CalculateG;
|
||||
AyStar_CalculateH *CalculateH;
|
||||
AyStar_GetNeighbours *GetNeighbours;
|
||||
AyStar_EndNodeCheck *EndNodeCheck;
|
||||
AyStar_FoundEndNode *FoundEndNode;
|
||||
|
||||
/* These are completely untouched by AyStar, they can be accesed by
|
||||
* the application specific routines to input and output data.
|
||||
* user_path should typically contain data about the resulting path
|
||||
* afterwards, user_target should typically contain information about
|
||||
* what where looking for, and user_data can contain just about
|
||||
* everything */
|
||||
void *user_path;
|
||||
void *user_target;
|
||||
uint user_data[10];
|
||||
|
||||
/* How many loops are there called before AyStarMain_Main gives
|
||||
* control back to the caller. 0 = until done */
|
||||
byte loops_per_tick;
|
||||
/* If the g-value goes over this number, it stops searching
|
||||
* 0 = infinite */
|
||||
uint max_path_cost;
|
||||
/* The maximum amount of nodes that will be expanded, 0 = infinite */
|
||||
uint max_search_nodes;
|
||||
|
||||
/* These should be filled with the neighbours of a tile by
|
||||
* GetNeighbours */
|
||||
AyStarNode neighbours[12];
|
||||
byte num_neighbours;
|
||||
|
||||
/* These will contain the methods for manipulating the AyStar. Only
|
||||
* main() should be called externally */
|
||||
AyStar_AddStartNode *addstart;
|
||||
AyStar_Main *main;
|
||||
AyStar_Loop *loop;
|
||||
AyStar_Free *free;
|
||||
AyStar_Clear *clear;
|
||||
AyStar_CheckTile *checktile;
|
||||
|
||||
/* These will contain the open and closed lists */
|
||||
|
||||
/* The actual closed list */
|
||||
Hash ClosedListHash;
|
||||
/* The open queue */
|
||||
Queue OpenListQueue;
|
||||
/* An extra hash to speed up the process of looking up an element in
|
||||
* the open list */
|
||||
Hash OpenListHash;
|
||||
};
|
||||
|
||||
|
||||
void AyStarMain_AddStartNode(AyStar *aystar, AyStarNode *start_node, uint g);
|
||||
int AyStarMain_Main(AyStar *aystar);
|
||||
int AyStarMain_Loop(AyStar *aystar);
|
||||
int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
|
||||
void AyStarMain_Free(AyStar *aystar);
|
||||
void AyStarMain_Clear(AyStar *aystar);
|
||||
|
||||
/* Initialize an AyStar. You should fill all appropriate fields before
|
||||
* callling init_AyStar (see the declaration of AyStar for which fields are
|
||||
* internal */
|
||||
void init_AyStar(AyStar *aystar, Hash_HashProc hash, uint num_buckets);
|
||||
|
||||
|
||||
#endif /* AYSTAR_H */
|
||||
378
src/bmp.c
Normal file
378
src/bmp.c
Normal file
@@ -0,0 +1,378 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "gfx.h"
|
||||
#include "bmp.h"
|
||||
#include "macros.h"
|
||||
|
||||
void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file) {
|
||||
buffer->pos = -1;
|
||||
buffer->file = file;
|
||||
buffer->read = 0;
|
||||
buffer->real_pos = ftell(file);
|
||||
}
|
||||
|
||||
static inline void AdvanceBuffer(BmpBuffer *buffer)
|
||||
{
|
||||
buffer->read = (int)fread(buffer->data, 1, BMP_BUFFER_SIZE, buffer->file);
|
||||
buffer->pos = 0;
|
||||
}
|
||||
|
||||
static inline bool EndOfBuffer(BmpBuffer *buffer)
|
||||
{
|
||||
if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer);
|
||||
return buffer->pos == buffer->read;
|
||||
}
|
||||
|
||||
static inline byte ReadByte(BmpBuffer *buffer)
|
||||
{
|
||||
if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer);
|
||||
buffer->real_pos++;
|
||||
return buffer->data[buffer->pos++];
|
||||
}
|
||||
|
||||
static inline uint16 ReadWord(BmpBuffer *buffer)
|
||||
{
|
||||
uint16 var = ReadByte(buffer);
|
||||
return var | (ReadByte(buffer) << 8);
|
||||
}
|
||||
|
||||
static inline uint32 ReadDword(BmpBuffer *buffer)
|
||||
{
|
||||
uint32 var = ReadWord(buffer);
|
||||
return var | (ReadWord(buffer) << 16);
|
||||
}
|
||||
|
||||
static inline void SkipBytes(BmpBuffer *buffer, int bytes)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < bytes; i++) ReadByte(buffer);
|
||||
}
|
||||
|
||||
static inline void SetStreamOffset(BmpBuffer *buffer, int offset)
|
||||
{
|
||||
fseek(buffer->file, offset, SEEK_SET);
|
||||
buffer->pos = -1;
|
||||
buffer->real_pos = offset;
|
||||
AdvanceBuffer(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 1 bpp uncompressed bitmap
|
||||
* The bitmap is converted to a 8 bpp bitmap
|
||||
*/
|
||||
static inline bool BmpRead1(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
|
||||
{
|
||||
uint x, y, i;
|
||||
byte pad = GB(4 - info->width / 8, 0, 2);
|
||||
byte *pixel_row;
|
||||
byte b;
|
||||
for (y = info->height; y > 0; y--) {
|
||||
x = 0;
|
||||
pixel_row = &data->bitmap[(y - 1) * info->width];
|
||||
while (x < info->width) {
|
||||
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
|
||||
b = ReadByte(buffer);
|
||||
for (i = 8; i > 0; i--) {
|
||||
if (x < info->width) *pixel_row++ = GB(b, i - 1, 1);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
/* Padding for 32 bit align */
|
||||
SkipBytes(buffer, pad);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 4 bpp uncompressed bitmap
|
||||
* The bitmap is converted to a 8 bpp bitmap
|
||||
*/
|
||||
static inline bool BmpRead4(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
|
||||
{
|
||||
uint x, y;
|
||||
byte pad = GB(4 - info->width / 2, 0, 2);
|
||||
byte *pixel_row;
|
||||
byte b;
|
||||
for (y = info->height; y > 0; y--) {
|
||||
x = 0;
|
||||
pixel_row = &data->bitmap[(y - 1) * info->width];
|
||||
while (x < info->width) {
|
||||
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
|
||||
b = ReadByte(buffer);
|
||||
*pixel_row++ = GB(b, 4, 4);
|
||||
x++;
|
||||
if (x < info->width) {
|
||||
*pixel_row++ = GB(b, 0, 4);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
/* Padding for 32 bit align */
|
||||
SkipBytes(buffer, pad);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 4-bit RLE compressed bitmap
|
||||
* The bitmap is converted to a 8 bpp bitmap
|
||||
*/
|
||||
static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
|
||||
{
|
||||
uint i;
|
||||
uint x = 0;
|
||||
uint y = info->height - 1;
|
||||
byte n, c, b;
|
||||
byte *pixel = &data->bitmap[y * info->width];
|
||||
while (y != 0 || x < info->width) {
|
||||
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
|
||||
n = ReadByte(buffer);
|
||||
c = ReadByte(buffer);
|
||||
if (n == 0) {
|
||||
switch (c) {
|
||||
case 0: // end of line
|
||||
x = 0;
|
||||
pixel = &data->bitmap[--y * info->width];
|
||||
break;
|
||||
case 1: // end of bitmap
|
||||
x = info->width;
|
||||
y = 0;
|
||||
pixel = NULL;
|
||||
break;
|
||||
case 2: // delta
|
||||
x += ReadByte(buffer);
|
||||
i = ReadByte(buffer);
|
||||
if (x >= info->width || (y == 0 && i > 0)) return false;
|
||||
y -= i;
|
||||
pixel = &data->bitmap[y * info->width + x];
|
||||
break;
|
||||
default: // uncompressed
|
||||
i = 0;
|
||||
while (i++ < c) {
|
||||
if (EndOfBuffer(buffer) || x >= info->width) return false;
|
||||
b = ReadByte(buffer);
|
||||
*pixel++ = GB(b, 4, 4);
|
||||
x++;
|
||||
if (x < info->width && i++ < c) {
|
||||
*pixel++ = GB(b, 0, 4);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
/* Padding for 16 bit align */
|
||||
SkipBytes(buffer, ((c + 1) / 2) % 2);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
i = 0;
|
||||
while (i++ < n) {
|
||||
if (EndOfBuffer(buffer) || x >= info->width) return false;
|
||||
*pixel++ = GB(c, 4, 4);
|
||||
x++;
|
||||
if (x < info->width && i++ < n) {
|
||||
*pixel++ = GB(c, 0, 4);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 8 bpp bitmap
|
||||
*/
|
||||
static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
|
||||
{
|
||||
uint i;
|
||||
uint y;
|
||||
byte pad = GB(4 - info->width, 0, 2);
|
||||
byte *pixel;
|
||||
for (y = info->height; y > 0; y--) {
|
||||
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
|
||||
pixel = &data->bitmap[(y - 1) * info->width];
|
||||
for (i = 0; i < info->width; i++) *pixel++ = ReadByte(buffer);
|
||||
/* Padding for 32 bit align */
|
||||
SkipBytes(buffer, pad);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 8-bit RLE compressed bpp bitmap
|
||||
*/
|
||||
static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
|
||||
{
|
||||
uint i;
|
||||
uint x = 0;
|
||||
uint y = info->height - 1;
|
||||
byte n, c;
|
||||
byte *pixel = &data->bitmap[y * info->width];
|
||||
while (y != 0 || x < info->width) {
|
||||
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
|
||||
n = ReadByte(buffer);
|
||||
c = ReadByte(buffer);
|
||||
if (n == 0) {
|
||||
switch (c) {
|
||||
case 0: // end of line
|
||||
x = 0;
|
||||
pixel = &data->bitmap[--y * info->width];
|
||||
break;
|
||||
case 1: // end of bitmap
|
||||
x = info->width;
|
||||
y = 0;
|
||||
pixel = NULL;
|
||||
break;
|
||||
case 2: // delta
|
||||
x += ReadByte(buffer);
|
||||
i = ReadByte(buffer);
|
||||
if (x >= info->width || (y == 0 && i > 0)) return false;
|
||||
y -= i;
|
||||
pixel = &data->bitmap[y * info->width + x];
|
||||
break;
|
||||
default: // uncompressed
|
||||
if ((x += c) > info->width) return false;
|
||||
for (i = 0; i < c; i++) *pixel++ = ReadByte(buffer);
|
||||
/* Padding for 16 bit align */
|
||||
SkipBytes(buffer, c % 2);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < n; i++) {
|
||||
if (x >= info->width) return false;
|
||||
*pixel++ = c;
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 24 bpp uncompressed bitmap
|
||||
*/
|
||||
static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
|
||||
{
|
||||
uint x, y;
|
||||
byte pad = GB(4 - info->width * 3, 0, 2);
|
||||
byte *pixel_row;
|
||||
for (y = info->height; y > 0; y--) {
|
||||
pixel_row = &data->bitmap[(y - 1) * info->width * 3];
|
||||
for (x = 0; x < info->width; x++) {
|
||||
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
|
||||
*(pixel_row + 2) = ReadByte(buffer); // green
|
||||
*(pixel_row + 1) = ReadByte(buffer); // blue
|
||||
*pixel_row = ReadByte(buffer); // red
|
||||
pixel_row += 3;
|
||||
}
|
||||
/* Padding for 32 bit align */
|
||||
SkipBytes(buffer, pad);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads bitmap headers, and palette (if any)
|
||||
*/
|
||||
bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
|
||||
{
|
||||
uint32 header_size;
|
||||
assert(info != NULL);
|
||||
|
||||
/* Reading BMP header */
|
||||
if (ReadWord(buffer) != 0x4D42) return false; // signature should be 'BM'
|
||||
SkipBytes(buffer, 8); // skip file size and reserved
|
||||
info->offset = ReadDword(buffer);
|
||||
|
||||
/* Reading info header */
|
||||
header_size = ReadDword(buffer);
|
||||
if (header_size < 12) return false; // info header should be at least 12 bytes long
|
||||
|
||||
info->os2_bmp = (header_size == 12); // OS/2 1.x or windows 2.x info header is 12 bytes long
|
||||
|
||||
if (info->os2_bmp) {
|
||||
info->width = ReadWord(buffer);
|
||||
info->height = ReadWord(buffer);
|
||||
header_size -= 8;
|
||||
} else {
|
||||
info->width = ReadDword(buffer);
|
||||
info->height = ReadDword(buffer);
|
||||
header_size -= 12;
|
||||
}
|
||||
|
||||
if (ReadWord(buffer) != 1) return false; // BMP can have only 1 plane
|
||||
|
||||
info->bpp = ReadWord(buffer);
|
||||
if (info->bpp != 1 && info->bpp != 4 && info->bpp != 8 && info->bpp != 24) {
|
||||
/* Only 1 bpp, 4 bpp, 8bpp and 24 bpp bitmaps are supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reads compression method if available in info header*/
|
||||
if ((header_size -= 4) >= 4) {
|
||||
info->compression = ReadDword(buffer);
|
||||
header_size -= 4;
|
||||
}
|
||||
|
||||
/* Only 4-bit and 8-bit rle compression is supported */
|
||||
if (info->compression > 2 || (info->compression > 0 && !(info->bpp == 4 || info->bpp == 8))) return false;
|
||||
|
||||
if (info->bpp <= 8) {
|
||||
uint i;
|
||||
|
||||
/* Reads number of colors if available in info header */
|
||||
if (header_size >= 16) {
|
||||
SkipBytes(buffer, 12); // skip image size and resolution
|
||||
info->palette_size = ReadDword(buffer); // number of colors in palette
|
||||
SkipBytes(buffer, header_size - 16); // skip the end of info header
|
||||
}
|
||||
if (info->palette_size == 0) info->palette_size = 1 << info->bpp;
|
||||
|
||||
data->palette = calloc(info->palette_size, sizeof(*(data->palette)));
|
||||
if (data->palette == NULL) return false;
|
||||
|
||||
for (i = 0; i < info->palette_size; i++) {
|
||||
data->palette[i].b = ReadByte(buffer);
|
||||
data->palette[i].g = ReadByte(buffer);
|
||||
data->palette[i].r = ReadByte(buffer);
|
||||
if (!info->os2_bmp) SkipBytes(buffer, 1); // unused
|
||||
}
|
||||
}
|
||||
|
||||
return buffer->real_pos <= info->offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the bitmap
|
||||
* 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps
|
||||
*/
|
||||
bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
|
||||
{
|
||||
assert(info != NULL && data != NULL);
|
||||
|
||||
data->bitmap = calloc(info->width * info->height, ((info->bpp == 24) ? 3 : 1) * sizeof(byte));
|
||||
if (data->bitmap == NULL) return false;
|
||||
|
||||
/* Load image */
|
||||
SetStreamOffset(buffer, info->offset);
|
||||
switch (info->compression) {
|
||||
case 0: // no compression
|
||||
switch (info->bpp) {
|
||||
case 1: return BmpRead1(buffer, info, data);
|
||||
case 4: return BmpRead4(buffer, info, data);
|
||||
case 8: return BmpRead8(buffer, info, data);
|
||||
case 24: return BmpRead24(buffer, info, data);
|
||||
default: NOT_REACHED(); return false;
|
||||
}
|
||||
case 1: return BmpRead8Rle(buffer, info, data); // 8-bit RLE compression
|
||||
case 2: return BmpRead4Rle(buffer, info, data); // 4-bit RLE compression
|
||||
default: NOT_REACHED(); return false;
|
||||
}
|
||||
}
|
||||
|
||||
void BmpDestroyData(BmpData *data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
free(data->palette);
|
||||
free(data->bitmap);
|
||||
}
|
||||
36
src/bmp.h
Normal file
36
src/bmp.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef BMP_H
|
||||
#define BMP_H
|
||||
|
||||
typedef struct {
|
||||
uint32 offset; ///< offset of bitmap data from .bmp file begining
|
||||
uint32 width; ///< bitmap width
|
||||
uint32 height; ///< bitmap height
|
||||
bool os2_bmp; ///< true if OS/2 1.x or windows 2.x bitmap
|
||||
uint16 bpp; ///< bits per pixel
|
||||
uint32 compression; ///< compression method (0 = none, 1 = 8-bit RLE, 2 = 4-bit RLE)
|
||||
uint32 palette_size; ///< number of colors in palette
|
||||
} BmpInfo;
|
||||
|
||||
typedef struct {
|
||||
Colour *palette;
|
||||
byte *bitmap;
|
||||
} BmpData;
|
||||
|
||||
#define BMP_BUFFER_SIZE 1024
|
||||
|
||||
typedef struct {
|
||||
byte data[BMP_BUFFER_SIZE];
|
||||
int pos;
|
||||
int read;
|
||||
FILE *file;
|
||||
uint real_pos;
|
||||
} BmpBuffer;
|
||||
|
||||
void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file);
|
||||
bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data);
|
||||
bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data);
|
||||
void BmpDestroyData(BmpData *data);
|
||||
|
||||
#endif /* BMP_H */
|
||||
32
src/bridge.h
Normal file
32
src/bridge.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file bridge.h Header file for bridges */
|
||||
|
||||
#ifndef BRIDGE_H
|
||||
#define BRIDGE_H
|
||||
|
||||
enum {
|
||||
MAX_BRIDGES = 13
|
||||
};
|
||||
|
||||
/** Struct containing information about a single bridge type
|
||||
*/
|
||||
typedef struct Bridge {
|
||||
Year avail_year; ///< the year in which the bridge becomes available
|
||||
byte min_length; ///< the minimum length of the bridge (not counting start and end tile)
|
||||
byte max_length; ///< the maximum length of the bridge (not counting start and end tile)
|
||||
uint16 price; ///< the relative price of the bridge
|
||||
uint16 speed; ///< maximum travel speed
|
||||
PalSpriteID sprite; ///< the sprite which is used in the GUI (possibly with a recolor sprite)
|
||||
StringID material; ///< the string that contains the bridge description
|
||||
PalSpriteID **sprite_table; ///< table of sprites for drawing the bridge
|
||||
byte flags; ///< bit 0 set: disable drawing of far pillars.
|
||||
} Bridge;
|
||||
|
||||
extern const Bridge orig_bridge[MAX_BRIDGES];
|
||||
extern Bridge _bridge[MAX_BRIDGES];
|
||||
|
||||
uint GetBridgeFoundation(Slope tileh, Axis axis);
|
||||
uint SetSpeedLimitOnBridge(Vehicle *);
|
||||
|
||||
#endif /* BRIDGE_H */
|
||||
167
src/bridge_gui.c
Normal file
167
src/bridge_gui.c
Normal file
@@ -0,0 +1,167 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file bridge_gui.c Graphical user interface for bridge construction*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "table/strings.h"
|
||||
#include "functions.h"
|
||||
#include "map.h"
|
||||
#include "window.h"
|
||||
#include "gui.h"
|
||||
#include "viewport.h"
|
||||
#include "gfx.h"
|
||||
#include "command.h"
|
||||
#include "sound.h"
|
||||
#include "variables.h"
|
||||
#include "bridge.h"
|
||||
|
||||
static struct BridgeData {
|
||||
uint count;
|
||||
TileIndex start_tile;
|
||||
TileIndex end_tile;
|
||||
byte type;
|
||||
byte indexes[MAX_BRIDGES];
|
||||
int32 costs[MAX_BRIDGES];
|
||||
} _bridgedata;
|
||||
|
||||
void CcBuildBridge(bool success, TileIndex tile, uint32 p1, uint32 p2)
|
||||
{
|
||||
if (success) SndPlayTileFx(SND_27_BLACKSMITH_ANVIL, tile);
|
||||
}
|
||||
|
||||
static void BuildBridge(Window *w, int i)
|
||||
{
|
||||
DeleteWindow(w);
|
||||
DoCommandP(_bridgedata.end_tile, _bridgedata.start_tile,
|
||||
_bridgedata.indexes[i] | (_bridgedata.type << 8), CcBuildBridge,
|
||||
CMD_BUILD_BRIDGE | CMD_AUTO | CMD_MSG(STR_5015_CAN_T_BUILD_BRIDGE_HERE));
|
||||
}
|
||||
|
||||
static void BuildBridgeWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_PAINT: {
|
||||
uint i;
|
||||
|
||||
DrawWindowWidgets(w);
|
||||
|
||||
for (i = 0; i < 4 && i + w->vscroll.pos < _bridgedata.count; i++) {
|
||||
const Bridge *b = &_bridge[_bridgedata.indexes[i + w->vscroll.pos]];
|
||||
|
||||
SetDParam(2, _bridgedata.costs[i + w->vscroll.pos]);
|
||||
SetDParam(1, b->speed);
|
||||
SetDParam(0, b->material);
|
||||
DrawSprite(b->sprite, 3, 15 + i * 22);
|
||||
|
||||
DrawString(44, 15 + i * 22 , STR_500D, 0);
|
||||
}
|
||||
} break;
|
||||
|
||||
case WE_KEYPRESS: {
|
||||
uint i = e->we.keypress.keycode - '1';
|
||||
if (i < 9 && i < _bridgedata.count) {
|
||||
e->we.keypress.cont = false;
|
||||
BuildBridge(w, i);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case WE_CLICK:
|
||||
if (e->we.click.widget == 2) {
|
||||
uint ind = ((int)e->we.click.pt.y - 14) / 22;
|
||||
if (ind < 4 && (ind += w->vscroll.pos) < _bridgedata.count)
|
||||
BuildBridge(w, ind);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const Widget _build_bridge_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 199, 0, 13, STR_100D_SELECT_RAIL_BRIDGE, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_MATRIX, RESIZE_NONE, 7, 0, 187, 14, 101, 0x401, STR_101F_BRIDGE_SELECTION_CLICK},
|
||||
{ WWT_SCROLLBAR, RESIZE_NONE, 7, 188, 199, 14, 101, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const WindowDesc _build_bridge_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 200, 102,
|
||||
WC_BUILD_BRIDGE, WC_BUILD_TOOLBAR,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_bridge_widgets,
|
||||
BuildBridgeWndProc
|
||||
};
|
||||
|
||||
|
||||
static const Widget _build_road_bridge_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 199, 0, 13, STR_1803_SELECT_ROAD_BRIDGE, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_MATRIX, RESIZE_NONE, 7, 0, 187, 14, 101, 0x401, STR_101F_BRIDGE_SELECTION_CLICK},
|
||||
{ WWT_SCROLLBAR, RESIZE_NONE, 7, 188, 199, 14, 101, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const WindowDesc _build_road_bridge_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 200, 102,
|
||||
WC_BUILD_BRIDGE, WC_BUILD_TOOLBAR,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_road_bridge_widgets,
|
||||
BuildBridgeWndProc
|
||||
};
|
||||
|
||||
|
||||
void ShowBuildBridgeWindow(TileIndex start, TileIndex end, byte bridge_type)
|
||||
{
|
||||
uint j = 0;
|
||||
int32 ret;
|
||||
StringID errmsg;
|
||||
|
||||
DeleteWindowById(WC_BUILD_BRIDGE, 0);
|
||||
|
||||
_bridgedata.type = bridge_type;
|
||||
_bridgedata.start_tile = start;
|
||||
_bridgedata.end_tile = end;
|
||||
|
||||
errmsg = INVALID_STRING_ID;
|
||||
|
||||
// only query bridge building possibility once, result is the same for all bridges!
|
||||
// returns CMD_ERROR on failure, and price on success
|
||||
ret = DoCommand(end, start, (bridge_type << 8), DC_AUTO | DC_QUERY_COST, CMD_BUILD_BRIDGE);
|
||||
|
||||
if (CmdFailed(ret)) {
|
||||
errmsg = _error_message;
|
||||
} else {
|
||||
// check which bridges can be built
|
||||
int bridge_len; // length of the middle parts of the bridge
|
||||
int tot_bridgedata_len; // total length of bridge
|
||||
|
||||
// get absolute bridge length
|
||||
bridge_len = GetBridgeLength(start, end);
|
||||
tot_bridgedata_len = bridge_len + 2;
|
||||
|
||||
tot_bridgedata_len = CalcBridgeLenCostFactor(tot_bridgedata_len);
|
||||
|
||||
for (bridge_type = 0; bridge_type != MAX_BRIDGES; bridge_type++) { // loop for all bridgetypes
|
||||
if (CheckBridge_Stuff(bridge_type, bridge_len)) {
|
||||
const Bridge *b = &_bridge[bridge_type];
|
||||
// bridge is accepted, add to list
|
||||
// add to terraforming & bulldozing costs the cost of the bridge itself (not computed with DC_QUERY_COST)
|
||||
_bridgedata.costs[j] = ret + (((int64)tot_bridgedata_len * _price.build_bridge * b->price) >> 8);
|
||||
_bridgedata.indexes[j] = bridge_type;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_bridgedata.count = j;
|
||||
|
||||
if (j != 0) {
|
||||
Window *w = AllocateWindowDesc((_bridgedata.type & 0x80) ? &_build_road_bridge_desc : &_build_bridge_desc);
|
||||
w->vscroll.cap = 4;
|
||||
w->vscroll.count = (byte)j;
|
||||
} else {
|
||||
ShowErrorMessage(errmsg, STR_5015_CAN_T_BUILD_BRIDGE_HERE, TileX(end) * TILE_SIZE, TileY(end) * TILE_SIZE);
|
||||
}
|
||||
}
|
||||
51
src/bridge_map.c
Normal file
51
src/bridge_map.c
Normal file
@@ -0,0 +1,51 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "bridge_map.h"
|
||||
#include "variables.h"
|
||||
|
||||
|
||||
TileIndex GetBridgeEnd(TileIndex tile, DiagDirection dir)
|
||||
{
|
||||
TileIndexDiff delta = TileOffsByDiagDir(dir);
|
||||
|
||||
dir = ReverseDiagDir(dir);
|
||||
do {
|
||||
tile += delta;
|
||||
} while (!IsBridgeTile(tile) || GetBridgeRampDirection(tile) != dir);
|
||||
|
||||
return tile;
|
||||
}
|
||||
|
||||
|
||||
TileIndex GetNorthernBridgeEnd(TileIndex t)
|
||||
{
|
||||
return GetBridgeEnd(t, ReverseDiagDir(AxisToDiagDir(GetBridgeAxis(t))));
|
||||
}
|
||||
|
||||
|
||||
TileIndex GetSouthernBridgeEnd(TileIndex t)
|
||||
{
|
||||
return GetBridgeEnd(t, AxisToDiagDir(GetBridgeAxis(t)));
|
||||
}
|
||||
|
||||
|
||||
TileIndex GetOtherBridgeEnd(TileIndex tile)
|
||||
{
|
||||
assert(IsBridgeTile(tile));
|
||||
return GetBridgeEnd(tile, GetBridgeRampDirection(tile));
|
||||
}
|
||||
|
||||
uint GetBridgeHeight(TileIndex t)
|
||||
{
|
||||
uint h;
|
||||
uint tileh = GetTileSlope(t, &h);
|
||||
uint f = GetBridgeFoundation(tileh, DiagDirToAxis(GetBridgeRampDirection(t)));
|
||||
|
||||
// one height level extra if the ramp is on a flat foundation
|
||||
return
|
||||
h + TILE_HEIGHT +
|
||||
(IS_INT_INSIDE(f, 1, 15) ? TILE_HEIGHT : 0) +
|
||||
(IsSteepSlope(tileh) ? TILE_HEIGHT : 0);
|
||||
}
|
||||
163
src/bridge_map.h
Normal file
163
src/bridge_map.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef BRIDGE_MAP_H
|
||||
#define BRIDGE_MAP_H
|
||||
|
||||
#include "direction.h"
|
||||
#include "macros.h"
|
||||
#include "map.h"
|
||||
#include "rail.h"
|
||||
#include "road_map.h"
|
||||
#include "tile.h"
|
||||
|
||||
|
||||
void DrawBridgeMiddle(const TileInfo* ti); // XXX
|
||||
|
||||
|
||||
static inline bool IsBridge(TileIndex t)
|
||||
{
|
||||
assert(IsTileType(t, MP_TUNNELBRIDGE));
|
||||
return HASBIT(_m[t].m5, 7);
|
||||
}
|
||||
|
||||
static inline bool IsBridgeTile(TileIndex t)
|
||||
{
|
||||
return IsTileType(t, MP_TUNNELBRIDGE) && IsBridge(t);
|
||||
}
|
||||
|
||||
|
||||
static inline bool MayHaveBridgeAbove(TileIndex t)
|
||||
{
|
||||
return
|
||||
IsTileType(t, MP_CLEAR) ||
|
||||
IsTileType(t, MP_RAILWAY) ||
|
||||
IsTileType(t, MP_STREET) ||
|
||||
IsTileType(t, MP_WATER) ||
|
||||
IsTileType(t, MP_TUNNELBRIDGE) ||
|
||||
IsTileType(t, MP_UNMOVABLE);
|
||||
}
|
||||
|
||||
|
||||
static inline bool IsBridgeAbove(TileIndex t)
|
||||
{
|
||||
assert(MayHaveBridgeAbove(t));
|
||||
return GB(_m[t].extra, 6, 2) != 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines the type of bridge on a tile
|
||||
* @param tile The tile to analyze
|
||||
* @return The bridge type
|
||||
*/
|
||||
static inline uint GetBridgeType(TileIndex t)
|
||||
{
|
||||
assert(IsBridgeTile(t));
|
||||
return GB(_m[t].m2, 4, 4);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the direction pointing onto the bridge
|
||||
*/
|
||||
static inline DiagDirection GetBridgeRampDirection(TileIndex t)
|
||||
{
|
||||
assert(IsBridgeTile(t));
|
||||
return (DiagDirection)GB(_m[t].m5, 0, 2);
|
||||
}
|
||||
|
||||
|
||||
static inline Axis GetBridgeAxis(TileIndex t)
|
||||
{
|
||||
assert(IsBridgeAbove(t));
|
||||
return (Axis)(GB(_m[t].extra, 6, 2) - 1);
|
||||
}
|
||||
|
||||
|
||||
static inline TransportType GetBridgeTransportType(TileIndex t)
|
||||
{
|
||||
assert(IsBridgeTile(t));
|
||||
return (TransportType)GB(_m[t].m5, 2, 2);
|
||||
}
|
||||
|
||||
|
||||
static inline bool HasBridgeSnowOrDesert(TileIndex t)
|
||||
{
|
||||
assert(IsBridgeTile(t));
|
||||
return HASBIT(_m[t].m4, 7);
|
||||
}
|
||||
|
||||
|
||||
static inline void SetBridgeSnowOrDesert(TileIndex t, bool snow_or_desert)
|
||||
{
|
||||
assert(IsBridgeTile(t));
|
||||
SB(_m[t].m4, 7, 1, snow_or_desert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the end of a bridge in the specified direction starting at a middle tile
|
||||
*/
|
||||
TileIndex GetBridgeEnd(TileIndex, DiagDirection);
|
||||
|
||||
/**
|
||||
* Finds the northern end of a bridge starting at a middle tile
|
||||
*/
|
||||
TileIndex GetNorthernBridgeEnd(TileIndex t);
|
||||
|
||||
/**
|
||||
* Finds the southern end of a bridge starting at a middle tile
|
||||
*/
|
||||
TileIndex GetSouthernBridgeEnd(TileIndex t);
|
||||
|
||||
|
||||
/**
|
||||
* Starting at one bridge end finds the other bridge end
|
||||
*/
|
||||
TileIndex GetOtherBridgeEnd(TileIndex);
|
||||
|
||||
uint GetBridgeHeight(TileIndex tile);
|
||||
uint GetBridgeFoundation(Slope tileh, Axis axis);
|
||||
|
||||
static inline void ClearSingleBridgeMiddle(TileIndex t, Axis a)
|
||||
{
|
||||
assert(MayHaveBridgeAbove(t));
|
||||
CLRBIT(_m[t].extra, 6 + a);
|
||||
}
|
||||
|
||||
|
||||
static inline void ClearBridgeMiddle(TileIndex t)
|
||||
{
|
||||
ClearSingleBridgeMiddle(t, AXIS_X);
|
||||
ClearSingleBridgeMiddle(t, AXIS_Y);
|
||||
}
|
||||
|
||||
static inline void SetBridgeMiddle(TileIndex t, Axis a)
|
||||
{
|
||||
assert(MayHaveBridgeAbove(t));
|
||||
SETBIT(_m[t].extra, 6 + a);
|
||||
}
|
||||
|
||||
|
||||
static inline void MakeBridgeRamp(TileIndex t, Owner o, uint bridgetype, DiagDirection d, TransportType tt)
|
||||
{
|
||||
SetTileType(t, MP_TUNNELBRIDGE);
|
||||
SetTileOwner(t, o);
|
||||
_m[t].m2 = bridgetype << 4;
|
||||
_m[t].m4 = 0;
|
||||
_m[t].m5 = 1 << 7 | tt << 2 | d;
|
||||
}
|
||||
|
||||
static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, uint bridgetype, DiagDirection d)
|
||||
{
|
||||
MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_ROAD);
|
||||
_m[t].m3 = 0;
|
||||
}
|
||||
|
||||
static inline void MakeRailBridgeRamp(TileIndex t, Owner o, uint bridgetype, DiagDirection d, RailType r)
|
||||
{
|
||||
MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_RAIL);
|
||||
_m[t].m3 = r;
|
||||
}
|
||||
|
||||
|
||||
#endif /* BRIDGE_MAP_H */
|
||||
492
src/build_vehicle_gui.c
Normal file
492
src/build_vehicle_gui.c
Normal file
@@ -0,0 +1,492 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "aircraft.h"
|
||||
#include "debug.h"
|
||||
#include "functions.h"
|
||||
#include "table/sprites.h"
|
||||
#include "table/strings.h"
|
||||
#include "window.h"
|
||||
#include "gui.h"
|
||||
#include "vehicle.h"
|
||||
#include "gfx.h"
|
||||
#include "station.h"
|
||||
#include "command.h"
|
||||
#include "engine.h"
|
||||
#include "player.h"
|
||||
#include "depot.h"
|
||||
#include "airport.h"
|
||||
#include "vehicle_gui.h"
|
||||
#include "newgrf_engine.h"
|
||||
#include "date.h"
|
||||
#include "strings.h"
|
||||
|
||||
|
||||
enum BuildVehicleWidgets {
|
||||
BUILD_VEHICLE_WIDGET_CLOSEBOX = 0,
|
||||
BUILD_VEHICLE_WIDGET_CAPTION,
|
||||
BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING,
|
||||
BUILD_VEHICLE_WIDGET_SORT_TEXT,
|
||||
BUILD_VEHICLE_WIDGET_SORT_DROPDOWN,
|
||||
BUILD_VEHICLE_WIDGET_LIST,
|
||||
BUILD_VEHICLE_WIDGET_SCROLLBAR,
|
||||
BUILD_VEHICLE_WIDGET_PANEL,
|
||||
BUILD_VEHICLE_WIDGET_BUILD,
|
||||
BUILD_VEHICLE_WIDGET_RENAME,
|
||||
BUILD_VEHICLE_WIDGET_RESIZE,
|
||||
};
|
||||
|
||||
static const Widget _build_vehicle_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW },
|
||||
{ WWT_CAPTION, RESIZE_NONE, 14, 11, 239, 0, 13, STR_A005_NEW_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS },
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 80, 14, 25, STR_SORT_BY, STR_SORT_ORDER_TIP},
|
||||
{ WWT_PANEL, RESIZE_NONE, 14, 81, 227, 14, 25, 0x0, STR_SORT_CRITERIA_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 228, 239, 14, 25, STR_0225, STR_SORT_CRITERIA_TIP},
|
||||
{ WWT_MATRIX, RESIZE_BOTTOM, 14, 0, 227, 26, 121, 0x401, STR_A025_AIRCRAFT_SELECTION_LIST },
|
||||
{ WWT_SCROLLBAR, RESIZE_BOTTOM, 14, 228, 239, 26, 121, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST },
|
||||
{ WWT_PANEL, RESIZE_TB, 14, 0, 239, 122, 213, 0x0, STR_NULL },
|
||||
|
||||
{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 114, 214, 225, STR_A006_BUILD_AIRCRAFT, STR_A026_BUILD_THE_HIGHLIGHTED_AIRCRAFT },
|
||||
{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 115, 227, 214, 225, STR_A037_RENAME, STR_A038_RENAME_AIRCRAFT_TYPE },
|
||||
{ WWT_RESIZEBOX, RESIZE_TB, 14, 228, 239, 214, 225, 0x0, STR_RESIZE_BUTTON },
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static bool _internal_sort_order; // descending/ascending
|
||||
static byte _last_sort_criteria = 0;
|
||||
static bool _last_sort_order = false;
|
||||
|
||||
static int CDECL EngineNumberSorter(const void *a, const void *b)
|
||||
{
|
||||
const EngineID va = *(const EngineID*)a;
|
||||
const EngineID vb = *(const EngineID*)b;
|
||||
int r = va - vb;
|
||||
|
||||
return _internal_sort_order ? -r : r;
|
||||
}
|
||||
|
||||
static int CDECL EngineIntroDateSorter(const void *a, const void *b)
|
||||
{
|
||||
const int va = GetEngine(*(const EngineID*)a)->intro_date;
|
||||
const int vb = GetEngine(*(const EngineID*)b)->intro_date;
|
||||
const int r = va - vb;
|
||||
|
||||
if (r == 0) {
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
return EngineNumberSorter(a, b);
|
||||
}
|
||||
return _internal_sort_order ? -r : r;
|
||||
}
|
||||
|
||||
static int CDECL EngineNameSorter(const void *a, const void *b)
|
||||
{
|
||||
static EngineID last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE };
|
||||
static char last_name[2][64] = { "\0", "\0" };
|
||||
|
||||
const EngineID va = *(const EngineID*)a;
|
||||
const EngineID vb = *(const EngineID*)b;
|
||||
int r;
|
||||
|
||||
if (va != last_engine[0]) {
|
||||
last_engine[0] = va;
|
||||
GetString(last_name[0], GetCustomEngineName(va), lastof(last_name[0]));
|
||||
}
|
||||
|
||||
if (vb != last_engine[1]) {
|
||||
last_engine[1] = vb;
|
||||
GetString(last_name[1], GetCustomEngineName(vb), lastof(last_name[1]));
|
||||
}
|
||||
|
||||
r = strcmp(last_name[0], last_name[1]); // sort by name
|
||||
|
||||
if (r == 0) {
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
return EngineNumberSorter(a, b);
|
||||
}
|
||||
return _internal_sort_order ? -r : r;
|
||||
}
|
||||
|
||||
static int CDECL EngineReliabilitySorter(const void *a, const void *b)
|
||||
{
|
||||
const int va = GetEngine(*(const EngineID*)a)->reliability;
|
||||
const int vb = GetEngine(*(const EngineID*)b)->reliability;
|
||||
const int r = va - vb;
|
||||
|
||||
if (r == 0) {
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
return EngineNumberSorter(a, b);
|
||||
}
|
||||
return _internal_sort_order ? -r : r;
|
||||
}
|
||||
|
||||
/* Aircraft sorting functions */
|
||||
|
||||
static int CDECL AircraftEngineCostSorter(const void *a, const void *b)
|
||||
{
|
||||
const int va = AircraftVehInfo(*(const EngineID*)a)->base_cost;
|
||||
const int vb = AircraftVehInfo(*(const EngineID*)b)->base_cost;
|
||||
int r = va - vb;
|
||||
|
||||
return _internal_sort_order ? -r : r;
|
||||
}
|
||||
|
||||
static int CDECL AircraftEngineSpeedSorter(const void *a, const void *b)
|
||||
{
|
||||
const int va = AircraftVehInfo(*(const EngineID*)a)->max_speed;
|
||||
const int vb = AircraftVehInfo(*(const EngineID*)b)->max_speed;
|
||||
const int r = va - vb;
|
||||
|
||||
if (r == 0) {
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
return EngineNumberSorter(a, b);
|
||||
}
|
||||
return _internal_sort_order ? -r : r;
|
||||
}
|
||||
|
||||
static int CDECL AircraftEngineRunningCostSorter(const void *a, const void *b)
|
||||
{
|
||||
const int va = AircraftVehInfo(*(const EngineID*)a)->running_cost;
|
||||
const int vb = AircraftVehInfo(*(const EngineID*)b)->running_cost;
|
||||
const int r = va - vb;
|
||||
|
||||
if (r == 0) {
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
return EngineNumberSorter(a, b);
|
||||
}
|
||||
return _internal_sort_order ? -r : r;
|
||||
}
|
||||
|
||||
static int CDECL AircraftEngineCargoSorter(const void *a, const void *b)
|
||||
{
|
||||
const int va = AircraftVehInfo(*(const EngineID*)a)->passenger_capacity;
|
||||
const int vb = AircraftVehInfo(*(const EngineID*)b)->passenger_capacity;
|
||||
const int r = va - vb;
|
||||
|
||||
if (r == 0) {
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
return EngineNumberSorter(a, b);
|
||||
}
|
||||
return _internal_sort_order ? -r : r;
|
||||
}
|
||||
|
||||
static EngList_SortTypeFunction * const _aircraft_sorter[] = {
|
||||
&EngineNumberSorter,
|
||||
&AircraftEngineCostSorter,
|
||||
&AircraftEngineSpeedSorter,
|
||||
&EngineIntroDateSorter,
|
||||
&EngineNameSorter,
|
||||
&AircraftEngineRunningCostSorter,
|
||||
&EngineReliabilitySorter,
|
||||
&AircraftEngineCargoSorter,
|
||||
};
|
||||
|
||||
static const StringID _aircraft_sort_listing[] = {
|
||||
STR_ENGINE_SORT_ENGINE_ID,
|
||||
STR_ENGINE_SORT_COST,
|
||||
STR_SORT_BY_MAX_SPEED,
|
||||
STR_ENGINE_SORT_INTRO_DATE,
|
||||
STR_SORT_BY_DROPDOWN_NAME,
|
||||
STR_ENGINE_SORT_RUNNING_COST,
|
||||
STR_SORT_BY_RELIABILITY,
|
||||
STR_ENGINE_SORT_CARGO_CAPACITY,
|
||||
INVALID_STRING_ID
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Draw the purchase info details of an aircraft at a given location.
|
||||
* @param x,y location where to draw the info
|
||||
* @param engine_number the engine of which to draw the info of
|
||||
*/
|
||||
void DrawAircraftPurchaseInfo(int x, int y, uint w, EngineID engine_number)
|
||||
{
|
||||
const AircraftVehicleInfo *avi = AircraftVehInfo(engine_number);
|
||||
const Engine *e = GetEngine(engine_number);
|
||||
CargoID cargo;
|
||||
YearMonthDay ymd;
|
||||
ConvertDateToYMD(e->intro_date, &ymd);
|
||||
|
||||
/* Purchase cost - Max speed */
|
||||
SetDParam(0, avi->base_cost * (_price.aircraft_base>>3)>>5);
|
||||
SetDParam(1, avi->max_speed * 128 / 10);
|
||||
DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, 0);
|
||||
y += 10;
|
||||
|
||||
/* Cargo capacity */
|
||||
cargo = FindFirstRefittableCargo(engine_number);
|
||||
if (cargo == CT_INVALID || cargo == CT_PASSENGERS) {
|
||||
SetDParam(0, avi->passenger_capacity);
|
||||
SetDParam(1, avi->mail_capacity);
|
||||
DrawString(x, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, 0);
|
||||
} else {
|
||||
/* Note, if the default capacity is selected by the refit capacity
|
||||
* callback, then the capacity shown is likely to be incorrect. */
|
||||
SetDParam(0, cargo);
|
||||
SetDParam(1, AircraftDefaultCargoCapacity(cargo, engine_number));
|
||||
SetDParam(2, STR_9842_REFITTABLE);
|
||||
DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, 0);
|
||||
}
|
||||
y += 10;
|
||||
|
||||
/* Running cost */
|
||||
SetDParam(0, avi->running_cost * _price.aircraft_running >> 8);
|
||||
DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, 0);
|
||||
y += 10;
|
||||
|
||||
/* Design date - Life length */
|
||||
SetDParam(0, ymd.year);
|
||||
SetDParam(1, e->lifelength);
|
||||
DrawString(x, y, STR_PURCHASE_INFO_DESIGNED_LIFE, 0);
|
||||
y += 10;
|
||||
|
||||
/* Reliability */
|
||||
SetDParam(0, e->reliability * 100 >> 16);
|
||||
DrawString(x, y, STR_PURCHASE_INFO_RELIABILITY, 0);
|
||||
y += 10;
|
||||
|
||||
/* Additional text from NewGRF */
|
||||
y += ShowAdditionalText(x, y, w, engine_number);
|
||||
y += ShowRefitOptionsList(x, y, w, engine_number);
|
||||
}
|
||||
|
||||
void DrawAircraftImage(const Vehicle *v, int x, int y, VehicleID selection)
|
||||
{
|
||||
PalSpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
|
||||
DrawSprite(GetAircraftImage(v, DIR_W) | pal, x + 25, y + 10);
|
||||
if (v->subtype == 0) {
|
||||
SpriteID rotor_sprite = GetCustomRotorSprite(v, true);
|
||||
if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
|
||||
DrawSprite(rotor_sprite, x + 25, y + 5);
|
||||
}
|
||||
if (v->index == selection) {
|
||||
DrawFrameRect(x - 1, y - 1, x + 58, y + 21, 0xF, FR_BORDERONLY);
|
||||
}
|
||||
}
|
||||
|
||||
void CcBuildAircraft(bool success, TileIndex tile, uint32 p1, uint32 p2)
|
||||
{
|
||||
if (success) {
|
||||
const Vehicle *v = GetVehicle(_new_vehicle_id);
|
||||
|
||||
if (v->tile == _backup_orders_tile) {
|
||||
_backup_orders_tile = 0;
|
||||
RestoreVehicleOrders(v, _backup_orders_data);
|
||||
}
|
||||
ShowAircraftViewWindow(v);
|
||||
}
|
||||
}
|
||||
|
||||
static void GenerateBuildAircraftList(Window *w)
|
||||
{
|
||||
EngineID eid, sel_id;
|
||||
buildvehicle_d *bv = &WP(w, buildvehicle_d);
|
||||
|
||||
EngList_RemoveAll(&bv->eng_list);
|
||||
|
||||
/* Make list of all available planes.
|
||||
* Also check to see if the previously selected plane is still available,
|
||||
* and if not, reset selection to INVALID_ENGINE. This could be the case
|
||||
* when planes become obsolete and are removed */
|
||||
sel_id = INVALID_ENGINE;
|
||||
for (eid = AIRCRAFT_ENGINES_INDEX; eid < AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES; eid++) {
|
||||
if (IsEngineBuildable(eid, VEH_Aircraft, _local_player)) {
|
||||
const AircraftVehicleInfo *avi = AircraftVehInfo(eid);
|
||||
switch (bv->filter.acc_planes) {
|
||||
case HELICOPTERS_ONLY:
|
||||
if (avi->subtype != 0) continue; // if not helicopter
|
||||
break;
|
||||
|
||||
case AIRCRAFT_ONLY:
|
||||
if (avi->subtype == 0) continue; // if helicopter
|
||||
break;
|
||||
|
||||
case ALL: break;
|
||||
}
|
||||
EngList_Add(&bv->eng_list, eid);
|
||||
|
||||
if (eid == bv->sel_engine) sel_id = eid;
|
||||
}
|
||||
}
|
||||
|
||||
bv->sel_engine = sel_id;
|
||||
}
|
||||
|
||||
static void GenerateBuildList(Window *w)
|
||||
{
|
||||
buildvehicle_d *bv = &WP(w, buildvehicle_d);
|
||||
|
||||
switch (bv->vehicle_type) {
|
||||
case VEH_Aircraft:
|
||||
GenerateBuildAircraftList(w);
|
||||
_internal_sort_order = bv->descending_sort_order;
|
||||
EngList_Sort(&bv->eng_list, _aircraft_sorter[bv->sort_criteria]);
|
||||
break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawBuildAircraftWindow(Window *w)
|
||||
{
|
||||
const buildvehicle_d *bv = &WP(w, buildvehicle_d);
|
||||
|
||||
SetWindowWidgetDisabledState(w, BUILD_VEHICLE_WIDGET_BUILD, w->window_number == 0);
|
||||
|
||||
SetVScrollCount(w, EngList_Count(&bv->eng_list));
|
||||
DrawWindowWidgets(w);
|
||||
|
||||
{
|
||||
int x = 2;
|
||||
int y = 27;
|
||||
EngineID selected_id = bv->sel_engine;
|
||||
EngineID eid = w->vscroll.pos;
|
||||
uint16 max = min(w->vscroll.pos + w->vscroll.cap, EngList_Count(&bv->eng_list));
|
||||
|
||||
for (; eid < max; eid++) {
|
||||
const EngineID engine = bv->eng_list[eid];
|
||||
|
||||
DrawString(x + 62, y + 7, GetCustomEngineName(engine), engine == selected_id ? 0xC : 0x10);
|
||||
DrawAircraftEngine(x + 29, y + 10, engine, GetEnginePalette(engine, _local_player));
|
||||
y += 24;
|
||||
}
|
||||
|
||||
if (selected_id != INVALID_ENGINE) {
|
||||
const Widget *wi = &w->widget[BUILD_VEHICLE_WIDGET_PANEL];
|
||||
DrawAircraftPurchaseInfo(x, wi->top + 1, wi->right - wi->left - 2, selected_id);
|
||||
}
|
||||
}
|
||||
DrawString(85, 15, _aircraft_sort_listing[bv->sort_criteria], 0x10);
|
||||
DoDrawString(bv->descending_sort_order ? DOWNARROW : UPARROW, 69, 15, 0x10);
|
||||
}
|
||||
|
||||
static void BuildAircraftClickEvent(Window *w, WindowEvent *e)
|
||||
{
|
||||
buildvehicle_d *bv = &WP(w, buildvehicle_d);
|
||||
|
||||
switch (e->we.click.widget) {
|
||||
case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING:
|
||||
bv->descending_sort_order ^= true;
|
||||
_last_sort_order = bv->descending_sort_order;
|
||||
GenerateBuildList(w);
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
|
||||
case BUILD_VEHICLE_WIDGET_LIST: {
|
||||
uint i = (e->we.click.pt.y - 26) / 24 + w->vscroll.pos;
|
||||
uint num_items = EngList_Count(&bv->eng_list);
|
||||
bv->sel_engine = (i < num_items) ? bv->eng_list[i] : INVALID_ENGINE;
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
}
|
||||
|
||||
case BUILD_VEHICLE_WIDGET_SORT_TEXT: case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN:/* Select sorting criteria dropdown menu */
|
||||
ShowDropDownMenu(w, _aircraft_sort_listing, bv->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, 0);
|
||||
return;
|
||||
|
||||
case BUILD_VEHICLE_WIDGET_BUILD: {
|
||||
EngineID sel_eng = bv->sel_engine;
|
||||
if (sel_eng != INVALID_ENGINE) {
|
||||
DoCommandP(w->window_number, sel_eng, 0, CcBuildAircraft, CMD_BUILD_AIRCRAFT | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BUILD_VEHICLE_WIDGET_RENAME: {
|
||||
EngineID sel_eng = bv->sel_engine;
|
||||
if (sel_eng != INVALID_ENGINE) {
|
||||
bv->rename_engine = sel_eng;
|
||||
ShowQueryString(GetCustomEngineName(sel_eng), STR_A039_RENAME_AIRCRAFT_TYPE, 31, 160, w, CS_ALPHANUMERAL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void NewAircraftWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
buildvehicle_d *bv = &WP(w, buildvehicle_d);
|
||||
|
||||
switch (e->event) {
|
||||
case WE_INVALIDATE_DATA:
|
||||
GenerateBuildList(w);
|
||||
break;
|
||||
|
||||
case WE_DESTROY:
|
||||
EngList_Destroy(&bv->eng_list);
|
||||
break;
|
||||
|
||||
case WE_PAINT:
|
||||
DrawBuildAircraftWindow(w);
|
||||
break;
|
||||
|
||||
case WE_CLICK:
|
||||
BuildAircraftClickEvent(w, e);
|
||||
break;
|
||||
|
||||
case WE_ON_EDIT_TEXT: {
|
||||
if (e->we.edittext.str[0] != '\0') {
|
||||
_cmd_text = e->we.edittext.str;
|
||||
DoCommandP(0, bv->rename_engine, 0, NULL, CMD_RENAME_ENGINE | CMD_MSG(STR_A03A_CAN_T_RENAME_AIRCRAFT_TYPE));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
|
||||
if (bv->sort_criteria != e->we.dropdown.index) {
|
||||
bv->sort_criteria = _last_sort_criteria = e->we.dropdown.index;
|
||||
GenerateBuildList(w);
|
||||
}
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
|
||||
case WE_RESIZE:
|
||||
w->vscroll.cap += e->we.sizing.diff.y / 24;
|
||||
w->widget[BUILD_VEHICLE_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const WindowDesc _build_vehicle_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 240, 226,
|
||||
WC_BUILD_VEHICLE,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
|
||||
_build_vehicle_widgets,
|
||||
NewAircraftWndProc
|
||||
};
|
||||
|
||||
void ShowBuildVehicleWindow(TileIndex tile, byte type)
|
||||
{
|
||||
buildvehicle_d *bv;
|
||||
Window *w;
|
||||
|
||||
DeleteWindowById(WC_BUILD_VEHICLE, tile);
|
||||
w = AllocateWindowDescFront(&_build_vehicle_desc, tile);
|
||||
if (w == NULL) return;
|
||||
|
||||
w->caption_color = (tile != 0) ? GetTileOwner(tile) : _local_player;
|
||||
w->resize.step_height = GetVehicleListHeight(type);
|
||||
w->vscroll.cap = 4;
|
||||
w->widget[BUILD_VEHICLE_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
|
||||
|
||||
bv = &WP(w, buildvehicle_d);
|
||||
EngList_Create(&bv->eng_list);
|
||||
bv->sel_engine = INVALID_ENGINE;
|
||||
bv->sort_criteria = _last_sort_criteria;
|
||||
bv->descending_sort_order = _last_sort_order;
|
||||
|
||||
bv->vehicle_type = type;
|
||||
|
||||
switch (type) {
|
||||
case VEH_Aircraft: {
|
||||
byte acc_planes = (tile == 0) ? ALL : GetAirport(GetStationByTile(tile)->airport_type)->acc_planes;
|
||||
bv->filter.acc_planes = acc_planes;
|
||||
break;
|
||||
}
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
|
||||
GenerateBuildList(w);
|
||||
/* Select the first plane in the list as default when opening the window */
|
||||
if (EngList_Count(&bv->eng_list) > 0) bv->sel_engine = bv->eng_list[0];
|
||||
}
|
||||
91
src/callback_table.c
Normal file
91
src/callback_table.c
Normal file
@@ -0,0 +1,91 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "callback_table.h"
|
||||
#include "functions.h"
|
||||
|
||||
// If you add a callback for DoCommandP, also add the callback in here
|
||||
// see below for the full list!
|
||||
// If you don't do it, it won't work across the network!!
|
||||
|
||||
/* aircraft_gui.c */
|
||||
CommandCallback CcBuildAircraft;
|
||||
CommandCallback CcCloneAircraft;
|
||||
|
||||
/* airport_gui.c */
|
||||
CommandCallback CcBuildAirport;
|
||||
|
||||
/* bridge_gui.c */
|
||||
CommandCallback CcBuildBridge;
|
||||
|
||||
/* dock_gui.c */
|
||||
CommandCallback CcBuildDocks;
|
||||
CommandCallback CcBuildCanal;
|
||||
|
||||
/* depot_gui.c */
|
||||
CommandCallback CcCloneVehicle;
|
||||
|
||||
/* main_gui.c */
|
||||
CommandCallback CcPlaySound10;
|
||||
CommandCallback CcPlaceSign;
|
||||
CommandCallback CcTerraform;
|
||||
CommandCallback CcBuildTown;
|
||||
|
||||
/* rail_gui.c */
|
||||
CommandCallback CcPlaySound1E;
|
||||
CommandCallback CcRailDepot;
|
||||
CommandCallback CcStation;
|
||||
CommandCallback CcBuildRailTunnel;
|
||||
|
||||
/* road_gui.c */
|
||||
CommandCallback CcPlaySound1D;
|
||||
CommandCallback CcBuildRoadTunnel;
|
||||
CommandCallback CcRoadDepot;
|
||||
|
||||
/* roadveh_gui.c */
|
||||
CommandCallback CcBuildRoadVeh;
|
||||
CommandCallback CcCloneRoadVeh;
|
||||
|
||||
/* ship_gui.c */
|
||||
CommandCallback CcBuildShip;
|
||||
CommandCallback CcCloneShip;
|
||||
|
||||
/* train_gui.c */
|
||||
CommandCallback CcBuildWagon;
|
||||
CommandCallback CcBuildLoco;
|
||||
CommandCallback CcCloneTrain;
|
||||
|
||||
CommandCallback CcAI;
|
||||
|
||||
CommandCallback *_callback_table[] = {
|
||||
/* 0x00 */ NULL,
|
||||
/* 0x01 */ CcBuildAircraft,
|
||||
/* 0x02 */ CcBuildAirport,
|
||||
/* 0x03 */ CcBuildBridge,
|
||||
/* 0x04 */ CcBuildCanal,
|
||||
/* 0x05 */ CcBuildDocks,
|
||||
/* 0x06 */ CcBuildLoco,
|
||||
/* 0x07 */ CcBuildRoadVeh,
|
||||
/* 0x08 */ CcBuildShip,
|
||||
/* 0x09 */ CcBuildTown,
|
||||
/* 0x0A */ CcBuildRoadTunnel,
|
||||
/* 0x0B */ CcBuildRailTunnel,
|
||||
/* 0x0C */ CcBuildWagon,
|
||||
/* 0x0D */ CcRoadDepot,
|
||||
/* 0x0E */ CcRailDepot,
|
||||
/* 0x0F */ CcPlaceSign,
|
||||
/* 0x10 */ CcPlaySound10,
|
||||
/* 0x11 */ CcPlaySound1D,
|
||||
/* 0x12 */ CcPlaySound1E,
|
||||
/* 0x13 */ CcStation,
|
||||
/* 0x14 */ CcTerraform,
|
||||
/* 0x15 */ CcCloneAircraft,
|
||||
/* 0x16 */ CcCloneRoadVeh,
|
||||
/* 0x17 */ CcCloneShip,
|
||||
/* 0x18 */ CcCloneTrain,
|
||||
/* 0x19 */ CcAI,
|
||||
/* 0x1A */ CcCloneVehicle
|
||||
};
|
||||
|
||||
const int _callback_table_count = lengthof(_callback_table);
|
||||
11
src/callback_table.h
Normal file
11
src/callback_table.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef CALLBACK_TABLE_H
|
||||
#define CALLBACK_TABLE_H
|
||||
|
||||
#include "command.h"
|
||||
|
||||
extern CommandCallback *_callback_table[];
|
||||
extern const int _callback_table_count;
|
||||
|
||||
#endif /* CALLBACK_TABLE_H */
|
||||
803
src/clear_cmd.c
Normal file
803
src/clear_cmd.c
Normal file
@@ -0,0 +1,803 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "clear_map.h"
|
||||
#include "rail_map.h"
|
||||
#include "table/strings.h"
|
||||
#include "functions.h"
|
||||
#include "map.h"
|
||||
#include "player.h"
|
||||
#include "tile.h"
|
||||
#include "viewport.h"
|
||||
#include "command.h"
|
||||
#include "tunnel_map.h"
|
||||
#include "bridge_map.h"
|
||||
#include "variables.h"
|
||||
#include "table/sprites.h"
|
||||
#include "unmovable_map.h"
|
||||
#include "genworld.h"
|
||||
#include "industry.h"
|
||||
|
||||
typedef struct TerraformerHeightMod {
|
||||
TileIndex tile;
|
||||
byte height;
|
||||
} TerraformerHeightMod;
|
||||
|
||||
typedef struct TerraformerState {
|
||||
int height[4];
|
||||
uint32 flags;
|
||||
|
||||
int direction;
|
||||
int modheight_count;
|
||||
int tile_table_count;
|
||||
|
||||
int32 cost;
|
||||
|
||||
TileIndex *tile_table;
|
||||
TerraformerHeightMod *modheight;
|
||||
|
||||
} TerraformerState;
|
||||
|
||||
static int TerraformAllowTileProcess(TerraformerState *ts, TileIndex tile)
|
||||
{
|
||||
TileIndex *t;
|
||||
int count;
|
||||
|
||||
if (TileX(tile) == MapMaxX() || TileY(tile) == MapMaxY()) return -1;
|
||||
|
||||
t = ts->tile_table;
|
||||
for (count = ts->tile_table_count; count != 0; count--, t++) {
|
||||
if (*t == tile) return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int TerraformGetHeightOfTile(TerraformerState *ts, TileIndex tile)
|
||||
{
|
||||
TerraformerHeightMod *mod = ts->modheight;
|
||||
int count;
|
||||
|
||||
for (count = ts->modheight_count; count != 0; count--, mod++) {
|
||||
if (mod->tile == tile) return mod->height;
|
||||
}
|
||||
|
||||
return TileHeight(tile);
|
||||
}
|
||||
|
||||
static void TerraformAddDirtyTile(TerraformerState *ts, TileIndex tile)
|
||||
{
|
||||
int count;
|
||||
TileIndex *t;
|
||||
|
||||
count = ts->tile_table_count;
|
||||
|
||||
if (count >= 625) return;
|
||||
|
||||
for (t = ts->tile_table; count != 0; count--,t++) {
|
||||
if (*t == tile) return;
|
||||
}
|
||||
|
||||
ts->tile_table[ts->tile_table_count++] = tile;
|
||||
}
|
||||
|
||||
static void TerraformAddDirtyTileAround(TerraformerState *ts, TileIndex tile)
|
||||
{
|
||||
TerraformAddDirtyTile(ts, tile + TileDiffXY( 0, -1));
|
||||
TerraformAddDirtyTile(ts, tile + TileDiffXY(-1, -1));
|
||||
TerraformAddDirtyTile(ts, tile + TileDiffXY(-1, 0));
|
||||
TerraformAddDirtyTile(ts, tile);
|
||||
}
|
||||
|
||||
static int TerraformProc(TerraformerState *ts, TileIndex tile, int mode)
|
||||
{
|
||||
int r;
|
||||
int32 ret;
|
||||
|
||||
assert(tile < MapSize());
|
||||
|
||||
r = TerraformAllowTileProcess(ts, tile);
|
||||
if (r <= 0) return r;
|
||||
|
||||
if (IsTileType(tile, MP_RAILWAY)) {
|
||||
static const TrackBits safe_track[] = { TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER, TRACK_BIT_RIGHT };
|
||||
static const Slope unsafe_slope[] = { SLOPE_S, SLOPE_W, SLOPE_N, SLOPE_E };
|
||||
|
||||
Slope tileh;
|
||||
uint z;
|
||||
|
||||
// Nothing could be built at the steep slope - this avoids a bug
|
||||
// when you have a single diagonal track in one corner on a
|
||||
// basement and then you raise/lower the other corner.
|
||||
tileh = GetTileSlope(tile, &z);
|
||||
if (tileh == unsafe_slope[mode] ||
|
||||
tileh == ComplementSlope(unsafe_slope[mode])) {
|
||||
_terraform_err_tile = tile;
|
||||
_error_message = STR_1008_MUST_REMOVE_RAILROAD_TRACK;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If we have a single diagonal track there, the other side of
|
||||
// tile can be terraformed.
|
||||
if (IsPlainRailTile(tile) && GetTrackBits(tile) == safe_track[mode]) {
|
||||
/* If terraforming downwards prevent damaging a potential tunnel below.
|
||||
* This check is only necessary for flat tiles, because if the tile is
|
||||
* non-flat, then the corner opposing the rail is raised. Only this corner
|
||||
* can be lowered and this is a safe action
|
||||
*/
|
||||
if (tileh == SLOPE_FLAT &&
|
||||
ts->direction == -1 &&
|
||||
IsTunnelInWay(tile, z - TILE_HEIGHT)) {
|
||||
_terraform_err_tile = tile;
|
||||
_error_message = STR_1002_EXCAVATION_WOULD_DAMAGE;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret = DoCommand(tile, 0,0, ts->flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR);
|
||||
|
||||
if (CmdFailed(ret)) {
|
||||
_terraform_err_tile = tile;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ts->cost += ret;
|
||||
|
||||
if (ts->tile_table_count >= 625) return -1;
|
||||
ts->tile_table[ts->tile_table_count++] = tile;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height)
|
||||
{
|
||||
int nh;
|
||||
TerraformerHeightMod *mod;
|
||||
int count;
|
||||
|
||||
assert(tile < MapSize());
|
||||
|
||||
if (height < 0) {
|
||||
_error_message = STR_1003_ALREADY_AT_SEA_LEVEL;
|
||||
return false;
|
||||
}
|
||||
|
||||
_error_message = STR_1004_TOO_HIGH;
|
||||
|
||||
if (height > 15) return false;
|
||||
|
||||
nh = TerraformGetHeightOfTile(ts, tile);
|
||||
if (nh < 0 || height == nh) return false;
|
||||
|
||||
if (TerraformProc(ts, tile, 0) < 0) return false;
|
||||
if (TerraformProc(ts, tile + TileDiffXY( 0, -1), 1) < 0) return false;
|
||||
if (TerraformProc(ts, tile + TileDiffXY(-1, -1), 2) < 0) return false;
|
||||
if (TerraformProc(ts, tile + TileDiffXY(-1, 0), 3) < 0) return false;
|
||||
|
||||
mod = ts->modheight;
|
||||
count = ts->modheight_count;
|
||||
|
||||
for (;;) {
|
||||
if (count == 0) {
|
||||
if (ts->modheight_count >= 576) return false;
|
||||
ts->modheight_count++;
|
||||
break;
|
||||
}
|
||||
if (mod->tile == tile) break;
|
||||
mod++;
|
||||
count--;
|
||||
}
|
||||
|
||||
mod->tile = tile;
|
||||
mod->height = (byte)height;
|
||||
|
||||
ts->cost += _price.terraform;
|
||||
|
||||
{
|
||||
int direction = ts->direction, r;
|
||||
const TileIndexDiffC *ttm;
|
||||
|
||||
static const TileIndexDiffC _terraform_tilepos[] = {
|
||||
{ 1, 0},
|
||||
{-2, 0},
|
||||
{ 1, 1},
|
||||
{ 0, -2}
|
||||
};
|
||||
|
||||
for (ttm = _terraform_tilepos; ttm != endof(_terraform_tilepos); ttm++) {
|
||||
tile += ToTileIndexDiff(*ttm);
|
||||
|
||||
r = TerraformGetHeightOfTile(ts, tile);
|
||||
if (r != height && r-direction != height && r+direction != height) {
|
||||
if (!TerraformTileHeight(ts, tile, r+direction))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Terraform land
|
||||
* @param tile tile to terraform
|
||||
* @param p1 corners to terraform.
|
||||
* @param p2 direction; eg up or down
|
||||
*/
|
||||
int32 CmdTerraformLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
||||
{
|
||||
TerraformerState ts;
|
||||
TileIndex t;
|
||||
int direction;
|
||||
|
||||
TerraformerHeightMod modheight_data[576];
|
||||
TileIndex tile_table_data[625];
|
||||
|
||||
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
||||
|
||||
_terraform_err_tile = 0;
|
||||
|
||||
ts.direction = direction = p2 ? 1 : -1;
|
||||
ts.flags = flags;
|
||||
ts.modheight_count = ts.tile_table_count = 0;
|
||||
ts.cost = 0;
|
||||
ts.modheight = modheight_data;
|
||||
ts.tile_table = tile_table_data;
|
||||
|
||||
/* Make an extra check for map-bounds cause we add tiles to the originating tile */
|
||||
if (tile + TileDiffXY(1, 1) >= MapSize()) return CMD_ERROR;
|
||||
|
||||
if (p1 & 1) {
|
||||
t = tile + TileDiffXY(1, 0);
|
||||
if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
|
||||
return CMD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (p1 & 2) {
|
||||
t = tile + TileDiffXY(1, 1);
|
||||
if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
|
||||
return CMD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (p1 & 4) {
|
||||
t = tile + TileDiffXY(0, 1);
|
||||
if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
|
||||
return CMD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (p1 & 8) {
|
||||
t = tile + TileDiffXY(0, 0);
|
||||
if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
|
||||
return CMD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* Check if tunnel would take damage */
|
||||
int count;
|
||||
TileIndex *ti = ts.tile_table;
|
||||
|
||||
for (count = ts.tile_table_count; count != 0; count--, ti++) {
|
||||
TileIndex tile = *ti;
|
||||
|
||||
if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) {
|
||||
return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
|
||||
}
|
||||
|
||||
if (direction == -1) {
|
||||
uint z, t;
|
||||
|
||||
z = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0));
|
||||
t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0));
|
||||
if (t <= z) z = t;
|
||||
t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 1));
|
||||
if (t <= z) z = t;
|
||||
t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 1));
|
||||
if (t <= z) z = t;
|
||||
|
||||
if (IsTunnelInWay(tile, z * TILE_HEIGHT)) {
|
||||
return_cmd_error(STR_1002_EXCAVATION_WOULD_DAMAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
/* Clear the landscape at the tiles */
|
||||
{
|
||||
int count;
|
||||
TileIndex *ti = ts.tile_table;
|
||||
for (count = ts.tile_table_count; count != 0; count--, ti++) {
|
||||
DoCommand(*ti, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
|
||||
}
|
||||
}
|
||||
|
||||
/* change the height */
|
||||
{
|
||||
int count;
|
||||
TerraformerHeightMod *mod;
|
||||
|
||||
mod = ts.modheight;
|
||||
for (count = ts.modheight_count; count != 0; count--, mod++) {
|
||||
TileIndex til = mod->tile;
|
||||
|
||||
SetTileHeight(til, mod->height);
|
||||
TerraformAddDirtyTileAround(&ts, til);
|
||||
}
|
||||
}
|
||||
|
||||
/* finally mark the dirty tiles dirty */
|
||||
{
|
||||
int count;
|
||||
TileIndex *ti = ts.tile_table;
|
||||
for (count = ts.tile_table_count; count != 0; count--, ti++) {
|
||||
MarkTileDirtyByTile(*ti);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ts.cost;
|
||||
}
|
||||
|
||||
|
||||
/** Levels a selected (rectangle) area of land
|
||||
* @param tile end tile of area-drag
|
||||
* @param p1 start tile of area drag
|
||||
* @param p2 unused
|
||||
*/
|
||||
int32 CmdLevelLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
||||
{
|
||||
int size_x, size_y;
|
||||
int ex;
|
||||
int ey;
|
||||
int sx, sy;
|
||||
uint h, curh;
|
||||
int32 ret, cost, money;
|
||||
|
||||
if (p1 >= MapSize()) return CMD_ERROR;
|
||||
|
||||
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
||||
|
||||
// remember level height
|
||||
h = TileHeight(p1);
|
||||
|
||||
// make sure sx,sy are smaller than ex,ey
|
||||
ex = TileX(tile);
|
||||
ey = TileY(tile);
|
||||
sx = TileX(p1);
|
||||
sy = TileY(p1);
|
||||
if (ex < sx) intswap(ex, sx);
|
||||
if (ey < sy) intswap(ey, sy);
|
||||
tile = TileXY(sx, sy);
|
||||
|
||||
size_x = ex-sx+1;
|
||||
size_y = ey-sy+1;
|
||||
|
||||
money = GetAvailableMoneyForCommand();
|
||||
cost = 0;
|
||||
|
||||
BEGIN_TILE_LOOP(tile2, size_x, size_y, tile) {
|
||||
curh = TileHeight(tile2);
|
||||
while (curh != h) {
|
||||
ret = DoCommand(tile2, 8, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND);
|
||||
if (CmdFailed(ret)) break;
|
||||
cost += ret;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
if ((money -= ret) < 0) {
|
||||
_additional_cash_required = ret;
|
||||
return cost - ret;
|
||||
}
|
||||
DoCommand(tile2, 8, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND);
|
||||
}
|
||||
|
||||
curh += (curh > h) ? -1 : 1;
|
||||
}
|
||||
} END_TILE_LOOP(tile2, size_x, size_y, tile)
|
||||
|
||||
return (cost == 0) ? CMD_ERROR : cost;
|
||||
}
|
||||
|
||||
/** Purchase a land area. Actually you only purchase one tile, so
|
||||
* the name is a bit confusing ;p
|
||||
* @param tile the tile the player is purchasing
|
||||
* @param p1 unused
|
||||
* @param p2 unused
|
||||
*/
|
||||
int32 CmdPurchaseLandArea(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
||||
{
|
||||
int32 cost;
|
||||
|
||||
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
||||
|
||||
if (!EnsureNoVehicle(tile)) return CMD_ERROR;
|
||||
|
||||
if (IsOwnedLandTile(tile) && IsTileOwner(tile, _current_player)) {
|
||||
return_cmd_error(STR_5807_YOU_ALREADY_OWN_IT);
|
||||
}
|
||||
|
||||
cost = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
|
||||
if (CmdFailed(cost)) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
MakeOwnedLand(tile, _current_player);
|
||||
MarkTileDirtyByTile(tile);
|
||||
}
|
||||
|
||||
return cost + _price.purchase_land * 10;
|
||||
}
|
||||
|
||||
|
||||
static int32 ClearTile_Clear(TileIndex tile, byte flags)
|
||||
{
|
||||
static const int32* clear_price_table[] = {
|
||||
&_price.clear_1,
|
||||
&_price.purchase_land,
|
||||
&_price.clear_2,
|
||||
&_price.clear_3,
|
||||
&_price.purchase_land,
|
||||
&_price.purchase_land,
|
||||
&_price.clear_2, // XXX unused?
|
||||
};
|
||||
int32 price;
|
||||
|
||||
if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) == 0) {
|
||||
price = 0;
|
||||
} else {
|
||||
price = *clear_price_table[GetClearGround(tile)];
|
||||
}
|
||||
|
||||
if (flags & DC_EXEC) DoClearSquare(tile);
|
||||
|
||||
return price;
|
||||
}
|
||||
|
||||
/** Sell a land area. Actually you only sell one tile, so
|
||||
* the name is a bit confusing ;p
|
||||
* @param tile the tile the player is selling
|
||||
* @param p1 unused
|
||||
* @param p2 unused
|
||||
*/
|
||||
int32 CmdSellLandArea(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
||||
{
|
||||
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
||||
|
||||
if (!IsOwnedLandTile(tile)) return CMD_ERROR;
|
||||
if (!CheckTileOwnership(tile) && _current_player != OWNER_WATER) return CMD_ERROR;
|
||||
|
||||
|
||||
if (!EnsureNoVehicle(tile)) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) DoClearSquare(tile);
|
||||
|
||||
return - _price.purchase_land * 2;
|
||||
}
|
||||
|
||||
|
||||
#include "table/clear_land.h"
|
||||
|
||||
|
||||
void DrawClearLandTile(const TileInfo *ti, byte set)
|
||||
{
|
||||
DrawGroundSprite(SPR_FLAT_BARE_LAND + _tileh_to_sprite[ti->tileh] + set * 19);
|
||||
}
|
||||
|
||||
void DrawHillyLandTile(const TileInfo *ti)
|
||||
{
|
||||
if (ti->tileh != SLOPE_FLAT) {
|
||||
DrawGroundSprite(SPR_FLAT_ROUGH_LAND + _tileh_to_sprite[ti->tileh]);
|
||||
} else {
|
||||
DrawGroundSprite(_landscape_clear_sprites[GB(ti->x ^ ti->y, 4, 3)]);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawClearLandFence(const TileInfo *ti)
|
||||
{
|
||||
byte z = ti->z;
|
||||
|
||||
if (ti->tileh & SLOPE_S) {
|
||||
z += TILE_HEIGHT;
|
||||
if (ti->tileh == SLOPE_STEEP_S) z += TILE_HEIGHT;
|
||||
}
|
||||
|
||||
if (GetFenceSW(ti->tile) != 0) {
|
||||
DrawGroundSpriteAt(_clear_land_fence_sprites_1[GetFenceSW(ti->tile) - 1] + _fence_mod_by_tileh[ti->tileh], ti->x, ti->y, z);
|
||||
}
|
||||
|
||||
if (GetFenceSE(ti->tile) != 0) {
|
||||
DrawGroundSpriteAt(_clear_land_fence_sprites_1[GetFenceSE(ti->tile) - 1] + _fence_mod_by_tileh_2[ti->tileh], ti->x, ti->y, z);
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawTile_Clear(TileInfo *ti)
|
||||
{
|
||||
switch (GetClearGround(ti->tile)) {
|
||||
case CLEAR_GRASS:
|
||||
DrawClearLandTile(ti, GetClearDensity(ti->tile));
|
||||
break;
|
||||
|
||||
case CLEAR_ROUGH:
|
||||
DrawHillyLandTile(ti);
|
||||
break;
|
||||
|
||||
case CLEAR_ROCKS:
|
||||
DrawGroundSprite(SPR_FLAT_ROCKY_LAND_1 + _tileh_to_sprite[ti->tileh]);
|
||||
break;
|
||||
|
||||
case CLEAR_FIELDS:
|
||||
DrawGroundSprite(_clear_land_sprites_1[GetFieldType(ti->tile)] + _tileh_to_sprite[ti->tileh]);
|
||||
break;
|
||||
|
||||
case CLEAR_SNOW:
|
||||
DrawGroundSprite(_clear_land_sprites_2[GetClearDensity(ti->tile)] + _tileh_to_sprite[ti->tileh]);
|
||||
break;
|
||||
|
||||
case CLEAR_DESERT:
|
||||
DrawGroundSprite(_clear_land_sprites_3[GetClearDensity(ti->tile)] + _tileh_to_sprite[ti->tileh]);
|
||||
break;
|
||||
}
|
||||
|
||||
DrawClearLandFence(ti);
|
||||
DrawBridgeMiddle(ti);
|
||||
}
|
||||
|
||||
static uint GetSlopeZ_Clear(TileIndex tile, uint x, uint y)
|
||||
{
|
||||
uint z;
|
||||
uint tileh = GetTileSlope(tile, &z);
|
||||
|
||||
return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
|
||||
}
|
||||
|
||||
static Slope GetSlopeTileh_Clear(TileIndex tile, Slope tileh)
|
||||
{
|
||||
return tileh;
|
||||
}
|
||||
|
||||
static void GetAcceptedCargo_Clear(TileIndex tile, AcceptedCargo ac)
|
||||
{
|
||||
/* unused */
|
||||
}
|
||||
|
||||
static void AnimateTile_Clear(TileIndex tile)
|
||||
{
|
||||
/* unused */
|
||||
}
|
||||
|
||||
void TileLoopClearHelper(TileIndex tile)
|
||||
{
|
||||
byte self;
|
||||
byte neighbour;
|
||||
TileIndex dirty = INVALID_TILE;
|
||||
|
||||
self = (IsTileType(tile, MP_CLEAR) && IsClearGround(tile, CLEAR_FIELDS));
|
||||
|
||||
neighbour = (IsTileType(TILE_ADDXY(tile, 1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 1, 0), CLEAR_FIELDS));
|
||||
if (GetFenceSW(tile) == 0) {
|
||||
if (self != neighbour) {
|
||||
SetFenceSW(tile, 3);
|
||||
dirty = tile;
|
||||
}
|
||||
} else {
|
||||
if (self == 0 && neighbour == 0) {
|
||||
SetFenceSW(tile, 0);
|
||||
dirty = tile;
|
||||
}
|
||||
}
|
||||
|
||||
neighbour = (IsTileType(TILE_ADDXY(tile, 0, 1), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 0, 1), CLEAR_FIELDS));
|
||||
if (GetFenceSE(tile) == 0) {
|
||||
if (self != neighbour) {
|
||||
SetFenceSE(tile, 3);
|
||||
dirty = tile;
|
||||
}
|
||||
} else {
|
||||
if (self == 0 && neighbour == 0) {
|
||||
SetFenceSE(tile, 0);
|
||||
dirty = tile;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty != INVALID_TILE) MarkTileDirtyByTile(dirty);
|
||||
}
|
||||
|
||||
|
||||
/* convert into snowy tiles */
|
||||
static void TileLoopClearAlps(TileIndex tile)
|
||||
{
|
||||
int k = GetTileZ(tile) - _opt.snow_line + TILE_HEIGHT;
|
||||
|
||||
if (k < 0) { // well below the snow line
|
||||
if (!IsClearGround(tile, CLEAR_SNOW)) return;
|
||||
if (GetClearDensity(tile) == 0) SetClearGroundDensity(tile, CLEAR_GRASS, 3);
|
||||
} else {
|
||||
if (!IsClearGround(tile, CLEAR_SNOW)) {
|
||||
SetClearGroundDensity(tile, CLEAR_SNOW, 0);
|
||||
} else {
|
||||
uint density = min((uint)k / TILE_HEIGHT, 3);
|
||||
|
||||
if (GetClearDensity(tile) < density) {
|
||||
AddClearDensity(tile, 1);
|
||||
} else if (GetClearDensity(tile) > density) {
|
||||
AddClearDensity(tile, -1);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MarkTileDirtyByTile(tile);
|
||||
}
|
||||
|
||||
static void TileLoopClearDesert(TileIndex tile)
|
||||
{
|
||||
if (IsClearGround(tile, CLEAR_DESERT)) return;
|
||||
|
||||
if (GetTropicZone(tile) == TROPICZONE_DESERT) {
|
||||
SetClearGroundDensity(tile, CLEAR_DESERT, 3);
|
||||
} else {
|
||||
if (GetTropicZone(tile + TileDiffXY( 1, 0)) != TROPICZONE_DESERT &&
|
||||
GetTropicZone(tile + TileDiffXY(-1, 0)) != TROPICZONE_DESERT &&
|
||||
GetTropicZone(tile + TileDiffXY( 0, 1)) != TROPICZONE_DESERT &&
|
||||
GetTropicZone(tile + TileDiffXY( 0, -1)) != TROPICZONE_DESERT)
|
||||
return;
|
||||
SetClearGroundDensity(tile, CLEAR_DESERT, 1);
|
||||
}
|
||||
|
||||
MarkTileDirtyByTile(tile);
|
||||
}
|
||||
|
||||
static void TileLoop_Clear(TileIndex tile)
|
||||
{
|
||||
TileLoopClearHelper(tile);
|
||||
|
||||
switch (_opt.landscape) {
|
||||
case LT_DESERT: TileLoopClearDesert(tile); break;
|
||||
case LT_HILLY: TileLoopClearAlps(tile); break;
|
||||
}
|
||||
|
||||
switch (GetClearGround(tile)) {
|
||||
case CLEAR_GRASS:
|
||||
if (GetClearDensity(tile) == 3) return;
|
||||
|
||||
if (_game_mode != GM_EDITOR) {
|
||||
if (GetClearCounter(tile) < 7) {
|
||||
AddClearCounter(tile, 1);
|
||||
return;
|
||||
} else {
|
||||
SetClearCounter(tile, 0);
|
||||
AddClearDensity(tile, 1);
|
||||
}
|
||||
} else {
|
||||
SetClearGroundDensity(tile, GB(Random(), 0, 8) > 21 ? CLEAR_GRASS : CLEAR_ROUGH, 3);
|
||||
}
|
||||
break;
|
||||
|
||||
case CLEAR_FIELDS: {
|
||||
uint field_type;
|
||||
|
||||
if (_game_mode == GM_EDITOR) return;
|
||||
|
||||
if (GetClearCounter(tile) < 7) {
|
||||
AddClearCounter(tile, 1);
|
||||
return;
|
||||
} else {
|
||||
SetClearCounter(tile, 0);
|
||||
}
|
||||
|
||||
if (GetIndustryIndexOfField(tile) == INVALID_INDUSTRY && GetFieldType(tile) >= 7) {
|
||||
/* This farmfield is no longer farmfield, so make it grass again */
|
||||
MakeClear(tile, CLEAR_GRASS, 2);
|
||||
} else {
|
||||
field_type = GetFieldType(tile);
|
||||
field_type = (field_type < 8) ? field_type + 1 : 0;
|
||||
SetFieldType(tile, field_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
MarkTileDirtyByTile(tile);
|
||||
}
|
||||
|
||||
void GenerateClearTile(void)
|
||||
{
|
||||
uint i, gi;
|
||||
TileIndex tile;
|
||||
|
||||
/* add rough tiles */
|
||||
i = ScaleByMapSize(GB(Random(), 0, 10) + 0x400);
|
||||
gi = ScaleByMapSize(GB(Random(), 0, 7) + 0x80);
|
||||
|
||||
SetGeneratingWorldProgress(GWP_ROUGH_ROCKY, gi + i);
|
||||
do {
|
||||
IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
|
||||
tile = RandomTile();
|
||||
if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) SetClearGroundDensity(tile, CLEAR_ROUGH, 3);
|
||||
} while (--i);
|
||||
|
||||
/* add rocky tiles */
|
||||
i = gi;
|
||||
do {
|
||||
uint32 r = Random();
|
||||
tile = RandomTileSeed(r);
|
||||
|
||||
IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
|
||||
if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) {
|
||||
uint j = GB(r, 16, 4) + 5;
|
||||
for (;;) {
|
||||
TileIndex tile_new;
|
||||
|
||||
SetClearGroundDensity(tile, CLEAR_ROCKS, 3);
|
||||
do {
|
||||
if (--j == 0) goto get_out;
|
||||
tile_new = tile + TileOffsByDiagDir(GB(Random(), 0, 2));
|
||||
} while (!IsTileType(tile_new, MP_CLEAR) || IsClearGround(tile_new, CLEAR_DESERT));
|
||||
tile = tile_new;
|
||||
}
|
||||
get_out:;
|
||||
}
|
||||
} while (--i);
|
||||
}
|
||||
|
||||
static void ClickTile_Clear(TileIndex tile)
|
||||
{
|
||||
/* not used */
|
||||
}
|
||||
|
||||
static uint32 GetTileTrackStatus_Clear(TileIndex tile, TransportType mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const StringID _clear_land_str[] = {
|
||||
STR_080D_GRASS,
|
||||
STR_080B_ROUGH_LAND,
|
||||
STR_080A_ROCKS,
|
||||
STR_080E_FIELDS,
|
||||
STR_080F_SNOW_COVERED_LAND,
|
||||
STR_0810_DESERT
|
||||
};
|
||||
|
||||
static void GetTileDesc_Clear(TileIndex tile, TileDesc *td)
|
||||
{
|
||||
if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) == 0) {
|
||||
td->str = STR_080C_BARE_LAND;
|
||||
} else {
|
||||
td->str = _clear_land_str[GetClearGround(tile)];
|
||||
}
|
||||
td->owner = GetTileOwner(tile);
|
||||
}
|
||||
|
||||
static void ChangeTileOwner_Clear(TileIndex tile, PlayerID old_player, PlayerID new_player)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void InitializeClearLand(void)
|
||||
{
|
||||
_opt.snow_line = _patches.snow_line_height * TILE_HEIGHT;
|
||||
}
|
||||
|
||||
const TileTypeProcs _tile_type_clear_procs = {
|
||||
DrawTile_Clear, /* draw_tile_proc */
|
||||
GetSlopeZ_Clear, /* get_slope_z_proc */
|
||||
ClearTile_Clear, /* clear_tile_proc */
|
||||
GetAcceptedCargo_Clear, /* get_accepted_cargo_proc */
|
||||
GetTileDesc_Clear, /* get_tile_desc_proc */
|
||||
GetTileTrackStatus_Clear, /* get_tile_track_status_proc */
|
||||
ClickTile_Clear, /* click_tile_proc */
|
||||
AnimateTile_Clear, /* animate_tile_proc */
|
||||
TileLoop_Clear, /* tile_loop_clear */
|
||||
ChangeTileOwner_Clear, /* change_tile_owner_clear */
|
||||
NULL, /* get_produced_cargo_proc */
|
||||
NULL, /* vehicle_enter_tile_proc */
|
||||
GetSlopeTileh_Clear, /* get_slope_tileh_proc */
|
||||
};
|
||||
145
src/clear_map.h
Normal file
145
src/clear_map.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef CLEAR_MAP_H
|
||||
#define CLEAR_MAP_H
|
||||
|
||||
#include "macros.h"
|
||||
#include "tile.h"
|
||||
|
||||
/* ground type, m5 bits 2...4
|
||||
* valid densities (bits 0...1) in comments after the enum
|
||||
*/
|
||||
typedef enum ClearGround {
|
||||
CLEAR_GRASS = 0, // 0-3
|
||||
CLEAR_ROUGH = 1, // 3
|
||||
CLEAR_ROCKS = 2, // 3
|
||||
CLEAR_FIELDS = 3, // 3
|
||||
CLEAR_SNOW = 4, // 0-3
|
||||
CLEAR_DESERT = 5 // 1,3
|
||||
} ClearGround;
|
||||
|
||||
|
||||
static inline ClearGround GetClearGround(TileIndex t)
|
||||
{
|
||||
assert(IsTileType(t, MP_CLEAR));
|
||||
return GB(_m[t].m5, 2, 3);
|
||||
}
|
||||
|
||||
static inline bool IsClearGround(TileIndex t, ClearGround ct)
|
||||
{
|
||||
return GetClearGround(t) == ct;
|
||||
}
|
||||
|
||||
|
||||
static inline uint GetClearDensity(TileIndex t)
|
||||
{
|
||||
assert(IsTileType(t, MP_CLEAR));
|
||||
return GB(_m[t].m5, 0, 2);
|
||||
}
|
||||
|
||||
static inline void AddClearDensity(TileIndex t, int d)
|
||||
{
|
||||
assert(IsTileType(t, MP_CLEAR)); // XXX incomplete
|
||||
_m[t].m5 += d;
|
||||
}
|
||||
|
||||
|
||||
static inline uint GetClearCounter(TileIndex t)
|
||||
{
|
||||
assert(IsTileType(t, MP_CLEAR));
|
||||
return GB(_m[t].m5, 5, 3);
|
||||
}
|
||||
|
||||
static inline void AddClearCounter(TileIndex t, int c)
|
||||
{
|
||||
assert(IsTileType(t, MP_CLEAR)); // XXX incomplete
|
||||
_m[t].m5 += c << 5;
|
||||
}
|
||||
|
||||
static inline void SetClearCounter(TileIndex t, uint c)
|
||||
{
|
||||
assert(IsTileType(t, MP_CLEAR)); // XXX incomplete
|
||||
SB(_m[t].m5, 5, 3, c);
|
||||
}
|
||||
|
||||
|
||||
/* Sets type and density in one go, also sets the counter to 0 */
|
||||
static inline void SetClearGroundDensity(TileIndex t, ClearGround type, uint density)
|
||||
{
|
||||
assert(IsTileType(t, MP_CLEAR)); // XXX incomplete
|
||||
_m[t].m5 = 0 << 5 | type << 2 | density;
|
||||
}
|
||||
|
||||
|
||||
static inline uint GetFieldType(TileIndex t)
|
||||
{
|
||||
assert(GetClearGround(t) == CLEAR_FIELDS);
|
||||
return GB(_m[t].m3, 0, 4);
|
||||
}
|
||||
|
||||
static inline void SetFieldType(TileIndex t, uint f)
|
||||
{
|
||||
assert(GetClearGround(t) == CLEAR_FIELDS); // XXX incomplete
|
||||
SB(_m[t].m3, 0, 4, f);
|
||||
}
|
||||
|
||||
static inline uint16 GetIndustryIndexOfField(TileIndex t)
|
||||
{
|
||||
assert(GetClearGround(t) == CLEAR_FIELDS);
|
||||
return _m[t].m2;
|
||||
}
|
||||
|
||||
static inline void SetIndustryIndexOfField(TileIndex t, uint16 i)
|
||||
{
|
||||
assert(GetClearGround(t) == CLEAR_FIELDS);
|
||||
_m[t].m2 = i;
|
||||
}
|
||||
|
||||
/* Is used by tree tiles, too */
|
||||
static inline uint GetFenceSE(TileIndex t)
|
||||
{
|
||||
assert(IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES));
|
||||
return GB(_m[t].m4, 2, 3);
|
||||
}
|
||||
|
||||
static inline void SetFenceSE(TileIndex t, uint h)
|
||||
{
|
||||
assert(IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)); // XXX incomplete
|
||||
SB(_m[t].m4, 2, 3, h);
|
||||
}
|
||||
|
||||
static inline uint GetFenceSW(TileIndex t)
|
||||
{
|
||||
assert(IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES));
|
||||
return GB(_m[t].m4, 5, 3);
|
||||
}
|
||||
|
||||
static inline void SetFenceSW(TileIndex t, uint h)
|
||||
{
|
||||
assert(IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)); // XXX incomplete
|
||||
SB(_m[t].m4, 5, 3, h);
|
||||
}
|
||||
|
||||
|
||||
static inline void MakeClear(TileIndex t, ClearGround g, uint density)
|
||||
{
|
||||
SetTileType(t, MP_CLEAR);
|
||||
SetTileOwner(t, OWNER_NONE);
|
||||
_m[t].m2 = 0;
|
||||
_m[t].m3 = 0;
|
||||
_m[t].m4 = 0 << 5 | 0 << 2;
|
||||
SetClearGroundDensity(t, g, density);
|
||||
}
|
||||
|
||||
|
||||
static inline void MakeField(TileIndex t, uint field_type, uint16 industry)
|
||||
{
|
||||
SetTileType(t, MP_CLEAR);
|
||||
SetTileOwner(t, OWNER_NONE);
|
||||
_m[t].m2 = industry;
|
||||
_m[t].m3 = field_type;
|
||||
_m[t].m4 = 0 << 5 | 0 << 2;
|
||||
SetClearGroundDensity(t, CLEAR_FIELDS, 3);
|
||||
}
|
||||
|
||||
#endif /* CLEAR_MAP_H */
|
||||
562
src/command.c
Normal file
562
src/command.c
Normal file
@@ -0,0 +1,562 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "table/strings.h"
|
||||
#include "functions.h"
|
||||
#include "map.h"
|
||||
#include "gui.h"
|
||||
#include "command.h"
|
||||
#include "player.h"
|
||||
#include "network/network.h"
|
||||
#include "variables.h"
|
||||
#include "genworld.h"
|
||||
|
||||
const char* _cmd_text = NULL;
|
||||
|
||||
#define DEF_COMMAND(yyyy) int32 yyyy(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
||||
|
||||
DEF_COMMAND(CmdBuildRailroadTrack);
|
||||
DEF_COMMAND(CmdRemoveRailroadTrack);
|
||||
DEF_COMMAND(CmdBuildSingleRail);
|
||||
DEF_COMMAND(CmdRemoveSingleRail);
|
||||
|
||||
DEF_COMMAND(CmdLandscapeClear);
|
||||
|
||||
DEF_COMMAND(CmdBuildBridge);
|
||||
|
||||
DEF_COMMAND(CmdBuildRailroadStation);
|
||||
DEF_COMMAND(CmdRemoveFromRailroadStation);
|
||||
DEF_COMMAND(CmdConvertRail);
|
||||
|
||||
DEF_COMMAND(CmdBuildSingleSignal);
|
||||
DEF_COMMAND(CmdRemoveSingleSignal);
|
||||
|
||||
DEF_COMMAND(CmdTerraformLand);
|
||||
|
||||
DEF_COMMAND(CmdPurchaseLandArea);
|
||||
DEF_COMMAND(CmdSellLandArea);
|
||||
|
||||
DEF_COMMAND(CmdBuildTunnel);
|
||||
|
||||
DEF_COMMAND(CmdBuildTrainDepot);
|
||||
DEF_COMMAND(CmdBuildTrainWaypoint);
|
||||
DEF_COMMAND(CmdRenameWaypoint);
|
||||
DEF_COMMAND(CmdRemoveTrainWaypoint);
|
||||
|
||||
DEF_COMMAND(CmdBuildRoadStop);
|
||||
|
||||
DEF_COMMAND(CmdBuildLongRoad);
|
||||
DEF_COMMAND(CmdRemoveLongRoad);
|
||||
DEF_COMMAND(CmdBuildRoad);
|
||||
DEF_COMMAND(CmdRemoveRoad);
|
||||
|
||||
DEF_COMMAND(CmdBuildRoadDepot);
|
||||
|
||||
DEF_COMMAND(CmdBuildAirport);
|
||||
|
||||
DEF_COMMAND(CmdBuildDock);
|
||||
|
||||
DEF_COMMAND(CmdBuildShipDepot);
|
||||
|
||||
DEF_COMMAND(CmdBuildBuoy);
|
||||
|
||||
DEF_COMMAND(CmdPlantTree);
|
||||
|
||||
DEF_COMMAND(CmdBuildRailVehicle);
|
||||
DEF_COMMAND(CmdMoveRailVehicle);
|
||||
|
||||
DEF_COMMAND(CmdStartStopTrain);
|
||||
|
||||
DEF_COMMAND(CmdSellRailWagon);
|
||||
|
||||
DEF_COMMAND(CmdSendTrainToDepot);
|
||||
DEF_COMMAND(CmdForceTrainProceed);
|
||||
DEF_COMMAND(CmdReverseTrainDirection);
|
||||
|
||||
DEF_COMMAND(CmdModifyOrder);
|
||||
DEF_COMMAND(CmdSkipOrder);
|
||||
DEF_COMMAND(CmdDeleteOrder);
|
||||
DEF_COMMAND(CmdInsertOrder);
|
||||
DEF_COMMAND(CmdChangeServiceInt);
|
||||
DEF_COMMAND(CmdRestoreOrderIndex);
|
||||
|
||||
DEF_COMMAND(CmdBuildIndustry);
|
||||
|
||||
DEF_COMMAND(CmdBuildCompanyHQ);
|
||||
DEF_COMMAND(CmdSetPlayerFace);
|
||||
DEF_COMMAND(CmdSetPlayerColor);
|
||||
|
||||
DEF_COMMAND(CmdIncreaseLoan);
|
||||
DEF_COMMAND(CmdDecreaseLoan);
|
||||
|
||||
DEF_COMMAND(CmdWantEnginePreview);
|
||||
|
||||
DEF_COMMAND(CmdNameVehicle);
|
||||
DEF_COMMAND(CmdRenameEngine);
|
||||
|
||||
DEF_COMMAND(CmdChangeCompanyName);
|
||||
DEF_COMMAND(CmdChangePresidentName);
|
||||
|
||||
DEF_COMMAND(CmdRenameStation);
|
||||
|
||||
DEF_COMMAND(CmdSellAircraft);
|
||||
DEF_COMMAND(CmdStartStopAircraft);
|
||||
DEF_COMMAND(CmdBuildAircraft);
|
||||
DEF_COMMAND(CmdSendAircraftToHangar);
|
||||
DEF_COMMAND(CmdRefitAircraft);
|
||||
|
||||
DEF_COMMAND(CmdPlaceSign);
|
||||
DEF_COMMAND(CmdRenameSign);
|
||||
|
||||
DEF_COMMAND(CmdBuildRoadVeh);
|
||||
DEF_COMMAND(CmdStartStopRoadVeh);
|
||||
DEF_COMMAND(CmdSellRoadVeh);
|
||||
DEF_COMMAND(CmdSendRoadVehToDepot);
|
||||
DEF_COMMAND(CmdTurnRoadVeh);
|
||||
DEF_COMMAND(CmdRefitRoadVeh);
|
||||
|
||||
DEF_COMMAND(CmdPause);
|
||||
|
||||
DEF_COMMAND(CmdBuyShareInCompany);
|
||||
DEF_COMMAND(CmdSellShareInCompany);
|
||||
DEF_COMMAND(CmdBuyCompany);
|
||||
|
||||
DEF_COMMAND(CmdBuildTown);
|
||||
|
||||
DEF_COMMAND(CmdRenameTown);
|
||||
DEF_COMMAND(CmdDoTownAction);
|
||||
|
||||
DEF_COMMAND(CmdSetRoadDriveSide);
|
||||
|
||||
DEF_COMMAND(CmdChangeDifficultyLevel);
|
||||
DEF_COMMAND(CmdChangePatchSetting);
|
||||
|
||||
DEF_COMMAND(CmdStartStopShip);
|
||||
DEF_COMMAND(CmdSellShip);
|
||||
DEF_COMMAND(CmdBuildShip);
|
||||
DEF_COMMAND(CmdSendShipToDepot);
|
||||
DEF_COMMAND(CmdRefitShip);
|
||||
|
||||
DEF_COMMAND(CmdOrderRefit);
|
||||
DEF_COMMAND(CmdCloneOrder);
|
||||
|
||||
DEF_COMMAND(CmdClearArea);
|
||||
|
||||
DEF_COMMAND(CmdGiveMoney);
|
||||
DEF_COMMAND(CmdMoneyCheat);
|
||||
DEF_COMMAND(CmdBuildCanal);
|
||||
DEF_COMMAND(CmdBuildLock);
|
||||
|
||||
DEF_COMMAND(CmdPlayerCtrl);
|
||||
|
||||
DEF_COMMAND(CmdLevelLand);
|
||||
|
||||
DEF_COMMAND(CmdRefitRailVehicle);
|
||||
|
||||
DEF_COMMAND(CmdBuildSignalTrack);
|
||||
DEF_COMMAND(CmdRemoveSignalTrack);
|
||||
|
||||
DEF_COMMAND(CmdSetAutoReplace);
|
||||
|
||||
DEF_COMMAND(CmdCloneVehicle);
|
||||
DEF_COMMAND(CmdMassStartStopVehicle);
|
||||
DEF_COMMAND(CmdDepotSellAllVehicles);
|
||||
DEF_COMMAND(CmdDepotMassAutoReplace);
|
||||
|
||||
/* The master command table */
|
||||
static const Command _command_proc_table[] = {
|
||||
{CmdBuildRailroadTrack, 0}, /* 0 */
|
||||
{CmdRemoveRailroadTrack, 0}, /* 1 */
|
||||
{CmdBuildSingleRail, 0}, /* 2 */
|
||||
{CmdRemoveSingleRail, 0}, /* 3 */
|
||||
{CmdLandscapeClear, 0}, /* 4 */
|
||||
{CmdBuildBridge, 0}, /* 5 */
|
||||
{CmdBuildRailroadStation, 0}, /* 6 */
|
||||
{CmdBuildTrainDepot, 0}, /* 7 */
|
||||
{CmdBuildSingleSignal, 0}, /* 8 */
|
||||
{CmdRemoveSingleSignal, 0}, /* 9 */
|
||||
{CmdTerraformLand, 0}, /* 10 */
|
||||
{CmdPurchaseLandArea, 0}, /* 11 */
|
||||
{CmdSellLandArea, 0}, /* 12 */
|
||||
{CmdBuildTunnel, 0}, /* 13 */
|
||||
{CmdRemoveFromRailroadStation, 0}, /* 14 */
|
||||
{CmdConvertRail, 0}, /* 15 */
|
||||
{CmdBuildTrainWaypoint, 0}, /* 16 */
|
||||
{CmdRenameWaypoint, 0}, /* 17 */
|
||||
{CmdRemoveTrainWaypoint, 0}, /* 18 */
|
||||
{NULL, 0}, /* 19 */
|
||||
{NULL, 0}, /* 20 */
|
||||
{CmdBuildRoadStop, 0}, /* 21 */
|
||||
{NULL, 0}, /* 22 */
|
||||
{CmdBuildLongRoad, 0}, /* 23 */
|
||||
{CmdRemoveLongRoad, 0}, /* 24 */
|
||||
{CmdBuildRoad, 0}, /* 25 */
|
||||
{CmdRemoveRoad, 0}, /* 26 */
|
||||
{CmdBuildRoadDepot, 0}, /* 27 */
|
||||
{NULL, 0}, /* 28 */
|
||||
{CmdBuildAirport, 0}, /* 29 */
|
||||
{CmdBuildDock, 0}, /* 30 */
|
||||
{CmdBuildShipDepot, 0}, /* 31 */
|
||||
{CmdBuildBuoy, 0}, /* 32 */
|
||||
{CmdPlantTree, 0}, /* 33 */
|
||||
{CmdBuildRailVehicle, 0}, /* 34 */
|
||||
{CmdMoveRailVehicle, 0}, /* 35 */
|
||||
{CmdStartStopTrain, 0}, /* 36 */
|
||||
{NULL, 0}, /* 37 */
|
||||
{CmdSellRailWagon, 0}, /* 38 */
|
||||
{CmdSendTrainToDepot, 0}, /* 39 */
|
||||
{CmdForceTrainProceed, 0}, /* 40 */
|
||||
{CmdReverseTrainDirection, 0}, /* 41 */
|
||||
|
||||
{CmdModifyOrder, 0}, /* 42 */
|
||||
{CmdSkipOrder, 0}, /* 43 */
|
||||
{CmdDeleteOrder, 0}, /* 44 */
|
||||
{CmdInsertOrder, 0}, /* 45 */
|
||||
|
||||
{CmdChangeServiceInt, 0}, /* 46 */
|
||||
|
||||
{CmdBuildIndustry, 0}, /* 47 */
|
||||
{CmdBuildCompanyHQ, 0}, /* 48 */
|
||||
{CmdSetPlayerFace, 0}, /* 49 */
|
||||
{CmdSetPlayerColor, 0}, /* 50 */
|
||||
|
||||
{CmdIncreaseLoan, 0}, /* 51 */
|
||||
{CmdDecreaseLoan, 0}, /* 52 */
|
||||
|
||||
{CmdWantEnginePreview, 0}, /* 53 */
|
||||
|
||||
{CmdNameVehicle, 0}, /* 54 */
|
||||
{CmdRenameEngine, 0}, /* 55 */
|
||||
|
||||
{CmdChangeCompanyName, 0}, /* 56 */
|
||||
{CmdChangePresidentName, 0}, /* 57 */
|
||||
|
||||
{CmdRenameStation, 0}, /* 58 */
|
||||
|
||||
{CmdSellAircraft, 0}, /* 59 */
|
||||
{CmdStartStopAircraft, 0}, /* 60 */
|
||||
|
||||
{CmdBuildAircraft, 0}, /* 61 */
|
||||
{CmdSendAircraftToHangar, 0}, /* 62 */
|
||||
{NULL, 0}, /* 63 */
|
||||
{CmdRefitAircraft, 0}, /* 64 */
|
||||
|
||||
{CmdPlaceSign, 0}, /* 65 */
|
||||
{CmdRenameSign, 0}, /* 66 */
|
||||
|
||||
{CmdBuildRoadVeh, 0}, /* 67 */
|
||||
{CmdStartStopRoadVeh, 0}, /* 68 */
|
||||
{CmdSellRoadVeh, 0}, /* 69 */
|
||||
{CmdSendRoadVehToDepot, 0}, /* 70 */
|
||||
{CmdTurnRoadVeh, 0}, /* 71 */
|
||||
{CmdRefitRoadVeh, 0}, /* 72 */
|
||||
|
||||
{CmdPause, CMD_SERVER}, /* 73 */
|
||||
|
||||
{CmdBuyShareInCompany, 0}, /* 74 */
|
||||
{CmdSellShareInCompany, 0}, /* 75 */
|
||||
{CmdBuyCompany, 0}, /* 76 */
|
||||
|
||||
{CmdBuildTown, CMD_OFFLINE}, /* 77 */
|
||||
{NULL, 0}, /* 78 */
|
||||
{NULL, 0}, /* 79 */
|
||||
{CmdRenameTown, CMD_SERVER}, /* 80 */
|
||||
{CmdDoTownAction, 0}, /* 81 */
|
||||
|
||||
{CmdSetRoadDriveSide, CMD_SERVER}, /* 82 */
|
||||
{NULL, 0}, /* 83 */
|
||||
{NULL, 0}, /* 84 */
|
||||
{CmdChangeDifficultyLevel, CMD_SERVER}, /* 85 */
|
||||
|
||||
{CmdStartStopShip, 0}, /* 86 */
|
||||
{CmdSellShip, 0}, /* 87 */
|
||||
{CmdBuildShip, 0}, /* 88 */
|
||||
{CmdSendShipToDepot, 0}, /* 89 */
|
||||
{NULL, 0}, /* 90 */
|
||||
{CmdRefitShip, 0}, /* 91 */
|
||||
|
||||
{NULL, 0}, /* 92 */
|
||||
{NULL, 0}, /* 93 */
|
||||
{NULL, 0}, /* 94 */
|
||||
{NULL, 0}, /* 95 */
|
||||
{NULL, 0}, /* 96 */
|
||||
{NULL, 0}, /* 97 */
|
||||
|
||||
{CmdOrderRefit, 0}, /* 98 */
|
||||
{CmdCloneOrder, 0}, /* 99 */
|
||||
|
||||
{CmdClearArea, 0}, /* 100 */
|
||||
{NULL, 0}, /* 101 */
|
||||
|
||||
{CmdMoneyCheat, CMD_OFFLINE}, /* 102 */
|
||||
{CmdBuildCanal, 0}, /* 103 */
|
||||
{CmdPlayerCtrl, 0}, /* 104 */
|
||||
|
||||
{CmdLevelLand, 0}, /* 105 */
|
||||
|
||||
{CmdRefitRailVehicle, 0}, /* 106 */
|
||||
{CmdRestoreOrderIndex, 0}, /* 107 */
|
||||
{CmdBuildLock, 0}, /* 108 */
|
||||
{NULL, 0}, /* 109 */
|
||||
{CmdBuildSignalTrack, 0}, /* 110 */
|
||||
{CmdRemoveSignalTrack, 0}, /* 111 */
|
||||
{NULL, 0}, /* 112 */
|
||||
{CmdGiveMoney, 0}, /* 113 */
|
||||
{CmdChangePatchSetting, CMD_SERVER}, /* 114 */
|
||||
{CmdSetAutoReplace, 0}, /* 115 */
|
||||
{CmdCloneVehicle, 0}, /* 116 */
|
||||
{CmdMassStartStopVehicle, 0}, /* 117 */
|
||||
{CmdDepotSellAllVehicles, 0}, /* 118 */
|
||||
{CmdDepotMassAutoReplace, 0}, /* 119 */
|
||||
};
|
||||
|
||||
/* This function range-checks a cmd, and checks if the cmd is not NULL */
|
||||
bool IsValidCommand(uint cmd)
|
||||
{
|
||||
cmd &= 0xFF;
|
||||
|
||||
return
|
||||
cmd < lengthof(_command_proc_table) &&
|
||||
_command_proc_table[cmd].proc != NULL;
|
||||
}
|
||||
|
||||
byte GetCommandFlags(uint cmd)
|
||||
{
|
||||
return _command_proc_table[cmd & 0xFF].flags;
|
||||
}
|
||||
|
||||
|
||||
static int _docommand_recursive;
|
||||
|
||||
int32 DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
|
||||
{
|
||||
int32 res;
|
||||
CommandProc *proc;
|
||||
|
||||
/* Do not even think about executing out-of-bounds tile-commands */
|
||||
if (tile >= MapSize()) {
|
||||
_cmd_text = NULL;
|
||||
return CMD_ERROR;
|
||||
}
|
||||
|
||||
proc = _command_proc_table[procc].proc;
|
||||
|
||||
if (_docommand_recursive == 0) _error_message = INVALID_STRING_ID;
|
||||
|
||||
_docommand_recursive++;
|
||||
|
||||
// only execute the test call if it's toplevel, or we're not execing.
|
||||
if (_docommand_recursive == 1 || !(flags & DC_EXEC) || (flags & DC_FORCETEST) ) {
|
||||
res = proc(tile, flags & ~DC_EXEC, p1, p2);
|
||||
if (CmdFailed(res)) {
|
||||
if (res & 0xFFFF) _error_message = res & 0xFFFF;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (_docommand_recursive == 1 &&
|
||||
!(flags & DC_QUERY_COST) &&
|
||||
res != 0 &&
|
||||
!CheckPlayerHasMoney(res)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!(flags & DC_EXEC)) {
|
||||
_docommand_recursive--;
|
||||
_cmd_text = NULL;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute the command here. All cost-relevant functions set the expenses type
|
||||
* themselves with "SET_EXPENSES_TYPE(...);" at the beginning of the function */
|
||||
res = proc(tile, flags, p1, p2);
|
||||
if (CmdFailed(res)) {
|
||||
if (res & 0xFFFF) _error_message = res & 0xFFFF;
|
||||
error:
|
||||
_docommand_recursive--;
|
||||
_cmd_text = NULL;
|
||||
return CMD_ERROR;
|
||||
}
|
||||
|
||||
// if toplevel, subtract the money.
|
||||
if (--_docommand_recursive == 0) {
|
||||
SubtractMoneyFromPlayer(res);
|
||||
// XXX - Old AI hack which doesn't use DoCommandDP; update last build coord of player
|
||||
if (tile != 0 && IsValidPlayer(_current_player)) {
|
||||
GetPlayer(_current_player)->last_build_coordinate = tile;
|
||||
}
|
||||
}
|
||||
|
||||
_cmd_text = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
int32 GetAvailableMoneyForCommand(void)
|
||||
{
|
||||
PlayerID pid = _current_player;
|
||||
if (!IsValidPlayer(pid)) return 0x7FFFFFFF; // max int
|
||||
return GetPlayer(pid)->player_money;
|
||||
}
|
||||
|
||||
// toplevel network safe docommand function for the current player. must not be called recursively.
|
||||
// the callback is called when the command succeeded or failed.
|
||||
bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, uint32 cmd)
|
||||
{
|
||||
int32 res = 0,res2;
|
||||
CommandProc *proc;
|
||||
uint32 flags;
|
||||
bool notest;
|
||||
StringID error_part1;
|
||||
|
||||
int x = TileX(tile) * TILE_SIZE;
|
||||
int y = TileY(tile) * TILE_SIZE;
|
||||
|
||||
/* Do not even think about executing out-of-bounds tile-commands */
|
||||
if (tile >= MapSize()) {
|
||||
_cmd_text = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(_docommand_recursive == 0);
|
||||
|
||||
_error_message = INVALID_STRING_ID;
|
||||
error_part1 = GB(cmd, 16, 16);
|
||||
_additional_cash_required = 0;
|
||||
|
||||
/** Spectator has no rights except for the (dedicated) server which
|
||||
* is/can be a spectator but as the server it can do anything */
|
||||
if (_current_player == PLAYER_SPECTATOR && !_network_server) {
|
||||
ShowErrorMessage(_error_message, error_part1, x, y);
|
||||
_cmd_text = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
flags = 0;
|
||||
if (cmd & CMD_AUTO) flags |= DC_AUTO;
|
||||
if (cmd & CMD_NO_WATER) flags |= DC_NO_WATER;
|
||||
|
||||
// get pointer to command handler
|
||||
assert((cmd & 0xFF) < lengthof(_command_proc_table));
|
||||
proc = _command_proc_table[cmd & 0xFF].proc;
|
||||
if (proc == NULL) {
|
||||
_cmd_text = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some commands have a different output in dryrun than the realrun
|
||||
// e.g.: if you demolish a whole town, the dryrun would say okay.
|
||||
// but by really destroying, your rating drops and at a certain point
|
||||
// it will fail. so res and res2 are different
|
||||
// CMD_REMOVE_ROAD: This command has special local authority
|
||||
// restrictions which may cause the test run to fail (the previous
|
||||
// road fragments still stay there and the town won't let you
|
||||
// disconnect the road system), but the exec will succeed and this
|
||||
// fact will trigger an assertion failure. --pasky
|
||||
notest =
|
||||
(cmd & 0xFF) == CMD_CLEAR_AREA ||
|
||||
(cmd & 0xFF) == CMD_CONVERT_RAIL ||
|
||||
(cmd & 0xFF) == CMD_LEVEL_LAND ||
|
||||
(cmd & 0xFF) == CMD_REMOVE_ROAD ||
|
||||
(cmd & 0xFF) == CMD_REMOVE_LONG_ROAD;
|
||||
|
||||
_docommand_recursive = 1;
|
||||
|
||||
// cost estimation only?
|
||||
if (!IsGeneratingWorld() && _shift_pressed && IsLocalPlayer() && !(cmd & (CMD_NETWORK_COMMAND | CMD_SHOW_NO_ERROR))) {
|
||||
// estimate the cost.
|
||||
res = proc(tile, flags, p1, p2);
|
||||
if (CmdFailed(res)) {
|
||||
if (res & 0xFFFF) _error_message = res & 0xFFFF;
|
||||
ShowErrorMessage(_error_message, error_part1, x, y);
|
||||
} else {
|
||||
ShowEstimatedCostOrIncome(res, x, y);
|
||||
}
|
||||
|
||||
_docommand_recursive = 0;
|
||||
_cmd_text = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) {
|
||||
// first test if the command can be executed.
|
||||
res = proc(tile, flags, p1, p2);
|
||||
if (CmdFailed(res)) {
|
||||
if (res & 0xFFFF) _error_message = res & 0xFFFF;
|
||||
goto show_error;
|
||||
}
|
||||
// no money? Only check if notest is off
|
||||
if (!notest && res != 0 && !CheckPlayerHasMoney(res)) goto show_error;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
/** If we are in network, and the command is not from the network
|
||||
* send it to the command-queue and abort execution
|
||||
* If we are a dedicated server temporarily switch local player, otherwise
|
||||
* the other parties won't be able to execute our command and will desync.
|
||||
* We also need to do this if the server's company has gone bankrupt
|
||||
* @todo Rewrite (dedicated) server to something more than a dirty hack!
|
||||
*/
|
||||
if (_networking && !(cmd & CMD_NETWORK_COMMAND)) {
|
||||
PlayerID pbck = _local_player;
|
||||
if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = 0;
|
||||
NetworkSend_Command(tile, p1, p2, cmd, callback);
|
||||
if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = pbck;
|
||||
_docommand_recursive = 0;
|
||||
_cmd_text = NULL;
|
||||
return true;
|
||||
}
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
// update last build coordinate of player.
|
||||
if (tile != 0 && IsValidPlayer(_current_player)) {
|
||||
GetPlayer(_current_player)->last_build_coordinate = tile;
|
||||
}
|
||||
|
||||
/* Actually try and execute the command. If no cost-type is given
|
||||
* use the construction one */
|
||||
_yearly_expenses_type = EXPENSES_CONSTRUCTION;
|
||||
res2 = proc(tile, flags | DC_EXEC, p1, p2);
|
||||
|
||||
// If notest is on, it means the result of the test can be different than
|
||||
// the real command.. so ignore the test
|
||||
if (!notest && !((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) {
|
||||
assert(res == res2); // sanity check
|
||||
} else {
|
||||
if (CmdFailed(res2)) {
|
||||
if (res2 & 0xFFFF) _error_message = res2 & 0xFFFF;
|
||||
goto show_error;
|
||||
}
|
||||
}
|
||||
|
||||
SubtractMoneyFromPlayer(res2);
|
||||
|
||||
if (IsLocalPlayer() && _game_mode != GM_EDITOR) {
|
||||
if (res2 != 0) ShowCostOrIncomeAnimation(x, y, GetSlopeZ(x, y), res2);
|
||||
if (_additional_cash_required) {
|
||||
SetDParam(0, _additional_cash_required);
|
||||
ShowErrorMessage(STR_0003_NOT_ENOUGH_CASH_REQUIRES, error_part1, x,y);
|
||||
if (res2 == 0) goto callb_err;
|
||||
}
|
||||
}
|
||||
|
||||
_docommand_recursive = 0;
|
||||
|
||||
if (callback) callback(true, tile, p1, p2);
|
||||
_cmd_text = NULL;
|
||||
return true;
|
||||
|
||||
show_error:
|
||||
// show error message if the command fails?
|
||||
if (IsLocalPlayer() && error_part1 != 0) {
|
||||
ShowErrorMessage(_error_message, error_part1, x,y);
|
||||
}
|
||||
|
||||
callb_err:
|
||||
_docommand_recursive = 0;
|
||||
|
||||
if (callback) callback(false, tile, p1, p2);
|
||||
_cmd_text = NULL;
|
||||
return false;
|
||||
}
|
||||
213
src/command.h
Normal file
213
src/command.h
Normal file
@@ -0,0 +1,213 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef COMMAND_H
|
||||
#define COMMAND_H
|
||||
|
||||
enum {
|
||||
CMD_BUILD_RAILROAD_TRACK = 0,
|
||||
CMD_REMOVE_RAILROAD_TRACK = 1,
|
||||
CMD_BUILD_SINGLE_RAIL = 2,
|
||||
CMD_REMOVE_SINGLE_RAIL = 3,
|
||||
CMD_LANDSCAPE_CLEAR = 4,
|
||||
CMD_BUILD_BRIDGE = 5,
|
||||
CMD_BUILD_RAILROAD_STATION = 6,
|
||||
CMD_BUILD_TRAIN_DEPOT = 7,
|
||||
CMD_BUILD_SIGNALS = 8,
|
||||
CMD_REMOVE_SIGNALS = 9,
|
||||
CMD_TERRAFORM_LAND = 10,
|
||||
CMD_PURCHASE_LAND_AREA = 11,
|
||||
CMD_SELL_LAND_AREA = 12,
|
||||
CMD_BUILD_TUNNEL = 13,
|
||||
|
||||
CMD_REMOVE_FROM_RAILROAD_STATION = 14,
|
||||
CMD_CONVERT_RAIL = 15,
|
||||
|
||||
CMD_BUILD_TRAIN_WAYPOINT = 16,
|
||||
CMD_RENAME_WAYPOINT = 17,
|
||||
CMD_REMOVE_TRAIN_WAYPOINT = 18,
|
||||
|
||||
CMD_BUILD_ROAD_STOP = 21,
|
||||
CMD_BUILD_LONG_ROAD = 23,
|
||||
CMD_REMOVE_LONG_ROAD = 24,
|
||||
CMD_BUILD_ROAD = 25,
|
||||
CMD_REMOVE_ROAD = 26,
|
||||
CMD_BUILD_ROAD_DEPOT = 27,
|
||||
|
||||
CMD_BUILD_AIRPORT = 29,
|
||||
|
||||
CMD_BUILD_DOCK = 30,
|
||||
|
||||
CMD_BUILD_SHIP_DEPOT = 31,
|
||||
CMD_BUILD_BUOY = 32,
|
||||
|
||||
CMD_PLANT_TREE = 33,
|
||||
|
||||
CMD_BUILD_RAIL_VEHICLE = 34,
|
||||
CMD_MOVE_RAIL_VEHICLE = 35,
|
||||
|
||||
CMD_START_STOP_TRAIN = 36,
|
||||
|
||||
CMD_SELL_RAIL_WAGON = 38,
|
||||
|
||||
CMD_SEND_TRAIN_TO_DEPOT = 39,
|
||||
CMD_FORCE_TRAIN_PROCEED = 40,
|
||||
CMD_REVERSE_TRAIN_DIRECTION = 41,
|
||||
|
||||
CMD_MODIFY_ORDER = 42,
|
||||
CMD_SKIP_ORDER = 43,
|
||||
CMD_DELETE_ORDER = 44,
|
||||
CMD_INSERT_ORDER = 45,
|
||||
|
||||
CMD_CHANGE_SERVICE_INT = 46,
|
||||
|
||||
CMD_BUILD_INDUSTRY = 47,
|
||||
|
||||
CMD_BUILD_COMPANY_HQ = 48,
|
||||
CMD_SET_PLAYER_FACE = 49,
|
||||
CMD_SET_PLAYER_COLOR = 50,
|
||||
|
||||
CMD_INCREASE_LOAN = 51,
|
||||
CMD_DECREASE_LOAN = 52,
|
||||
|
||||
CMD_WANT_ENGINE_PREVIEW = 53,
|
||||
|
||||
CMD_NAME_VEHICLE = 54,
|
||||
CMD_RENAME_ENGINE = 55,
|
||||
CMD_CHANGE_COMPANY_NAME = 56,
|
||||
CMD_CHANGE_PRESIDENT_NAME = 57,
|
||||
CMD_RENAME_STATION = 58,
|
||||
|
||||
CMD_SELL_AIRCRAFT = 59,
|
||||
CMD_START_STOP_AIRCRAFT = 60,
|
||||
CMD_BUILD_AIRCRAFT = 61,
|
||||
CMD_SEND_AIRCRAFT_TO_HANGAR = 62,
|
||||
CMD_REFIT_AIRCRAFT = 64,
|
||||
|
||||
CMD_PLACE_SIGN = 65,
|
||||
CMD_RENAME_SIGN = 66,
|
||||
|
||||
CMD_BUILD_ROAD_VEH = 67,
|
||||
CMD_START_STOP_ROADVEH = 68,
|
||||
CMD_SELL_ROAD_VEH = 69,
|
||||
CMD_SEND_ROADVEH_TO_DEPOT = 70,
|
||||
CMD_TURN_ROADVEH = 71,
|
||||
CMD_REFIT_ROAD_VEH = 72,
|
||||
|
||||
CMD_PAUSE = 73,
|
||||
|
||||
CMD_BUY_SHARE_IN_COMPANY = 74,
|
||||
CMD_SELL_SHARE_IN_COMPANY = 75,
|
||||
CMD_BUY_COMPANY = 76,
|
||||
|
||||
CMD_BUILD_TOWN = 77,
|
||||
|
||||
CMD_RENAME_TOWN = 80,
|
||||
CMD_DO_TOWN_ACTION = 81,
|
||||
|
||||
CMD_SET_ROAD_DRIVE_SIDE = 82,
|
||||
|
||||
CMD_CHANGE_DIFFICULTY_LEVEL = 85,
|
||||
|
||||
CMD_START_STOP_SHIP = 86,
|
||||
CMD_SELL_SHIP = 87,
|
||||
CMD_BUILD_SHIP = 88,
|
||||
CMD_SEND_SHIP_TO_DEPOT = 89,
|
||||
CMD_REFIT_SHIP = 91,
|
||||
|
||||
CMD_ORDER_REFIT = 98,
|
||||
CMD_CLONE_ORDER = 99,
|
||||
CMD_CLEAR_AREA = 100,
|
||||
|
||||
CMD_MONEY_CHEAT = 102,
|
||||
CMD_BUILD_CANAL = 103,
|
||||
|
||||
CMD_PLAYER_CTRL = 104, // used in multiplayer to create a new player etc.
|
||||
CMD_LEVEL_LAND = 105, // level land
|
||||
|
||||
CMD_REFIT_RAIL_VEHICLE = 106,
|
||||
CMD_RESTORE_ORDER_INDEX = 107,
|
||||
CMD_BUILD_LOCK = 108,
|
||||
|
||||
CMD_BUILD_SIGNAL_TRACK = 110,
|
||||
CMD_REMOVE_SIGNAL_TRACK = 111,
|
||||
|
||||
CMD_GIVE_MONEY = 113,
|
||||
CMD_CHANGE_PATCH_SETTING = 114,
|
||||
|
||||
CMD_SET_AUTOREPLACE = 115,
|
||||
|
||||
CMD_CLONE_VEHICLE = 116,
|
||||
CMD_MASS_START_STOP = 117,
|
||||
CMD_DEPOT_SELL_ALL_VEHICLES = 118,
|
||||
CMD_DEPOT_MASS_AUTOREPLACE = 119,
|
||||
};
|
||||
|
||||
enum {
|
||||
DC_EXEC = 0x01,
|
||||
DC_AUTO = 0x02, // don't allow building on structures
|
||||
DC_QUERY_COST = 0x04, // query cost only, don't build.
|
||||
DC_NO_WATER = 0x08, // don't allow building on water
|
||||
DC_NO_RAIL_OVERLAP = 0x10, // don't allow overlap of rails (used in buildrail)
|
||||
DC_AI_BUILDING = 0x20, // special building rules for AI
|
||||
DC_NO_TOWN_RATING = 0x40, // town rating does not disallow you from building
|
||||
DC_FORCETEST = 0x80, // force test too.
|
||||
|
||||
CMD_ERROR = ((int32)0x80000000),
|
||||
};
|
||||
|
||||
#define CMD_MSG(x) ((x)<<16)
|
||||
|
||||
enum {
|
||||
CMD_AUTO = 0x0200,
|
||||
CMD_NO_WATER = 0x0400,
|
||||
CMD_NETWORK_COMMAND = 0x0800, // execute the command without sending it on the network
|
||||
CMD_NO_TEST_IF_IN_NETWORK = 0x1000, // When enabled, the command will bypass the no-DC_EXEC round if in network
|
||||
CMD_SHOW_NO_ERROR = 0x2000,
|
||||
};
|
||||
|
||||
/** Command flags for the command table
|
||||
* @see _command_proc_table
|
||||
*/
|
||||
enum {
|
||||
CMD_SERVER = 0x1, /// the command can only be initiated by the server
|
||||
CMD_OFFLINE = 0x2, /// the command cannot be executed in a multiplayer game; single-player only
|
||||
};
|
||||
|
||||
typedef int32 CommandProc(TileIndex tile, uint32 flags, uint32 p1, uint32 p2);
|
||||
|
||||
typedef struct Command {
|
||||
CommandProc *proc;
|
||||
byte flags;
|
||||
} Command;
|
||||
|
||||
//#define return_cmd_error(errcode) do { _error_message=(errcode); return CMD_ERROR; } while(0)
|
||||
#define return_cmd_error(errcode) do { return CMD_ERROR | (errcode); } while (0)
|
||||
|
||||
/**
|
||||
* Check the return value of a DoCommand*() function
|
||||
* @param res the resulting value from the command to be checked
|
||||
* @return Return true if the command failed, false otherwise
|
||||
*/
|
||||
static inline bool CmdFailed(int32 res)
|
||||
{
|
||||
// lower 16bits are the StringID of the possible error
|
||||
return res <= (CMD_ERROR | INVALID_STRING_ID);
|
||||
}
|
||||
|
||||
/* command.c */
|
||||
typedef void CommandCallback(bool success, TileIndex tile, uint32 p1, uint32 p2);
|
||||
int32 DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc);
|
||||
bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, uint32 cmd);
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
void NetworkSend_Command(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback);
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
extern const char* _cmd_text; // Text, which gets sent with a command
|
||||
|
||||
bool IsValidCommand(uint cmd);
|
||||
byte GetCommandFlags(uint cmd);
|
||||
int32 GetAvailableMoneyForCommand(void);
|
||||
|
||||
#endif /* COMMAND_H */
|
||||
323
src/configure
vendored
Executable file
323
src/configure
vendored
Executable file
@@ -0,0 +1,323 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This 'configure' script is a very easy wrapper around 'make updateconf'
|
||||
# It allows cross-compilers to do their job much more easy.
|
||||
|
||||
function showhelp() {
|
||||
echo "Configure for OpenTTD"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo " $0 --your_options"
|
||||
echo ""
|
||||
echo "Params:"
|
||||
echo " --debug Create debug-release [no]"
|
||||
echo " --profile Create profile-release [no]"
|
||||
echo " --dedicated Make a dedicated build [no]"
|
||||
echo " --revision Set the revision of the compilation [detected]"
|
||||
echo " --target-cc Sets the target-compiler [\$CC]"
|
||||
echo " --target-cxx Sets the C++ target-compiler []"
|
||||
echo " --host-cc Sets the host-compiler [\$CC]"
|
||||
echo " --host-cxx Sets the C++ host-compiler []"
|
||||
echo " --os Sets the OS. Listens to: [detected]"
|
||||
echo " UNIX, OSX, FREEBSD, MORPHOS"
|
||||
echo " BEOS, SUNOS, CYGWIN, MINGW, OS2"
|
||||
echo " --windres Sets the windres (Windows) [windres]"
|
||||
echo " --force-le Force LE platform [no]"
|
||||
echo " --force-be Force BE platform [no]"
|
||||
echo ""
|
||||
echo "Params that can be used with --with or --without"
|
||||
echo " (e.g.: --without-static disables static (default))"
|
||||
echo " static Do you want a static build? [no]"
|
||||
echo " directmusic Do you want direct-music? [no]"
|
||||
echo " zlib Do you want zlib-support? [yes]"
|
||||
echo " sdl Do you want SDL-support? [yes]"
|
||||
echo " png Do you want PNG-support? [yes]"
|
||||
echo " iconv Do you want iconv-support? [no]"
|
||||
echo " network Do you want network-support? [yes]"
|
||||
echo " cocoa Do you want cocoa-support? (MacOSX) [no]"
|
||||
echo " freetype Do you want freetype-support? [yes]"
|
||||
echo " fontconfig Do you want fontconfig-support? [yes]"
|
||||
echo ""
|
||||
echo "Params used to configure external libs:"
|
||||
echo " --static-zlib-path Set the path to your static zlib []"
|
||||
echo " --sdl-config Where is your sdl-config [sdl-config]"
|
||||
echo " --libpng-config Where is your libpng-config [libpng-config]"
|
||||
echo " --freetype-config Where is your freetype-config [freetype-config]"
|
||||
echo " --fontconfig-config Where is your fontconfig-config [pkg-config fontconfig]"
|
||||
echo " --with-iconv Set the path to your iconv headers []"
|
||||
echo " "
|
||||
}
|
||||
|
||||
function handle() {
|
||||
PARAM="$PARAM \"$1=`awk 'BEGIN { FS="="; $0="'"$2"'"; print $2;}'`\""
|
||||
}
|
||||
|
||||
# The things you can use inside this case:
|
||||
# handle NAME VALUE - Sets the value to give the 'make upgradeconf'
|
||||
# Value is in form: tag=REAL_VALUE
|
||||
# ITEM="NAME" - Will set the value as above, only with the next param
|
||||
# SITEM="NAME" - Will set the var $NAME to the next param
|
||||
for n in "$@"
|
||||
do
|
||||
case "$n" in
|
||||
--help | -h)
|
||||
showhelp
|
||||
exit 0
|
||||
;;
|
||||
|
||||
--debug)
|
||||
DEBUG_SET=1
|
||||
ITEM="DEBUG"
|
||||
;;
|
||||
--debug=*)
|
||||
handle "DEBUG" "$n"
|
||||
;;
|
||||
--profile)
|
||||
PARAM="$PARAM PROFILE=1"
|
||||
;;
|
||||
--dedicated)
|
||||
PARAM="$PARAM DEDICATED=1"
|
||||
;;
|
||||
--revision=*)
|
||||
RELEASE=`awk 'BEGIN { FS="="; $0="'"$n"'"; print $2;}'`
|
||||
;;
|
||||
--revision)
|
||||
SITEM="RELEASE"
|
||||
;;
|
||||
--target-cc=*)
|
||||
handle "CC_TARGET" "$n"
|
||||
;;
|
||||
--target-cc)
|
||||
ITEM="CC_TARGET"
|
||||
;;
|
||||
--target-cxx=*)
|
||||
handle "CXX_TARGET" "$n"
|
||||
;;
|
||||
--target-cxx)
|
||||
SITEM="CXX_TARGET"
|
||||
;;
|
||||
--host-cc=*)
|
||||
handle "CC_HOST" "$n"
|
||||
;;
|
||||
--host-cc)
|
||||
ITEM="CC_HOST"
|
||||
;;
|
||||
--host-cxx=*)
|
||||
handle "CXX_HOST" "$n"
|
||||
;;
|
||||
--host-cxx)
|
||||
ITEM="CXX_HOST"
|
||||
;;
|
||||
--host-cflags=*)
|
||||
handle CFLAGS_HOST "$n"
|
||||
;;
|
||||
--host-cflags)
|
||||
ITEM="CFLAGS_HOST"
|
||||
;;
|
||||
--os=*)
|
||||
TARGET_OS=`awk 'BEGIN { FS="="; $0="'"$n"'"; print $2;}'`
|
||||
;;
|
||||
--os)
|
||||
SITEM="TARGET_OS"
|
||||
;;
|
||||
--windres=*)
|
||||
handle WINDRES "$n"
|
||||
;;
|
||||
--windres)
|
||||
ITEM="WINDRES"
|
||||
;;
|
||||
--force-le)
|
||||
PARAM="$PARAM ENDIAN_FORCE=LE"
|
||||
;;
|
||||
--force-be)
|
||||
PARAM="$PARAM ENDIAN_FORCE=BE"
|
||||
;;
|
||||
|
||||
--with-static)
|
||||
PARAM="$PARAM STATIC=1"
|
||||
;;
|
||||
--without-static)
|
||||
PARAM="$PARAM STATIC="
|
||||
;;
|
||||
--with-directmusic)
|
||||
PARAM="$PARAM WITH_DIRECTMUSIC=1"
|
||||
;;
|
||||
--without-directmusic)
|
||||
PARAM="$PARAM WITH_DIRECTMUSIC="
|
||||
;;
|
||||
--with-zlib)
|
||||
PARAM="$PARAM WITH_ZLIB=1"
|
||||
;;
|
||||
--without-zlib)
|
||||
PARAM="$PARAM WITH_ZLIB="
|
||||
;;
|
||||
--with-sdl)
|
||||
PARAM="$PARAM WITH_SDL=1"
|
||||
;;
|
||||
--without-sdl)
|
||||
PARAM="$PARAM WITH_SDL="
|
||||
;;
|
||||
--with-png)
|
||||
PARAM="$PARAM WITH_PNG=1"
|
||||
;;
|
||||
--without-png)
|
||||
PARAM="$PARAM WITH_PNG="
|
||||
;;
|
||||
--with-iconv)
|
||||
PARAM="$PARAM WITH_ICONV=1"
|
||||
;;
|
||||
--with-iconv=*)
|
||||
PARAM="$PARAM WITH_ICONV=1"
|
||||
handle WITH_ICONV_PATH "$n"
|
||||
;;
|
||||
--without-iconv)
|
||||
PARAM="$PARAM WITH_ICONV="
|
||||
;;
|
||||
--with-cocoa)
|
||||
PARAM="$PARAM WITH_COCOA=1"
|
||||
;;
|
||||
--with-network)
|
||||
PARAM="$PARAM WITH_NETWORK=1"
|
||||
;;
|
||||
--without-network)
|
||||
PARAM="$PARAM WITH_NETWORK="
|
||||
;;
|
||||
--without-cocoa)
|
||||
PARAM="$PARAM WITH_COCOA="
|
||||
;;
|
||||
--with-freetype)
|
||||
PARAM="$PARAM WITH_FREETYPE=1"
|
||||
;;
|
||||
--without-freetype)
|
||||
PARAM="$PARAM WITH_FREETYPE="
|
||||
;;
|
||||
--with-fontconfig)
|
||||
PARAM="$PARAM WITH_FONTCONFIG=1"
|
||||
;;
|
||||
--without-fontconfig)
|
||||
PARAM="$PARAM WITH_FONTCONFIG="
|
||||
;;
|
||||
--static-zlib-path=*)
|
||||
handle STATIC_ZLIB_PATH "$n"
|
||||
;;
|
||||
--static-zlib-path)
|
||||
ITEM="STATIC_ZLIB_PATH"
|
||||
;;
|
||||
--sdl-config=*)
|
||||
handle SDL_CONFIG "$n"
|
||||
;;
|
||||
--sdl-config)
|
||||
ITEM="SDL_CONFIG"
|
||||
;;
|
||||
--libpng-config=*)
|
||||
handle LIBPNG_CONFIG "$n"
|
||||
;;
|
||||
--libpng-config)
|
||||
ITEM="LIBPNG_CONFIG"
|
||||
;;
|
||||
--freetype-config=*)
|
||||
handle FREETYPE_CONFIG "$n"
|
||||
;;
|
||||
--freetype-config)
|
||||
ITEM="FREETYPE_CONFIG"
|
||||
;;
|
||||
--fontconfig-config=*)
|
||||
handle FONTCONFIG_CONFIG "$n"
|
||||
;;
|
||||
--fontconfig-config)
|
||||
ITEM="FONTCONFIG_CONFIG"
|
||||
;;
|
||||
|
||||
--*=*)
|
||||
echo -n "Unknown switch "
|
||||
echo `awk 'BEGIN { FS="="; $0="'"$n"'"; print $1;}'`
|
||||
exit 1
|
||||
;;
|
||||
-*)
|
||||
echo "Unknown switch $n"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
*)
|
||||
if ! test -z "$ITEM"
|
||||
then
|
||||
PARAM="$PARAM $ITEM=\"$n\""
|
||||
ITEM="";
|
||||
elif ! test -z "$SITEM"
|
||||
then
|
||||
export $SITEM="$n"
|
||||
SITEM=""
|
||||
else
|
||||
echo "Unknown switch $n"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if ! test -z "$TARGET_OS"
|
||||
then
|
||||
TARGET_OS=`echo $TARGET_OS | tr '[:lower:]' '[:upper:]'`
|
||||
case "$TARGET_OS" in
|
||||
WIN32)
|
||||
PARAM="$PARAM WIN32=1"
|
||||
;;
|
||||
UNIX)
|
||||
PARAM="$PARAM UNIX=1"
|
||||
;;
|
||||
OSX)
|
||||
PARAM="$PARAM OSX=1 UNIX=1"
|
||||
;;
|
||||
FREEBSD)
|
||||
PARAM="$PARAM FREEBSD=1"
|
||||
;;
|
||||
MORPHOS)
|
||||
PARAM="$PARAM MORPHOS=1 UNIX=1"
|
||||
;;
|
||||
BEOS)
|
||||
PARAM="$PARAM BEOS=1 UNIX=1"
|
||||
;;
|
||||
OS2)
|
||||
PARAM="$PARAM OS2=1 UNIX=1"
|
||||
;;
|
||||
SUNOS)
|
||||
PARAM="$PARAM SUNOS=1 UNIX=1"
|
||||
;;
|
||||
CYGWIN)
|
||||
PARAM="$PARAM CYGWIN=1 WIN32=1"
|
||||
;;
|
||||
MINGW)
|
||||
PARAM="$PARAM MINGW=1 WIN32=1"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown OS: $TARGET_OS"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
PARAM="$PARAM BYPASS_OS_DETECT=1"
|
||||
fi
|
||||
|
||||
if ! test -z "$DEBUG_SET"
|
||||
then
|
||||
if test -z "`echo $PARAM | grep "DEBUG="`"
|
||||
then
|
||||
# Someone did --debug, without assigning a value, assume 1
|
||||
PARAM="$PARAM DEBUG=1"
|
||||
fi
|
||||
fi
|
||||
|
||||
# First remove the Makefile.config, else you can have double entries
|
||||
rm -f Makefile.config
|
||||
|
||||
echo "make upgradeconf $PARAM" > Makefile.run
|
||||
. Makefile.run
|
||||
rm -f Makefile.run
|
||||
|
||||
# Makefile.config currently doesn't support custom RELEASE (revision), so, we add the line
|
||||
# yourself!
|
||||
|
||||
if ! test -z "$RELEASE"
|
||||
then
|
||||
echo "RELEASE=$RELEASE" >> Makefile.config
|
||||
fi
|
||||
|
||||
1150
src/console.c
Normal file
1150
src/console.c
Normal file
File diff suppressed because it is too large
Load Diff
160
src/console.h
Normal file
160
src/console.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef CONSOLE_H
|
||||
#define CONSOLE_H
|
||||
|
||||
// maximum length of a typed in command
|
||||
#define ICON_CMDLN_SIZE 255
|
||||
// maximum length of a totally expanded command
|
||||
#define ICON_MAX_STREAMSIZE 1024
|
||||
|
||||
typedef enum IConsoleVarTypes {
|
||||
ICONSOLE_VAR_BOOLEAN,
|
||||
ICONSOLE_VAR_BYTE,
|
||||
ICONSOLE_VAR_UINT16,
|
||||
ICONSOLE_VAR_UINT32,
|
||||
ICONSOLE_VAR_INT16,
|
||||
ICONSOLE_VAR_INT32,
|
||||
ICONSOLE_VAR_STRING
|
||||
} IConsoleVarTypes;
|
||||
|
||||
typedef enum IConsoleModes {
|
||||
ICONSOLE_FULL,
|
||||
ICONSOLE_OPENED,
|
||||
ICONSOLE_CLOSED
|
||||
} IConsoleModes;
|
||||
|
||||
typedef enum IConsoleHookTypes {
|
||||
ICONSOLE_HOOK_ACCESS,
|
||||
ICONSOLE_HOOK_PRE_ACTION,
|
||||
ICONSOLE_HOOK_POST_ACTION
|
||||
} IConsoleHookTypes;
|
||||
|
||||
/** --Hooks--
|
||||
* Hooks are certain triggers get get accessed/executed on either
|
||||
* access, before execution/change or after execution/change. This allows
|
||||
* for general flow of permissions or special action needed in some cases
|
||||
*/
|
||||
typedef bool IConsoleHook(void);
|
||||
typedef struct IConsoleHooks{
|
||||
IConsoleHook *access; // trigger when accessing the variable/command
|
||||
IConsoleHook *pre; // trigger before the variable/command is changed/executed
|
||||
IConsoleHook *post; // trigger after the variable/command is changed/executed
|
||||
} IConsoleHooks;
|
||||
|
||||
/** --Commands--
|
||||
* Commands are commands, or functions. They get executed once and any
|
||||
* effect they produce are carried out. The arguments to the commands
|
||||
* are given to them, each input word seperated by a double-quote (") is an argument
|
||||
* If you want to handle multiple words as one, enclose them in double-quotes
|
||||
* eg. 'say "hello sexy boy"'
|
||||
*/
|
||||
typedef bool (IConsoleCmdProc)(byte argc, char *argv[]);
|
||||
|
||||
struct IConsoleCmd;
|
||||
typedef struct IConsoleCmd {
|
||||
char *name; // name of command
|
||||
struct IConsoleCmd *next; // next command in list
|
||||
|
||||
IConsoleCmdProc *proc; // process executed when command is typed
|
||||
IConsoleHooks hook; // any special trigger action that needs executing
|
||||
} IConsoleCmd;
|
||||
|
||||
/** --Variables--
|
||||
* Variables are pointers to real ingame variables which allow for
|
||||
* changing while ingame. After changing they keep their new value
|
||||
* and can be used for debugging, gameplay, etc. It accepts:
|
||||
* - no arguments; just print out current value
|
||||
* - '= <new value>' to assign a new value to the variable
|
||||
* - '++' to increase value by one
|
||||
* - '--' to decrease value by one
|
||||
*/
|
||||
struct IConsoleVar;
|
||||
typedef struct IConsoleVar {
|
||||
char *name; // name of the variable
|
||||
struct IConsoleVar *next; // next variable in list
|
||||
|
||||
void *addr; // the address where the variable is pointing at
|
||||
uint32 size; // size of the variable, used for strings
|
||||
char *help; // the optional help string shown when requesting information
|
||||
IConsoleVarTypes type; // type of variable (for correct assignment/output)
|
||||
IConsoleCmdProc *proc; // some variables need really special handling, use a callback function for that
|
||||
IConsoleHooks hook; // any special trigger action that needs executing
|
||||
} IConsoleVar;
|
||||
|
||||
/** --Aliases--
|
||||
* Aliases are like shortcuts for complex functions, variable assignments,
|
||||
* etc. You can use a simple alias to rename a longer command (eg 'lv' for
|
||||
* 'list_vars' for example), or concatenate more commands into one
|
||||
* (eg. 'ng' for 'load %A; unpause; debug_level 5'). Aliases can parse the arguments
|
||||
* given to them in the command line.
|
||||
* - "%A - %Z" substitute arguments 1 t/m 26
|
||||
* - "%+" lists all parameters keeping them seperated
|
||||
* - "%!" also lists all parameters but presenting them to the aliased command as one argument
|
||||
* - ";" allows for combining commands (see example 'ng')
|
||||
*/
|
||||
struct IConsoleAlias;
|
||||
typedef struct IConsoleAlias {
|
||||
char *name; // name of the alias
|
||||
struct IConsoleAlias *next; // next alias in list
|
||||
|
||||
char *cmdline; // command(s) that is/are being aliased
|
||||
} IConsoleAlias;
|
||||
|
||||
/* console parser */
|
||||
VARDEF IConsoleCmd *_iconsole_cmds; // list of registred commands
|
||||
VARDEF IConsoleVar *_iconsole_vars; // list of registred vars
|
||||
VARDEF IConsoleAlias *_iconsole_aliases; // list of registred aliases
|
||||
|
||||
/* console colors/modes */
|
||||
VARDEF byte _icolour_def;
|
||||
VARDEF byte _icolour_err;
|
||||
VARDEF byte _icolour_warn;
|
||||
VARDEF byte _icolour_dbg;
|
||||
VARDEF byte _icolour_cmd;
|
||||
VARDEF IConsoleModes _iconsole_mode;
|
||||
|
||||
/* console functions */
|
||||
void IConsoleInit(void);
|
||||
void IConsoleFree(void);
|
||||
void IConsoleClearBuffer(void);
|
||||
void IConsoleResize(Window *w);
|
||||
void IConsoleSwitch(void);
|
||||
void IConsoleClose(void);
|
||||
void IConsoleOpen(void);
|
||||
|
||||
/* console output */
|
||||
void IConsolePrint(uint16 color_code, const char *string);
|
||||
void CDECL IConsolePrintF(uint16 color_code, const char *s, ...);
|
||||
void IConsoleDebug(const char *dbg, const char *string);
|
||||
void IConsoleWarning(const char *string);
|
||||
void IConsoleError(const char *string);
|
||||
|
||||
/* Commands */
|
||||
void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc);
|
||||
void IConsoleAliasRegister(const char *name, const char *cmd);
|
||||
IConsoleCmd *IConsoleCmdGet(const char *name);
|
||||
IConsoleAlias *IConsoleAliasGet(const char *name);
|
||||
|
||||
/* Variables */
|
||||
void IConsoleVarRegister(const char *name, void *addr, IConsoleVarTypes type, const char *help);
|
||||
void IConsoleVarStringRegister(const char *name, void *addr, uint32 size, const char *help);
|
||||
IConsoleVar* IConsoleVarGet(const char *name);
|
||||
void IConsoleVarPrintGetValue(const IConsoleVar *var);
|
||||
void IConsoleVarPrintSetValue(const IConsoleVar *var);
|
||||
|
||||
/* Parser */
|
||||
void IConsoleCmdExec(const char *cmdstr);
|
||||
void IConsoleVarExec(const IConsoleVar *var, byte tokencount, char *token[]);
|
||||
|
||||
/* console std lib (register ingame commands/aliases/variables) */
|
||||
void IConsoleStdLibRegister(void);
|
||||
|
||||
/* Hooking code */
|
||||
void IConsoleCmdHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc);
|
||||
void IConsoleVarHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc);
|
||||
void IConsoleVarProcAdd(const char *name, IConsoleCmdProc *proc);
|
||||
|
||||
/* Supporting functions */
|
||||
bool GetArgumentInteger(uint32 *value, const char *arg);
|
||||
#endif /* CONSOLE_H */
|
||||
1608
src/console_cmds.c
Normal file
1608
src/console_cmds.c
Normal file
File diff suppressed because it is too large
Load Diff
182
src/currency.c
Normal file
182
src/currency.c
Normal file
@@ -0,0 +1,182 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "currency.h"
|
||||
#include "news.h"
|
||||
#include "variables.h"
|
||||
#include "table/strings.h"
|
||||
#include "date.h"
|
||||
|
||||
// exchange rate prefix symbol_pos
|
||||
// | separator | postfix |
|
||||
// | | Euro year | | | name
|
||||
// | | | | | | |
|
||||
static const CurrencySpec origin_currency_specs[NUM_CURRENCY] = {
|
||||
{ 1, ',', CF_NOEURO, "£", "", 0, STR_CURR_GBP }, // british pounds
|
||||
{ 2, ',', CF_NOEURO, "$", "", 0, STR_CURR_USD }, // us dollars
|
||||
{ 2, ',', CF_ISEURO, "€", "", 0, STR_CURR_EUR }, // Euro
|
||||
{ 220, ',', CF_NOEURO, "¥", "", 0, STR_CURR_YEN }, // yen
|
||||
{ 20, ',', 2002, "", " S.", 1, STR_CURR_ATS }, // austrian schilling
|
||||
{ 59, ',', 2002, "BEF ", "", 0, STR_CURR_BEF }, // belgian franc
|
||||
{ 2, ',', CF_NOEURO, "CHF ", "", 0, STR_CURR_CHF }, // swiss franc
|
||||
{ 41, ',', CF_NOEURO, "", " Kč", 1, STR_CURR_CZK }, // czech koruna
|
||||
{ 3, '.', 2002, "DM ", "", 0, STR_CURR_DEM }, // deutsche mark
|
||||
{ 11, '.', CF_NOEURO, "", " kr", 1, STR_CURR_DKK }, // danish krone
|
||||
{ 245, '.', 2002, "Pts ", "", 0, STR_CURR_ESP }, // spanish pesetas
|
||||
{ 9, ',', 2002, "", " mk", 1, STR_CURR_FIM }, // finnish markka
|
||||
{ 10, '.', 2002, "FF ", "", 0, STR_CURR_FRF }, // french francs
|
||||
{ 500, ',', 2002, "", "Dr.", 1, STR_CURR_GRD }, // greek drachma
|
||||
{ 378, ',', 2010, "", " Ft", 1, STR_CURR_HUF }, // hungarian forint
|
||||
{ 130, '.', CF_NOEURO, "", " Kr", 1, STR_CURR_ISK }, // icelandic krona
|
||||
{ 2850, ',', 2002, "", " L.", 1, STR_CURR_ITL }, // italian lira
|
||||
{ 3, ',', 2002, "NLG ", "", 0, STR_CURR_NLG }, // dutch gulden
|
||||
{ 12, '.', CF_NOEURO, "", " Kr", 1, STR_CURR_NOK }, // norwegian krone
|
||||
{ 6, ' ', CF_NOEURO, "", " zl", 1, STR_CURR_PLN }, // polish zloty
|
||||
{ 5, '.', CF_NOEURO, "", " Lei", 1, STR_CURR_ROL }, // romanian Lei
|
||||
{ 50, ' ', CF_NOEURO, "", " p", 1, STR_CURR_RUR }, // russian rouble
|
||||
{ 352, '.', CF_NOEURO, "", " SIT", 1, STR_CURR_SIT }, // slovenian tolar
|
||||
{ 13, '.', CF_NOEURO, "", " Kr", 1, STR_CURR_SEK }, // swedish krona
|
||||
{ 3, '.', CF_NOEURO, "", " YTL", 1, STR_CURR_YTL }, // turkish lira
|
||||
{ 52, ',', CF_NOEURO, "", " Sk", 1, STR_CURR_SKK }, // slovak koruna
|
||||
{ 4, ',', CF_NOEURO, "R$ ", "", 0, STR_CURR_BRR }, // brazil real
|
||||
{ 1, ' ', CF_NOEURO, "", "", 2, STR_CURR_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 {
|
||||
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_ROL,
|
||||
CURR_RUR,
|
||||
CURR_SIT,
|
||||
CURR_SEK,
|
||||
CURR_YTL,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 then the array,
|
||||
* it is a grf written for ottd, thus returning the same id.
|
||||
* Only called from newgrf.c
|
||||
* @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 */
|
||||
uint GetMaskOfAllowedCurrencies(void)
|
||||
{
|
||||
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(void)
|
||||
{
|
||||
if (_currency_specs[_opt.currency].to_euro != CF_NOEURO &&
|
||||
_currency_specs[_opt.currency].to_euro != CF_ISEURO &&
|
||||
_cur_year >= _currency_specs[_opt.currency].to_euro) {
|
||||
_opt.currency = 2; // this is the index of euro above.
|
||||
AddNewsItem(STR_EURO_INTRODUCE, NEWS_FLAGS(NM_NORMAL, 0, NT_ECONOMY, 0), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only from newgrf.c. Will fill _currency_specs array with
|
||||
* default values from origin_currency_specs
|
||||
**/
|
||||
void ResetCurrencies(void)
|
||||
{
|
||||
memcpy(&_currency_specs, &origin_currency_specs, sizeof(origin_currency_specs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of currency names StringIDs to use in a dropdown list
|
||||
* @return Pointer to a (static) array of StringIDs
|
||||
*/
|
||||
StringID* BuildCurrencyDropdown(void)
|
||||
{
|
||||
/* Allow room for all currencies, plus a terminator entry */
|
||||
static StringID names[CUSTOM_CURRENCY_ID];
|
||||
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;
|
||||
}
|
||||
45
src/currency.h
Normal file
45
src/currency.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef CURRENCY_H
|
||||
#define CURRENCY_H
|
||||
|
||||
enum {
|
||||
CF_NOEURO = 0,
|
||||
CF_ISEURO = 1,
|
||||
NUM_CURRENCY = 28,
|
||||
CUSTOM_CURRENCY_ID = NUM_CURRENCY - 1
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint16 rate;
|
||||
char separator;
|
||||
Year to_euro;
|
||||
char prefix[16];
|
||||
char suffix[16];
|
||||
/**
|
||||
* The currency symbol is represented by two possible values, prefix and suffix
|
||||
* Usage of one or the other is determined by symbol_pos.
|
||||
* 0 = prefix
|
||||
* 1 = suffix
|
||||
* 2 = both : Special case only for custom currency.
|
||||
* It is not a spec from Newgrf,
|
||||
* rather a way to let users do what they want with custom curency
|
||||
*/
|
||||
byte symbol_pos;
|
||||
StringID name;
|
||||
} CurrencySpec;
|
||||
|
||||
|
||||
extern CurrencySpec _currency_specs[NUM_CURRENCY];
|
||||
|
||||
// XXX small hack, but makes the rest of the code a bit nicer to read
|
||||
#define _custom_currency (_currency_specs[CUSTOM_CURRENCY_ID])
|
||||
#define _currency ((const CurrencySpec*)&_currency_specs[_opt_ptr->currency])
|
||||
|
||||
uint GetMaskOfAllowedCurrencies(void);
|
||||
void CheckSwitchToEuro(void);
|
||||
void ResetCurrencies(void);
|
||||
StringID* BuildCurrencyDropdown(void);
|
||||
byte GetNewgrfCurrencyIdConverted(byte grfcurr_id);
|
||||
|
||||
#endif /* CURRENCY_H */
|
||||
304
src/date.c
Normal file
304
src/date.c
Normal file
@@ -0,0 +1,304 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "date.h"
|
||||
#include "variables.h"
|
||||
#include "macros.h"
|
||||
#include "vehicle.h"
|
||||
#include "network/network.h"
|
||||
#include "network/network_data.h"
|
||||
#include "network/network_server.h"
|
||||
#include "functions.h"
|
||||
#include "currency.h"
|
||||
|
||||
Year _cur_year;
|
||||
Month _cur_month;
|
||||
Date _date;
|
||||
DateFract _date_fract;
|
||||
|
||||
|
||||
void SetDate(Date date)
|
||||
{
|
||||
YearMonthDay ymd;
|
||||
|
||||
_date = date;
|
||||
ConvertDateToYMD(date, &ymd);
|
||||
_cur_year = ymd.year;
|
||||
_cur_month = ymd.month;
|
||||
#ifdef ENABLE_NETWORK
|
||||
_network_last_advertise_frame = 0;
|
||||
_network_need_advertise = true;
|
||||
#endif /* ENABLE_NETWORK */
|
||||
}
|
||||
|
||||
#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 {
|
||||
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,
|
||||
};
|
||||
|
||||
static inline bool IsLeapYear(Year yr)
|
||||
{
|
||||
return yr % 4 == 0 && (yr % 100 != 0 || yr % 400 == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 / (365 * 400 + 97));
|
||||
int rem = date % (365 * 400 + 97);
|
||||
uint16 x;
|
||||
|
||||
if (rem >= 365 * 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 -= 365 * 100 + 25;
|
||||
|
||||
/* There are 24 leap years in the next couple of 100 years */
|
||||
yr += 100 * (rem / (365 * 100 + 24));
|
||||
rem = (rem % (365 * 100 + 24));
|
||||
}
|
||||
|
||||
if (!IsLeapYear(yr) && rem >= 365 * 4) {
|
||||
/* The first 4 year of the century are not always a leap year */
|
||||
yr += 4;
|
||||
rem -= 365 * 4;
|
||||
}
|
||||
|
||||
/* There is 1 leap year every 4 years */
|
||||
yr += 4 * (rem / (365 * 4 + 1));
|
||||
rem = rem % (365 * 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) ? 366 : 365)) {
|
||||
rem -= IsLeapYear(yr) ? 366 : 365;
|
||||
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)
|
||||
{
|
||||
/*
|
||||
* Each passed leap year adds one day to the 'day count'.
|
||||
*
|
||||
* A special case for the year 0 as no year has been passed,
|
||||
* but '(year - 1) / 4' does not yield '-1' to counteract the
|
||||
* '+1' at the end of the formula as divisions round to zero.
|
||||
*/
|
||||
int nr_of_leap_years = (year == 0) ? 0 : ((year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400 + 1);
|
||||
|
||||
/* 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 year * 365 + nr_of_leap_years + days;
|
||||
}
|
||||
|
||||
/** Functions used by the IncreaseDate function */
|
||||
|
||||
extern void OnNewDay_Train(Vehicle *v);
|
||||
extern void OnNewDay_RoadVeh(Vehicle *v);
|
||||
extern void OnNewDay_Aircraft(Vehicle *v);
|
||||
extern void OnNewDay_Ship(Vehicle *v);
|
||||
static void OnNewDay_EffectVehicle(Vehicle *v) { /* empty */ }
|
||||
extern void OnNewDay_DisasterVehicle(Vehicle *v);
|
||||
|
||||
typedef void OnNewVehicleDayProc(Vehicle *v);
|
||||
|
||||
static OnNewVehicleDayProc * _on_new_vehicle_day_proc[] = {
|
||||
OnNewDay_Train,
|
||||
OnNewDay_RoadVeh,
|
||||
OnNewDay_Ship,
|
||||
OnNewDay_Aircraft,
|
||||
OnNewDay_EffectVehicle,
|
||||
OnNewDay_DisasterVehicle,
|
||||
};
|
||||
|
||||
extern void WaypointsDailyLoop(void);
|
||||
extern void TextMessageDailyLoop(void);
|
||||
extern void EnginesDailyLoop(void);
|
||||
extern void DisasterDailyLoop(void);
|
||||
|
||||
extern void PlayersMonthlyLoop(void);
|
||||
extern void EnginesMonthlyLoop(void);
|
||||
extern void TownsMonthlyLoop(void);
|
||||
extern void IndustryMonthlyLoop(void);
|
||||
extern void StationMonthlyLoop(void);
|
||||
|
||||
extern void PlayersYearlyLoop(void);
|
||||
extern void TrainsYearlyLoop(void);
|
||||
extern void RoadVehiclesYearlyLoop(void);
|
||||
extern void AircraftYearlyLoop(void);
|
||||
extern void ShipsYearlyLoop(void);
|
||||
|
||||
extern void ShowEndGameChart(void);
|
||||
|
||||
|
||||
static const Month _autosave_months[] = {
|
||||
0, // never
|
||||
1, // every month
|
||||
3, // every 3 months
|
||||
6, // every 6 months
|
||||
12, // every 12 months
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs the day_proc for every DAY_TICKS vehicle starting at daytick.
|
||||
*/
|
||||
static void RunVehicleDayProc(uint daytick)
|
||||
{
|
||||
uint total = GetMaxVehicleIndex() + 1;
|
||||
uint i;
|
||||
|
||||
for (i = daytick; i < total; i += DAY_TICKS) {
|
||||
Vehicle *v = GetVehicle(i);
|
||||
|
||||
if (IsValidVehicle(v)) _on_new_vehicle_day_proc[v->type - 0x10](v);
|
||||
}
|
||||
}
|
||||
|
||||
void IncreaseDate(void)
|
||||
{
|
||||
YearMonthDay ymd;
|
||||
|
||||
if (_game_mode == GM_MENU) {
|
||||
_tick_counter++;
|
||||
return;
|
||||
}
|
||||
|
||||
RunVehicleDayProc(_date_fract);
|
||||
|
||||
/* increase day, and check if a new day is there? */
|
||||
_tick_counter++;
|
||||
|
||||
_date_fract++;
|
||||
if (_date_fract < DAY_TICKS) return;
|
||||
_date_fract = 0;
|
||||
|
||||
/* yeah, increase day counter and call various daily loops */
|
||||
_date++;
|
||||
|
||||
TextMessageDailyLoop();
|
||||
|
||||
DisasterDailyLoop();
|
||||
WaypointsDailyLoop();
|
||||
|
||||
if (_game_mode != GM_MENU) {
|
||||
InvalidateWindowWidget(WC_STATUS_BAR, 0, 0);
|
||||
EnginesDailyLoop();
|
||||
}
|
||||
|
||||
/* check if we entered a new month? */
|
||||
ConvertDateToYMD(_date, &ymd);
|
||||
if (ymd.month == _cur_month) return;
|
||||
_cur_month = ymd.month;
|
||||
|
||||
/* yes, call various monthly loops */
|
||||
if (_game_mode != GM_MENU) {
|
||||
if (_opt.autosave != 0 && (_cur_month % _autosave_months[_opt.autosave]) == 0) {
|
||||
_do_autosave = true;
|
||||
RedrawAutosave();
|
||||
}
|
||||
|
||||
PlayersMonthlyLoop();
|
||||
EnginesMonthlyLoop();
|
||||
TownsMonthlyLoop();
|
||||
IndustryMonthlyLoop();
|
||||
StationMonthlyLoop();
|
||||
if (_network_server) NetworkServerMonthlyLoop();
|
||||
}
|
||||
|
||||
/* check if we entered a new year? */
|
||||
if (ymd.year == _cur_year) return;
|
||||
_cur_year = ymd.year;
|
||||
|
||||
/* yes, call various yearly loops */
|
||||
PlayersYearlyLoop();
|
||||
TrainsYearlyLoop();
|
||||
RoadVehiclesYearlyLoop();
|
||||
AircraftYearlyLoop();
|
||||
ShipsYearlyLoop();
|
||||
if (_network_server) NetworkServerYearlyLoop();
|
||||
|
||||
/* check if we reached end of the game */
|
||||
if (_cur_year == _patches.ending_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) ? 366 : 365;
|
||||
_date -= days_this_year;
|
||||
FOR_ALL_VEHICLES(v) v->date_of_last_service -= days_this_year;
|
||||
|
||||
/* 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 */
|
||||
InitTextMessage();
|
||||
}
|
||||
|
||||
if (_patches.auto_euro) CheckSwitchToEuro();
|
||||
}
|
||||
61
src/date.h
Normal file
61
src/date.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef DATE_H
|
||||
#define DATE_H
|
||||
|
||||
#include "openttd.h"
|
||||
|
||||
/**
|
||||
* 1 day is 74 ticks; _date_fract used to be uint16 and incremented by 885. On
|
||||
* an overflow the new day begun and 65535 / 885 = 74.
|
||||
* 1 tick is approximately 30 ms.
|
||||
* 1 day is thus about 2 seconds (74 * 30 = 2220) on a machine that can run OpenTTD normally
|
||||
*/
|
||||
#define DAY_TICKS 74
|
||||
|
||||
/*
|
||||
* ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR and DAYS_TILL_ORIGINAL_BASE_YEAR are
|
||||
* primarily used for loading newgrf and savegame data and returning some
|
||||
* newgrf (callback) functions that were in the original (TTD) inherited
|
||||
* format, where '_date == 0' meant that it was 1920-01-01.
|
||||
*/
|
||||
|
||||
/** The minimum starting year/base year of the original TTD */
|
||||
#define ORIGINAL_BASE_YEAR 1920
|
||||
/** The maximum year of the original TTD */
|
||||
#define ORIGINAL_MAX_YEAR 2090
|
||||
|
||||
/**
|
||||
* The offset in days from the '_date == 0' till
|
||||
* 'ConvertYMDToDate(ORIGINAL_BASE_YEAR, 0, 1)'
|
||||
*/
|
||||
#define DAYS_TILL_ORIGINAL_BASE_YEAR (365 * ORIGINAL_BASE_YEAR + ORIGINAL_BASE_YEAR / 4 - ORIGINAL_BASE_YEAR / 100 + ORIGINAL_BASE_YEAR / 400)
|
||||
|
||||
/* The absolute minimum & maximum years in OTTD */
|
||||
#define MIN_YEAR 0
|
||||
/* MAX_YEAR, nicely rounded value of the number of years that can
|
||||
* be encoded in a single 32 bits date, about 2^31 / 366 years. */
|
||||
#define MAX_YEAR 5000000
|
||||
|
||||
/* Year and Date are defined elsewhere */
|
||||
typedef uint8 Month;
|
||||
typedef uint8 Day;
|
||||
typedef uint16 DateFract;
|
||||
|
||||
typedef struct YearMonthDay {
|
||||
Year year;
|
||||
Month month;
|
||||
Day day;
|
||||
} YearMonthDay;
|
||||
|
||||
extern Year _cur_year;
|
||||
extern Month _cur_month;
|
||||
extern Date _date;
|
||||
extern DateFract _date_fract;
|
||||
|
||||
|
||||
void SetDate(Date date);
|
||||
void ConvertDateToYMD(Date date, YearMonthDay *ymd);
|
||||
Date ConvertYMDToDate(Year year, Month month, Day day);
|
||||
|
||||
#endif /* DATE_H */
|
||||
153
src/debug.c
Normal file
153
src/debug.c
Normal file
@@ -0,0 +1,153 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "openttd.h"
|
||||
#include "console.h"
|
||||
#include "debug.h"
|
||||
#include "functions.h"
|
||||
#include "string.h"
|
||||
|
||||
int _debug_ai_level;
|
||||
int _debug_driver_level;
|
||||
int _debug_grf_level;
|
||||
int _debug_map_level;
|
||||
int _debug_misc_level;
|
||||
int _debug_ms_level;
|
||||
int _debug_net_level;
|
||||
int _debug_sprite_level;
|
||||
int _debug_oldloader_level;
|
||||
int _debug_ntp_level;
|
||||
int _debug_npf_level;
|
||||
int _debug_yapf_level;
|
||||
int _debug_freetype_level;
|
||||
int _debug_sl_level;
|
||||
|
||||
|
||||
typedef struct DebugLevel {
|
||||
const char *name;
|
||||
int *level;
|
||||
} DebugLevel;
|
||||
|
||||
#define DEBUG_LEVEL(x) { #x, &_debug_##x##_level }
|
||||
static const DebugLevel debug_level[] = {
|
||||
DEBUG_LEVEL(ai),
|
||||
DEBUG_LEVEL(driver),
|
||||
DEBUG_LEVEL(grf),
|
||||
DEBUG_LEVEL(map),
|
||||
DEBUG_LEVEL(misc),
|
||||
DEBUG_LEVEL(ms),
|
||||
DEBUG_LEVEL(net),
|
||||
DEBUG_LEVEL(sprite),
|
||||
DEBUG_LEVEL(oldloader),
|
||||
DEBUG_LEVEL(ntp),
|
||||
DEBUG_LEVEL(npf),
|
||||
DEBUG_LEVEL(yapf),
|
||||
DEBUG_LEVEL(freetype),
|
||||
DEBUG_LEVEL(sl),
|
||||
};
|
||||
#undef DEBUG_LEVEL
|
||||
|
||||
#if !defined(NO_DEBUG_MESSAGES)
|
||||
|
||||
/** Functionized DEBUG macro for compilers that don't support
|
||||
* variadic macros (__VA_ARGS__) such as...yes MSVC2003 and lower */
|
||||
#if defined(NO_VARARG_MACRO)
|
||||
void CDECL DEBUG(int name, int level, ...)
|
||||
{
|
||||
va_list va;
|
||||
const char *dbg;
|
||||
const DebugLevel *dl = &debug_level[name];
|
||||
|
||||
if (level != 0 && *dl->level < level) return;
|
||||
dbg = dl->name;
|
||||
va_start(va, level);
|
||||
#else
|
||||
void CDECL debug(const char *dbg, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, dbg);
|
||||
#endif /* NO_VARARG_MACRO */
|
||||
{
|
||||
const char *s;
|
||||
char buf[1024];
|
||||
|
||||
s = va_arg(va, const char*);
|
||||
vsnprintf(buf, lengthof(buf), s, va);
|
||||
va_end(va);
|
||||
fprintf(stderr, "dbg: [%s] %s\n", dbg, buf);
|
||||
IConsoleDebug(dbg, buf);
|
||||
}
|
||||
}
|
||||
#endif /* NO_DEBUG_MESSAGES */
|
||||
|
||||
void SetDebugString(const char *s)
|
||||
{
|
||||
int v;
|
||||
char *end;
|
||||
const char *t;
|
||||
|
||||
// global debugging level?
|
||||
if (*s >= '0' && *s <= '9') {
|
||||
const DebugLevel *i;
|
||||
|
||||
v = strtoul(s, &end, 0);
|
||||
s = end;
|
||||
|
||||
for (i = debug_level; i != endof(debug_level); ++i) *i->level = v;
|
||||
}
|
||||
|
||||
// individual levels
|
||||
for (;;) {
|
||||
const DebugLevel *i;
|
||||
int *p;
|
||||
|
||||
// skip delimiters
|
||||
while (*s == ' ' || *s == ',' || *s == '\t') s++;
|
||||
if (*s == '\0') break;
|
||||
|
||||
t = s;
|
||||
while (*s >= 'a' && *s <= 'z') s++;
|
||||
|
||||
// check debugging levels
|
||||
p = NULL;
|
||||
for (i = debug_level; i != endof(debug_level); ++i)
|
||||
if (s == t + strlen(i->name) && strncmp(t, i->name, s - t) == 0) {
|
||||
p = i->level;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*s == '=') s++;
|
||||
v = strtoul(s, &end, 0);
|
||||
s = end;
|
||||
if (p != NULL) {
|
||||
*p = v;
|
||||
} else {
|
||||
ShowInfoF("Unknown debug level '%.*s'", s - t, t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Print out the current debug-level
|
||||
* Just return a string with the values of all the debug categorites
|
||||
* @return string with debug-levels
|
||||
*/
|
||||
const char *GetDebugString(void)
|
||||
{
|
||||
const DebugLevel *i;
|
||||
static char dbgstr[100];
|
||||
char dbgval[20];
|
||||
|
||||
memset(dbgstr, 0, sizeof(dbgstr));
|
||||
i = debug_level;
|
||||
snprintf(dbgstr, sizeof(dbgstr), "%s=%d", i->name, *i->level);
|
||||
|
||||
for (i++; i != endof(debug_level); i++) {
|
||||
snprintf(dbgval, sizeof(dbgval), ", %s=%d", i->name, *i->level);
|
||||
ttd_strlcat(dbgstr, dbgval, sizeof(dbgstr));
|
||||
}
|
||||
|
||||
return dbgstr;
|
||||
}
|
||||
108
src/debug.h
Normal file
108
src/debug.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef DEBUG_H
|
||||
#define DEBUG_H
|
||||
|
||||
/* Debugging messages policy:
|
||||
* These should be the severities used for direct DEBUG() calls
|
||||
* maximum debugging level should be 10 if really deep, deep
|
||||
* debugging is needed.
|
||||
* (there is room for exceptions, but you have to have a good cause):
|
||||
* 0 - errors or severe warnings
|
||||
* 1 - other non-fatal, non-severe warnings
|
||||
* 2 - crude progress indicator of functionality
|
||||
* 3 - important debugging messages (function entry)
|
||||
* 4 - debugging messages (crude loop status, etc.)
|
||||
* 5 - detailed debugging information
|
||||
* 6.. - extremely detailed spamming
|
||||
*/
|
||||
|
||||
/* Of course MSVC 2003 and lower has no support for variadic macros
|
||||
* so we need to work around this... *sigh* */
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1400)
|
||||
#define NO_VARARG_MACRO
|
||||
#endif
|
||||
|
||||
#if defined(NO_VARARG_MACRO)
|
||||
enum DebugLevelType {
|
||||
ai,
|
||||
driver,
|
||||
grf,
|
||||
map,
|
||||
misc,
|
||||
ms,
|
||||
net,
|
||||
sprite,
|
||||
oldloader,
|
||||
ntp,
|
||||
npf,
|
||||
yapf,
|
||||
freetype,
|
||||
sl,
|
||||
};
|
||||
#endif /* NO_VARARG_MACRO */
|
||||
|
||||
#ifdef NO_DEBUG_MESSAGES
|
||||
#if defined(NO_VARARG_MACRO)
|
||||
static inline void DEBUG(int name, int level, ...) {}
|
||||
#elif defined(__GNUC__) && (__GNUC__ < 3)
|
||||
#define DEBUG(name, level, args...)
|
||||
#else
|
||||
#define DEBUG(name, level, ...)
|
||||
#endif
|
||||
#else /* NO_DEBUG_MESSAGES */
|
||||
#if defined(NO_VARARG_MACRO)
|
||||
void CDECL DEBUG(int name, int level, ...);
|
||||
#elif defined(__GNUC__) && (__GNUC__ < 3)
|
||||
#define DEBUG(name, level, args...) if ((level == 0) || ( _debug_ ## name ## _level >= level)) debug(#name, args)
|
||||
#else
|
||||
#define DEBUG(name, level, ...) if (level == 0 || _debug_ ## name ## _level >= level) debug(#name, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
extern int _debug_ai_level;
|
||||
extern int _debug_driver_level;
|
||||
extern int _debug_grf_level;
|
||||
extern int _debug_map_level;
|
||||
extern int _debug_misc_level;
|
||||
extern int _debug_ms_level;
|
||||
extern int _debug_net_level;
|
||||
extern int _debug_sprite_level;
|
||||
extern int _debug_oldloader_level;
|
||||
extern int _debug_ntp_level;
|
||||
extern int _debug_npf_level;
|
||||
extern int _debug_yapf_level;
|
||||
extern int _debug_freetype_level;
|
||||
extern int _debug_sl_level;
|
||||
|
||||
#if !defined(NO_VARARG_MACRO)
|
||||
void CDECL debug(const char *dbg, ...);
|
||||
#endif /* NO_VARARG_MACRO */
|
||||
#endif /* NO_DEBUG_MESSAGES */
|
||||
|
||||
void SetDebugString(const char *s);
|
||||
const char *GetDebugString(void);
|
||||
|
||||
/* MSVCRT of course has to have a different syntax for long long *sigh* */
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#define OTTD_PRINTF64 "I64"
|
||||
#else
|
||||
#define OTTD_PRINTF64 "ll"
|
||||
#endif
|
||||
|
||||
// Used for profiling
|
||||
#define TIC() {\
|
||||
extern uint64 _rdtsc(void);\
|
||||
uint64 _xxx_ = _rdtsc();\
|
||||
static uint64 __sum__ = 0;\
|
||||
static uint32 __i__ = 0;
|
||||
|
||||
#define TOC(str, count)\
|
||||
__sum__ += _rdtsc() - _xxx_;\
|
||||
if (++__i__ == count) {\
|
||||
DEBUG(misc, 0, "[%s] %" OTTD_PRINTF64 "u [avg: %.1f]\n", str, __sum__, __sum__/(double)__i__);\
|
||||
__i__ = 0;\
|
||||
__sum__ = 0;\
|
||||
}\
|
||||
}
|
||||
|
||||
#endif /* DEBUG_H */
|
||||
58
src/dedicated.c
Normal file
58
src/dedicated.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#if defined(UNIX) && !defined(__MORPHOS__)
|
||||
|
||||
#include "openttd.h"
|
||||
#include "variables.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void DedicatedFork(void)
|
||||
{
|
||||
/* Fork the program */
|
||||
pid_t pid = fork();
|
||||
switch (pid) {
|
||||
case -1:
|
||||
perror("Unable to fork");
|
||||
exit(1);
|
||||
|
||||
case 0: { // We're the child
|
||||
FILE* f;
|
||||
|
||||
/* Open the log-file to log all stuff too */
|
||||
f = fopen(_log_file, "a");
|
||||
if (f == NULL) {
|
||||
perror("Unable to open logfile");
|
||||
exit(1);
|
||||
}
|
||||
/* Redirect stdout and stderr to log-file */
|
||||
if (dup2(fileno(f), fileno(stdout)) == -1) {
|
||||
perror("Rerouting stdout");
|
||||
exit(1);
|
||||
}
|
||||
if (dup2(fileno(f), fileno(stderr)) == -1) {
|
||||
perror("Rerouting stderr");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// We're the parent
|
||||
printf("Loading dedicated server...\n");
|
||||
printf(" - Forked to background with pid %d\n", pid);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
void DedicatedFork(void) {}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
127
src/depot.c
Normal file
127
src/depot.c
Normal file
@@ -0,0 +1,127 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "depot.h"
|
||||
#include "functions.h"
|
||||
#include "tile.h"
|
||||
#include "map.h"
|
||||
#include "table/strings.h"
|
||||
#include "saveload.h"
|
||||
#include "order.h"
|
||||
|
||||
|
||||
/**
|
||||
* Called if a new block is added to the depot-pool
|
||||
*/
|
||||
static void DepotPoolNewBlock(uint start_item)
|
||||
{
|
||||
Depot *d;
|
||||
|
||||
/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
|
||||
* TODO - This is just a temporary stage, this will be removed. */
|
||||
for (d = GetDepot(start_item); d != NULL; d = (d->index + 1U < GetDepotPoolSize()) ? GetDepot(d->index + 1U) : NULL) d->index = start_item++;
|
||||
}
|
||||
|
||||
DEFINE_OLD_POOL(Depot, Depot, DepotPoolNewBlock, NULL)
|
||||
|
||||
|
||||
/**
|
||||
* Gets a depot from a tile
|
||||
*
|
||||
* @return Returns the depot if the tile had a depot, else it returns NULL
|
||||
*/
|
||||
Depot *GetDepotByTile(TileIndex tile)
|
||||
{
|
||||
Depot *depot;
|
||||
|
||||
FOR_ALL_DEPOTS(depot) {
|
||||
if (depot->xy == tile) return depot;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a new depot
|
||||
*/
|
||||
Depot *AllocateDepot(void)
|
||||
{
|
||||
Depot *d;
|
||||
|
||||
/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
|
||||
* TODO - This is just a temporary stage, this will be removed. */
|
||||
for (d = GetDepot(0); d != NULL; d = (d->index + 1U < GetDepotPoolSize()) ? GetDepot(d->index + 1U) : NULL) {
|
||||
if (!IsValidDepot(d)) {
|
||||
DepotID index = d->index;
|
||||
|
||||
memset(d, 0, sizeof(Depot));
|
||||
d->index = index;
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we can add a block to the pool */
|
||||
if (AddBlockToPool(&_Depot_pool)) return AllocateDepot();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up a depot
|
||||
*/
|
||||
void DestroyDepot(Depot *depot)
|
||||
{
|
||||
/* Clear the tile */
|
||||
DoClearSquare(depot->xy);
|
||||
|
||||
/* Clear the depot from all order-lists */
|
||||
RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, depot->index);
|
||||
|
||||
/* Delete the depot-window */
|
||||
DeleteWindowById(WC_VEHICLE_DEPOT, depot->xy);
|
||||
}
|
||||
|
||||
void InitializeDepots(void)
|
||||
{
|
||||
CleanPool(&_Depot_pool);
|
||||
AddBlockToPool(&_Depot_pool);
|
||||
}
|
||||
|
||||
|
||||
static const SaveLoad _depot_desc[] = {
|
||||
SLE_CONDVAR(Depot, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
|
||||
SLE_CONDVAR(Depot, xy, SLE_UINT32, 6, SL_MAX_VERSION),
|
||||
SLE_VAR(Depot, town_index, SLE_UINT16),
|
||||
SLE_END()
|
||||
};
|
||||
|
||||
static void Save_DEPT(void)
|
||||
{
|
||||
Depot *depot;
|
||||
|
||||
FOR_ALL_DEPOTS(depot) {
|
||||
SlSetArrayIndex(depot->index);
|
||||
SlObject(depot, _depot_desc);
|
||||
}
|
||||
}
|
||||
|
||||
static void Load_DEPT(void)
|
||||
{
|
||||
int index;
|
||||
|
||||
while ((index = SlIterateArray()) != -1) {
|
||||
Depot *depot;
|
||||
|
||||
if (!AddBlockIfNeeded(&_Depot_pool, index))
|
||||
error("Depots: failed loading savegame: too many depots");
|
||||
|
||||
depot = GetDepot(index);
|
||||
SlObject(depot, _depot_desc);
|
||||
}
|
||||
}
|
||||
|
||||
const ChunkHandler _depot_chunk_handlers[] = {
|
||||
{ 'DEPT', Save_DEPT, Load_DEPT, CH_ARRAY | CH_LAST},
|
||||
};
|
||||
112
src/depot.h
Normal file
112
src/depot.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef DEPOT_H
|
||||
#define DEPOT_H
|
||||
|
||||
/** @file depot.h Header files for depots (not hangars)
|
||||
* @see depot.c */
|
||||
|
||||
#include "direction.h"
|
||||
#include "oldpool.h"
|
||||
#include "tile.h"
|
||||
#include "variables.h"
|
||||
|
||||
struct Depot {
|
||||
TileIndex xy;
|
||||
TownID town_index;
|
||||
DepotID index;
|
||||
};
|
||||
|
||||
DECLARE_OLD_POOL(Depot, Depot, 3, 8000)
|
||||
|
||||
/**
|
||||
* Check if a depot really exists.
|
||||
*/
|
||||
static inline bool IsValidDepot(const Depot *depot)
|
||||
{
|
||||
return depot != NULL && depot->xy != 0;
|
||||
}
|
||||
|
||||
static inline bool IsValidDepotID(uint index)
|
||||
{
|
||||
return index < GetDepotPoolSize() && IsValidDepot(GetDepot(index));
|
||||
}
|
||||
|
||||
void DestroyDepot(Depot *depot);
|
||||
|
||||
static inline void DeleteDepot(Depot *depot)
|
||||
{
|
||||
DestroyDepot(depot);
|
||||
depot->xy = 0;
|
||||
}
|
||||
|
||||
void ShowDepotWindow(TileIndex tile, byte type);
|
||||
|
||||
#define FOR_ALL_DEPOTS_FROM(d, start) for (d = GetDepot(start); d != NULL; d = (d->index + 1U < GetDepotPoolSize()) ? GetDepot(d->index + 1U) : NULL) if (IsValidDepot(d))
|
||||
#define FOR_ALL_DEPOTS(d) FOR_ALL_DEPOTS_FROM(d, 0)
|
||||
|
||||
#define MIN_SERVINT_PERCENT 5
|
||||
#define MAX_SERVINT_PERCENT 90
|
||||
#define MIN_SERVINT_DAYS 30
|
||||
#define MAX_SERVINT_DAYS 800
|
||||
|
||||
/**
|
||||
* Get the service interval domain.
|
||||
* Get the new proposed service interval for the vehicle is indeed, clamped
|
||||
* within the given bounds. @see MIN_SERVINT_PERCENT ,etc.
|
||||
* @param index proposed service interval
|
||||
*/
|
||||
static inline Date GetServiceIntervalClamped(uint index)
|
||||
{
|
||||
return (_patches.servint_ispercent) ? clamp(index, MIN_SERVINT_PERCENT, MAX_SERVINT_PERCENT) : clamp(index, MIN_SERVINT_DAYS, MAX_SERVINT_DAYS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a tile is a depot of the given type.
|
||||
*/
|
||||
static inline bool IsTileDepotType(TileIndex tile, TransportType type)
|
||||
{
|
||||
switch (type) {
|
||||
case TRANSPORT_RAIL:
|
||||
return IsTileType(tile, MP_RAILWAY) && (_m[tile].m5 & 0xFC) == 0xC0;
|
||||
|
||||
case TRANSPORT_ROAD:
|
||||
return IsTileType(tile, MP_STREET) && (_m[tile].m5 & 0xF0) == 0x20;
|
||||
|
||||
case TRANSPORT_WATER:
|
||||
return IsTileType(tile, MP_WATER) && (_m[tile].m5 & ~3) == 0x80;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find out if the slope of the tile is suitable to build a depot of given direction
|
||||
* @param direction The direction in which the depot's exit points. Starts with 0 as NE and goes Clockwise
|
||||
* @param tileh The slope of the tile in question
|
||||
* @return true if the construction is possible
|
||||
|
||||
* This is checked by the ugly 0x4C >> direction magic, which does the following:
|
||||
* 0x4C is 0100 1100 and tileh has only bits 0..3 set (steep tiles are ruled out)
|
||||
* So: for direction (only the significant bits are shown)<p>
|
||||
* 00 (exit towards NE) we need either bit 2 or 3 set in tileh: 0x4C >> 0 = 1100<p>
|
||||
* 01 (exit towards SE) we need either bit 1 or 2 set in tileh: 0x4C >> 1 = 0110<p>
|
||||
* 02 (exit towards SW) we need either bit 0 or 1 set in tileh: 0x4C >> 2 = 0011<p>
|
||||
* 03 (exit towards NW) we need either bit 0 or 4 set in tileh: 0x4C >> 3 = 1001<p>
|
||||
* So ((0x4C >> direction) & tileh) determines whether the depot can be built on the current tileh
|
||||
*/
|
||||
static inline bool CanBuildDepotByTileh(uint32 direction, Slope tileh)
|
||||
{
|
||||
return ((0x4C >> direction) & tileh) != 0;
|
||||
}
|
||||
|
||||
Depot *GetDepotByTile(TileIndex tile);
|
||||
void InitializeDepots(void);
|
||||
Depot *AllocateDepot(void);
|
||||
|
||||
void DeleteDepotHighlightOfVehicle(const Vehicle *v);
|
||||
|
||||
#endif /* DEPOT_H */
|
||||
954
src/depot_gui.c
Normal file
954
src/depot_gui.c
Normal file
@@ -0,0 +1,954 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "functions.h"
|
||||
#include "train.h"
|
||||
#include "roadveh.h"
|
||||
#include "ship.h"
|
||||
#include "aircraft.h"
|
||||
#include "table/strings.h"
|
||||
#include "table/sprites.h"
|
||||
#include "gui.h"
|
||||
#include "gfx.h"
|
||||
#include "vehicle.h"
|
||||
#include "viewport.h"
|
||||
#include "command.h"
|
||||
#include "depot.h"
|
||||
#include "vehicle_gui.h"
|
||||
#include "station_map.h"
|
||||
#include "newgrf_engine.h"
|
||||
|
||||
/*
|
||||
* Since all depot window sizes aren't the same, we need to modify sizes a little.
|
||||
* It's done with the following arrays of widget indexes. Each of them tells if a widget side should be moved and in what direction.
|
||||
* How long they should be moved and for what window types are controlled in ShowDepotWindow()
|
||||
*/
|
||||
|
||||
/* Names of the widgets. Keep them in the same order as in the widget array */
|
||||
enum DepotWindowWidgets {
|
||||
DEPOT_WIDGET_CLOSEBOX = 0,
|
||||
DEPOT_WIDGET_CAPTION,
|
||||
DEPOT_WIDGET_STICKY,
|
||||
DEPOT_WIDGET_SELL,
|
||||
DEPOT_WIDGET_SELL_CHAIN,
|
||||
DEPOT_WIDGET_SELL_ALL,
|
||||
DEPOT_WIDGET_AUTOREPLACE,
|
||||
DEPOT_WIDGET_MATRIX,
|
||||
DEPOT_WIDGET_V_SCROLL, // Vertical scrollbar
|
||||
DEPOT_WIDGET_H_SCROLL, // Horizontal scrollbar
|
||||
DEPOT_WIDGET_BUILD,
|
||||
DEPOT_WIDGET_CLONE,
|
||||
DEPOT_WIDGET_LOCATION,
|
||||
DEPOT_WIDGET_VEHICLE_LIST,
|
||||
DEPOT_WIDGET_STOP_ALL,
|
||||
DEPOT_WIDGET_START_ALL,
|
||||
DEPOT_WIDGET_RESIZE,
|
||||
};
|
||||
|
||||
/* Widget array for all depot windows.
|
||||
* If a widget is needed in some windows only (like train specific), add it for all windows
|
||||
* and use HideWindowWidget in ShowDepotWindow() to remove it in the windows where it should not be
|
||||
* Keep the widget numbers in sync with the enum or really bad stuff will happen!!! */
|
||||
|
||||
/* When adding widgets, place them as you would place them for the ship depot and define how you want it to move in widget_moves[]
|
||||
* If you want a widget for one window only, set it to be hidden in ShowDepotWindow() for the windows where you don't want it
|
||||
* NOTE: the train only widgets are moved/resized in ShowDepotWindow() so they follow certain other widgets if they are moved to ensure that they stick together.
|
||||
* Changing the size of those here will not have an effect at all. It should be done in ShowDepotWindow()
|
||||
*/
|
||||
static const Widget _depot_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // DEPOT_WIDGET_CLOSEBOX
|
||||
{ WWT_CAPTION, RESIZE_RIGHT, 14, 11, 292, 0, 13, 0x0, STR_018C_WINDOW_TITLE_DRAG_THIS}, // DEPOT_WIDGET_CAPTION
|
||||
{ WWT_STICKYBOX, RESIZE_LR, 14, 293, 304, 0, 13, 0x0, STR_STICKY_BUTTON}, // DEPOT_WIDGET_STICKY
|
||||
|
||||
/* Widgets are set up run-time */
|
||||
{ WWT_IMGBTN, RESIZE_LRB, 14, 270, 292, 14, 37, 0x0, STR_NULL}, // DEPOT_WIDGET_SELL
|
||||
{ WWT_IMGBTN, RESIZE_LRTB, 14, 270, 292, 14, 37, SPR_SELL_CHAIN_TRAIN,STR_DRAG_WHOLE_TRAIN_TO_SELL_TIP}, // DEPOT_WIDGET_SELL_CHAIN, trains only
|
||||
{ WWT_PUSHIMGBTN, RESIZE_LRTB, 14, 270, 292, 38, 60, 0x0, STR_NULL}, // DEPOT_WIDGET_SELL_ALL
|
||||
{ WWT_PUSHIMGBTN, RESIZE_LRTB, 14, 270, 292, 61, 83, 0x0, STR_NULL}, // DEPOT_WIDGET_AUTOREPLACE
|
||||
|
||||
{ WWT_MATRIX, RESIZE_RB, 14, 0, 269, 14, 83, 0x0, STR_NULL}, // DEPOT_WIDGET_MATRIX
|
||||
{ WWT_SCROLLBAR, RESIZE_LRB, 14, 293, 304, 14, 83, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, // DEPOT_WIDGET_V_SCROLL
|
||||
|
||||
{ WWT_HSCROLLBAR, RESIZE_RTB, 14, 0, 269, 72, 83, 0x0, STR_HSCROLL_BAR_SCROLLS_LIST}, // DEPOT_WIDGET_H_SCROLL, trains only
|
||||
|
||||
/* The buttons in the bottom of the window. left and right is not important as they are later resized to be equal in size
|
||||
* This calculation is based on right in DEPOT_WIDGET_LOCATION and it presumes left of DEPOT_WIDGET_BUILD is 0 */
|
||||
{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 85, 84, 95, 0x0, STR_NULL}, // DEPOT_WIDGET_BUILD
|
||||
{ WWT_TEXTBTN, RESIZE_TB, 14, 86, 170, 84, 95, 0x0, STR_NULL}, // DEPOT_WIDGET_CLONE
|
||||
{ WWT_PUSHTXTBTN, RESIZE_RTB, 14, 171, 257, 84, 95, STR_00E4_LOCATION, STR_NULL}, // DEPOT_WIDGET_LOCATION
|
||||
{ WWT_PUSHTXTBTN, RESIZE_LRTB, 14, 258, 269, 84, 95, 0x0, STR_NULL}, // DEPOT_WIDGET_VEHICLE_LIST
|
||||
{ WWT_PUSHIMGBTN, RESIZE_LRTB, 14, 270, 280, 84, 95, SPR_FLAG_VEH_STOPPED,STR_NULL}, // DEPOT_WIDGET_STOP_ALL
|
||||
{ WWT_PUSHIMGBTN, RESIZE_LRTB, 14, 281, 292, 84, 95, SPR_FLAG_VEH_RUNNING,STR_NULL}, // DEPOT_WIDGET_START_ALL
|
||||
{ WWT_RESIZEBOX, RESIZE_LRTB, 14, 293, 304, 84, 95, 0x0, STR_RESIZE_BUTTON}, // DEPOT_WIDGET_RESIZE
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static void DepotWndProc(Window *w, WindowEvent *e);
|
||||
|
||||
static const WindowDesc _train_depot_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 305, 96,
|
||||
WC_VEHICLE_DEPOT,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
|
||||
_depot_widgets,
|
||||
DepotWndProc
|
||||
};
|
||||
|
||||
static const WindowDesc _road_depot_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 305, 96,
|
||||
WC_VEHICLE_DEPOT,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
|
||||
_depot_widgets,
|
||||
DepotWndProc
|
||||
};
|
||||
|
||||
static const WindowDesc _ship_depot_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 305, 96,
|
||||
WC_VEHICLE_DEPOT,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
|
||||
_depot_widgets,
|
||||
DepotWndProc
|
||||
};
|
||||
|
||||
static const WindowDesc _aircraft_depot_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 305, 96,
|
||||
WC_VEHICLE_DEPOT,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
|
||||
_depot_widgets,
|
||||
DepotWndProc
|
||||
};
|
||||
|
||||
extern int WagonLengthToPixels(int len);
|
||||
|
||||
void CcCloneVehicle(bool success, TileIndex tile, uint32 p1, uint32 p2)
|
||||
{
|
||||
if (!success) return;
|
||||
switch(GetVehicle(p1)->type) {
|
||||
case VEH_Train: CcCloneTrain( true, tile, p1, p2); break;
|
||||
case VEH_Road: CcCloneRoadVeh( true, tile, p1, p2); break;
|
||||
case VEH_Ship: CcCloneShip( true, tile, p1, p2); break;
|
||||
case VEH_Aircraft: CcCloneAircraft(true, tile, p1, p2); break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ShowVehicleViewWindow(const Vehicle *v)
|
||||
{
|
||||
switch (v->type) {
|
||||
case VEH_Train: ShowTrainViewWindow(v); break;
|
||||
case VEH_Road: ShowRoadVehViewWindow(v); break;
|
||||
case VEH_Ship: ShowShipViewWindow(v); break;
|
||||
case VEH_Aircraft: ShowAircraftViewWindow(v); break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
static void DepotSellAllConfirmationCallback(Window *w, bool confirmed)
|
||||
{
|
||||
if (confirmed) {
|
||||
TileIndex tile = w->window_number;
|
||||
byte vehtype = WP(w, depot_d).type;
|
||||
DoCommandP(tile, vehtype, 0, NULL, CMD_DEPOT_SELL_ALL_VEHICLES);
|
||||
}
|
||||
}
|
||||
|
||||
/** Draw a vehicle in the depot window in the box with the top left corner at x,y
|
||||
* @param *w Window to draw in
|
||||
* @param *v Vehicle to draw
|
||||
* @param x Left side of the box to draw in
|
||||
* @param y Top of the box to draw in
|
||||
*/
|
||||
static void DrawVehicleInDepot(Window *w, const Vehicle *v, int x, int y)
|
||||
{
|
||||
byte diff_x = 0, diff_y = 0;
|
||||
|
||||
switch (v->type) {
|
||||
case VEH_Train:
|
||||
DrawTrainImage(v, x + 21, y, w->hscroll.cap + 4, w->hscroll.pos, WP(w,depot_d).sel);
|
||||
|
||||
/* Number of wagons relative to a standard length wagon (rounded up) */
|
||||
SetDParam(0, (v->u.rail.cached_total_length + 7) / 8);
|
||||
DrawStringRightAligned(w->widget[DEPOT_WIDGET_MATRIX].right - 1, y + 4, STR_TINY_BLACK, 0); // Draw the counter
|
||||
break;
|
||||
|
||||
case VEH_Road: DrawRoadVehImage( v, x + 24, y, WP(w, depot_d).sel); break;
|
||||
case VEH_Ship: DrawShipImage( v, x + 19, y, WP(w, depot_d).sel); break;
|
||||
case VEH_Aircraft: DrawAircraftImage(v, x + 12, y, WP(w, depot_d).sel); break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
|
||||
if (w->resize.step_height == 14) {
|
||||
/* VEH_Train and VEH_Road, which are low */
|
||||
diff_x = 15;
|
||||
} else {
|
||||
/* VEH_Ship and VEH_Aircraft, which are tall */
|
||||
diff_y = 12;
|
||||
}
|
||||
|
||||
DrawSprite((v->vehstatus & VS_STOPPED) ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, x + diff_x, y + diff_y);
|
||||
|
||||
SetDParam(0, v->unitnumber);
|
||||
DrawString(x, y + 2, (uint16)(v->max_age-366) >= v->age ? STR_00E2 : STR_00E3, 0);
|
||||
}
|
||||
|
||||
static void DrawDepotWindow(Window *w)
|
||||
{
|
||||
Vehicle **vl = WP(w, depot_d).vehicle_list;
|
||||
TileIndex tile = w->window_number;
|
||||
int x, y, i, hnum, max;
|
||||
uint16 num = WP(w, depot_d).engine_count;
|
||||
|
||||
/* Set the row and number of boxes in each row based on the number of boxes drawn in the matrix */
|
||||
uint16 rows_in_display = w->widget[DEPOT_WIDGET_MATRIX].data >> 8;
|
||||
uint16 boxes_in_each_row = w->widget[DEPOT_WIDGET_MATRIX].data & 0xFF;
|
||||
|
||||
/* setup disabled buttons */
|
||||
SetWindowWidgetsDisabledState(w, !IsTileOwner(tile, _local_player),
|
||||
DEPOT_WIDGET_STOP_ALL,
|
||||
DEPOT_WIDGET_START_ALL,
|
||||
DEPOT_WIDGET_SELL,
|
||||
DEPOT_WIDGET_SELL_CHAIN,
|
||||
DEPOT_WIDGET_SELL_ALL,
|
||||
DEPOT_WIDGET_BUILD,
|
||||
DEPOT_WIDGET_CLONE,
|
||||
DEPOT_WIDGET_AUTOREPLACE,
|
||||
WIDGET_LIST_END);
|
||||
|
||||
/* determine amount of items for scroller */
|
||||
if (WP(w, depot_d).type == VEH_Train) {
|
||||
hnum = 8;
|
||||
for (num = 0; num < WP(w, depot_d).engine_count; num++) {
|
||||
const Vehicle *v = vl[num];
|
||||
hnum = maxu(hnum, v->u.rail.cached_total_length);
|
||||
}
|
||||
/* Always have 1 empty row, so people can change the setting of the train */
|
||||
SetVScrollCount(w, WP(w, depot_d).engine_count + WP(w, depot_d).wagon_count + 1);
|
||||
SetHScrollCount(w, WagonLengthToPixels(hnum));
|
||||
} else {
|
||||
SetVScrollCount(w, (num + w->hscroll.cap - 1) / w->hscroll.cap);
|
||||
}
|
||||
|
||||
/* locate the depot struct */
|
||||
if (WP(w, depot_d).type == VEH_Aircraft) {
|
||||
SetDParam(0, GetStationIndex(tile)); // Airport name
|
||||
} else {
|
||||
Depot *depot = GetDepotByTile(tile);
|
||||
assert(depot != NULL);
|
||||
|
||||
SetDParam(0, depot->town_index);
|
||||
}
|
||||
|
||||
DrawWindowWidgets(w);
|
||||
|
||||
num = w->vscroll.pos * boxes_in_each_row;
|
||||
max = min(WP(w, depot_d).engine_count, num + (rows_in_display * boxes_in_each_row));
|
||||
|
||||
for (x = 2, y = 15; num < max; y += w->resize.step_height, x = 2) { // Draw the rows
|
||||
byte i;
|
||||
|
||||
for (i = 0; i < boxes_in_each_row && num < max; i++, num++, x += w->resize.step_width) {
|
||||
/* Draw all vehicles in the current row */
|
||||
const Vehicle *v = vl[num];
|
||||
DrawVehicleInDepot(w, v, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
max = min(WP(w, depot_d).engine_count + WP(w, depot_d).wagon_count, (w->vscroll.pos * boxes_in_each_row) + (rows_in_display * boxes_in_each_row));
|
||||
|
||||
/* draw the train wagons, that do not have an engine in front */
|
||||
for (; num < max; num++, y += 14) {
|
||||
const Vehicle *v = WP(w, depot_d).wagon_list[num - WP(w, depot_d).engine_count];
|
||||
const Vehicle *u;
|
||||
|
||||
DrawTrainImage(v, x + 50, y, w->hscroll.cap - 29, 0, WP(w,depot_d).sel);
|
||||
DrawString(x, y + 2, STR_8816, 0);
|
||||
|
||||
/*Draw the train counter */
|
||||
i = 0;
|
||||
u = v;
|
||||
do i++; while ( (u=u->next) != NULL); // Determine length of train
|
||||
SetDParam(0, i); // Set the counter
|
||||
DrawStringRightAligned(w->widget[DEPOT_WIDGET_MATRIX].right - 1, y + 4, STR_TINY_BLACK, 0); // Draw the counter
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct GetDepotVehiclePtData {
|
||||
Vehicle *head;
|
||||
Vehicle *wagon;
|
||||
} GetDepotVehiclePtData;
|
||||
|
||||
enum {
|
||||
MODE_ERROR = 1,
|
||||
MODE_DRAG_VEHICLE = 0,
|
||||
MODE_SHOW_VEHICLE = -1,
|
||||
MODE_START_STOP = -2,
|
||||
};
|
||||
|
||||
static int GetVehicleFromDepotWndPt(const Window *w, int x, int y, Vehicle **veh, GetDepotVehiclePtData *d)
|
||||
{
|
||||
Vehicle **vl = WP(w, depot_d).vehicle_list;
|
||||
uint xt, row, xm = 0, ym = 0;
|
||||
int pos, skip = 0;
|
||||
uint16 boxes_in_each_row = w->widget[DEPOT_WIDGET_MATRIX].data & 0xFF;
|
||||
|
||||
if (WP(w, depot_d).type == VEH_Train) {
|
||||
xt = 0;
|
||||
x -= 23;
|
||||
} else {
|
||||
xt = x / w->resize.step_width;
|
||||
xm = x % w->resize.step_width;
|
||||
if (xt >= w->hscroll.cap) return MODE_ERROR;
|
||||
|
||||
ym = (y - 14) % w->resize.step_height;
|
||||
}
|
||||
|
||||
row = (y - 14) / w->resize.step_height;
|
||||
if (row >= w->vscroll.cap) return MODE_ERROR;
|
||||
|
||||
pos = ((row + w->vscroll.pos) * boxes_in_each_row) + xt;
|
||||
|
||||
if (WP(w, depot_d).engine_count + WP(w, depot_d).wagon_count <= pos) {
|
||||
if (WP(w, depot_d).type == VEH_Train) {
|
||||
d->head = NULL;
|
||||
d->wagon = NULL;
|
||||
return MODE_DRAG_VEHICLE;
|
||||
} else {
|
||||
return MODE_ERROR; // empty block, so no vehicle is selected
|
||||
}
|
||||
}
|
||||
|
||||
if (WP(w, depot_d).engine_count > pos) {
|
||||
*veh = vl[pos];
|
||||
skip = w->hscroll.pos;
|
||||
} else {
|
||||
vl = WP(w, depot_d).wagon_list;
|
||||
pos -= WP(w, depot_d).engine_count;
|
||||
*veh = vl[pos];
|
||||
/* free wagons don't have an initial loco. */
|
||||
x -= _traininfo_vehicle_width;
|
||||
}
|
||||
|
||||
switch (WP(w, depot_d).type) {
|
||||
case VEH_Train: {
|
||||
Vehicle *v = *veh;
|
||||
d->head = d->wagon = v;
|
||||
|
||||
/* either pressed the flag or the number, but only when it's a loco */
|
||||
if (x < 0 && IsFrontEngine(v)) return (x >= -10) ? MODE_START_STOP : MODE_SHOW_VEHICLE;
|
||||
|
||||
skip = (skip * 8) / _traininfo_vehicle_width;
|
||||
x = (x * 8) / _traininfo_vehicle_width;
|
||||
|
||||
/* Skip vehicles that are scrolled off the list */
|
||||
x += skip;
|
||||
|
||||
/* find the vehicle in this row that was clicked */
|
||||
while (v != NULL && (x -= v->u.rail.cached_veh_length) >= 0) v = v->next;
|
||||
|
||||
/* if an articulated part was selected, find its parent */
|
||||
while (v != NULL && IsArticulatedPart(v)) v = GetPrevVehicleInChain(v);
|
||||
|
||||
d->wagon = v;
|
||||
|
||||
return MODE_DRAG_VEHICLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case VEH_Road:
|
||||
if (xm >= 24) return MODE_DRAG_VEHICLE;
|
||||
if (xm <= 16) return MODE_SHOW_VEHICLE;
|
||||
break;
|
||||
|
||||
case VEH_Ship:
|
||||
if (xm >= 19) return MODE_DRAG_VEHICLE;
|
||||
if (ym <= 10) return MODE_SHOW_VEHICLE;
|
||||
break;
|
||||
|
||||
case VEH_Aircraft:
|
||||
if (xm >= 12) return MODE_DRAG_VEHICLE;
|
||||
if (ym <= 12) return MODE_SHOW_VEHICLE;
|
||||
break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
return MODE_START_STOP;
|
||||
}
|
||||
|
||||
static void TrainDepotMoveVehicle(Vehicle *wagon, VehicleID sel, Vehicle *head)
|
||||
{
|
||||
Vehicle *v;
|
||||
|
||||
v = GetVehicle(sel);
|
||||
|
||||
if (v == wagon) return;
|
||||
|
||||
if (wagon == NULL) {
|
||||
if (head != NULL) wagon = GetLastVehicleInChain(head);
|
||||
} else {
|
||||
wagon = GetPrevVehicleInChain(wagon);
|
||||
if (wagon == NULL) return;
|
||||
}
|
||||
|
||||
if (wagon == v) return;
|
||||
|
||||
DoCommandP(v->tile, v->index + ((wagon == NULL ? INVALID_VEHICLE : wagon->index) << 16), _ctrl_pressed ? 1 : 0, NULL, CMD_MOVE_RAIL_VEHICLE | CMD_MSG(STR_8837_CAN_T_MOVE_VEHICLE));
|
||||
}
|
||||
|
||||
static void DepotClick(Window *w, int x, int y)
|
||||
{
|
||||
GetDepotVehiclePtData gdvp;
|
||||
Vehicle *v = NULL;
|
||||
int mode = GetVehicleFromDepotWndPt(w, x, y, &v, &gdvp);
|
||||
|
||||
/* share / copy orders */
|
||||
if (_thd.place_mode && mode <= 0) {
|
||||
_place_clicked_vehicle = (WP(w, depot_d).type == VEH_Train ? gdvp.head : v);
|
||||
return;
|
||||
}
|
||||
|
||||
if (WP(w, depot_d).type == VEH_Train) v = gdvp.wagon;
|
||||
|
||||
switch (mode) {
|
||||
case MODE_ERROR: // invalid
|
||||
return;
|
||||
|
||||
case MODE_DRAG_VEHICLE: { // start dragging of vehicle
|
||||
VehicleID sel = WP(w, depot_d).sel;
|
||||
|
||||
if (WP(w, depot_d).type == VEH_Train && sel != INVALID_VEHICLE) {
|
||||
WP(w,depot_d).sel = INVALID_VEHICLE;
|
||||
TrainDepotMoveVehicle(v, sel, gdvp.head);
|
||||
} else if (v != NULL) {
|
||||
int image;
|
||||
|
||||
switch (WP(w, depot_d).type) {
|
||||
case VEH_Train: image = GetTrainImage(v, DIR_W); break;
|
||||
case VEH_Road: image = GetRoadVehImage(v, DIR_W); break;
|
||||
case VEH_Ship: image = GetShipImage(v, DIR_W); break;
|
||||
case VEH_Aircraft: image = GetAircraftImage(v, DIR_W); break;
|
||||
default: NOT_REACHED(); image = 0;
|
||||
}
|
||||
|
||||
WP(w, depot_d).sel = v->index;
|
||||
SetWindowDirty(w);
|
||||
SetObjectToPlaceWnd(GetVehiclePalette(v) | image, 4, w);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MODE_SHOW_VEHICLE: // show info window
|
||||
ShowVehicleViewWindow(v);
|
||||
break;
|
||||
|
||||
case MODE_START_STOP: { // click start/stop flag
|
||||
uint command;
|
||||
|
||||
switch (WP(w, depot_d).type) {
|
||||
case VEH_Train: command = CMD_START_STOP_TRAIN | CMD_MSG(STR_883B_CAN_T_STOP_START_TRAIN); break;
|
||||
case VEH_Road: command = CMD_START_STOP_ROADVEH | CMD_MSG(STR_9015_CAN_T_STOP_START_ROAD_VEHICLE); break;
|
||||
case VEH_Ship: command = CMD_START_STOP_SHIP | CMD_MSG(STR_9818_CAN_T_STOP_START_SHIP); break;
|
||||
case VEH_Aircraft: command = CMD_START_STOP_AIRCRAFT | CMD_MSG(STR_A016_CAN_T_STOP_START_AIRCRAFT); break;
|
||||
default: NOT_REACHED(); command = 0;
|
||||
}
|
||||
DoCommandP(v->tile, v->index, 0, NULL, command);
|
||||
}
|
||||
break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones a vehicle
|
||||
* @param *v is the original vehicle to clone
|
||||
* @param *w is the window of the depot where the clone is build
|
||||
*/
|
||||
static void HandleCloneVehClick(const Vehicle *v, const Window *w)
|
||||
{
|
||||
uint error_str;
|
||||
|
||||
if (v == NULL) return;
|
||||
|
||||
if (v->type == VEH_Train && !IsFrontEngine(v)) {
|
||||
v = GetFirstVehicleInChain(v);
|
||||
/* Do nothing when clicking on a train in depot with no loc attached */
|
||||
if (!IsFrontEngine(v)) return;
|
||||
}
|
||||
|
||||
switch (v->type) {
|
||||
case VEH_Train: error_str = CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE); break;
|
||||
case VEH_Road: error_str = CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE); break;
|
||||
case VEH_Ship: error_str = CMD_MSG(STR_980D_CAN_T_BUILD_SHIP); break;
|
||||
case VEH_Aircraft: error_str = CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT); break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
DoCommandP(w->window_number, v->index, _ctrl_pressed ? 1 : 0, CcCloneVehicle, CMD_CLONE_VEHICLE | error_str);
|
||||
|
||||
ResetObjectToPlace();
|
||||
}
|
||||
|
||||
static void ClonePlaceObj(const Window *w)
|
||||
{
|
||||
const Vehicle *v = CheckMouseOverVehicle();
|
||||
|
||||
if (v != NULL) HandleCloneVehClick(v, w);
|
||||
}
|
||||
|
||||
static void ResizeDepotButtons(Window *w)
|
||||
{
|
||||
/* We got the widget moved around. Now we will make some widgets to fill the gap between some widgets in equal sizes */
|
||||
|
||||
/* Make the buttons in the bottom equal in size */
|
||||
w->widget[DEPOT_WIDGET_BUILD].right = w->widget[DEPOT_WIDGET_LOCATION].right / 3;
|
||||
w->widget[DEPOT_WIDGET_LOCATION].left = w->widget[DEPOT_WIDGET_BUILD].right * 2;
|
||||
w->widget[DEPOT_WIDGET_CLONE].left = w->widget[DEPOT_WIDGET_BUILD].right + 1;
|
||||
w->widget[DEPOT_WIDGET_CLONE].right = w->widget[DEPOT_WIDGET_LOCATION].left - 1;
|
||||
|
||||
if (WP(w, depot_d).type == VEH_Train) {
|
||||
/* Divide the size of DEPOT_WIDGET_SELL into two equally big buttons so DEPOT_WIDGET_SELL and DEPOT_WIDGET_SELL_CHAIN will get the same size.
|
||||
* This way it will stay the same even if DEPOT_WIDGET_SELL_CHAIN is resized for some reason */
|
||||
w->widget[DEPOT_WIDGET_SELL_CHAIN].top = ((w->widget[DEPOT_WIDGET_SELL_CHAIN].bottom - w->widget[DEPOT_WIDGET_SELL].top) / 2) + w->widget[DEPOT_WIDGET_SELL].top;
|
||||
w->widget[DEPOT_WIDGET_SELL].bottom = w->widget[DEPOT_WIDGET_SELL_CHAIN].top - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Function to set up vehicle specific sprites and strings
|
||||
* Only use this if it's the same widget, that's used for more than one vehicle type and it needs different text/sprites
|
||||
* Vehicle specific text/sprites, that's in a widget, that's only shown for one vehicle type (like sell whole train) is set in the widget array
|
||||
*/
|
||||
static void SetupStringsForDepotWindow(Window *w, byte type)
|
||||
{
|
||||
switch (type) {
|
||||
case VEH_Train:
|
||||
w->widget[DEPOT_WIDGET_CAPTION].data = STR_8800_TRAIN_DEPOT;
|
||||
w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_DEPOT_TRAIN_TIP;
|
||||
w->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_DEPOT_TRAIN_TIP;
|
||||
w->widget[DEPOT_WIDGET_SELL].tooltips = STR_8841_DRAG_TRAIN_VEHICLE_TO_HERE;
|
||||
w->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_TRAIN_TIP;
|
||||
w->widget[DEPOT_WIDGET_MATRIX].tooltips = STR_883F_TRAINS_CLICK_ON_TRAIN_FOR;
|
||||
|
||||
w->widget[DEPOT_WIDGET_BUILD].data = STR_8815_NEW_VEHICLES;
|
||||
w->widget[DEPOT_WIDGET_BUILD].tooltips = STR_8840_BUILD_NEW_TRAIN_VEHICLE;
|
||||
w->widget[DEPOT_WIDGET_CLONE].data = STR_CLONE_TRAIN;
|
||||
w->widget[DEPOT_WIDGET_CLONE].tooltips = STR_CLONE_TRAIN_DEPOT_INFO;
|
||||
|
||||
w->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_8842_CENTER_MAIN_VIEW_ON_TRAIN;
|
||||
w->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_TRAIN;
|
||||
w->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_TRAIN_TIP;
|
||||
w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_TRAIN_TIP;
|
||||
|
||||
/* Sprites */
|
||||
w->widget[DEPOT_WIDGET_SELL].data = SPR_SELL_TRAIN;
|
||||
w->widget[DEPOT_WIDGET_SELL_ALL].data = SPR_SELL_ALL_TRAIN;
|
||||
w->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_TRAIN;
|
||||
break;
|
||||
|
||||
case VEH_Road:
|
||||
w->widget[DEPOT_WIDGET_CAPTION].data = STR_9003_ROAD_VEHICLE_DEPOT;
|
||||
w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_DEPOT_ROADVEH_TIP;
|
||||
w->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_DEPOT_ROADVEH_TIP;
|
||||
w->widget[DEPOT_WIDGET_SELL].tooltips = STR_9024_DRAG_ROAD_VEHICLE_TO_HERE;
|
||||
w->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_ROADVEH_TIP;
|
||||
w->widget[DEPOT_WIDGET_MATRIX].tooltips = STR_9022_VEHICLES_CLICK_ON_VEHICLE;
|
||||
|
||||
w->widget[DEPOT_WIDGET_BUILD].data = STR_9004_NEW_VEHICLES;
|
||||
w->widget[DEPOT_WIDGET_BUILD].tooltips = STR_9023_BUILD_NEW_ROAD_VEHICLE;
|
||||
w->widget[DEPOT_WIDGET_CLONE].data = STR_CLONE_ROAD_VEHICLE;
|
||||
w->widget[DEPOT_WIDGET_CLONE].tooltips = STR_CLONE_ROAD_VEHICLE_DEPOT_INFO;
|
||||
|
||||
w->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_9025_CENTER_MAIN_VIEW_ON_ROAD;
|
||||
w->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_LORRY;
|
||||
w->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_ROADVEH_TIP;
|
||||
w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_ROADVEH_TIP;
|
||||
|
||||
/* Sprites */
|
||||
w->widget[DEPOT_WIDGET_SELL].data = SPR_SELL_ROADVEH;
|
||||
w->widget[DEPOT_WIDGET_SELL_ALL].data = SPR_SELL_ALL_ROADVEH;
|
||||
w->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_ROADVEH;
|
||||
break;
|
||||
|
||||
case VEH_Ship:
|
||||
w->widget[DEPOT_WIDGET_CAPTION].data = STR_9803_SHIP_DEPOT;
|
||||
w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_DEPOT_SHIP_TIP;
|
||||
w->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_DEPOT_SHIP_TIP;
|
||||
w->widget[DEPOT_WIDGET_SELL].tooltips = STR_9821_DRAG_SHIP_TO_HERE_TO_SELL;
|
||||
w->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_SHIP_TIP;
|
||||
w->widget[DEPOT_WIDGET_MATRIX].tooltips = STR_981F_SHIPS_CLICK_ON_SHIP_FOR;
|
||||
|
||||
w->widget[DEPOT_WIDGET_BUILD].data = STR_9804_NEW_SHIPS;
|
||||
w->widget[DEPOT_WIDGET_BUILD].tooltips = STR_9820_BUILD_NEW_SHIP;
|
||||
w->widget[DEPOT_WIDGET_CLONE].data = STR_CLONE_SHIP;
|
||||
w->widget[DEPOT_WIDGET_CLONE].tooltips = STR_CLONE_SHIP_DEPOT_INFO;
|
||||
|
||||
w->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_9822_CENTER_MAIN_VIEW_ON_SHIP;
|
||||
w->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_SHIP;
|
||||
w->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_SHIP_TIP;
|
||||
w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_SHIP_TIP;
|
||||
|
||||
/* Sprites */
|
||||
w->widget[DEPOT_WIDGET_SELL].data = SPR_SELL_SHIP;
|
||||
w->widget[DEPOT_WIDGET_SELL_ALL].data = SPR_SELL_ALL_SHIP;
|
||||
w->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_SHIP;
|
||||
break;
|
||||
|
||||
case VEH_Aircraft:
|
||||
w->widget[DEPOT_WIDGET_CAPTION].data = STR_A002_AIRCRAFT_HANGAR;
|
||||
w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_HANGAR_TIP;
|
||||
w->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_HANGAR_TIP;
|
||||
w->widget[DEPOT_WIDGET_SELL].tooltips = STR_A023_DRAG_AIRCRAFT_TO_HERE_TO;
|
||||
w->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_AIRCRAFT_TIP;
|
||||
w->widget[DEPOT_WIDGET_MATRIX].tooltips = STR_A021_AIRCRAFT_CLICK_ON_AIRCRAFT;
|
||||
|
||||
w->widget[DEPOT_WIDGET_BUILD].data = STR_A003_NEW_AIRCRAFT;
|
||||
w->widget[DEPOT_WIDGET_BUILD].tooltips = STR_A022_BUILD_NEW_AIRCRAFT;
|
||||
w->widget[DEPOT_WIDGET_CLONE].data = STR_CLONE_AIRCRAFT;
|
||||
w->widget[DEPOT_WIDGET_CLONE].tooltips = STR_CLONE_AIRCRAFT_INFO_HANGAR_WINDOW;
|
||||
|
||||
w->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_A024_CENTER_MAIN_VIEW_ON_HANGAR;
|
||||
w->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_PLANE;
|
||||
w->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_AIRCRAFT_TIP;
|
||||
w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_AIRCRAFT_TIP;
|
||||
|
||||
/* Sprites */
|
||||
w->widget[DEPOT_WIDGET_SELL].data = SPR_SELL_AIRCRAFT;
|
||||
w->widget[DEPOT_WIDGET_SELL_ALL].data = SPR_SELL_ALL_AIRCRAFT;
|
||||
w->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_AIRCRAFT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void CreateDepotListWindow(Window *w, byte type)
|
||||
{
|
||||
WP(w, depot_d).type = type;
|
||||
_backup_orders_tile = 0;
|
||||
|
||||
/* Resize the window according to the vehicle type */
|
||||
switch (type) {
|
||||
default: NOT_REACHED();
|
||||
case VEH_Train:
|
||||
w->vscroll.cap = 6;
|
||||
w->hscroll.cap = 10 * 29;
|
||||
w->resize.step_width = 1;
|
||||
ResizeWindow(w, 56, 26);
|
||||
break;
|
||||
|
||||
case VEH_Road:
|
||||
w->vscroll.cap = 5;
|
||||
w->hscroll.cap = 5;
|
||||
w->resize.step_width = 56;
|
||||
ResizeWindow(w, 10, 0);
|
||||
break;
|
||||
|
||||
case VEH_Ship:
|
||||
w->vscroll.cap = 3;
|
||||
w->hscroll.cap = 3;
|
||||
w->resize.step_width = 90;
|
||||
ResizeWindow(w, 0, 2);
|
||||
break;
|
||||
|
||||
case VEH_Aircraft:
|
||||
w->vscroll.cap = 3;
|
||||
w->hscroll.cap = 4;
|
||||
w->resize.step_width = 74;
|
||||
ResizeWindow(w, 26, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the minimum window size to the current window size */
|
||||
w->resize.width = w->width;
|
||||
w->resize.height = w->height;
|
||||
w->resize.step_height = GetVehicleListHeight(type);
|
||||
|
||||
SetupStringsForDepotWindow(w, type);
|
||||
|
||||
w->widget[DEPOT_WIDGET_MATRIX].data =
|
||||
(w->vscroll.cap * 0x100) // number of rows to draw on the background
|
||||
+ (type == VEH_Train ? 1 : w->hscroll.cap); // number of boxes in each row. Trains always have just one
|
||||
|
||||
|
||||
SetWindowWidgetsHiddenState(w, type != VEH_Train,
|
||||
DEPOT_WIDGET_H_SCROLL,
|
||||
DEPOT_WIDGET_SELL_CHAIN,
|
||||
WIDGET_LIST_END);
|
||||
|
||||
/* The train depot has a horizontal scroller, make the matrix that much shorter to fit */
|
||||
if (type == VEH_Train) w->widget[DEPOT_WIDGET_MATRIX].bottom -= 12;
|
||||
ResizeDepotButtons(w);
|
||||
}
|
||||
|
||||
void DepotSortList(Vehicle **v, uint16 length);
|
||||
|
||||
static void DepotWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_CREATE:
|
||||
WP(w, depot_d).sel = INVALID_VEHICLE;
|
||||
WP(w, depot_d).vehicle_list = NULL;
|
||||
WP(w, depot_d).wagon_list = NULL;
|
||||
WP(w, depot_d).engine_count = 0;
|
||||
WP(w, depot_d).wagon_count = 0;
|
||||
WP(w, depot_d).generate_list = true;
|
||||
break;
|
||||
|
||||
case WE_INVALIDATE_DATA:
|
||||
WP(w, depot_d).generate_list = true;
|
||||
break;
|
||||
|
||||
case WE_PAINT:
|
||||
if (WP(w, depot_d).generate_list) {
|
||||
/* Generate the vehicle list
|
||||
* It's ok to use the wagon pointers for non-trains as they will be ignored */
|
||||
BuildDepotVehicleList(WP(w, depot_d).type, w->window_number,
|
||||
&WP(w, depot_d).vehicle_list, &WP(w, depot_d).engine_list_length, &WP(w, depot_d).engine_count,
|
||||
&WP(w, depot_d).wagon_list, &WP(w, depot_d).wagon_list_length, &WP(w, depot_d).wagon_count);
|
||||
WP(w, depot_d).generate_list = false;
|
||||
DepotSortList(WP(w, depot_d).vehicle_list, WP(w, depot_d).engine_count);
|
||||
//#ifndef NDEBUG
|
||||
#if 0
|
||||
/* We disabled this check for now, but will keep it to quickly make this test again later (if we change some code) */
|
||||
} else {
|
||||
/* Here we got a piece of code, that only checks if we got a different number of vehicles in the depot list and the number of vehicles actually being in the depot.
|
||||
* IF they aren't the same, then WE_INVALIDATE_DATA should have been called somewhere, but it wasn't and we got a bug
|
||||
* Since this is a time consuming check and not nice to memory fragmentation, it may not stay for long, but it's a good way to check this
|
||||
* We can turn it on/off by switching between #ifndef NDEBUG and #if 0 */
|
||||
Vehicle **engines = NULL, **wagons = NULL;
|
||||
uint16 engine_count = 0, engine_length = 0;
|
||||
uint16 wagon_count = 0, wagon_length = 0;
|
||||
BuildDepotVehicleList(WP(w, depot_d).type, w->window_number, &engines, &engine_length, &engine_count,
|
||||
&wagons, &wagon_length, &wagon_count);
|
||||
|
||||
assert(engine_count == WP(w, depot_d).engine_count);
|
||||
assert(wagon_count == WP(w, depot_d).wagon_count);
|
||||
free((void*)engines);
|
||||
free((void*)wagons);
|
||||
#endif
|
||||
}
|
||||
DrawDepotWindow(w);
|
||||
break;
|
||||
|
||||
case WE_CLICK:
|
||||
switch (e->we.click.widget) {
|
||||
case DEPOT_WIDGET_MATRIX: // List
|
||||
DepotClick(w, e->we.click.pt.x, e->we.click.pt.y);
|
||||
break;
|
||||
|
||||
case DEPOT_WIDGET_BUILD: // Build vehicle
|
||||
ResetObjectToPlace();
|
||||
switch (WP(w, depot_d).type) {
|
||||
case VEH_Train: ShowBuildTrainWindow(w->window_number); break;
|
||||
case VEH_Road: ShowBuildRoadVehWindow(w->window_number); break;
|
||||
case VEH_Ship: ShowBuildShipWindow(w->window_number); break;
|
||||
case VEH_Aircraft:
|
||||
ShowBuildVehicleWindow(w->window_number, WP(w, depot_d).type);
|
||||
break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
break;
|
||||
|
||||
case DEPOT_WIDGET_CLONE: // Clone button
|
||||
InvalidateWidget(w, DEPOT_WIDGET_CLONE);
|
||||
ToggleWidgetLoweredState(w, DEPOT_WIDGET_CLONE);
|
||||
|
||||
if (IsWindowWidgetLowered(w, DEPOT_WIDGET_CLONE)) {
|
||||
static const CursorID clone_icons[] = {
|
||||
SPR_CURSOR_CLONE_TRAIN, SPR_CURSOR_CLONE_ROADVEH,
|
||||
SPR_CURSOR_CLONE_SHIP, SPR_CURSOR_CLONE_AIRPLANE
|
||||
};
|
||||
|
||||
_place_clicked_vehicle = NULL;
|
||||
SetObjectToPlaceWnd(clone_icons[WP(w, depot_d).type - VEH_Train], VHM_RECT, w);
|
||||
} else {
|
||||
ResetObjectToPlace();
|
||||
}
|
||||
break;
|
||||
|
||||
case DEPOT_WIDGET_LOCATION: ScrollMainWindowToTile(w->window_number); break;
|
||||
|
||||
case DEPOT_WIDGET_STOP_ALL:
|
||||
case DEPOT_WIDGET_START_ALL:
|
||||
DoCommandP(w->window_number, 0, WP(w, depot_d).type | (e->we.click.widget == DEPOT_WIDGET_START_ALL ? (1 << 5) : 0), NULL, CMD_MASS_START_STOP);
|
||||
break;
|
||||
|
||||
case DEPOT_WIDGET_SELL_ALL:
|
||||
/* Only open the confimation window if there are anything to sell */
|
||||
if (WP(w, depot_d).engine_count != 0 || WP(w, depot_d).wagon_count != 0) {
|
||||
static const StringID confirm_captions[] = {
|
||||
STR_8800_TRAIN_DEPOT,
|
||||
STR_9003_ROAD_VEHICLE_DEPOT,
|
||||
STR_9803_SHIP_DEPOT,
|
||||
STR_A002_AIRCRAFT_HANGAR
|
||||
};
|
||||
TileIndex tile = w->window_number;
|
||||
byte vehtype = WP(w, depot_d).type;
|
||||
|
||||
SetDParam(0, (vehtype == VEH_Aircraft) ? GetStationIndex(tile) : GetDepotByTile(tile)->town_index);
|
||||
ShowQuery(
|
||||
confirm_captions[vehtype - VEH_Train],
|
||||
STR_DEPOT_SELL_CONFIRMATION_TEXT,
|
||||
w,
|
||||
DepotSellAllConfirmationCallback
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case DEPOT_WIDGET_VEHICLE_LIST:
|
||||
ShowVehDepotOrders(GetTileOwner(w->window_number), WP(w, depot_d).type, w->window_number);
|
||||
break;
|
||||
|
||||
case DEPOT_WIDGET_AUTOREPLACE:
|
||||
DoCommandP(w->window_number, WP(w, depot_d).type, 0, NULL, CMD_DEPOT_MASS_AUTOREPLACE);
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_PLACE_OBJ: {
|
||||
ClonePlaceObj(w);
|
||||
} break;
|
||||
|
||||
case WE_ABORT_PLACE_OBJ: {
|
||||
RaiseWindowWidget(w, DEPOT_WIDGET_CLONE);
|
||||
InvalidateWidget(w, DEPOT_WIDGET_CLONE);
|
||||
} break;
|
||||
|
||||
/* check if a vehicle in a depot was clicked.. */
|
||||
case WE_MOUSELOOP: {
|
||||
const Vehicle *v = _place_clicked_vehicle;
|
||||
|
||||
/* since OTTD checks all open depot windows, we will make sure that it triggers the one with a clicked clone button */
|
||||
if (v != NULL && IsWindowWidgetLowered(w, DEPOT_WIDGET_CLONE)) {
|
||||
_place_clicked_vehicle = NULL;
|
||||
HandleCloneVehClick(v, w);
|
||||
}
|
||||
} break;
|
||||
|
||||
case WE_DESTROY:
|
||||
DeleteWindowById(WC_BUILD_VEHICLE, w->window_number);
|
||||
free((void*)WP(w, depot_d).vehicle_list);
|
||||
free((void*)WP(w, depot_d).wagon_list);
|
||||
break;
|
||||
|
||||
case WE_DRAGDROP:
|
||||
switch (e->we.click.widget) {
|
||||
case DEPOT_WIDGET_MATRIX: {
|
||||
Vehicle *v;
|
||||
VehicleID sel = WP(w, depot_d).sel;
|
||||
|
||||
WP(w, depot_d).sel = INVALID_VEHICLE;
|
||||
SetWindowDirty(w);
|
||||
|
||||
if (WP(w, depot_d).type == VEH_Train) {
|
||||
GetDepotVehiclePtData gdvp;
|
||||
|
||||
if (GetVehicleFromDepotWndPt(w, e->we.dragdrop.pt.x, e->we.dragdrop.pt.y, &v, &gdvp) == MODE_DRAG_VEHICLE &&
|
||||
sel != INVALID_VEHICLE) {
|
||||
if (gdvp.wagon != NULL && gdvp.wagon->index == sel && _ctrl_pressed) {
|
||||
DoCommandP(GetVehicle(sel)->tile, GetVehicle(sel)->index, true, NULL, CMD_REVERSE_TRAIN_DIRECTION | CMD_MSG(STR_9033_CAN_T_MAKE_VEHICLE_TURN));
|
||||
} else if (gdvp.wagon == NULL || gdvp.wagon->index != sel) {
|
||||
TrainDepotMoveVehicle(gdvp.wagon, sel, gdvp.head);
|
||||
} else if (gdvp.head != NULL && IsFrontEngine(gdvp.head)) {
|
||||
ShowTrainViewWindow(gdvp.head);
|
||||
}
|
||||
}
|
||||
} else if (GetVehicleFromDepotWndPt(w, e->we.dragdrop.pt.x, e->we.dragdrop.pt.y, &v, NULL) == MODE_DRAG_VEHICLE &&
|
||||
v != NULL &&
|
||||
sel == v->index) {
|
||||
ShowVehicleViewWindow(v);
|
||||
}
|
||||
} break;
|
||||
|
||||
case DEPOT_WIDGET_SELL: case DEPOT_WIDGET_SELL_CHAIN:
|
||||
if (!IsWindowWidgetDisabled(w, DEPOT_WIDGET_SELL) &&
|
||||
WP(w, depot_d).sel != INVALID_VEHICLE) {
|
||||
Vehicle *v;
|
||||
uint command;
|
||||
int sell_cmd;
|
||||
bool is_engine;
|
||||
|
||||
if (IsWindowWidgetDisabled(w, e->we.click.widget)) return;
|
||||
if (WP(w, depot_d).sel == INVALID_VEHICLE) return;
|
||||
|
||||
HandleButtonClick(w, e->we.click.widget);
|
||||
|
||||
v = GetVehicle(WP(w, depot_d).sel);
|
||||
WP(w, depot_d).sel = INVALID_VEHICLE;
|
||||
SetWindowDirty(w);
|
||||
|
||||
sell_cmd = (v->type == VEH_Train && (e->we.click.widget == DEPOT_WIDGET_SELL_CHAIN || _ctrl_pressed)) ? 1 : 0;
|
||||
|
||||
is_engine = (!(v->type == VEH_Train && !IsFrontEngine(v)));
|
||||
|
||||
if (is_engine) {
|
||||
_backup_orders_tile = v->tile;
|
||||
BackupVehicleOrders(v, _backup_orders_data);
|
||||
}
|
||||
|
||||
switch (v->type) {
|
||||
case VEH_Train: command = CMD_SELL_RAIL_WAGON | CMD_MSG(STR_8839_CAN_T_SELL_RAILROAD_VEHICLE); break;
|
||||
case VEH_Road: command = CMD_SELL_ROAD_VEH | CMD_MSG(STR_9014_CAN_T_SELL_ROAD_VEHICLE); break;
|
||||
case VEH_Ship: command = CMD_SELL_SHIP | CMD_MSG(STR_980C_CAN_T_SELL_SHIP); break;
|
||||
case VEH_Aircraft: command = CMD_SELL_AIRCRAFT | CMD_MSG(STR_A01C_CAN_T_SELL_AIRCRAFT); break;
|
||||
default: NOT_REACHED(); command = 0;
|
||||
}
|
||||
|
||||
if (!DoCommandP(v->tile, v->index, sell_cmd, NULL, command) && is_engine) _backup_orders_tile = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WP(w, depot_d).sel = INVALID_VEHICLE;
|
||||
SetWindowDirty(w);
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_RESIZE:
|
||||
w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
|
||||
w->hscroll.cap += e->we.sizing.diff.x / (int)w->resize.step_width;
|
||||
w->widget[DEPOT_WIDGET_MATRIX].data = (w->vscroll.cap << 8) + (WP(w, depot_d).type == VEH_Train ? 1 : w->hscroll.cap);
|
||||
ResizeDepotButtons(w);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Opens a depot window
|
||||
* @param tile The tile where the depot/hangar is located
|
||||
* @param type The type of vehicles in the depot
|
||||
*/
|
||||
void ShowDepotWindow(TileIndex tile, byte type)
|
||||
{
|
||||
Window *w;
|
||||
|
||||
switch (type) {
|
||||
default: NOT_REACHED();
|
||||
case VEH_Train:
|
||||
w = AllocateWindowDescFront(&_train_depot_desc, tile); break;
|
||||
case VEH_Road:
|
||||
w = AllocateWindowDescFront(&_road_depot_desc, tile); break;
|
||||
case VEH_Ship:
|
||||
w = AllocateWindowDescFront(&_ship_depot_desc, tile); break;
|
||||
case VEH_Aircraft:
|
||||
w = AllocateWindowDescFront(&_aircraft_depot_desc, tile); break;
|
||||
}
|
||||
|
||||
if (w != NULL) {
|
||||
w->caption_color = GetTileOwner(tile);
|
||||
CreateDepotListWindow(w, type);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes the highlight of a vehicle in a depot window
|
||||
* @param *v Vehicle to remove all highlights from
|
||||
*/
|
||||
void DeleteDepotHighlightOfVehicle(const Vehicle *v)
|
||||
{
|
||||
Window *w;
|
||||
|
||||
/* If we haven't got any vehicles on the mouse pointer, we haven't got any highlighted in any depots either
|
||||
* If that is the case, we can skip looping though the windows and save time */
|
||||
if (_special_mouse_mode != WSM_DRAGDROP) return;
|
||||
|
||||
w = FindWindowById(WC_VEHICLE_DEPOT, v->tile);
|
||||
if (w != NULL) {
|
||||
WP(w, depot_d).sel = INVALID_VEHICLE;
|
||||
ResetObjectToPlace();
|
||||
}
|
||||
}
|
||||
147
src/direction.h
Normal file
147
src/direction.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef DIRECTION_H
|
||||
#define DIRECTION_H
|
||||
|
||||
/* Direction as commonly used in v->direction, 8 way. */
|
||||
typedef enum Direction {
|
||||
DIR_N = 0,
|
||||
DIR_NE = 1, /* Northeast, upper right on your monitor */
|
||||
DIR_E = 2,
|
||||
DIR_SE = 3,
|
||||
DIR_S = 4,
|
||||
DIR_SW = 5,
|
||||
DIR_W = 6,
|
||||
DIR_NW = 7,
|
||||
DIR_END,
|
||||
INVALID_DIR = 0xFF,
|
||||
} Direction;
|
||||
|
||||
static inline Direction ReverseDir(Direction d)
|
||||
{
|
||||
return (Direction)(4 ^ d);
|
||||
}
|
||||
|
||||
|
||||
typedef enum DirDiff {
|
||||
DIRDIFF_SAME = 0,
|
||||
DIRDIFF_45RIGHT = 1,
|
||||
DIRDIFF_90RIGHT = 2,
|
||||
DIRDIFF_REVERSE = 4,
|
||||
DIRDIFF_90LEFT = 6,
|
||||
DIRDIFF_45LEFT = 7
|
||||
} DirDiff;
|
||||
|
||||
static inline DirDiff DirDifference(Direction d0, Direction d1)
|
||||
{
|
||||
return (DirDiff)((d0 + 8 - d1) % 8);
|
||||
}
|
||||
|
||||
static inline DirDiff ChangeDirDiff(DirDiff d, DirDiff delta)
|
||||
{
|
||||
return (DirDiff)((d + delta) % 8);
|
||||
}
|
||||
|
||||
|
||||
static inline Direction ChangeDir(Direction d, DirDiff delta)
|
||||
{
|
||||
return (Direction)((d + delta) % 8);
|
||||
}
|
||||
|
||||
|
||||
/* Direction commonly used as the direction of entering and leaving tiles, 4-way */
|
||||
typedef enum DiagDirection {
|
||||
DIAGDIR_NE = 0, /* Northeast, upper right on your monitor */
|
||||
DIAGDIR_SE = 1,
|
||||
DIAGDIR_SW = 2,
|
||||
DIAGDIR_NW = 3,
|
||||
DIAGDIR_END,
|
||||
INVALID_DIAGDIR = 0xFF,
|
||||
} DiagDirection;
|
||||
|
||||
static inline DiagDirection ReverseDiagDir(DiagDirection d)
|
||||
{
|
||||
return (DiagDirection)(2 ^ d);
|
||||
}
|
||||
|
||||
|
||||
typedef enum DiagDirDiff {
|
||||
DIAGDIRDIFF_SAME = 0,
|
||||
DIAGDIRDIFF_90RIGHT = 1,
|
||||
DIAGDIRDIFF_REVERSE = 2,
|
||||
DIAGDIRDIFF_90LEFT = 3
|
||||
} DiagDirDiff;
|
||||
|
||||
static inline DiagDirection ChangeDiagDir(DiagDirection d, DiagDirDiff delta)
|
||||
{
|
||||
return (DiagDirection)((d + delta) % 4);
|
||||
}
|
||||
|
||||
|
||||
static inline DiagDirection DirToDiagDir(Direction dir)
|
||||
{
|
||||
return (DiagDirection)(dir >> 1);
|
||||
}
|
||||
|
||||
|
||||
static inline Direction DiagDirToDir(DiagDirection dir)
|
||||
{
|
||||
return (Direction)(dir * 2 + 1);
|
||||
}
|
||||
|
||||
|
||||
/* the 2 axis */
|
||||
typedef enum Axis {
|
||||
AXIS_X = 0,
|
||||
AXIS_Y = 1,
|
||||
AXIS_END
|
||||
} Axis;
|
||||
|
||||
|
||||
static inline Axis OtherAxis(Axis a)
|
||||
{
|
||||
return (Axis)(a ^ 1);
|
||||
}
|
||||
|
||||
|
||||
static inline Axis DiagDirToAxis(DiagDirection d)
|
||||
{
|
||||
return (Axis)(d & 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Converts an Axis to a DiagDirection
|
||||
* Points always in the positive direction, i.e. S[EW]
|
||||
*/
|
||||
static inline DiagDirection AxisToDiagDir(Axis a)
|
||||
{
|
||||
return (DiagDirection)(2 - a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an axis and a flag for north/south into a DiagDirection
|
||||
* @param ns north -> 0, south -> 1
|
||||
*/
|
||||
static inline DiagDirection XYNSToDiagDir(Axis xy, uint ns)
|
||||
{
|
||||
return (DiagDirection)(xy * 3 ^ ns * 2);
|
||||
}
|
||||
|
||||
|
||||
static inline bool IsValidDiagDirection(DiagDirection d)
|
||||
{
|
||||
return d < DIAGDIR_END;
|
||||
}
|
||||
|
||||
static inline bool IsValidDirection(Direction d)
|
||||
{
|
||||
return d < DIR_END;
|
||||
}
|
||||
|
||||
static inline bool IsValidAxis(Axis d)
|
||||
{
|
||||
return d < AXIS_END;
|
||||
}
|
||||
|
||||
#endif /* DIRECTION_H */
|
||||
1001
src/disaster_cmd.c
Normal file
1001
src/disaster_cmd.c
Normal file
File diff suppressed because it is too large
Load Diff
378
src/dock_gui.c
Normal file
378
src/dock_gui.c
Normal file
@@ -0,0 +1,378 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "table/sprites.h"
|
||||
#include "table/strings.h"
|
||||
#include "functions.h"
|
||||
#include "map.h"
|
||||
#include "window.h"
|
||||
#include "station.h"
|
||||
#include "gui.h"
|
||||
#include "viewport.h"
|
||||
#include "gfx.h"
|
||||
#include "sound.h"
|
||||
#include "command.h"
|
||||
#include "variables.h"
|
||||
|
||||
static void ShowBuildDockStationPicker(void);
|
||||
static void ShowBuildDocksDepotPicker(void);
|
||||
|
||||
static Axis _ship_depot_direction;
|
||||
|
||||
void CcBuildDocks(bool success, TileIndex tile, uint32 p1, uint32 p2)
|
||||
{
|
||||
if (success) {
|
||||
SndPlayTileFx(SND_02_SPLAT, tile);
|
||||
ResetObjectToPlace();
|
||||
}
|
||||
}
|
||||
|
||||
void CcBuildCanal(bool success, TileIndex tile, uint32 p1, uint32 p2)
|
||||
{
|
||||
if (success) SndPlayTileFx(SND_02_SPLAT, tile);
|
||||
}
|
||||
|
||||
|
||||
static void PlaceDocks_Dock(TileIndex tile)
|
||||
{
|
||||
DoCommandP(tile, 0, 0, CcBuildDocks, CMD_BUILD_DOCK | CMD_AUTO | CMD_MSG(STR_9802_CAN_T_BUILD_DOCK_HERE));
|
||||
}
|
||||
|
||||
static void PlaceDocks_Depot(TileIndex tile)
|
||||
{
|
||||
DoCommandP(tile, _ship_depot_direction, 0, CcBuildDocks, CMD_BUILD_SHIP_DEPOT | CMD_AUTO | CMD_MSG(STR_3802_CAN_T_BUILD_SHIP_DEPOT));
|
||||
}
|
||||
|
||||
static void PlaceDocks_Buoy(TileIndex tile)
|
||||
{
|
||||
DoCommandP(tile, 0, 0, CcBuildDocks, CMD_BUILD_BUOY | CMD_AUTO | CMD_MSG(STR_9835_CAN_T_POSITION_BUOY_HERE));
|
||||
}
|
||||
|
||||
static void PlaceDocks_DemolishArea(TileIndex tile)
|
||||
{
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y | GUI_PlaceProc_DemolishArea);
|
||||
}
|
||||
|
||||
static void PlaceDocks_BuildCanal(TileIndex tile)
|
||||
{
|
||||
VpStartPlaceSizing(tile, VPM_X_OR_Y);
|
||||
}
|
||||
|
||||
static void PlaceDocks_BuildLock(TileIndex tile)
|
||||
{
|
||||
DoCommandP(tile, 0, 0, CcBuildDocks, CMD_BUILD_LOCK | CMD_AUTO | CMD_MSG(STR_CANT_BUILD_LOCKS));
|
||||
}
|
||||
|
||||
|
||||
enum {
|
||||
DTW_CANAL = 3,
|
||||
DTW_LOCK = 4,
|
||||
DTW_DEMOLISH = 6,
|
||||
DTW_DEPOT = 7,
|
||||
DTW_STATION = 8,
|
||||
DTW_BUOY = 9
|
||||
};
|
||||
|
||||
|
||||
static void BuildDocksClick_Canal(Window *w)
|
||||
{
|
||||
HandlePlacePushButton(w, DTW_CANAL, SPR_CURSOR_CANAL, 1, PlaceDocks_BuildCanal);
|
||||
}
|
||||
|
||||
static void BuildDocksClick_Lock(Window *w)
|
||||
{
|
||||
HandlePlacePushButton(w, DTW_LOCK, SPR_CURSOR_LOCK, 1, PlaceDocks_BuildLock);
|
||||
}
|
||||
|
||||
static void BuildDocksClick_Demolish(Window *w)
|
||||
{
|
||||
HandlePlacePushButton(w, DTW_DEMOLISH, ANIMCURSOR_DEMOLISH, 1, PlaceDocks_DemolishArea);
|
||||
}
|
||||
|
||||
static void BuildDocksClick_Depot(Window *w)
|
||||
{
|
||||
if (HandlePlacePushButton(w, DTW_DEPOT, SPR_CURSOR_SHIP_DEPOT, 1, PlaceDocks_Depot)) ShowBuildDocksDepotPicker();
|
||||
}
|
||||
|
||||
static void BuildDocksClick_Dock(Window *w)
|
||||
{
|
||||
if (HandlePlacePushButton(w, DTW_STATION, SPR_CURSOR_DOCK, 3, PlaceDocks_Dock)) ShowBuildDockStationPicker();
|
||||
}
|
||||
|
||||
static void BuildDocksClick_Buoy(Window *w)
|
||||
{
|
||||
HandlePlacePushButton(w, DTW_BUOY, SPR_CURSOR_BOUY, 1, PlaceDocks_Buoy);
|
||||
}
|
||||
|
||||
static void BuildDocksClick_Landscaping(Window *w)
|
||||
{
|
||||
ShowTerraformToolbar();
|
||||
}
|
||||
|
||||
typedef void OnButtonClick(Window *w);
|
||||
static OnButtonClick * const _build_docks_button_proc[] = {
|
||||
BuildDocksClick_Canal,
|
||||
BuildDocksClick_Lock,
|
||||
NULL,
|
||||
BuildDocksClick_Demolish,
|
||||
BuildDocksClick_Depot,
|
||||
BuildDocksClick_Dock,
|
||||
BuildDocksClick_Buoy,
|
||||
BuildDocksClick_Landscaping,
|
||||
};
|
||||
|
||||
static void BuildDocksToolbWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_PAINT:
|
||||
DrawWindowWidgets(w);
|
||||
break;
|
||||
|
||||
case WE_CLICK:
|
||||
if (e->we.click.widget - 3 >= 0 && e->we.click.widget != 5) _build_docks_button_proc[e->we.click.widget - 3](w);
|
||||
break;
|
||||
|
||||
case WE_KEYPRESS:
|
||||
switch (e->we.keypress.keycode) {
|
||||
case '1': BuildDocksClick_Canal(w); break;
|
||||
case '2': BuildDocksClick_Lock(w); break;
|
||||
case '3': BuildDocksClick_Demolish(w); break;
|
||||
case '4': BuildDocksClick_Depot(w); break;
|
||||
case '5': BuildDocksClick_Dock(w); break;
|
||||
case '6': BuildDocksClick_Buoy(w); break;
|
||||
case 'l': BuildDocksClick_Landscaping(w); break;
|
||||
default: return;
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_PLACE_OBJ:
|
||||
_place_proc(e->we.place.tile);
|
||||
break;
|
||||
|
||||
case WE_PLACE_DRAG: {
|
||||
VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.userdata);
|
||||
return;
|
||||
}
|
||||
|
||||
case WE_PLACE_MOUSEUP:
|
||||
if (e->we.place.pt.x != -1) {
|
||||
if ((e->we.place.userdata & 0xF) == VPM_X_AND_Y) { // dragged actions
|
||||
GUIPlaceProcDragXY(e);
|
||||
} else if (e->we.place.userdata == VPM_X_OR_Y) {
|
||||
DoCommandP(e->we.place.tile, e->we.place.starttile, _ctrl_pressed, CcBuildCanal, CMD_BUILD_CANAL | CMD_AUTO | CMD_MSG(STR_CANT_BUILD_CANALS));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_ABORT_PLACE_OBJ:
|
||||
RaiseWindowButtons(w);
|
||||
|
||||
w = FindWindowById(WC_BUILD_STATION, 0);
|
||||
if (w != NULL) WP(w,def_d).close = true;
|
||||
|
||||
w = FindWindowById(WC_BUILD_DEPOT, 0);
|
||||
if (w != NULL) WP(w,def_d).close = true;
|
||||
break;
|
||||
|
||||
case WE_PLACE_PRESIZE: {
|
||||
TileIndex tile_from;
|
||||
TileIndex tile_to;
|
||||
|
||||
tile_from = tile_to = e->we.place.tile;
|
||||
switch (GetTileSlope(tile_from, NULL)) {
|
||||
case SLOPE_SW: tile_to += TileDiffXY(-1, 0); break;
|
||||
case SLOPE_SE: tile_to += TileDiffXY( 0, -1); break;
|
||||
case SLOPE_NW: tile_to += TileDiffXY( 0, 1); break;
|
||||
case SLOPE_NE: tile_to += TileDiffXY( 1, 0); break;
|
||||
default: break;
|
||||
}
|
||||
VpSetPresizeRange(tile_from, tile_to);
|
||||
} break;
|
||||
|
||||
case WE_DESTROY:
|
||||
if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const Widget _build_docks_toolb_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 145, 0, 13, STR_9801_DOCK_CONSTRUCTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_STICKYBOX, RESIZE_NONE, 7, 146, 157, 0, 13, 0x0, STR_STICKY_BUTTON},
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 7, 0, 21, 14, 35, SPR_IMG_BUILD_CANAL, STR_BUILD_CANALS_TIP},
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 7, 22, 43, 14, 35, SPR_IMG_BUILD_LOCK, STR_BUILD_LOCKS_TIP},
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 44, 47, 14, 35, 0x0, STR_NULL},
|
||||
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 7, 48, 69, 14, 35, SPR_IMG_DYNAMITE, STR_018D_DEMOLISH_BUILDINGS_ETC},
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 7, 70, 91, 14, 35, SPR_IMG_SHIP_DEPOT, STR_981E_BUILD_SHIP_DEPOT_FOR_BUILDING},
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 7, 92, 113, 14, 35, SPR_IMG_SHIP_DOCK, STR_981D_BUILD_SHIP_DOCK},
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 7, 114, 135, 14, 35, SPR_IMG_BOUY, STR_9834_POSITION_BUOY_WHICH_CAN},
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 7, 136, 157, 14, 35, SPR_IMG_LANDSCAPING, STR_LANDSCAPING_TOOLBAR_TIP},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const WindowDesc _build_docks_toolbar_desc = {
|
||||
WDP_ALIGN_TBR, 22, 158, 36,
|
||||
WC_BUILD_TOOLBAR, 0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
|
||||
_build_docks_toolb_widgets,
|
||||
BuildDocksToolbWndProc
|
||||
};
|
||||
|
||||
void ShowBuildDocksToolbar(void)
|
||||
{
|
||||
if (!IsValidPlayer(_current_player)) return;
|
||||
|
||||
DeleteWindowById(WC_BUILD_TOOLBAR, 0);
|
||||
AllocateWindowDesc(&_build_docks_toolbar_desc);
|
||||
if (_patches.link_terraform_toolbar) ShowTerraformToolbar();
|
||||
}
|
||||
|
||||
static void BuildDockStationWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_CREATE: LowerWindowWidget(w, _station_show_coverage + 3); break;
|
||||
|
||||
case WE_PAINT: {
|
||||
int rad;
|
||||
|
||||
if (WP(w,def_d).close) return;
|
||||
DrawWindowWidgets(w);
|
||||
|
||||
rad = (_patches.modified_catchment) ? CA_DOCK : 4;
|
||||
|
||||
if (_station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
|
||||
|
||||
DrawStationCoverageAreaText(4, 50, (uint)-1, rad);
|
||||
break;
|
||||
}
|
||||
|
||||
case WE_CLICK:
|
||||
switch (e->we.click.widget) {
|
||||
case 3:
|
||||
case 4:
|
||||
RaiseWindowWidget(w, _station_show_coverage + 3);
|
||||
_station_show_coverage = e->we.click.widget - 3;
|
||||
LowerWindowWidget(w, _station_show_coverage + 3);
|
||||
SndPlayFx(SND_15_BEEP);
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_MOUSELOOP:
|
||||
if (WP(w,def_d).close) {
|
||||
DeleteWindow(w);
|
||||
return;
|
||||
}
|
||||
|
||||
CheckRedrawStationCoverage(w);
|
||||
break;
|
||||
|
||||
case WE_DESTROY:
|
||||
if (!WP(w,def_d).close) ResetObjectToPlace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const Widget _build_dock_station_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 147, 0, 13, STR_3068_DOCK, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 14, 74, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 14, 73, 30, 40, STR_02DB_OFF, STR_3065_DON_T_HIGHLIGHT_COVERAGE},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 74, 133, 30, 40, STR_02DA_ON, STR_3064_HIGHLIGHT_COVERAGE_AREA},
|
||||
{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 17, 30, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const WindowDesc _build_dock_station_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 148, 75,
|
||||
WC_BUILD_STATION, WC_BUILD_TOOLBAR,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_dock_station_widgets,
|
||||
BuildDockStationWndProc
|
||||
};
|
||||
|
||||
static void ShowBuildDockStationPicker(void)
|
||||
{
|
||||
AllocateWindowDesc(&_build_dock_station_desc);
|
||||
}
|
||||
|
||||
static void UpdateDocksDirection(void)
|
||||
{
|
||||
if (_ship_depot_direction != AXIS_X) {
|
||||
SetTileSelectSize(1, 2);
|
||||
} else {
|
||||
SetTileSelectSize(2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void BuildDocksDepotWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_CREATE: LowerWindowWidget(w, _ship_depot_direction + 3); break;
|
||||
|
||||
case WE_PAINT:
|
||||
DrawWindowWidgets(w);
|
||||
|
||||
DrawShipDepotSprite(67, 35, 0);
|
||||
DrawShipDepotSprite(35, 51, 1);
|
||||
DrawShipDepotSprite(135, 35, 2);
|
||||
DrawShipDepotSprite(167, 51, 3);
|
||||
return;
|
||||
|
||||
case WE_CLICK: {
|
||||
switch (e->we.click.widget) {
|
||||
case 3:
|
||||
case 4:
|
||||
RaiseWindowWidget(w, _ship_depot_direction + 3);
|
||||
_ship_depot_direction = e->we.click.widget - 3;
|
||||
LowerWindowWidget(w, _ship_depot_direction + 3);
|
||||
SndPlayFx(SND_15_BEEP);
|
||||
UpdateDocksDirection();
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case WE_MOUSELOOP:
|
||||
if (WP(w,def_d).close) DeleteWindow(w);
|
||||
break;
|
||||
|
||||
case WE_DESTROY:
|
||||
if (!WP(w,def_d).close) ResetObjectToPlace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const Widget _build_docks_depot_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 203, 0, 13, STR_3800_SHIP_DEPOT_ORIENTATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 203, 14, 85, 0x0, STR_NULL},
|
||||
{ WWT_PANEL, RESIZE_NONE, 14, 3, 100, 17, 82, 0x0, STR_3803_SELECT_SHIP_DEPOT_ORIENTATION},
|
||||
{ WWT_PANEL, RESIZE_NONE, 14, 103, 200, 17, 82, 0x0, STR_3803_SELECT_SHIP_DEPOT_ORIENTATION},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const WindowDesc _build_docks_depot_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 204, 86,
|
||||
WC_BUILD_DEPOT, WC_BUILD_TOOLBAR,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_docks_depot_widgets,
|
||||
BuildDocksDepotWndProc
|
||||
};
|
||||
|
||||
|
||||
static void ShowBuildDocksDepotPicker(void)
|
||||
{
|
||||
AllocateWindowDesc(&_build_docks_depot_desc);
|
||||
UpdateDocksDirection();
|
||||
}
|
||||
|
||||
|
||||
void InitializeDockGui(void)
|
||||
{
|
||||
_ship_depot_direction = AXIS_X;
|
||||
}
|
||||
221
src/driver.c
Normal file
221
src/driver.c
Normal file
@@ -0,0 +1,221 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "debug.h"
|
||||
#include "driver.h"
|
||||
#include "functions.h"
|
||||
#include "hal.h"
|
||||
#include "string.h"
|
||||
|
||||
#include "music/bemidi.h"
|
||||
#include "music/dmusic.h"
|
||||
#include "music/extmidi.h"
|
||||
#include "music/null_m.h"
|
||||
#include "music/os2_m.h"
|
||||
#include "music/win32_m.h"
|
||||
#include "music/qtmidi.h"
|
||||
|
||||
#include "sound/null_s.h"
|
||||
#include "sound/sdl_s.h"
|
||||
#include "sound/cocoa_s.h"
|
||||
#include "sound/win32_s.h"
|
||||
|
||||
#include "video/dedicated_v.h"
|
||||
#include "video/null_v.h"
|
||||
#include "video/sdl_v.h"
|
||||
#include "video/cocoa_v.h"
|
||||
#include "video/win32_v.h"
|
||||
|
||||
typedef struct DriverDesc {
|
||||
const char* name;
|
||||
const char* longname;
|
||||
const HalCommonDriver* drv;
|
||||
} DriverDesc;
|
||||
|
||||
typedef struct DriverClass {
|
||||
const DriverDesc *descs;
|
||||
const char *name;
|
||||
const HalCommonDriver** drv;
|
||||
} DriverClass;
|
||||
|
||||
|
||||
#define M(x, y, z) { x, y, (const HalCommonDriver *)(void *)z }
|
||||
static const DriverDesc _music_driver_descs[] = {
|
||||
#ifdef __BEOS__
|
||||
M("bemidi", "BeOS MIDI Driver", &_bemidi_music_driver),
|
||||
#endif
|
||||
#if defined(__OS2__) && !defined(__INNOTEK_LIBC__)
|
||||
M("os2", "OS/2 Music Driver", &_os2_music_driver),
|
||||
#endif
|
||||
#ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT
|
||||
M("dmusic", "DirectMusic MIDI Driver", &_dmusic_midi_driver),
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
M("win32", "Win32 MIDI Driver", &_win32_music_driver),
|
||||
#endif
|
||||
#if defined(__APPLE__) && !defined(DEDICATED)
|
||||
M("qt", "QuickTime MIDI Driver", &_qtime_music_driver),
|
||||
#endif
|
||||
#ifdef UNIX
|
||||
#if !defined(__MORPHOS__) && !defined(__AMIGA__)
|
||||
M("extmidi", "External MIDI Driver", &_extmidi_music_driver),
|
||||
#endif
|
||||
#endif
|
||||
M("null", "Null Music Driver", &_null_music_driver),
|
||||
M(NULL, NULL, NULL)
|
||||
};
|
||||
|
||||
static const DriverDesc _sound_driver_descs[] = {
|
||||
#ifdef WIN32
|
||||
M("win32", "Win32 WaveOut Driver", &_win32_sound_driver),
|
||||
#endif
|
||||
#ifdef WITH_SDL
|
||||
M("sdl", "SDL Sound Driver", &_sdl_sound_driver),
|
||||
#endif
|
||||
#ifdef WITH_COCOA
|
||||
M("cocoa", "Cocoa Sound Driver", &_cocoa_sound_driver),
|
||||
#endif
|
||||
M("null", "Null Sound Driver", &_null_sound_driver),
|
||||
M(NULL, NULL, NULL)
|
||||
};
|
||||
|
||||
static const DriverDesc _video_driver_descs[] = {
|
||||
#ifdef WIN32
|
||||
M("win32", "Win32 GDI Video Driver", &_win32_video_driver),
|
||||
#endif
|
||||
#ifdef WITH_SDL
|
||||
M("sdl", "SDL Video Driver", &_sdl_video_driver),
|
||||
#endif
|
||||
#ifdef WITH_COCOA
|
||||
M("cocoa", "Cocoa Video Driver", &_cocoa_video_driver),
|
||||
#endif
|
||||
M("null", "Null Video Driver", &_null_video_driver),
|
||||
#ifdef ENABLE_NETWORK
|
||||
M("dedicated", "Dedicated Video Driver", &_dedicated_video_driver),
|
||||
#endif
|
||||
M(NULL, NULL, NULL)
|
||||
};
|
||||
#undef M
|
||||
|
||||
|
||||
#define M(x, y, z) { x, y, (const HalCommonDriver **)(void *)z }
|
||||
static const DriverClass _driver_classes[] = {
|
||||
M(_video_driver_descs, "video", &_video_driver),
|
||||
M(_sound_driver_descs, "sound", &_sound_driver),
|
||||
M(_music_driver_descs, "music", &_music_driver)
|
||||
};
|
||||
#undef M
|
||||
|
||||
static const DriverDesc* GetDriverByName(const DriverDesc* dd, const char* name)
|
||||
{
|
||||
for (; dd->name != NULL; dd++) {
|
||||
if (strcmp(dd->name, name) == 0) return dd;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void LoadDriver(int driver, const char *name)
|
||||
{
|
||||
const DriverClass *dc = &_driver_classes[driver];
|
||||
const DriverDesc *dd;
|
||||
const char *err;
|
||||
|
||||
if (*name == '\0') {
|
||||
for (dd = dc->descs; dd->name != NULL; dd++) {
|
||||
err = dd->drv->start(NULL);
|
||||
if (err == NULL) break;
|
||||
DEBUG(driver, 1, "Probing %s driver '%s' failed with error: %s",
|
||||
dc->name, dd->name, err
|
||||
);
|
||||
}
|
||||
if (dd->name == NULL) error("Couldn't find any suitable %s driver", dc->name);
|
||||
|
||||
DEBUG(driver, 1, "Successfully probed %s driver '%s'", dc->name, dd->name);
|
||||
|
||||
*dc->drv = dd->drv;
|
||||
} else {
|
||||
char* parm;
|
||||
char buffer[256];
|
||||
const char* parms[32];
|
||||
|
||||
// Extract the driver name and put parameter list in parm
|
||||
ttd_strlcpy(buffer, name, sizeof(buffer));
|
||||
parm = strchr(buffer, ':');
|
||||
parms[0] = NULL;
|
||||
if (parm != NULL) {
|
||||
uint np = 0;
|
||||
// Tokenize the parm.
|
||||
do {
|
||||
*parm++ = '\0';
|
||||
if (np < lengthof(parms) - 1)
|
||||
parms[np++] = parm;
|
||||
while (*parm != '\0' && *parm != ',')
|
||||
parm++;
|
||||
} while (*parm == ',');
|
||||
parms[np] = NULL;
|
||||
}
|
||||
dd = GetDriverByName(dc->descs, buffer);
|
||||
if (dd == NULL)
|
||||
error("No such %s driver: %s\n", dc->name, buffer);
|
||||
|
||||
if (*dc->drv != NULL) (*dc->drv)->stop();
|
||||
*dc->drv = NULL;
|
||||
|
||||
err = dd->drv->start(parms);
|
||||
if (err != NULL) {
|
||||
error("Unable to load driver %s(%s). The error was: %s\n",
|
||||
dd->name, dd->longname, err
|
||||
);
|
||||
}
|
||||
*dc->drv = dd->drv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char* GetDriverParam(const char* const* parm, const char* name)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (parm == NULL) return NULL;
|
||||
|
||||
len = strlen(name);
|
||||
for (; *parm != NULL; parm++) {
|
||||
const char* p = *parm;
|
||||
|
||||
if (strncmp(p, name, len) == 0) {
|
||||
if (p[len] == '=') return p + len + 1;
|
||||
if (p[len] == '\0') return p + len;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool GetDriverParamBool(const char* const* parm, const char* name)
|
||||
{
|
||||
return GetDriverParam(parm, name) != NULL;
|
||||
}
|
||||
|
||||
int GetDriverParamInt(const char* const* parm, const char* name, int def)
|
||||
{
|
||||
const char* p = GetDriverParam(parm, name);
|
||||
return p != NULL ? atoi(p) : def;
|
||||
}
|
||||
|
||||
|
||||
char *GetDriverList(char* p, const char *last)
|
||||
{
|
||||
const DriverClass* dc;
|
||||
|
||||
for (dc = _driver_classes; dc != endof(_driver_classes); dc++) {
|
||||
const DriverDesc* dd;
|
||||
|
||||
p += snprintf(p, last - p, "List of %s drivers:\n", dc->name);
|
||||
for (dd = dc->descs; dd->name != NULL; dd++) {
|
||||
p += snprintf(p, last - p, "%10s: %s\n", dd->name, dd->longname);
|
||||
}
|
||||
p = strecpy(p, "\n", last);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
13
src/driver.h
Normal file
13
src/driver.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef DRIVER_H
|
||||
#define DRIVER_H
|
||||
|
||||
void LoadDriver(int driver, const char *name);
|
||||
|
||||
bool GetDriverParamBool(const char* const* parm, const char* name);
|
||||
int GetDriverParamInt(const char* const* parm, const char* name, int def);
|
||||
|
||||
char *GetDriverList(char *p, const char *last);
|
||||
|
||||
#endif /* DRIVER_H */
|
||||
83
src/dummy_land.c
Normal file
83
src/dummy_land.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "table/strings.h"
|
||||
#include "functions.h"
|
||||
#include "viewport.h"
|
||||
#include "command.h"
|
||||
#include "table/sprites.h"
|
||||
|
||||
static void DrawTile_Dummy(TileInfo *ti)
|
||||
{
|
||||
DrawGroundSpriteAt(SPR_SHADOW_CELL, ti->x, ti->y, ti->z);
|
||||
}
|
||||
|
||||
|
||||
static uint GetSlopeZ_Dummy(TileIndex tile, uint x, uint y)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Slope GetSlopeTileh_Dummy(TileIndex tile, Slope tileh)
|
||||
{
|
||||
return SLOPE_FLAT;
|
||||
}
|
||||
|
||||
static int32 ClearTile_Dummy(TileIndex tile, byte flags)
|
||||
{
|
||||
return_cmd_error(STR_0001_OFF_EDGE_OF_MAP);
|
||||
}
|
||||
|
||||
|
||||
static void GetAcceptedCargo_Dummy(TileIndex tile, AcceptedCargo ac)
|
||||
{
|
||||
/* not used */
|
||||
}
|
||||
|
||||
static void GetTileDesc_Dummy(TileIndex tile, TileDesc *td)
|
||||
{
|
||||
td->str = STR_EMPTY;
|
||||
td->owner = OWNER_NONE;
|
||||
}
|
||||
|
||||
static void AnimateTile_Dummy(TileIndex tile)
|
||||
{
|
||||
/* not used */
|
||||
}
|
||||
|
||||
static void TileLoop_Dummy(TileIndex tile)
|
||||
{
|
||||
/* not used */
|
||||
}
|
||||
|
||||
static void ClickTile_Dummy(TileIndex tile)
|
||||
{
|
||||
/* not used */
|
||||
}
|
||||
|
||||
static void ChangeTileOwner_Dummy(TileIndex tile, PlayerID old_player, PlayerID new_player)
|
||||
{
|
||||
/* not used */
|
||||
}
|
||||
|
||||
static uint32 GetTileTrackStatus_Dummy(TileIndex tile, TransportType mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const TileTypeProcs _tile_type_dummy_procs = {
|
||||
DrawTile_Dummy, /* draw_tile_proc */
|
||||
GetSlopeZ_Dummy, /* get_slope_z_proc */
|
||||
ClearTile_Dummy, /* clear_tile_proc */
|
||||
GetAcceptedCargo_Dummy, /* get_accepted_cargo_proc */
|
||||
GetTileDesc_Dummy, /* get_tile_desc_proc */
|
||||
GetTileTrackStatus_Dummy, /* get_tile_track_status_proc */
|
||||
ClickTile_Dummy, /* click_tile_proc */
|
||||
AnimateTile_Dummy, /* animate_tile_proc */
|
||||
TileLoop_Dummy, /* tile_loop_clear */
|
||||
ChangeTileOwner_Dummy, /* change_tile_owner_clear */
|
||||
NULL, /* get_produced_cargo_proc */
|
||||
NULL, /* vehicle_enter_tile_proc */
|
||||
GetSlopeTileh_Dummy, /* get_slope_tileh_proc */
|
||||
};
|
||||
1738
src/economy.c
Normal file
1738
src/economy.c
Normal file
File diff suppressed because it is too large
Load Diff
70
src/economy.h
Normal file
70
src/economy.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef ECONOMY_H
|
||||
#define ECONOMY_H
|
||||
|
||||
void ResetPriceBaseMultipliers(void);
|
||||
void SetPriceBaseMultiplier(uint price, byte factor);
|
||||
|
||||
typedef struct {
|
||||
// Maximum possible loan
|
||||
int32 max_loan;
|
||||
int32 max_loan_unround;
|
||||
// Economy fluctuation status
|
||||
int fluct;
|
||||
// Interest
|
||||
byte interest_rate;
|
||||
byte infl_amount;
|
||||
byte infl_amount_pr;
|
||||
} Economy;
|
||||
|
||||
VARDEF Economy _economy;
|
||||
|
||||
typedef struct Subsidy {
|
||||
CargoID cargo_type;
|
||||
byte age;
|
||||
/* from and to can either be TownID, StationID or IndustryID */
|
||||
uint16 from;
|
||||
uint16 to;
|
||||
} Subsidy;
|
||||
|
||||
|
||||
enum {
|
||||
SCORE_VEHICLES = 0,
|
||||
SCORE_STATIONS = 1,
|
||||
SCORE_MIN_PROFIT = 2,
|
||||
SCORE_MIN_INCOME = 3,
|
||||
SCORE_MAX_INCOME = 4,
|
||||
SCORE_DELIVERED = 5,
|
||||
SCORE_CARGO = 6,
|
||||
SCORE_MONEY = 7,
|
||||
SCORE_LOAN = 8,
|
||||
SCORE_TOTAL = 9, // This must always be the last entry
|
||||
|
||||
NUM_SCORE = 10, // How many scores are there..
|
||||
|
||||
SCORE_MAX = 1000 // The max score that can be in the performance history
|
||||
// the scores together of score_info is allowed to be more!
|
||||
};
|
||||
|
||||
typedef struct ScoreInfo {
|
||||
byte id; // Unique ID of the score
|
||||
int needed; // How much you need to get the perfect score
|
||||
int score; // How much score it will give
|
||||
} ScoreInfo;
|
||||
|
||||
extern const ScoreInfo _score_info[];
|
||||
extern int _score_part[MAX_PLAYERS][NUM_SCORE];
|
||||
|
||||
int UpdateCompanyRatingAndValue(Player *p, bool update);
|
||||
|
||||
VARDEF Subsidy _subsidies[MAX_PLAYERS];
|
||||
Pair SetupSubsidyDecodeParam(const Subsidy* s, bool mode);
|
||||
void DeleteSubsidyWithTown(TownID index);
|
||||
void DeleteSubsidyWithIndustry(IndustryID index);
|
||||
void DeleteSubsidyWithStation(StationID index);
|
||||
|
||||
int32 GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type);
|
||||
uint MoveGoodsToStation(TileIndex tile, int w, int h, int type, uint amount);
|
||||
|
||||
#endif /* ECONOMY_H */
|
||||
445
src/elrail.c
Normal file
445
src/elrail.c
Normal file
@@ -0,0 +1,445 @@
|
||||
/* $Id$ */
|
||||
/** @file elrail.c
|
||||
* This file deals with displaying wires and pylons for electric railways.
|
||||
* <h2>Basics</h2>
|
||||
*
|
||||
* <h3>Tile Types</h3>
|
||||
*
|
||||
* We have two different types of tiles in the drawing code:
|
||||
* Normal Railway Tiles (NRTs) which can have more than one track on it, and
|
||||
* Special Railways tiles (SRTs) which have only one track (like crossings, depots
|
||||
* stations, etc).
|
||||
*
|
||||
* <h3>Location Categories</h3>
|
||||
*
|
||||
* All tiles are categorized into three location groups (TLG):
|
||||
* Group 0: Tiles with both an even X coordinate and an even Y coordinate
|
||||
* Group 1: Tiles with an even X and an odd Y coordinate
|
||||
* Group 2: Tiles with an odd X and an even Y coordinate
|
||||
* Group 3: Tiles with both an odd X and Y coordnate.
|
||||
*
|
||||
* <h3>Pylon Points</h3>
|
||||
* <h4>Control Points</h4>
|
||||
* A Pylon Control Point (PCP) is a position where a wire (or rather two)
|
||||
* is mounted onto a pylon.
|
||||
* Each NRT does contain 4 PCPs which are bitmapped to a byte
|
||||
* variable and are represented by the DiagDirection enum
|
||||
*
|
||||
* Each track ends on two PCPs and thus requires one pylon on each end. However,
|
||||
* there is one exception: Straight-and-level tracks only have one pylon every
|
||||
* other tile.
|
||||
*
|
||||
* Now on each edge there are two PCPs: One from each adjacent tile. Both PCPs
|
||||
* are merged using an OR operation (i. e. if one tile needs a PCP at the postion
|
||||
* in question, both tiles get it).
|
||||
*
|
||||
* <h4>Position Points</h4>
|
||||
* A Pylon Position Point (PPP) is a position where a pylon is located on the
|
||||
* ground. Each PCP owns 8 in (45 degree steps) PPPs that are located around
|
||||
* it. PPPs are represented using the Direction enum. Each track bit has PPPs
|
||||
* that are impossible (because the pylon would be situated on the track) and
|
||||
* some that are preferred (because the pylon would be rectangular to the track).
|
||||
*
|
||||
* <img src="../../elrail_tile.png">
|
||||
* <img src="../../elrail_track.png">
|
||||
*
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "station_map.h"
|
||||
#include "tile.h"
|
||||
#include "viewport.h"
|
||||
#include "functions.h" /* We should REALLY get rid of this goddamn file, as it is butt-ugly */
|
||||
#include "variables.h" /* ... same here */
|
||||
#include "rail.h"
|
||||
#include "debug.h"
|
||||
#include "tunnel_map.h"
|
||||
#include "road_map.h"
|
||||
#include "bridge_map.h"
|
||||
#include "bridge.h"
|
||||
#include "rail_map.h"
|
||||
#include "table/sprites.h"
|
||||
#include "table/elrail_data.h"
|
||||
#include "vehicle.h"
|
||||
#include "train.h"
|
||||
#include "gui.h"
|
||||
|
||||
static inline TLG GetTLG(TileIndex t)
|
||||
{
|
||||
return (HASBIT(TileX(t), 0) << 1) + HASBIT(TileY(t), 0);
|
||||
}
|
||||
|
||||
/** Finds which Rail Bits are present on a given tile. For bridge tiles,
|
||||
* returns track bits under the bridge
|
||||
*/
|
||||
static TrackBits GetRailTrackBitsUniversal(TileIndex t, byte *override)
|
||||
{
|
||||
switch (GetTileType(t)) {
|
||||
case MP_RAILWAY:
|
||||
if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
|
||||
switch (GetRailTileType(t)) {
|
||||
case RAIL_TILE_NORMAL: case RAIL_TILE_SIGNALS:
|
||||
return GetTrackBits(t);
|
||||
case RAIL_TILE_DEPOT_WAYPOINT:
|
||||
if (GetRailTileSubtype(t) == RAIL_SUBTYPE_WAYPOINT) return GetRailWaypointBits(t);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case MP_TUNNELBRIDGE:
|
||||
if (IsTunnel(t)) {
|
||||
if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
|
||||
if (override != NULL) *override = 1 << GetTunnelDirection(t);
|
||||
return AxisToTrackBits(DiagDirToAxis(GetTunnelDirection(t)));
|
||||
} else {
|
||||
if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
|
||||
if (override != NULL && DistanceMax(t, GetOtherBridgeEnd(t)) > 1) {
|
||||
*override = 1 << GetBridgeRampDirection(t);
|
||||
}
|
||||
return AxisToTrackBits(DiagDirToAxis(GetBridgeRampDirection(t)));
|
||||
}
|
||||
|
||||
case MP_STREET:
|
||||
if (GetRoadTileType(t) != ROAD_TILE_CROSSING) return 0;
|
||||
if (GetRailTypeCrossing(t) != RAILTYPE_ELECTRIC) return 0;
|
||||
return GetCrossingRailBits(t);
|
||||
|
||||
case MP_STATION:
|
||||
if (!IsRailwayStation(t)) return 0;
|
||||
if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
|
||||
if (!IsStationTileElectrifiable(t)) return 0;
|
||||
return TrackToTrackBits(GetRailStationTrack(t));
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Corrects the tileh for certain tile types. Returns an effective tileh for the track on the tile.
|
||||
* @param tile The tile to analyse
|
||||
* @param *tileh the tileh
|
||||
*/
|
||||
static void AdjustTileh(TileIndex tile, Slope *tileh)
|
||||
{
|
||||
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
|
||||
if (IsTunnel(tile)) {
|
||||
*tileh = SLOPE_FLAT;
|
||||
} else {
|
||||
if (*tileh != SLOPE_FLAT) {
|
||||
*tileh = SLOPE_FLAT;
|
||||
} else {
|
||||
switch (GetBridgeRampDirection(tile)) {
|
||||
case DIAGDIR_NE: *tileh = SLOPE_NE; break;
|
||||
case DIAGDIR_SE: *tileh = SLOPE_SE; break;
|
||||
case DIAGDIR_SW: *tileh = SLOPE_SW; break;
|
||||
case DIAGDIR_NW: *tileh = SLOPE_NW; break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Draws wires and, if required, pylons on a given tile
|
||||
* @param ti The Tileinfo to draw the tile for
|
||||
*/
|
||||
static void DrawCatenaryRailway(const TileInfo *ti)
|
||||
{
|
||||
/* Pylons are placed on a tile edge, so we need to take into account
|
||||
* the track configuration of 2 adjacent tiles. trackconfig[0] stores the
|
||||
* current tile (home tile) while [1] holds the neighbour */
|
||||
TrackBits trackconfig[TS_END];
|
||||
bool isflat[TS_END];
|
||||
/* Note that ti->tileh has already been adjusted for Foundations */
|
||||
Slope tileh[TS_END] = { ti->tileh, SLOPE_FLAT };
|
||||
|
||||
TLG tlg = GetTLG(ti->tile);
|
||||
byte PCPstatus = 0;
|
||||
byte OverridePCP = 0;
|
||||
byte PPPpreferred[DIAGDIR_END];
|
||||
byte PPPallowed[DIAGDIR_END];
|
||||
DiagDirection i;
|
||||
Track t;
|
||||
|
||||
/* Find which rail bits are present, and select the override points.
|
||||
* We don't draw a pylon:
|
||||
* 1) INSIDE a tunnel (we wouldn't see it anyway)
|
||||
* 2) on the "far" end of a bridge head (the one that connects to bridge middle),
|
||||
* because that one is drawn on the bridge. Exception is for length 0 bridges
|
||||
* which have no middle tiles */
|
||||
trackconfig[TS_HOME] = GetRailTrackBitsUniversal(ti->tile, &OverridePCP);
|
||||
/* If a track bit is present that is not in the main direction, the track is level */
|
||||
isflat[TS_HOME] = trackconfig[TS_HOME] & (TRACK_BIT_HORZ | TRACK_BIT_VERT);
|
||||
|
||||
AdjustTileh(ti->tile, &tileh[TS_HOME]);
|
||||
|
||||
for (i = DIAGDIR_NE; i < DIAGDIR_END; i++) {
|
||||
TileIndex neighbour = ti->tile + TileOffsByDiagDir(i);
|
||||
uint foundation = 0;
|
||||
int k;
|
||||
|
||||
/* Here's one of the main headaches. GetTileSlope does not correct for possibly
|
||||
* existing foundataions, so we do have to do that manually later on.*/
|
||||
tileh[TS_NEIGHBOUR] = GetTileSlope(neighbour, NULL);
|
||||
trackconfig[TS_NEIGHBOUR] = GetRailTrackBitsUniversal(neighbour, NULL);
|
||||
if (IsTunnelTile(neighbour) && i != GetTunnelDirection(neighbour)) trackconfig[TS_NEIGHBOUR] = 0;
|
||||
isflat[TS_NEIGHBOUR] = trackconfig[TS_NEIGHBOUR] & (TRACK_BIT_HORZ | TRACK_BIT_VERT);
|
||||
|
||||
PPPpreferred[i] = 0xFF; /* We start with preferring everything (end-of-line in any direction) */
|
||||
PPPallowed[i] = AllowedPPPonPCP[i];
|
||||
|
||||
/* We cycle through all the existing tracks at a PCP and see what
|
||||
* PPPs we want to have, or may not have at all */
|
||||
for (k = 0; k < NUM_TRACKS_AT_PCP; k++) {
|
||||
/* Next to us, we have a bridge head, don't worry about that one, if it shows away from us */
|
||||
if (TrackSourceTile[i][k] == TS_NEIGHBOUR &&
|
||||
IsBridgeTile(neighbour) &&
|
||||
GetBridgeRampDirection(neighbour) == ReverseDiagDir(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We check whether the track in question (k) is present in the tile
|
||||
* (TrackSourceTile) */
|
||||
if (HASBIT(trackconfig[TrackSourceTile[i][k]], TracksAtPCP[i][k])) {
|
||||
/* track found, if track is in the neighbour tile, adjust the number
|
||||
* of the PCP for preferred/allowed determination*/
|
||||
DiagDirection PCPpos = (TrackSourceTile[i][k] == TS_HOME) ? i : ReverseDiagDir(i);
|
||||
SETBIT(PCPstatus, i); /* This PCP is in use */
|
||||
|
||||
PPPpreferred[i] &= PreferredPPPofTrackAtPCP[TracksAtPCP[i][k]][PCPpos];
|
||||
PPPallowed[i] &= ~DisallowedPPPofTrackAtPCP[TracksAtPCP[i][k]][PCPpos];
|
||||
}
|
||||
}
|
||||
|
||||
/* Deactivate all PPPs if PCP is not used */
|
||||
PPPpreferred[i] *= HASBIT(PCPstatus, i);
|
||||
PPPallowed[i] *= HASBIT(PCPstatus, i);
|
||||
|
||||
/* A station is always "flat", so adjust the tileh accordingly */
|
||||
if (IsTileType(neighbour, MP_STATION)) tileh[TS_NEIGHBOUR] = SLOPE_FLAT;
|
||||
|
||||
/* Read the foundataions if they are present, and adjust the tileh */
|
||||
if (IsTileType(neighbour, MP_RAILWAY) && GetRailType(neighbour) == RAILTYPE_ELECTRIC) foundation = GetRailFoundation(tileh[TS_NEIGHBOUR], trackconfig[TS_NEIGHBOUR]);
|
||||
if (IsBridgeTile(neighbour)) {
|
||||
foundation = GetBridgeFoundation(tileh[TS_NEIGHBOUR], DiagDirToAxis(GetBridgeRampDirection(neighbour)));
|
||||
}
|
||||
|
||||
if (foundation != 0) {
|
||||
if (foundation < 15) {
|
||||
tileh[TS_NEIGHBOUR] = SLOPE_FLAT;
|
||||
} else {
|
||||
tileh[TS_NEIGHBOUR] = _inclined_tileh[foundation - 15];
|
||||
}
|
||||
}
|
||||
|
||||
AdjustTileh(neighbour, &tileh[TS_NEIGHBOUR]);
|
||||
|
||||
/* If we have a straight (and level) track, we want a pylon only every 2 tiles
|
||||
* Delete the PCP if this is the case. */
|
||||
/* Level means that the slope is the same, or the track is flat */
|
||||
if (tileh[TS_HOME] == tileh[TS_NEIGHBOUR] || (isflat[TS_HOME] && isflat[TS_NEIGHBOUR])) {
|
||||
for (k = 0; k < NUM_IGNORE_GROUPS; k++)
|
||||
if (PPPpreferred[i] == IgnoredPCP[k][tlg][i]) CLRBIT(PCPstatus, i);
|
||||
}
|
||||
|
||||
/* Now decide where we draw our pylons. First try the preferred PPPs, but they may not exist.
|
||||
* In that case, we try the any of the allowed ones. if they don't exist either, don't draw
|
||||
* anything. Note that the preferred PPPs still contain the end-of-line markers.
|
||||
* Remove those (simply by ANDing with allowed, since these markers are never allowed) */
|
||||
if ((PPPallowed[i] & PPPpreferred[i]) != 0) PPPallowed[i] &= PPPpreferred[i];
|
||||
|
||||
if (MayHaveBridgeAbove(ti->tile) && IsBridgeAbove(ti->tile)) {
|
||||
Track bridgetrack = GetBridgeAxis(ti->tile) == AXIS_X ? TRACK_X : TRACK_Y;
|
||||
uint height = GetBridgeHeight(GetNorthernBridgeEnd(ti->tile));
|
||||
|
||||
if ((height <= TilePixelHeight(ti->tile) + TILE_HEIGHT) &&
|
||||
(i == PCPpositions[bridgetrack][0] || i == PCPpositions[bridgetrack][1])) SETBIT(OverridePCP, i);
|
||||
}
|
||||
|
||||
if (PPPallowed[i] != 0 && HASBIT(PCPstatus, i) && !HASBIT(OverridePCP, i)) {
|
||||
for (k = 0; k < DIR_END; k++) {
|
||||
byte temp = PPPorder[i][GetTLG(ti->tile)][k];
|
||||
|
||||
if (HASBIT(PPPallowed[i], temp)) {
|
||||
uint x = ti->x + x_pcp_offsets[i] + x_ppp_offsets[temp];
|
||||
uint y = ti->y + y_pcp_offsets[i] + y_ppp_offsets[temp];
|
||||
|
||||
/* Don't build the pylon if it would be outside the tile */
|
||||
if (!HASBIT(OwnedPPPonPCP[i], temp)) {
|
||||
/* We have a neighour that will draw it, bail out */
|
||||
if (trackconfig[TS_NEIGHBOUR] != 0) break;
|
||||
continue; /* No neighbour, go looking for a better position */
|
||||
}
|
||||
|
||||
AddSortableSpriteToDraw(pylons_normal[temp], x, y, 1, 1, 10,
|
||||
GetSlopeZ(ti->x + x_pcp_offsets[i], ti->y + y_pcp_offsets[i]));
|
||||
break; /* We already have drawn a pylon, bail out */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't draw a wire under a low bridge */
|
||||
if (MayHaveBridgeAbove(ti->tile) && IsBridgeAbove(ti->tile) && !(_display_opt & DO_TRANS_BUILDINGS)) {
|
||||
uint height = GetBridgeHeight(GetNorthernBridgeEnd(ti->tile));
|
||||
|
||||
if (height <= TilePixelHeight(ti->tile) + TILE_HEIGHT) return;
|
||||
}
|
||||
|
||||
/* Drawing of pylons is finished, now draw the wires */
|
||||
for (t = 0; t < TRACK_END; t++) {
|
||||
if (HASBIT(trackconfig[TS_HOME], t)) {
|
||||
|
||||
byte PCPconfig = HASBIT(PCPstatus, PCPpositions[t][0]) +
|
||||
(HASBIT(PCPstatus, PCPpositions[t][1]) << 1);
|
||||
|
||||
const SortableSpriteStruct *sss;
|
||||
int tileh_selector = !(tileh[TS_HOME] % 3) * tileh[TS_HOME] / 3; /* tileh for the slopes, 0 otherwise */
|
||||
|
||||
assert(PCPconfig != 0); /* We have a pylon on neither end of the wire, that doesn't work (since we have no sprites for that) */
|
||||
assert(!IsSteepSlope(tileh[TS_HOME]));
|
||||
sss = &CatenarySpriteData[Wires[tileh_selector][t][PCPconfig]];
|
||||
|
||||
AddSortableSpriteToDraw( sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
|
||||
sss->x_size, sss->y_size, sss->z_size, GetSlopeZ(ti->x + min(sss->x_offset, TILE_SIZE - 1), ti->y + min(sss->y_offset, TILE_SIZE - 1)) + sss->z_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawCatenaryOnBridge(const TileInfo *ti)
|
||||
{
|
||||
TileIndex end = GetSouthernBridgeEnd(ti->tile);
|
||||
TileIndex start = GetOtherBridgeEnd(end);
|
||||
|
||||
uint length = GetBridgeLength(start, end);
|
||||
uint num = DistanceMax(ti->tile, start);
|
||||
uint height;
|
||||
|
||||
const SortableSpriteStruct *sss;
|
||||
Axis axis = GetBridgeAxis(ti->tile);
|
||||
TLG tlg = GetTLG(ti->tile);
|
||||
|
||||
CatenarySprite offset = axis == AXIS_X ? 0 : WIRE_Y_FLAT_BOTH - WIRE_X_FLAT_BOTH;
|
||||
|
||||
if ((length % 2) && num == length) {
|
||||
/* Draw the "short" wire on the southern end of the bridge
|
||||
* only needed if the length of the bridge is odd */
|
||||
sss = &CatenarySpriteData[WIRE_X_FLAT_BOTH + offset];
|
||||
} else {
|
||||
/* Draw "long" wires on all other tiles of the bridge (one pylon every two tiles) */
|
||||
sss = &CatenarySpriteData[WIRE_X_FLAT_SW + (num % 2) + offset];
|
||||
}
|
||||
|
||||
height = GetBridgeHeight(end);
|
||||
|
||||
AddSortableSpriteToDraw( sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
|
||||
sss->x_size, sss->y_size, sss->z_size, height + sss->z_offset
|
||||
);
|
||||
|
||||
/* Finished with wires, draw pylons */
|
||||
/* every other tile needs a pylon on the northern end */
|
||||
if (num % 2) {
|
||||
if (axis == AXIS_X) {
|
||||
AddSortableSpriteToDraw(pylons_bridge[0 + HASBIT(tlg, 0)], ti->x, ti->y + 4 + 8 * HASBIT(tlg, 0), 1, 1, 10, height);
|
||||
} else {
|
||||
AddSortableSpriteToDraw(pylons_bridge[2 + HASBIT(tlg, 1)], ti->x + 4 + 8 * HASBIT(tlg, 1), ti->y, 1, 1, 10, height);
|
||||
}
|
||||
}
|
||||
|
||||
/* need a pylon on the southern end of the bridge */
|
||||
if (DistanceMax(ti->tile, start) == length) {
|
||||
if (axis == AXIS_X) {
|
||||
AddSortableSpriteToDraw(pylons_bridge[0 + HASBIT(tlg, 0)], ti->x + 16, ti->y + 4 + 8 * HASBIT(tlg, 0), 1, 1, 10, height);
|
||||
} else {
|
||||
AddSortableSpriteToDraw(pylons_bridge[2 + HASBIT(tlg, 1)], ti->x + 4 + 8 * HASBIT(tlg, 1), ti->y + 16, 1, 1, 10, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCatenary(const TileInfo *ti)
|
||||
{
|
||||
if (MayHaveBridgeAbove(ti->tile) && IsBridgeAbove(ti->tile)) {
|
||||
TileIndex head = GetNorthernBridgeEnd(ti->tile);
|
||||
|
||||
if (GetBridgeTransportType(head) == TRANSPORT_RAIL && GetRailType(head) == RAILTYPE_ELECTRIC) {
|
||||
DrawCatenaryOnBridge(ti);
|
||||
}
|
||||
}
|
||||
if (_patches.disable_elrails) return;
|
||||
|
||||
switch (GetTileType(ti->tile)) {
|
||||
case MP_RAILWAY:
|
||||
if (IsRailDepot(ti->tile)) {
|
||||
const SortableSpriteStruct* sss = &CatenarySpriteData_Depot[GetRailDepotDirection(ti->tile)];
|
||||
|
||||
AddSortableSpriteToDraw(
|
||||
sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
|
||||
sss->x_size, sss->y_size, sss->z_size,
|
||||
GetTileMaxZ(ti->tile) + sss->z_offset
|
||||
);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case MP_TUNNELBRIDGE:
|
||||
case MP_STREET:
|
||||
case MP_STATION:
|
||||
break;
|
||||
|
||||
default: return;
|
||||
}
|
||||
DrawCatenaryRailway(ti);
|
||||
}
|
||||
|
||||
int32 SettingsDisableElrail(int32 p1)
|
||||
{
|
||||
EngineID e_id;
|
||||
Vehicle* v;
|
||||
Player *p;
|
||||
bool disable = (p1 != 0);
|
||||
|
||||
/* we will now walk through all electric train engines and change their railtypes if it is the wrong one*/
|
||||
const RailType old_railtype = disable ? RAILTYPE_ELECTRIC : RAILTYPE_RAIL;
|
||||
const RailType new_railtype = disable ? RAILTYPE_RAIL : RAILTYPE_ELECTRIC;
|
||||
|
||||
/* walk through all train engines */
|
||||
for (e_id = 0; e_id < NUM_TRAIN_ENGINES; e_id++) {
|
||||
const RailVehicleInfo *rv_info = RailVehInfo(e_id);
|
||||
Engine *e = GetEngine(e_id);
|
||||
/* if it is an electric rail engine and its railtype is the wrong one */
|
||||
if (rv_info->engclass == 2 && e->railtype == old_railtype) {
|
||||
/* change it to the proper one */
|
||||
e->railtype = new_railtype;
|
||||
}
|
||||
}
|
||||
|
||||
/* when disabling elrails, make sure that all existing trains can run on
|
||||
* normal rail too */
|
||||
if (disable) {
|
||||
FOR_ALL_VEHICLES(v) {
|
||||
if (v->type == VEH_Train && v->u.rail.railtype == RAILTYPE_ELECTRIC) {
|
||||
/* this railroad vehicle is now compatible only with elrail,
|
||||
* so add there also normal rail compatibility */
|
||||
v->u.rail.compatible_railtypes |= (1 << RAILTYPE_RAIL);
|
||||
v->u.rail.railtype = RAILTYPE_RAIL;
|
||||
SETBIT(v->u.rail.flags, VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* setup total power for trains */
|
||||
FOR_ALL_VEHICLES(v) {
|
||||
/* power is cached only for front engines */
|
||||
if (v->type == VEH_Train && IsFrontEngine(v)) TrainPowerChanged(v);
|
||||
}
|
||||
|
||||
FOR_ALL_PLAYERS(p) p->avail_railtypes = GetPlayerRailtypes(p->index);
|
||||
|
||||
/* This resets the _last_built_railtype, which will be invalid for electric
|
||||
* rails. It may have unintended consequences if that function is ever
|
||||
* extended, though. */
|
||||
ReinitGuiAfterToggleElrail(disable);
|
||||
return 0;
|
||||
}
|
||||
52
src/endian_check.c
Normal file
52
src/endian_check.c
Normal file
@@ -0,0 +1,52 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// This pretty simple file checks if the system is LITTLE_ENDIAN or BIG_ENDIAN
|
||||
// it does that by putting a 1 and a 0 in an array, and read it out as one
|
||||
// number. If it is 1, it is LITTLE_ENDIAN, if it is 256, it is BIG_ENDIAN
|
||||
//
|
||||
// After that it outputs the contents of an include files (endian.h)
|
||||
// that says or TTD_LITTLE_ENDIAN, or TTD_BIG_ENDIAN. Makefile takes
|
||||
// care of the real writing to the file.
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
unsigned char EndianTest[2] = { 1, 0 };
|
||||
int force_BE = 0, force_LE = 0, force_PREPROCESSOR = 0;
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "BE") == 0)
|
||||
force_BE = 1;
|
||||
if (argc > 1 && strcmp(argv[1], "LE") == 0)
|
||||
force_LE = 1;
|
||||
if (argc > 1 && strcmp(argv[1], "PREPROCESSOR") == 0)
|
||||
force_PREPROCESSOR = 1;
|
||||
|
||||
printf("#ifndef ENDIAN_H\n#define ENDIAN_H\n");
|
||||
|
||||
if (force_LE == 1) {
|
||||
printf("#define TTD_LITTLE_ENDIAN\n");
|
||||
} else {
|
||||
if (force_BE == 1) {
|
||||
printf("#define TTD_BIG_ENDIAN\n");
|
||||
} else {
|
||||
if (force_PREPROCESSOR == 1) {
|
||||
// adding support for universal binaries on OSX
|
||||
// Universal binaries supports both PPC and x86
|
||||
// If a compiler for OSX gets this setting, it will always pick the correct endian and no test is needed
|
||||
printf("#ifdef __BIG_ENDIAN__\n");
|
||||
printf("#define TTD_BIG_ENDIAN\n");
|
||||
printf("#else\n");
|
||||
printf("#define TTD_LITTLE_ENDIAN\n");
|
||||
printf("#endif\n");
|
||||
} else {
|
||||
if ( *(short *) EndianTest == 1 )
|
||||
printf("#define TTD_LITTLE_ENDIAN\n");
|
||||
else
|
||||
printf("#define TTD_BIG_ENDIAN\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("#endif\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
650
src/engine.c
Normal file
650
src/engine.c
Normal file
@@ -0,0 +1,650 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "debug.h"
|
||||
#include "functions.h"
|
||||
#include "table/strings.h"
|
||||
#include "engine.h"
|
||||
#include "gfx.h"
|
||||
#include "player.h"
|
||||
#include "command.h"
|
||||
#include "vehicle.h"
|
||||
#include "news.h"
|
||||
#include "saveload.h"
|
||||
#include "variables.h"
|
||||
#include "train.h"
|
||||
#include "newgrf_cargo.h"
|
||||
#include "date.h"
|
||||
#include "table/engines.h"
|
||||
|
||||
EngineInfo _engine_info[TOTAL_NUM_ENGINES];
|
||||
RailVehicleInfo _rail_vehicle_info[NUM_TRAIN_ENGINES];
|
||||
ShipVehicleInfo _ship_vehicle_info[NUM_SHIP_ENGINES];
|
||||
AircraftVehicleInfo _aircraft_vehicle_info[NUM_AIRCRAFT_ENGINES];
|
||||
RoadVehicleInfo _road_vehicle_info[NUM_ROAD_ENGINES];
|
||||
|
||||
enum {
|
||||
ENGINE_AVAILABLE = 1,
|
||||
ENGINE_INTRODUCING = 2,
|
||||
ENGINE_PREVIEWING = 4,
|
||||
};
|
||||
|
||||
enum {
|
||||
YEAR_ENGINE_AGING_STOPS = 2050,
|
||||
};
|
||||
|
||||
|
||||
void ShowEnginePreviewWindow(EngineID engine);
|
||||
|
||||
void DeleteCustomEngineNames(void)
|
||||
{
|
||||
uint i;
|
||||
StringID old;
|
||||
|
||||
for (i = 0; i != TOTAL_NUM_ENGINES; i++) {
|
||||
old = _engine_name_strings[i];
|
||||
_engine_name_strings[i] = i + STR_8000_KIRBY_PAUL_TANK_STEAM;
|
||||
DeleteName(old);
|
||||
}
|
||||
|
||||
_vehicle_design_names &= ~1;
|
||||
}
|
||||
|
||||
void LoadCustomEngineNames(void)
|
||||
{
|
||||
/* XXX: not done */
|
||||
DEBUG(misc, 1, "LoadCustomEngineNames: not done");
|
||||
}
|
||||
|
||||
static void SetupEngineNames(void)
|
||||
{
|
||||
StringID *name;
|
||||
|
||||
for (name = _engine_name_strings; name != endof(_engine_name_strings); name++)
|
||||
*name = STR_SV_EMPTY;
|
||||
|
||||
DeleteCustomEngineNames();
|
||||
LoadCustomEngineNames();
|
||||
}
|
||||
|
||||
static void AdjustAvailAircraft(void)
|
||||
{
|
||||
byte avail = 0;
|
||||
if (_cur_year >= 1955) avail |= 2; // big airport
|
||||
if (_cur_year < 1960 || _patches.always_small_airport) avail |= 1; // small airport
|
||||
if (_cur_year >= 1963) avail |= 4; // enable heliport
|
||||
|
||||
if (avail != _avail_aircraft) {
|
||||
_avail_aircraft = avail;
|
||||
InvalidateWindow(WC_BUILD_STATION, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void CalcEngineReliability(Engine *e)
|
||||
{
|
||||
uint age = e->age;
|
||||
|
||||
if (age < e->duration_phase_1) {
|
||||
uint start = e->reliability_start;
|
||||
e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start;
|
||||
} else if ((age -= e->duration_phase_1) < e->duration_phase_2 || _patches.never_expire_vehicles) {
|
||||
/* We are at the peak of this engines life. It will have max reliability.
|
||||
* This is also true if the engines never expire. They will not go bad over time */
|
||||
e->reliability = e->reliability_max;
|
||||
} else if ((age -= e->duration_phase_2) < e->duration_phase_3) {
|
||||
uint max = e->reliability_max;
|
||||
e->reliability = (int)age * (int)(e->reliability_final - max) / e->duration_phase_3 + max;
|
||||
} else {
|
||||
/* time's up for this engine.
|
||||
* We will now completely retire this design */
|
||||
e->player_avail = 0;
|
||||
e->reliability = e->reliability_final;
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Kick this engine out of the lists
|
||||
}
|
||||
InvalidateWindowClasses(WC_BUILD_VEHICLE); // Update to show the new reliability
|
||||
}
|
||||
|
||||
void AddTypeToEngines(void)
|
||||
{
|
||||
Engine* e = _engines;
|
||||
|
||||
do e->type = VEH_Train; while (++e < &_engines[ROAD_ENGINES_INDEX]);
|
||||
do e->type = VEH_Road; while (++e < &_engines[SHIP_ENGINES_INDEX]);
|
||||
do e->type = VEH_Ship; while (++e < &_engines[AIRCRAFT_ENGINES_INDEX]);
|
||||
do e->type = VEH_Aircraft; while (++e < &_engines[TOTAL_NUM_ENGINES]);
|
||||
do e->type = VEH_Special; while (++e < endof(_engines));
|
||||
}
|
||||
|
||||
void StartupEngines(void)
|
||||
{
|
||||
Engine *e;
|
||||
const EngineInfo *ei;
|
||||
/* Aging of vehicles stops, so account for that when starting late */
|
||||
const Date aging_date = min(_date, ConvertYMDToDate(YEAR_ENGINE_AGING_STOPS, 0, 1));
|
||||
|
||||
SetupEngineNames();
|
||||
|
||||
for (e = _engines, ei = _engine_info; e != endof(_engines); e++, ei++) {
|
||||
uint32 r;
|
||||
|
||||
e->age = 0;
|
||||
e->railtype = ei->railtype;
|
||||
e->flags = 0;
|
||||
e->player_avail = 0;
|
||||
|
||||
// The magic value of 729 days below comes from the NewGRF spec. If the
|
||||
// base intro date is before 1922 then the random number of days is not
|
||||
// added.
|
||||
r = Random();
|
||||
e->intro_date = ei->base_intro <= ConvertYMDToDate(1922, 0, 1) ? ei->base_intro : (Date)GB(r, 0, 9) + ei->base_intro;
|
||||
if (e->intro_date <= _date) {
|
||||
e->age = (aging_date - e->intro_date) >> 5;
|
||||
e->player_avail = (byte)-1;
|
||||
e->flags |= ENGINE_AVAILABLE;
|
||||
}
|
||||
|
||||
e->reliability_start = GB(r, 16, 14) + 0x7AE0;
|
||||
r = Random();
|
||||
e->reliability_max = GB(r, 0, 14) + 0xBFFF;
|
||||
e->reliability_final = GB(r, 16, 14) + 0x3FFF;
|
||||
|
||||
r = Random();
|
||||
e->duration_phase_1 = GB(r, 0, 5) + 7;
|
||||
e->duration_phase_2 = GB(r, 5, 4) + ei->base_life * 12 - 96;
|
||||
e->duration_phase_3 = GB(r, 9, 7) + 120;
|
||||
|
||||
e->reliability_spd_dec = (ei->unk2&0x7F) << 2;
|
||||
|
||||
/* my invented flag for something that is a wagon */
|
||||
if (ei->unk2 & 0x80) {
|
||||
e->age = 0xFFFF;
|
||||
} else {
|
||||
CalcEngineReliability(e);
|
||||
}
|
||||
|
||||
e->lifelength = ei->lifelength + _patches.extend_vehicle_life;
|
||||
|
||||
// prevent certain engines from ever appearing.
|
||||
if (!HASBIT(ei->climates, _opt.landscape)) {
|
||||
e->flags |= ENGINE_AVAILABLE;
|
||||
e->player_avail = 0;
|
||||
}
|
||||
|
||||
/* This sets up type for the engine
|
||||
* It is needed if you want to ask the engine what type it is
|
||||
* It should hopefully be the same as when you ask a vehicle what it is
|
||||
* but using this, you can ask what type an engine number is
|
||||
* even if it is not a vehicle (yet)*/
|
||||
}
|
||||
|
||||
AdjustAvailAircraft();
|
||||
}
|
||||
|
||||
static void AcceptEnginePreview(Engine *e, PlayerID player)
|
||||
{
|
||||
Player *p = GetPlayer(player);
|
||||
|
||||
assert(e->railtype < RAILTYPE_END);
|
||||
SETBIT(e->player_avail, player);
|
||||
SETBIT(p->avail_railtypes, e->railtype);
|
||||
|
||||
e->preview_player = 0xFF;
|
||||
if (player == _local_player) {
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE);
|
||||
InvalidateWindowClasses(WC_REPLACE_VEHICLE);
|
||||
}
|
||||
}
|
||||
|
||||
static PlayerID GetBestPlayer(PlayerID pp)
|
||||
{
|
||||
const Player *p;
|
||||
int32 best_hist;
|
||||
PlayerID best_player;
|
||||
uint mask = 0;
|
||||
|
||||
do {
|
||||
best_hist = -1;
|
||||
best_player = PLAYER_SPECTATOR;
|
||||
FOR_ALL_PLAYERS(p) {
|
||||
if (p->is_active && p->block_preview == 0 && !HASBIT(mask, p->index) &&
|
||||
p->old_economy[0].performance_history > best_hist) {
|
||||
best_hist = p->old_economy[0].performance_history;
|
||||
best_player = p->index;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_player == PLAYER_SPECTATOR) return PLAYER_SPECTATOR;
|
||||
|
||||
SETBIT(mask, best_player);
|
||||
} while (--pp != 0);
|
||||
|
||||
return best_player;
|
||||
}
|
||||
|
||||
void EnginesDailyLoop(void)
|
||||
{
|
||||
EngineID i;
|
||||
|
||||
if (_cur_year >= YEAR_ENGINE_AGING_STOPS) return;
|
||||
|
||||
for (i = 0; i != lengthof(_engines); i++) {
|
||||
Engine *e = &_engines[i];
|
||||
|
||||
if (e->flags & ENGINE_INTRODUCING) {
|
||||
if (e->flags & ENGINE_PREVIEWING) {
|
||||
if (e->preview_player != 0xFF && !--e->preview_wait) {
|
||||
e->flags &= ~ENGINE_PREVIEWING;
|
||||
DeleteWindowById(WC_ENGINE_PREVIEW, i);
|
||||
e->preview_player++;
|
||||
}
|
||||
} else if (e->preview_player != 0xFF) {
|
||||
PlayerID best_player = GetBestPlayer(e->preview_player);
|
||||
|
||||
if (best_player == PLAYER_SPECTATOR) {
|
||||
e->preview_player = 0xFF;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsHumanPlayer(best_player)) {
|
||||
/* XXX - TTDBUG: TTD has a bug here ???? */
|
||||
AcceptEnginePreview(e, best_player);
|
||||
} else {
|
||||
e->flags |= ENGINE_PREVIEWING;
|
||||
e->preview_wait = 20;
|
||||
if (IsInteractivePlayer(best_player)) ShowEnginePreviewWindow(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Accept an engine prototype. XXX - it is possible that the top-player
|
||||
* changes while you are waiting to accept the offer? Then it becomes invalid
|
||||
* @param tile unused
|
||||
* @param p1 engine-prototype offered
|
||||
* @param p2 unused
|
||||
*/
|
||||
int32 CmdWantEnginePreview(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
||||
{
|
||||
Engine *e;
|
||||
|
||||
if (!IsEngineIndex(p1)) return CMD_ERROR;
|
||||
e = GetEngine(p1);
|
||||
if (GetBestPlayer(e->preview_player) != _current_player) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) AcceptEnginePreview(e, _current_player);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Determine if an engine type is a wagon (and not a loco)
|
||||
static bool IsWagon(EngineID index)
|
||||
{
|
||||
return index < NUM_TRAIN_ENGINES && RailVehInfo(index)->flags & RVI_WAGON;
|
||||
}
|
||||
|
||||
static void NewVehicleAvailable(Engine *e)
|
||||
{
|
||||
Vehicle *v;
|
||||
Player *p;
|
||||
EngineID index = e - _engines;
|
||||
|
||||
// In case the player didn't build the vehicle during the intro period,
|
||||
// prevent that player from getting future intro periods for a while.
|
||||
if (e->flags & ENGINE_INTRODUCING) {
|
||||
FOR_ALL_PLAYERS(p) {
|
||||
uint block_preview = p->block_preview;
|
||||
|
||||
if (!HASBIT(e->player_avail, p->index)) continue;
|
||||
|
||||
/* We assume the user did NOT build it.. prove me wrong ;) */
|
||||
p->block_preview = 20;
|
||||
|
||||
FOR_ALL_VEHICLES(v) {
|
||||
if (v->type == VEH_Train || v->type == VEH_Road || v->type == VEH_Ship ||
|
||||
(v->type == VEH_Aircraft && v->subtype <= 2)) {
|
||||
if (v->owner == p->index && v->engine_type == index) {
|
||||
/* The user did prove me wrong, so restore old value */
|
||||
p->block_preview = block_preview;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
e->flags = (e->flags & ~ENGINE_INTRODUCING) | ENGINE_AVAILABLE;
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE);
|
||||
InvalidateWindowClasses(WC_REPLACE_VEHICLE);
|
||||
|
||||
// Now available for all players
|
||||
e->player_avail = (byte)-1;
|
||||
|
||||
// Do not introduce new rail wagons
|
||||
if (IsWagon(index)) return;
|
||||
|
||||
// make maglev / monorail available
|
||||
FOR_ALL_PLAYERS(p) {
|
||||
if (p->is_active) {
|
||||
assert(e->railtype < RAILTYPE_END);
|
||||
SETBIT(p->avail_railtypes, e->railtype);
|
||||
}
|
||||
}
|
||||
|
||||
if (index < NUM_TRAIN_ENGINES) {
|
||||
AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_TRAINAVAIL), 0, 0);
|
||||
} else if (index < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES) {
|
||||
AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_ROADAVAIL), 0, 0);
|
||||
} else if (index < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES) {
|
||||
AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_SHIPAVAIL), 0, 0);
|
||||
} else {
|
||||
AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_AIRCRAFTAVAIL), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void EnginesMonthlyLoop(void)
|
||||
{
|
||||
Engine *e;
|
||||
|
||||
if (_cur_year < YEAR_ENGINE_AGING_STOPS) {
|
||||
for (e = _engines; e != endof(_engines); e++) {
|
||||
// Age the vehicle
|
||||
if (e->flags & ENGINE_AVAILABLE && e->age != 0xFFFF) {
|
||||
e->age++;
|
||||
CalcEngineReliability(e);
|
||||
}
|
||||
|
||||
if (!(e->flags & ENGINE_AVAILABLE) && _date >= (e->intro_date + 365)) {
|
||||
// Introduce it to all players
|
||||
NewVehicleAvailable(e);
|
||||
} else if (!(e->flags & (ENGINE_AVAILABLE|ENGINE_INTRODUCING)) && _date >= e->intro_date) {
|
||||
// Introduction date has passed.. show introducing dialog to one player.
|
||||
e->flags |= ENGINE_INTRODUCING;
|
||||
|
||||
// Do not introduce new rail wagons
|
||||
if (!IsWagon(e - _engines))
|
||||
e->preview_player = 1; // Give to the player with the highest rating.
|
||||
}
|
||||
}
|
||||
}
|
||||
AdjustAvailAircraft();
|
||||
}
|
||||
|
||||
/** Rename an engine.
|
||||
* @param tile unused
|
||||
* @param p1 engine ID to rename
|
||||
* @param p2 unused
|
||||
*/
|
||||
int32 CmdRenameEngine(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
||||
{
|
||||
StringID str;
|
||||
|
||||
if (!IsEngineIndex(p1) || _cmd_text[0] == '\0') return CMD_ERROR;
|
||||
|
||||
str = AllocateNameUnique(_cmd_text, 0);
|
||||
if (str == 0) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
StringID old_str = _engine_name_strings[p1];
|
||||
_engine_name_strings[p1] = str;
|
||||
DeleteName(old_str);
|
||||
_vehicle_design_names |= 3;
|
||||
MarkWholeScreenDirty();
|
||||
} else {
|
||||
DeleteName(str);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* returns true if an engine is valid, of the specified type, and buildable by
|
||||
* the given player, false otherwise
|
||||
*
|
||||
* engine = index of the engine to check
|
||||
* type = the type the engine should be of (VEH_xxx)
|
||||
* player = index of the player
|
||||
*/
|
||||
bool IsEngineBuildable(EngineID engine, byte type, PlayerID player)
|
||||
{
|
||||
const Engine *e;
|
||||
|
||||
// check if it's an engine that is in the engine array
|
||||
if (!IsEngineIndex(engine)) return false;
|
||||
|
||||
e = GetEngine(engine);
|
||||
|
||||
// check if it's an engine of specified type
|
||||
if (e->type != type) return false;
|
||||
|
||||
// check if it's available
|
||||
if (!HASBIT(e->player_avail, player)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Engine Replacement stuff
|
||||
************************************************************************/
|
||||
|
||||
static void EngineRenewPoolNewBlock(uint start_item);
|
||||
|
||||
DEFINE_OLD_POOL(EngineRenew, EngineRenew, EngineRenewPoolNewBlock, NULL)
|
||||
|
||||
static void EngineRenewPoolNewBlock(uint start_item)
|
||||
{
|
||||
EngineRenew *er;
|
||||
|
||||
/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
|
||||
* TODO - This is just a temporary stage, this will be removed. */
|
||||
for (er = GetEngineRenew(start_item); er != NULL; er = (er->index + 1U < GetEngineRenewPoolSize()) ? GetEngineRenew(er->index + 1U) : NULL) {
|
||||
er->index = start_item++;
|
||||
er->from = INVALID_ENGINE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static EngineRenew *AllocateEngineRenew(void)
|
||||
{
|
||||
EngineRenew *er;
|
||||
|
||||
/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
|
||||
* TODO - This is just a temporary stage, this will be removed. */
|
||||
for (er = GetEngineRenew(0); er != NULL; er = (er->index + 1U < GetEngineRenewPoolSize()) ? GetEngineRenew(er->index + 1U) : NULL) {
|
||||
if (IsValidEngineRenew(er)) continue;
|
||||
|
||||
er->to = INVALID_ENGINE;
|
||||
er->next = NULL;
|
||||
return er;
|
||||
}
|
||||
|
||||
/* Check if we can add a block to the pool */
|
||||
if (AddBlockToPool(&_EngineRenew_pool)) return AllocateEngineRenew();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the EngineRenew that specifies the replacement of the given
|
||||
* engine type from the given renewlist */
|
||||
static EngineRenew *GetEngineReplacement(EngineRenewList erl, EngineID engine)
|
||||
{
|
||||
EngineRenew *er = (EngineRenew *)erl;
|
||||
|
||||
while (er) {
|
||||
if (er->from == engine) return er;
|
||||
er = er->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void RemoveAllEngineReplacement(EngineRenewList *erl)
|
||||
{
|
||||
EngineRenew *er = (EngineRenew *)(*erl);
|
||||
EngineRenew *next;
|
||||
|
||||
while (er) {
|
||||
next = er->next;
|
||||
DeleteEngineRenew(er);
|
||||
er = next;
|
||||
}
|
||||
*erl = NULL; // Empty list
|
||||
}
|
||||
|
||||
EngineID EngineReplacement(EngineRenewList erl, EngineID engine)
|
||||
{
|
||||
const EngineRenew *er = GetEngineReplacement(erl, engine);
|
||||
return er == NULL ? INVALID_ENGINE : er->to;
|
||||
}
|
||||
|
||||
int32 AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, uint32 flags)
|
||||
{
|
||||
EngineRenew *er;
|
||||
|
||||
/* Check if the old vehicle is already in the list */
|
||||
er = GetEngineReplacement(*erl, old_engine);
|
||||
if (er != NULL) {
|
||||
if (flags & DC_EXEC) er->to = new_engine;
|
||||
return 0;
|
||||
}
|
||||
|
||||
er = AllocateEngineRenew();
|
||||
if (er == NULL) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
er->from = old_engine;
|
||||
er->to = new_engine;
|
||||
|
||||
/* Insert before the first element */
|
||||
er->next = (EngineRenew *)(*erl);
|
||||
*erl = (EngineRenewList)er;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, uint32 flags)
|
||||
{
|
||||
EngineRenew *er = (EngineRenew *)(*erl);
|
||||
EngineRenew *prev = NULL;
|
||||
|
||||
while (er)
|
||||
{
|
||||
if (er->from == engine) {
|
||||
if (flags & DC_EXEC) {
|
||||
if (prev == NULL) { // First element
|
||||
/* The second becomes the new first element */
|
||||
*erl = (EngineRenewList)er->next;
|
||||
} else {
|
||||
/* Cut this element out */
|
||||
prev->next = er->next;
|
||||
}
|
||||
DeleteEngineRenew(er);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
prev = er;
|
||||
er = er->next;
|
||||
}
|
||||
|
||||
return CMD_ERROR;
|
||||
}
|
||||
|
||||
static const SaveLoad _engine_renew_desc[] = {
|
||||
SLE_VAR(EngineRenew, from, SLE_UINT16),
|
||||
SLE_VAR(EngineRenew, to, SLE_UINT16),
|
||||
|
||||
SLE_REF(EngineRenew, next, REF_ENGINE_RENEWS),
|
||||
|
||||
SLE_END()
|
||||
};
|
||||
|
||||
static void Save_ERNW(void)
|
||||
{
|
||||
EngineRenew *er;
|
||||
|
||||
FOR_ALL_ENGINE_RENEWS(er) {
|
||||
SlSetArrayIndex(er->index);
|
||||
SlObject(er, _engine_renew_desc);
|
||||
}
|
||||
}
|
||||
|
||||
static void Load_ERNW(void)
|
||||
{
|
||||
int index;
|
||||
|
||||
while ((index = SlIterateArray()) != -1) {
|
||||
EngineRenew *er;
|
||||
|
||||
if (!AddBlockIfNeeded(&_EngineRenew_pool, index))
|
||||
error("EngineRenews: failed loading savegame: too many EngineRenews");
|
||||
|
||||
er = GetEngineRenew(index);
|
||||
SlObject(er, _engine_renew_desc);
|
||||
}
|
||||
}
|
||||
|
||||
static const SaveLoad _engine_desc[] = {
|
||||
SLE_CONDVAR(Engine, intro_date, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
|
||||
SLE_CONDVAR(Engine, intro_date, SLE_INT32, 31, SL_MAX_VERSION),
|
||||
SLE_CONDVAR(Engine, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
|
||||
SLE_CONDVAR(Engine, age, SLE_INT32, 31, SL_MAX_VERSION),
|
||||
SLE_VAR(Engine, reliability, SLE_UINT16),
|
||||
SLE_VAR(Engine, reliability_spd_dec, SLE_UINT16),
|
||||
SLE_VAR(Engine, reliability_start, SLE_UINT16),
|
||||
SLE_VAR(Engine, reliability_max, SLE_UINT16),
|
||||
SLE_VAR(Engine, reliability_final, SLE_UINT16),
|
||||
SLE_VAR(Engine, duration_phase_1, SLE_UINT16),
|
||||
SLE_VAR(Engine, duration_phase_2, SLE_UINT16),
|
||||
SLE_VAR(Engine, duration_phase_3, SLE_UINT16),
|
||||
|
||||
SLE_VAR(Engine, lifelength, SLE_UINT8),
|
||||
SLE_VAR(Engine, flags, SLE_UINT8),
|
||||
SLE_VAR(Engine, preview_player, SLE_UINT8),
|
||||
SLE_VAR(Engine, preview_wait, SLE_UINT8),
|
||||
SLE_VAR(Engine, railtype, SLE_UINT8),
|
||||
SLE_VAR(Engine, player_avail, SLE_UINT8),
|
||||
|
||||
// reserve extra space in savegame here. (currently 16 bytes)
|
||||
SLE_CONDNULL(16, 2, SL_MAX_VERSION),
|
||||
|
||||
SLE_END()
|
||||
};
|
||||
|
||||
static void Save_ENGN(void)
|
||||
{
|
||||
uint i;
|
||||
|
||||
for (i = 0; i != lengthof(_engines); i++) {
|
||||
SlSetArrayIndex(i);
|
||||
SlObject(&_engines[i], _engine_desc);
|
||||
}
|
||||
}
|
||||
|
||||
static void Load_ENGN(void)
|
||||
{
|
||||
int index;
|
||||
while ((index = SlIterateArray()) != -1) {
|
||||
SlObject(GetEngine(index), _engine_desc);
|
||||
}
|
||||
}
|
||||
|
||||
static void LoadSave_ENGS(void)
|
||||
{
|
||||
SlArray(_engine_name_strings, lengthof(_engine_name_strings), SLE_STRINGID);
|
||||
}
|
||||
|
||||
const ChunkHandler _engine_chunk_handlers[] = {
|
||||
{ 'ENGN', Save_ENGN, Load_ENGN, CH_ARRAY },
|
||||
{ 'ENGS', LoadSave_ENGS, LoadSave_ENGS, CH_RIFF },
|
||||
{ 'ERNW', Save_ERNW, Load_ERNW, CH_ARRAY | CH_LAST},
|
||||
};
|
||||
|
||||
void InitializeEngines(void)
|
||||
{
|
||||
/* Clean the engine renew pool and create 1 block in it */
|
||||
CleanPool(&_EngineRenew_pool);
|
||||
AddBlockToPool(&_EngineRenew_pool);
|
||||
}
|
||||
309
src/engine.h
Normal file
309
src/engine.h
Normal file
@@ -0,0 +1,309 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef ENGINE_H
|
||||
#define ENGINE_H
|
||||
|
||||
/** @file engine.h */
|
||||
|
||||
#include "oldpool.h"
|
||||
|
||||
typedef struct RailVehicleInfo {
|
||||
byte image_index;
|
||||
byte flags; /* 1=multihead engine, 2=wagon */
|
||||
byte base_cost;
|
||||
uint16 max_speed;
|
||||
uint16 power;
|
||||
uint16 weight;
|
||||
byte running_cost_base;
|
||||
byte running_cost_class;
|
||||
byte engclass; // 0: steam, 1: diesel, 2: electric
|
||||
byte capacity;
|
||||
CargoID cargo_type;
|
||||
byte ai_rank;
|
||||
uint16 pow_wag_power;
|
||||
byte pow_wag_weight;
|
||||
byte visual_effect; // NOTE: this is not 100% implemented yet, at the moment it is only used as a 'fallback' value
|
||||
// for when the 'powered wagon' callback fails. But it should really also determine what
|
||||
// kind of visual effect to generate for a vehicle (default, steam, diesel, electric).
|
||||
// Same goes for the callback result, which atm is only used to check if a wagon is powered.
|
||||
byte shorten_factor; // length on main map for this type is 8 - shorten_factor
|
||||
byte tractive_effort; ///< Tractive effort coefficient
|
||||
byte user_def_data; ///! Property 0x25: "User-defined bit mask" Used only for (very few) NewGRF vehicles
|
||||
} RailVehicleInfo;
|
||||
|
||||
typedef struct ShipVehicleInfo {
|
||||
byte image_index;
|
||||
byte base_cost;
|
||||
uint16 max_speed;
|
||||
CargoID cargo_type;
|
||||
uint16 capacity;
|
||||
byte running_cost;
|
||||
byte sfx;
|
||||
byte refittable;
|
||||
} ShipVehicleInfo;
|
||||
|
||||
// Aircraft subtypes
|
||||
enum {
|
||||
AIR_CTOL = 1, // Conventional Take Off and Landing, i.e. planes
|
||||
AIR_FAST = 2
|
||||
};
|
||||
|
||||
typedef struct AircraftVehicleInfo {
|
||||
byte image_index;
|
||||
byte base_cost;
|
||||
byte running_cost;
|
||||
byte subtype;
|
||||
byte sfx;
|
||||
byte acceleration;
|
||||
byte max_speed;
|
||||
byte mail_capacity;
|
||||
uint16 passenger_capacity;
|
||||
} AircraftVehicleInfo;
|
||||
|
||||
typedef struct RoadVehicleInfo {
|
||||
byte image_index;
|
||||
byte base_cost;
|
||||
byte running_cost;
|
||||
byte sfx;
|
||||
byte max_speed;
|
||||
byte capacity;
|
||||
CargoID cargo_type;
|
||||
} RoadVehicleInfo;
|
||||
|
||||
/** Information about a vehicle
|
||||
* @see table/engines.h
|
||||
*/
|
||||
typedef struct EngineInfo {
|
||||
Date base_intro;
|
||||
byte unk2; ///< Carriages have the highest bit set in this one
|
||||
Year lifelength;
|
||||
Year base_life;
|
||||
byte load_amount;
|
||||
byte railtype:4;
|
||||
byte climates:4;
|
||||
uint32 refit_mask;
|
||||
byte refit_cost;
|
||||
byte misc_flags;
|
||||
byte callbackmask;
|
||||
} EngineInfo;
|
||||
|
||||
typedef struct Engine {
|
||||
Date intro_date;
|
||||
Date age;
|
||||
uint16 reliability;
|
||||
uint16 reliability_spd_dec;
|
||||
uint16 reliability_start, reliability_max, reliability_final;
|
||||
uint16 duration_phase_1, duration_phase_2, duration_phase_3;
|
||||
byte lifelength;
|
||||
byte flags;
|
||||
byte preview_player;
|
||||
byte preview_wait;
|
||||
byte railtype;
|
||||
byte player_avail;
|
||||
byte type; // type, ie VEH_Road, VEH_Train, etc. Same as in vehicle.h
|
||||
} Engine;
|
||||
|
||||
/**
|
||||
* EngineInfo.misc_flags is a bitmask, with the following values
|
||||
*/
|
||||
enum {
|
||||
EF_RAIL_TILTS = 0, ///< Rail vehicle tilts in curves (unsupported)
|
||||
EF_ROAD_TRAM = 0, ///< Road vehicle is a tram/light rail vehicle (unsup)
|
||||
EF_USES_2CC = 1, ///< Vehicle uses two company colours
|
||||
EF_RAIL_IS_MU = 2, ///< Rail vehicle is a multiple-unit (DMU/EMU)
|
||||
};
|
||||
|
||||
enum {
|
||||
RVI_MULTIHEAD = 1,
|
||||
RVI_WAGON = 2,
|
||||
};
|
||||
|
||||
enum {
|
||||
NUM_VEHICLE_TYPES = 6
|
||||
};
|
||||
|
||||
enum {
|
||||
INVALID_ENGINE = 0xFFFF,
|
||||
};
|
||||
|
||||
void AddTypeToEngines(void);
|
||||
void StartupEngines(void);
|
||||
|
||||
|
||||
void DrawTrainEngine(int x, int y, EngineID engine, uint32 image_ormod);
|
||||
void DrawRoadVehEngine(int x, int y, EngineID engine, uint32 image_ormod);
|
||||
void DrawShipEngine(int x, int y, EngineID engine, uint32 image_ormod);
|
||||
void DrawAircraftEngine(int x, int y, EngineID engine, uint32 image_ormod);
|
||||
|
||||
void LoadCustomEngineNames(void);
|
||||
void DeleteCustomEngineNames(void);
|
||||
|
||||
bool IsEngineBuildable(EngineID engine, byte type, PlayerID player);
|
||||
|
||||
enum {
|
||||
NUM_NORMAL_RAIL_ENGINES = 54,
|
||||
NUM_MONORAIL_ENGINES = 30,
|
||||
NUM_MAGLEV_ENGINES = 32,
|
||||
NUM_TRAIN_ENGINES = NUM_NORMAL_RAIL_ENGINES + NUM_MONORAIL_ENGINES + NUM_MAGLEV_ENGINES,
|
||||
NUM_ROAD_ENGINES = 88,
|
||||
NUM_SHIP_ENGINES = 11,
|
||||
NUM_AIRCRAFT_ENGINES = 41,
|
||||
TOTAL_NUM_ENGINES = NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES + NUM_AIRCRAFT_ENGINES,
|
||||
AIRCRAFT_ENGINES_INDEX = NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES,
|
||||
SHIP_ENGINES_INDEX = NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES,
|
||||
ROAD_ENGINES_INDEX = NUM_TRAIN_ENGINES,
|
||||
};
|
||||
VARDEF Engine _engines[TOTAL_NUM_ENGINES];
|
||||
#define FOR_ALL_ENGINES(e) for (e = _engines; e != endof(_engines); e++)
|
||||
|
||||
static inline Engine* GetEngine(EngineID i)
|
||||
{
|
||||
assert(i < lengthof(_engines));
|
||||
return &_engines[i];
|
||||
}
|
||||
|
||||
VARDEF StringID _engine_name_strings[TOTAL_NUM_ENGINES];
|
||||
|
||||
static inline bool IsEngineIndex(uint index)
|
||||
{
|
||||
return index < TOTAL_NUM_ENGINES;
|
||||
}
|
||||
|
||||
/* Access Vehicle Data */
|
||||
//#include "table/engines.h"
|
||||
extern const EngineInfo orig_engine_info[TOTAL_NUM_ENGINES];
|
||||
extern const RailVehicleInfo orig_rail_vehicle_info[NUM_TRAIN_ENGINES];
|
||||
extern const ShipVehicleInfo orig_ship_vehicle_info[NUM_SHIP_ENGINES];
|
||||
extern const AircraftVehicleInfo orig_aircraft_vehicle_info[NUM_AIRCRAFT_ENGINES];
|
||||
extern const RoadVehicleInfo orig_road_vehicle_info[NUM_ROAD_ENGINES];
|
||||
|
||||
extern EngineInfo _engine_info[TOTAL_NUM_ENGINES];
|
||||
extern RailVehicleInfo _rail_vehicle_info[NUM_TRAIN_ENGINES];
|
||||
extern ShipVehicleInfo _ship_vehicle_info[NUM_SHIP_ENGINES];
|
||||
extern AircraftVehicleInfo _aircraft_vehicle_info[NUM_AIRCRAFT_ENGINES];
|
||||
extern RoadVehicleInfo _road_vehicle_info[NUM_ROAD_ENGINES];
|
||||
|
||||
static inline const EngineInfo *EngInfo(EngineID e)
|
||||
{
|
||||
assert(e < lengthof(_engine_info));
|
||||
return &_engine_info[e];
|
||||
}
|
||||
|
||||
static inline const RailVehicleInfo* RailVehInfo(EngineID e)
|
||||
{
|
||||
assert(e < lengthof(_rail_vehicle_info));
|
||||
return &_rail_vehicle_info[e];
|
||||
}
|
||||
|
||||
static inline const ShipVehicleInfo* ShipVehInfo(EngineID e)
|
||||
{
|
||||
assert(e >= SHIP_ENGINES_INDEX && e < SHIP_ENGINES_INDEX + lengthof(_ship_vehicle_info));
|
||||
return &_ship_vehicle_info[e - SHIP_ENGINES_INDEX];
|
||||
}
|
||||
|
||||
static inline const AircraftVehicleInfo* AircraftVehInfo(EngineID e)
|
||||
{
|
||||
assert(e >= AIRCRAFT_ENGINES_INDEX && e < AIRCRAFT_ENGINES_INDEX + lengthof(_aircraft_vehicle_info));
|
||||
return &_aircraft_vehicle_info[e - AIRCRAFT_ENGINES_INDEX];
|
||||
}
|
||||
|
||||
static inline const RoadVehicleInfo* RoadVehInfo(EngineID e)
|
||||
{
|
||||
assert(e >= ROAD_ENGINES_INDEX && e < ROAD_ENGINES_INDEX + lengthof(_road_vehicle_info));
|
||||
return &_road_vehicle_info[e - ROAD_ENGINES_INDEX];
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Engine Replacement stuff
|
||||
************************************************************************/
|
||||
|
||||
/**
|
||||
* Struct to store engine replacements. DO NOT USE outside of engine.c. Is
|
||||
* placed here so the only exception to this rule, the saveload code, can use
|
||||
* it.
|
||||
*/
|
||||
struct EngineRenew {
|
||||
EngineRenewID index;
|
||||
EngineID from;
|
||||
EngineID to;
|
||||
struct EngineRenew *next;
|
||||
};
|
||||
|
||||
typedef struct EngineRenew EngineRenew;
|
||||
|
||||
/**
|
||||
* Memory pool for engine renew elements. DO NOT USE outside of engine.c. Is
|
||||
* placed here so the only exception to this rule, the saveload code, can use
|
||||
* it.
|
||||
*/
|
||||
DECLARE_OLD_POOL(EngineRenew, EngineRenew, 3, 8000)
|
||||
|
||||
/**
|
||||
* Check if a EngineRenew really exists.
|
||||
*/
|
||||
static inline bool IsValidEngineRenew(const EngineRenew *er)
|
||||
{
|
||||
return er->from != INVALID_ENGINE;
|
||||
}
|
||||
|
||||
static inline void DeleteEngineRenew(EngineRenew *er)
|
||||
{
|
||||
er->from = INVALID_ENGINE;
|
||||
}
|
||||
|
||||
#define FOR_ALL_ENGINE_RENEWS_FROM(er, start) for (er = GetEngineRenew(start); er != NULL; er = (er->index + 1U < GetEngineRenewPoolSize()) ? GetEngineRenew(er->index + 1U) : NULL) if (er->from != INVALID_ENGINE) if (IsValidEngineRenew(er))
|
||||
#define FOR_ALL_ENGINE_RENEWS(er) FOR_ALL_ENGINE_RENEWS_FROM(er, 0)
|
||||
|
||||
|
||||
/**
|
||||
* A list to group EngineRenew directives together (such as per-player).
|
||||
*/
|
||||
typedef EngineRenew* EngineRenewList;
|
||||
|
||||
/**
|
||||
* Remove all engine replacement settings for the player.
|
||||
* @param er The renewlist for a given player.
|
||||
* @return The new renewlist for the player.
|
||||
*/
|
||||
void RemoveAllEngineReplacement(EngineRenewList* erl);
|
||||
|
||||
/**
|
||||
* Retrieve the engine replacement in a given renewlist for an original engine type.
|
||||
* @param erl The renewlist to search in.
|
||||
* @param engine Engine type to be replaced.
|
||||
* @return The engine type to replace with, or INVALID_ENGINE if no
|
||||
* replacement is in the list.
|
||||
*/
|
||||
EngineID EngineReplacement(EngineRenewList erl, EngineID engine);
|
||||
|
||||
/**
|
||||
* Add an engine replacement to the given renewlist.
|
||||
* @param erl The renewlist to add to.
|
||||
* @param old_engine The original engine type.
|
||||
* @param new_engine The replacement engine type.
|
||||
* @param flags The calling command flags.
|
||||
* @return 0 on success, CMD_ERROR on failure.
|
||||
*/
|
||||
int32 AddEngineReplacement(EngineRenewList* erl, EngineID old_engine, EngineID new_engine, uint32 flags);
|
||||
|
||||
/**
|
||||
* Remove an engine replacement from a given renewlist.
|
||||
* @param erl The renewlist from which to remove the replacement
|
||||
* @param engine The original engine type.
|
||||
* @param flags The calling command flags.
|
||||
* @return 0 on success, CMD_ERROR on failure.
|
||||
*/
|
||||
int32 RemoveEngineReplacement(EngineRenewList* erl, EngineID engine, uint32 flags);
|
||||
|
||||
/* Engine list manipulators - current implementation is only C wrapper of CBlobT<EngineID> class (helpers.cpp) */
|
||||
void EngList_Create(EngineList *el); ///< Creates engine list
|
||||
void EngList_Destroy(EngineList *el); ///< Deallocate and destroy engine list
|
||||
uint EngList_Count(const EngineList *el); ///< Returns number of items in the engine list
|
||||
void EngList_Add(EngineList *el, EngineID eid); ///< Append one item at the end of engine list
|
||||
EngineID* EngList_Items(EngineList *el); ///< Returns engine list items as C array
|
||||
void EngList_RemoveAll(EngineList *el); ///< Removes all items from engine list
|
||||
typedef int CDECL EngList_SortTypeFunction(const void*, const void*); ///< argument type for EngList_Sort()
|
||||
void EngList_Sort(EngineList *el, EngList_SortTypeFunction compare); ///< qsort of the engine list
|
||||
void EngList_SortPartial(EngineList *el, EngList_SortTypeFunction compare, uint begin, uint num_items); ///< qsort of specified portion of the engine list
|
||||
|
||||
#endif /* ENGINE_H */
|
||||
285
src/engine_gui.c
Normal file
285
src/engine_gui.c
Normal file
@@ -0,0 +1,285 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "table/strings.h"
|
||||
#include "table/sprites.h"
|
||||
#include "functions.h"
|
||||
#include "window.h"
|
||||
#include "gui.h"
|
||||
#include "viewport.h"
|
||||
#include "gfx.h"
|
||||
#include "engine.h"
|
||||
#include "command.h"
|
||||
#include "news.h"
|
||||
#include "variables.h"
|
||||
#include "newgrf_engine.h"
|
||||
|
||||
|
||||
static StringID GetEngineCategoryName(EngineID engine)
|
||||
{
|
||||
if (engine < NUM_TRAIN_ENGINES) {
|
||||
switch (GetEngine(engine)->railtype) {
|
||||
case RAILTYPE_RAIL: return STR_8102_RAILROAD_LOCOMOTIVE;
|
||||
case RAILTYPE_ELECTRIC: return STR_8102_RAILROAD_LOCOMOTIVE;
|
||||
case RAILTYPE_MONO: return STR_8106_MONORAIL_LOCOMOTIVE;
|
||||
case RAILTYPE_MAGLEV: return STR_8107_MAGLEV_LOCOMOTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
if (engine < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES)
|
||||
return STR_8103_ROAD_VEHICLE;
|
||||
|
||||
if (engine < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES)
|
||||
return STR_8105_SHIP;
|
||||
|
||||
return STR_8104_AIRCRAFT;
|
||||
}
|
||||
|
||||
static const Widget _engine_preview_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 5, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 5, 11, 299, 0, 13, STR_8100_MESSAGE_FROM_VEHICLE_MANUFACTURE, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 5, 0, 299, 14, 191, 0x0, STR_NULL},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 5, 85, 144, 172, 183, STR_00C9_NO, STR_NULL},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 5, 155, 214, 172, 183, STR_00C8_YES, STR_NULL},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
typedef void DrawEngineProc(int x, int y, EngineID engine, uint32 image_ormod);
|
||||
typedef void DrawEngineInfoProc(EngineID, int x, int y, int maxw);
|
||||
|
||||
typedef struct DrawEngineInfo {
|
||||
DrawEngineProc *engine_proc;
|
||||
DrawEngineInfoProc *info_proc;
|
||||
} DrawEngineInfo;
|
||||
|
||||
static void DrawTrainEngineInfo(EngineID engine, int x, int y, int maxw);
|
||||
static void DrawRoadVehEngineInfo(EngineID engine, int x, int y, int maxw);
|
||||
static void DrawShipEngineInfo(EngineID engine, int x, int y, int maxw);
|
||||
static void DrawAircraftEngineInfo(EngineID engine, int x, int y, int maxw);
|
||||
|
||||
static const DrawEngineInfo _draw_engine_list[4] = {
|
||||
{DrawTrainEngine,DrawTrainEngineInfo},
|
||||
{DrawRoadVehEngine,DrawRoadVehEngineInfo},
|
||||
{DrawShipEngine,DrawShipEngineInfo},
|
||||
{DrawAircraftEngine,DrawAircraftEngineInfo},
|
||||
};
|
||||
|
||||
static void EnginePreviewWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_PAINT: {
|
||||
EngineID engine = w->window_number;
|
||||
const DrawEngineInfo* dei;
|
||||
int width;
|
||||
|
||||
DrawWindowWidgets(w);
|
||||
|
||||
SetDParam(0, GetEngineCategoryName(engine));
|
||||
DrawStringMultiCenter(150, 44, STR_8101_WE_HAVE_JUST_DESIGNED_A, 296);
|
||||
|
||||
DrawStringCentered(w->width >> 1, 80, GetCustomEngineName(engine), 0x10);
|
||||
|
||||
(dei = _draw_engine_list,engine < NUM_TRAIN_ENGINES) ||
|
||||
(dei++,engine < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES) ||
|
||||
(dei++,engine < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES) ||
|
||||
(dei++, true);
|
||||
|
||||
width = w->width;
|
||||
dei->engine_proc(width >> 1, 100, engine, 0);
|
||||
dei->info_proc(engine, width >> 1, 130, width - 52);
|
||||
break;
|
||||
}
|
||||
|
||||
case WE_CLICK:
|
||||
switch (e->we.click.widget) {
|
||||
case 4:
|
||||
DoCommandP(0, w->window_number, 0, NULL, CMD_WANT_ENGINE_PREVIEW);
|
||||
/* Fallthrough */
|
||||
case 3:
|
||||
DeleteWindow(w);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const WindowDesc _engine_preview_desc = {
|
||||
WDP_CENTER, WDP_CENTER, 300, 192,
|
||||
WC_ENGINE_PREVIEW,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_engine_preview_widgets,
|
||||
EnginePreviewWndProc
|
||||
};
|
||||
|
||||
|
||||
void ShowEnginePreviewWindow(EngineID engine)
|
||||
{
|
||||
AllocateWindowDescFront(&_engine_preview_desc, engine);
|
||||
}
|
||||
|
||||
static void DrawTrainEngineInfo(EngineID engine, int x, int y, int maxw)
|
||||
{
|
||||
const RailVehicleInfo *rvi = RailVehInfo(engine);
|
||||
uint multihead = (rvi->flags & RVI_MULTIHEAD) ? 1 : 0;
|
||||
|
||||
SetDParam(0, (_price.build_railvehicle >> 3) * rvi->base_cost >> 5);
|
||||
SetDParam(2, rvi->max_speed);
|
||||
SetDParam(3, rvi->power << multihead);
|
||||
SetDParam(1, rvi->weight << multihead);
|
||||
|
||||
SetDParam(4, rvi->running_cost_base * _price.running_rail[rvi->running_cost_class] >> 8 << multihead);
|
||||
|
||||
if (rvi->capacity != 0) {
|
||||
SetDParam(5, rvi->cargo_type);
|
||||
SetDParam(6, rvi->capacity << multihead);
|
||||
} else {
|
||||
SetDParam(5, CT_INVALID);
|
||||
}
|
||||
DrawStringMultiCenter(x, y, STR_VEHICLE_INFO_COST_WEIGHT_SPEED_POWER, maxw);
|
||||
}
|
||||
|
||||
void DrawNewsNewTrainAvail(Window *w)
|
||||
{
|
||||
EngineID engine;
|
||||
|
||||
DrawNewsBorder(w);
|
||||
|
||||
engine = WP(w,news_d).ni->string_id;
|
||||
SetDParam(0, GetEngineCategoryName(engine));
|
||||
DrawStringMultiCenter(w->width >> 1, 20, STR_8859_NEW_NOW_AVAILABLE, w->width - 2);
|
||||
|
||||
GfxFillRect(25, 56, w->width - 25, w->height - 2, 10);
|
||||
|
||||
SetDParam(0, GetCustomEngineName(engine));
|
||||
DrawStringMultiCenter(w->width >> 1, 57, STR_885A, w->width - 2);
|
||||
|
||||
DrawTrainEngine(w->width >> 1, 88, engine, 0);
|
||||
GfxFillRect(25, 56, w->width - 56, 112, 0x323 | USE_COLORTABLE);
|
||||
DrawTrainEngineInfo(engine, w->width >> 1, 129, w->width - 52);
|
||||
}
|
||||
|
||||
StringID GetNewsStringNewTrainAvail(const NewsItem *ni)
|
||||
{
|
||||
EngineID engine = ni->string_id;
|
||||
SetDParam(0, STR_8859_NEW_NOW_AVAILABLE);
|
||||
SetDParam(1, GetEngineCategoryName(engine));
|
||||
SetDParam(2, GetCustomEngineName(engine));
|
||||
return STR_02B6;
|
||||
}
|
||||
|
||||
static void DrawAircraftEngineInfo(EngineID engine, int x, int y, int maxw)
|
||||
{
|
||||
const AircraftVehicleInfo *avi = AircraftVehInfo(engine);
|
||||
SetDParam(0, (_price.aircraft_base >> 3) * avi->base_cost >> 5);
|
||||
SetDParam(1, avi->max_speed * 128 / 10);
|
||||
SetDParam(2, avi->passenger_capacity);
|
||||
SetDParam(3, avi->mail_capacity);
|
||||
SetDParam(4, avi->running_cost * _price.aircraft_running >> 8);
|
||||
|
||||
DrawStringMultiCenter(x, y, STR_A02E_COST_MAX_SPEED_CAPACITY, maxw);
|
||||
}
|
||||
|
||||
void DrawNewsNewAircraftAvail(Window *w)
|
||||
{
|
||||
EngineID engine;
|
||||
|
||||
DrawNewsBorder(w);
|
||||
|
||||
engine = WP(w,news_d).ni->string_id;
|
||||
|
||||
DrawStringMultiCenter(w->width >> 1, 20, STR_A02C_NEW_AIRCRAFT_NOW_AVAILABLE, w->width - 2);
|
||||
GfxFillRect(25, 56, w->width - 25, w->height - 2, 10);
|
||||
|
||||
SetDParam(0, GetCustomEngineName(engine));
|
||||
DrawStringMultiCenter(w->width >> 1, 57, STR_A02D, w->width - 2);
|
||||
|
||||
DrawAircraftEngine(w->width >> 1, 93, engine, 0);
|
||||
GfxFillRect(25, 56, w->width - 56, 110, 0x323 | USE_COLORTABLE);
|
||||
DrawAircraftEngineInfo(engine, w->width >> 1, 131, w->width - 52);
|
||||
}
|
||||
|
||||
StringID GetNewsStringNewAircraftAvail(const NewsItem *ni)
|
||||
{
|
||||
EngineID engine = ni->string_id;
|
||||
SetDParam(0, STR_A02C_NEW_AIRCRAFT_NOW_AVAILABLE);
|
||||
SetDParam(1, GetCustomEngineName(engine));
|
||||
return STR_02B6;
|
||||
}
|
||||
|
||||
static void DrawRoadVehEngineInfo(EngineID engine, int x, int y, int maxw)
|
||||
{
|
||||
const RoadVehicleInfo *rvi = RoadVehInfo(engine);
|
||||
|
||||
SetDParam(0, (_price.roadveh_base >> 3) * rvi->base_cost >> 5);
|
||||
SetDParam(1, rvi->max_speed / 2);
|
||||
SetDParam(2, rvi->running_cost * _price.roadveh_running >> 8);
|
||||
SetDParam(3, rvi->cargo_type);
|
||||
SetDParam(4, rvi->capacity);
|
||||
|
||||
DrawStringMultiCenter(x, y, STR_902A_COST_SPEED_RUNNING_COST, maxw);
|
||||
}
|
||||
|
||||
void DrawNewsNewRoadVehAvail(Window *w)
|
||||
{
|
||||
EngineID engine;
|
||||
|
||||
DrawNewsBorder(w);
|
||||
|
||||
engine = WP(w,news_d).ni->string_id;
|
||||
DrawStringMultiCenter(w->width >> 1, 20, STR_9028_NEW_ROAD_VEHICLE_NOW_AVAILABLE, w->width - 2);
|
||||
GfxFillRect(25, 56, w->width - 25, w->height - 2, 10);
|
||||
|
||||
SetDParam(0, GetCustomEngineName(engine));
|
||||
DrawStringMultiCenter(w->width >> 1, 57, STR_9029, w->width - 2);
|
||||
|
||||
DrawRoadVehEngine(w->width >> 1, 88, engine, 0);
|
||||
GfxFillRect(25, 56, w->width - 56, 112, 0x323 | USE_COLORTABLE);
|
||||
DrawRoadVehEngineInfo(engine, w->width >> 1, 129, w->width - 52);
|
||||
}
|
||||
|
||||
StringID GetNewsStringNewRoadVehAvail(const NewsItem *ni)
|
||||
{
|
||||
EngineID engine = ni->string_id;
|
||||
SetDParam(0, STR_9028_NEW_ROAD_VEHICLE_NOW_AVAILABLE);
|
||||
SetDParam(1, GetCustomEngineName(engine));
|
||||
return STR_02B6;
|
||||
}
|
||||
|
||||
static void DrawShipEngineInfo(EngineID engine, int x, int y, int maxw)
|
||||
{
|
||||
const ShipVehicleInfo *svi = ShipVehInfo(engine);
|
||||
SetDParam(0, svi->base_cost * (_price.ship_base >> 3) >> 5);
|
||||
SetDParam(1, svi->max_speed / 2);
|
||||
SetDParam(2, svi->cargo_type);
|
||||
SetDParam(3, svi->capacity);
|
||||
SetDParam(4, svi->running_cost * _price.ship_running >> 8);
|
||||
DrawStringMultiCenter(x, y, STR_982E_COST_MAX_SPEED_CAPACITY, maxw);
|
||||
}
|
||||
|
||||
void DrawNewsNewShipAvail(Window *w)
|
||||
{
|
||||
EngineID engine;
|
||||
|
||||
DrawNewsBorder(w);
|
||||
|
||||
engine = WP(w,news_d).ni->string_id;
|
||||
|
||||
DrawStringMultiCenter(w->width >> 1, 20, STR_982C_NEW_SHIP_NOW_AVAILABLE, w->width - 2);
|
||||
GfxFillRect(25, 56, w->width - 25, w->height - 2, 10);
|
||||
|
||||
SetDParam(0, GetCustomEngineName(engine));
|
||||
DrawStringMultiCenter(w->width >> 1, 57, STR_982D, w->width - 2);
|
||||
|
||||
DrawShipEngine(w->width >> 1, 93, engine, 0);
|
||||
GfxFillRect(25, 56, w->width - 56, 110, 0x323 | USE_COLORTABLE);
|
||||
DrawShipEngineInfo(engine, w->width >> 1, 131, w->width - 52);
|
||||
}
|
||||
|
||||
StringID GetNewsStringNewShipAvail(const NewsItem *ni)
|
||||
{
|
||||
EngineID engine = ni->string_id;
|
||||
SetDParam(0, STR_982C_NEW_SHIP_NOW_AVAILABLE);
|
||||
SetDParam(1, GetCustomEngineName(engine));
|
||||
return STR_02B6;
|
||||
}
|
||||
151
src/fileio.c
Normal file
151
src/fileio.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "fileio.h"
|
||||
#include "functions.h"
|
||||
#include "string.h"
|
||||
#include "macros.h"
|
||||
#include "variables.h"
|
||||
|
||||
/*************************************************/
|
||||
/* FILE IO ROUTINES ******************************/
|
||||
/*************************************************/
|
||||
|
||||
#define FIO_BUFFER_SIZE 512
|
||||
|
||||
typedef struct {
|
||||
byte *buffer, *buffer_end; ///< position pointer in local buffer and last valid byte of buffer
|
||||
uint32 pos; ///< current (system) position in file
|
||||
FILE *cur_fh; ///< current file handle
|
||||
FILE *handles[64]; ///< array of file handles we can have open
|
||||
byte buffer_start[FIO_BUFFER_SIZE]; ///< local buffer when read from file
|
||||
} Fio;
|
||||
|
||||
static Fio _fio;
|
||||
|
||||
// Get current position in file
|
||||
uint32 FioGetPos(void)
|
||||
{
|
||||
return _fio.pos + (_fio.buffer - _fio.buffer_start) - FIO_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
void FioSeekTo(uint32 pos, int mode)
|
||||
{
|
||||
if (mode == SEEK_CUR) pos += FioGetPos();
|
||||
_fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE;
|
||||
_fio.pos = pos;
|
||||
fseek(_fio.cur_fh, _fio.pos, SEEK_SET);
|
||||
}
|
||||
|
||||
// Seek to a file and a position
|
||||
void FioSeekToFile(uint32 pos)
|
||||
{
|
||||
FILE *f = _fio.handles[pos >> 24];
|
||||
assert(f != NULL);
|
||||
_fio.cur_fh = f;
|
||||
FioSeekTo(GB(pos, 0, 24), SEEK_SET);
|
||||
}
|
||||
|
||||
byte FioReadByte(void)
|
||||
{
|
||||
if (_fio.buffer == _fio.buffer_end) {
|
||||
_fio.pos += FIO_BUFFER_SIZE;
|
||||
fread(_fio.buffer = _fio.buffer_start, 1, FIO_BUFFER_SIZE, _fio.cur_fh);
|
||||
}
|
||||
return *_fio.buffer++;
|
||||
}
|
||||
|
||||
void FioSkipBytes(int n)
|
||||
{
|
||||
for (;;) {
|
||||
int m = min(_fio.buffer_end - _fio.buffer, n);
|
||||
_fio.buffer += m;
|
||||
n -= m;
|
||||
if (n == 0) break;
|
||||
FioReadByte();
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 FioReadWord(void)
|
||||
{
|
||||
byte b = FioReadByte();
|
||||
return (FioReadByte() << 8) | b;
|
||||
}
|
||||
|
||||
uint32 FioReadDword(void)
|
||||
{
|
||||
uint b = FioReadWord();
|
||||
return (FioReadWord() << 16) | b;
|
||||
}
|
||||
|
||||
void FioReadBlock(void *ptr, uint size)
|
||||
{
|
||||
FioSeekTo(FioGetPos(), SEEK_SET);
|
||||
_fio.pos += size;
|
||||
fread(ptr, 1, size, _fio.cur_fh);
|
||||
}
|
||||
|
||||
static inline void FioCloseFile(int slot)
|
||||
{
|
||||
if (_fio.handles[slot] != NULL) {
|
||||
fclose(_fio.handles[slot]);
|
||||
_fio.handles[slot] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void FioCloseAll(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i != lengthof(_fio.handles); i++)
|
||||
FioCloseFile(i);
|
||||
}
|
||||
|
||||
bool FioCheckFileExists(const char *filename)
|
||||
{
|
||||
FILE *f = FioFOpenFile(filename);
|
||||
if (f == NULL) return false;
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
FILE *FioFOpenFile(const char *filename)
|
||||
{
|
||||
FILE *f;
|
||||
char buf[MAX_PATH];
|
||||
|
||||
snprintf(buf, lengthof(buf), "%s%s", _paths.data_dir, filename);
|
||||
|
||||
f = fopen(buf, "rb");
|
||||
#if !defined(WIN32)
|
||||
if (f == NULL) {
|
||||
strtolower(buf + strlen(_paths.data_dir) - 1);
|
||||
f = fopen(buf, "rb");
|
||||
|
||||
#if defined SECOND_DATA_DIR
|
||||
// tries in the 2nd data directory
|
||||
if (f == NULL) {
|
||||
snprintf(buf, lengthof(buf), "%s%s", _paths.second_data_dir, filename);
|
||||
strtolower(buf + strlen(_paths.second_data_dir) - 1);
|
||||
f = fopen(buf, "rb");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void FioOpenFile(int slot, const char *filename)
|
||||
{
|
||||
FILE *f = FioFOpenFile(filename);
|
||||
|
||||
if (f == NULL) error("Cannot open file '%s%s'", _paths.data_dir, filename);
|
||||
|
||||
FioCloseFile(slot); // if file was opened before, close it
|
||||
_fio.handles[slot] = f;
|
||||
FioSeekToFile(slot << 24);
|
||||
}
|
||||
19
src/fileio.h
Normal file
19
src/fileio.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef FILEIO_H
|
||||
#define FILEIO_H
|
||||
|
||||
void FioSeekTo(uint32 pos, int mode);
|
||||
void FioSeekToFile(uint32 pos);
|
||||
uint32 FioGetPos(void);
|
||||
byte FioReadByte(void);
|
||||
uint16 FioReadWord(void);
|
||||
uint32 FioReadDword(void);
|
||||
void FioCloseAll(void);
|
||||
FILE *FioFOpenFile(const char *filename);
|
||||
void FioOpenFile(int slot, const char *filename);
|
||||
void FioReadBlock(void *ptr, uint size);
|
||||
void FioSkipBytes(int n);
|
||||
bool FioCheckFileExists(const char *filename);
|
||||
|
||||
#endif /* FILEIO_H */
|
||||
412
src/fios.c
Normal file
412
src/fios.c
Normal file
@@ -0,0 +1,412 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file fios.c
|
||||
* This file contains functions for building file lists for the save/load dialogs.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "hal.h"
|
||||
#include "string.h"
|
||||
#include "variables.h"
|
||||
#include "functions.h"
|
||||
#include "heightmap.h"
|
||||
#include "table/strings.h"
|
||||
#include "fios.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef WIN32
|
||||
# include <io.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
# include <dirent.h>
|
||||
#endif /* WIN32 */
|
||||
|
||||
/* Variables to display file lists */
|
||||
int _fios_num;
|
||||
|
||||
static char *_fios_path;
|
||||
static FiosItem *_fios_items;
|
||||
static int _fios_count, _fios_alloc;
|
||||
|
||||
/* OS-specific functions are taken from their respective files (win32/unix/os2 .c) */
|
||||
extern bool FiosIsRoot(const char *path);
|
||||
extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
|
||||
extern void FiosGetDrives(void);
|
||||
extern bool FiosGetDiskFreeSpace(const char *path, uint32 *tot);
|
||||
|
||||
/* get the name of an oldstyle savegame */
|
||||
extern void GetOldSaveGameName(char *title, const char *path, const char *file);
|
||||
|
||||
/**
|
||||
* Allocate a new FiosItem.
|
||||
* @return A pointer to the newly allocated FiosItem.
|
||||
*/
|
||||
FiosItem *FiosAlloc(void)
|
||||
{
|
||||
if (_fios_count == _fios_alloc) {
|
||||
_fios_alloc += 256;
|
||||
_fios_items = realloc(_fios_items, _fios_alloc * sizeof(FiosItem));
|
||||
}
|
||||
return &_fios_items[_fios_count++];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two FiosItem's. Used with qsort when sorting the file list.
|
||||
* @param a A pointer to the first FiosItem to compare.
|
||||
* @param a A pointer to the second FiosItem to compare.
|
||||
* @return -1, 0 or 1, depending on how the two items should be sorted.
|
||||
*/
|
||||
int CDECL compare_FiosItems(const void *a, const void *b)
|
||||
{
|
||||
const FiosItem *da = (const FiosItem *)a;
|
||||
const FiosItem *db = (const FiosItem *)b;
|
||||
int r;
|
||||
|
||||
if (_savegame_sort_order & SORT_BY_NAME) {
|
||||
r = strcasecmp(da->title, db->title);
|
||||
} else {
|
||||
r = da->mtime < db->mtime ? -1 : 1;
|
||||
}
|
||||
|
||||
if (_savegame_sort_order & SORT_DESCENDING) r = -r;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the list of savegames
|
||||
*/
|
||||
void FiosFreeSavegameList(void)
|
||||
{
|
||||
free(_fios_items);
|
||||
_fios_items = NULL;
|
||||
_fios_alloc = _fios_count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get descriptive texts. Returns the path and free space
|
||||
* left on the device
|
||||
* @param path string describing the path
|
||||
* @param total_free total free space in megabytes, optional (can be NULL)
|
||||
* @return StringID describing the path (free space or failure)
|
||||
*/
|
||||
StringID FiosGetDescText(const char **path, uint32 *total_free)
|
||||
{
|
||||
*path = _fios_path;
|
||||
return FiosGetDiskFreeSpace(*path, total_free) ? STR_4005_BYTES_FREE : STR_4006_UNABLE_TO_READ_DRIVE;
|
||||
}
|
||||
|
||||
/* Browse to a new path based on the passed FiosItem struct
|
||||
* @param *item FiosItem object telling us what to do
|
||||
* @return a string if we have given a file as a target, otherwise NULL */
|
||||
char *FiosBrowseTo(const FiosItem *item)
|
||||
{
|
||||
char *s;
|
||||
char *path = _fios_path;
|
||||
|
||||
switch (item->type) {
|
||||
#if defined(WIN32) || defined(__OS2__)
|
||||
case FIOS_TYPE_DRIVE: sprintf(path, "%c:" PATHSEP, item->title[0]); break;
|
||||
#endif
|
||||
|
||||
case FIOS_TYPE_PARENT:
|
||||
/* Check for possible NULL ptr (not required for UNIXes, but AmigaOS-alikes) */
|
||||
if ((s = strrchr(path, PATHSEPCHAR)) != NULL) {
|
||||
s[1] = '\0'; // go up a directory
|
||||
if (!FiosIsRoot(path)) s[0] = '\0';
|
||||
}
|
||||
#if defined(__MORPHOS__) || defined(__AMIGAOS__)
|
||||
/* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
|
||||
else if ((s = strrchr(path, ':')) != NULL) s[1] = '\0';
|
||||
#endif
|
||||
break;
|
||||
|
||||
case FIOS_TYPE_DIR:
|
||||
if (!FiosIsRoot(path)) strcat(path, PATHSEP);
|
||||
strcat(path, item->name);
|
||||
break;
|
||||
|
||||
case FIOS_TYPE_DIRECT:
|
||||
sprintf(path, "%s" PATHSEP, item->name);
|
||||
s = strrchr(path, PATHSEPCHAR);
|
||||
if (s != NULL && s[1] == '\0') s[0] = '\0'; // strip trailing slash
|
||||
break;
|
||||
|
||||
case FIOS_TYPE_FILE:
|
||||
case FIOS_TYPE_OLDFILE:
|
||||
case FIOS_TYPE_SCENARIO:
|
||||
case FIOS_TYPE_OLD_SCENARIO:
|
||||
case FIOS_TYPE_PNG:
|
||||
case FIOS_TYPE_BMP:
|
||||
{
|
||||
static char str_buffr[512];
|
||||
|
||||
#if defined(__MORPHOS__) || defined(__AMIGAOS__)
|
||||
/* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
|
||||
if (FiosIsRoot(path)) {
|
||||
snprintf(str_buffr, lengthof(str_buffr), "%s:%s", path, item->name);
|
||||
} else // XXX - only next line!
|
||||
#endif
|
||||
snprintf(str_buffr, lengthof(str_buffr), "%s" PATHSEP "%s", path, item->name);
|
||||
|
||||
return str_buffr;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void FiosMakeSavegameName(char *buf, const char *name, size_t size)
|
||||
{
|
||||
const char *extension, *period;
|
||||
|
||||
extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav";
|
||||
|
||||
/* Don't append the extension if it is already there */
|
||||
period = strrchr(name, '.');
|
||||
if (period != NULL && strcasecmp(period, extension) == 0) extension = "";
|
||||
|
||||
snprintf(buf, size, "%s" PATHSEP "%s%s", _fios_path, name, extension);
|
||||
}
|
||||
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
# define unlink _wunlink
|
||||
#endif
|
||||
|
||||
bool FiosDelete(const char *name)
|
||||
{
|
||||
char filename[512];
|
||||
|
||||
FiosMakeSavegameName(filename, name, lengthof(filename));
|
||||
return unlink(OTTD2FS(filename)) == 0;
|
||||
}
|
||||
|
||||
bool FileExists(const char *filename)
|
||||
{
|
||||
return access(filename, 0) == 0;
|
||||
}
|
||||
|
||||
typedef byte fios_getlist_callback_proc(int mode, const char *filename, const char *ext, char *title);
|
||||
|
||||
/** Create a list of the files in a directory, according to some arbitrary rule.
|
||||
* @param num Will be filled with the amount of items.
|
||||
* @param mode The mode we are in. Some modes don't allow 'parent'.
|
||||
* @param callback The function that is called where you need to do the filtering.
|
||||
* @return Return the list of files. */
|
||||
static FiosItem *FiosGetFileList(int mode, fios_getlist_callback_proc *callback_proc)
|
||||
{
|
||||
struct stat sb;
|
||||
struct dirent *dirent;
|
||||
DIR *dir;
|
||||
FiosItem *fios;
|
||||
int sort_start;
|
||||
|
||||
/* A parent directory link exists if we are not in the root directory */
|
||||
if (!FiosIsRoot(_fios_path) && mode != SLD_NEW_GAME) {
|
||||
fios = FiosAlloc();
|
||||
fios->type = FIOS_TYPE_PARENT;
|
||||
fios->mtime = 0;
|
||||
ttd_strlcpy(fios->name, "..", lengthof(fios->name));
|
||||
ttd_strlcpy(fios->title, ".. (Parent directory)", lengthof(fios->title));
|
||||
}
|
||||
|
||||
/* Show subdirectories */
|
||||
if (mode != SLD_NEW_GAME && (dir = opendir(_fios_path)) != NULL) {
|
||||
while ((dirent = readdir(dir)) != NULL) {
|
||||
const char *d_name = FS2OTTD(dirent->d_name);
|
||||
|
||||
/* found file must be directory, but not '.' or '..' */
|
||||
if (FiosIsValidFile(_fios_path, dirent, &sb) && (sb.st_mode & S_IFDIR) &&
|
||||
strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
|
||||
fios = FiosAlloc();
|
||||
fios->type = FIOS_TYPE_DIR;
|
||||
fios->mtime = 0;
|
||||
ttd_strlcpy(fios->name, d_name, lengthof(fios->name));
|
||||
snprintf(fios->title, lengthof(fios->title), "%s" PATHSEP " (Directory)", d_name);
|
||||
str_validate(fios->title);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
/* Sort the subdirs always by name, ascending, remember user-sorting order */
|
||||
{
|
||||
byte order = _savegame_sort_order;
|
||||
_savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
|
||||
qsort(_fios_items, _fios_count, sizeof(FiosItem), compare_FiosItems);
|
||||
_savegame_sort_order = order;
|
||||
}
|
||||
|
||||
/* This is where to start sorting for the filenames */
|
||||
sort_start = _fios_count;
|
||||
|
||||
/* Show files */
|
||||
dir = opendir(_fios_path);
|
||||
if (dir != NULL) {
|
||||
while ((dirent = readdir(dir)) != NULL) {
|
||||
char fios_title[64];
|
||||
char *t;
|
||||
char *d_name = (char*)FS2OTTD(dirent->d_name);
|
||||
byte type;
|
||||
|
||||
if (!FiosIsValidFile(_fios_path, dirent, &sb) || !(sb.st_mode & S_IFREG)) continue;
|
||||
|
||||
/* File has no extension, skip it */
|
||||
if ((t = strrchr(d_name, '.')) == NULL) continue;
|
||||
fios_title[0] = '\0'; // reset the title;
|
||||
|
||||
type = callback_proc(mode, d_name, t, fios_title);
|
||||
if (type != FIOS_TYPE_INVALID) {
|
||||
fios = FiosAlloc();
|
||||
fios->mtime = sb.st_mtime;
|
||||
fios->type = type;
|
||||
ttd_strlcpy(fios->name, d_name, lengthof(fios->name));
|
||||
|
||||
/* Some callbacks want to lookup the title of the file. Allow that.
|
||||
* If we just copy the title from the filename, strip the extension */
|
||||
t = (fios_title[0] == '\0') ? *t = '\0', d_name : fios_title;
|
||||
ttd_strlcpy(fios->title, t, lengthof(fios->title));
|
||||
str_validate(fios->title);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
qsort(_fios_items + sort_start, _fios_count - sort_start, sizeof(FiosItem), compare_FiosItems);
|
||||
|
||||
/* Show drives */
|
||||
if (mode != SLD_NEW_GAME) FiosGetDrives();
|
||||
|
||||
_fios_num = _fios_count;
|
||||
return _fios_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for FiosGetFileList. It tells if a file is a savegame or not.
|
||||
* @param mode Save/load mode.
|
||||
* @param file Name of the file to check.
|
||||
* @param ext A pointer to the extension identifier inside file
|
||||
* @param title Buffer if a callback wants to lookup the title of the file
|
||||
* @return a FIOS_TYPE_* type of the found file, FIOS_TYPE_INVALID if not a savegame
|
||||
* @see FiosGetFileList
|
||||
* @see FiosGetSavegameList
|
||||
*/
|
||||
static byte FiosGetSavegameListCallback(int mode, const char *file, const char *ext, char *title)
|
||||
{
|
||||
/* Show savegame files
|
||||
* .SAV OpenTTD saved game
|
||||
* .SS1 Transport Tycoon Deluxe preset game
|
||||
* .SV1 Transport Tycoon Deluxe (Patch) saved game
|
||||
* .SV2 Transport Tycoon Deluxe (Patch) saved 2-player game */
|
||||
if (strcasecmp(ext, ".sav") == 0) return FIOS_TYPE_FILE;
|
||||
|
||||
if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
|
||||
if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 ||
|
||||
strcasecmp(ext, ".sv2") == 0) {
|
||||
GetOldSaveGameName(title, _fios_path, file);
|
||||
return FIOS_TYPE_OLDFILE;
|
||||
}
|
||||
}
|
||||
|
||||
return FIOS_TYPE_INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of savegames.
|
||||
* @param mode Save/load mode.
|
||||
* @return A pointer to an array of FiosItem representing all the files to be shown in the save/load dialog.
|
||||
* @see FiosGetFileList
|
||||
*/
|
||||
FiosItem *FiosGetSavegameList(int mode)
|
||||
{
|
||||
static char *_fios_save_path = NULL;
|
||||
|
||||
if (_fios_save_path == NULL) {
|
||||
_fios_save_path = malloc(MAX_PATH);
|
||||
ttd_strlcpy(_fios_save_path, _paths.save_dir, MAX_PATH);
|
||||
}
|
||||
|
||||
_fios_path = _fios_save_path;
|
||||
|
||||
return FiosGetFileList(mode, &FiosGetSavegameListCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for FiosGetFileList. It tells if a file is a scenario or not.
|
||||
* @param mode Save/load mode.
|
||||
* @param file Name of the file to check.
|
||||
* @param ext A pointer to the extension identifier inside file
|
||||
* @param title Buffer if a callback wants to lookup the title of the file
|
||||
* @return a FIOS_TYPE_* type of the found file, FIOS_TYPE_INVALID if not a scenario
|
||||
* @see FiosGetFileList
|
||||
* @see FiosGetScenarioList
|
||||
*/
|
||||
static byte FiosGetScenarioListCallback(int mode, const char *file, const char *ext, char *title)
|
||||
{
|
||||
/* Show scenario files
|
||||
* .SCN OpenTTD style scenario file
|
||||
* .SV0 Transport Tycoon Deluxe (Patch) scenario
|
||||
* .SS0 Transport Tycoon Deluxe preset scenario */
|
||||
if (strcasecmp(ext, ".scn") == 0) return FIOS_TYPE_SCENARIO;
|
||||
|
||||
if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO || mode == SLD_NEW_GAME) {
|
||||
if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) {
|
||||
GetOldSaveGameName(title, _fios_path, file);
|
||||
return FIOS_TYPE_OLD_SCENARIO;
|
||||
}
|
||||
}
|
||||
|
||||
return FIOS_TYPE_INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of scenarios.
|
||||
* @param mode Save/load mode.
|
||||
* @return A pointer to an array of FiosItem representing all the files to be shown in the save/load dialog.
|
||||
* @see FiosGetFileList
|
||||
*/
|
||||
FiosItem *FiosGetScenarioList(int mode)
|
||||
{
|
||||
static char *_fios_scn_path = NULL;
|
||||
|
||||
if (_fios_scn_path == NULL) {
|
||||
_fios_scn_path = malloc(MAX_PATH);
|
||||
ttd_strlcpy(_fios_scn_path, _paths.scenario_dir, MAX_PATH);
|
||||
}
|
||||
|
||||
_fios_path = _fios_scn_path;
|
||||
|
||||
return FiosGetFileList(mode, &FiosGetScenarioListCallback);
|
||||
}
|
||||
|
||||
static byte FiosGetHeightmapListCallback(int mode, const char *file, const char *ext, char *title)
|
||||
{
|
||||
/* Show heightmap files
|
||||
* .PNG PNG Based heightmap files
|
||||
* .BMP BMP Based heightmap files
|
||||
*/
|
||||
|
||||
#ifdef WITH_PNG
|
||||
if (strcasecmp(ext, ".png") == 0) return FIOS_TYPE_PNG;
|
||||
#endif /* WITH_PNG */
|
||||
|
||||
if (strcasecmp(ext, ".bmp") == 0) return FIOS_TYPE_BMP;
|
||||
|
||||
return FIOS_TYPE_INVALID;
|
||||
}
|
||||
|
||||
// Get a list of Heightmaps
|
||||
FiosItem *FiosGetHeightmapList(int mode)
|
||||
{
|
||||
static char *_fios_hmap_path = NULL;
|
||||
|
||||
if (_fios_hmap_path == NULL) {
|
||||
_fios_hmap_path = malloc(MAX_PATH);
|
||||
strcpy(_fios_hmap_path, _paths.heightmap_dir);
|
||||
}
|
||||
|
||||
_fios_path = _fios_hmap_path;
|
||||
|
||||
return FiosGetFileList(mode, &FiosGetHeightmapListCallback);
|
||||
}
|
||||
86
src/fios.h
Normal file
86
src/fios.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef FIOS_H
|
||||
#define FIOS_H
|
||||
|
||||
/* Deals with finding savegames */
|
||||
typedef struct {
|
||||
byte type;
|
||||
uint64 mtime;
|
||||
char title[64];
|
||||
char name[256 - 12 - 64];
|
||||
} FiosItem;
|
||||
|
||||
enum {
|
||||
FIOS_TYPE_DRIVE = 0,
|
||||
FIOS_TYPE_PARENT = 1,
|
||||
FIOS_TYPE_DIR = 2,
|
||||
FIOS_TYPE_FILE = 3,
|
||||
FIOS_TYPE_OLDFILE = 4,
|
||||
FIOS_TYPE_SCENARIO = 5,
|
||||
FIOS_TYPE_OLD_SCENARIO = 6,
|
||||
FIOS_TYPE_DIRECT = 7,
|
||||
FIOS_TYPE_PNG = 8,
|
||||
FIOS_TYPE_BMP = 9,
|
||||
FIOS_TYPE_INVALID = 255,
|
||||
};
|
||||
|
||||
/* Variables to display file lists */
|
||||
extern FiosItem *_fios_list; // defined in misc_gui.c
|
||||
extern int _fios_num; // defined in fios.c, read_only version of _fios_count
|
||||
extern int _saveload_mode; // defined in misc_gui.c
|
||||
|
||||
// Get a list of savegames
|
||||
FiosItem *FiosGetSavegameList(int mode);
|
||||
// Get a list of scenarios
|
||||
FiosItem *FiosGetScenarioList(int mode);
|
||||
// Get a list of Heightmaps
|
||||
FiosItem *FiosGetHeightmapList(int mode);
|
||||
// Free the list of savegames
|
||||
void FiosFreeSavegameList(void);
|
||||
// Browse to. Returns a filename w/path if we reached a file.
|
||||
char *FiosBrowseTo(const FiosItem *item);
|
||||
// Return path, free space and stringID
|
||||
StringID FiosGetDescText(const char **path, uint32 *total_free);
|
||||
// Delete a name
|
||||
bool FiosDelete(const char *name);
|
||||
// Make a filename from a name
|
||||
void FiosMakeSavegameName(char *buf, const char *name, size_t size);
|
||||
// Allocate a new FiosItem
|
||||
FiosItem *FiosAlloc(void);
|
||||
|
||||
int CDECL compare_FiosItems(const void *a, const void *b);
|
||||
|
||||
/* Implementation of opendir/readdir/closedir for Windows */
|
||||
#if defined(WIN32)
|
||||
#include <windows.h>
|
||||
typedef struct DIR DIR;
|
||||
|
||||
typedef struct dirent { // XXX - only d_name implemented
|
||||
wchar_t *d_name; /* name of found file */
|
||||
/* little hack which will point to parent DIR struct which will
|
||||
* save us a call to GetFileAttributes if we want information
|
||||
* about the file (for example in function fio_bla */
|
||||
DIR *dir;
|
||||
} dirent;
|
||||
|
||||
struct DIR {
|
||||
HANDLE hFind;
|
||||
/* the dirent returned by readdir.
|
||||
* note: having only one global instance is not possible because
|
||||
* multiple independent opendir/readdir sequences must be supported. */
|
||||
dirent ent;
|
||||
WIN32_FIND_DATAW fd;
|
||||
/* since opendir calls FindFirstFile, we need a means of telling the
|
||||
* first call to readdir that we already have a file.
|
||||
* that's the case iff this is true */
|
||||
bool at_first_entry;
|
||||
};
|
||||
|
||||
DIR *opendir(const char *path);
|
||||
struct dirent *readdir(DIR *d);
|
||||
int closedir(DIR *d);
|
||||
|
||||
#endif /* defined(WIN32) */
|
||||
|
||||
#endif /* FIOS_H */
|
||||
524
src/fontcache.c
Normal file
524
src/fontcache.c
Normal file
@@ -0,0 +1,524 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "functions.h"
|
||||
#include "macros.h"
|
||||
#include "debug.h"
|
||||
#include "table/sprites.h"
|
||||
#include "table/control_codes.h"
|
||||
#include "spritecache.h"
|
||||
#include "gfx.h"
|
||||
#include "string.h"
|
||||
#include "fontcache.h"
|
||||
|
||||
#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;
|
||||
|
||||
FreeTypeSettings _freetype;
|
||||
|
||||
enum {
|
||||
FACE_COLOUR = 1,
|
||||
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 <tchar.h>
|
||||
#include <shlobj.h> // SHGetFolderPath
|
||||
#include "win32.h"
|
||||
|
||||
/* 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 = malloc(MAX_PATH * sizeof(TCHAR));
|
||||
MB_TO_WIDE_BUFFER(font_name, font_namep, MAX_PATH * sizeof(TCHAR));
|
||||
#else
|
||||
font_namep = (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));
|
||||
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;
|
||||
err = FT_Err_Cannot_Open_Resource;
|
||||
|
||||
} while ((FT_Long)++index != (*face)->num_faces);
|
||||
|
||||
|
||||
folder_error:
|
||||
#if defined(UNICODE)
|
||||
free(font_path);
|
||||
#endif
|
||||
registry_no_font_found:
|
||||
RegCloseKey(hKey);
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
# ifdef WITH_FONTCONFIG
|
||||
static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
|
||||
{
|
||||
FT_Error err = FT_Err_Cannot_Open_Resource;
|
||||
|
||||
if (!FcInit()) {
|
||||
ShowInfoF("Unable to load font configuration");
|
||||
} else {
|
||||
FcPattern *match;
|
||||
FcPattern *pat;
|
||||
FcFontSet *fs;
|
||||
FcResult result;
|
||||
char *font_style;
|
||||
char *font_family;
|
||||
|
||||
/* Split & strip the font's style */
|
||||
font_family = strdup(font_name);
|
||||
font_style = strchr(font_family, ',');
|
||||
if (font_style != NULL) {
|
||||
font_style[0] = '\0';
|
||||
font_style++;
|
||||
while (*font_style == ' ' || *font_style == '\t') font_style++;
|
||||
}
|
||||
|
||||
/* Resolve the name and populate the information structure */
|
||||
pat = FcNameParse((FcChar8*)font_family);
|
||||
if (font_style != NULL) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
|
||||
FcConfigSubstitute(0, pat, FcMatchPattern);
|
||||
FcDefaultSubstitute(pat);
|
||||
fs = FcFontSetCreate();
|
||||
match = FcFontMatch(0, pat, &result);
|
||||
|
||||
if (fs != NULL && match != NULL) {
|
||||
int i;
|
||||
FcChar8 *family;
|
||||
FcChar8 *style;
|
||||
FcChar8 *file;
|
||||
FcFontSetAdd(fs, match);
|
||||
|
||||
for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
|
||||
/* Try the new filename */
|
||||
if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch &&
|
||||
FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
|
||||
FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) {
|
||||
|
||||
/* The correct style? */
|
||||
if (font_style != NULL && strcasecmp(font_style, (char*)style) != 0) continue;
|
||||
|
||||
/* Font config takes the best shot, which, if the family name is spelled
|
||||
* wrongly a 'random' font, so check whether the family name is the
|
||||
* same as the supplied name */
|
||||
if (strcasecmp(font_family, (char*)family) == 0) {
|
||||
err = FT_New_Face(_library, (char *)file, 0, face);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(font_family);
|
||||
FcPatternDestroy(pat);
|
||||
FcFontSetDestroy(fs);
|
||||
FcFini();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
# else
|
||||
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;}
|
||||
# endif /* WITH_FONTCONFIG */
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Loads the freetype font.
|
||||
* First type to load the fontname as if it were a path. If that fails,
|
||||
* try to resolve the filename of the font using fontconfig, where the
|
||||
* format is 'font family name' or 'font family name, font style'.
|
||||
*/
|
||||
static void LoadFreeTypeFont(const char *font_name, FT_Face *face, const char *type)
|
||||
{
|
||||
FT_Error error;
|
||||
|
||||
if (strlen(font_name) == 0) return;
|
||||
|
||||
error = FT_New_Face(_library, font_name, 0, face);
|
||||
|
||||
if (error != FT_Err_Ok) error = GetFontByFaceName(font_name, face);
|
||||
|
||||
if (error == FT_Err_Ok) {
|
||||
DEBUG(freetype, 2, "Requested '%s', using '%s %s'", font_name, (*face)->family_name, (*face)->style_name);
|
||||
|
||||
/* Attempt to select the unicode character map */
|
||||
error = FT_Select_Charmap(*face, ft_encoding_unicode);
|
||||
if (error == FT_Err_Ok) return; // Success
|
||||
|
||||
if (error == FT_Err_Invalid_CharMap_Handle) {
|
||||
/* Try to pick a different character map instead. We default to
|
||||
* the first map, but platform_id 0 encoding_id 0 should also
|
||||
* be unicode (strange system...) */
|
||||
FT_CharMap found = (*face)->charmaps[0];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (*face)->num_charmaps; i++) {
|
||||
FT_CharMap charmap = (*face)->charmaps[i];
|
||||
if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
|
||||
found = charmap;
|
||||
}
|
||||
}
|
||||
|
||||
if (found != NULL) {
|
||||
error = FT_Set_Charmap(*face, found);
|
||||
if (error == FT_Err_Ok) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FT_Done_Face(*face);
|
||||
*face = NULL;
|
||||
|
||||
ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name, type, error);
|
||||
}
|
||||
|
||||
|
||||
void InitFreeType(void)
|
||||
{
|
||||
if (strlen(_freetype.small_font) == 0 && strlen(_freetype.medium_font) == 0 && strlen(_freetype.large_font) == 0) {
|
||||
DEBUG(freetype, 1, "No font faces specified, using sprite fonts instead");
|
||||
return;
|
||||
}
|
||||
|
||||
if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
|
||||
ShowInfoF("Unable to initialize FreeType, using sprite fonts instead");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG(freetype, 2, "Initialized");
|
||||
|
||||
/* Load each font */
|
||||
LoadFreeTypeFont(_freetype.small_font, &_face_small, "small");
|
||||
LoadFreeTypeFont(_freetype.medium_font, &_face_medium, "medium");
|
||||
LoadFreeTypeFont(_freetype.large_font, &_face_large, "large");
|
||||
|
||||
/* Set each font size */
|
||||
if (_face_small != NULL) FT_Set_Pixel_Sizes(_face_small, 0, _freetype.small_size);
|
||||
if (_face_medium != NULL) FT_Set_Pixel_Sizes(_face_medium, 0, _freetype.medium_size);
|
||||
if (_face_large != NULL) FT_Set_Pixel_Sizes(_face_large, 0, _freetype.large_size);
|
||||
}
|
||||
|
||||
|
||||
static FT_Face GetFontFace(FontSize size)
|
||||
{
|
||||
switch (size) {
|
||||
default: NOT_REACHED();
|
||||
case FS_NORMAL: return _face_medium;
|
||||
case FS_SMALL: return _face_small;
|
||||
case FS_LARGE: return _face_large;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct GlyphEntry {
|
||||
Sprite *sprite;
|
||||
byte width;
|
||||
} GlyphEntry;
|
||||
|
||||
|
||||
/* The glyph cache. This is structured to reduce memory consumption.
|
||||
* 1) There is a 'segment' table for each font size.
|
||||
* 2) Each segment table is a discrete block of characters.
|
||||
* 3) Each block contains 256 (aligned) characters sequential characters.
|
||||
*
|
||||
* The cache is accessed in the following way:
|
||||
* For character 0x0041 ('A'): _glyph_ptr[FS_NORMAL][0x00][0x41]
|
||||
* For character 0x20AC (Euro): _glyph_ptr[FS_NORMAL][0x20][0xAC]
|
||||
*
|
||||
* Currently only 256 segments are allocated, "limiting" us to 65536 characters.
|
||||
* This can be simply changed in the two functions Get & SetGlyphPtr.
|
||||
*/
|
||||
static GlyphEntry **_glyph_ptr[FS_END];
|
||||
|
||||
|
||||
static GlyphEntry *GetGlyphPtr(FontSize size, WChar key)
|
||||
{
|
||||
if (_glyph_ptr[size] == NULL) return NULL;
|
||||
if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) return NULL;
|
||||
return &_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)];
|
||||
}
|
||||
|
||||
|
||||
static void SetGlyphPtr(FontSize size, WChar key, const GlyphEntry *glyph)
|
||||
{
|
||||
if (_glyph_ptr[size] == NULL) {
|
||||
DEBUG(freetype, 3, "Allocating root glyph cache for size %u", size);
|
||||
_glyph_ptr[size] = calloc(256, sizeof(**_glyph_ptr));
|
||||
}
|
||||
|
||||
if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) {
|
||||
DEBUG(freetype, 3, "Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), size);
|
||||
_glyph_ptr[size][GB(key, 8, 8)] = calloc(256, sizeof(***_glyph_ptr));
|
||||
}
|
||||
|
||||
DEBUG(freetype, 4, "Set glyph for unicode character 0x%04X, size %u", key, size);
|
||||
_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].sprite = glyph->sprite;
|
||||
_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].width = glyph->width;
|
||||
}
|
||||
|
||||
|
||||
const Sprite *GetGlyph(FontSize size, WChar key)
|
||||
{
|
||||
FT_Face face = GetFontFace(size);
|
||||
FT_GlyphSlot slot;
|
||||
GlyphEntry new_glyph;
|
||||
GlyphEntry *glyph;
|
||||
Sprite *sprite;
|
||||
int width;
|
||||
int height;
|
||||
int x;
|
||||
int y;
|
||||
int y_adj;
|
||||
|
||||
assert(IsPrintable(key));
|
||||
|
||||
/* Bail out if no face loaded, or for our special characters */
|
||||
if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
|
||||
SpriteID sprite = GetUnicodeGlyph(size, key);
|
||||
if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
|
||||
return GetSprite(sprite);
|
||||
}
|
||||
|
||||
/* Check for the glyph in our cache */
|
||||
glyph = GetGlyphPtr(size, key);
|
||||
if (glyph != NULL && glyph->sprite != NULL) return glyph->sprite;
|
||||
|
||||
slot = face->glyph;
|
||||
|
||||
FT_Load_Char(face, key, FT_LOAD_DEFAULT);
|
||||
FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO);
|
||||
|
||||
/* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
|
||||
width = max(1, slot->bitmap.width + (size == FS_NORMAL));
|
||||
height = max(1, slot->bitmap.rows + (size == FS_NORMAL));
|
||||
|
||||
/* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
|
||||
sprite = calloc(width * height + 8, 1);
|
||||
sprite->info = 1;
|
||||
sprite->width = width;
|
||||
sprite->height = height;
|
||||
sprite->x_offs = slot->bitmap_left;
|
||||
// XXX 2 should be determined somehow... it's right for the normal face
|
||||
y_adj = (size == FS_NORMAL) ? 2 : 0;
|
||||
sprite->y_offs = GetCharacterHeight(size) - slot->bitmap_top - y_adj;
|
||||
|
||||
/* Draw shadow for medium size */
|
||||
if (size == FS_NORMAL) {
|
||||
for (y = 0; y < slot->bitmap.rows; y++) {
|
||||
for (x = 0; x < slot->bitmap.width; x++) {
|
||||
if (HASBIT(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
|
||||
sprite->data[1 + x + (1 + y) * sprite->width] = SHADOW_COLOUR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (y = 0; y < slot->bitmap.rows; y++) {
|
||||
for (x = 0; x < slot->bitmap.width; x++) {
|
||||
if (HASBIT(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
|
||||
sprite->data[x + y * sprite->width] = FACE_COLOUR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_glyph.sprite = sprite;
|
||||
new_glyph.width = (slot->advance.x >> 6) + (size != FS_NORMAL);
|
||||
|
||||
SetGlyphPtr(size, key, &new_glyph);
|
||||
|
||||
return sprite;
|
||||
}
|
||||
|
||||
|
||||
uint GetGlyphWidth(FontSize size, WChar key)
|
||||
{
|
||||
FT_Face face = GetFontFace(size);
|
||||
GlyphEntry *glyph;
|
||||
|
||||
if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
|
||||
SpriteID sprite = GetUnicodeGlyph(size, key);
|
||||
if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
|
||||
return SpriteExists(sprite) ? GetSprite(sprite)->width + (size != FS_NORMAL) : 0;
|
||||
}
|
||||
|
||||
glyph = GetGlyphPtr(size, key);
|
||||
if (glyph == NULL || glyph->sprite == NULL) {
|
||||
GetGlyph(size, key);
|
||||
glyph = GetGlyphPtr(size, key);
|
||||
}
|
||||
|
||||
return glyph->width;
|
||||
}
|
||||
|
||||
|
||||
#endif /* WITH_FREETYPE */
|
||||
|
||||
/* Sprite based glyph mapping */
|
||||
|
||||
#include "table/unicode.h"
|
||||
|
||||
static SpriteID **_unicode_glyph_map[FS_END];
|
||||
|
||||
|
||||
/** Get the SpriteID of the first glyph for the given font size */
|
||||
static SpriteID GetFontBase(FontSize size)
|
||||
{
|
||||
switch (size) {
|
||||
default: NOT_REACHED();
|
||||
case FS_NORMAL: return SPR_ASCII_SPACE;
|
||||
case FS_SMALL: return SPR_ASCII_SPACE_SMALL;
|
||||
case FS_LARGE: return SPR_ASCII_SPACE_BIG;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SpriteID GetUnicodeGlyph(FontSize size, uint32 key)
|
||||
{
|
||||
if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) return 0;
|
||||
return _unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)];
|
||||
}
|
||||
|
||||
|
||||
void SetUnicodeGlyph(FontSize size, uint32 key, SpriteID sprite)
|
||||
{
|
||||
if (_unicode_glyph_map[size] == NULL) _unicode_glyph_map[size] = calloc(256, sizeof(*_unicode_glyph_map[size]));
|
||||
if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) _unicode_glyph_map[size][GB(key, 8, 8)] = calloc(256, sizeof(**_unicode_glyph_map[size]));
|
||||
_unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)] = sprite;
|
||||
}
|
||||
|
||||
|
||||
void InitializeUnicodeGlyphMap(void)
|
||||
{
|
||||
FontSize size;
|
||||
SpriteID base;
|
||||
SpriteID sprite;
|
||||
uint i;
|
||||
|
||||
for (size = FS_NORMAL; size != FS_END; size++) {
|
||||
/* Clear out existing glyph map if it exists */
|
||||
if (_unicode_glyph_map[size] != NULL) {
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (_unicode_glyph_map[size][i] != NULL) free(_unicode_glyph_map[size][i]);
|
||||
}
|
||||
free(_unicode_glyph_map[size]);
|
||||
_unicode_glyph_map[size] = NULL;
|
||||
}
|
||||
|
||||
base = GetFontBase(size);
|
||||
for (i = ASCII_LETTERSTART; i < 256; i++) {
|
||||
sprite = base + i - ASCII_LETTERSTART;
|
||||
if (!SpriteExists(sprite)) continue;
|
||||
SetUnicodeGlyph(size, i, sprite);
|
||||
SetUnicodeGlyph(size, i + SCC_SPRITE_START, sprite);
|
||||
}
|
||||
for (i = 0; i < lengthof(_default_unicode_map); i++) {
|
||||
sprite = base + _default_unicode_map[i].key - ASCII_LETTERSTART;
|
||||
SetUnicodeGlyph(size, _default_unicode_map[i].code, sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
56
src/fontcache.h
Normal file
56
src/fontcache.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef FONTCACHE_H
|
||||
#define FONTCACHE_H
|
||||
|
||||
/** Get the SpriteID mapped to the given font size and key */
|
||||
SpriteID GetUnicodeGlyph(FontSize size, uint32 key);
|
||||
|
||||
/** Map a SpriteID to the font size and key */
|
||||
void SetUnicodeGlyph(FontSize size, uint32 key, SpriteID sprite);
|
||||
|
||||
/** Initialize the glyph map */
|
||||
void InitializeUnicodeGlyphMap(void);
|
||||
|
||||
#ifdef WITH_FREETYPE
|
||||
|
||||
typedef struct FreeTypeSettings {
|
||||
char small_font[260];
|
||||
char medium_font[260];
|
||||
char large_font[260];
|
||||
uint small_size;
|
||||
uint medium_size;
|
||||
uint large_size;
|
||||
} FreeTypeSettings;
|
||||
|
||||
extern FreeTypeSettings _freetype;
|
||||
|
||||
void InitFreeType(void);
|
||||
const struct Sprite *GetGlyph(FontSize size, uint32 key);
|
||||
uint GetGlyphWidth(FontSize size, uint32 key);
|
||||
|
||||
#else
|
||||
|
||||
/* Stub for initializiation */
|
||||
static inline void InitFreeType(void) {}
|
||||
|
||||
/** Get the Sprite for a glyph */
|
||||
static inline const Sprite *GetGlyph(FontSize size, uint32 key)
|
||||
{
|
||||
SpriteID sprite = GetUnicodeGlyph(size, key);
|
||||
if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
|
||||
return GetSprite(sprite);
|
||||
}
|
||||
|
||||
|
||||
/** Get the width of a glyph */
|
||||
static inline uint GetGlyphWidth(FontSize size, uint32 key)
|
||||
{
|
||||
SpriteID sprite = GetUnicodeGlyph(size, key);
|
||||
if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
|
||||
return SpriteExists(sprite) ? GetSprite(sprite)->width + (size != FS_NORMAL) : 0;
|
||||
}
|
||||
|
||||
#endif /* WITH_FREETYPE */
|
||||
|
||||
#endif /* FONTCACHE_H */
|
||||
227
src/functions.h
Normal file
227
src/functions.h
Normal file
@@ -0,0 +1,227 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef FUNCTIONS_H
|
||||
#define FUNCTIONS_H
|
||||
|
||||
void DoClearSquare(TileIndex tile);
|
||||
void RunTileLoop(void);
|
||||
|
||||
uint GetPartialZ(int x, int y, Slope corners);
|
||||
uint GetSlopeZ(int x, int y);
|
||||
uint32 GetTileTrackStatus(TileIndex tile, TransportType mode);
|
||||
void GetAcceptedCargo(TileIndex tile, AcceptedCargo ac);
|
||||
void ChangeTileOwner(TileIndex tile, byte old_player, byte new_player);
|
||||
void AnimateTile(TileIndex tile);
|
||||
void ClickTile(TileIndex tile);
|
||||
void GetTileDesc(TileIndex tile, TileDesc *td);
|
||||
void UpdateTownMaxPass(Town *t);
|
||||
|
||||
bool IsValidTile(TileIndex tile);
|
||||
|
||||
static inline Point RemapCoords(int x, int y, int z)
|
||||
{
|
||||
#if !defined(NEW_ROTATION)
|
||||
Point pt;
|
||||
pt.x = (y - x) * 2;
|
||||
pt.y = y + x - z;
|
||||
#else
|
||||
Point pt;
|
||||
pt.x = (x + y) * 2;
|
||||
pt.y = x - y - z;
|
||||
#endif
|
||||
return pt;
|
||||
}
|
||||
|
||||
static inline Point RemapCoords2(int x, int y)
|
||||
{
|
||||
return RemapCoords(x, y, GetSlopeZ(x, y));
|
||||
}
|
||||
|
||||
|
||||
/* clear_land.c */
|
||||
void DrawHillyLandTile(const TileInfo *ti);
|
||||
void DrawClearLandTile(const TileInfo *ti, byte set);
|
||||
void DrawClearLandFence(const TileInfo *ti);
|
||||
void TileLoopClearHelper(TileIndex tile);
|
||||
|
||||
/* water_land.c */
|
||||
void DrawShipDepotSprite(int x, int y, int image);
|
||||
void TileLoop_Water(TileIndex tile);
|
||||
|
||||
/* players.c */
|
||||
bool CheckPlayerHasMoney(int32 cost);
|
||||
void SubtractMoneyFromPlayer(int32 cost);
|
||||
void SubtractMoneyFromPlayerFract(PlayerID player, int32 cost);
|
||||
bool CheckOwnership(Owner owner);
|
||||
bool CheckTileOwnership(TileIndex tile);
|
||||
StringID GetPlayerNameString(PlayerID player, uint index);
|
||||
|
||||
/* standard */
|
||||
void ShowInfo(const char *str);
|
||||
void CDECL ShowInfoF(const char *str, ...);
|
||||
void NORETURN CDECL error(const char *str, ...);
|
||||
|
||||
/* openttd.c */
|
||||
|
||||
/**************
|
||||
* Warning: DO NOT enable this unless you understand what it does
|
||||
*
|
||||
* If enabled, in a network game all randoms will be dumped to the
|
||||
* stdout if the first client joins (or if you are a client). This
|
||||
* is to help finding desync problems.
|
||||
*
|
||||
* Warning: DO NOT enable this unless you understand what it does
|
||||
**************/
|
||||
|
||||
//#define RANDOM_DEBUG
|
||||
|
||||
|
||||
// Enable this to produce higher quality random numbers.
|
||||
// Doesn't work with network yet.
|
||||
//#define MERSENNE_TWISTER
|
||||
|
||||
// Mersenne twister functions
|
||||
void SeedMT(uint32 seed);
|
||||
uint32 RandomMT(void);
|
||||
|
||||
|
||||
#ifdef MERSENNE_TWISTER
|
||||
static inline uint32 Random(void) { return RandomMT(); }
|
||||
uint RandomRange(uint max);
|
||||
#else
|
||||
|
||||
#ifdef RANDOM_DEBUG
|
||||
#define Random() DoRandom(__LINE__, __FILE__)
|
||||
uint32 DoRandom(int line, const char *file);
|
||||
#define RandomRange(max) DoRandomRange(max, __LINE__, __FILE__)
|
||||
uint DoRandomRange(uint max, int line, const char *file);
|
||||
#else
|
||||
uint32 Random(void);
|
||||
uint RandomRange(uint max);
|
||||
#endif
|
||||
#endif // MERSENNE_TWISTER
|
||||
|
||||
static inline TileIndex RandomTileSeed(uint32 r) { return TILE_MASK(r); }
|
||||
static inline TileIndex RandomTile(void) { return TILE_MASK(Random()); }
|
||||
|
||||
|
||||
uint32 InteractiveRandom(void); /* Used for random sequences that are not the same on the other end of the multiplayer link */
|
||||
uint InteractiveRandomRange(uint max);
|
||||
|
||||
/* facedraw.c */
|
||||
void DrawPlayerFace(uint32 face, int color, int x, int y);
|
||||
|
||||
/* texteff.c */
|
||||
void MoveAllTextEffects(void);
|
||||
void AddTextEffect(StringID msg, int x, int y, uint16 duration);
|
||||
void InitTextEffects(void);
|
||||
void DrawTextEffects(DrawPixelInfo *dpi);
|
||||
|
||||
void InitTextMessage(void);
|
||||
void DrawTextMessage(void);
|
||||
void CDECL AddTextMessage(uint16 color, uint8 duration, const char *message, ...);
|
||||
void UndrawTextMessage(void);
|
||||
|
||||
bool AddAnimatedTile(TileIndex tile);
|
||||
void DeleteAnimatedTile(TileIndex tile);
|
||||
void AnimateAnimatedTiles(void);
|
||||
void InitializeAnimatedTiles(void);
|
||||
|
||||
/* tunnelbridge_cmd.c */
|
||||
bool CheckBridge_Stuff(byte bridge_type, uint bridge_len);
|
||||
uint32 GetBridgeLength(TileIndex begin, TileIndex end);
|
||||
int CalcBridgeLenCostFactor(int x);
|
||||
|
||||
/* misc_cmd.c */
|
||||
void PlaceTreesRandomly(void);
|
||||
|
||||
void InitializeLandscapeVariables(bool only_constants);
|
||||
|
||||
/* misc.c */
|
||||
bool IsCustomName(StringID id);
|
||||
void DeleteName(StringID id);
|
||||
char *GetName(char *buff, StringID id, const char* last);
|
||||
|
||||
// AllocateNameUnique also tests if the name used is not used anywere else
|
||||
// and if it is used, it returns an error.
|
||||
#define AllocateNameUnique(name, skip) RealAllocateName(name, skip, true)
|
||||
#define AllocateName(name, skip) RealAllocateName(name, skip, false)
|
||||
StringID RealAllocateName(const char *name, byte skip, bool check_double);
|
||||
void ConvertNameArray(void);
|
||||
|
||||
/* misc functions */
|
||||
void MarkTileDirty(int x, int y);
|
||||
void MarkTileDirtyByTile(TileIndex tile);
|
||||
void InvalidateWindow(WindowClass cls, WindowNumber number);
|
||||
void InvalidateWindowWidget(WindowClass cls, WindowNumber number, byte widget_index);
|
||||
void InvalidateWindowClasses(WindowClass cls);
|
||||
void InvalidateWindowClassesData(WindowClass cls);
|
||||
void DeleteWindowById(WindowClass cls, WindowNumber number);
|
||||
void DeleteWindowByClass(WindowClass cls);
|
||||
|
||||
void SetObjectToPlaceWnd(CursorID icon, byte mode, Window *w);
|
||||
void SetObjectToPlace(CursorID icon, byte mode, WindowClass window_class, WindowNumber window_num);
|
||||
|
||||
void ResetObjectToPlace(void);
|
||||
|
||||
bool ScrollWindowTo(int x, int y, Window * w);
|
||||
|
||||
bool ScrollMainWindowToTile(TileIndex tile);
|
||||
bool ScrollMainWindowTo(int x, int y);
|
||||
void DrawSprite(uint32 img, int x, int y);
|
||||
bool EnsureNoVehicle(TileIndex tile);
|
||||
bool EnsureNoVehicleOnGround(TileIndex tile);
|
||||
void MarkAllViewportsDirty(int left, int top, int right, int bottom);
|
||||
void ShowCostOrIncomeAnimation(int x, int y, int z, int32 cost);
|
||||
void ShowFeederIncomeAnimation(int x, int y, int z, int32 cost);
|
||||
|
||||
void DrawFoundation(TileInfo *ti, uint f);
|
||||
|
||||
bool CheckIfAuthorityAllows(TileIndex tile);
|
||||
Town *ClosestTownFromTile(TileIndex tile, uint threshold);
|
||||
void ChangeTownRating(Town *t, int add, int max);
|
||||
|
||||
uint GetTownRadiusGroup(const Town* t, TileIndex tile);
|
||||
int FindFirstBit(uint32 x);
|
||||
void ShowHighscoreTable(int difficulty, int8 rank);
|
||||
TileIndex AdjustTileCoordRandomly(TileIndex a, byte rng);
|
||||
|
||||
void AfterLoadTown(void);
|
||||
void UpdatePatches(void);
|
||||
void AskExitGame(void);
|
||||
void AskExitToGameMenu(void);
|
||||
|
||||
void RedrawAutosave(void);
|
||||
|
||||
StringID RemapOldStringID(StringID s);
|
||||
|
||||
void UpdateViewportSignPos(ViewportSign *sign, int left, int top, StringID str);
|
||||
|
||||
enum {
|
||||
SLD_LOAD_GAME,
|
||||
SLD_LOAD_SCENARIO,
|
||||
SLD_SAVE_GAME,
|
||||
SLD_SAVE_SCENARIO,
|
||||
SLD_LOAD_HEIGHTMAP,
|
||||
SLD_NEW_GAME,
|
||||
};
|
||||
void ShowSaveLoadDialog(int mode);
|
||||
|
||||
// callback from drivers that is called if the game size changes dynamically
|
||||
void GameSizeChanged(void);
|
||||
bool FileExists(const char *filename);
|
||||
bool ReadLanguagePack(int index);
|
||||
void InitializeLanguagePacks(void);
|
||||
const char *GetCurrentLocale(const char *param);
|
||||
void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize);
|
||||
|
||||
void LoadFromConfig(void);
|
||||
void SaveToConfig(void);
|
||||
void CheckConfig(void);
|
||||
int ttd_main(int argc, char* argv[]);
|
||||
void HandleExitGameRequest(void);
|
||||
|
||||
void DeterminePaths(void);
|
||||
|
||||
void CSleep(int milliseconds);
|
||||
#endif /* FUNCTIONS_H */
|
||||
292
src/genworld.c
Normal file
292
src/genworld.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "functions.h"
|
||||
#include "player.h"
|
||||
#include "table/sprites.h"
|
||||
#include "variables.h"
|
||||
#include "thread.h"
|
||||
#include "genworld.h"
|
||||
#include "gfx.h"
|
||||
#include "gfxinit.h"
|
||||
#include "gui.h"
|
||||
#include "network/network.h"
|
||||
#include "debug.h"
|
||||
#include "settings.h"
|
||||
#include "heightmap.h"
|
||||
#include "date.h"
|
||||
|
||||
void GenerateLandscape(byte mode);
|
||||
void GenerateClearTile(void);
|
||||
void GenerateIndustries(void);
|
||||
void GenerateUnmovables(void);
|
||||
bool GenerateTowns(void);
|
||||
void GenerateTrees(void);
|
||||
|
||||
void StartupEconomy(void);
|
||||
void StartupPlayers(void);
|
||||
void StartupDisasters(void);
|
||||
|
||||
void InitializeGame(int mode, uint size_x, uint size_y);
|
||||
|
||||
void ConvertGroundTilesIntoWaterTiles(void);
|
||||
|
||||
/* Please only use this variable in genworld.h and genworld.c and
|
||||
* nowhere else. For speed improvements we need it to be global, but
|
||||
* in no way the meaning of it is to use it anywhere else besides
|
||||
* in the genworld.h and genworld.c! -- TrueLight */
|
||||
gw_info _gw;
|
||||
|
||||
/**
|
||||
* Set the status of the Paint flag.
|
||||
* If it is true, the thread will hold with any futher generating till
|
||||
* the drawing of the screen is done. This is handled by
|
||||
* SetGeneratingWorldProgress(), so calling that function will stall
|
||||
* from time to time.
|
||||
*/
|
||||
void SetGeneratingWorldPaintStatus(bool status)
|
||||
{
|
||||
_gw.wait_for_draw = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the thread wants the main program to do a (full) paint.
|
||||
* If this returns false, please do not update the screen. Because we are
|
||||
* writing in a thread, it can cause damaged data (reading and writing the
|
||||
* same tile at the same time).
|
||||
*/
|
||||
bool IsGeneratingWorldReadyForPaint(void)
|
||||
{
|
||||
/* If we are in quit_thread mode, ignore this and always return false. This
|
||||
* forces the screen to not be drawn, and the GUI not to wait for a draw. */
|
||||
if (!_gw.active || _gw.quit_thread || !_gw.threaded) return false;
|
||||
|
||||
return _gw.wait_for_draw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the world generation is done in a thread or not.
|
||||
*/
|
||||
bool IsGenerateWorldThreaded(void)
|
||||
{
|
||||
return _gw.threaded && !_gw.quit_thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* The internal, real, generate function.
|
||||
*/
|
||||
static void *_GenerateWorld(void *arg)
|
||||
{
|
||||
_generating_world = true;
|
||||
if (_network_dedicated) DEBUG(net, 0, "Generating map, please wait...");
|
||||
/* Set the Random() seed to generation_seed so we produce the same map with the same seed */
|
||||
if (_patches.generation_seed == GENERATE_NEW_SEED) _patches.generation_seed = _patches_newgame.generation_seed = InteractiveRandom();
|
||||
_random_seeds[0][0] = _random_seeds[0][1] = _patches.generation_seed;
|
||||
SetGeneratingWorldProgress(GWP_MAP_INIT, 2);
|
||||
SetObjectToPlace(SPR_CURSOR_ZZZ, 0, 0, 0);
|
||||
|
||||
IncreaseGeneratingWorldProgress(GWP_MAP_INIT);
|
||||
// Must start economy early because of the costs.
|
||||
StartupEconomy();
|
||||
|
||||
// Don't generate landscape items when in the scenario editor.
|
||||
if (_gw.mode == GW_EMPTY) {
|
||||
SetGeneratingWorldProgress(GWP_UNMOVABLE, 1);
|
||||
|
||||
/* Make the map the height of the patch setting */
|
||||
if (_game_mode != GM_MENU) FlatEmptyWorld(_patches.se_flat_world_height);
|
||||
|
||||
ConvertGroundTilesIntoWaterTiles();
|
||||
IncreaseGeneratingWorldProgress(GWP_UNMOVABLE);
|
||||
} else {
|
||||
GenerateLandscape(_gw.mode);
|
||||
GenerateClearTile();
|
||||
|
||||
// only generate towns, tree and industries in newgame mode.
|
||||
if (_game_mode != GM_EDITOR) {
|
||||
GenerateTowns();
|
||||
GenerateIndustries();
|
||||
GenerateUnmovables();
|
||||
GenerateTrees();
|
||||
}
|
||||
}
|
||||
|
||||
// These are probably pointless when inside the scenario editor.
|
||||
SetGeneratingWorldProgress(GWP_GAME_INIT, 3);
|
||||
StartupPlayers();
|
||||
IncreaseGeneratingWorldProgress(GWP_GAME_INIT);
|
||||
StartupEngines();
|
||||
IncreaseGeneratingWorldProgress(GWP_GAME_INIT);
|
||||
StartupDisasters();
|
||||
_generating_world = false;
|
||||
|
||||
// No need to run the tile loop in the scenario editor.
|
||||
if (_gw.mode != GW_EMPTY) {
|
||||
uint i;
|
||||
|
||||
SetGeneratingWorldProgress(GWP_RUNTILELOOP, 0x500);
|
||||
for (i = 0; i < 0x500; i++) {
|
||||
RunTileLoop();
|
||||
IncreaseGeneratingWorldProgress(GWP_RUNTILELOOP);
|
||||
}
|
||||
}
|
||||
|
||||
ResetObjectToPlace();
|
||||
SetLocalPlayer(_gw.lp);
|
||||
|
||||
SetGeneratingWorldProgress(GWP_GAME_START, 1);
|
||||
/* Call any callback */
|
||||
if (_gw.proc != NULL) _gw.proc();
|
||||
IncreaseGeneratingWorldProgress(GWP_GAME_START);
|
||||
|
||||
if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE);
|
||||
/* Show all vital windows again, because we have hidden them */
|
||||
if (_gw.threaded && _game_mode != GM_MENU) ShowVitalWindows();
|
||||
_gw.active = false;
|
||||
_gw.thread = NULL;
|
||||
_gw.proc = NULL;
|
||||
_gw.threaded = false;
|
||||
|
||||
DeleteWindowById(WC_GENERATE_PROGRESS_WINDOW, 0);
|
||||
MarkWholeScreenDirty();
|
||||
|
||||
if (_network_dedicated) DEBUG(net, 0, "Map generated, starting game");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set here the function, if any, that you want to be called when landscape
|
||||
* generation is done.
|
||||
*/
|
||||
void GenerateWorldSetCallback(gw_done_proc *proc)
|
||||
{
|
||||
_gw.proc = proc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set here the function, if any, that you want to be called when landscape
|
||||
* generation is aborted.
|
||||
*/
|
||||
void GenerateWorldSetAbortCallback(gw_abort_proc *proc)
|
||||
{
|
||||
_gw.abortp = proc;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will wait for the thread to finish up his work. It will not continue
|
||||
* till the work is done.
|
||||
*/
|
||||
void WaitTillGeneratedWorld(void)
|
||||
{
|
||||
if (_gw.thread == NULL) return;
|
||||
_gw.quit_thread = true;
|
||||
OTTDJoinThread(_gw.thread);
|
||||
_gw.thread = NULL;
|
||||
_gw.threaded = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the abortion process
|
||||
*/
|
||||
void AbortGeneratingWorld(void)
|
||||
{
|
||||
_gw.abort = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the generation being aborted?
|
||||
*/
|
||||
bool IsGeneratingWorldAborted(void)
|
||||
{
|
||||
return _gw.abort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Really handle the abortion, i.e. clean up some of the mess
|
||||
*/
|
||||
void HandleGeneratingWorldAbortion(void)
|
||||
{
|
||||
/* Clean up - in SE create an empty map, otherwise, go to intro menu */
|
||||
_switch_mode = (_game_mode == GM_EDITOR) ? SM_EDITOR : SM_MENU;
|
||||
|
||||
if (_gw.abortp != NULL) _gw.abortp();
|
||||
|
||||
if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE);
|
||||
/* Show all vital windows again, because we have hidden them */
|
||||
if (_gw.threaded && _game_mode != GM_MENU) ShowVitalWindows();
|
||||
_gw.active = false;
|
||||
_gw.thread = NULL;
|
||||
_gw.proc = NULL;
|
||||
_gw.abortp = NULL;
|
||||
_gw.threaded = false;
|
||||
|
||||
DeleteWindowById(WC_GENERATE_PROGRESS_WINDOW, 0);
|
||||
MarkWholeScreenDirty();
|
||||
|
||||
OTTDExitThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a world.
|
||||
* @param mode The mode of world generation (@see GenerateWorldModes).
|
||||
* @param size_x The X-size of the map.
|
||||
* @param size_y The Y-size of the map.
|
||||
*/
|
||||
void GenerateWorld(int mode, uint size_x, uint size_y)
|
||||
{
|
||||
if (_gw.active) return;
|
||||
_gw.mode = mode;
|
||||
_gw.size_x = size_x;
|
||||
_gw.size_y = size_y;
|
||||
_gw.active = true;
|
||||
_gw.abort = false;
|
||||
_gw.abortp = NULL;
|
||||
_gw.lp = _local_player;
|
||||
_gw.wait_for_draw = false;
|
||||
_gw.quit_thread = false;
|
||||
_gw.threaded = true;
|
||||
|
||||
/* This disables some commands and stuff */
|
||||
SetLocalPlayer(PLAYER_SPECTATOR);
|
||||
/* Make sure everything is done via OWNER_NONE */
|
||||
_current_player = OWNER_NONE;
|
||||
|
||||
/* Set the date before loading sprites as some newgrfs check it */
|
||||
SetDate(ConvertYMDToDate(_patches.starting_year, 0, 1));
|
||||
|
||||
/* Load the right landscape stuff */
|
||||
GfxLoadSprites();
|
||||
LoadStringWidthTable();
|
||||
|
||||
InitializeGame(IG_NONE, _gw.size_x, _gw.size_y);
|
||||
PrepareGenerateWorldProgress();
|
||||
|
||||
/* Re-init the windowing system */
|
||||
ResetWindowSystem();
|
||||
|
||||
/* Create toolbars */
|
||||
SetupColorsAndInitialWindow();
|
||||
|
||||
if (_network_dedicated ||
|
||||
(_gw.thread = OTTDCreateThread(&_GenerateWorld, NULL)) == NULL) {
|
||||
DEBUG(misc, 1, "Cannot create genworld thread, reverting to single-threaded mode");
|
||||
_gw.threaded = false;
|
||||
_GenerateWorld(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remove any open window */
|
||||
DeleteAllNonVitalWindows();
|
||||
/* Hide vital windows, because we don't allow to use them */
|
||||
HideVitalWindows();
|
||||
|
||||
/* Don't show the dialog if we don't have a thread */
|
||||
ShowGenerateWorldProgress();
|
||||
|
||||
/* Centre the view on the map */
|
||||
if (FindWindowById(WC_MAIN_WINDOW, 0) != NULL) {
|
||||
ScrollMainWindowToTile(TileXY(MapSizeX() / 2, MapSizeY() / 2));
|
||||
}
|
||||
}
|
||||
94
src/genworld.h
Normal file
94
src/genworld.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef GENWORLD_H
|
||||
#define GENWORLD_H
|
||||
|
||||
/* If OTTDThread isn't defined, define it to a void, but make sure to undefine
|
||||
* it after this include. This makes including genworld.h easier, as you
|
||||
* don't need to include thread.h before it, while it stays possible to
|
||||
* include it after it, and still work.
|
||||
*/
|
||||
#ifndef OTTDThread
|
||||
#define TEMPORARY_OTTDTHREAD_DEFINITION
|
||||
#define OTTDThread void
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Order of these enums has to be the same as in lang/english.txt
|
||||
* Otherwise you will get inconsistent behaviour.
|
||||
*/
|
||||
enum {
|
||||
LG_ORIGINAL = 0, //! The original landscape generator
|
||||
LG_TERRAGENESIS = 1, //! TerraGenesis Perlin landscape generator
|
||||
|
||||
GENERATE_NEW_SEED = (uint)-1, //! Create a new random seed
|
||||
};
|
||||
|
||||
typedef void gw_done_proc(void);
|
||||
typedef void gw_abort_proc(void);
|
||||
|
||||
typedef struct gw_info {
|
||||
bool active; //! Is generating world active
|
||||
bool abort; //! Whether to abort the thread ASAP
|
||||
bool wait_for_draw; //! Are we waiting on a draw event
|
||||
bool quit_thread; //! Do we want to quit the active thread
|
||||
bool threaded; //! Whether we run _GenerateWorld threaded
|
||||
int mode; //! What mode are we making a world in
|
||||
byte lp; //! The local_player before generating
|
||||
uint size_x; //! X-size of the map
|
||||
uint size_y; //! Y-size of the map
|
||||
gw_done_proc *proc; //! Proc that is called when done (can be NULL)
|
||||
gw_abort_proc *abortp; //! Proc that is called when aborting (can be NULL)
|
||||
OTTDThread *thread; //! The thread we are in (can be NULL)
|
||||
} gw_info;
|
||||
|
||||
#ifdef TEMPORARY_OTTDTHREAD_DEFINITION
|
||||
#undef OTTDThread
|
||||
#undef TEMPORARY_OTTDTHREAD_DEFINITION
|
||||
#endif
|
||||
|
||||
typedef enum gwp_classes {
|
||||
GWP_MAP_INIT, /* Initialize/allocate the map, start economy */
|
||||
GWP_LANDSCAPE, /* Create the landscape */
|
||||
GWP_ROUGH_ROCKY, /* Make rough and rocky areas */
|
||||
GWP_TOWN, /* Generate towns */
|
||||
GWP_INDUSTRY, /* Generate industries */
|
||||
GWP_UNMOVABLE, /* Generate unmovables (radio tower, light houses) */
|
||||
GWP_TREE, /* Generate trees */
|
||||
GWP_GAME_INIT, /* Initialize the game */
|
||||
GWP_RUNTILELOOP, /* Runs the tile loop 1280 times to make snow etc */
|
||||
GWP_GAME_START, /* Really prepare to start the game */
|
||||
GWP_CLASS_COUNT
|
||||
} gwp_class;
|
||||
|
||||
/**
|
||||
* Check if we are currently in the process of generating a world.
|
||||
*/
|
||||
static inline bool IsGeneratingWorld(void)
|
||||
{
|
||||
extern gw_info _gw;
|
||||
|
||||
return _gw.active;
|
||||
}
|
||||
|
||||
/* genworld.c */
|
||||
void SetGeneratingWorldPaintStatus(bool status);
|
||||
bool IsGeneratingWorldReadyForPaint(void);
|
||||
bool IsGenerateWorldThreaded(void);
|
||||
void GenerateWorldSetCallback(gw_done_proc *proc);
|
||||
void GenerateWorldSetAbortCallback(gw_abort_proc *proc);
|
||||
void WaitTillGeneratedWorld(void);
|
||||
void GenerateWorld(int mode, uint size_x, uint size_y);
|
||||
void AbortGeneratingWorld(void);
|
||||
bool IsGeneratingWorldAborted(void);
|
||||
void HandleGeneratingWorldAbortion(void);
|
||||
|
||||
/* genworld_gui.c */
|
||||
void SetGeneratingWorldProgress(gwp_class class, uint total);
|
||||
void IncreaseGeneratingWorldProgress(gwp_class class);
|
||||
void PrepareGenerateWorldProgress(void);
|
||||
void ShowGenerateWorldProgress(void);
|
||||
void StartNewGameWithoutGUI(uint seed);
|
||||
void ShowCreateScenario(void);
|
||||
|
||||
#endif /* GENWORLD_H */
|
||||
898
src/genworld_gui.c
Normal file
898
src/genworld_gui.c
Normal file
@@ -0,0 +1,898 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "heightmap.h"
|
||||
#include "functions.h"
|
||||
#include "table/strings.h"
|
||||
#include "table/sprites.h"
|
||||
#include "window.h"
|
||||
#include "gui.h"
|
||||
#include "gfx.h"
|
||||
#include "strings.h"
|
||||
#include "gfxinit.h"
|
||||
#include "player.h"
|
||||
#include "command.h"
|
||||
#include "sound.h"
|
||||
#include "variables.h"
|
||||
#include "string.h"
|
||||
#include "settings.h"
|
||||
#include "debug.h"
|
||||
#include "genworld.h"
|
||||
#include "network/network.h"
|
||||
#include "thread.h"
|
||||
#include "date.h"
|
||||
#include "newgrf_config.h"
|
||||
|
||||
enum {
|
||||
START_DATE_QUERY,
|
||||
SNOW_LINE_QUERY,
|
||||
FLAT_WORLD_HEIGHT_QUERY,
|
||||
|
||||
LEN_RND_SEED = 11,
|
||||
SEED_EDIT = 15,
|
||||
};
|
||||
|
||||
/**
|
||||
* In what 'mode' the GenerateLandscapeWindowProc is.
|
||||
*/
|
||||
typedef enum glwp_modes {
|
||||
GLWP_GENERATE,
|
||||
GLWP_HEIGHTMAP,
|
||||
GLWP_SCENARIO,
|
||||
GLWP_END
|
||||
} glwp_modes;
|
||||
|
||||
static uint _heightmap_x = 0;
|
||||
static uint _heightmap_y = 0;
|
||||
static StringID _heightmap_str = STR_NULL;
|
||||
static bool _goto_editor = false;
|
||||
|
||||
extern void SwitchMode(int new_mode);
|
||||
|
||||
static inline void SetNewLandscapeType(byte landscape)
|
||||
{
|
||||
_opt_newgame.landscape = landscape;
|
||||
InvalidateWindowClasses(WC_SELECT_GAME);
|
||||
InvalidateWindowClasses(WC_GENERATE_LANDSCAPE);
|
||||
}
|
||||
|
||||
// no longer static to allow calling from outside module
|
||||
const Widget _generate_landscape_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 13, 11, 337, 0, 13, STR_WORLD_GENERATION_CAPTION, STR_NULL},
|
||||
{ WWT_PANEL, RESIZE_NONE, 13, 0, 337, 14, 267, 0x0, STR_NULL},
|
||||
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 10, 86, 24, 78, SPR_SELECT_TEMPERATE, STR_030E_SELECT_TEMPERATE_LANDSCAPE},
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 90, 166, 24, 78, SPR_SELECT_SUB_ARCTIC, STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 170, 246, 24, 78, SPR_SELECT_SUB_TROPICAL, STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 250, 326, 24, 78, SPR_SELECT_TOYLAND, STR_0311_SELECT_TOYLAND_LANDSCAPE},
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 114, 149, 90, 101, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 150, 161, 90, 101, STR_0225, STR_NULL}, // Mapsize X
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 180, 215, 90, 101, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 216, 227, 90, 101, STR_0225, STR_NULL}, // Mapsize Y
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 114, 163, 112, 123, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 164, 175, 112, 123, STR_0225, STR_NULL}, // Number of towns
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 114, 163, 130, 141, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 164, 175, 130, 141, STR_0225, STR_NULL}, // Number of industries
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, 15, 114, 207, 152, 163, 0x0, STR_RANDOM_SEED_HELP}, // Edit box for seed
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 216, 326, 152, 163, STR_RANDOM, STR_RANDOM_HELP},
|
||||
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 6, 243, 326, 228, 257, STR_GENERATE, STR_NULL}, // Generate button
|
||||
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 12, 216, 227, 112, 123, SPR_ARROW_DOWN, STR_029E_MOVE_THE_STARTING_DATE},
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 228, 314, 112, 123, 0x0, STR_NULL},
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 112, 123, SPR_ARROW_UP, STR_029F_MOVE_THE_STARTING_DATE},
|
||||
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 12, 282, 293, 130, 141, SPR_ARROW_DOWN, STR_SNOW_LINE_DOWN},
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 294, 314, 130, 141, 0x0, STR_NULL},
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 130, 141, SPR_ARROW_UP, STR_SNOW_LINE_UP},
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 114, 219, 192, 203, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 192, 203, STR_0225, STR_NULL}, // Tree placer
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 114, 219, 174, 185, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 174, 185, STR_0225, STR_NULL}, // Landscape generator
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 114, 219, 210, 221, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 210, 221, STR_0225, STR_NULL}, // Terrain type
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 113, 219, 228, 239, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 228, 239, STR_0225, STR_NULL}, // Water quantity
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 113, 219, 246, 257, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 246, 257, STR_0225, STR_NULL}, // Map smoothness
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
const Widget _heightmap_load_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 13, 11, 337, 0, 13, STR_WORLD_GENERATION_CAPTION, STR_NULL},
|
||||
{ WWT_PANEL, RESIZE_NONE, 13, 0, 337, 14, 235, 0x0, STR_NULL},
|
||||
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 10, 86, 24, 78, SPR_SELECT_TEMPERATE, STR_030E_SELECT_TEMPERATE_LANDSCAPE},
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 90, 166, 24, 78, SPR_SELECT_SUB_ARCTIC, STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 170, 246, 24, 78, SPR_SELECT_SUB_TROPICAL, STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 250, 326, 24, 78, SPR_SELECT_TOYLAND, STR_0311_SELECT_TOYLAND_LANDSCAPE},
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 114, 149, 112, 123, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 150, 161, 112, 123, STR_0225, STR_NULL}, // Mapsize X
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 180, 215, 112, 123, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 216, 227, 112, 123, STR_0225, STR_NULL}, // Mapsize Y
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 114, 163, 134, 145, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 164, 175, 134, 145, STR_0225, STR_NULL}, // Number of towns
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 114, 163, 152, 163, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 164, 175, 152, 163, STR_0225, STR_NULL}, // Number of industries
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, 15, 114, 194, 174, 185, 0x0, STR_RANDOM_SEED_HELP}, // Edit box for seed
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 203, 285, 174, 185, STR_RANDOM, STR_RANDOM_HELP},
|
||||
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 6, 243, 326, 196, 225, STR_GENERATE, STR_NULL}, // Generate button
|
||||
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 12, 216, 227, 134, 145, SPR_ARROW_DOWN, STR_029E_MOVE_THE_STARTING_DATE},
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 228, 314, 134, 145, 0x0, STR_NULL},
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 134, 145, SPR_ARROW_UP, STR_029F_MOVE_THE_STARTING_DATE},
|
||||
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 12, 282, 293, 152, 163, SPR_ARROW_DOWN, STR_SNOW_LINE_DOWN},
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 294, 314, 152, 163, 0x0, STR_NULL},
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 152, 163, SPR_ARROW_UP, STR_SNOW_LINE_UP},
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 114, 219, 196, 207, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 196, 207, STR_0225, STR_NULL}, // Tree placer
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 114, 219, 214, 225, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 214, 225, STR_0225, STR_NULL}, // Heightmap rotation
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static void StartGeneratingLandscape(glwp_modes mode)
|
||||
{
|
||||
/* If we want to go to the editor, and aren't yet, we need to delay
|
||||
* it as long as possible, else it gives nasty side-effects (aborting
|
||||
* results in ending up in the SE, which you don't want. Therefor we
|
||||
* use this switch to do it at the very end.
|
||||
*/
|
||||
if (_goto_editor) _game_mode = GM_EDITOR;
|
||||
|
||||
DeleteWindowByClass(WC_GENERATE_LANDSCAPE);
|
||||
DeleteWindowByClass(WC_INDUSTRY_VIEW);
|
||||
DeleteWindowByClass(WC_TOWN_VIEW);
|
||||
DeleteWindowByClass(WC_LAND_INFO);
|
||||
|
||||
/* Copy all XXX_newgame to XXX */
|
||||
UpdatePatches();
|
||||
_opt_ptr = &_opt;
|
||||
*_opt_ptr = _opt_newgame;
|
||||
ResetGRFConfig(true);
|
||||
|
||||
SndPlayFx(SND_15_BEEP);
|
||||
switch (mode) {
|
||||
case GLWP_GENERATE: _switch_mode = (_game_mode == GM_EDITOR) ? SM_GENRANDLAND : SM_NEWGAME; break;
|
||||
case GLWP_HEIGHTMAP: _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_HEIGHTMAP : SM_START_HEIGHTMAP; break;
|
||||
case GLWP_SCENARIO: _switch_mode = SM_EDITOR; break;
|
||||
default: NOT_REACHED(); return;
|
||||
}
|
||||
}
|
||||
|
||||
static void HeightmapScaledTooMuchCallback(Window *w, bool confirmed)
|
||||
{
|
||||
if (confirmed) StartGeneratingLandscape((glwp_modes)w->window_number);
|
||||
}
|
||||
|
||||
void GenerateLandscapeWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
static const StringID mapsizes[] = {STR_64, STR_128, STR_256, STR_512, STR_1024, STR_2048, INVALID_STRING_ID};
|
||||
static const StringID elevations[] = {STR_682A_VERY_FLAT, STR_682B_FLAT, STR_682C_HILLY, STR_682D_MOUNTAINOUS, INVALID_STRING_ID};
|
||||
static const StringID sea_lakes[] = {STR_VERY_LOW, STR_6820_LOW, STR_6821_MEDIUM, STR_6822_HIGH, INVALID_STRING_ID};
|
||||
static const StringID smoothness[] = {STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_ROUGH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_ROUGH, INVALID_STRING_ID};
|
||||
static const StringID tree_placer[] = {STR_CONFIG_PATCHES_TREE_PLACER_NONE, STR_CONFIG_PATCHES_TREE_PLACER_ORIGINAL, STR_CONFIG_PATCHES_TREE_PLACER_IMPROVED, INVALID_STRING_ID};
|
||||
static const StringID rotation[] = {STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_COUNTER_CLOCKWISE, STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_CLOCKWISE, INVALID_STRING_ID};
|
||||
static const StringID landscape[] = {STR_CONFIG_PATCHES_LAND_GENERATOR_ORIGINAL, STR_CONFIG_PATCHES_LAND_GENERATOR_TERRA_GENESIS, INVALID_STRING_ID};
|
||||
static const StringID num_towns[] = {STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID};
|
||||
static const StringID num_inds[] = {STR_26816_NONE, STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID};
|
||||
|
||||
/* Data used for the generate seed edit box */
|
||||
static querystr_d _genseed_query;
|
||||
static char _genseed_buffer[LEN_RND_SEED];
|
||||
|
||||
uint mode = w->window_number;
|
||||
uint y;
|
||||
|
||||
switch (e->event) {
|
||||
case WE_CREATE:
|
||||
LowerWindowWidget(w, _opt_newgame.landscape + 3);
|
||||
|
||||
snprintf(_genseed_buffer, sizeof(_genseed_buffer), "%u", _patches_newgame.generation_seed);
|
||||
InitializeTextBuffer(&_genseed_query.text, _genseed_buffer, lengthof(_genseed_buffer), 120);
|
||||
_genseed_query.caption = STR_NULL;
|
||||
_genseed_query.afilter = CS_NUMERAL;
|
||||
break;
|
||||
|
||||
case WE_PAINT:
|
||||
/* You can't select smoothness if not terragenesis */
|
||||
if (mode == GLWP_GENERATE) {
|
||||
SetWindowWidgetDisabledState(w, 32, _patches_newgame.land_generator == 0);
|
||||
SetWindowWidgetDisabledState(w, 33, _patches_newgame.land_generator == 0);
|
||||
}
|
||||
/* Disable snowline if not hilly */
|
||||
SetWindowWidgetDisabledState(w, 22, _opt_newgame.landscape != LT_HILLY);
|
||||
/* Disable town and industry in SE */
|
||||
SetWindowWidgetDisabledState(w, 11, _game_mode == GM_EDITOR);
|
||||
SetWindowWidgetDisabledState(w, 12, _game_mode == GM_EDITOR);
|
||||
SetWindowWidgetDisabledState(w, 13, _game_mode == GM_EDITOR);
|
||||
SetWindowWidgetDisabledState(w, 14, _game_mode == GM_EDITOR);
|
||||
SetWindowWidgetDisabledState(w, 24, _game_mode == GM_EDITOR);
|
||||
SetWindowWidgetDisabledState(w, 25, _game_mode == GM_EDITOR);
|
||||
|
||||
SetWindowWidgetDisabledState(w, 18, _patches_newgame.starting_year <= MIN_YEAR);
|
||||
SetWindowWidgetDisabledState(w, 20, _patches_newgame.starting_year >= MAX_YEAR);
|
||||
SetWindowWidgetDisabledState(w, 21, _patches_newgame.snow_line_height <= 2 || _opt_newgame.landscape != LT_HILLY);
|
||||
SetWindowWidgetDisabledState(w, 23, _patches_newgame.snow_line_height >= 13 || _opt_newgame.landscape != LT_HILLY);
|
||||
|
||||
SetWindowWidgetLoweredState(w, 3, _opt_newgame.landscape == LT_NORMAL);
|
||||
SetWindowWidgetLoweredState(w, 4, _opt_newgame.landscape == LT_HILLY);
|
||||
SetWindowWidgetLoweredState(w, 5, _opt_newgame.landscape == LT_DESERT);
|
||||
SetWindowWidgetLoweredState(w, 6, _opt_newgame.landscape == LT_CANDY);
|
||||
DrawWindowWidgets(w);
|
||||
|
||||
y = (mode == GLWP_HEIGHTMAP) ? 22 : 0;
|
||||
|
||||
DrawString( 12, 91 + y, STR_MAPSIZE, 0);
|
||||
DrawString(119, 91 + y, mapsizes[_patches_newgame.map_x - 6], 0x10);
|
||||
DrawString(168, 91 + y, STR_BY, 0);
|
||||
DrawString(182, 91 + y, mapsizes[_patches_newgame.map_y - 6], 0x10);
|
||||
|
||||
DrawString( 12, 113 + y, STR_NUMBER_OF_TOWNS, 0);
|
||||
DrawString( 12, 131 + y, STR_NUMBER_OF_INDUSTRIES, 0);
|
||||
if (_game_mode == GM_EDITOR) {
|
||||
DrawString(118, 113 + y, STR_6836_OFF, 0x10);
|
||||
DrawString(118, 131 + y, STR_6836_OFF, 0x10);
|
||||
} else {
|
||||
DrawString(118, 113 + y, num_towns[_opt_newgame.diff.number_towns], 0x10);
|
||||
DrawString(118, 131 + y, num_inds[_opt_newgame.diff.number_industries], 0x10);
|
||||
}
|
||||
|
||||
DrawString( 12, 153 + y, STR_RANDOM_SEED, 0);
|
||||
DrawEditBox(w, &_genseed_query, SEED_EDIT);
|
||||
|
||||
DrawString(182, 113 + y, STR_DATE, 0);
|
||||
SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
|
||||
DrawStringCentered(271, 113 + y, STR_GENERATE_DATE, 0);
|
||||
|
||||
DrawString(182, 131 + y, STR_SNOW_LINE_HEIGHT, 0);
|
||||
SetDParam(0, _patches_newgame.snow_line_height);
|
||||
DrawStringCentered(303, 131 + y, STR_SNOW_LINE_HEIGHT_NUM, 0x10);
|
||||
|
||||
if (mode == GLWP_GENERATE) {
|
||||
DrawString( 12, 175, STR_LAND_GENERATOR, 0);
|
||||
DrawString(118, 175, landscape[_patches_newgame.land_generator], 0x10);
|
||||
|
||||
DrawString( 12, 193, STR_TREE_PLACER, 0);
|
||||
DrawString(118, 193, tree_placer[_patches_newgame.tree_placer], 0x10);
|
||||
|
||||
DrawString( 12, 211, STR_TERRAIN_TYPE, 0);
|
||||
DrawString(118, 211, elevations[_opt_newgame.diff.terrain_type], 0x10);
|
||||
|
||||
DrawString( 12, 229, STR_QUANTITY_OF_SEA_LAKES, 0);
|
||||
DrawString(118, 229, sea_lakes[_opt_newgame.diff.quantity_sea_lakes], 0x10);
|
||||
|
||||
DrawString( 12, 247, STR_SMOOTHNESS, 0);
|
||||
DrawString(118, 247, smoothness[_patches_newgame.tgen_smoothness], 0x10);
|
||||
} else {
|
||||
char buffer[512];
|
||||
|
||||
if (_patches_newgame.heightmap_rotation == HM_CLOCKWISE) {
|
||||
SetDParam(0, _heightmap_y);
|
||||
SetDParam(1, _heightmap_x);
|
||||
} else {
|
||||
SetDParam(0, _heightmap_x);
|
||||
SetDParam(1, _heightmap_y);
|
||||
}
|
||||
GetString(buffer, STR_HEIGHTMAP_SIZE, lastof(buffer));
|
||||
DrawStringRightAligned(326, 91, STR_HEIGHTMAP_SIZE, 0x10);
|
||||
|
||||
DrawString( 12, 91, STR_HEIGHTMAP_NAME, 0x10);
|
||||
SetDParam(0, _heightmap_str);
|
||||
DrawStringTruncated(114, 91, STR_ORANGE, 0x10, 326 - 114 - GetStringBoundingBox(buffer).width - 5);
|
||||
|
||||
DrawString( 12, 197, STR_TREE_PLACER, 0);
|
||||
DrawString(118, 197, tree_placer[_patches_newgame.tree_placer], 0x10);
|
||||
|
||||
DrawString( 12, 215, STR_HEIGHTMAP_ROTATION, 0);
|
||||
DrawString(118, 215, rotation[_patches_newgame.heightmap_rotation], 0x10);
|
||||
}
|
||||
|
||||
break;
|
||||
case WE_CLICK:
|
||||
switch (e->we.click.widget) {
|
||||
case 0: DeleteWindow(w); break;
|
||||
case 3: case 4: case 5: case 6:
|
||||
RaiseWindowWidget(w, _opt_newgame.landscape + 3);
|
||||
SetNewLandscapeType(e->we.click.widget - 3);
|
||||
break;
|
||||
case 7: case 8: // Mapsize X
|
||||
ShowDropDownMenu(w, mapsizes, _patches_newgame.map_x - 6, 8, 0, 0);
|
||||
break;
|
||||
case 9: case 10: // Mapsize Y
|
||||
ShowDropDownMenu(w, mapsizes, _patches_newgame.map_y - 6, 10, 0, 0);
|
||||
break;
|
||||
case 11: case 12: // Number of towns
|
||||
ShowDropDownMenu(w, num_towns, _opt_newgame.diff.number_towns, 12, 0, 0);
|
||||
break;
|
||||
case 13: case 14: // Number of industries
|
||||
ShowDropDownMenu(w, num_inds, _opt_newgame.diff.number_industries, 14, 0, 0);
|
||||
break;
|
||||
case 16: // Random seed
|
||||
_patches_newgame.generation_seed = InteractiveRandom();
|
||||
snprintf(_genseed_buffer, lengthof(_genseed_buffer), "%u", _patches_newgame.generation_seed);
|
||||
UpdateTextBufferSize(&_genseed_query.text);
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
case 17: // Generate
|
||||
if (mode == GLWP_HEIGHTMAP && (
|
||||
_heightmap_x * 2 < (1U << _patches_newgame.map_x) || _heightmap_x / 2 > (1U << _patches_newgame.map_x) ||
|
||||
_heightmap_y * 2 < (1U << _patches_newgame.map_y) || _heightmap_y / 2 > (1U << _patches_newgame.map_y))) {
|
||||
ShowQuery(
|
||||
STR_HEIGHTMAP_SCALE_WARNING_CAPTION,
|
||||
STR_HEIGHTMAP_SCALE_WARNING_MESSAGE,
|
||||
w,
|
||||
HeightmapScaledTooMuchCallback);
|
||||
} else {
|
||||
StartGeneratingLandscape(mode);
|
||||
}
|
||||
break;
|
||||
case 18: case 20: // Year buttons
|
||||
/* Don't allow too fast scrolling */
|
||||
if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
|
||||
HandleButtonClick(w, e->we.click.widget);
|
||||
SetWindowDirty(w);
|
||||
|
||||
_patches_newgame.starting_year = clamp(_patches_newgame.starting_year + e->we.click.widget - 19, MIN_YEAR, MAX_YEAR);
|
||||
}
|
||||
_left_button_clicked = false;
|
||||
break;
|
||||
case 19: // Year text
|
||||
WP(w, def_d).data_3 = START_DATE_QUERY;
|
||||
SetDParam(0, _patches_newgame.starting_year);
|
||||
ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 8, 100, w, CS_NUMERAL);
|
||||
break;
|
||||
case 21: case 23: // Snow line buttons
|
||||
/* Don't allow too fast scrolling */
|
||||
if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
|
||||
HandleButtonClick(w, e->we.click.widget);
|
||||
SetWindowDirty(w);
|
||||
|
||||
_patches_newgame.snow_line_height = clamp(_patches_newgame.snow_line_height + e->we.click.widget - 22, 2, 13);
|
||||
}
|
||||
_left_button_clicked = false;
|
||||
break;
|
||||
case 22: // Snow line text
|
||||
WP(w, def_d).data_3 = SNOW_LINE_QUERY;
|
||||
SetDParam(0, _patches_newgame.snow_line_height);
|
||||
ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_SNOW_LINE_QUERY_CAPT, 3, 100, w, CS_NUMERAL);
|
||||
break;
|
||||
case 24: case 25: // Tree placer
|
||||
ShowDropDownMenu(w, tree_placer, _patches_newgame.tree_placer, 25, 0, 0);
|
||||
break;
|
||||
case 26: case 27: // Landscape generator OR Heightmap rotation
|
||||
if (mode == GLWP_HEIGHTMAP) {
|
||||
ShowDropDownMenu(w, rotation, _patches_newgame.heightmap_rotation, 27, 0, 0);
|
||||
} else {
|
||||
ShowDropDownMenu(w, landscape, _patches_newgame.land_generator, 27, 0, 0);
|
||||
}
|
||||
break;
|
||||
case 28: case 29: // Terrain type
|
||||
ShowDropDownMenu(w, elevations, _opt_newgame.diff.terrain_type, 29, 0, 0);
|
||||
break;
|
||||
case 30: case 31: // Water quantity
|
||||
ShowDropDownMenu(w, sea_lakes, _opt_newgame.diff.quantity_sea_lakes, 31, 0, 0);
|
||||
break;
|
||||
case 32: case 33: // Map smoothness
|
||||
ShowDropDownMenu(w, smoothness, _patches_newgame.tgen_smoothness, 33, 0, 0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_MOUSELOOP:
|
||||
HandleEditBox(w, &_genseed_query, SEED_EDIT);
|
||||
break;
|
||||
|
||||
case WE_KEYPRESS:
|
||||
HandleEditBoxKey(w, &_genseed_query, SEED_EDIT, e);
|
||||
/* the seed is unsigned, therefore atoi cannot be used.
|
||||
* As 2^32 - 1 (MAX_UVALUE(uint32)) is a 'magic' value
|
||||
* (use random seed) it should not be possible to be
|
||||
* entered into the input field; the generate seed
|
||||
* button can be used instead. */
|
||||
_patches_newgame.generation_seed = minu(strtoul(_genseed_buffer, NULL, sizeof(_genseed_buffer) - 1), MAX_UVALUE(uint32) - 1);
|
||||
break;
|
||||
|
||||
case WE_DROPDOWN_SELECT:
|
||||
switch (e->we.dropdown.button) {
|
||||
case 8: _patches_newgame.map_x = e->we.dropdown.index + 6; break;
|
||||
case 10: _patches_newgame.map_y = e->we.dropdown.index + 6; break;
|
||||
case 12:
|
||||
_opt_newgame.diff.number_towns = e->we.dropdown.index;
|
||||
if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
|
||||
DoCommandP(0, 2, _opt_newgame.diff.number_towns, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
|
||||
break;
|
||||
case 14:
|
||||
_opt_newgame.diff.number_industries = e->we.dropdown.index;
|
||||
if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
|
||||
DoCommandP(0, 3, _opt_newgame.diff.number_industries, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
|
||||
break;
|
||||
case 25:
|
||||
_patches_newgame.tree_placer = e->we.dropdown.index;
|
||||
break;
|
||||
case 27:
|
||||
if (mode == GLWP_HEIGHTMAP) {
|
||||
_patches_newgame.heightmap_rotation = e->we.dropdown.index;
|
||||
} else {
|
||||
_patches_newgame.land_generator = e->we.dropdown.index;
|
||||
}
|
||||
break;
|
||||
case 29:
|
||||
_opt_newgame.diff.terrain_type = e->we.dropdown.index;
|
||||
if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
|
||||
DoCommandP(0, 12, _opt_newgame.diff.terrain_type, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
|
||||
break;
|
||||
case 31:
|
||||
_opt_newgame.diff.quantity_sea_lakes = e->we.dropdown.index;
|
||||
if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
|
||||
DoCommandP(0, 13, _opt_newgame.diff.quantity_sea_lakes, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
|
||||
break;
|
||||
case 33:
|
||||
_patches_newgame.tgen_smoothness = e->we.dropdown.index;
|
||||
break;
|
||||
}
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
|
||||
case WE_ON_EDIT_TEXT: {
|
||||
if (e->we.edittext.str != NULL) {
|
||||
int32 value = atoi(e->we.edittext.str);
|
||||
|
||||
switch (WP(w, def_d).data_3) {
|
||||
case START_DATE_QUERY:
|
||||
InvalidateWidget(w, 19);
|
||||
_patches_newgame.starting_year = clamp(value, MIN_YEAR, MAX_YEAR);
|
||||
break;
|
||||
case SNOW_LINE_QUERY:
|
||||
InvalidateWidget(w, 22);
|
||||
_patches_newgame.snow_line_height = clamp(value, 2, 13);
|
||||
break;
|
||||
}
|
||||
|
||||
SetWindowDirty(w);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const WindowDesc _generate_landscape_desc = {
|
||||
WDP_CENTER, WDP_CENTER, 338, 268,
|
||||
WC_GENERATE_LANDSCAPE, 0,
|
||||
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
|
||||
_generate_landscape_widgets,
|
||||
GenerateLandscapeWndProc,
|
||||
};
|
||||
|
||||
const WindowDesc _heightmap_load_desc = {
|
||||
WDP_CENTER, WDP_CENTER, 338, 236,
|
||||
WC_GENERATE_LANDSCAPE, 0,
|
||||
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
|
||||
_heightmap_load_widgets,
|
||||
GenerateLandscapeWndProc,
|
||||
};
|
||||
|
||||
static void _ShowGenerateLandscape(glwp_modes mode)
|
||||
{
|
||||
Window *w;
|
||||
|
||||
/* Don't kill WC_GENERATE_LANDSCAPE:GLWP_SCENARIO, because it resets
|
||||
* _goto_editor, which we maybe need later on. */
|
||||
DeleteWindowById(WC_GENERATE_LANDSCAPE, GLWP_GENERATE);
|
||||
DeleteWindowById(WC_GENERATE_LANDSCAPE, GLWP_HEIGHTMAP);
|
||||
|
||||
/* Always give a new seed if not editor */
|
||||
if (_game_mode != GM_EDITOR) _patches_newgame.generation_seed = InteractiveRandom();
|
||||
|
||||
if (mode == GLWP_HEIGHTMAP) {
|
||||
if (_heightmap_str != STR_NULL) DeleteName(_heightmap_str);
|
||||
|
||||
_heightmap_x = 0;
|
||||
_heightmap_y = 0;
|
||||
_heightmap_str = AllocateName(_file_to_saveload.title, 0);
|
||||
/* If the function returns negative, it means there was a problem loading the heightmap */
|
||||
if (!GetHeightmapDimensions(_file_to_saveload.name, &_heightmap_x, &_heightmap_y))
|
||||
return;
|
||||
}
|
||||
|
||||
w = AllocateWindowDescFront((mode == GLWP_HEIGHTMAP) ? &_heightmap_load_desc : &_generate_landscape_desc, mode);
|
||||
|
||||
if (w != NULL) {
|
||||
|
||||
InvalidateWindow(WC_GENERATE_LANDSCAPE, mode);
|
||||
}
|
||||
}
|
||||
|
||||
void ShowGenerateLandscape(void)
|
||||
{
|
||||
_ShowGenerateLandscape(GLWP_GENERATE);
|
||||
}
|
||||
|
||||
void ShowHeightmapLoad(void)
|
||||
{
|
||||
_ShowGenerateLandscape(GLWP_HEIGHTMAP);
|
||||
}
|
||||
|
||||
void StartNewGameWithoutGUI(uint seed)
|
||||
{
|
||||
/* GenerateWorld takes care of the possible GENERATE_NEW_SEED value in 'seed' */
|
||||
_patches_newgame.generation_seed = seed;
|
||||
|
||||
StartGeneratingLandscape(GLWP_GENERATE);
|
||||
}
|
||||
|
||||
|
||||
void CreateScenarioWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
static const StringID mapsizes[] = {STR_64, STR_128, STR_256, STR_512, STR_1024, STR_2048, INVALID_STRING_ID};
|
||||
|
||||
switch (e->event) {
|
||||
case WE_CREATE: LowerWindowWidget(w, _opt_newgame.landscape + 3); break;
|
||||
|
||||
case WE_PAINT:
|
||||
SetWindowWidgetDisabledState(w, 14, _patches_newgame.starting_year <= MIN_YEAR);
|
||||
SetWindowWidgetDisabledState(w, 16, _patches_newgame.starting_year >= MAX_YEAR);
|
||||
SetWindowWidgetDisabledState(w, 17, _patches_newgame.se_flat_world_height <= 0);
|
||||
SetWindowWidgetDisabledState(w, 19, _patches_newgame.se_flat_world_height >= 15);
|
||||
|
||||
SetWindowWidgetLoweredState(w, 3, _opt_newgame.landscape == LT_NORMAL);
|
||||
SetWindowWidgetLoweredState(w, 4, _opt_newgame.landscape == LT_HILLY);
|
||||
SetWindowWidgetLoweredState(w, 5, _opt_newgame.landscape == LT_DESERT);
|
||||
SetWindowWidgetLoweredState(w, 6, _opt_newgame.landscape == LT_CANDY);
|
||||
DrawWindowWidgets(w);
|
||||
|
||||
DrawString( 12, 96, STR_MAPSIZE, 0);
|
||||
DrawString(167, 96, mapsizes[_patches_newgame.map_x - 6], 0x10);
|
||||
DrawString(216, 96, STR_BY, 0);
|
||||
DrawString(230, 96, mapsizes[_patches_newgame.map_y - 6], 0x10);
|
||||
|
||||
DrawString(162, 118, STR_DATE, 0);
|
||||
SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
|
||||
DrawStringCentered(271, 118, STR_GENERATE_DATE, 0);
|
||||
|
||||
DrawString(162, 136, STR_FLAT_WORLD_HEIGHT, 0);
|
||||
SetDParam(0, _patches_newgame.se_flat_world_height);
|
||||
DrawStringCentered(303, 136, STR_FLAT_WORLD_HEIGHT_NUM, 0x10);
|
||||
|
||||
break;
|
||||
case WE_CLICK:
|
||||
switch (e->we.click.widget) {
|
||||
case 0: DeleteWindow(w); break;
|
||||
case 3: case 4: case 5: case 6:
|
||||
RaiseWindowWidget(w, _opt_newgame.landscape + 3);
|
||||
SetNewLandscapeType(e->we.click.widget - 3);
|
||||
break;
|
||||
case 7: case 8: // Mapsize X
|
||||
ShowDropDownMenu(w, mapsizes, _patches_newgame.map_x - 6, 8, 0, 0);
|
||||
break;
|
||||
case 9: case 10: // Mapsize Y
|
||||
ShowDropDownMenu(w, mapsizes, _patches_newgame.map_y - 6, 10, 0, 0);
|
||||
break;
|
||||
case 11: // Empty world / flat world
|
||||
StartGeneratingLandscape(GLWP_SCENARIO);
|
||||
break;
|
||||
case 12: // Generate
|
||||
_goto_editor = true;
|
||||
ShowGenerateLandscape();
|
||||
break;
|
||||
case 13: // Heightmap
|
||||
_goto_editor = true;
|
||||
ShowSaveLoadDialog(SLD_LOAD_HEIGHTMAP);
|
||||
break;
|
||||
case 14: case 16: // Year buttons
|
||||
/* Don't allow too fast scrolling */
|
||||
if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
|
||||
HandleButtonClick(w, e->we.click.widget);
|
||||
SetWindowDirty(w);
|
||||
|
||||
_patches_newgame.starting_year = clamp(_patches_newgame.starting_year + e->we.click.widget - 15, MIN_YEAR, MAX_YEAR);
|
||||
}
|
||||
_left_button_clicked = false;
|
||||
break;
|
||||
case 15: // Year text
|
||||
WP(w, def_d).data_3 = START_DATE_QUERY;
|
||||
SetDParam(0, _patches_newgame.starting_year);
|
||||
ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 8, 100, w, CS_NUMERAL);
|
||||
break;
|
||||
case 17: case 19: // Height level buttons
|
||||
/* Don't allow too fast scrolling */
|
||||
if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
|
||||
HandleButtonClick(w, e->we.click.widget);
|
||||
SetWindowDirty(w);
|
||||
|
||||
_patches_newgame.se_flat_world_height = clamp(_patches_newgame.se_flat_world_height + e->we.click.widget - 18, 0, 15);
|
||||
}
|
||||
_left_button_clicked = false;
|
||||
break;
|
||||
case 18: // Height level text
|
||||
WP(w, def_d).data_3 = FLAT_WORLD_HEIGHT_QUERY;
|
||||
SetDParam(0, _patches_newgame.se_flat_world_height);
|
||||
ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_FLAT_WORLD_HEIGHT_QUERY_CAPT, 3, 100, w, CS_NUMERAL);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_DROPDOWN_SELECT:
|
||||
switch (e->we.dropdown.button) {
|
||||
case 8: _patches_newgame.map_x = e->we.dropdown.index + 6; break;
|
||||
case 10: _patches_newgame.map_y = e->we.dropdown.index + 6; break;
|
||||
}
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
|
||||
case WE_DESTROY:
|
||||
_goto_editor = false;
|
||||
break;
|
||||
|
||||
case WE_ON_EDIT_TEXT: {
|
||||
if (e->we.edittext.str != NULL) {
|
||||
int32 value = atoi(e->we.edittext.str);
|
||||
|
||||
switch (WP(w, def_d).data_3) {
|
||||
case START_DATE_QUERY:
|
||||
InvalidateWidget(w, 15);
|
||||
_patches_newgame.starting_year = clamp(value, MIN_YEAR, MAX_YEAR);
|
||||
break;
|
||||
case FLAT_WORLD_HEIGHT_QUERY:
|
||||
InvalidateWidget(w, 18);
|
||||
_patches_newgame.se_flat_world_height = clamp(value, 0, 15);
|
||||
break;
|
||||
}
|
||||
|
||||
SetWindowDirty(w);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Widget _create_scenario_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 13, 11, 337, 0, 13, STR_SE_CAPTION, STR_NULL},
|
||||
{ WWT_PANEL, RESIZE_NONE, 13, 0, 337, 14, 179, 0x0, STR_NULL},
|
||||
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 10, 86, 24, 78, SPR_SELECT_TEMPERATE, STR_030E_SELECT_TEMPERATE_LANDSCAPE},
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 90, 166, 24, 78, SPR_SELECT_SUB_ARCTIC, STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 170, 246, 24, 78, SPR_SELECT_SUB_TROPICAL, STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 250, 326, 24, 78, SPR_SELECT_TOYLAND, STR_0311_SELECT_TOYLAND_LANDSCAPE},
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 162, 197, 95, 106, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 198, 209, 95, 106, STR_0225, STR_NULL}, // Mapsize X
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 228, 263, 95, 106, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 12, 264, 275, 95, 106, STR_0225, STR_NULL}, // Mapsize Y
|
||||
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 6, 12, 145, 117, 128, STR_SE_FLAT_WORLD, STR_SE_FLAT_WORLD_TIP}, // Empty (sea-level) map
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 6, 12, 145, 135, 146, STR_SE_RANDOM_LAND, STR_022A_GENERATE_RANDOM_LAND}, // Generate
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 6, 12, 145, 153, 164, STR_LOAD_GAME_HEIGHTMAP, STR_LOAD_SCEN_HEIGHTMAP}, // Heightmap
|
||||
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 12, 216, 227, 117, 128, SPR_ARROW_DOWN, STR_029E_MOVE_THE_STARTING_DATE},
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 228, 314, 117, 128, 0x0, STR_NULL},
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 117, 128, SPR_ARROW_UP, STR_029F_MOVE_THE_STARTING_DATE},
|
||||
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 12, 282, 293, 135, 146, SPR_ARROW_DOWN, STR_FLAT_WORLD_HEIGHT_DOWN},
|
||||
{ WWT_PANEL, RESIZE_NONE, 12, 294, 314, 135, 146, 0x0, STR_NULL},
|
||||
{ WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 135, 146, SPR_ARROW_UP, STR_FLAT_WORLD_HEIGHT_UP},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
const WindowDesc _create_scenario_desc = {
|
||||
WDP_CENTER, WDP_CENTER, 338, 180,
|
||||
WC_GENERATE_LANDSCAPE, 0,
|
||||
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
|
||||
_create_scenario_widgets,
|
||||
CreateScenarioWndProc,
|
||||
};
|
||||
|
||||
void ShowCreateScenario(void)
|
||||
{
|
||||
DeleteWindowByClass(WC_GENERATE_LANDSCAPE);
|
||||
AllocateWindowDescFront(&_create_scenario_desc, GLWP_SCENARIO);
|
||||
}
|
||||
|
||||
|
||||
static const Widget _show_terrain_progress_widgets[] = {
|
||||
{ WWT_CAPTION, RESIZE_NONE, 14, 0, 180, 0, 13, STR_GENERATION_WORLD, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 14, 0, 180, 14, 96, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 15, 20, 161, 74, 85, STR_GENERATION_ABORT, STR_NULL}, // Abort button
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
typedef struct tp_info {
|
||||
uint percent;
|
||||
StringID class;
|
||||
uint current;
|
||||
uint total;
|
||||
int timer;
|
||||
} tp_info;
|
||||
|
||||
static tp_info _tp;
|
||||
|
||||
static void AbortGeneratingWorldCallback(Window *w, bool confirmed)
|
||||
{
|
||||
if (confirmed) {
|
||||
AbortGeneratingWorld();
|
||||
} else if (IsGeneratingWorld() && !IsGeneratingWorldAborted()) {
|
||||
SetMouseCursor(SPR_CURSOR_ZZZ);
|
||||
}
|
||||
}
|
||||
|
||||
static void ShowTerrainProgressProc(Window* w, WindowEvent* e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_CLICK:
|
||||
switch (e->we.click.widget) {
|
||||
case 2:
|
||||
if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE);
|
||||
ShowQuery(
|
||||
STR_GENERATION_ABORT_CAPTION,
|
||||
STR_GENERATION_ABORT_MESSAGE,
|
||||
w,
|
||||
AbortGeneratingWorldCallback
|
||||
);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_PAINT:
|
||||
DrawWindowWidgets(w);
|
||||
|
||||
/* Draw the % complete with a bar and a text */
|
||||
DrawFrameRect(19, 20, (w->width - 18), 37, 14, FR_BORDERONLY);
|
||||
DrawFrameRect(20, 21, (int)((w->width - 40) * _tp.percent / 100) + 20, 36, 10, 0);
|
||||
SetDParam(0, _tp.percent);
|
||||
DrawStringCentered(90, 25, STR_PROGRESS, 0);
|
||||
|
||||
/* Tell which class we are generating */
|
||||
DrawStringCentered(90, 46, _tp.class, 0);
|
||||
|
||||
/* And say where we are in that class */
|
||||
SetDParam(0, _tp.current);
|
||||
SetDParam(1, _tp.total);
|
||||
DrawStringCentered(90, 58, STR_GENERATION_PROGRESS, 0);
|
||||
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const WindowDesc _show_terrain_progress_desc = {
|
||||
WDP_CENTER, WDP_CENTER, 181, 97,
|
||||
WC_GENERATE_PROGRESS_WINDOW, 0,
|
||||
WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
|
||||
_show_terrain_progress_widgets,
|
||||
ShowTerrainProgressProc
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the progress counters to the starting point.
|
||||
*/
|
||||
void PrepareGenerateWorldProgress(void)
|
||||
{
|
||||
_tp.class = STR_WORLD_GENERATION;
|
||||
_tp.current = 0;
|
||||
_tp.total = 0;
|
||||
_tp.percent = 0;
|
||||
_tp.timer = 0; // Forces to paint the progress window immediatelly
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the window where a user can follow the process of the map generation.
|
||||
*/
|
||||
void ShowGenerateWorldProgress(void)
|
||||
{
|
||||
AllocateWindowDescFront(&_show_terrain_progress_desc, 0);
|
||||
}
|
||||
|
||||
static void _SetGeneratingWorldProgress(gwp_class class, uint progress, uint total)
|
||||
{
|
||||
static const int percent_table[GWP_CLASS_COUNT + 1] = {0, 5, 15, 20, 40, 60, 65, 80, 85, 99, 100 };
|
||||
static const StringID class_table[GWP_CLASS_COUNT] = {
|
||||
STR_WORLD_GENERATION,
|
||||
STR_022E_LANDSCAPE_GENERATION,
|
||||
STR_CLEARING_TILES,
|
||||
STR_022F_TOWN_GENERATION,
|
||||
STR_0230_INDUSTRY_GENERATION,
|
||||
STR_UNMOVABLE_GENERATION,
|
||||
STR_TREE_GENERATION,
|
||||
STR_SETTINGUP_GAME,
|
||||
STR_PREPARING_TILELOOP,
|
||||
STR_PREPARING_GAME
|
||||
};
|
||||
|
||||
assert(class < GWP_CLASS_COUNT);
|
||||
|
||||
/* Do not run this function if we aren't in a thread */
|
||||
if (!IsGenerateWorldThreaded() && !_network_dedicated) return;
|
||||
|
||||
if (IsGeneratingWorldAborted()) HandleGeneratingWorldAbortion();
|
||||
|
||||
if (total == 0) {
|
||||
assert(_tp.class == class_table[class]);
|
||||
_tp.current += progress;
|
||||
} else {
|
||||
_tp.class = class_table[class];
|
||||
_tp.current = progress;
|
||||
_tp.total = total;
|
||||
_tp.percent = percent_table[class];
|
||||
}
|
||||
|
||||
/* Don't update the screen too often. So update it once in every 200ms.
|
||||
* However, the _tick_counter increases by 8 every 30ms, so compensate
|
||||
* for that. */
|
||||
if (!_network_dedicated && _tp.timer != 0 && _timer_counter - _tp.timer < (200 * 8 / 30)) return;
|
||||
|
||||
/* Percentage is about the number of completed tasks, so 'current - 1' */
|
||||
_tp.percent = percent_table[class] + (percent_table[class + 1] - percent_table[class]) * (_tp.current == 0 ? 0 : _tp.current - 1) / _tp.total;
|
||||
_tp.timer = _timer_counter;
|
||||
|
||||
if (_network_dedicated) {
|
||||
static uint last_percent = 0;
|
||||
|
||||
/* Never display 0% */
|
||||
if (_tp.percent == 0) return;
|
||||
/* Reset if percent is lower then the last recorded */
|
||||
if (_tp.percent < last_percent) last_percent = 0;
|
||||
/* Display every 5%, but 6% is also very valid.. just not smaller steps then 5% */
|
||||
if (_tp.percent % 5 != 0 && _tp.percent <= last_percent + 5) return;
|
||||
/* Never show steps smaller then 2%, even if it is a mod 5% */
|
||||
if (_tp.percent <= last_percent + 2) return;
|
||||
|
||||
DEBUG(net, 1, "Map generation percentage complete: %d", _tp.percent);
|
||||
last_percent = _tp.percent;
|
||||
|
||||
/* Don't continue as dedicated never has a thread running */
|
||||
return;
|
||||
}
|
||||
|
||||
InvalidateWindow(WC_GENERATE_PROGRESS_WINDOW, 0);
|
||||
MarkWholeScreenDirty();
|
||||
SetGeneratingWorldPaintStatus(true);
|
||||
|
||||
/* We wait here till the paint is done, so we don't read and write
|
||||
* on the same tile at the same moment. Nasty hack, but that happens
|
||||
* if you implement threading afterwards */
|
||||
while (IsGeneratingWorldReadyForPaint()) { CSleep(10); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the total of a stage of the world generation.
|
||||
* @param class the current class we are in.
|
||||
* @param total Set the total expected items for this class.
|
||||
*
|
||||
* Warning: this function isn't clever. Don't go from class 4 to 3. Go upwards, always.
|
||||
* Also, progress works if total is zero, total works if progress is zero.
|
||||
*/
|
||||
void SetGeneratingWorldProgress(gwp_class class, uint total)
|
||||
{
|
||||
if (total == 0) return;
|
||||
|
||||
_SetGeneratingWorldProgress(class, 0, total);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the current stage of the world generation with one.
|
||||
* @param class the current class we are in.
|
||||
*
|
||||
* Warning: this function isn't clever. Don't go from class 4 to 3. Go upwards, always.
|
||||
* Also, progress works if total is zero, total works if progress is zero.
|
||||
*/
|
||||
void IncreaseGeneratingWorldProgress(gwp_class class)
|
||||
{
|
||||
/* In fact the param 'class' isn't needed.. but for some security reasons, we want it around */
|
||||
_SetGeneratingWorldProgress(class, 1, 0);
|
||||
}
|
||||
163
src/gfx.h
Normal file
163
src/gfx.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef GFX_H
|
||||
#define GFX_H
|
||||
|
||||
typedef byte Pixel;
|
||||
|
||||
struct DrawPixelInfo {
|
||||
Pixel *dst_ptr;
|
||||
int left, top, width, height;
|
||||
int pitch;
|
||||
uint16 zoom;
|
||||
};
|
||||
|
||||
|
||||
typedef struct CursorVars {
|
||||
Point pos, size, offs, delta; ///< position, size, offset from top-left, and movement
|
||||
Point draw_pos, draw_size; ///< position and size bounding-box for drawing
|
||||
CursorID sprite; ///< current image of cursor
|
||||
|
||||
int wheel; ///< mouse wheel movement
|
||||
const CursorID *animate_list, *animate_cur; ///< in case of animated cursor, list of frames
|
||||
uint animate_timeout; ///< current frame in list of animated cursor
|
||||
|
||||
bool visible; ///< cursor is visible
|
||||
bool dirty; ///< the rect occupied by the mouse is dirty (redraw)
|
||||
bool fix_at; ///< mouse is moving, but cursor is not (used for scrolling)
|
||||
bool in_window; ///< mouse inside this window, determines drawing logic
|
||||
} CursorVars;
|
||||
|
||||
|
||||
typedef enum FontSizes {
|
||||
FS_NORMAL,
|
||||
FS_SMALL,
|
||||
FS_LARGE,
|
||||
FS_END,
|
||||
} FontSize;
|
||||
|
||||
|
||||
void RedrawScreenRect(int left, int top, int right, int bottom);
|
||||
void GfxScroll(int left, int top, int width, int height, int xo, int yo);
|
||||
|
||||
|
||||
// XXX doesn't really belong here, but the only
|
||||
// consumers always use it in conjunction with DoDrawString()
|
||||
#define UPARROW "\xEE\x8A\x80"
|
||||
#define DOWNARROW "\xEE\x8A\xAA"
|
||||
|
||||
|
||||
int DrawStringCentered(int x, int y, StringID str, uint16 color);
|
||||
int DrawStringCenteredTruncated(int xl, int xr, int y, StringID str, uint16 color);
|
||||
int DoDrawStringCentered(int x, int y, const char *str, uint16 color);
|
||||
|
||||
int DrawString(int x, int y, StringID str, uint16 color);
|
||||
int DrawStringTruncated(int x, int y, StringID str, uint16 color, uint maxw);
|
||||
|
||||
int DoDrawString(const char *string, int x, int y, uint16 color);
|
||||
int DoDrawStringTruncated(const char *str, int x, int y, uint16 color, uint maxw);
|
||||
|
||||
void DrawStringCenterUnderline(int x, int y, StringID str, uint16 color);
|
||||
void DrawStringCenterUnderlineTruncated(int xl, int xr, int y, StringID str, uint16 color);
|
||||
|
||||
int DrawStringRightAligned(int x, int y, StringID str, uint16 color);
|
||||
void DrawStringRightAlignedTruncated(int x, int y, StringID str, uint16 color, uint maxw);
|
||||
void DrawStringRightAlignedUnderline(int x, int y, StringID str, uint16 color);
|
||||
|
||||
void GfxFillRect(int left, int top, int right, int bottom, int color);
|
||||
void GfxDrawLine(int left, int top, int right, int bottom, int color);
|
||||
|
||||
BoundingRect GetStringBoundingBox(const char *str);
|
||||
uint32 FormatStringLinebreaks(char *str, int maxw);
|
||||
void LoadStringWidthTable(void);
|
||||
void DrawStringMultiCenter(int x, int y, StringID str, int maxw);
|
||||
uint DrawStringMultiLine(int x, int y, StringID str, int maxw);
|
||||
void DrawDirtyBlocks(void);
|
||||
void SetDirtyBlocks(int left, int top, int right, int bottom);
|
||||
void MarkWholeScreenDirty(void);
|
||||
|
||||
void GfxInitPalettes(void);
|
||||
|
||||
bool FillDrawPixelInfo(DrawPixelInfo* n, int left, int top, int width, int height);
|
||||
|
||||
/* window.c */
|
||||
void DrawOverlappedWindowForAll(int left, int top, int right, int bottom);
|
||||
|
||||
void SetMouseCursor(uint cursor);
|
||||
void SetAnimatedMouseCursor(const CursorID *table);
|
||||
void CursorTick(void);
|
||||
void DrawMouseCursor(void);
|
||||
void ScreenSizeChanged(void);
|
||||
void UndrawMouseCursor(void);
|
||||
bool ChangeResInGame(int w, int h);
|
||||
void SortResolutions(int count);
|
||||
void ToggleFullScreen(bool fs);
|
||||
|
||||
/* gfx.c */
|
||||
#define ASCII_LETTERSTART 32
|
||||
extern FontSize _cur_fontsize;
|
||||
|
||||
byte GetCharacterWidth(FontSize size, uint32 key);
|
||||
|
||||
static inline byte GetCharacterHeight(FontSize size)
|
||||
{
|
||||
switch (size) {
|
||||
default: NOT_REACHED();
|
||||
case FS_NORMAL: return 10;
|
||||
case FS_SMALL: return 6;
|
||||
case FS_LARGE: return 18;
|
||||
}
|
||||
}
|
||||
|
||||
VARDEF DrawPixelInfo _screen;
|
||||
VARDEF DrawPixelInfo *_cur_dpi;
|
||||
VARDEF CursorVars _cursor;
|
||||
|
||||
enum {
|
||||
COLOUR_DARK_BLUE,
|
||||
COLOUR_PALE_GREEN,
|
||||
COLOUR_PINK,
|
||||
COLOUR_YELLOW,
|
||||
COLOUR_RED,
|
||||
COLOUR_LIGHT_BLUE,
|
||||
COLOUR_GREEN,
|
||||
COLOUR_DARK_GREEN,
|
||||
COLOUR_BLUE,
|
||||
COLOUR_CREAM,
|
||||
COLOUR_MAUVE,
|
||||
COLOUR_PURPLE,
|
||||
COLOUR_ORANGE,
|
||||
COLOUR_BROWN,
|
||||
COLOUR_GREY,
|
||||
COLOUR_WHITE
|
||||
};
|
||||
|
||||
/**
|
||||
* All 16 colour gradients
|
||||
* 8 colours per gradient from darkest (0) to lightest (7)
|
||||
*/
|
||||
VARDEF byte _colour_gradient[16][8];
|
||||
|
||||
VARDEF int _pal_first_dirty;
|
||||
VARDEF int _pal_last_dirty;
|
||||
|
||||
VARDEF bool _use_dos_palette;
|
||||
|
||||
typedef struct Colour {
|
||||
byte r;
|
||||
byte g;
|
||||
byte b;
|
||||
} Colour;
|
||||
|
||||
extern Colour _cur_palette[256];
|
||||
|
||||
|
||||
typedef enum StringColorFlags {
|
||||
IS_PALETTE_COLOR = 0x100, // color value is already a real palette color index, not an index of a StringColor
|
||||
} StringColorFlags;
|
||||
|
||||
#ifdef _DEBUG
|
||||
extern bool _dbg_screen_rect;
|
||||
#endif
|
||||
|
||||
#endif /* GFX_H */
|
||||
390
src/gfxinit.c
Normal file
390
src/gfxinit.c
Normal file
@@ -0,0 +1,390 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "debug.h"
|
||||
#include "functions.h"
|
||||
#include "gfx.h"
|
||||
#include "gfxinit.h"
|
||||
#include "spritecache.h"
|
||||
#include "table/sprites.h"
|
||||
#include "fileio.h"
|
||||
#include "string.h"
|
||||
#include "newgrf.h"
|
||||
#include "md5.h"
|
||||
#include "variables.h"
|
||||
#include "fontcache.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef struct MD5File {
|
||||
const char * const filename; // filename
|
||||
const md5_byte_t hash[16]; // md5 sum of the file
|
||||
} MD5File;
|
||||
|
||||
typedef struct FileList {
|
||||
const MD5File basic[4]; // grf files that always have to be loaded
|
||||
const MD5File landscape[3]; // landscape specific grf files
|
||||
} FileList;
|
||||
|
||||
enum {
|
||||
SKIP = 0xFFFE,
|
||||
END = 0xFFFF
|
||||
};
|
||||
|
||||
#include "table/files.h"
|
||||
#include "table/landscape_sprite.h"
|
||||
|
||||
static const SpriteID * const _landscape_spriteindexes[] = {
|
||||
_landscape_spriteindexes_1,
|
||||
_landscape_spriteindexes_2,
|
||||
_landscape_spriteindexes_3,
|
||||
};
|
||||
|
||||
static const SpriteID * const _slopes_spriteindexes[] = {
|
||||
_slopes_spriteindexes_0,
|
||||
_slopes_spriteindexes_1,
|
||||
_slopes_spriteindexes_2,
|
||||
_slopes_spriteindexes_3,
|
||||
};
|
||||
|
||||
|
||||
static uint LoadGrfFile(const char* filename, uint load_index, int file_index)
|
||||
{
|
||||
uint load_index_org = load_index;
|
||||
|
||||
FioOpenFile(file_index, filename);
|
||||
|
||||
DEBUG(sprite, 2, "Reading grf-file '%s'", filename);
|
||||
|
||||
while (LoadNextSprite(load_index, file_index)) {
|
||||
load_index++;
|
||||
if (load_index >= MAX_SPRITES) {
|
||||
error("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
|
||||
}
|
||||
}
|
||||
DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
|
||||
|
||||
return load_index - load_index_org;
|
||||
}
|
||||
|
||||
|
||||
static void LoadGrfIndexed(const char* filename, const SpriteID* index_tbl, int file_index)
|
||||
{
|
||||
uint start;
|
||||
|
||||
FioOpenFile(file_index, filename);
|
||||
|
||||
DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
|
||||
|
||||
while ((start = *index_tbl++) != END) {
|
||||
uint end = *index_tbl++;
|
||||
|
||||
if (start == SKIP) { // skip sprites (amount in second var)
|
||||
SkipSprites(end);
|
||||
} else { // load sprites and use indexes from start to end
|
||||
do {
|
||||
#ifdef NDEBUG
|
||||
LoadNextSprite(start, file_index);
|
||||
#else
|
||||
bool b = LoadNextSprite(start, file_index);
|
||||
assert(b);
|
||||
#endif
|
||||
} while (++start <= end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Check that the supplied MD5 hash matches that stored for the supplied filename */
|
||||
static bool CheckMD5Digest(const MD5File file, md5_byte_t *digest, bool warn)
|
||||
{
|
||||
if (memcmp(file.hash, digest, sizeof(file.hash)) == 0) return true;
|
||||
if (warn) fprintf(stderr, "MD5 of %s is ****INCORRECT**** - File Corrupt.\n", file.filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Calculate and check the MD5 hash of the supplied filename.
|
||||
* returns true if the checksum is correct */
|
||||
static bool FileMD5(const MD5File file, bool warn)
|
||||
{
|
||||
FILE *f;
|
||||
char buf[MAX_PATH];
|
||||
|
||||
// open file
|
||||
snprintf(buf, lengthof(buf), "%s%s", _paths.data_dir, file.filename);
|
||||
f = fopen(buf, "rb");
|
||||
|
||||
#if !defined(WIN32)
|
||||
if (f == NULL) {
|
||||
strtolower(buf + strlen(_paths.data_dir) - 1);
|
||||
f = fopen(buf, "rb");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (f != NULL) {
|
||||
md5_state_t filemd5state;
|
||||
md5_byte_t buffer[1024];
|
||||
md5_byte_t digest[16];
|
||||
size_t len;
|
||||
|
||||
md5_init(&filemd5state);
|
||||
while ((len = fread(buffer, 1, sizeof(buffer), f)) != 0)
|
||||
md5_append(&filemd5state, buffer, len);
|
||||
|
||||
if (ferror(f) && warn) fprintf(stderr, "Error Reading from %s \n", buf);
|
||||
fclose(f);
|
||||
|
||||
md5_finish(&filemd5state, digest);
|
||||
return CheckMD5Digest(file, digest, warn);
|
||||
} else { // file not found
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Checks, if either the Windows files exist (TRG1R.GRF) or the DOS files (TRG1.GRF)
|
||||
* by comparing the MD5 checksums of the files. _use_dos_palette is set accordingly.
|
||||
* If neither are found, Windows palette is assumed.
|
||||
*
|
||||
* (Note: Also checks sample.cat for corruption) */
|
||||
void CheckExternalFiles(void)
|
||||
{
|
||||
uint i;
|
||||
// count of files from this version
|
||||
uint dos = 0;
|
||||
uint win = 0;
|
||||
|
||||
for (i = 0; i < 2; i++) if (FileMD5(files_dos.basic[i], true)) dos++;
|
||||
for (i = 0; i < 3; i++) if (FileMD5(files_dos.landscape[i], true)) dos++;
|
||||
|
||||
for (i = 0; i < 2; i++) if (FileMD5(files_win.basic[i], true)) win++;
|
||||
for (i = 0; i < 3; i++) if (FileMD5(files_win.landscape[i], true)) win++;
|
||||
|
||||
if (!FileMD5(sample_cat_win, false) && !FileMD5(sample_cat_dos, false))
|
||||
fprintf(stderr, "Your sample.cat file is corrupted or missing!\n");
|
||||
|
||||
for (i = 0; i < lengthof(files_openttd); i++) {
|
||||
if (!FileMD5(files_openttd[i], false)) {
|
||||
fprintf(stderr, "Your %s file is corrupted or missing!\n", files_openttd[i].filename);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* forced DOS palette via command line -> leave it that way
|
||||
* all Windows files present -> Windows palette
|
||||
* all DOS files present -> DOS palette
|
||||
* no Windows files present and any DOS file present -> DOS palette
|
||||
* otherwise -> Windows palette
|
||||
*/
|
||||
if (_use_dos_palette) {
|
||||
return;
|
||||
} else if (win == 5) {
|
||||
_use_dos_palette = false;
|
||||
} else if (dos == 5 || (win == 0 && dos > 0)) {
|
||||
_use_dos_palette = true;
|
||||
} else {
|
||||
_use_dos_palette = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const SpriteID trg1idx[] = {
|
||||
0, 1, // Mouse cursor, ZZZ
|
||||
/* Medium font */
|
||||
2, 92, // ' ' till 'z'
|
||||
SKIP, 36,
|
||||
160, 160, // Move <20> to the correct position
|
||||
98, 98, // Up arrow
|
||||
131, 133,
|
||||
SKIP, 1, // skip currency sign
|
||||
135, 135,
|
||||
SKIP, 1,
|
||||
137, 137,
|
||||
SKIP, 1,
|
||||
139, 139,
|
||||
140, 140, // TODO Down arrow
|
||||
141, 141,
|
||||
142, 142, // TODO Check mark
|
||||
143, 143, // TODO Cross
|
||||
144, 144,
|
||||
145, 145, // TODO Right arrow
|
||||
146, 149,
|
||||
118, 122, // Transport markers
|
||||
SKIP, 2,
|
||||
157, 157,
|
||||
114, 115, // Small up/down arrows
|
||||
SKIP, 1,
|
||||
161, 225,
|
||||
/* Small font */
|
||||
226, 316, // ' ' till 'z'
|
||||
SKIP, 36,
|
||||
384, 384, // Move <20> to the correct position
|
||||
322, 322, // Up arrow
|
||||
355, 357,
|
||||
SKIP, 1, // skip currency sign
|
||||
359, 359,
|
||||
SKIP, 1,
|
||||
361, 361,
|
||||
SKIP, 1,
|
||||
363, 363,
|
||||
364, 364, // TODO Down arrow
|
||||
365, 366,
|
||||
SKIP, 1,
|
||||
368, 368,
|
||||
369, 369, // TODO Right arrow
|
||||
370, 373,
|
||||
SKIP, 7,
|
||||
381, 381,
|
||||
SKIP, 3,
|
||||
385, 449,
|
||||
/* Big font */
|
||||
450, 540, // ' ' till 'z'
|
||||
SKIP, 36,
|
||||
608, 608, // Move <20> to the correct position
|
||||
SKIP, 1,
|
||||
579, 581,
|
||||
SKIP, 1,
|
||||
583, 583,
|
||||
SKIP, 5,
|
||||
589, 589,
|
||||
SKIP, 15,
|
||||
605, 605,
|
||||
SKIP, 3,
|
||||
609, 625,
|
||||
SKIP, 1,
|
||||
627, 632,
|
||||
SKIP, 1,
|
||||
634, 639,
|
||||
SKIP, 1,
|
||||
641, 657,
|
||||
SKIP, 1,
|
||||
659, 664,
|
||||
SKIP, 2,
|
||||
667, 671,
|
||||
SKIP, 1,
|
||||
673, 673,
|
||||
/* Graphics */
|
||||
674, 4792,
|
||||
END
|
||||
};
|
||||
|
||||
/* NOTE: When adding a normal sprite, increase OPENTTD_SPRITES_COUNT with the
|
||||
* amount of sprites and add them to the end of the list, with the index of
|
||||
* the old sprite-count offset from SPR_OPENTTD_BASE. With this there is no
|
||||
* correspondence of any kind with the ID's in the grf file, but results in
|
||||
* a maximum use of sprite slots. */
|
||||
static const SpriteID _openttd_grf_indexes[] = {
|
||||
SPR_IMG_AUTORAIL, SPR_CURSOR_WAYPOINT, // icons etc
|
||||
134, 134, // euro symbol medium size
|
||||
582, 582, // euro symbol large size
|
||||
358, 358, // euro symbol tiny
|
||||
SPR_CURSOR_CANAL, SPR_IMG_FASTFORWARD, // more icons
|
||||
648, 648, // nordic char: <20>
|
||||
616, 616, // nordic char: <20>
|
||||
666, 666, // nordic char: <20>
|
||||
634, 634, // nordic char: <20>
|
||||
SPR_PIN_UP, SPR_CURSOR_CLONE_TRAIN, // more icons
|
||||
382, 383, // <20> <20> tiny
|
||||
158, 159, // <20> <20> medium
|
||||
606, 607, // <20> <20> large
|
||||
360, 360, // <20> tiny
|
||||
362, 362, // <20> tiny
|
||||
136, 136, // <20> medium
|
||||
138, 138, // <20> medium
|
||||
584, 584, // <20> large
|
||||
586, 586, // <20> large
|
||||
626, 626, // <20> large
|
||||
658, 658, // <20> large
|
||||
374, 374, // <20> tiny
|
||||
378, 378, // <20> tiny
|
||||
150, 150, // <20> medium
|
||||
154, 154, // <20> medium
|
||||
598, 598, // <20> large
|
||||
602, 602, // <20> large
|
||||
640, 640, // <20> large
|
||||
672, 672, // <20> large
|
||||
380, 380, // <20> tiny
|
||||
156, 156, // <20> medium
|
||||
604, 604, // <20> large
|
||||
317, 320, // { | } ~ tiny
|
||||
93, 96, // { | } ~ medium
|
||||
541, 544, // { | } ~ large
|
||||
SPR_HOUSE_ICON, SPR_HOUSE_ICON,
|
||||
585, 585, // <20> large
|
||||
587, 587, // <20> large
|
||||
592, 592, // <20> large
|
||||
594, 597, // <20> <20> <20> <20> large
|
||||
633, 633, // <20> large
|
||||
665, 665, // <20> large
|
||||
SPR_SELL_TRAIN, SPR_SHARED_ORDERS_ICON,
|
||||
377, 377, // <20> small
|
||||
153, 153, // <20> medium
|
||||
601, 601, // <20> large
|
||||
END
|
||||
};
|
||||
|
||||
|
||||
static void LoadSpriteTables(void)
|
||||
{
|
||||
const FileList* files = _use_dos_palette ? &files_dos : &files_win;
|
||||
uint load_index;
|
||||
uint i;
|
||||
|
||||
LoadGrfIndexed(files->basic[0].filename, trg1idx, 0);
|
||||
DupSprite( 2, 130); // non-breaking space medium
|
||||
DupSprite(226, 354); // non-breaking space tiny
|
||||
DupSprite(450, 578); // non-breaking space large
|
||||
load_index = 4793;
|
||||
|
||||
for (i = 1; files->basic[i].filename != NULL; i++) {
|
||||
load_index += LoadGrfFile(files->basic[i].filename, load_index, i);
|
||||
}
|
||||
|
||||
/* Load additional sprites for climates other than temperate */
|
||||
if (_opt.landscape != LT_NORMAL) {
|
||||
LoadGrfIndexed(
|
||||
files->landscape[_opt.landscape - 1].filename,
|
||||
_landscape_spriteindexes[_opt.landscape - 1],
|
||||
i++
|
||||
);
|
||||
}
|
||||
|
||||
assert(load_index == SPR_SIGNALS_BASE);
|
||||
load_index += LoadGrfFile("nsignalsw.grf", load_index, i++);
|
||||
|
||||
assert(load_index == SPR_CANALS_BASE);
|
||||
load_index += LoadGrfFile("canalsw.grf", load_index, i++);
|
||||
|
||||
assert(load_index == SPR_SLOPES_BASE);
|
||||
LoadGrfIndexed("trkfoundw.grf", _slopes_spriteindexes[_opt.landscape], i++);
|
||||
|
||||
load_index = SPR_AUTORAIL_BASE;
|
||||
load_index += LoadGrfFile("autorail.grf", load_index, i++);
|
||||
|
||||
assert(load_index == SPR_ELRAIL_BASE);
|
||||
load_index += LoadGrfFile("elrailsw.grf", load_index, i++);
|
||||
|
||||
assert(load_index == SPR_2CCMAP_BASE);
|
||||
load_index += LoadGrfFile("2ccmap.grf", load_index, i++);
|
||||
|
||||
assert(load_index == SPR_OPENTTD_BASE);
|
||||
LoadGrfIndexed("openttd.grf", _openttd_grf_indexes, i++);
|
||||
load_index = SPR_OPENTTD_BASE + OPENTTD_SPRITES_COUNT;
|
||||
|
||||
assert(load_index == SPR_AIRPORTX_BASE);
|
||||
load_index += LoadGrfFile("airports.grf", load_index, i++);
|
||||
|
||||
/* Initialize the unicode to sprite mapping table */
|
||||
InitializeUnicodeGlyphMap();
|
||||
|
||||
LoadNewGRF(load_index, i);
|
||||
}
|
||||
|
||||
|
||||
void GfxLoadSprites(void)
|
||||
{
|
||||
DEBUG(sprite, 2, "Loading sprite set %d", _opt.landscape);
|
||||
|
||||
GfxInitSpriteMem();
|
||||
LoadSpriteTables();
|
||||
GfxInitPalettes();
|
||||
}
|
||||
9
src/gfxinit.h
Normal file
9
src/gfxinit.h
Normal file
@@ -0,0 +1,9 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef GFXINIT_H
|
||||
#define GFXINIT_H
|
||||
|
||||
void CheckExternalFiles(void);
|
||||
void GfxLoadSprites(void);
|
||||
|
||||
#endif /* GFXINIT_H */
|
||||
1258
src/graph_gui.c
Normal file
1258
src/graph_gui.c
Normal file
File diff suppressed because it is too large
Load Diff
141
src/gui.h
Normal file
141
src/gui.h
Normal file
@@ -0,0 +1,141 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef GUI_H
|
||||
#define GUI_H
|
||||
|
||||
#include "station.h"
|
||||
#include "window.h"
|
||||
#include "string.h"
|
||||
|
||||
/* main_gui.c */
|
||||
void SetupColorsAndInitialWindow(void);
|
||||
void CcPlaySound10(bool success, TileIndex tile, uint32 p1, uint32 p2);
|
||||
void CcBuildCanal(bool success, TileIndex tile, uint32 p1, uint32 p2);
|
||||
void CcTerraform(bool success, TileIndex tile, uint32 p1, uint32 p2);
|
||||
|
||||
/* settings_gui.c */
|
||||
void ShowGameOptions(void);
|
||||
void ShowGameDifficulty(void);
|
||||
void ShowPatchesSelection(void);
|
||||
void DrawArrowButtons(int x, int y, int ctab, byte state, bool clickable_left, bool clickable_right);
|
||||
|
||||
/* graph_gui.c */
|
||||
extern const byte _cargo_colours[NUM_CARGO];
|
||||
void ShowOperatingProfitGraph(void);
|
||||
void ShowIncomeGraph(void);
|
||||
void ShowDeliveredCargoGraph(void);
|
||||
void ShowPerformanceHistoryGraph(void);
|
||||
void ShowCompanyValueGraph(void);
|
||||
void ShowCargoPaymentRates(void);
|
||||
void ShowCompanyLeagueTable(void);
|
||||
void ShowPerformanceRatingDetail(void);
|
||||
|
||||
/* news_gui.c */
|
||||
void ShowLastNewsMessage(void);
|
||||
void ShowMessageOptions(void);
|
||||
void ShowMessageHistory(void);
|
||||
|
||||
/* rail_gui.c */
|
||||
void ShowBuildRailToolbar(RailType railtype, int button);
|
||||
void PlaceProc_BuyLand(TileIndex tile);
|
||||
void ReinitGuiAfterToggleElrail(bool disable);
|
||||
|
||||
/* train_gui.c */
|
||||
void ShowTrainViewWindow(const Vehicle *v);
|
||||
void ShowOrdersWindow(const Vehicle *v);
|
||||
|
||||
/* road_gui.c */
|
||||
void ShowBuildRoadToolbar(void);
|
||||
void ShowBuildRoadScenToolbar(void);
|
||||
void ShowRoadVehViewWindow(const Vehicle *v);
|
||||
|
||||
/* dock_gui.c */
|
||||
void ShowBuildDocksToolbar(void);
|
||||
void ShowShipViewWindow(const Vehicle *v);
|
||||
|
||||
/* aircraft_gui.c */
|
||||
void ShowBuildAirToolbar(void);
|
||||
|
||||
/* terraform_gui.c */
|
||||
void ShowTerraformToolbar(void);
|
||||
|
||||
/* tgp_gui.c */
|
||||
void ShowGenerateLandscape(void);
|
||||
void ShowHeightmapLoad(void);
|
||||
|
||||
void PlaceProc_DemolishArea(TileIndex tile);
|
||||
void PlaceProc_LevelLand(TileIndex tile);
|
||||
bool GUIPlaceProcDragXY(const WindowEvent *e);
|
||||
|
||||
enum { // max 32 - 4 = 28 types
|
||||
GUI_PlaceProc_DemolishArea = 0 << 4,
|
||||
GUI_PlaceProc_LevelArea = 1 << 4,
|
||||
GUI_PlaceProc_DesertArea = 2 << 4,
|
||||
GUI_PlaceProc_WaterArea = 3 << 4,
|
||||
GUI_PlaceProc_ConvertRailArea = 4 << 4,
|
||||
GUI_PlaceProc_RockyArea = 5 << 4,
|
||||
};
|
||||
|
||||
/* misc_gui.c */
|
||||
void PlaceLandBlockInfo(void);
|
||||
void ShowAboutWindow(void);
|
||||
void ShowBuildTreesToolbar(void);
|
||||
void ShowBuildTreesScenToolbar(void);
|
||||
void ShowTownDirectory(void);
|
||||
void ShowIndustryDirectory(void);
|
||||
void ShowSubsidiesList(void);
|
||||
void ShowPlayerStations(PlayerID player);
|
||||
void ShowPlayerFinances(PlayerID player);
|
||||
void ShowPlayerCompany(PlayerID player);
|
||||
void ShowSignList(void);
|
||||
|
||||
void ShowEstimatedCostOrIncome(int32 cost, int x, int y);
|
||||
void ShowErrorMessage(StringID msg_1, StringID msg_2, int x, int y);
|
||||
|
||||
void DrawStationCoverageAreaText(int sx, int sy, uint mask,int rad);
|
||||
void CheckRedrawStationCoverage(const Window *w);
|
||||
|
||||
void ShowSmallMap(void);
|
||||
void ShowExtraViewPortWindow(void);
|
||||
void SetVScrollCount(Window *w, int num);
|
||||
void SetVScroll2Count(Window *w, int num);
|
||||
void SetHScrollCount(Window *w, int num);
|
||||
|
||||
void ShowCheatWindow(void);
|
||||
|
||||
void DrawEditBox(Window *w, querystr_d *string, int wid);
|
||||
void HandleEditBox(Window *w, querystr_d *string, int wid);
|
||||
int HandleEditBoxKey(Window *w, querystr_d *string, int wid, WindowEvent *we);
|
||||
bool HandleCaret(Textbuf *tb);
|
||||
|
||||
void DeleteTextBufferAll(Textbuf *tb);
|
||||
bool DeleteTextBufferChar(Textbuf *tb, int delmode);
|
||||
bool InsertTextBufferChar(Textbuf *tb, uint32 key);
|
||||
bool InsertTextBufferClipboard(Textbuf *tb);
|
||||
bool MoveTextBufferPos(Textbuf *tb, int navmode);
|
||||
void InitializeTextBuffer(Textbuf *tb, const char *buf, uint16 maxlength, uint16 maxwidth);
|
||||
void UpdateTextBufferSize(Textbuf *tb);
|
||||
|
||||
void BuildFileList(void);
|
||||
void SetFiosType(const byte fiostype);
|
||||
|
||||
/* FIOS_TYPE_FILE, FIOS_TYPE_OLDFILE etc. different colours */
|
||||
extern const byte _fios_colors[];
|
||||
|
||||
/* bridge_gui.c */
|
||||
void ShowBuildBridgeWindow(uint start, uint end, byte type);
|
||||
|
||||
void ShowBuildIndustryWindow(void);
|
||||
void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, Window *parent, CharSetFilter afilter);
|
||||
void ShowQuery(StringID caption, StringID message, Window *w, void (*callback)(Window*, bool));
|
||||
void ShowMusicWindow(void);
|
||||
|
||||
/* main_gui.c */
|
||||
void HandleOnEditText(const char *str);
|
||||
VARDEF byte _station_show_coverage;
|
||||
VARDEF PlaceProc *_place_proc;
|
||||
|
||||
/* vehicle_gui.c */
|
||||
void InitializeGUI(void);
|
||||
|
||||
#endif /* GUI_H */
|
||||
49
src/hal.h
Normal file
49
src/hal.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef HAL_H
|
||||
#define HAL_H
|
||||
|
||||
typedef struct {
|
||||
const char *(*start)(const char * const *parm);
|
||||
void (*stop)(void);
|
||||
} HalCommonDriver;
|
||||
|
||||
typedef struct {
|
||||
const char *(*start)(const char * const *parm);
|
||||
void (*stop)(void);
|
||||
void (*make_dirty)(int left, int top, int width, int height);
|
||||
void (*main_loop)(void);
|
||||
bool (*change_resolution)(int w, int h);
|
||||
void (*toggle_fullscreen)(bool fullscreen);
|
||||
} HalVideoDriver;
|
||||
|
||||
typedef struct {
|
||||
const char *(*start)(const char * const *parm);
|
||||
void (*stop)(void);
|
||||
} HalSoundDriver;
|
||||
|
||||
typedef struct {
|
||||
const char *(*start)(const char * const *parm);
|
||||
void (*stop)(void);
|
||||
|
||||
void (*play_song)(const char *filename);
|
||||
void (*stop_song)(void);
|
||||
bool (*is_song_playing)(void);
|
||||
void (*set_volume)(byte vol);
|
||||
} HalMusicDriver;
|
||||
|
||||
VARDEF HalMusicDriver *_music_driver;
|
||||
VARDEF HalSoundDriver *_sound_driver;
|
||||
VARDEF HalVideoDriver *_video_driver;
|
||||
|
||||
enum DriverType {
|
||||
VIDEO_DRIVER = 0,
|
||||
SOUND_DRIVER = 1,
|
||||
MUSIC_DRIVER = 2,
|
||||
};
|
||||
|
||||
void GameLoop(void);
|
||||
|
||||
void CreateConsole(void);
|
||||
|
||||
#endif /* HAL_H */
|
||||
459
src/heightmap.c
Normal file
459
src/heightmap.c
Normal file
@@ -0,0 +1,459 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "variables.h"
|
||||
#include "functions.h"
|
||||
#include "heightmap.h"
|
||||
#include "clear_map.h"
|
||||
#include "table/strings.h"
|
||||
#include "void_map.h"
|
||||
#include "debug.h"
|
||||
#include "gfx.h"
|
||||
#include "gui.h"
|
||||
#include "saveload.h"
|
||||
#include "bmp.h"
|
||||
|
||||
/**
|
||||
* Convert RGB colors to Grayscale using 29.9% Red, 58.7% Green, 11.4% Blue
|
||||
* (average luminosity formula) -- Dalestan
|
||||
* This in fact is the NTSC Color Space -- TrueLight
|
||||
*/
|
||||
static inline byte RGBToGrayscale(byte red, byte green, byte blue)
|
||||
{
|
||||
/* To avoid doubles and stuff, multiple it with a total of 65536 (16bits), then
|
||||
* divide by it to normalize the value to a byte again. */
|
||||
return ((red * 19595) + (green * 38470) + (blue * 7471)) / 65536;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WITH_PNG
|
||||
|
||||
#include "png.h"
|
||||
|
||||
/**
|
||||
* The PNG Heightmap loader.
|
||||
*/
|
||||
static void ReadHeightmapPNGImageData(byte *map, png_structp png_ptr, png_infop info_ptr)
|
||||
{
|
||||
uint x, y;
|
||||
byte gray_palette[256];
|
||||
png_bytep *row_pointers = NULL;
|
||||
|
||||
/* Get palette and convert it to grayscale */
|
||||
if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
int i;
|
||||
int palette_size;
|
||||
png_color *palette;
|
||||
bool all_gray = true;
|
||||
|
||||
png_get_PLTE(png_ptr, info_ptr, &palette, &palette_size);
|
||||
for (i = 0; i < palette_size && (palette_size != 16 || all_gray); i++) {
|
||||
all_gray &= palette[i].red == palette[i].green && palette[i].red == palette[i].blue;
|
||||
gray_palette[i] = RGBToGrayscale(palette[i].red, palette[i].green, palette[i].blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a non-gray palette of size 16 we assume that
|
||||
* the order of the palette determines the height;
|
||||
* the first entry is the sea (level 0), the second one
|
||||
* level 1, etc.
|
||||
*/
|
||||
if (palette_size == 16 && !all_gray) {
|
||||
for (i = 0; i < palette_size; i++) {
|
||||
gray_palette[i] = 256 * i / palette_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
row_pointers = png_get_rows(png_ptr, info_ptr);
|
||||
|
||||
/* Read the raw image data and convert in 8-bit grayscale */
|
||||
for (x = 0; x < info_ptr->width; x++) {
|
||||
for (y = 0; y < info_ptr->height; y++) {
|
||||
byte *pixel = &map[y * info_ptr->width + x];
|
||||
uint x_offset = x * info_ptr->channels;
|
||||
|
||||
if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
*pixel = gray_palette[row_pointers[y][x_offset]];
|
||||
} else if (info_ptr->channels == 3) {
|
||||
*pixel = RGBToGrayscale(row_pointers[y][x_offset + 0],
|
||||
row_pointers[y][x_offset + 1], row_pointers[y][x_offset + 2]);
|
||||
} else {
|
||||
*pixel = row_pointers[y][x_offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the heightmap and/or size of the heightmap from a PNG file.
|
||||
* If map == NULL only the size of the PNG is read, otherwise a map
|
||||
* with grayscale pixels is allocated and assigned to *map.
|
||||
*/
|
||||
static bool ReadHeightmapPNG(char *filename, uint *x, uint *y, byte **map)
|
||||
{
|
||||
FILE *fp;
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr = NULL;
|
||||
|
||||
fp = fopen(filename, "rb");
|
||||
if (fp == NULL) {
|
||||
ShowErrorMessage(STR_PNGMAP_ERR_FILE_NOT_FOUND, STR_PNGMAP_ERROR, 0, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (png_ptr == NULL) {
|
||||
ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == NULL || setjmp(png_jmpbuf(png_ptr))) {
|
||||
ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
|
||||
fclose(fp);
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_init_io(png_ptr, fp);
|
||||
|
||||
/* Allocate memory and read image, without alpha or 16-bit samples
|
||||
* (result is either 8-bit indexed/grayscale or 24-bit RGB) */
|
||||
png_set_packing(png_ptr);
|
||||
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_STRIP_16, NULL);
|
||||
|
||||
/* Maps of wrong color-depth are not used.
|
||||
* (this should have been taken care of by stripping alpha and 16-bit samples on load) */
|
||||
if ((info_ptr->channels != 1) && (info_ptr->channels != 3) && (info_ptr->bit_depth != 8)) {
|
||||
ShowErrorMessage(STR_PNGMAP_ERR_IMAGE_TYPE, STR_PNGMAP_ERROR, 0, 0);
|
||||
fclose(fp);
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (map != NULL) {
|
||||
*map = malloc(info_ptr->width * info_ptr->height * sizeof(byte));
|
||||
|
||||
if (*map == NULL) {
|
||||
ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
|
||||
fclose(fp);
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadHeightmapPNGImageData(*map, png_ptr, info_ptr);
|
||||
}
|
||||
|
||||
*x = info_ptr->width;
|
||||
*y = info_ptr->height;
|
||||
|
||||
fclose(fp);
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* WITH_PNG */
|
||||
|
||||
|
||||
/**
|
||||
* The BMP Heightmap loader.
|
||||
*/
|
||||
static void ReadHeightmapBMPImageData(byte *map, BmpInfo *info, BmpData *data)
|
||||
{
|
||||
uint x, y;
|
||||
byte gray_palette[256];
|
||||
|
||||
if (data->palette != NULL) {
|
||||
uint i;
|
||||
bool all_gray = true;
|
||||
|
||||
if (info->palette_size != 2) {
|
||||
for (i = 0; i < info->palette_size && (info->palette_size != 16 || all_gray); i++) {
|
||||
all_gray &= data->palette[i].r == data->palette[i].g && data->palette[i].r == data->palette[i].b;
|
||||
gray_palette[i] = RGBToGrayscale(data->palette[i].r, data->palette[i].g, data->palette[i].b);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a non-gray palette of size 16 we assume that
|
||||
* the order of the palette determines the height;
|
||||
* the first entry is the sea (level 0), the second one
|
||||
* level 1, etc.
|
||||
*/
|
||||
if (info->palette_size == 16 && !all_gray) {
|
||||
for (i = 0; i < info->palette_size; i++) {
|
||||
gray_palette[i] = 256 * i / info->palette_size;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* For a palette of size 2 we assume that the order of the palette determines the height;
|
||||
* the first entry is the sea (level 0), the second one is the land (level 1)
|
||||
*/
|
||||
gray_palette[0] = 0;
|
||||
gray_palette[1] = 16;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the raw image data and convert in 8-bit grayscale */
|
||||
for (y = 0; y < info->height; y++) {
|
||||
byte *pixel = &map[y * info->width];
|
||||
byte *bitmap = &data->bitmap[y * info->width * (info->bpp == 24 ? 3 : 1)];
|
||||
|
||||
for (x = 0; x < info->width; x++) {
|
||||
if (info->bpp != 24) {
|
||||
*pixel++ = gray_palette[*bitmap++];
|
||||
} else {
|
||||
*pixel++ = RGBToGrayscale(*bitmap, *(bitmap + 1), *(bitmap + 2));
|
||||
bitmap += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the heightmap and/or size of the heightmap from a BMP file.
|
||||
* If map == NULL only the size of the BMP is read, otherwise a map
|
||||
* with grayscale pixels is allocated and assigned to *map.
|
||||
*/
|
||||
static bool ReadHeightmapBMP(char *filename, uint *x, uint *y, byte **map)
|
||||
{
|
||||
FILE *f;
|
||||
BmpInfo info;
|
||||
BmpData data;
|
||||
BmpBuffer buffer;
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
if (f == NULL) {
|
||||
ShowErrorMessage(STR_PNGMAP_ERR_FILE_NOT_FOUND, STR_BMPMAP_ERROR, 0, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
BmpInitializeBuffer(&buffer, f);
|
||||
|
||||
if (!BmpReadHeader(&buffer, &info, &data)) {
|
||||
ShowErrorMessage(STR_BMPMAP_ERR_IMAGE_TYPE, STR_BMPMAP_ERROR, 0, 0);
|
||||
fclose(f);
|
||||
BmpDestroyData(&data);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (map != NULL) {
|
||||
if (!BmpReadBitmap(&buffer, &info, &data)) {
|
||||
ShowErrorMessage(STR_BMPMAP_ERR_IMAGE_TYPE, STR_BMPMAP_ERROR, 0, 0);
|
||||
fclose(f);
|
||||
BmpDestroyData(&data);
|
||||
return false;
|
||||
}
|
||||
|
||||
*map = malloc(info.width * info.height * sizeof(byte));
|
||||
if (*map == NULL) {
|
||||
ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_BMPMAP_ERROR, 0, 0);
|
||||
fclose(f);
|
||||
BmpDestroyData(&data);
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadHeightmapBMPImageData(*map, &info, &data);
|
||||
|
||||
}
|
||||
|
||||
BmpDestroyData(&data);
|
||||
|
||||
*x = info.width;
|
||||
*y = info.height;
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void GrayscaleToMapHeights(uint img_width, uint img_height, byte *map)
|
||||
{
|
||||
/* Defines the detail of the aspect ratio (to avoid doubles) */
|
||||
const uint num_div = 16384;
|
||||
|
||||
uint width, height;
|
||||
uint row, col;
|
||||
uint row_pad = 0, col_pad = 0;
|
||||
uint img_scale;
|
||||
uint img_row, img_col;
|
||||
TileIndex tile;
|
||||
|
||||
/* Get map size and calculate scale and padding values */
|
||||
switch (_patches.heightmap_rotation) {
|
||||
case HM_COUNTER_CLOCKWISE:
|
||||
width = MapSizeX();
|
||||
height = MapSizeY();
|
||||
break;
|
||||
case HM_CLOCKWISE:
|
||||
width = MapSizeY();
|
||||
height = MapSizeX();
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED();
|
||||
/* Avoids compiler warnings */
|
||||
return;
|
||||
}
|
||||
|
||||
if ((img_width * num_div) / img_height > ((width * num_div) / height)) {
|
||||
/* Image is wider than map - center vertically */
|
||||
img_scale = (width * num_div) / img_width;
|
||||
row_pad = (height - ((img_height * img_scale) / num_div)) / 2;
|
||||
} else {
|
||||
/* Image is taller than map - center horizontally */
|
||||
img_scale = (height * num_div) / img_height;
|
||||
col_pad = (width - ((img_width * img_scale) / num_div)) / 2;
|
||||
}
|
||||
|
||||
/* Form the landscape */
|
||||
for (row = 0; row < height - 1; row++) {
|
||||
for (col = 0; col < width - 1; col++) {
|
||||
switch (_patches.heightmap_rotation) {
|
||||
case HM_COUNTER_CLOCKWISE: tile = TileXY(col, row); break;
|
||||
case HM_CLOCKWISE: tile = TileXY(row, col); break;
|
||||
default: NOT_REACHED(); return;
|
||||
}
|
||||
|
||||
/* Check if current tile is within the 1-pixel map edge or padding regions */
|
||||
if ((DistanceFromEdge(tile) <= 1) ||
|
||||
(row < row_pad) || (row >= (img_height + row_pad)) ||
|
||||
(col < col_pad) || (col >= (img_width + col_pad))) {
|
||||
SetTileHeight(tile, 0);
|
||||
} else {
|
||||
/* Use nearest neighbor resizing to scale map data.
|
||||
* We rotate the map 45 degrees (counter)clockwise */
|
||||
img_row = (((row - row_pad) * num_div) / img_scale);
|
||||
switch (_patches.heightmap_rotation) {
|
||||
case HM_COUNTER_CLOCKWISE:
|
||||
img_col = (((width - 1 - col - col_pad) * num_div) / img_scale);
|
||||
break;
|
||||
case HM_CLOCKWISE:
|
||||
img_col = (((col - col_pad) * num_div) / img_scale);
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED();
|
||||
/* Avoids compiler warnings */
|
||||
return;
|
||||
}
|
||||
|
||||
assert(img_row < img_height);
|
||||
assert(img_col < img_width);
|
||||
|
||||
/* Color scales from 0 to 255, OpenTTD height scales from 0 to 15 */
|
||||
SetTileHeight(tile, map[img_row * img_width + img_col] / 16);
|
||||
}
|
||||
MakeClear(tile, CLEAR_GRASS, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function takes care of the fact that land in OpenTTD can never differ
|
||||
* more than 1 in height
|
||||
*/
|
||||
static void FixSlopes(void)
|
||||
{
|
||||
uint width, height;
|
||||
uint row, col;
|
||||
byte current_tile;
|
||||
|
||||
/* Adjust height difference to maximum one horizontal/vertical change. */
|
||||
width = MapSizeX();
|
||||
height = MapSizeY();
|
||||
|
||||
/* Top and left edge */
|
||||
for (row = 1; row < height - 2; row++) {
|
||||
for (col = 1; col < width - 2; col++) {
|
||||
/* Find lowest tile; either the top or left one */
|
||||
current_tile = TileHeight(TileXY(col - 1, row)); // top edge
|
||||
if (TileHeight(TileXY(col, row - 1)) < current_tile) {
|
||||
current_tile = TileHeight(TileXY(col, row - 1)); // left edge
|
||||
}
|
||||
|
||||
/* Does the height differ more than one? */
|
||||
if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
|
||||
/* Then change the height to be no more than one */
|
||||
SetTileHeight(TileXY(col, row), current_tile + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Bottom and right edge */
|
||||
for (row = height - 2; row > 0; row--) {
|
||||
for (col = width - 2; col > 0; col--) {
|
||||
/* Find lowest tile; either the bottom and right one */
|
||||
current_tile = TileHeight(TileXY(col + 1, row)); // bottom edge
|
||||
if (TileHeight(TileXY(col, row + 1)) < current_tile) {
|
||||
current_tile = TileHeight(TileXY(col, row + 1)); // right edge
|
||||
}
|
||||
|
||||
/* Does the height differ more than one? */
|
||||
if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
|
||||
/* Then change the height to be no more than one */
|
||||
SetTileHeight(TileXY(col, row), current_tile + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the heightmap with the correct file reader
|
||||
*/
|
||||
static bool ReadHeightMap(char *filename, uint *x, uint *y, byte **map)
|
||||
{
|
||||
switch (_file_to_saveload.mode) {
|
||||
#ifdef WITH_PNG
|
||||
case SL_PNG:
|
||||
return ReadHeightmapPNG(filename, x, y, map);
|
||||
#endif /* WITH_PNG */
|
||||
case SL_BMP:
|
||||
return ReadHeightmapBMP(filename, x, y, map);
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
/* Avoids compiler warnings */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetHeightmapDimensions(char *filename, uint *x, uint *y)
|
||||
{
|
||||
return ReadHeightMap(filename, x, y, NULL);
|
||||
}
|
||||
|
||||
void LoadHeightmap(char *filename)
|
||||
{
|
||||
uint x, y;
|
||||
byte *map = NULL;
|
||||
|
||||
if (!ReadHeightMap(filename, &x, &y, &map)) {
|
||||
free(map);
|
||||
return;
|
||||
}
|
||||
|
||||
GrayscaleToMapHeights(x, y, map);
|
||||
free(map);
|
||||
|
||||
FixSlopes();
|
||||
MarkWholeScreenDirty();
|
||||
}
|
||||
|
||||
void FlatEmptyWorld(byte tile_height)
|
||||
{
|
||||
uint width, height;
|
||||
uint row, col;
|
||||
|
||||
width = MapSizeX();
|
||||
height = MapSizeY();
|
||||
|
||||
for (row = 2; row < height - 2; row++) {
|
||||
for (col = 2; col < width - 2; col++) {
|
||||
SetTileHeight(TileXY(col, row), tile_height);
|
||||
}
|
||||
}
|
||||
|
||||
FixSlopes();
|
||||
MarkWholeScreenDirty();
|
||||
}
|
||||
33
src/heightmap.h
Normal file
33
src/heightmap.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef HEIGHTMAP_H
|
||||
#define HEIGHTMAP_H
|
||||
|
||||
/*
|
||||
* Order of these enums has to be the same as in lang/english.txt
|
||||
* Otherwise you will get inconsistent behaviour.
|
||||
*/
|
||||
enum {
|
||||
HM_COUNTER_CLOCKWISE, //! Rotate the map counter clockwise 45 degrees
|
||||
HM_CLOCKWISE, //! Rotate the map clockwise 45 degrees
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the dimensions of a heightmap.
|
||||
* @return Returns false if loading of the image failed.
|
||||
*/
|
||||
bool GetHeightmapDimensions(char *filename, uint *x, uint *y);
|
||||
|
||||
/**
|
||||
* Load a heightmap from file and change the map in his current dimensions
|
||||
* to a landscape representing the heightmap.
|
||||
* It converts pixels to height. The brighter, the higher.
|
||||
*/
|
||||
void LoadHeightmap(char *filename);
|
||||
|
||||
/**
|
||||
* Make an empty world where all tiles are of height 'tile_height'.
|
||||
*/
|
||||
void FlatEmptyWorld(byte tile_height);
|
||||
|
||||
#endif /* HEIGHTMAP_H */
|
||||
70
src/helpers.cpp
Normal file
70
src/helpers.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/* $Id$ */
|
||||
#include "stdafx.h"
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
#include "openttd.h"
|
||||
#include "engine.h"
|
||||
EXTERN_C_END
|
||||
|
||||
#include <new>
|
||||
#include "yapf/blob.hpp"
|
||||
|
||||
/* Engine list manipulators - current implementation is only C wrapper around CBlobT<EngineID> (see yapf/blob.hpp) */
|
||||
|
||||
/* we cannot expose CBlobT directly to C so we must cast EngineList* to CBlobT<EngineID>* always when we are called from C */
|
||||
#define B (*(CBlobT<EngineID>*)el)
|
||||
|
||||
/** Create Engine List (and initialize it to empty) */
|
||||
void EngList_Create(EngineList *el)
|
||||
{
|
||||
// call CBlobT constructor explicitly
|
||||
new (&B) CBlobT<EngineID>();
|
||||
}
|
||||
|
||||
/** Destroy Engine List (and free its contents) */
|
||||
void EngList_Destroy(EngineList *el)
|
||||
{
|
||||
// call CBlobT destructor explicitly
|
||||
B.~CBlobT<EngineID>();
|
||||
}
|
||||
|
||||
/** Return number of items stored in the Engine List */
|
||||
uint EngList_Count(const EngineList *el)
|
||||
{
|
||||
return B.Size();
|
||||
}
|
||||
|
||||
/** Add new item at the end of Engine List */
|
||||
void EngList_Add(EngineList *el, EngineID eid)
|
||||
{
|
||||
B.Append(eid);
|
||||
}
|
||||
|
||||
/** Return pointer to the items array held by Engine List */
|
||||
EngineID* EngList_Items(EngineList *el)
|
||||
{
|
||||
return B.Data();
|
||||
}
|
||||
|
||||
/** Clear the Engine List (by invalidating all its items == reseting item count to zero) */
|
||||
void EngList_RemoveAll(EngineList *el)
|
||||
{
|
||||
B.Clear();
|
||||
}
|
||||
|
||||
/** Sort all items using qsort() and given 'CompareItems' function */
|
||||
void EngList_Sort(EngineList *el, EngList_SortTypeFunction compare)
|
||||
{
|
||||
qsort(B.Data(), B.Size(), sizeof(**el), compare);
|
||||
}
|
||||
|
||||
/** Sort selected range of items (on indices @ <begin, begin+num_items-1>) */
|
||||
void EngList_SortPartial(EngineList *el, EngList_SortTypeFunction compare, uint begin, uint num_items)
|
||||
{
|
||||
assert(begin <= (uint)B.Size());
|
||||
assert(begin + num_items <= (uint)B.Size());
|
||||
qsort(B.Data() + begin, num_items, sizeof(**el), compare);
|
||||
}
|
||||
|
||||
#undef B
|
||||
|
||||
199
src/industry.h
Normal file
199
src/industry.h
Normal file
@@ -0,0 +1,199 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef INDUSTRY_H
|
||||
#define INDUSTRY_H
|
||||
|
||||
#include "oldpool.h"
|
||||
|
||||
typedef byte IndustryGfx;
|
||||
typedef uint8 IndustryType;
|
||||
|
||||
enum {
|
||||
INVALID_INDUSTRY = 0xFFFF,
|
||||
};
|
||||
|
||||
typedef enum IndustryLifeTypes {
|
||||
INDUSTRYLIFE_NOT_CLOSABLE, ///< Industry can never close
|
||||
INDUSTRYLIFE_PRODUCTION, ///< Industry can close and change of production
|
||||
INDUSTRYLIFE_CLOSABLE, ///< Industry can only close (no production change)
|
||||
} IndustryLifeType;
|
||||
|
||||
struct Industry {
|
||||
TileIndex xy;
|
||||
byte width; /* swapped order of w/h with town */
|
||||
byte height;
|
||||
const Town* town;
|
||||
CargoID produced_cargo[2];
|
||||
uint16 cargo_waiting[2];
|
||||
byte production_rate[2];
|
||||
CargoID accepts_cargo[3];
|
||||
byte prod_level;
|
||||
uint16 last_mo_production[2];
|
||||
uint16 last_mo_transported[2];
|
||||
byte pct_transported[2];
|
||||
uint16 total_production[2];
|
||||
uint16 total_transported[2];
|
||||
uint16 counter;
|
||||
|
||||
byte type;
|
||||
byte owner;
|
||||
byte random_color;
|
||||
Year last_prod_year;
|
||||
byte was_cargo_delivered;
|
||||
|
||||
IndustryID index;
|
||||
};
|
||||
|
||||
typedef struct IndustryTileTable {
|
||||
TileIndexDiffC ti;
|
||||
IndustryGfx gfx;
|
||||
} IndustryTileTable;
|
||||
|
||||
typedef struct IndustrySpec {
|
||||
/** Tables with the 'layout' of different composition of GFXes */
|
||||
const IndustryTileTable *const *table;
|
||||
/** Number of elements in the table */
|
||||
byte num_table;
|
||||
/** Base cost multiplier*/
|
||||
byte cost_multiplier;
|
||||
/** Industries this industry cannot be close to */
|
||||
IndustryType conflicting[3];
|
||||
/** index to a procedure to check for conflicting circumstances */
|
||||
byte check_proc;
|
||||
|
||||
CargoID produced_cargo[2];
|
||||
byte production_rate[2];
|
||||
/** The minimum amount of cargo transported to the stations; if the
|
||||
* waiting cargo is less than this number, no cargo is moved to it*/
|
||||
byte minimal_cargo;
|
||||
CargoID accepts_cargo[3];
|
||||
|
||||
IndustryLifeType life_type; ///< This is also known as Industry production flag, in newgrf specs
|
||||
|
||||
byte climate_availability; ///< Bitmask, giving landscape enums as bit position
|
||||
|
||||
StringID name;
|
||||
StringID closure_text;
|
||||
StringID production_up_text;
|
||||
StringID production_down_text;
|
||||
} IndustrySpec;
|
||||
|
||||
const IndustrySpec *GetIndustrySpec(IndustryType thistype);
|
||||
|
||||
DECLARE_OLD_POOL(Industry, Industry, 3, 8000)
|
||||
|
||||
/**
|
||||
* Check if an Industry really exists.
|
||||
*/
|
||||
static inline bool IsValidIndustry(const Industry *industry)
|
||||
{
|
||||
return industry->xy != 0;
|
||||
}
|
||||
|
||||
static inline bool IsValidIndustryID(IndustryID index)
|
||||
{
|
||||
return index < GetIndustryPoolSize() && IsValidIndustry(GetIndustry(index));
|
||||
}
|
||||
|
||||
VARDEF int _total_industries;
|
||||
|
||||
static inline IndustryID GetMaxIndustryIndex(void)
|
||||
{
|
||||
/* TODO - This isn't the real content of the function, but
|
||||
* with the new pool-system this will be replaced with one that
|
||||
* _really_ returns the highest index. Now it just returns
|
||||
* the next safe value we are sure about everything is below.
|
||||
*/
|
||||
return GetIndustryPoolSize() - 1;
|
||||
}
|
||||
|
||||
static inline uint GetNumIndustries(void)
|
||||
{
|
||||
return _total_industries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a random valid industry.
|
||||
*/
|
||||
static inline Industry *GetRandomIndustry(void)
|
||||
{
|
||||
int num = RandomRange(GetNumIndustries());
|
||||
IndustryID index = INVALID_INDUSTRY;
|
||||
|
||||
if (GetNumIndustries() == 0) return NULL;
|
||||
|
||||
while (num >= 0) {
|
||||
num--;
|
||||
index++;
|
||||
|
||||
/* Make sure we have a valid industry */
|
||||
while (!IsValidIndustryID(index)) {
|
||||
index++;
|
||||
assert(index <= GetMaxIndustryIndex());
|
||||
}
|
||||
}
|
||||
|
||||
return GetIndustry(index);
|
||||
}
|
||||
|
||||
void DestroyIndustry(Industry *i);
|
||||
|
||||
static inline void DeleteIndustry(Industry *i)
|
||||
{
|
||||
DestroyIndustry(i);
|
||||
i->xy = 0;
|
||||
}
|
||||
|
||||
#define FOR_ALL_INDUSTRIES_FROM(i, start) for (i = GetIndustry(start); i != NULL; i = (i->index + 1U < GetIndustryPoolSize()) ? GetIndustry(i->index + 1U) : NULL) if (IsValidIndustry(i))
|
||||
#define FOR_ALL_INDUSTRIES(i) FOR_ALL_INDUSTRIES_FROM(i, 0)
|
||||
|
||||
VARDEF const Industry** _industry_sort;
|
||||
VARDEF bool _industry_sort_dirty;
|
||||
|
||||
|
||||
void DeleteIndustry(Industry *is);
|
||||
void PlantRandomFarmField(const Industry *i);
|
||||
|
||||
enum {
|
||||
IT_COAL_MINE = 0,
|
||||
IT_POWER_STATION = 1,
|
||||
IT_SAWMILL = 2,
|
||||
IT_FOREST = 3,
|
||||
IT_OIL_REFINERY = 4,
|
||||
IT_OIL_RIG = 5,
|
||||
IT_FACTORY = 6,
|
||||
IT_PRINTING_WORKS = 7,
|
||||
IT_STEEL_MILL = 8,
|
||||
IT_FARM = 9,
|
||||
IT_COPPER_MINE = 10,
|
||||
IT_OIL_WELL = 11,
|
||||
IT_BANK_TEMP = 12,
|
||||
IT_FOOD_PROCESS = 13,
|
||||
IT_PAPER_MILL = 14,
|
||||
IT_GOLD_MINE = 15,
|
||||
IT_BANK_TROPIC_ARCTIC = 16,
|
||||
IT_DIAMOND_MINE = 17,
|
||||
IT_IRON_MINE = 18,
|
||||
IT_FRUIT_PLANTATION = 19,
|
||||
IT_RUBBER_PLANTATION = 20,
|
||||
IT_WATER_SUPPLY = 21,
|
||||
IT_WATER_TOWER = 22,
|
||||
IT_FACTORY_2 = 23,
|
||||
IT_FARM_2 = 24,
|
||||
IT_LUMBER_MILL = 25,
|
||||
IT_COTTON_CANDY = 26,
|
||||
IT_CANDY_FACTORY = 27,
|
||||
IT_BATTERY_FARM = 28,
|
||||
IT_COLA_WELLS = 29,
|
||||
IT_TOY_SHOP = 30,
|
||||
IT_TOY_FACTORY = 31,
|
||||
IT_PLASTIC_FOUNTAINS = 32,
|
||||
IT_FIZZY_DRINK_FACTORY = 33,
|
||||
IT_BUBBLE_GENERATOR = 34,
|
||||
IT_TOFFEE_QUARRY = 35,
|
||||
IT_SUGAR_MINE = 36,
|
||||
IT_END,
|
||||
IT_INVALID = 255,
|
||||
};
|
||||
|
||||
#endif /* INDUSTRY_H */
|
||||
1910
src/industry_cmd.c
Normal file
1910
src/industry_cmd.c
Normal file
File diff suppressed because it is too large
Load Diff
693
src/industry_gui.c
Normal file
693
src/industry_gui.c
Normal file
@@ -0,0 +1,693 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "debug.h"
|
||||
#include "functions.h"
|
||||
#include "strings.h"
|
||||
#include "table/strings.h"
|
||||
#include "table/sprites.h"
|
||||
#include "map.h"
|
||||
#include "gui.h"
|
||||
#include "window.h"
|
||||
#include "gfx.h"
|
||||
#include "command.h"
|
||||
#include "viewport.h"
|
||||
#include "industry.h"
|
||||
#include "town.h"
|
||||
#include "variables.h"
|
||||
|
||||
const byte _build_industry_types[4][12] = {
|
||||
{ 1, 2, 4, 6, 8, 0, 3, 5, 9, 11, 18 },
|
||||
{ 1, 14, 4, 13, 7, 0, 3, 9, 11, 15 },
|
||||
{ 25, 13, 4, 23, 22, 11, 17, 10, 24, 19, 20, 21 },
|
||||
{ 27, 30, 31, 33, 26, 28, 29, 32, 34, 35, 36 },
|
||||
};
|
||||
|
||||
static void UpdateIndustryProduction(Industry *i);
|
||||
|
||||
static void BuildIndustryWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_PAINT:
|
||||
DrawWindowWidgets(w);
|
||||
if (_thd.place_mode == 1 && _thd.window_class == WC_BUILD_INDUSTRY) {
|
||||
int ind_type = _build_industry_types[_opt_ptr->landscape][WP(w,def_d).data_1];
|
||||
|
||||
SetDParam(0, (_price.build_industry >> 5) * GetIndustrySpec(ind_type)->cost_multiplier);
|
||||
DrawStringCentered(85, w->height - 21, STR_482F_COST, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_CLICK: {
|
||||
int wid = e->we.click.widget;
|
||||
if (wid >= 3) {
|
||||
if (HandlePlacePushButton(w, wid, SPR_CURSOR_INDUSTRY, 1, NULL))
|
||||
WP(w,def_d).data_1 = wid - 3;
|
||||
}
|
||||
} break;
|
||||
|
||||
case WE_PLACE_OBJ:
|
||||
if (DoCommandP(e->we.place.tile, _build_industry_types[_opt_ptr->landscape][WP(w,def_d).data_1], 0, NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY)))
|
||||
ResetObjectToPlace();
|
||||
break;
|
||||
|
||||
case WE_ABORT_PLACE_OBJ:
|
||||
RaiseWindowButtons(w);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const Widget _build_industry_land0_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 169, 0, 13, STR_0314_FUND_NEW_INDUSTRY, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 169, 14, 115, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 16, 27, STR_0241_POWER_STATION, STR_0263_CONSTRUCT_POWER_STATION},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 29, 40, STR_0242_SAWMILL, STR_0264_CONSTRUCT_SAWMILL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 42, 53, STR_0244_OIL_REFINERY, STR_0266_CONSTRUCT_OIL_REFINERY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 55, 66, STR_0246_FACTORY, STR_0268_CONSTRUCT_FACTORY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 68, 79, STR_0247_STEEL_MILL, STR_0269_CONSTRUCT_STEEL_MILL},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const Widget _build_industry_land1_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 169, 0, 13, STR_0314_FUND_NEW_INDUSTRY, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 169, 14, 115, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 16, 27, STR_0241_POWER_STATION, STR_0263_CONSTRUCT_POWER_STATION},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 29, 40, STR_024C_PAPER_MILL, STR_026E_CONSTRUCT_PAPER_MILL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 42, 53, STR_0244_OIL_REFINERY, STR_0266_CONSTRUCT_OIL_REFINERY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 55, 66, STR_024D_FOOD_PROCESSING_PLANT, STR_026F_CONSTRUCT_FOOD_PROCESSING},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 68, 79, STR_024E_PRINTING_WORKS, STR_0270_CONSTRUCT_PRINTING_WORKS},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const Widget _build_industry_land2_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 169, 0, 13, STR_0314_FUND_NEW_INDUSTRY, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 169, 14, 115, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 16, 27, STR_0250_LUMBER_MILL, STR_0273_CONSTRUCT_LUMBER_MILL_TO},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 29, 40, STR_024D_FOOD_PROCESSING_PLANT, STR_026F_CONSTRUCT_FOOD_PROCESSING},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 42, 53, STR_0244_OIL_REFINERY, STR_0266_CONSTRUCT_OIL_REFINERY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 55, 66, STR_0246_FACTORY, STR_0268_CONSTRUCT_FACTORY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 68, 79, STR_0254_WATER_TOWER, STR_0277_CONSTRUCT_WATER_TOWER_CAN},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const Widget _build_industry_land3_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 169, 0, 13, STR_0314_FUND_NEW_INDUSTRY, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 169, 14, 115, 0x0, STR_NULL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 16, 27, STR_0258_CANDY_FACTORY, STR_027B_CONSTRUCT_CANDY_FACTORY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 29, 40, STR_025B_TOY_SHOP, STR_027E_CONSTRUCT_TOY_SHOP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 42, 53, STR_025C_TOY_FACTORY, STR_027F_CONSTRUCT_TOY_FACTORY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 55, 66, STR_025E_FIZZY_DRINK_FACTORY, STR_0281_CONSTRUCT_FIZZY_DRINK_FACTORY},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const Widget _build_industry_land0_widgets_extra[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 169, 0, 13, STR_0314_FUND_NEW_INDUSTRY, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 169, 14, 187, 0x0, STR_NULL},
|
||||
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 16, 27, STR_0241_POWER_STATION, STR_0263_CONSTRUCT_POWER_STATION},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 29, 40, STR_0242_SAWMILL, STR_0264_CONSTRUCT_SAWMILL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 42, 53, STR_0244_OIL_REFINERY, STR_0266_CONSTRUCT_OIL_REFINERY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 55, 66, STR_0246_FACTORY, STR_0268_CONSTRUCT_FACTORY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 68, 79, STR_0247_STEEL_MILL, STR_0269_CONSTRUCT_STEEL_MILL},
|
||||
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 84, 95, STR_0240_COAL_MINE, STR_CONSTRUCT_COAL_MINE_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 97, 108, STR_0243_FOREST, STR_CONSTRUCT_FOREST_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 110, 121, STR_0245_OIL_RIG, STR_CONSTRUCT_OIL_RIG_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 123, 134, STR_0248_FARM, STR_CONSTRUCT_FARM_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 136, 147, STR_024A_OIL_WELLS, STR_CONSTRUCT_OIL_WELLS_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 149, 160, STR_0249_IRON_ORE_MINE, STR_CONSTRUCT_IRON_ORE_MINE_TIP},
|
||||
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const Widget _build_industry_land1_widgets_extra[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 169, 0, 13, STR_0314_FUND_NEW_INDUSTRY, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 169, 14, 174, 0x0, STR_NULL},
|
||||
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 16, 27, STR_0241_POWER_STATION, STR_0263_CONSTRUCT_POWER_STATION},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 29, 40, STR_024C_PAPER_MILL, STR_026E_CONSTRUCT_PAPER_MILL},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 42, 53, STR_0244_OIL_REFINERY, STR_0266_CONSTRUCT_OIL_REFINERY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 55, 66, STR_024D_FOOD_PROCESSING_PLANT, STR_026F_CONSTRUCT_FOOD_PROCESSING},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 68, 79, STR_024E_PRINTING_WORKS, STR_0270_CONSTRUCT_PRINTING_WORKS},
|
||||
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 84, 95, STR_0240_COAL_MINE, STR_CONSTRUCT_COAL_MINE_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 97, 108, STR_0243_FOREST, STR_CONSTRUCT_FOREST_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 110, 121, STR_0248_FARM, STR_CONSTRUCT_FARM_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 123, 134, STR_024A_OIL_WELLS, STR_CONSTRUCT_OIL_WELLS_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 136, 147, STR_024F_GOLD_MINE, STR_CONSTRUCT_GOLD_MINE_TIP},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const Widget _build_industry_land2_widgets_extra[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 169, 0, 13, STR_0314_FUND_NEW_INDUSTRY, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 169, 14, 200, 0x0, STR_NULL},
|
||||
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 16, 27, STR_0250_LUMBER_MILL, STR_0273_CONSTRUCT_LUMBER_MILL_TO},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 29, 40, STR_024D_FOOD_PROCESSING_PLANT, STR_026F_CONSTRUCT_FOOD_PROCESSING},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 42, 53, STR_0244_OIL_REFINERY, STR_0266_CONSTRUCT_OIL_REFINERY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 55, 66, STR_0246_FACTORY, STR_0268_CONSTRUCT_FACTORY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 68, 79, STR_0254_WATER_TOWER, STR_0277_CONSTRUCT_WATER_TOWER_CAN},
|
||||
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 84, 95, STR_024A_OIL_WELLS, STR_CONSTRUCT_OIL_WELLS_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 97, 108, STR_0255_DIAMOND_MINE, STR_CONSTRUCT_DIAMOND_MINE_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 110, 121, STR_0256_COPPER_ORE_MINE, STR_CONSTRUCT_COPPER_ORE_MINE_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 123, 134, STR_0248_FARM, STR_CONSTRUCT_FARM_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 136, 147, STR_0251_FRUIT_PLANTATION, STR_CONSTRUCT_FRUIT_PLANTATION_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 149, 160, STR_0252_RUBBER_PLANTATION, STR_CONSTRUCT_RUBBER_PLANTATION_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 162, 173, STR_0253_WATER_SUPPLY, STR_CONSTRUCT_WATER_SUPPLY_TIP},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const Widget _build_industry_land3_widgets_extra[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 169, 0, 13, STR_0314_FUND_NEW_INDUSTRY, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_PANEL, RESIZE_NONE, 7, 0, 169, 14, 187, 0x0, STR_NULL},
|
||||
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 16, 27, STR_0258_CANDY_FACTORY, STR_027B_CONSTRUCT_CANDY_FACTORY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 29, 40, STR_025B_TOY_SHOP, STR_027E_CONSTRUCT_TOY_SHOP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 42, 53, STR_025C_TOY_FACTORY, STR_027F_CONSTRUCT_TOY_FACTORY},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 55, 66, STR_025E_FIZZY_DRINK_FACTORY, STR_0281_CONSTRUCT_FIZZY_DRINK_FACTORY},
|
||||
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 71, 82, STR_0257_COTTON_CANDY_FOREST, STR_CONSTRUCT_COTTON_CANDY_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 84, 95, STR_0259_BATTERY_FARM, STR_CONSTRUCT_BATTERY_FARM_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 97, 108, STR_025A_COLA_WELLS, STR_CONSTRUCT_COLA_WELLS_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 110, 121, STR_025D_PLASTIC_FOUNTAINS, STR_CONSTRUCT_PLASTIC_FOUNTAINS_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 123, 134, STR_025F_BUBBLE_GENERATOR, STR_CONSTRUCT_BUBBLE_GENERATOR_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 136, 147, STR_0260_TOFFEE_QUARRY, STR_CONSTRUCT_TOFFEE_QUARRY_TIP},
|
||||
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 167, 149, 160, STR_0261_SUGAR_MINE, STR_CONSTRUCT_SUGAR_MINE_TIP},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
|
||||
static const WindowDesc _build_industry_land0_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 170, 116,
|
||||
WC_BUILD_INDUSTRY,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_industry_land0_widgets,
|
||||
BuildIndustryWndProc
|
||||
};
|
||||
|
||||
static const WindowDesc _build_industry_land1_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 170, 116,
|
||||
WC_BUILD_INDUSTRY,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_industry_land1_widgets,
|
||||
BuildIndustryWndProc
|
||||
};
|
||||
|
||||
static const WindowDesc _build_industry_land2_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 170, 116,
|
||||
WC_BUILD_INDUSTRY,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_industry_land2_widgets,
|
||||
BuildIndustryWndProc
|
||||
};
|
||||
|
||||
static const WindowDesc _build_industry_land3_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 170, 116,
|
||||
WC_BUILD_INDUSTRY,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_industry_land3_widgets,
|
||||
BuildIndustryWndProc
|
||||
};
|
||||
|
||||
static const WindowDesc _build_industry_land0_desc_extra = {
|
||||
WDP_AUTO, WDP_AUTO, 170, 188,
|
||||
WC_BUILD_INDUSTRY,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_industry_land0_widgets_extra,
|
||||
BuildIndustryWndProc
|
||||
};
|
||||
|
||||
static const WindowDesc _build_industry_land1_desc_extra = {
|
||||
WDP_AUTO, WDP_AUTO, 170, 175,
|
||||
WC_BUILD_INDUSTRY,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_industry_land1_widgets_extra,
|
||||
BuildIndustryWndProc
|
||||
};
|
||||
|
||||
static const WindowDesc _build_industry_land2_desc_extra = {
|
||||
WDP_AUTO, WDP_AUTO, 170, 201,
|
||||
WC_BUILD_INDUSTRY,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_industry_land2_widgets_extra,
|
||||
BuildIndustryWndProc
|
||||
};
|
||||
|
||||
static const WindowDesc _build_industry_land3_desc_extra = {
|
||||
WDP_AUTO, WDP_AUTO, 170, 188,
|
||||
WC_BUILD_INDUSTRY,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
||||
_build_industry_land3_widgets_extra,
|
||||
BuildIndustryWndProc
|
||||
};
|
||||
|
||||
static const WindowDesc * const _industry_window_desc[2][4] = {
|
||||
{
|
||||
&_build_industry_land0_desc,
|
||||
&_build_industry_land1_desc,
|
||||
&_build_industry_land2_desc,
|
||||
&_build_industry_land3_desc,
|
||||
},
|
||||
{
|
||||
&_build_industry_land0_desc_extra,
|
||||
&_build_industry_land1_desc_extra,
|
||||
&_build_industry_land2_desc_extra,
|
||||
&_build_industry_land3_desc_extra,
|
||||
},
|
||||
};
|
||||
|
||||
void ShowBuildIndustryWindow(void)
|
||||
{
|
||||
if (!IsValidPlayer(_current_player)) return;
|
||||
AllocateWindowDescFront(_industry_window_desc[_patches.build_rawmaterial_ind][_opt_ptr->landscape],0);
|
||||
}
|
||||
|
||||
static inline bool isProductionMinimum(const Industry *i, int pt) {
|
||||
return i->production_rate[pt] == 1;
|
||||
}
|
||||
|
||||
static inline bool isProductionMaximum(const Industry *i, int pt) {
|
||||
return i->production_rate[pt] == 255;
|
||||
}
|
||||
|
||||
static inline bool IsProductionAlterable(const Industry *i)
|
||||
{
|
||||
return ((_game_mode == GM_EDITOR || _cheats.setup_prod.value) &&
|
||||
(i->accepts_cargo[0] == CT_INVALID || i->accepts_cargo[0] == CT_VALUABLES));
|
||||
}
|
||||
|
||||
static void IndustryViewWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
// WP(w,vp2_d).data_1 is for the editbox line
|
||||
// WP(w,vp2_d).data_2 is for the clickline
|
||||
// WP(w,vp2_d).data_3 is for the click pos (left or right)
|
||||
|
||||
switch (e->event) {
|
||||
case WE_PAINT: {
|
||||
const Industry *i = GetIndustry(w->window_number);
|
||||
|
||||
SetDParam(0, w->window_number);
|
||||
DrawWindowWidgets(w);
|
||||
|
||||
if (i->accepts_cargo[0] != CT_INVALID) {
|
||||
StringID str;
|
||||
|
||||
SetDParam(0, _cargoc.names_s[i->accepts_cargo[0]]);
|
||||
str = STR_4827_REQUIRES;
|
||||
if (i->accepts_cargo[1] != CT_INVALID) {
|
||||
SetDParam(1, _cargoc.names_s[i->accepts_cargo[1]]);
|
||||
str = STR_4828_REQUIRES;
|
||||
if (i->accepts_cargo[2] != CT_INVALID) {
|
||||
SetDParam(2, _cargoc.names_s[i->accepts_cargo[2]]);
|
||||
str = STR_4829_REQUIRES;
|
||||
}
|
||||
}
|
||||
DrawString(2, 107, str, 0);
|
||||
}
|
||||
|
||||
if (i->produced_cargo[0] != CT_INVALID) {
|
||||
DrawString(2, 117, STR_482A_PRODUCTION_LAST_MONTH, 0);
|
||||
|
||||
SetDParam(0, i->produced_cargo[0]);
|
||||
SetDParam(1, i->total_production[0]);
|
||||
|
||||
SetDParam(2, i->pct_transported[0] * 100 >> 8);
|
||||
DrawString(4 + (IsProductionAlterable(i) ? 30 : 0), 127, STR_482B_TRANSPORTED, 0);
|
||||
// Let's put out those buttons..
|
||||
if (IsProductionAlterable(i)) {
|
||||
DrawArrowButtons(5, 127, 3, (WP(w,vp2_d).data_2 == 1) ? WP(w,vp2_d).data_3 : 0,
|
||||
!isProductionMinimum(i, 0), !isProductionMaximum(i, 0));
|
||||
}
|
||||
|
||||
if (i->produced_cargo[1] != CT_INVALID) {
|
||||
SetDParam(0, i->produced_cargo[1]);
|
||||
SetDParam(1, i->total_production[1]);
|
||||
SetDParam(2, i->pct_transported[1] * 100 >> 8);
|
||||
DrawString(4 + (IsProductionAlterable(i) ? 30 : 0), 137, STR_482B_TRANSPORTED, 0);
|
||||
// Let's put out those buttons..
|
||||
if (IsProductionAlterable(i)) {
|
||||
DrawArrowButtons(5, 137, 3, (WP(w,vp2_d).data_2 == 2) ? WP(w,vp2_d).data_3 : 0,
|
||||
!isProductionMinimum(i, 1), !isProductionMaximum(i, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawWindowViewport(w);
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_CLICK: {
|
||||
Industry *i;
|
||||
|
||||
switch (e->we.click.widget) {
|
||||
case 5: {
|
||||
int line, x;
|
||||
|
||||
i = GetIndustry(w->window_number);
|
||||
|
||||
// We should work if needed..
|
||||
if (!IsProductionAlterable(i)) return;
|
||||
|
||||
x = e->we.click.pt.x;
|
||||
line = (e->we.click.pt.y - 127) / 10;
|
||||
if (e->we.click.pt.y >= 127 && IS_INT_INSIDE(line, 0, 2) && i->produced_cargo[line] != CT_INVALID) {
|
||||
if (IS_INT_INSIDE(x, 5, 25) ) {
|
||||
/* Clicked buttons, decrease or increase production */
|
||||
if (x < 15) {
|
||||
if (isProductionMinimum(i, line)) return;
|
||||
i->production_rate[line] = maxu(i->production_rate[line] / 2, 1);
|
||||
} else {
|
||||
if (isProductionMaximum(i, line)) return;
|
||||
i->production_rate[line] = minu(i->production_rate[line] * 2, 255);
|
||||
}
|
||||
|
||||
UpdateIndustryProduction(i);
|
||||
SetWindowDirty(w);
|
||||
w->flags4 |= 5 << WF_TIMEOUT_SHL;
|
||||
WP(w,vp2_d).data_2 = line+1;
|
||||
WP(w,vp2_d).data_3 = (x < 15 ? 1 : 2);
|
||||
} else if (IS_INT_INSIDE(x, 34, 160)) {
|
||||
// clicked the text
|
||||
WP(w,vp2_d).data_1 = line;
|
||||
SetDParam(0, i->production_rate[line] * 8);
|
||||
ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_CONFIG_GAME_PRODUCTION, 10, 100, w, CS_ALPHANUMERAL);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case 6:
|
||||
i = GetIndustry(w->window_number);
|
||||
ScrollMainWindowToTile(i->xy + TileDiffXY(1, 1));
|
||||
} break;
|
||||
|
||||
}
|
||||
break;
|
||||
case WE_TIMEOUT:
|
||||
WP(w,vp2_d).data_2 = 0;
|
||||
WP(w,vp2_d).data_3 = 0;
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
|
||||
case WE_ON_EDIT_TEXT:
|
||||
if (e->we.edittext.str[0] != '\0') {
|
||||
Industry* i = GetIndustry(w->window_number);
|
||||
int line = WP(w,vp2_d).data_1;
|
||||
|
||||
i->production_rate[line] = clampu(atoi(e->we.edittext.str), 0, 255);
|
||||
UpdateIndustryProduction(i);
|
||||
SetWindowDirty(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateIndustryProduction(Industry *i)
|
||||
{
|
||||
if (i->produced_cargo[0] != CT_INVALID)
|
||||
i->total_production[0] = 8 * i->production_rate[0];
|
||||
|
||||
if (i->produced_cargo[1] != CT_INVALID)
|
||||
i->total_production[1] = 8 * i->production_rate[1];
|
||||
}
|
||||
|
||||
static const Widget _industry_view_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 9, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 9, 11, 247, 0, 13, STR_4801, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_STICKYBOX, RESIZE_NONE, 9, 248, 259, 0, 13, 0x0, STR_STICKY_BUTTON},
|
||||
{ WWT_PANEL, RESIZE_NONE, 9, 0, 259, 14, 105, 0x0, STR_NULL},
|
||||
{ WWT_INSET, RESIZE_NONE, 9, 2, 257, 16, 103, 0x0, STR_NULL},
|
||||
{ WWT_PANEL, RESIZE_NONE, 9, 0, 259, 106, 147, 0x0, STR_NULL},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 9, 0, 129, 148, 159, STR_00E4_LOCATION, STR_482C_CENTER_THE_MAIN_VIEW_ON},
|
||||
{ WWT_PANEL, RESIZE_NONE, 9, 130, 259, 148, 159, 0x0, STR_NULL},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const WindowDesc _industry_view_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 260, 160,
|
||||
WC_INDUSTRY_VIEW,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
|
||||
_industry_view_widgets,
|
||||
IndustryViewWndProc
|
||||
};
|
||||
|
||||
void ShowIndustryViewWindow(int industry)
|
||||
{
|
||||
Window *w = AllocateWindowDescFront(&_industry_view_desc, industry);
|
||||
|
||||
if (w != NULL) {
|
||||
w->flags4 |= WF_DISABLE_VP_SCROLL;
|
||||
WP(w,vp2_d).data_1 = 0;
|
||||
WP(w,vp2_d).data_2 = 0;
|
||||
WP(w,vp2_d).data_3 = 0;
|
||||
AssignWindowViewport(w, 3, 17, 0xFE, 0x56, GetIndustry(w->window_number)->xy + TileDiffXY(1, 1), 1);
|
||||
}
|
||||
}
|
||||
|
||||
static const Widget _industry_directory_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
||||
{ WWT_CAPTION, RESIZE_NONE, 13, 11, 495, 0, 13, STR_INDUSTRYDIR_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
||||
{ WWT_STICKYBOX, RESIZE_NONE, 13, 496, 507, 0, 13, 0x0, STR_STICKY_BUTTON},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 13, 0, 100, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 13, 101, 200, 14, 25, STR_SORT_BY_TYPE, STR_SORT_ORDER_TIP},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 13, 201, 300, 14, 25, STR_SORT_BY_PRODUCTION, STR_SORT_ORDER_TIP},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 13, 301, 400, 14, 25, STR_SORT_BY_TRANSPORTED, STR_SORT_ORDER_TIP},
|
||||
{ WWT_PANEL, RESIZE_NONE, 13, 401, 495, 14, 25, 0x0, STR_NULL},
|
||||
{ WWT_PANEL, RESIZE_BOTTOM, 13, 0, 495, 26, 189, 0x0, STR_200A_TOWN_NAMES_CLICK_ON_NAME},
|
||||
{ WWT_SCROLLBAR, RESIZE_BOTTOM, 13, 496, 507, 14, 177, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
|
||||
{ WWT_RESIZEBOX, RESIZE_TB, 13, 496, 507, 178, 189, 0x0, STR_RESIZE_BUTTON},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static uint _num_industry_sort;
|
||||
|
||||
static char _bufcache[96];
|
||||
static const Industry* _last_industry;
|
||||
|
||||
static byte _industry_sort_order;
|
||||
|
||||
static int CDECL GeneralIndustrySorter(const void *a, const void *b)
|
||||
{
|
||||
const Industry* i = *(const Industry**)a;
|
||||
const Industry* j = *(const Industry**)b;
|
||||
int r;
|
||||
|
||||
switch (_industry_sort_order >> 1) {
|
||||
default: NOT_REACHED();
|
||||
case 0: /* Sort by Name (handled later) */
|
||||
r = 0;
|
||||
break;
|
||||
|
||||
case 1: /* Sort by Type */
|
||||
r = i->type - j->type;
|
||||
break;
|
||||
|
||||
case 2: /* Sort by Production */
|
||||
if (i->produced_cargo[0] == CT_INVALID) {
|
||||
r = (j->produced_cargo[0] == CT_INVALID ? 0 : -1);
|
||||
} else {
|
||||
if (j->produced_cargo[0] == CT_INVALID) {
|
||||
r = 1;
|
||||
} else {
|
||||
r =
|
||||
(i->total_production[0] + i->total_production[1]) -
|
||||
(j->total_production[0] + j->total_production[1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: /* Sort by transported fraction */
|
||||
if (i->produced_cargo[0] == CT_INVALID) {
|
||||
r = (j->produced_cargo[0] == CT_INVALID ? 0 : -1);
|
||||
} else {
|
||||
if (j->produced_cargo[0] == CT_INVALID) {
|
||||
r = 1;
|
||||
} else {
|
||||
int pi;
|
||||
int pj;
|
||||
|
||||
pi = i->pct_transported[0] * 100 >> 8;
|
||||
if (i->produced_cargo[1] != CT_INVALID) {
|
||||
int p = i->pct_transported[1] * 100 >> 8;
|
||||
if (p < pi) pi = p;
|
||||
}
|
||||
|
||||
pj = j->pct_transported[0] * 100 >> 8;
|
||||
if (j->produced_cargo[1] != CT_INVALID) {
|
||||
int p = j->pct_transported[1] * 100 >> 8;
|
||||
if (p < pj) pj = p;
|
||||
}
|
||||
|
||||
r = pi - pj;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// default to string sorting if they are otherwise equal
|
||||
if (r == 0) {
|
||||
char buf1[96];
|
||||
|
||||
SetDParam(0, i->town->index);
|
||||
GetString(buf1, STR_TOWN, lastof(buf1));
|
||||
|
||||
if (j != _last_industry) {
|
||||
_last_industry = j;
|
||||
SetDParam(0, j->town->index);
|
||||
GetString(_bufcache, STR_TOWN, lastof(_bufcache));
|
||||
}
|
||||
r = strcmp(buf1, _bufcache);
|
||||
}
|
||||
|
||||
if (_industry_sort_order & 1) r = -r;
|
||||
return r;
|
||||
}
|
||||
|
||||
static void MakeSortedIndustryList(void)
|
||||
{
|
||||
const Industry* i;
|
||||
int n = 0;
|
||||
|
||||
/* Don't attempt a sort if there are no industries */
|
||||
if (GetNumIndustries() == 0) return;
|
||||
|
||||
/* Create array for sorting */
|
||||
_industry_sort = realloc((void *)_industry_sort, (GetMaxIndustryIndex() + 1) * sizeof(_industry_sort[0]));
|
||||
if (_industry_sort == NULL) error("Could not allocate memory for the industry-sorting-list");
|
||||
|
||||
FOR_ALL_INDUSTRIES(i) _industry_sort[n++] = i;
|
||||
|
||||
_num_industry_sort = n;
|
||||
_last_industry = NULL; // used for "cache"
|
||||
|
||||
qsort((void*)_industry_sort, n, sizeof(_industry_sort[0]), GeneralIndustrySorter);
|
||||
|
||||
DEBUG(misc, 3, "Resorting industries list");
|
||||
}
|
||||
|
||||
|
||||
static void IndustryDirectoryWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_PAINT: {
|
||||
int n;
|
||||
uint p;
|
||||
static const uint16 _indicator_positions[4] = {88, 187, 284, 387};
|
||||
|
||||
if (_industry_sort_dirty) {
|
||||
_industry_sort_dirty = false;
|
||||
MakeSortedIndustryList();
|
||||
}
|
||||
|
||||
SetVScrollCount(w, _num_industry_sort);
|
||||
|
||||
DrawWindowWidgets(w);
|
||||
DoDrawString(_industry_sort_order & 1 ? DOWNARROW : UPARROW, _indicator_positions[_industry_sort_order>>1], 15, 0x10);
|
||||
|
||||
p = w->vscroll.pos;
|
||||
n = 0;
|
||||
|
||||
while (p < _num_industry_sort) {
|
||||
const Industry* i = _industry_sort[p];
|
||||
|
||||
SetDParam(0, i->index);
|
||||
if (i->produced_cargo[0] != CT_INVALID) {
|
||||
SetDParam(1, i->produced_cargo[0]);
|
||||
SetDParam(2, i->total_production[0]);
|
||||
|
||||
if (i->produced_cargo[1] != CT_INVALID) {
|
||||
SetDParam(3, i->produced_cargo[1]);
|
||||
SetDParam(4, i->total_production[1]);
|
||||
SetDParam(5, i->pct_transported[0] * 100 >> 8);
|
||||
SetDParam(6, i->pct_transported[1] * 100 >> 8);
|
||||
DrawString(4, 28+n*10, STR_INDUSTRYDIR_ITEM_TWO, 0);
|
||||
} else {
|
||||
SetDParam(3, i->pct_transported[0] * 100 >> 8);
|
||||
DrawString(4, 28+n*10, STR_INDUSTRYDIR_ITEM, 0);
|
||||
}
|
||||
} else {
|
||||
DrawString(4, 28+n*10, STR_INDUSTRYDIR_ITEM_NOPROD, 0);
|
||||
}
|
||||
p++;
|
||||
if (++n == w->vscroll.cap) break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case WE_CLICK:
|
||||
switch (e->we.click.widget) {
|
||||
case 3: {
|
||||
_industry_sort_order = _industry_sort_order==0 ? 1 : 0;
|
||||
_industry_sort_dirty = true;
|
||||
SetWindowDirty(w);
|
||||
} break;
|
||||
|
||||
case 4: {
|
||||
_industry_sort_order = _industry_sort_order==2 ? 3 : 2;
|
||||
_industry_sort_dirty = true;
|
||||
SetWindowDirty(w);
|
||||
} break;
|
||||
|
||||
case 5: {
|
||||
_industry_sort_order = _industry_sort_order==4 ? 5 : 4;
|
||||
_industry_sort_dirty = true;
|
||||
SetWindowDirty(w);
|
||||
} break;
|
||||
|
||||
case 6: {
|
||||
_industry_sort_order = _industry_sort_order==6 ? 7 : 6;
|
||||
_industry_sort_dirty = true;
|
||||
SetWindowDirty(w);
|
||||
} break;
|
||||
|
||||
case 8: {
|
||||
int y = (e->we.click.pt.y - 28) / 10;
|
||||
uint16 p;
|
||||
|
||||
if (!IS_INT_INSIDE(y, 0, w->vscroll.cap)) return;
|
||||
p = y + w->vscroll.pos;
|
||||
if (p < _num_industry_sort) {
|
||||
ScrollMainWindowToTile(_industry_sort[p]->xy);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_4:
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
|
||||
case WE_RESIZE:
|
||||
w->vscroll.cap += e->we.sizing.diff.y / 10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Industry List */
|
||||
static const WindowDesc _industry_directory_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 508, 190,
|
||||
WC_INDUSTRY_DIRECTORY,0,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
|
||||
_industry_directory_widgets,
|
||||
IndustryDirectoryWndProc
|
||||
};
|
||||
|
||||
|
||||
void ShowIndustryDirectory(void)
|
||||
{
|
||||
Window *w = AllocateWindowDescFront(&_industry_directory_desc, 0);
|
||||
|
||||
if (w != NULL) {
|
||||
w->vscroll.cap = 16;
|
||||
w->resize.height = w->height - 6 * 10; // minimum 10 items
|
||||
w->resize.step_height = 10;
|
||||
SetWindowDirty(w);
|
||||
}
|
||||
}
|
||||
259
src/industry_map.h
Normal file
259
src/industry_map.h
Normal file
@@ -0,0 +1,259 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file industry_map.h Accessors for industries */
|
||||
|
||||
#ifndef INDUSTRY_MAP_H
|
||||
#define INDUSTRY_MAP_H
|
||||
|
||||
#include "industry.h"
|
||||
#include "macros.h"
|
||||
#include "tile.h"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The following enums are indices used to know what to draw for this industry tile.
|
||||
* They all are pointing toward array _industry_draw_tile_data, in table/industry_land.h
|
||||
* How to calculate the correct position ? GFXid << 2 | IndustryStage (0 to 3)
|
||||
*/
|
||||
enum {
|
||||
GFX_COAL_MINE_TOWER_NOT_ANIMATED = 0,
|
||||
GFX_COAL_MINE_TOWER_ANIMATED = 1,
|
||||
GFX_POWERPLANT_CHIMNEY = 8,
|
||||
GFX_POWERPLANT_SPARKS = 10,
|
||||
GFX_OILRIG_1 = 24,
|
||||
GFX_OILRIG_2 = 25,
|
||||
GFX_OILRIG_3 = 26,
|
||||
GFX_OILRIG_4 = 27,
|
||||
GFX_OILRIG_5 = 28,
|
||||
GFX_OILWELL_NOT_ANIMATED = 29,
|
||||
GFX_OILWELL_ANIMATED_1 = 30,
|
||||
GFX_OILWELL_ANIMATED_2 = 31,
|
||||
GFX_OILWELL_ANIMATED_3 = 32,
|
||||
GFX_COPPER_MINE_TOWER_NOT_ANIMATED = 47,
|
||||
GFX_COPPER_MINE_TOWER_ANIMATED = 48,
|
||||
GFX_COPPER_MINE_CHIMNEY = 49,
|
||||
GFX_GOLD_MINE_TOWER_NOT_ANIMATED = 79,
|
||||
GFX_GOLD_MINE_TOWER_ANIMATED = 88,
|
||||
GFX_TOY_FACTORY = 143,
|
||||
GFX_PLASTIC_FOUNTAIN_ANIMATED_1 = 148,
|
||||
GFX_PLASTIC_FOUNTAIN_ANIMATED_2 = 149,
|
||||
GFX_PLASTIC_FOUNTAIN_ANIMATED_3 = 150,
|
||||
GFX_PLASTIC_FOUNTAIN_ANIMATED_4 = 151,
|
||||
GFX_PLASTIC_FOUNTAIN_ANIMATED_5 = 152,
|
||||
GFX_PLASTIC_FOUNTAIN_ANIMATED_6 = 153,
|
||||
GFX_PLASTIC_FOUNTAIN_ANIMATED_7 = 154,
|
||||
GFX_PLASTIC_FOUNTAIN_ANIMATED_8 = 155,
|
||||
GFX_BUBBLE_GENERATOR = 161,
|
||||
GFX_BUBBLE_CATCHER = 162,
|
||||
GFX_TOFFEE_QUARY = 165,
|
||||
GFX_SUGAR_MINE_SIEVE = 174,
|
||||
NUM_INDUSTRY_GFXES = 175,
|
||||
};
|
||||
|
||||
static inline IndustryID GetIndustryIndex(TileIndex t)
|
||||
{
|
||||
assert(IsTileType(t, MP_INDUSTRY));
|
||||
return _m[t].m2;
|
||||
}
|
||||
|
||||
static inline Industry* GetIndustryByTile(TileIndex t)
|
||||
{
|
||||
return GetIndustry(GetIndustryIndex(t));
|
||||
}
|
||||
|
||||
static inline bool IsIndustryCompleted(TileIndex t)
|
||||
{
|
||||
assert(IsTileType(t, MP_INDUSTRY));
|
||||
return HASBIT(_m[t].m1, 7);
|
||||
}
|
||||
|
||||
IndustryType GetIndustryType(TileIndex tile);
|
||||
|
||||
/**
|
||||
* Set if the industry that owns the tile as under construction or not
|
||||
* @param tile the tile to query
|
||||
* @param isCompleted whether it is completed or not
|
||||
* @pre IsTileType(tile, MP_INDUSTRY)
|
||||
*/
|
||||
static inline void SetIndustryCompleted(TileIndex tile, bool isCompleted)
|
||||
{
|
||||
assert(IsTileType(tile, MP_INDUSTRY));
|
||||
SB(_m[tile].m1, 7, 1, isCompleted ? 1 :0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the industry construction stage of the specified tile
|
||||
* @param tile the tile to query
|
||||
* @pre IsTileType(tile, MP_INDUSTRY)
|
||||
* @return the construction stage
|
||||
*/
|
||||
static inline byte GetIndustryConstructionStage(TileIndex tile)
|
||||
{
|
||||
assert(IsTileType(tile, MP_INDUSTRY));
|
||||
return GB(_m[tile].m1, 0, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the industry construction stage of the specified tile
|
||||
* @param tile the tile to query
|
||||
* @param value the new construction stage
|
||||
* @pre IsTileType(tile, MP_INDUSTRY)
|
||||
*/
|
||||
static inline void SetIndustryConstructionStage(TileIndex tile, byte value)
|
||||
{
|
||||
assert(IsTileType(tile, MP_INDUSTRY));
|
||||
SB(_m[tile].m1, 0, 2, value);
|
||||
}
|
||||
|
||||
static inline IndustryGfx GetIndustryGfx(TileIndex t)
|
||||
{
|
||||
assert(IsTileType(t, MP_INDUSTRY));
|
||||
return _m[t].m5;
|
||||
}
|
||||
|
||||
static inline void SetIndustryGfx(TileIndex t, IndustryGfx gfx)
|
||||
{
|
||||
assert(IsTileType(t, MP_INDUSTRY));
|
||||
_m[t].m5 = gfx;
|
||||
}
|
||||
|
||||
static inline void MakeIndustry(TileIndex t, IndustryID index, IndustryGfx gfx)
|
||||
{
|
||||
SetTileType(t, MP_INDUSTRY);
|
||||
_m[t].m1 = 0;
|
||||
_m[t].m2 = index;
|
||||
_m[t].m3 = 0;
|
||||
_m[t].m4 = 0;
|
||||
_m[t].m5 = gfx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this indutry tile's construction counter value
|
||||
* @param tile the tile to query
|
||||
* @pre IsTileType(tile, MP_INDUSTRY)
|
||||
* @return the construction counter
|
||||
*/
|
||||
static inline byte GetIndustryConstructionCounter(TileIndex tile)
|
||||
{
|
||||
assert(IsTileType(tile, MP_INDUSTRY));
|
||||
return GB(_m[tile].m1, 2, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this indutry tile's construction counter value
|
||||
* @param tile the tile to query
|
||||
* @param value the new value for the construction counter
|
||||
* @pre IsTileType(tile, MP_INDUSTRY)
|
||||
*/
|
||||
static inline void SetIndustryConstructionCounter(TileIndex tile, byte value)
|
||||
{
|
||||
assert(IsTileType(tile, MP_INDUSTRY));
|
||||
SB(_m[tile].m1, 2, 2, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the construction stage counter of the industry,
|
||||
* as well as the completion bit.
|
||||
* In fact, it is the same as restarting construction frmo ground up
|
||||
* @param tile the tile to query
|
||||
* @param generating_world whether generating a world or not
|
||||
* @pre IsTileType(tile, MP_INDUSTRY)
|
||||
*/
|
||||
static inline void ResetIndustryConstructionStage(TileIndex tile)
|
||||
{
|
||||
assert(IsTileType(tile, MP_INDUSTRY));
|
||||
_m[tile].m1 = 0;
|
||||
}
|
||||
|
||||
typedef struct IndustryTypeSolver {
|
||||
IndustryGfx MinGfx;
|
||||
IndustryGfx MaxGfx;
|
||||
} IndustryTypeSolver;
|
||||
|
||||
static const IndustryTypeSolver industry_gfx_Solver [IT_END] = {
|
||||
{ 0, 6}, //IT_COAL_MINE
|
||||
{ 7, 10}, //IT_POWER_STATION,
|
||||
{ 11, 15}, //IT_SAWMILL,
|
||||
{ 16, 17}, //IT_FOREST,
|
||||
{ 18, 23}, //IT_OIL_REFINERY,
|
||||
{ 24, 28}, //IT_OIL_RIG,
|
||||
{ 29, 31}, //IT_OIL_WELL,
|
||||
{ 32, 38}, //IT_FARM,
|
||||
{ 39, 42}, //IT_FACTORY,
|
||||
{ 43, 46}, //IT_PRINTING_WORKS,
|
||||
{ 47, 51}, //IT_COPPER_MINE,
|
||||
{ 52, 57}, //IT_STEEL_MILL,
|
||||
{ 58, 59}, //IT_BANK_TEMP,
|
||||
{ 60, 63}, //IT_FOOD_PROCESS,
|
||||
{ 64, 71}, //IT_PAPER_MILL,
|
||||
{ 72, 88}, //IT_GOLD_MINE,
|
||||
{ 89, 90}, //IT_BANK_TROPIC_ARCTIC,
|
||||
{ 91, 99}, //IT_DIAMOND_MINE,
|
||||
{100, 115}, //IT_IRON_MINE,
|
||||
{116, 116}, //IT_FRUIT_PLANTATION,
|
||||
{117, 117}, //IT_RUBBER_PLANTATION,
|
||||
{118, 119}, //IT_WATER_SUPPLY,
|
||||
{120, 120}, //IT_WATER_TOWER,
|
||||
{121, 124}, //IT_FACTORY_2,
|
||||
{125, 128}, //IT_LUMBER_MILL,
|
||||
{129, 130}, //IT_COTTON_CANDY,
|
||||
{131, 134}, //IT_CANDY_FACTORY or sweet factory
|
||||
{135, 136}, //IT_BATTERY_FARM,
|
||||
{137, 137}, //IT_COLA_WELLS,
|
||||
{138, 141}, //IT_TOY_SHOP,
|
||||
{142, 147}, //IT_TOY_FACTORY,
|
||||
{148, 155}, //IT_PLASTIC_FOUNTAINS,
|
||||
{156, 159}, //IT_FIZZY_DRINK_FACTORY,
|
||||
{160, 163}, //IT_BUBBLE_GENERATOR,
|
||||
{164, 166}, //IT_TOFFEE_QUARRY,
|
||||
{167, 174} //IT_SUGAR_MINE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the animation loop number
|
||||
* @param tile the tile to get the animation loop number of
|
||||
* @pre IsTileType(tile, MP_INDUSTRY)
|
||||
*/
|
||||
static inline byte GetIndustryAnimationLoop(TileIndex tile)
|
||||
{
|
||||
assert(IsTileType(tile, MP_INDUSTRY));
|
||||
return _m[tile].m4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the animation loop number
|
||||
* @param tile the tile to set the animation loop number of
|
||||
* @param count the new animation frame number
|
||||
* @pre IsTileType(tile, MP_INDUSTRY)
|
||||
*/
|
||||
static inline void SetIndustryAnimationLoop(TileIndex tile, byte count)
|
||||
{
|
||||
assert(IsTileType(tile, MP_INDUSTRY));
|
||||
_m[tile].m4 = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the animation state
|
||||
* @param tile the tile to get the animation state of
|
||||
* @pre IsTileType(tile, MP_INDUSTRY)
|
||||
*/
|
||||
static inline byte GetIndustryAnimationState(TileIndex tile)
|
||||
{
|
||||
assert(IsTileType(tile, MP_INDUSTRY));
|
||||
return _m[tile].m3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the animation state
|
||||
* @param tile the tile to set the animation state of
|
||||
* @param count the new animation state
|
||||
* @pre IsTileType(tile, MP_INDUSTRY)
|
||||
*/
|
||||
static inline void SetIndustryAnimationState(TileIndex tile, byte state)
|
||||
{
|
||||
assert(IsTileType(tile, MP_INDUSTRY));
|
||||
_m[tile].m3 = state;
|
||||
}
|
||||
|
||||
#endif /* INDUSTRY_MAP_H */
|
||||
149
src/intro_gui.c
Normal file
149
src/intro_gui.c
Normal file
@@ -0,0 +1,149 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "table/strings.h"
|
||||
#include "table/sprites.h"
|
||||
#include "functions.h"
|
||||
#include "window.h"
|
||||
#include "gui.h"
|
||||
#include "gfx.h"
|
||||
#include "player.h"
|
||||
#include "network/network.h"
|
||||
#include "variables.h"
|
||||
#include "settings.h"
|
||||
#include "heightmap.h"
|
||||
#include "genworld.h"
|
||||
#include "network/network_gui.h"
|
||||
#include "newgrf.h"
|
||||
|
||||
static const Widget _select_game_widgets[] = {
|
||||
{ WWT_CAPTION, RESIZE_NONE, 13, 0, 335, 0, 13, STR_0307_OPENTTD, STR_NULL},
|
||||
{ WWT_PANEL, RESIZE_NONE, 13, 0, 335, 14, 194, 0x0, STR_NULL},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 22, 33, STR_0140_NEW_GAME, STR_02FB_START_A_NEW_GAME},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 22, 33, STR_0141_LOAD_GAME, STR_02FC_LOAD_A_SAVED_GAME},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 40, 51, STR_029A_PLAY_SCENARIO, STR_0303_START_A_NEW_GAME_USING},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 40, 51, STR_PLAY_HEIGHTMAP, STR_PLAY_HEIGHTMAP_HINT},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 58, 69, STR_0220_CREATE_SCENARIO, STR_02FE_CREATE_A_CUSTOMIZED_GAME},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 58, 69, STR_MULTIPLAYER, STR_0300_SELECT_MULTIPLAYER_GAME},
|
||||
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 10, 86, 77, 131, SPR_SELECT_TEMPERATE, STR_030E_SELECT_TEMPERATE_LANDSCAPE},
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 90, 166, 77, 131, SPR_SELECT_SUB_ARCTIC, STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 170, 246, 77, 131, SPR_SELECT_SUB_TROPICAL, STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
|
||||
{ WWT_IMGBTN_2, RESIZE_NONE, 12, 250, 326, 77, 131, SPR_SELECT_TOYLAND, STR_0311_SELECT_TOYLAND_LANDSCAPE},
|
||||
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 139, 150, STR_0148_GAME_OPTIONS, STR_0301_DISPLAY_GAME_OPTIONS},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 139, 150, STR_01FE_DIFFICULTY, STR_0302_DISPLAY_DIFFICULTY_OPTIONS},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 157, 168, STR_CONFIG_PATCHES, STR_CONFIG_PATCHES_TIP},
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 157, 168, STR_NEWGRF_SETTINGS_BUTTON, STR_NULL},
|
||||
|
||||
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 104, 231, 175, 186, STR_0304_QUIT, STR_0305_QUIT_OPENTTD},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static inline void SetNewLandscapeType(byte landscape)
|
||||
{
|
||||
_opt_newgame.landscape = landscape;
|
||||
InvalidateWindowClasses(WC_SELECT_GAME);
|
||||
}
|
||||
|
||||
static void SelectGameWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch (e->event) {
|
||||
case WE_CREATE: LowerWindowWidget(w, _opt_newgame.landscape + 8); break;
|
||||
|
||||
case WE_PAINT:
|
||||
SetWindowWidgetLoweredState(w, 8, _opt_newgame.landscape == LT_NORMAL);
|
||||
SetWindowWidgetLoweredState(w, 9, _opt_newgame.landscape == LT_HILLY);
|
||||
SetWindowWidgetLoweredState(w, 10, _opt_newgame.landscape == LT_DESERT);
|
||||
SetWindowWidgetLoweredState(w, 11, _opt_newgame.landscape == LT_CANDY);
|
||||
SetDParam(0, STR_6801_EASY + _opt_newgame.diff_level);
|
||||
DrawWindowWidgets(w);
|
||||
break;
|
||||
|
||||
case WE_CLICK:
|
||||
switch (e->we.click.widget) {
|
||||
case 2: ShowGenerateLandscape(); break;
|
||||
case 3: ShowSaveLoadDialog(SLD_LOAD_GAME); break;
|
||||
case 4: ShowSaveLoadDialog(SLD_LOAD_SCENARIO); break;
|
||||
case 5: ShowSaveLoadDialog(SLD_LOAD_HEIGHTMAP); break;
|
||||
case 6: ShowCreateScenario(); break;
|
||||
case 7:
|
||||
if (!_network_available) {
|
||||
ShowErrorMessage(INVALID_STRING_ID, STR_NETWORK_ERR_NOTAVAILABLE, 0, 0);
|
||||
} else {
|
||||
ShowNetworkGameWindow();
|
||||
}
|
||||
break;
|
||||
case 8: case 9: case 10: case 11:
|
||||
RaiseWindowWidget(w, _opt_newgame.landscape + 8);
|
||||
SetNewLandscapeType(e->we.click.widget - 8);
|
||||
break;
|
||||
case 12: ShowGameOptions(); break;
|
||||
case 13: ShowGameDifficulty(); break;
|
||||
case 14: ShowPatchesSelection(); break;
|
||||
case 15: ShowNewGRFSettings(true, true, false, &_grfconfig_newgame); break;
|
||||
case 16: HandleExitGameRequest(); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const WindowDesc _select_game_desc = {
|
||||
WDP_CENTER, WDP_CENTER, 336, 195,
|
||||
WC_SELECT_GAME,0,
|
||||
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
|
||||
_select_game_widgets,
|
||||
SelectGameWndProc
|
||||
};
|
||||
|
||||
void ShowSelectGameWindow(void)
|
||||
{
|
||||
AllocateWindowDesc(&_select_game_desc);
|
||||
}
|
||||
|
||||
static void AskExitGameCallback(Window *w, bool confirmed)
|
||||
{
|
||||
if (confirmed) _exit_game = true;
|
||||
}
|
||||
|
||||
void AskExitGame(void)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
SetDParam(0, STR_0133_WINDOWS);
|
||||
#elif defined(__APPLE__)
|
||||
SetDParam(0, STR_0135_OSX);
|
||||
#elif defined(__BEOS__)
|
||||
SetDParam(0, STR_OSNAME_BEOS);
|
||||
#elif defined(__MORPHOS__)
|
||||
SetDParam(0, STR_OSNAME_MORPHOS);
|
||||
#elif defined(__AMIGA__)
|
||||
SetDParam(0, STR_OSNAME_AMIGAOS);
|
||||
#elif defined(__OS2__)
|
||||
SetDParam(0, STR_OSNAME_OS2);
|
||||
#else
|
||||
SetDParam(0, STR_0134_UNIX);
|
||||
#endif
|
||||
ShowQuery(
|
||||
STR_00C7_QUIT,
|
||||
STR_00CA_ARE_YOU_SURE_YOU_WANT_TO,
|
||||
NULL,
|
||||
AskExitGameCallback
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
static void AskExitToGameMenuCallback(Window *w, bool confirmed)
|
||||
{
|
||||
if (confirmed) _switch_mode = SM_MENU;
|
||||
}
|
||||
|
||||
void AskExitToGameMenu(void)
|
||||
{
|
||||
ShowQuery(
|
||||
STR_0161_QUIT_GAME,
|
||||
(_game_mode != GM_EDITOR) ? STR_ABANDON_GAME_QUERY : STR_QUIT_SCENARIO_QUERY,
|
||||
NULL,
|
||||
AskExitToGameMenuCallback
|
||||
);
|
||||
}
|
||||
731
src/landscape.c
Normal file
731
src/landscape.c
Normal file
@@ -0,0 +1,731 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openttd.h"
|
||||
#include "bridge_map.h"
|
||||
#include "heightmap.h"
|
||||
#include "clear_map.h"
|
||||
#include "functions.h"
|
||||
#include "map.h"
|
||||
#include "player.h"
|
||||
#include "spritecache.h"
|
||||
#include "table/sprites.h"
|
||||
#include "tile.h"
|
||||
#include <stdarg.h>
|
||||
#include "viewport.h"
|
||||
#include "command.h"
|
||||
#include "vehicle.h"
|
||||
#include "variables.h"
|
||||
#include "void_map.h"
|
||||
#include "water_map.h"
|
||||
#include "tgp.h"
|
||||
#include "genworld.h"
|
||||
#include "heightmap.h"
|
||||
|
||||
extern const TileTypeProcs
|
||||
_tile_type_clear_procs,
|
||||
_tile_type_rail_procs,
|
||||
_tile_type_road_procs,
|
||||
_tile_type_town_procs,
|
||||
_tile_type_trees_procs,
|
||||
_tile_type_station_procs,
|
||||
_tile_type_water_procs,
|
||||
_tile_type_dummy_procs,
|
||||
_tile_type_industry_procs,
|
||||
_tile_type_tunnelbridge_procs,
|
||||
_tile_type_unmovable_procs;
|
||||
|
||||
const TileTypeProcs * const _tile_type_procs[16] = {
|
||||
&_tile_type_clear_procs,
|
||||
&_tile_type_rail_procs,
|
||||
&_tile_type_road_procs,
|
||||
&_tile_type_town_procs,
|
||||
&_tile_type_trees_procs,
|
||||
&_tile_type_station_procs,
|
||||
&_tile_type_water_procs,
|
||||
&_tile_type_dummy_procs,
|
||||
&_tile_type_industry_procs,
|
||||
&_tile_type_tunnelbridge_procs,
|
||||
&_tile_type_unmovable_procs,
|
||||
};
|
||||
|
||||
/* landscape slope => sprite */
|
||||
const byte _tileh_to_sprite[32] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 17, 0, 15, 18, 0,
|
||||
};
|
||||
|
||||
const byte _inclined_tileh[] = {
|
||||
SLOPE_SW, SLOPE_NW, SLOPE_SW, SLOPE_SE, SLOPE_NE, SLOPE_SE, SLOPE_NE, SLOPE_NW,
|
||||
SLOPE_E, SLOPE_N, SLOPE_W, SLOPE_S,
|
||||
SLOPE_NWS, SLOPE_WSE, SLOPE_SEN, SLOPE_ENW
|
||||
};
|
||||
|
||||
|
||||
uint GetPartialZ(int x, int y, Slope corners)
|
||||
{
|
||||
int z = 0;
|
||||
|
||||
switch (corners) {
|
||||
case SLOPE_W:
|
||||
if (x - y >= 0)
|
||||
z = (x - y) >> 1;
|
||||
break;
|
||||
|
||||
case SLOPE_S:
|
||||
y^=0xF;
|
||||
if ( (x - y) >= 0)
|
||||
z = (x - y) >> 1;
|
||||
break;
|
||||
|
||||
case SLOPE_SW:
|
||||
z = (x>>1) + 1;
|
||||
break;
|
||||
|
||||
case SLOPE_E:
|
||||
if (y - x >= 0)
|
||||
z = (y - x) >> 1;
|
||||
break;
|
||||
|
||||
case SLOPE_EW:
|
||||
case SLOPE_NS:
|
||||
case SLOPE_ELEVATED:
|
||||
z = 4;
|
||||
break;
|
||||
|
||||
case SLOPE_SE:
|
||||
z = (y>>1) + 1;
|
||||
break;
|
||||
|
||||
case SLOPE_WSE:
|
||||
z = 8;
|
||||
y^=0xF;
|
||||
if (x - y < 0)
|
||||
z += (x - y) >> 1;
|
||||
break;
|
||||
|
||||
case SLOPE_N:
|
||||
y ^= 0xF;
|
||||
if (y - x >= 0)
|
||||
z = (y - x) >> 1;
|
||||
break;
|
||||
|
||||
case SLOPE_NW:
|
||||
z = (y^0xF)>>1;
|
||||
break;
|
||||
|
||||
case SLOPE_NWS:
|
||||
z = 8;
|
||||
if (x - y < 0)
|
||||
z += (x - y) >> 1;
|
||||
break;
|
||||
|
||||
case SLOPE_NE:
|
||||
z = (x^0xF)>>1;
|
||||
break;
|
||||
|
||||
case SLOPE_ENW:
|
||||
z = 8;
|
||||
y ^= 0xF;
|
||||
if (y - x < 0)
|
||||
z += (y - x) >> 1;
|
||||
break;
|
||||
|
||||
case SLOPE_SEN:
|
||||
z = 8;
|
||||
if (y - x < 0)
|
||||
z += (y - x) >> 1;
|
||||
break;
|
||||
|
||||
case SLOPE_STEEP_S:
|
||||
z = 1 + ((x+y)>>1);
|
||||
break;
|
||||
|
||||
case SLOPE_STEEP_W:
|
||||
z = 1 + ((x+(y^0xF))>>1);
|
||||
break;
|
||||
|
||||
case SLOPE_STEEP_N:
|
||||
z = 1 + (((x^0xF)+(y^0xF))>>1);
|
||||
break;
|
||||
|
||||
case SLOPE_STEEP_E:
|
||||
z = 1 + (((x^0xF)+(y^0xF))>>1);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
uint GetSlopeZ(int x, int y)
|
||||
{
|
||||
TileIndex tile = TileVirtXY(x, y);
|
||||
|
||||
return _tile_type_procs[GetTileType(tile)]->get_slope_z_proc(tile, x, y);
|
||||
}
|
||||
|
||||
|
||||
static Slope GetFoundationSlope(TileIndex tile, uint* z)
|
||||
{
|
||||
Slope tileh = GetTileSlope(tile, z);
|
||||
Slope slope = _tile_type_procs[GetTileType(tile)]->get_slope_tileh_proc(tile, tileh);
|
||||
|
||||
// Flatter slope -> higher base height
|
||||
if (slope < tileh) *z += TILE_HEIGHT;
|
||||
return slope;
|
||||
}
|
||||
|
||||
|
||||
static bool HasFoundationNW(TileIndex tile, Slope slope_here, uint z_here)
|
||||
{
|
||||
uint z;
|
||||
Slope slope = GetFoundationSlope(TILE_ADDXY(tile, 0, -1), &z);
|
||||
|
||||
return
|
||||
(
|
||||
z_here + (slope_here & SLOPE_N ? TILE_HEIGHT : 0) + (slope_here == SLOPE_STEEP_N ? TILE_HEIGHT : 0) >
|
||||
z + (slope & SLOPE_E ? TILE_HEIGHT : 0) + (slope == SLOPE_STEEP_E ? TILE_HEIGHT : 0)
|
||||
) || (
|
||||
z_here + (slope_here & SLOPE_W ? TILE_HEIGHT : 0) + (slope_here == SLOPE_STEEP_W ? TILE_HEIGHT : 0) >
|
||||
z + (slope & SLOPE_S ? TILE_HEIGHT : 0) + (slope == SLOPE_STEEP_S ? TILE_HEIGHT : 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
static bool HasFoundationNE(TileIndex tile, Slope slope_here, uint z_here)
|
||||
{
|
||||
uint z;
|
||||
Slope slope = GetFoundationSlope(TILE_ADDXY(tile, -1, 0), &z);
|
||||
|
||||
return
|
||||
(
|
||||
z_here + (slope_here & SLOPE_N ? TILE_HEIGHT : 0) + (slope_here == SLOPE_STEEP_N ? TILE_HEIGHT : 0) >
|
||||
z + (slope & SLOPE_W ? TILE_HEIGHT : 0) + (slope == SLOPE_STEEP_W ? TILE_HEIGHT : 0)
|
||||
) || (
|
||||
z_here + (slope_here & SLOPE_E ? TILE_HEIGHT : 0) + (slope_here == SLOPE_STEEP_E ? TILE_HEIGHT : 0) >
|
||||
z + (slope & SLOPE_S ? TILE_HEIGHT : 0) + (slope == SLOPE_STEEP_S ? TILE_HEIGHT : 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void DrawFoundation(TileInfo *ti, uint f)
|
||||
{
|
||||
uint32 sprite_base = SPR_SLOPES_BASE - 15;
|
||||
Slope slope;
|
||||
uint z;
|
||||
|
||||
slope = GetFoundationSlope(ti->tile, &z);
|
||||
if (!HasFoundationNW(ti->tile, slope, z)) sprite_base += 22;
|
||||
if (!HasFoundationNE(ti->tile, slope, z)) sprite_base += 44;
|
||||
|
||||
if (IsSteepSlope(ti->tileh)) {
|
||||
uint32 lower_base;
|
||||
|
||||
// Lower part of foundation
|
||||
lower_base = sprite_base;
|
||||
if (lower_base == SPR_SLOPES_BASE - 15) lower_base = SPR_FOUNDATION_BASE;
|
||||
AddSortableSpriteToDraw(
|
||||
lower_base + (ti->tileh & ~SLOPE_STEEP), ti->x, ti->y, 16, 16, 7, ti->z
|
||||
);
|
||||
ti->z += TILE_HEIGHT;
|
||||
ti->tileh = _inclined_tileh[f - 15];
|
||||
if (f < 15 + 8) {
|
||||
// inclined
|
||||
AddSortableSpriteToDraw(sprite_base + f, ti->x, ti->y, 16, 16, 1, ti->z);
|
||||
OffsetGroundSprite(31, 9);
|
||||
} else if (f >= 15 + 8 + 4) {
|
||||
// three corners raised
|
||||
uint32 upper = sprite_base + 15 + (f - 15 - 8 - 4) * 2;
|
||||
|
||||
AddSortableSpriteToDraw(upper, ti->x, ti->y, 16, 16, 1, ti->z);
|
||||
AddChildSpriteScreen(upper + 1, 31, 9);
|
||||
OffsetGroundSprite(31, 9);
|
||||
} else {
|
||||
// one corner raised
|
||||
OffsetGroundSprite(31, 1);
|
||||
}
|
||||
} else {
|
||||
if (f < 15) {
|
||||
// leveled foundation
|
||||
// Use the original slope sprites if NW and NE borders should be visible
|
||||
if (sprite_base == SPR_SLOPES_BASE - 15) sprite_base = SPR_FOUNDATION_BASE;
|
||||
|
||||
AddSortableSpriteToDraw(sprite_base + f, ti->x, ti->y, 16, 16, 7, ti->z);
|
||||
ti->z += TILE_HEIGHT;
|
||||
ti->tileh = SLOPE_FLAT;
|
||||
OffsetGroundSprite(31, 1);
|
||||
} else {
|
||||
// inclined foundation
|
||||
AddSortableSpriteToDraw(sprite_base + f, ti->x, ti->y, 16, 16, 1, ti->z);
|
||||
ti->tileh = _inclined_tileh[f - 15];
|
||||
OffsetGroundSprite(31, 9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DoClearSquare(TileIndex tile)
|
||||
{
|
||||
MakeClear(tile, CLEAR_GRASS, _generating_world ? 3 : 0);
|
||||
MarkTileDirtyByTile(tile);
|
||||
}
|
||||
|
||||
uint32 GetTileTrackStatus(TileIndex tile, TransportType mode)
|
||||
{
|
||||
return _tile_type_procs[GetTileType(tile)]->get_tile_track_status_proc(tile, mode);
|
||||
}
|
||||
|
||||
void ChangeTileOwner(TileIndex tile, byte old_player, byte new_player)
|
||||
{
|
||||
_tile_type_procs[GetTileType(tile)]->change_tile_owner_proc(tile, old_player, new_player);
|
||||
}
|
||||
|
||||
void GetAcceptedCargo(TileIndex tile, AcceptedCargo ac)
|
||||
{
|
||||
memset(ac, 0, sizeof(AcceptedCargo));
|
||||
_tile_type_procs[GetTileType(tile)]->get_accepted_cargo_proc(tile, ac);
|
||||
}
|
||||
|
||||
void AnimateTile(TileIndex tile)
|
||||
{
|
||||
_tile_type_procs[GetTileType(tile)]->animate_tile_proc(tile);
|
||||
}
|
||||
|
||||
void ClickTile(TileIndex tile)
|
||||
{
|
||||
_tile_type_procs[GetTileType(tile)]->click_tile_proc(tile);
|
||||
}
|
||||
|
||||
void GetTileDesc(TileIndex tile, TileDesc *td)
|
||||
{
|
||||
_tile_type_procs[GetTileType(tile)]->get_tile_desc_proc(tile, td);
|
||||
}
|
||||
|
||||
/** Clear a piece of landscape
|
||||
* @param tile tile to clear
|
||||
* @param p1 unused
|
||||
* @param p2 unused
|
||||
*/
|
||||
int32 CmdLandscapeClear(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
||||
{
|
||||
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
||||
|
||||
return _tile_type_procs[GetTileType(tile)]->clear_tile_proc(tile, flags);
|
||||
}
|
||||
|
||||
/** Clear a big piece of landscape
|
||||
* @param tile end tile of area dragging
|
||||
* @param p1 start tile of area dragging
|
||||
* @param p2 unused
|
||||
*/
|
||||
int32 CmdClearArea(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
||||
{
|
||||
int32 cost, ret, money;
|
||||
int ex;
|
||||
int ey;
|
||||
int sx,sy;
|
||||
int x,y;
|
||||
bool success = false;
|
||||
|
||||
if (p1 >= MapSize()) return CMD_ERROR;
|
||||
|
||||
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
||||
|
||||
// make sure sx,sy are smaller than ex,ey
|
||||
ex = TileX(tile);
|
||||
ey = TileY(tile);
|
||||
sx = TileX(p1);
|
||||
sy = TileY(p1);
|
||||
if (ex < sx) intswap(ex, sx);
|
||||
if (ey < sy) intswap(ey, sy);
|
||||
|
||||
money = GetAvailableMoneyForCommand();
|
||||
cost = 0;
|
||||
|
||||
for (x = sx; x <= ex; ++x) {
|
||||
for (y = sy; y <= ey; ++y) {
|
||||
ret = DoCommand(TileXY(x, y), 0, 0, flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR);
|
||||
if (CmdFailed(ret)) continue;
|
||||
cost += ret;
|
||||
success = true;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
if (ret > 0 && (money -= ret) < 0) {
|
||||
_additional_cash_required = ret;
|
||||
return cost - ret;
|
||||
}
|
||||
DoCommand(TileXY(x, y), 0, 0, flags, CMD_LANDSCAPE_CLEAR);
|
||||
|
||||
// draw explosion animation...
|
||||
if ((x == sx || x == ex) && (y == sy || y == ey)) {
|
||||
// big explosion in each corner, or small explosion for single tiles
|
||||
CreateEffectVehicleAbove(x * TILE_SIZE + TILE_SIZE / 2, y * TILE_SIZE + TILE_SIZE / 2, 2,
|
||||
sy == ey && sx == ex ? EV_EXPLOSION_SMALL : EV_EXPLOSION_LARGE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (success) ? cost : CMD_ERROR;
|
||||
}
|
||||
|
||||
|
||||
#define TILELOOP_BITS 4
|
||||
#define TILELOOP_SIZE (1 << TILELOOP_BITS)
|
||||
#define TILELOOP_ASSERTMASK ((TILELOOP_SIZE-1) + ((TILELOOP_SIZE-1) << MapLogX()))
|
||||
#define TILELOOP_CHKMASK (((1 << (MapLogX() - TILELOOP_BITS))-1) << TILELOOP_BITS)
|
||||
|
||||
void RunTileLoop(void)
|
||||
{
|
||||
TileIndex tile;
|
||||
uint count;
|
||||
|
||||
tile = _cur_tileloop_tile;
|
||||
|
||||
assert( (tile & ~TILELOOP_ASSERTMASK) == 0);
|
||||
count = (MapSizeX() / TILELOOP_SIZE) * (MapSizeY() / TILELOOP_SIZE);
|
||||
do {
|
||||
_tile_type_procs[GetTileType(tile)]->tile_loop_proc(tile);
|
||||
|
||||
if (TileX(tile) < MapSizeX() - TILELOOP_SIZE) {
|
||||
tile += TILELOOP_SIZE; /* no overflow */
|
||||
} else {
|
||||
tile = TILE_MASK(tile - TILELOOP_SIZE * (MapSizeX() / TILELOOP_SIZE - 1) + TileDiffXY(0, TILELOOP_SIZE)); /* x would overflow, also increase y */
|
||||
}
|
||||
} while (--count);
|
||||
assert( (tile & ~TILELOOP_ASSERTMASK) == 0);
|
||||
|
||||
tile += 9;
|
||||
if (tile & TILELOOP_CHKMASK)
|
||||
tile = (tile + MapSizeX()) & TILELOOP_ASSERTMASK;
|
||||
_cur_tileloop_tile = tile;
|
||||
}
|
||||
|
||||
void InitializeLandscape(void)
|
||||
{
|
||||
uint maxx = MapMaxX();
|
||||
uint maxy = MapMaxY();
|
||||
uint sizex = MapSizeX();
|
||||
uint x;
|
||||
uint y;
|
||||
|
||||
for (y = 0; y < maxy; y++) {
|
||||
for (x = 0; x < maxx; x++) {
|
||||
MakeClear(sizex * y + x, CLEAR_GRASS, 3);
|
||||
SetTileHeight(sizex * y + x, 0);
|
||||
_m[sizex * y + x].extra = 0;
|
||||
ClearBridgeMiddle(sizex * y + x);
|
||||
}
|
||||
MakeVoid(sizex * y + x);
|
||||
}
|
||||
for (x = 0; x < sizex; x++) MakeVoid(sizex * y + x);
|
||||
}
|
||||
|
||||
void ConvertGroundTilesIntoWaterTiles(void)
|
||||
{
|
||||
TileIndex tile;
|
||||
uint z;
|
||||
Slope slope;
|
||||
|
||||
for (tile = 0; tile < MapSize(); ++tile) {
|
||||
slope = GetTileSlope(tile, &z);
|
||||
if (IsTileType(tile, MP_CLEAR) && z == 0) {
|
||||
/* Make both water for tiles at level 0
|
||||
* and make shore, as that looks much better
|
||||
* during the generation. */
|
||||
switch (slope) {
|
||||
case SLOPE_FLAT:
|
||||
MakeWater(tile);
|
||||
break;
|
||||
|
||||
case SLOPE_N:
|
||||
case SLOPE_E:
|
||||
case SLOPE_S:
|
||||
case SLOPE_W:
|
||||
case SLOPE_NW:
|
||||
case SLOPE_SW:
|
||||
case SLOPE_SE:
|
||||
case SLOPE_NE:
|
||||
MakeShore(tile);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const byte _genterrain_tbl_1[5] = { 10, 22, 33, 37, 4 };
|
||||
static const byte _genterrain_tbl_2[5] = { 0, 0, 0, 0, 33 };
|
||||
|
||||
static void GenerateTerrain(int type, int flag)
|
||||
{
|
||||
uint32 r;
|
||||
uint x;
|
||||
uint y;
|
||||
uint w;
|
||||
uint h;
|
||||
const Sprite* template;
|
||||
const byte *p;
|
||||
Tile* tile;
|
||||
byte direction;
|
||||
|
||||
r = Random();
|
||||
template = GetSprite((((r >> 24) * _genterrain_tbl_1[type]) >> 8) + _genterrain_tbl_2[type] + 4845);
|
||||
|
||||
x = r & MapMaxX();
|
||||
y = (r >> MapLogX()) & MapMaxY();
|
||||
|
||||
|
||||
if (x < 2 || y < 2) return;
|
||||
|
||||
direction = GB(r, 22, 2);
|
||||
if (direction & 1) {
|
||||
w = template->height;
|
||||
h = template->width;
|
||||
} else {
|
||||
w = template->width;
|
||||
h = template->height;
|
||||
}
|
||||
p = template->data;
|
||||
|
||||
if (flag & 4) {
|
||||
uint xw = x * MapSizeY();
|
||||
uint yw = y * MapSizeX();
|
||||
uint bias = (MapSizeX() + MapSizeY()) * 16;
|
||||
|
||||
switch (flag & 3) {
|
||||
case 0:
|
||||
if (xw + yw > MapSize() - bias) return;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (yw < xw + bias) return;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (xw + yw < MapSize() + bias) return;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (xw < yw + bias) return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x + w >= MapMaxX() - 1) return;
|
||||
if (y + h >= MapMaxY() - 1) return;
|
||||
|
||||
tile = &_m[TileXY(x, y)];
|
||||
|
||||
switch (direction) {
|
||||
case 0:
|
||||
do {
|
||||
Tile* tile_cur = tile;
|
||||
uint w_cur;
|
||||
|
||||
for (w_cur = w; w_cur != 0; --w_cur) {
|
||||
if (*p >= tile_cur->type_height) tile_cur->type_height = *p;
|
||||
p++;
|
||||
tile_cur++;
|
||||
}
|
||||
tile += TileDiffXY(0, 1);
|
||||
} while (--h != 0);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
do {
|
||||
Tile* tile_cur = tile;
|
||||
uint h_cur;
|
||||
|
||||
for (h_cur = h; h_cur != 0; --h_cur) {
|
||||
if (*p >= tile_cur->type_height) tile_cur->type_height = *p;
|
||||
p++;
|
||||
tile_cur += TileDiffXY(0, 1);
|
||||
}
|
||||
tile++;
|
||||
} while (--w != 0);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
tile += TileDiffXY(w - 1, 0);
|
||||
do {
|
||||
Tile* tile_cur = tile;
|
||||
uint w_cur;
|
||||
|
||||
for (w_cur = w; w_cur != 0; --w_cur) {
|
||||
if (*p >= tile_cur->type_height) tile_cur->type_height = *p;
|
||||
p++;
|
||||
tile_cur--;
|
||||
}
|
||||
tile += TileDiffXY(0, 1);
|
||||
} while (--h != 0);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
tile += TileDiffXY(0, h - 1);
|
||||
do {
|
||||
Tile* tile_cur = tile;
|
||||
uint h_cur;
|
||||
|
||||
for (h_cur = h; h_cur != 0; --h_cur) {
|
||||
if (*p >= tile_cur->type_height) tile_cur->type_height = *p;
|
||||
p++;
|
||||
tile_cur -= TileDiffXY(0, 1);
|
||||
}
|
||||
tile++;
|
||||
} while (--w != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#include "table/genland.h"
|
||||
|
||||
static void CreateDesertOrRainForest(void)
|
||||
{
|
||||
TileIndex tile;
|
||||
TileIndex update_freq = MapSize() / 4;
|
||||
const TileIndexDiffC *data;
|
||||
uint i;
|
||||
|
||||
for (tile = 0; tile != MapSize(); ++tile) {
|
||||
if ((tile % update_freq) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
|
||||
|
||||
for (data = _make_desert_or_rainforest_data;
|
||||
data != endof(_make_desert_or_rainforest_data); ++data) {
|
||||
TileIndex t = TILE_MASK(tile + ToTileIndexDiff(*data));
|
||||
if (TileHeight(t) >= 4 || IsTileType(t, MP_WATER)) break;
|
||||
}
|
||||
if (data == endof(_make_desert_or_rainforest_data))
|
||||
SetTropicZone(tile, TROPICZONE_DESERT);
|
||||
}
|
||||
|
||||
for (i = 0; i != 256; i++) {
|
||||
if ((i % 64) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
|
||||
|
||||
RunTileLoop();
|
||||
}
|
||||
|
||||
for (tile = 0; tile != MapSize(); ++tile) {
|
||||
if ((tile % update_freq) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
|
||||
|
||||
for (data = _make_desert_or_rainforest_data;
|
||||
data != endof(_make_desert_or_rainforest_data); ++data) {
|
||||
TileIndex t = TILE_MASK(tile + ToTileIndexDiff(*data));
|
||||
if (IsTileType(t, MP_CLEAR) && IsClearGround(t, CLEAR_DESERT)) break;
|
||||
}
|
||||
if (data == endof(_make_desert_or_rainforest_data))
|
||||
SetTropicZone(tile, TROPICZONE_RAINFOREST);
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateLandscape(byte mode)
|
||||
{
|
||||
const int gwp_desert_amount = 4 + 8;
|
||||
uint i;
|
||||
uint flag;
|
||||
uint32 r;
|
||||
|
||||
if (mode == GW_HEIGHTMAP) {
|
||||
SetGeneratingWorldProgress(GWP_LANDSCAPE, (_opt.landscape == LT_DESERT) ? 1 + gwp_desert_amount : 1);
|
||||
LoadHeightmap(_file_to_saveload.name);
|
||||
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
|
||||
} else if (_patches.land_generator == LG_TERRAGENESIS) {
|
||||
SetGeneratingWorldProgress(GWP_LANDSCAPE, (_opt.landscape == LT_DESERT) ? 3 + gwp_desert_amount : 3);
|
||||
GenerateTerrainPerlin();
|
||||
} else {
|
||||
switch (_opt.landscape) {
|
||||
case LT_HILLY:
|
||||
SetGeneratingWorldProgress(GWP_LANDSCAPE, 2);
|
||||
|
||||
for (i = ScaleByMapSize((Random() & 0x7F) + 950); i != 0; --i) {
|
||||
GenerateTerrain(2, 0);
|
||||
}
|
||||
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
|
||||
|
||||
r = Random();
|
||||
flag = GB(r, 0, 2) | 4;
|
||||
for (i = ScaleByMapSize(GB(r, 16, 7) + 450); i != 0; --i) {
|
||||
GenerateTerrain(4, flag);
|
||||
}
|
||||
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
|
||||
break;
|
||||
|
||||
case LT_DESERT:
|
||||
SetGeneratingWorldProgress(GWP_LANDSCAPE, 3 + gwp_desert_amount);
|
||||
|
||||
for (i = ScaleByMapSize((Random() & 0x7F) + 170); i != 0; --i) {
|
||||
GenerateTerrain(0, 0);
|
||||
}
|
||||
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
|
||||
|
||||
r = Random();
|
||||
flag = GB(r, 0, 2) | 4;
|
||||
for (i = ScaleByMapSize(GB(r, 16, 8) + 1700); i != 0; --i) {
|
||||
GenerateTerrain(0, flag);
|
||||
}
|
||||
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
|
||||
|
||||
flag ^= 2;
|
||||
|
||||
for (i = ScaleByMapSize((Random() & 0x7F) + 410); i != 0; --i) {
|
||||
GenerateTerrain(3, flag);
|
||||
}
|
||||
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
|
||||
break;
|
||||
|
||||
default:
|
||||
SetGeneratingWorldProgress(GWP_LANDSCAPE, 1);
|
||||
|
||||
i = ScaleByMapSize((Random() & 0x7F) + (3 - _opt.diff.quantity_sea_lakes) * 256 + 100);
|
||||
for (; i != 0; --i) {
|
||||
GenerateTerrain(_opt.diff.terrain_type, 0);
|
||||
}
|
||||
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ConvertGroundTilesIntoWaterTiles();
|
||||
|
||||
if (_opt.landscape == LT_DESERT) CreateDesertOrRainForest();
|
||||
}
|
||||
|
||||
void OnTick_Town(void);
|
||||
void OnTick_Trees(void);
|
||||
void OnTick_Station(void);
|
||||
void OnTick_Industry(void);
|
||||
|
||||
void OnTick_Players(void);
|
||||
void OnTick_Train(void);
|
||||
|
||||
void CallLandscapeTick(void)
|
||||
{
|
||||
OnTick_Town();
|
||||
OnTick_Trees();
|
||||
OnTick_Station();
|
||||
OnTick_Industry();
|
||||
|
||||
OnTick_Players();
|
||||
OnTick_Train();
|
||||
}
|
||||
|
||||
TileIndex AdjustTileCoordRandomly(TileIndex a, byte rng)
|
||||
{
|
||||
int rn = rng;
|
||||
uint32 r = Random();
|
||||
|
||||
return TILE_MASK(TileXY(
|
||||
TileX(a) + (GB(r, 0, 8) * rn * 2 >> 8) - rn,
|
||||
TileY(a) + (GB(r, 8, 8) * rn * 2 >> 8) - rn
|
||||
));
|
||||
}
|
||||
|
||||
bool IsValidTile(TileIndex tile)
|
||||
{
|
||||
return (tile < MapSizeX() * MapMaxY() && TileX(tile) != MapMaxX());
|
||||
}
|
||||
3116
src/lang/american.txt
Normal file
3116
src/lang/american.txt
Normal file
File diff suppressed because it is too large
Load Diff
3109
src/lang/brazilian_portuguese.txt
Normal file
3109
src/lang/brazilian_portuguese.txt
Normal file
File diff suppressed because it is too large
Load Diff
3068
src/lang/bulgarian.txt
Normal file
3068
src/lang/bulgarian.txt
Normal file
File diff suppressed because it is too large
Load Diff
3116
src/lang/catalan.txt
Normal file
3116
src/lang/catalan.txt
Normal file
File diff suppressed because it is too large
Load Diff
3174
src/lang/czech.txt
Normal file
3174
src/lang/czech.txt
Normal file
File diff suppressed because it is too large
Load Diff
3116
src/lang/danish.txt
Normal file
3116
src/lang/danish.txt
Normal file
File diff suppressed because it is too large
Load Diff
3116
src/lang/dutch.txt
Normal file
3116
src/lang/dutch.txt
Normal file
File diff suppressed because it is too large
Load Diff
3116
src/lang/english.txt
Normal file
3116
src/lang/english.txt
Normal file
File diff suppressed because it is too large
Load Diff
3116
src/lang/esperanto.txt
Normal file
3116
src/lang/esperanto.txt
Normal file
File diff suppressed because it is too large
Load Diff
3216
src/lang/estonian.txt
Normal file
3216
src/lang/estonian.txt
Normal file
File diff suppressed because it is too large
Load Diff
3111
src/lang/finnish.txt
Normal file
3111
src/lang/finnish.txt
Normal file
File diff suppressed because it is too large
Load Diff
3117
src/lang/french.txt
Normal file
3117
src/lang/french.txt
Normal file
File diff suppressed because it is too large
Load Diff
2985
src/lang/galician.txt
Normal file
2985
src/lang/galician.txt
Normal file
File diff suppressed because it is too large
Load Diff
3117
src/lang/german.txt
Normal file
3117
src/lang/german.txt
Normal file
File diff suppressed because it is too large
Load Diff
3217
src/lang/hungarian.txt
Normal file
3217
src/lang/hungarian.txt
Normal file
File diff suppressed because it is too large
Load Diff
2900
src/lang/icelandic.txt
Normal file
2900
src/lang/icelandic.txt
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user