diff options
author | Darkvater <Darkvater@openttd.org> | 2005-05-12 23:46:01 +0000 |
---|---|---|
committer | Darkvater <Darkvater@openttd.org> | 2005-05-12 23:46:01 +0000 |
commit | ddd4958164980954577fcca388ffeb455f3fc463 (patch) | |
tree | dde69a1afcdeffecc49a408ef9164ad83781a82f | |
parent | 0727085b3b4f1fde705f307274ccf4d785a20854 (diff) | |
download | openttd-ddd4958164980954577fcca388ffeb455f3fc463.tar.xz |
(svn r2300) - CodeChange: check the last number of commands, now only the refit ones remain, and some server-only commands.
- CodeChange: remove cmd-misuses CmdStartScenario() and CmdDestroyCompanyHQ()
- Fix (invisible): when parameter checking CmdRestoreOrderIndex() the vehicle did not have its orders yet, so it would fail. So move doing this until AFTER the orders have been added back in RestoreVehicleOrders()
-rw-r--r-- | clear_cmd.c | 24 | ||||
-rw-r--r-- | command.c | 32 | ||||
-rw-r--r-- | command.h | 11 | ||||
-rw-r--r-- | functions.h | 1 | ||||
-rw-r--r-- | intro_gui.c | 14 | ||||
-rw-r--r-- | misc_cmd.c | 27 | ||||
-rw-r--r-- | misc_gui.c | 2 | ||||
-rw-r--r-- | network_gui.c | 2 | ||||
-rw-r--r-- | order.h | 2 | ||||
-rw-r--r-- | order_cmd.c | 63 | ||||
-rw-r--r-- | players.c | 110 | ||||
-rw-r--r-- | rail_cmd.c | 76 | ||||
-rw-r--r-- | settings_gui.c | 14 | ||||
-rw-r--r-- | unmovable_cmd.c | 186 | ||||
-rw-r--r-- | vehicle.c | 29 | ||||
-rw-r--r-- | vehicle.h | 2 | ||||
-rw-r--r-- | water_cmd.c | 61 |
17 files changed, 353 insertions, 303 deletions
diff --git a/clear_cmd.c b/clear_cmd.c index a854b3985..9dc30bd8e 100644 --- a/clear_cmd.c +++ b/clear_cmd.c @@ -322,18 +322,21 @@ int32 CmdTerraformLand(int x, int y, uint32 flags, uint32 p1, uint32 p2) } -/* - * p1 - start +/** Levels a selected (rectangle) area of land + * @param x,y end tile of area-drag + * @param p1 start tile of area drag + * @param p2 unused */ - int32 CmdLevelLand(int ex, int ey, uint32 flags, uint32 p1, uint32 p2) { int size_x, size_y; int sx, sy; uint h, curh; - uint tile; + TileIndex tile; int32 ret, cost, money; + if (p1 > MapSize()) return CMD_ERROR; + SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); // remember level height @@ -354,11 +357,11 @@ int32 CmdLevelLand(int ex, int ey, uint32 flags, uint32 p1, uint32 p2) money = GetAvailableMoneyForCommand(); cost = 0; - BEGIN_TILE_LOOP(tile2, size_x, size_y, tile) + BEGIN_TILE_LOOP(tile2, size_x, size_y, tile) { curh = TileHeight(tile2); while (curh != h) { - ret = DoCommandByTile(tile2, 8, (curh > h)?0:1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND); - if (ret == CMD_ERROR) break; + ret = DoCommandByTile(tile2, 8, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND); + if (CmdFailed(ret)) break; cost += ret; if (flags & DC_EXEC) { @@ -366,15 +369,14 @@ int32 CmdLevelLand(int ex, int ey, uint32 flags, uint32 p1, uint32 p2) _additional_cash_required = ret; return cost - ret; } - DoCommandByTile(tile2, 8, (curh > h)?0:1, flags, CMD_TERRAFORM_LAND); + DoCommandByTile(tile2, 8, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND); } curh += (curh > h) ? -1 : 1; } - END_TILE_LOOP(tile2, size_x, size_y, tile) + } END_TILE_LOOP(tile2, size_x, size_y, tile) - if (cost == 0) return CMD_ERROR; - return cost; + return (cost == 0) ? CMD_ERROR : cost; } /** Purchase a land area. Actually you only purchase one tile, so @@ -77,7 +77,6 @@ DEF_COMMAND(CmdRestoreOrderIndex); DEF_COMMAND(CmdBuildIndustry); DEF_COMMAND(CmdBuildCompanyHQ); -DEF_COMMAND(CmdDestroyCompanyHQ); DEF_COMMAND(CmdSetPlayerFace); DEF_COMMAND(CmdSetPlayerColor); @@ -149,8 +148,6 @@ DEF_COMMAND(CmdLevelLand); DEF_COMMAND(CmdRefitRailVehicle); -DEF_COMMAND(CmdStartScenario); - DEF_COMMAND(CmdBuildSignalTrack); DEF_COMMAND(CmdRemoveSignalTrack); @@ -232,7 +229,7 @@ static CommandProc * const _command_proc_table[] = { CmdBuildAircraft, /* 61 */ CmdSendAircraftToHangar, /* 62 */ CmdChangeAircraftServiceInt, /* 63 */ - CmdRefitAircraft, /* 64 <-- Hackykid */ + CmdRefitAircraft, /* 64 <-- REFIT: Hackykid */ CmdPlaceSign, /* 65 */ CmdRenameSign, /* 66 */ @@ -250,7 +247,7 @@ static CommandProc * const _command_proc_table[] = { CmdSellShareInCompany, /* 75 */ CmdBuyCompany, /* 76 */ - CmdBuildTown, /* 77 <-- offline / scenario only */ + CmdBuildTown, /* 77 <-- offline */ NULL, /* 78 */ NULL, /* 79 */ CmdRenameTown, /* 80 <-- TODO: check/enforce by server */ @@ -266,7 +263,7 @@ static CommandProc * const _command_proc_table[] = { CmdBuildShip, /* 88 */ CmdSendShipToDepot, /* 89 */ CmdChangeShipServiceInt, /* 90 */ - CmdRefitShip, /* 91 <-- Hackykid */ + CmdRefitShip, /* 91 <-- REFIT: Hackykid */ NULL, /* 92 */ NULL, /* 93 */ @@ -274,30 +271,29 @@ static CommandProc * const _command_proc_table[] = { NULL, /* 95 */ NULL, /* 96 */ NULL, /* 97 */ - NULL, /* 98 */ CmdCloneOrder, /* 99 */ CmdClearArea, /* 100 */ NULL, /* 101 */ - /***************************************************/ - CmdMoneyCheat, /* 102 <-- offline only */ + + CmdMoneyCheat, /* 102 <-- offline (debug) */ CmdBuildCanal, /* 103 */ - CmdPlayerCtrl, /* 104 <-- TODO: check/enforce by server */ + CmdPlayerCtrl, /* 104 */ - CmdLevelLand, /* 105 <-- Hackykid */ + CmdLevelLand, /* 105 */ - CmdRefitRailVehicle, /* 106 <-- Hackykid */ + CmdRefitRailVehicle, /* 106 <-- REFIT: Hackykid */ CmdRestoreOrderIndex, /* 107 */ - CmdBuildLock, /* 108 <-- Hackykid */ - CmdStartScenario, /* 109 <-- UNNEEDED */ - CmdBuildSignalTrack, /* 110 <-- Hackykid */ - CmdRemoveSignalTrack, /* 111 <-- Hackykid */ - CmdDestroyCompanyHQ, /* 112 */ + CmdBuildLock, /* 108 */ + NULL, /* 109 */ + CmdBuildSignalTrack, /* 110 */ + CmdRemoveSignalTrack, /* 111 */ + NULL, /* 112 */ CmdGiveMoney, /* 113 */ CmdChangePatchSetting, /* 114 <-- TODO: check/enforce by server */ - CmdReplaceVehicle, /* 115 <-- Hackykid */ + CmdReplaceVehicle, /* 115 */ }; /* This function range-checks a cmd, and checks if the cmd is not NULL */ @@ -104,8 +104,6 @@ enum { CMD_DO_TOWN_ACTION = 81, CMD_SET_ROAD_DRIVE_SIDE = 82, - CMD_SET_TOWN_NAME_TYPE = 83, - CMD_CHANGE_DIFFICULTY_LEVEL = 85, @@ -116,13 +114,6 @@ enum { CMD_CHANGE_SHIP_SERVICE_INT = 90, CMD_REFIT_SHIP = 91, - CMD_START_NEW_GAME = 92, - CMD_CREATE_SCENARIO = 94, - - CMD_SET_NEW_LANDSCAPE_TYPE = 97, - - CMD_GEN_RANDOM_NEW_GAME = 98, - CMD_CLONE_ORDER = 99, CMD_CLEAR_AREA = 100, @@ -136,11 +127,9 @@ enum { CMD_RESTORE_ORDER_INDEX = 107, CMD_BUILD_LOCK = 108, - CMD_START_SCENARIO = 109, CMD_BUILD_SIGNAL_TRACK = 110, CMD_REMOVE_SIGNAL_TRACK = 111, - CMD_DESTROY_COMPANY_HQ = 112, CMD_GIVE_MONEY = 113, CMD_CHANGE_PATCH_SETTING = 114, diff --git a/functions.h b/functions.h index 4d59d7cd9..032e3c94f 100644 --- a/functions.h +++ b/functions.h @@ -229,6 +229,7 @@ int SaveOrLoad(const char *filename, int mode); void AfterLoadTown(void); void GenRandomNewGame(uint32 rnd1, uint32 rnd2); +void StartScenarioEditor(uint32 rnd1, uint32 rnd2); void AskExitGame(void); void AskExitToGameMenu(void); diff --git a/intro_gui.c b/intro_gui.c index 07d26fcaa..1725d400d 100644 --- a/intro_gui.c +++ b/intro_gui.c @@ -137,22 +137,14 @@ void GenRandomNewGame(uint32 rnd1, uint32 rnd2) SwitchMode(SM_NEWGAME); } -int32 CmdStartScenario(int x, int y, uint32 flags, uint32 p1, uint32 p2) +void StartScenarioEditor(uint32 rnd1, uint32 rnd2) { - if (!(flags & DC_EXEC)) - return 0; - - // this forces stuff into test mode. - _docommand_recursive = 0; - - _random_seeds[0][0] = p1; - _random_seeds[0][1] = p2; + _random_seeds[0][0] = rnd1; + _random_seeds[0][1] = rnd2; SwitchMode(SM_START_SCENARIO); - return 0; } - static const Widget _ask_abandon_game_widgets[] = { { WWT_TEXTBTN, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_NULL}, { WWT_CAPTION, RESIZE_NONE, 4, 11, 179, 0, 13, STR_00C7_QUIT, STR_NULL}, diff --git a/misc_cmd.c b/misc_cmd.c index ea7a6ecb6..469f9d862 100644 --- a/misc_cmd.c +++ b/misc_cmd.c @@ -191,31 +191,42 @@ int32 CmdPause(int x, int y, uint32 flags, uint32 p1, uint32 p2) return 0; } - +/** Change the financial flow of your company. + * This is normally only enabled in offline mode, but if there is a debug + * build, you can cheat (to test). + * @param x,y unused + * @param p1 the amount of money to receive (if negative), or spend (if positive) + * @param p2 unused + */ int32 CmdMoneyCheat(int x, int y, uint32 flags, uint32 p1, uint32 p2) { +#ifndef _DEBUG + if (_networking) return CMD_ERROR; +#endif SET_EXPENSES_TYPE(EXPENSES_OTHER); return (int32)p1; } +/** Transfer funds (money) from one player to another. + * @param x,y unused + * @param p1 the amount of money to transfer; max 16.000.000 + * @param p2 the player to transfer the money to + */ int32 CmdGiveMoney(int x, int y, uint32 flags, uint32 p1, uint32 p2) { SET_EXPENSES_TYPE(EXPENSES_OTHER); - p1 = clamp(p1, 0, 0xFFFFFF); // Clamp between 16 million and 0 - - if (p1 == 0) - return CMD_ERROR; + if (!_networking || (int32)p1 <= 0 || p2 >= MAX_PLAYERS) return CMD_ERROR; if (flags & DC_EXEC) { - // Add money to player - byte old_cp = _current_player; + /* Add money to player (cast to signed to prevent 'stealing' money) */ + PlayerID old_cp = _current_player; _current_player = p2; SubtractMoneyFromPlayer(-(int32)p1); _current_player = old_cp; } - // Subtract money from local-player + /* Subtract money from local-player (cast to signed to prevent 'stealing' money) */ return (int32)p1; } diff --git a/misc_gui.c b/misc_gui.c index 20b18a0ae..39def21c3 100644 --- a/misc_gui.c +++ b/misc_gui.c @@ -1496,7 +1496,7 @@ static void SelectScenarioWndProc(Window *w, WindowEvent *e) { SetFiosType(file->type); strcpy(_file_to_saveload.name, name); DeleteWindow(w); - DoCommandP(0, Random(), InteractiveRandom(), NULL, CMD_START_SCENARIO); + StartScenarioEditor(Random(), InteractiveRandom()); } } break; diff --git a/network_gui.c b/network_gui.c index 028ef770c..833ab72d0 100644 --- a/network_gui.c +++ b/network_gui.c @@ -572,7 +572,7 @@ static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e) strcpy(_file_to_saveload.name, name); snprintf(_network_game_info.map_name, sizeof(_network_game_info.map_name), "Loaded scenario"); DeleteWindow(w); - DoCommandP(0, Random(), InteractiveRandom(), NULL, CMD_START_SCENARIO); + StartScenarioEditor(Random(), InteractiveRandom()); } } break; @@ -141,7 +141,7 @@ static inline Order UnpackOrder(uint32 packed) } /* Functions */ -void BackupVehicleOrders(Vehicle *v, BackuppedOrders *order); +void BackupVehicleOrders(const Vehicle *v, BackuppedOrders *order); void RestoreVehicleOrders(Vehicle *v, BackuppedOrders *order); void DeleteDestinationFromVehicleOrder(Order dest); void InvalidateVehicleOrder(const Vehicle *v); diff --git a/order_cmd.c b/order_cmd.c index fb4178492..1a214a302 100644 --- a/order_cmd.c +++ b/order_cmd.c @@ -697,6 +697,7 @@ int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) } break; case CO_UNSHARE: return DecloneOrder(dst, flags); + default: return CMD_ERROR; } return 0; @@ -708,10 +709,8 @@ int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) * without loosing the order-list * */ -void BackupVehicleOrders(Vehicle *v, BackuppedOrders *bak) +void BackupVehicleOrders(const Vehicle *v, BackuppedOrders *bak) { - bool shared = IsOrderListShared(v); - /* Save general info */ bak->orderindex = v->cur_order_index; bak->service_interval = v->service_interval; @@ -724,19 +723,15 @@ void BackupVehicleOrders(Vehicle *v, BackuppedOrders *bak) } /* If we have shared orders, store it on a special way */ - if (shared) { - Vehicle *u; - if (v->next_shared) - u = v->next_shared; - else - u = v->prev_shared; + if (IsOrderListShared(v)) { + const Vehicle *u = (v->next_shared) ? v->next_shared : v->prev_shared; bak->clone = u->index; } else { /* Else copy the orders */ Order *order, *dest; - dest = bak->order; + dest = bak->order; /* We do not have shared orders */ bak->clone = INVALID_VEHICLE; @@ -759,7 +754,7 @@ void BackupVehicleOrders(Vehicle *v, BackuppedOrders *bak) */ void RestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak) { - int i; + uint i; /* If we have a custom name, process that */ if (bak->name[0] != 0) { @@ -767,9 +762,6 @@ void RestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak) DoCommandP(0, v->index, 0, NULL, CMD_NAME_VEHICLE); } - /* Restore vehicle number and service interval */ - DoCommandP(0, v->index, bak->orderindex | (bak->service_interval << 16) , NULL, CMD_RESTORE_ORDER_INDEX); - /* If we had shared orders, recover that */ if (bak->clone != INVALID_VEHICLE) { DoCommandP(0, v->index | (bak->clone << 16), 0, NULL, CMD_CLONE_ORDER); @@ -780,31 +772,44 @@ void RestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak) order number is one more than the current amount of orders, and because in network the commands are queued before send, the second insert always fails in test mode. By bypassing the test-mode, that no longer is a problem. */ - for (i = 0; bak->order[i].type != OT_NOTHING; i++) + for (i = 0; bak->order[i].type != OT_NOTHING; i++) { if (!DoCommandP(0, v->index + (i << 16), PackOrder(&bak->order[i]), NULL, CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) break; + } + + /* Restore vehicle order-index and service interval */ + DoCommandP(0, v->index, bak->orderindex | (bak->service_interval << 16) , NULL, CMD_RESTORE_ORDER_INDEX); } -/** - * - * Restore the current-order-index of a vehicle and sets service-interval - * - * @param vehicle_id The ID of the vehicle - * @param data First 16 bits are the current-order-index - * The last 16 bits are the service-interval - * +/** Restore the current order-index of a vehicle and sets service-interval. + * @param x,y unused + * @param p1 the ID of the vehicle + * @param p2 various bistuffed elements + * - p2 = (bit 0-15) - current order-index (p2 & 0xFFFF) + * - p2 = (bit 16-31) - service interval (p2 >> 16) + * @todo Unfortunately you cannot safely restore the unitnumber or the old vehicle + * as far as I can see. We can store it in BackuppedOrders, and restore it, but + * but we have no way of seeing it has been tampered with or not, as we have no + * legit way of knowing what that ID was.@n + * If we do want to backup/restore it, just add UnitID uid to BackuppedOrders, and + * restore it as parameter 'y' (ugly hack I know) for example. "v->unitnumber = y;" */ -int32 CmdRestoreOrderIndex(int x, int y, uint32 flags, uint32 vehicle_id, uint32 data) +int32 CmdRestoreOrderIndex(int x, int y, uint32 flags, uint32 p1, uint32 p2) { - Vehicle* v; + Vehicle *v; + OrderID cur_ord = p2 & 0xFFFF; + uint16 serv_int = p2 >> 16; + + if (!IsVehicleIndex(p1)) return CMD_ERROR; - if (!IsVehicleIndex(vehicle_id)) return CMD_ERROR; - v = GetVehicle(vehicle_id); + v = GetVehicle(p1); + /* Check the vehicle type and ownership, and if the service interval and order are in range */ if (v->type == 0 || !CheckOwnership(v->owner)) return CMD_ERROR; + if (serv_int != GetServiceIntervalClamped(serv_int) || cur_ord >= v->num_orders) return CMD_ERROR; if (flags & DC_EXEC) { - v->service_interval = data >> 16; - v->cur_order_index = data & 0xFFFF; + v->cur_order_index = cur_ord; + v->service_interval = serv_int; } return 0; @@ -1,3 +1,6 @@ +/** @file, + * sjdlfkasjdf + */ #include "stdafx.h" #include "ttd.h" #include "string.h" @@ -217,7 +220,7 @@ static void SubtractMoneyFromAnyPlayer(Player *p, int32 cost) void SubtractMoneyFromPlayer(int32 cost) { - uint pid = _current_player; + PlayerID pid = _current_player; if (pid < MAX_PLAYERS) SubtractMoneyFromAnyPlayer(DEREF_PLAYER(pid), cost); } @@ -630,23 +633,36 @@ static void DeletePlayerStuff(int pi) p->president_name_1 = 0; } -// functionality. -// 0 - make new player -// 1 - make new AI player -// 2 - delete player (p2) -// 3 - join player (p1 >> 8) & 0xFF with (p1 >> 16) & 0xFF +/** Control the players: add, delete, etc. + * @param x,y unused + * @param p1 various functionality + * - p1 = 0 - create a new player, Which player (network) it will be is in p2 + * - p1 = 1 - create a new AI player + * - p1 = 2 - delete a player. Player is identified by p2 + * - p1 = 3 - merge two companies together. Player to merge #1 with player #2. Identified by p2 + * @param p2 various functionality, dictated by p1 + * - p1 = 0 - ClientID of the newly created player + * - p1 = 2 - PlayerID of the that is getting deleted + * - p1 = 3 - #1 p2 = (bit 0-15) - player to merge (p2 & 0xFFFF) + * - #2 p2 = (bit 16-31) - player to be merged into ((p2>>16)&0xFFFF) + * @todo In the case of p1=0, create new player, the clientID of the new player is in parameter + * p2. This parameter is passed in at function DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) + * on the server itself. First of all this is unbelievably ugly; second of all, well, + * it IS ugly! <b>Someone fix this up :)</b> So where to fix?@n + * @arg - network_server.c:838 DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND)@n + * @arg - network_client.c:536 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP) from where the map has been received + */ int32 CmdPlayerCtrl(int x, int y, uint32 flags, uint32 p1, uint32 p2) { - int pi; - Player *p; + if (flags & DC_EXEC) _current_player = OWNER_NONE; - if (!(flags & DC_EXEC)) - return 0; + switch (p1) { + case 0: { /* Create a new player */ + Player *p; + PlayerID pid = p2; - _current_player = OWNER_NONE; + if (!(flags & DC_EXEC) || pid >= MAX_PLAYERS) return 0; - switch(p1 & 0xff) { - case 0: // make new player p = DoStartupNewPlayer(false); #ifdef ENABLE_NETWORK @@ -665,9 +681,9 @@ int32 CmdPlayerCtrl(int x, int y, uint32 flags, uint32 p1, uint32 p2) } #ifdef ENABLE_NETWORK if (_network_server) { - NetworkClientInfo *ci; - // UGLY! p2 is mis-used to fetch the client-id - ci = &_network_client_info[p2]; + /* XXX - UGLY! p2 (pid) is mis-used to fetch the client-id, done at server-side + * in network_server.c:838, function DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) */ + NetworkClientInfo *ci = &_network_client_info[pid]; ci->client_playas = p->index + 1; NetworkUpdateClientInfo(ci->client_index); @@ -690,47 +706,61 @@ int32 CmdPlayerCtrl(int x, int y, uint32 flags, uint32 p1, uint32 p2) _local_player = player_backup; } } - } else { - if (_network_server) { - NetworkClientInfo *ci; - // UGLY! p2 is mis-used to fetch the client-id - ci = &_network_client_info[p2]; - ci->client_playas = OWNER_SPECTATOR; - NetworkUpdateClientInfo(ci->client_index); - } -#endif /* ENABLE_NETWORK */ + } else if (_network_server) { + /* XXX - UGLY! p2 (pid) is mis-used to fetch the client-id, done at server-side + * in network_server.c:838, function DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) */ + NetworkClientInfo *ci = &_network_client_info[pid]; + ci->client_playas = OWNER_SPECTATOR; + NetworkUpdateClientInfo(ci->client_index); } - break; - case 1: // make new ai player +#endif /* ENABLE_NETWORK */ + } break; + + case 1: /* Make a new AI player */ + if (!(flags & DC_EXEC)) return 0; + DoStartupNewPlayer(true); break; - case 2: // delete player + + case 2: { /* Delete a player */ + Player *p; + + if (p2 >= MAX_PLAYERS) return CMD_ERROR; + + if (!(flags & DC_EXEC)) return 0; + p = DEREF_PLAYER(p2); /* Only allow removal of HUMAN companies */ - if (IS_HUMAN_PLAYER(p2)) { + if (IS_HUMAN_PLAYER(p->index)) { /* Delete any open window of the company */ - DeletePlayerWindows(p2); + DeletePlayerWindows(p->index); /* Show the bankrupt news */ SetDParam(0, p->name_1); SetDParam(1, p->name_2); - AddNewsItem( (StringID)(p2 + 16*3), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0); + AddNewsItem( (StringID)(p->index + 16*3), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0); /* Remove the company */ - ChangeOwnershipOfPlayerItems(p2, 255); - p->money64 = p->player_money = 100000000; + ChangeOwnershipOfPlayerItems(p->index, OWNER_SPECTATOR); + p->money64 = p->player_money = 100000000; // XXX - wtf? p->is_active = false; } - break; + } break; - case 3: // join player - pi = (byte)(p1 >> 8); - ChangeOwnershipOfPlayerItems(pi, (byte)(p1 >> 16)); - DeletePlayerStuff(pi); - break; - } + case 3: { /* Merge a company (#1) into another company (#2), elimination company #1 */ + PlayerID pid_old = p2 & 0xFFFF; + PlayerID pid_new = (p2 >> 16) & 0xFFFF; + + if (pid_old >= MAX_PLAYERS || pid_new >= MAX_PLAYERS) return CMD_ERROR; + if (!(flags & DC_EXEC)) return CMD_ERROR; + + ChangeOwnershipOfPlayerItems(pid_old, pid_new); + DeletePlayerStuff(pid_old); + } break; + default: return CMD_ERROR; + } return 0; } diff --git a/rail_cmd.c b/rail_cmd.c index 624a1d9fe..d88590c9e 100644 --- a/rail_cmd.c +++ b/rail_cmd.c @@ -555,8 +555,8 @@ static int32 ValidateAutoDrag(byte *railbit, int x, int y, int ex, int ey) // validate the direction while (((trdx <= 0) && (dx > 0)) || ((trdx >= 0) && (dx < 0)) || ((trdy <= 0) && (dy > 0)) || ((trdy >= 0) && (dy < 0))) { - if (*railbit < 8) { // first direction is invalid, try the other - SETBIT(*railbit, 3); + if (!HASBIT(*railbit, 3)) { // first direction is invalid, try the other + SETBIT(*railbit, 3); // reverse the direction trdx = -trdx; trdy = -trdy; } else // other direction is invalid too, invalid drag @@ -625,11 +625,19 @@ static int32 CmdRailTrackHelper(int x, int y, uint32 flags, uint32 p1, uint32 p2 return (total_cost == 0) ? CMD_ERROR : total_cost; } +/** Build rail on a stretch of track. + * Stub for the unified rail builder/remover + * @see CmdRailTrackHelper + */ int32 CmdBuildRailroadTrack(int x, int y, uint32 flags, uint32 p1, uint32 p2) { return CmdRailTrackHelper(x, y, flags, p1, CLRBIT(p2, 7)); } +/** Build rail on a stretch of track. + * Stub for the unified rail builder/remover + * @see CmdRailTrackHelper + */ int32 CmdRemoveRailroadTrack(int x, int y, uint32 flags, uint32 p1, uint32 p2) { return CmdRailTrackHelper(x, y, flags, p1, SETBIT(p2, 7)); @@ -813,36 +821,42 @@ int32 CmdBuildSingleSignal(int x, int y, uint32 flags, uint32 p1, uint32 p2) return cost; } -/* Build many signals by dragging: AutoSignals - * x,y = start tile - * p1 = end tile - * p2 = (bit 0) - 0 = build, 1 = remove signals - * p2 = (bit 3) - 0 = signals, 1 = semaphores - * p2 = (bit 4-6) - track-orientation, valid values: 0-5 - * p2 = (bit 24-31) - user defined signals_density +/** Build many signals by dragging; AutoSignals + * @param x,y start tile of drag + * @param p1 end tile of drag + * @param p2 various bitstuffed elements + * - p2 = (bit 0) - 0 = build, 1 = remove signals + * - p2 = (bit 3) - 0 = signals, 1 = semaphores + * - p2 = (bit 4- 6) - track-orientation, valid values: 0-5 + * - p2 = (bit 24-31) - user defined signals_density */ static int32 CmdSignalTrackHelper(int x, int y, uint32 flags, uint32 p1, uint32 p2) { int ex, ey; - byte railbit = (p2 >> 4) & 7; - bool error = true; - TileIndex tile = TILE_FROM_XY(x, y); int32 ret, total_cost, signal_ctr; - byte m5, semaphores = (HASBIT(p2, 3)) ? 8 : 0; + byte m5, signals; + TileIndex tile = TILE_FROM_XY(x, y); + bool error = true; + int mode = p2 & 0x1; - // for vertical/horizontal tracks, double the given signals density - // since the original amount will be too dense (shorter tracks) - byte signal_density = (railbit & 0x6) ? (p2 >> 24) * 2: (p2 >> 24); - byte signals; + byte semaphores = (HASBIT(p2, 3)) ? 8 : 0; + byte railbit = (p2 >> 4) & 0x7; + byte signal_density = (p2 >> 24); + + if (p1 > MapSize()) return CMD_ERROR; + if (signal_density == 0 || signal_density > 20) return CMD_ERROR; SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); + /* for vertical/horizontal tracks, double the given signals density + * since the original amount will be too dense (shorter tracks) */ + if (railbit & 0x6) signal_density *= 2; + // unpack end tile ex = TileX(p1) * 16; ey = TileY(p1) * 16; - if (ValidateAutoDrag(&railbit, x, y, ex, ey) == CMD_ERROR) - return CMD_ERROR; + if (CmdFailed(ValidateAutoDrag(&railbit, x, y, ex, ey))) return CMD_ERROR; // copy the signal-style of the first rail-piece if existing m5 = _map5[tile]; @@ -851,9 +865,8 @@ static int32 CmdSignalTrackHelper(int x, int y, uint32 flags, uint32 p1, uint32 if (signals == 0) signals = _signals_table_both[railbit]; semaphores = (_map3_hi[tile] & ~3) ? 8 : 0; // copy signal/semaphores style (independent of CTRL) - } else { // no signals exist, drag a two-way signal stretch + } else // no signals exist, drag a two-way signal stretch signals = _signals_table_both[railbit]; - } /* signal_density_ctr - amount of tiles already processed * signals_density - patch setting to put signal on every Nth tile (double space on |, -- tracks) @@ -864,25 +877,22 @@ static int32 CmdSignalTrackHelper(int x, int y, uint32 flags, uint32 p1, uint32 and convert all others to semaphore/signal * mode - 1 remove signals, 0 build signals */ signal_ctr = total_cost = 0; - for(;;) { + for (;;) { // only build/remove signals with the specified density if ((signal_ctr % signal_density) == 0 ) { ret = DoCommand(x, y, (railbit & 7) | semaphores, signals, flags, (mode == 1) ? CMD_REMOVE_SIGNALS : CMD_BUILD_SIGNALS); /* Abort placement for any other error than NOT_SUITABLE_TRACK * This includes vehicles on track, competitor's tracks, etc. */ - if (ret == CMD_ERROR) { - if (_error_message != STR_1005_NO_SUITABLE_RAILROAD_TRACK && mode != 1) { - return CMD_ERROR; - } + if (CmdFailed(ret)) { + if (_error_message != STR_1005_NO_SUITABLE_RAILROAD_TRACK && mode != 1) return CMD_ERROR; } else { error = false; total_cost += ret; } } - if (ex == x && ey == y) // reached end of drag - break; + if (ex == x && ey == y) break; // reached end of drag x += _railbit.xinc[railbit]; y += _railbit.yinc[railbit]; @@ -895,7 +905,10 @@ static int32 CmdSignalTrackHelper(int x, int y, uint32 flags, uint32 p1, uint32 return (error) ? CMD_ERROR : total_cost; } -/* Stub for the unified Signal builder/remover */ +/** Build signals on a stretch of track. + * Stub for the unified signal builder/remover + * @see CmdSignalTrackHelper + */ int32 CmdBuildSignalTrack(int x, int y, uint32 flags, uint32 p1, uint32 p2) { return CmdSignalTrackHelper(x, y, flags, p1, p2); @@ -941,7 +954,10 @@ int32 CmdRemoveSingleSignal(int x, int y, uint32 flags, uint32 p1, uint32 p2) return _price.remove_signals; } -/* Stub for the unified Signal builder/remover */ +/** Remove signals on a stretch of track. + * Stub for the unified signal builder/remover + * @see CmdSignalTrackHelper + */ int32 CmdRemoveSignalTrack(int x, int y, uint32 flags, uint32 p1, uint32 p2) { return CmdSignalTrackHelper(x, y, flags, p1, SETBIT(p2, 0)); diff --git a/settings_gui.c b/settings_gui.c index f6af41354..1d4241aad 100644 --- a/settings_gui.c +++ b/settings_gui.c @@ -1017,19 +1017,21 @@ static void PatchesSelectionWndProc(Window *w, WindowEvent *e) } } -/** - * Network-safe changing of patch-settings. - * @param p1 bytes 0 - 7: the patches type (page) that is being changed (construction, network, ai) - * @param p1 bytes 8 - ..: the actual patch (entry) being set inside the category +/** Network-safe changing of patch-settings. + * @param p1 various bitstuffed elements + * - p1 = (bit 0- 7) - the patches type (page) that is being changed (construction, network, ai) (p1 & 0xFF) + * - p2 = (bit 8-15) - the actual patch (entry) being set inside the category ((p1>>8) & 0xFF) * @param p2 the new value for the patch + * @todo check that the new value is a valid one. Awful lot of work, but since only + * the server is allowed to do this, we trust it on this one :) */ int32 CmdChangePatchSetting(int x, int y, uint32 flags, uint32 p1, uint32 p2) { byte pcat = p1 & 0xFF; byte pel = (p1 >> 8) & 0xFF; - if (pcat >= lengthof(_patches_page)) return 0; - if (pel >= _patches_page[pcat].num) return 0; + if (pcat >= lengthof(_patches_page)) return CMD_ERROR; + if (pel >= _patches_page[pcat].num) return CMD_ERROR; if (flags & DC_EXEC) { const PatchEntry *pe = &_patches_page[pcat].entries[pel]; diff --git a/unmovable_cmd.c b/unmovable_cmd.c index b492febd6..5a0c5345b 100644 --- a/unmovable_cmd.c +++ b/unmovable_cmd.c @@ -12,6 +12,94 @@ #include "town.h" #include "sprite.h" +/** Destroy a HQ. + * During normal gameplay you can only implicitely destroy a HQ when you are + * rebuilding it. Otherwise, only water can destroy it. + * @param tile tile coordinates where HQ is located to destroy + * @param flags docommand flags of calling function + */ +int32 DestroyCompanyHQ(TileIndex tile, uint32 flags) +{ + Player *p; + + SET_EXPENSES_TYPE(EXPENSES_PROPERTY); + + /* Find player that has HQ flooded, and reset their location_of_house */ + if (_current_player == OWNER_WATER) { + bool dodelete = false; + + FOR_ALL_PLAYERS(p) { + if (p->location_of_house == tile) { + dodelete = true; + break; + } + } + if (!dodelete) return CMD_ERROR; + } else /* Destruction was initiated by player */ + p = DEREF_PLAYER(_current_player); + + if (p->location_of_house == 0) return CMD_ERROR; + + if (flags & DC_EXEC) { + DoClearSquare(p->location_of_house + TILE_XY(0,0)); + DoClearSquare(p->location_of_house + TILE_XY(0,1)); + DoClearSquare(p->location_of_house + TILE_XY(1,0)); + DoClearSquare(p->location_of_house + TILE_XY(1,1)); + p->location_of_house = 0; // reset HQ position + InvalidateWindow(WC_COMPANY, (int)p->index); + } + + // cost of relocating company is 1% of company value + return CalculateCompanyValue(p) / 100; +} + +/** Build or relocate the HQ. This depends if the HQ is already built or not + * @param x,y the coordinates where the HQ will be built or relocated to + * @param p1 relocate HQ (set to some value, usually 1 or true) + * @param p2 unused + */ + extern int32 CheckFlatLandBelow(uint tile, uint w, uint h, uint flags, uint invalid_dirs, int *); +int32 CmdBuildCompanyHQ(int x, int y, uint32 flags, uint32 p1, uint32 p2) +{ + TileIndex tile = TILE_FROM_XY(x,y); + Player *p = DEREF_PLAYER(_current_player); + int cost; + + SET_EXPENSES_TYPE(EXPENSES_PROPERTY); + + cost = CheckFlatLandBelow(tile, 2, 2, flags, 0, NULL); + if (CmdFailed(cost)) return CMD_ERROR; + + if (p1) { /* Moving HQ */ + int32 ret; + + if (p->location_of_house == 0) return CMD_ERROR; + + ret = DestroyCompanyHQ(p->location_of_house, flags); + + if (CmdFailed(ret)) return CMD_ERROR; + + cost += ret; + } else { /* Building new HQ */ + if (p->location_of_house != 0) return CMD_ERROR; + } + + if (flags & DC_EXEC) { + int score = UpdateCompanyRatingAndValue(p, false); + + p->location_of_house = tile; + + ModifyTile(tile + TILE_XY(0,0), MP_SETTYPE(MP_UNMOVABLE) | MP_MAPOWNER_CURRENT | MP_MAP5, 0x80); + ModifyTile(tile + TILE_XY(0,1), MP_SETTYPE(MP_UNMOVABLE) | MP_MAPOWNER_CURRENT | MP_MAP5, 0x81); + ModifyTile(tile + TILE_XY(1,0), MP_SETTYPE(MP_UNMOVABLE) | MP_MAPOWNER_CURRENT | MP_MAP5, 0x82); + ModifyTile(tile + TILE_XY(1,1), MP_SETTYPE(MP_UNMOVABLE) | MP_MAPOWNER_CURRENT | MP_MAP5, 0x83); + UpdatePlayerHouse(p, score); + InvalidateWindow(WC_COMPANY, (int)p->index); + } + + return cost; +} + typedef struct DrawTileUnmovableStruct { uint16 image; byte subcoord_x; @@ -110,8 +198,7 @@ static int32 ClearTile_Unmovable(uint tile, byte flags) byte m5 = _map5[tile]; if (m5 & 0x80) { - if (_current_player == OWNER_WATER) - return DoCommandByTile(tile, 0, 0, DC_EXEC, CMD_DESTROY_COMPANY_HQ); + if (_current_player == OWNER_WATER) return DestroyCompanyHQ(tile, DC_EXEC); return_cmd_error(STR_5804_COMPANY_HEADQUARTERS_IN); } @@ -307,101 +394,6 @@ restart: } while (--i); } -extern int32 CheckFlatLandBelow(uint tile, uint w, uint h, uint flags, uint invalid_dirs, int *); - -/** Build or relocate the HQ. This depends if the HQ is already built or not - * @param x,y the coordinates where the HQ will be built or relocated to - * @param p1 relocate HQ (set to some value, usually 1 or true) - * @param p2 unused - */ -int32 CmdBuildCompanyHQ(int x, int y, uint32 flags, uint32 p1, uint32 p2) -{ - TileIndex tile = TILE_FROM_XY(x,y); - Player *p = DEREF_PLAYER(_current_player); - int cost; - - SET_EXPENSES_TYPE(EXPENSES_PROPERTY); - - cost = CheckFlatLandBelow(tile, 2, 2, flags, 0, NULL); - if (CmdFailed(cost)) return CMD_ERROR; - - if (p1) { /* Moving HQ */ - int32 ret; - - if (p->location_of_house == 0) return CMD_ERROR; - - ret = DoCommandByTile(p->location_of_house, 0, 0, flags, CMD_DESTROY_COMPANY_HQ); - - if (CmdFailed(ret)) return CMD_ERROR; - - cost += ret; - } else { /* Building new HQ */ - if (p->location_of_house != 0) return CMD_ERROR; - } - - if (flags & DC_EXEC) { - int score = UpdateCompanyRatingAndValue(p, false); - - p->location_of_house = tile; - - ModifyTile(tile + TILE_XY(0,0), MP_SETTYPE(MP_UNMOVABLE) | MP_MAPOWNER_CURRENT | MP_MAP5, 0x80); - ModifyTile(tile + TILE_XY(0,1), MP_SETTYPE(MP_UNMOVABLE) | MP_MAPOWNER_CURRENT | MP_MAP5, 0x81); - ModifyTile(tile + TILE_XY(1,0), MP_SETTYPE(MP_UNMOVABLE) | MP_MAPOWNER_CURRENT | MP_MAP5, 0x82); - ModifyTile(tile + TILE_XY(1,1), MP_SETTYPE(MP_UNMOVABLE) | MP_MAPOWNER_CURRENT | MP_MAP5, 0x83); - UpdatePlayerHouse(p, score); - InvalidateWindow(WC_COMPANY, (int)p->index); - } - - return cost; -} - -/** Destroy a HQ. - * During normal gameplay you can only implicitely destroy a HQ when you are - * rebuilding it. Otherwise, only water can destroy it. Unfortunately there is - * no safeguard against a hacked client to call this command, unless we also add - * flags to the command table which commands can be called directly and which not. - * @param x,y tile coordinates where HQ is located to destroy - * @param p1 unused - * @param p2 unused - */ -int32 CmdDestroyCompanyHQ(int x, int y, uint32 flags, uint32 p1, uint32 p2) -{ - TileIndex tile; - Player *p; - - SET_EXPENSES_TYPE(EXPENSES_PROPERTY); - - /* Find player that has HQ flooded, and reset their location_of_house */ - if (_current_player == OWNER_WATER) { - bool dodelete = false; - tile = TILE_FROM_XY(x,y); - - FOR_ALL_PLAYERS(p) { - if (p->location_of_house == tile) { - dodelete = true; - break; - } - } - if (!dodelete) return CMD_ERROR; - } else /* Destruction was initiated by player */ - p = DEREF_PLAYER(_current_player); - - if (p->location_of_house == 0) return CMD_ERROR; - - if (flags & DC_EXEC) { - DoClearSquare(p->location_of_house + TILE_XY(0,0)); - DoClearSquare(p->location_of_house + TILE_XY(0,1)); - DoClearSquare(p->location_of_house + TILE_XY(1,0)); - DoClearSquare(p->location_of_house + TILE_XY(1,1)); - p->location_of_house = 0; // reset HQ position - InvalidateWindow(WC_COMPANY, (int)p->index); - } - - // cost of relocating company is 1% of company value - return CalculateCompanyValue(p) / 100; -} - - static void ChangeTileOwner_Unmovable(uint tile, byte old_player, byte new_player) { if (_map_owner[tile] != old_player) @@ -1306,9 +1306,13 @@ extern int32 CmdRefitRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p extern int32 CmdRefitShip(int x, int y, uint32 flags, uint32 p1, uint32 p2); extern int32 CmdRefitAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2); -/* Replaces a vehicle (used to be called autorenew) - p1 - Index of vehicle - p2 - Type of new engine */ +/** Replaces a vehicle (used to be called autorenew). + * @param x,y unused + * @param p1 index of vehicle being replaced + * @param p2 various bitstuffed elements + * - p2 = (bit 0-15) - new engine type for the vehicle (p2 & 0xFFFF) + * - p2 = (bit 16-31) - money the player wants to have left after replacement counted in 100.000 (100K) (p2 >> 16) + */ int32 CmdReplaceVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) { /* makesvariables to inform about how much money the player wants to have left after replacing @@ -1317,11 +1321,11 @@ int32 CmdReplaceVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) This way the max is 6553 millions and it is more than the 32 bit that is stored in _patches This is a nice way to send 32 bit and only use 16 bit the last 8 bit is the engine. The 8 bits in front of the engine is free so it have room for 16 bit engine entries */ - uint16 new_engine_type = (uint16)(p2 & 0xFFFF); - uint32 autorefit_money = (p2 >> 16) * 100000; + EngineID new_engine_type = (p2 & 0xFFFF); + uint32 autorefit_money = (p2 >> 16) * 100000; Vehicle *v, *u, *first; int cost, build_cost, rear_engine_cost = 0; - byte old_engine_type; + EngineID old_engine_type; if (!IsVehicleIndex(p1)) return CMD_ERROR; @@ -1329,9 +1333,9 @@ int32 CmdReplaceVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) old_engine_type = v->engine_type; - // first we make sure that it's a valid type the user requested - // check that it's an engine that is in the engine array - if (new_engine_type >= TOTAL_NUM_ENGINES ) return CMD_ERROR; + /* First we make sure that it's a valid type the user requested + * check that it's an engine that is in the engine array */ + if (!IsEngineIndex(new_engine_type)) return CMD_ERROR; // check that the new vehicle type is the same as the original one if (v->type != DEREF_ENGINE(new_engine_type)->type) return CMD_ERROR; @@ -1357,8 +1361,7 @@ int32 CmdReplaceVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) /* In a rare situation, when 2 clients are connected to 1 company and have the same settings, a vehicle can be replaced twice.. check if this is the situation here */ - if (old_engine_type == new_engine_type && v->age == 0) - return CMD_ERROR; + if (old_engine_type == new_engine_type && v->age == 0) return CMD_ERROR; if ( v->type == VEH_Train ) { first = GetFirstVehicleInChain(v); @@ -1415,8 +1418,7 @@ int32 CmdReplaceVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (flags & DC_EXEC) { /* We do not really buy a new vehicle, we upgrade the old one */ - Engine *e; - e = DEREF_ENGINE(new_engine_type); + Engine *e = DEREF_ENGINE(new_engine_type); v->reliability = e->reliability; v->reliability_spd_dec = e->reliability_spd_dec; @@ -1427,7 +1429,6 @@ int32 CmdReplaceVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) v->value = build_cost; - if (v->engine_type != new_engine_type) { byte sprite = v->spritenum; byte cargo_type = v->cargo_type; @@ -55,7 +55,7 @@ typedef struct VehicleRail { // NOSAVE: for wagon override - id of the first engine in train // 0xffff == not in train - uint16 first_engine; + EngineID first_engine; byte track; byte force_proceed; diff --git a/water_cmd.c b/water_cmd.c index 55a9e7188..9c14736e2 100644 --- a/water_cmd.c +++ b/water_cmd.c @@ -159,30 +159,44 @@ static void MarkTilesAroundDirty(uint tile) MarkTileDirtyByTile(TILE_ADDXY(tile, -1, 0)); } +/** Builds a lock (ship-lift) + * @param x,y tile coordinates where to place the lock + * @param p1 unused + * @param p2 unused + */ int32 CmdBuildLock(int x, int y, uint32 flags, uint32 p1, uint32 p2) { - uint tile = TILE_FROM_XY(x,y); - int32 ret; - uint th; + TileIndex tile = TILE_FROM_XY(x,y); + uint tileh; SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); - th = GetTileSlope(tile, NULL); + tileh = GetTileSlope(tile, NULL); - if (th==3 || th==6 || th==9 || th==12) { - static const byte _shiplift_dirs[16] = {0,0,0,2,0,0,1,0,0,3,0,0,0}; - ret = DoBuildShiplift(tile, _shiplift_dirs[th], flags); - return ret; - } - else - return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); + if (tileh == 3 || tileh == 6 || tileh == 9 || tileh == 12) { + static const byte _shiplift_dirs[16] = {0, 0, 0, 2, 0, 0, 1, 0, 0, 3, 0, 0, 0}; + return DoBuildShiplift(tile, _shiplift_dirs[tileh], flags); + } + + return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); } +/** Build a piece of canal. + * @param x,y end tile of stretch-dragging + * @param p1 start tile of stretch-dragging + * @param p2 unused + */ int32 CmdBuildCanal(int x, int y, uint32 flags, uint32 p1, uint32 p2) { int32 ret, cost; int size_x, size_y; - int sx = TileX((TileIndex)p1); - int sy = TileY((TileIndex)p1); + int sx, sy; + + if (p1 > MapSize()) return CMD_ERROR; + + sx = TileX(p1); + sy = TileY(p1); + /* x,y are in pixel-coordinates, transform to tile-coordinates + * to be able to use the BEGIN_TILE_LOOP() macro */ x >>= 4; y >>= 4; SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); @@ -192,11 +206,13 @@ int32 CmdBuildCanal(int x, int y, uint32 flags, uint32 p1, uint32 p2) size_x = (x - sx) + 1; size_y = (y - sy) + 1; + /* Outside the editor you can only drag canals, and not areas */ + if (_game_mode != GM_EDITOR && (sx != x && sy != y)) return CMD_ERROR; + cost = 0; BEGIN_TILE_LOOP(tile, size_x, size_y, TILE_XY(sx, sy)) { ret = 0; - if (GetTileSlope(tile, NULL) != 0) - return_cmd_error(STR_0007_FLAT_LAND_REQUIRED); + if (GetTileSlope(tile, NULL) != 0) return_cmd_error(STR_0007_FLAT_LAND_REQUIRED); // can't make water of water! if (IsTileType(tile, MP_WATER)) { @@ -204,19 +220,16 @@ int32 CmdBuildCanal(int x, int y, uint32 flags, uint32 p1, uint32 p2) } else { /* is middle piece of a bridge? */ if (IsTileType(tile, MP_TUNNELBRIDGE) && _map5[tile] & 0x40) { /* build under bridge */ - if (_map5[tile] & 0x20) { // transport route under bridge - _error_message = STR_5800_OBJECT_IN_THE_WAY; - ret = CMD_ERROR; - } - else if (_map5[tile] & 0x18) { // already water under bridge - _error_message = STR_1007_ALREADY_BUILT; - ret = CMD_ERROR; - } + if (_map5[tile] & 0x20) // transport route under bridge + return_cmd_error(STR_5800_OBJECT_IN_THE_WAY); + + if (_map5[tile] & 0x18) // already water under bridge + return_cmd_error(STR_1007_ALREADY_BUILT); /* no bridge? then try to clear it. */ } else ret = DoCommandByTile(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); - if (ret == CMD_ERROR) return ret; + if (CmdFailed(ret)) return CMD_ERROR; cost += ret; /* execute modifications */ |