tron@2186: /* $Id$ */ tron@2186: belugas@6123: /** @file command.cpp */ belugas@6123: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@507: #include "table/strings.h" rubidium@7266: #include "strings.h" tron@2163: #include "functions.h" maedhros@6453: #include "landscape.h" tron@679: #include "map.h" truelight@0: #include "gui.h" truelight@0: #include "command.h" truelight@0: #include "player.h" rubidium@5469: #include "network/network.h" tron@2159: #include "variables.h" truelight@4300: #include "genworld.h" truelight@0: tron@1820: const char* _cmd_text = NULL; tron@1820: rubidium@6943: #define DEF_COMMAND(yyyy) CommandCost yyyy(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: truelight@0: DEF_COMMAND(CmdBuildRailroadTrack); truelight@0: DEF_COMMAND(CmdRemoveRailroadTrack); truelight@0: DEF_COMMAND(CmdBuildSingleRail); truelight@0: DEF_COMMAND(CmdRemoveSingleRail); truelight@0: truelight@0: DEF_COMMAND(CmdLandscapeClear); truelight@0: truelight@0: DEF_COMMAND(CmdBuildBridge); truelight@0: truelight@0: DEF_COMMAND(CmdBuildRailroadStation); truelight@0: DEF_COMMAND(CmdRemoveFromRailroadStation); truelight@0: DEF_COMMAND(CmdConvertRail); truelight@0: darkvater@1227: DEF_COMMAND(CmdBuildSingleSignal); darkvater@1227: DEF_COMMAND(CmdRemoveSingleSignal); truelight@0: truelight@0: DEF_COMMAND(CmdTerraformLand); truelight@0: truelight@0: DEF_COMMAND(CmdPurchaseLandArea); truelight@0: DEF_COMMAND(CmdSellLandArea); truelight@0: truelight@0: DEF_COMMAND(CmdBuildTunnel); truelight@0: truelight@0: DEF_COMMAND(CmdBuildTrainDepot); darkvater@395: DEF_COMMAND(CmdBuildTrainWaypoint); darkvater@395: DEF_COMMAND(CmdRenameWaypoint); darkvater@395: DEF_COMMAND(CmdRemoveTrainWaypoint); truelight@0: celestar@1217: DEF_COMMAND(CmdBuildRoadStop); rubidium@6012: DEF_COMMAND(CmdRemoveRoadStop); truelight@0: truelight@0: DEF_COMMAND(CmdBuildLongRoad); truelight@0: DEF_COMMAND(CmdRemoveLongRoad); truelight@0: DEF_COMMAND(CmdBuildRoad); truelight@0: DEF_COMMAND(CmdRemoveRoad); truelight@0: truelight@0: DEF_COMMAND(CmdBuildRoadDepot); truelight@0: truelight@0: DEF_COMMAND(CmdBuildAirport); truelight@0: truelight@0: DEF_COMMAND(CmdBuildDock); truelight@0: truelight@0: DEF_COMMAND(CmdBuildShipDepot); truelight@0: truelight@0: DEF_COMMAND(CmdBuildBuoy); truelight@0: truelight@0: DEF_COMMAND(CmdPlantTree); truelight@0: truelight@0: DEF_COMMAND(CmdBuildRailVehicle); truelight@0: DEF_COMMAND(CmdMoveRailVehicle); truelight@0: truelight@0: DEF_COMMAND(CmdStartStopTrain); truelight@0: truelight@0: DEF_COMMAND(CmdSellRailWagon); truelight@0: Darkvater@1794: DEF_COMMAND(CmdSendTrainToDepot); truelight@0: DEF_COMMAND(CmdForceTrainProceed); truelight@0: DEF_COMMAND(CmdReverseTrainDirection); truelight@0: truelight@0: DEF_COMMAND(CmdModifyOrder); rubidium@6794: DEF_COMMAND(CmdSkipToOrder); truelight@0: DEF_COMMAND(CmdDeleteOrder); truelight@0: DEF_COMMAND(CmdInsertOrder); tron@2819: DEF_COMMAND(CmdChangeServiceInt); truelight@0: DEF_COMMAND(CmdRestoreOrderIndex); truelight@0: truelight@0: DEF_COMMAND(CmdBuildIndustry); truelight@0: truelight@0: DEF_COMMAND(CmdBuildCompanyHQ); truelight@0: DEF_COMMAND(CmdSetPlayerFace); truelight@0: DEF_COMMAND(CmdSetPlayerColor); truelight@0: truelight@0: DEF_COMMAND(CmdIncreaseLoan); truelight@0: DEF_COMMAND(CmdDecreaseLoan); truelight@0: truelight@0: DEF_COMMAND(CmdWantEnginePreview); truelight@0: truelight@0: DEF_COMMAND(CmdNameVehicle); truelight@0: DEF_COMMAND(CmdRenameEngine); truelight@0: truelight@0: DEF_COMMAND(CmdChangeCompanyName); truelight@0: DEF_COMMAND(CmdChangePresidentName); truelight@0: truelight@0: DEF_COMMAND(CmdRenameStation); truelight@0: truelight@0: DEF_COMMAND(CmdSellAircraft); truelight@0: DEF_COMMAND(CmdStartStopAircraft); truelight@0: DEF_COMMAND(CmdBuildAircraft); truelight@0: DEF_COMMAND(CmdSendAircraftToHangar); truelight@0: DEF_COMMAND(CmdRefitAircraft); truelight@0: truelight@0: DEF_COMMAND(CmdPlaceSign); truelight@0: DEF_COMMAND(CmdRenameSign); truelight@0: truelight@0: DEF_COMMAND(CmdBuildRoadVeh); truelight@0: DEF_COMMAND(CmdStartStopRoadVeh); truelight@0: DEF_COMMAND(CmdSellRoadVeh); truelight@0: DEF_COMMAND(CmdSendRoadVehToDepot); truelight@0: DEF_COMMAND(CmdTurnRoadVeh); peter1138@3990: DEF_COMMAND(CmdRefitRoadVeh); truelight@0: truelight@0: DEF_COMMAND(CmdPause); truelight@0: truelight@0: DEF_COMMAND(CmdBuyShareInCompany); truelight@0: DEF_COMMAND(CmdSellShareInCompany); truelight@0: DEF_COMMAND(CmdBuyCompany); truelight@0: truelight@0: DEF_COMMAND(CmdBuildTown); truelight@0: truelight@0: DEF_COMMAND(CmdRenameTown); truelight@0: DEF_COMMAND(CmdDoTownAction); truelight@0: truelight@0: DEF_COMMAND(CmdSetRoadDriveSide); truelight@0: truelight@0: DEF_COMMAND(CmdChangeDifficultyLevel); truelight@543: DEF_COMMAND(CmdChangePatchSetting); truelight@0: truelight@0: DEF_COMMAND(CmdStartStopShip); truelight@0: DEF_COMMAND(CmdSellShip); truelight@0: DEF_COMMAND(CmdBuildShip); truelight@0: DEF_COMMAND(CmdSendShipToDepot); truelight@0: DEF_COMMAND(CmdRefitShip); truelight@0: bjarni@4712: DEF_COMMAND(CmdOrderRefit); truelight@0: DEF_COMMAND(CmdCloneOrder); truelight@0: truelight@0: DEF_COMMAND(CmdClearArea); truelight@0: truelight@543: DEF_COMMAND(CmdGiveMoney); truelight@0: DEF_COMMAND(CmdMoneyCheat); truelight@0: DEF_COMMAND(CmdBuildCanal); darkvater@147: DEF_COMMAND(CmdBuildLock); truelight@0: truelight@0: DEF_COMMAND(CmdPlayerCtrl); truelight@0: truelight@0: DEF_COMMAND(CmdLevelLand); truelight@0: truelight@0: DEF_COMMAND(CmdRefitRailVehicle); truelight@0: darkvater@1227: DEF_COMMAND(CmdBuildSignalTrack); darkvater@1227: DEF_COMMAND(CmdRemoveSignalTrack); darkvater@58: peter1138@4661: DEF_COMMAND(CmdSetAutoReplace); truelight@812: bjarni@2244: DEF_COMMAND(CmdCloneVehicle); bjarni@4640: DEF_COMMAND(CmdMassStartStopVehicle); bjarni@4659: DEF_COMMAND(CmdDepotSellAllVehicles); bjarni@4662: DEF_COMMAND(CmdDepotMassAutoReplace); bjarni@2244: rubidium@6643: DEF_COMMAND(CmdCreateGroup); rubidium@6643: DEF_COMMAND(CmdRenameGroup); rubidium@6643: DEF_COMMAND(CmdDeleteGroup); rubidium@6643: DEF_COMMAND(CmdAddVehicleGroup); rubidium@6643: DEF_COMMAND(CmdAddSharedVehicleGroup); rubidium@6643: DEF_COMMAND(CmdRemoveAllVehiclesGroup); peter1138@6667: DEF_COMMAND(CmdSetGroupReplaceProtection); peter1138@6667: rubidium@6832: DEF_COMMAND(CmdMoveOrder); maedhros@6980: DEF_COMMAND(CmdChangeTimetable); maedhros@6980: DEF_COMMAND(CmdSetVehicleOnTime); maedhros@7066: DEF_COMMAND(CmdAutofillTimetable); rubidium@6832: truelight@0: /* The master command table */ Darkvater@1804: static const Command _command_proc_table[] = { Darkvater@1804: {CmdBuildRailroadTrack, 0}, /* 0 */ Darkvater@1804: {CmdRemoveRailroadTrack, 0}, /* 1 */ Darkvater@1804: {CmdBuildSingleRail, 0}, /* 2 */ Darkvater@1804: {CmdRemoveSingleRail, 0}, /* 3 */ Darkvater@1804: {CmdLandscapeClear, 0}, /* 4 */ Darkvater@1804: {CmdBuildBridge, 0}, /* 5 */ Darkvater@1804: {CmdBuildRailroadStation, 0}, /* 6 */ Darkvater@1804: {CmdBuildTrainDepot, 0}, /* 7 */ Darkvater@1804: {CmdBuildSingleSignal, 0}, /* 8 */ Darkvater@1804: {CmdRemoveSingleSignal, 0}, /* 9 */ Darkvater@1804: {CmdTerraformLand, 0}, /* 10 */ Darkvater@1804: {CmdPurchaseLandArea, 0}, /* 11 */ Darkvater@1804: {CmdSellLandArea, 0}, /* 12 */ Darkvater@1804: {CmdBuildTunnel, 0}, /* 13 */ Darkvater@1804: {CmdRemoveFromRailroadStation, 0}, /* 14 */ Darkvater@1804: {CmdConvertRail, 0}, /* 15 */ Darkvater@1804: {CmdBuildTrainWaypoint, 0}, /* 16 */ Darkvater@1804: {CmdRenameWaypoint, 0}, /* 17 */ Darkvater@1804: {CmdRemoveTrainWaypoint, 0}, /* 18 */ Darkvater@1804: {NULL, 0}, /* 19 */ Darkvater@1804: {NULL, 0}, /* 20 */ Darkvater@1804: {CmdBuildRoadStop, 0}, /* 21 */ rubidium@6012: {CmdRemoveRoadStop, 0}, /* 22 */ Darkvater@1804: {CmdBuildLongRoad, 0}, /* 23 */ Darkvater@1804: {CmdRemoveLongRoad, 0}, /* 24 */ Darkvater@1804: {CmdBuildRoad, 0}, /* 25 */ Darkvater@1804: {CmdRemoveRoad, 0}, /* 26 */ Darkvater@1804: {CmdBuildRoadDepot, 0}, /* 27 */ Darkvater@1804: {NULL, 0}, /* 28 */ Darkvater@1804: {CmdBuildAirport, 0}, /* 29 */ Darkvater@1804: {CmdBuildDock, 0}, /* 30 */ Darkvater@1804: {CmdBuildShipDepot, 0}, /* 31 */ Darkvater@1804: {CmdBuildBuoy, 0}, /* 32 */ Darkvater@1804: {CmdPlantTree, 0}, /* 33 */ Darkvater@1804: {CmdBuildRailVehicle, 0}, /* 34 */ Darkvater@1804: {CmdMoveRailVehicle, 0}, /* 35 */ Darkvater@1804: {CmdStartStopTrain, 0}, /* 36 */ Darkvater@1804: {NULL, 0}, /* 37 */ Darkvater@1804: {CmdSellRailWagon, 0}, /* 38 */ Darkvater@1804: {CmdSendTrainToDepot, 0}, /* 39 */ Darkvater@1804: {CmdForceTrainProceed, 0}, /* 40 */ Darkvater@1804: {CmdReverseTrainDirection, 0}, /* 41 */ darkvater@147: Darkvater@1804: {CmdModifyOrder, 0}, /* 42 */ rubidium@6794: {CmdSkipToOrder, 0}, /* 43 */ Darkvater@1804: {CmdDeleteOrder, 0}, /* 44 */ Darkvater@1804: {CmdInsertOrder, 0}, /* 45 */ truelight@0: tron@2819: {CmdChangeServiceInt, 0}, /* 46 */ darkvater@147: Darkvater@1804: {CmdBuildIndustry, 0}, /* 47 */ Darkvater@1804: {CmdBuildCompanyHQ, 0}, /* 48 */ Darkvater@1804: {CmdSetPlayerFace, 0}, /* 49 */ Darkvater@1804: {CmdSetPlayerColor, 0}, /* 50 */ Darkvater@1796: Darkvater@1804: {CmdIncreaseLoan, 0}, /* 51 */ Darkvater@1804: {CmdDecreaseLoan, 0}, /* 52 */ Darkvater@1804: Darkvater@1804: {CmdWantEnginePreview, 0}, /* 53 */ Darkvater@1804: Darkvater@1804: {CmdNameVehicle, 0}, /* 54 */ Darkvater@1804: {CmdRenameEngine, 0}, /* 55 */ Darkvater@1804: Darkvater@1804: {CmdChangeCompanyName, 0}, /* 56 */ Darkvater@1804: {CmdChangePresidentName, 0}, /* 57 */ Darkvater@1804: Darkvater@1804: {CmdRenameStation, 0}, /* 58 */ Darkvater@1804: Darkvater@1804: {CmdSellAircraft, 0}, /* 59 */ Darkvater@1804: {CmdStartStopAircraft, 0}, /* 60 */ Darkvater@1804: Darkvater@1804: {CmdBuildAircraft, 0}, /* 61 */ Darkvater@1804: {CmdSendAircraftToHangar, 0}, /* 62 */ tron@2819: {NULL, 0}, /* 63 */ Darkvater@1804: {CmdRefitAircraft, 0}, /* 64 */ Darkvater@1804: Darkvater@1804: {CmdPlaceSign, 0}, /* 65 */ Darkvater@1804: {CmdRenameSign, 0}, /* 66 */ Darkvater@1804: Darkvater@1804: {CmdBuildRoadVeh, 0}, /* 67 */ Darkvater@1804: {CmdStartStopRoadVeh, 0}, /* 68 */ Darkvater@1804: {CmdSellRoadVeh, 0}, /* 69 */ Darkvater@1804: {CmdSendRoadVehToDepot, 0}, /* 70 */ Darkvater@1804: {CmdTurnRoadVeh, 0}, /* 71 */ peter1138@3990: {CmdRefitRoadVeh, 0}, /* 72 */ Darkvater@1804: Darkvater@1804: {CmdPause, CMD_SERVER}, /* 73 */ Darkvater@1804: Darkvater@1804: {CmdBuyShareInCompany, 0}, /* 74 */ Darkvater@1804: {CmdSellShareInCompany, 0}, /* 75 */ Darkvater@1804: {CmdBuyCompany, 0}, /* 76 */ Darkvater@1804: Darkvater@1804: {CmdBuildTown, CMD_OFFLINE}, /* 77 */ Darkvater@1804: {NULL, 0}, /* 78 */ Darkvater@1804: {NULL, 0}, /* 79 */ Darkvater@1804: {CmdRenameTown, CMD_SERVER}, /* 80 */ Darkvater@1804: {CmdDoTownAction, 0}, /* 81 */ Darkvater@1804: Darkvater@1804: {CmdSetRoadDriveSide, CMD_SERVER}, /* 82 */ Darkvater@1804: {NULL, 0}, /* 83 */ Darkvater@1804: {NULL, 0}, /* 84 */ Darkvater@1804: {CmdChangeDifficultyLevel, CMD_SERVER}, /* 85 */ Darkvater@1804: Darkvater@1804: {CmdStartStopShip, 0}, /* 86 */ Darkvater@1804: {CmdSellShip, 0}, /* 87 */ Darkvater@1804: {CmdBuildShip, 0}, /* 88 */ Darkvater@1804: {CmdSendShipToDepot, 0}, /* 89 */ tron@2819: {NULL, 0}, /* 90 */ Darkvater@1804: {CmdRefitShip, 0}, /* 91 */ Darkvater@1804: Darkvater@1804: {NULL, 0}, /* 92 */ Darkvater@1804: {NULL, 0}, /* 93 */ Darkvater@1804: {NULL, 0}, /* 94 */ Darkvater@1804: {NULL, 0}, /* 95 */ Darkvater@1804: {NULL, 0}, /* 96 */ Darkvater@1804: {NULL, 0}, /* 97 */ Darkvater@1804: bjarni@4712: {CmdOrderRefit, 0}, /* 98 */ Darkvater@1804: {CmdCloneOrder, 0}, /* 99 */ Darkvater@1804: Darkvater@1804: {CmdClearArea, 0}, /* 100 */ Darkvater@1804: {NULL, 0}, /* 101 */ Darkvater@1804: Darkvater@1804: {CmdMoneyCheat, CMD_OFFLINE}, /* 102 */ Darkvater@1804: {CmdBuildCanal, 0}, /* 103 */ Darkvater@1804: {CmdPlayerCtrl, 0}, /* 104 */ Darkvater@1804: Darkvater@1804: {CmdLevelLand, 0}, /* 105 */ Darkvater@1804: Darkvater@1804: {CmdRefitRailVehicle, 0}, /* 106 */ Darkvater@1804: {CmdRestoreOrderIndex, 0}, /* 107 */ Darkvater@1804: {CmdBuildLock, 0}, /* 108 */ Darkvater@1804: {NULL, 0}, /* 109 */ Darkvater@1804: {CmdBuildSignalTrack, 0}, /* 110 */ Darkvater@1804: {CmdRemoveSignalTrack, 0}, /* 111 */ Darkvater@1804: {NULL, 0}, /* 112 */ Darkvater@1804: {CmdGiveMoney, 0}, /* 113 */ Darkvater@1804: {CmdChangePatchSetting, CMD_SERVER}, /* 114 */ peter1138@4661: {CmdSetAutoReplace, 0}, /* 115 */ peter1138@3991: {CmdCloneVehicle, 0}, /* 116 */ bjarni@4640: {CmdMassStartStopVehicle, 0}, /* 117 */ bjarni@4659: {CmdDepotSellAllVehicles, 0}, /* 118 */ bjarni@4662: {CmdDepotMassAutoReplace, 0}, /* 119 */ rubidium@6643: {CmdCreateGroup, 0}, /* 120 */ rubidium@6643: {CmdDeleteGroup, 0}, /* 121 */ rubidium@6643: {CmdRenameGroup, 0}, /* 122 */ rubidium@6643: {CmdAddVehicleGroup, 0}, /* 123 */ rubidium@6643: {CmdAddSharedVehicleGroup, 0}, /* 124 */ rubidium@6643: {CmdRemoveAllVehiclesGroup, 0}, /* 125 */ peter1138@6667: {CmdSetGroupReplaceProtection, 0}, /* 126 */ rubidium@6832: {CmdMoveOrder, 0}, /* 127 */ maedhros@6980: {CmdChangeTimetable, 0}, /* 128 */ maedhros@6980: {CmdSetVehicleOnTime, 0}, /* 129 */ maedhros@7066: {CmdAutofillTimetable, 0}, /* 130 */ truelight@0: }; truelight@0: truelight@903: /* This function range-checks a cmd, and checks if the cmd is not NULL */ tron@959: bool IsValidCommand(uint cmd) truelight@903: { tron@2645: cmd &= 0xFF; truelight@903: tron@2645: return tron@2645: cmd < lengthof(_command_proc_table) && tron@2645: _command_proc_table[cmd].proc != NULL; truelight@903: } truelight@903: tron@4000: byte GetCommandFlags(uint cmd) tron@4000: { tron@4000: return _command_proc_table[cmd & 0xFF].flags; tron@4000: } Darkvater@1804: truelight@0: tron@2304: static int _docommand_recursive; tron@2304: rubidium@6943: CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc) truelight@0: { rubidium@6943: CommandCost res; truelight@0: CommandProc *proc; truelight@193: Darkvater@1776: /* Do not even think about executing out-of-bounds tile-commands */ rubidium@6630: if (tile >= MapSize() || IsTileType(tile, MP_VOID)) { tron@1820: _cmd_text = NULL; tron@1820: return CMD_ERROR; tron@1820: } Darkvater@1776: Darkvater@1804: proc = _command_proc_table[procc].proc; truelight@0: Darkvater@2749: if (_docommand_recursive == 0) _error_message = INVALID_STRING_ID; truelight@0: truelight@0: _docommand_recursive++; truelight@0: belugas@6123: /* only execute the test call if it's toplevel, or we're not execing. */ truelight@0: if (_docommand_recursive == 1 || !(flags & DC_EXEC) || (flags & DC_FORCETEST) ) { tron@3491: res = proc(tile, flags & ~DC_EXEC, p1, p2); tron@1691: if (CmdFailed(res)) { rubidium@6950: res.SetGlobalErrorMessage(); truelight@0: goto error; truelight@0: } truelight@0: tron@4000: if (_docommand_recursive == 1 && tron@4000: !(flags & DC_QUERY_COST) && rubidium@6950: res.GetCost() != 0 && tron@4000: !CheckPlayerHasMoney(res)) { tron@4000: goto error; truelight@0: } truelight@0: truelight@0: if (!(flags & DC_EXEC)) { truelight@0: _docommand_recursive--; tron@1820: _cmd_text = NULL; truelight@0: return res; truelight@0: } truelight@0: } truelight@0: darkvater@889: /* Execute the command here. All cost-relevant functions set the expenses type darkvater@889: * themselves with "SET_EXPENSES_TYPE(...);" at the beginning of the function */ tron@3491: res = proc(tile, flags, p1, p2); tron@1691: if (CmdFailed(res)) { rubidium@6950: res.SetGlobalErrorMessage(); truelight@0: error: truelight@0: _docommand_recursive--; tron@1820: _cmd_text = NULL; truelight@0: return CMD_ERROR; truelight@0: } truelight@0: belugas@6123: /* if toplevel, subtract the money. */ truelight@0: if (--_docommand_recursive == 0) { truelight@0: SubtractMoneyFromPlayer(res); belugas@6123: /* XXX - Old AI hack which doesn't use DoCommandDP; update last build coord of player */ Darkvater@4850: if (tile != 0 && IsValidPlayer(_current_player)) { tron@3491: GetPlayer(_current_player)->last_build_coordinate = tile; Darkvater@2772: } truelight@0: } truelight@0: tron@1820: _cmd_text = NULL; truelight@0: return res; truelight@0: } truelight@0: rubidium@6990: Money GetAvailableMoneyForCommand() truelight@0: { Darkvater@1794: PlayerID pid = _current_player; rubidium@6990: if (!IsValidPlayer(pid)) return INT64_MAX; celestar@1962: return GetPlayer(pid)->player_money; truelight@0: } truelight@0: belugas@6123: /* toplevel network safe docommand function for the current player. must not be called recursively. belugas@6123: * the callback is called when the command succeeded or failed. */ rubidium@7222: bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, uint32 cmd, bool my_cmd) truelight@0: { rubidium@6950: CommandCost res, res2; truelight@0: CommandProc *proc; truelight@0: uint32 flags; truelight@0: bool notest; tron@3182: StringID error_part1; truelight@0: celestar@3421: int x = TileX(tile) * TILE_SIZE; celestar@3421: int y = TileY(tile) * TILE_SIZE; truelight@0: Darkvater@1776: /* Do not even think about executing out-of-bounds tile-commands */ rubidium@6630: if (tile >= MapSize() || IsTileType(tile, MP_VOID)) { tron@1820: _cmd_text = NULL; tron@1820: return false; tron@1820: } Darkvater@1776: truelight@193: assert(_docommand_recursive == 0); truelight@0: truelight@0: _error_message = INVALID_STRING_ID; tron@3182: error_part1 = GB(cmd, 16, 16); truelight@0: _additional_cash_required = 0; truelight@0: Darkvater@5067: /** Spectator has no rights except for the (dedicated) server which Darkvater@5067: * is/can be a spectator but as the server it can do anything */ Darkvater@5067: if (_current_player == PLAYER_SPECTATOR && !_network_server) { rubidium@7222: if (my_cmd) ShowErrorMessage(_error_message, error_part1, x, y); tron@1820: _cmd_text = NULL; truelight@0: return false; truelight@0: } truelight@0: truelight@0: flags = 0; truelight@0: if (cmd & CMD_AUTO) flags |= DC_AUTO; truelight@0: if (cmd & CMD_NO_WATER) flags |= DC_NO_WATER; truelight@0: belugas@6123: /* get pointer to command handler */ truelight@0: assert((cmd & 0xFF) < lengthof(_command_proc_table)); Darkvater@1804: proc = _command_proc_table[cmd & 0xFF].proc; Darkvater@1877: if (proc == NULL) { Darkvater@1877: _cmd_text = NULL; Darkvater@1877: return false; Darkvater@1877: } truelight@0: belugas@6123: /* Some commands have a different output in dryrun than the realrun belugas@6123: * e.g.: if you demolish a whole town, the dryrun would say okay. belugas@6123: * but by really destroying, your rating drops and at a certain point belugas@6123: * it will fail. so res and res2 are different belugas@6123: * CMD_REMOVE_ROAD: This command has special local authority belugas@6123: * restrictions which may cause the test run to fail (the previous belugas@6123: * road fragments still stay there and the town won't let you belugas@6123: * disconnect the road system), but the exec will succeed and this maedhros@6546: * fact will trigger an assertion failure. --pasky maedhros@6546: * CMD_CLONE_VEHICLE: Both building new vehicles and refitting them can be maedhros@6546: * influenced by newgrf callbacks, which makes it impossible to accurately maedhros@6546: * estimate the cost of cloning a vehicle. */ truelight@193: notest = truelight@193: (cmd & 0xFF) == CMD_CLEAR_AREA || truelight@193: (cmd & 0xFF) == CMD_CONVERT_RAIL || truelight@0: (cmd & 0xFF) == CMD_LEVEL_LAND || Darkvater@1701: (cmd & 0xFF) == CMD_REMOVE_ROAD || maedhros@6546: (cmd & 0xFF) == CMD_REMOVE_LONG_ROAD || maedhros@6546: (cmd & 0xFF) == CMD_CLONE_VEHICLE; truelight@0: truelight@0: _docommand_recursive = 1; truelight@0: belugas@6123: /* cost estimation only? */ celestar@5580: if (!IsGeneratingWorld() && celestar@5580: _shift_pressed && celestar@5580: IsLocalPlayer() && celestar@5580: !(cmd & (CMD_NETWORK_COMMAND | CMD_SHOW_NO_ERROR)) && celestar@5580: (cmd & 0xFF) != CMD_PAUSE) { belugas@6123: /* estimate the cost. */ tron@3491: res = proc(tile, flags, p1, p2); tron@1691: if (CmdFailed(res)) { rubidium@6950: res.SetGlobalErrorMessage(); tron@3182: ShowErrorMessage(_error_message, error_part1, x, y); truelight@0: } else { rubidium@6950: ShowEstimatedCostOrIncome(res.GetCost(), x, y); truelight@0: } truelight@0: truelight@0: _docommand_recursive = 0; tron@1820: _cmd_text = NULL; truelight@0: return false; truelight@0: } truelight@0: truelight@193: truelight@543: if (!((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) { belugas@6123: /* first test if the command can be executed. */ tron@3491: res = proc(tile, flags, p1, p2); tron@1691: if (CmdFailed(res)) { rubidium@6950: res.SetGlobalErrorMessage(); truelight@0: goto show_error; truelight@0: } belugas@6123: /* no money? Only check if notest is off */ rubidium@6950: if (!notest && res.GetCost() != 0 && !CheckPlayerHasMoney(res)) goto show_error; truelight@0: } truelight@0: truelight@543: #ifdef ENABLE_NETWORK Darkvater@1838: /** If we are in network, and the command is not from the network Darkvater@1838: * send it to the command-queue and abort execution Darkvater@1838: * If we are a dedicated server temporarily switch local player, otherwise Darkvater@1838: * the other parties won't be able to execute our command and will desync. Darkvater@5067: * We also need to do this if the server's company has gone bankrupt Darkvater@5067: * @todo Rewrite (dedicated) server to something more than a dirty hack! Darkvater@1838: */ truelight@543: if (_networking && !(cmd & CMD_NETWORK_COMMAND)) { Darkvater@5067: PlayerID pbck = _local_player; rubidium@5587: if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = PLAYER_FIRST; truelight@543: NetworkSend_Command(tile, p1, p2, cmd, callback); Darkvater@5067: if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = pbck; truelight@543: _docommand_recursive = 0; tron@1820: _cmd_text = NULL; truelight@543: return true; truelight@0: } truelight@543: #endif /* ENABLE_NETWORK */ truelight@0: belugas@6123: /* update last build coordinate of player. */ Darkvater@4850: if (tile != 0 && IsValidPlayer(_current_player)) { tron@4000: GetPlayer(_current_player)->last_build_coordinate = tile; tron@4000: } truelight@0: darkvater@889: /* Actually try and execute the command. If no cost-type is given darkvater@889: * use the construction one */ darkvater@889: _yearly_expenses_type = EXPENSES_CONSTRUCTION; tron@4000: res2 = proc(tile, flags | DC_EXEC, p1, p2); truelight@193: belugas@6123: /* If notest is on, it means the result of the test can be different than belugas@6123: * the real command.. so ignore the test */ truelight@652: if (!notest && !((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) { rubidium@6950: assert(res.GetCost() == res2.GetCost() && CmdFailed(res) == CmdFailed(res2)); // sanity check truelight@0: } else { Darkvater@1702: if (CmdFailed(res2)) { rubidium@6950: res.SetGlobalErrorMessage(); truelight@0: goto show_error; truelight@0: } truelight@0: } truelight@0: truelight@0: SubtractMoneyFromPlayer(res2); truelight@0: Darkvater@2425: if (IsLocalPlayer() && _game_mode != GM_EDITOR) { rubidium@6950: if (res2.GetCost() != 0) ShowCostOrIncomeAnimation(x, y, GetSlopeZ(x, y), res2.GetCost()); rubidium@6990: if (_additional_cash_required != 0) { rubidium@7002: SetDParam(0, _additional_cash_required); rubidium@7222: if (my_cmd) ShowErrorMessage(STR_0003_NOT_ENOUGH_CASH_REQUIRES, error_part1, x, y); rubidium@6950: if (res2.GetCost() == 0) goto callb_err; truelight@0: } truelight@0: } truelight@0: truelight@0: _docommand_recursive = 0; truelight@193: truelight@0: if (callback) callback(true, tile, p1, p2); tron@1820: _cmd_text = NULL; truelight@0: return true; truelight@0: truelight@0: show_error: belugas@6123: /* show error message if the command fails? */ rubidium@7222: if (IsLocalPlayer() && error_part1 != 0 && my_cmd) { rubidium@6491: ShowErrorMessage(_error_message, error_part1, x, y); tron@3182: } truelight@0: truelight@0: callb_err: truelight@0: _docommand_recursive = 0; truelight@193: truelight@0: if (callback) callback(false, tile, p1, p2); tron@1820: _cmd_text = NULL; truelight@0: return false; truelight@0: } rubidium@6950: rubidium@6950: rubidium@6950: CommandCost CommandCost::AddCost(CommandCost ret) rubidium@6950: { rubidium@6957: this->AddCost(ret.cost); rubidium@6950: if (this->success && !ret.success) { rubidium@6950: this->message = ret.message; rubidium@6950: this->success = false; rubidium@6950: } rubidium@6950: return *this; rubidium@6950: } rubidium@6950: rubidium@6953: CommandCost CommandCost::AddCost(Money cost) rubidium@6950: { rubidium@6957: /* Overflow protection */ rubidium@6957: if (cost < 0 && (this->cost + cost) > this->cost) { rubidium@6957: this->cost = INT64_MIN; rubidium@6957: } else if (cost > 0 && (this->cost + cost) < this->cost) { rubidium@6957: this->cost = INT64_MAX; rubidium@6957: } else { rubidium@6957: this->cost += cost; rubidium@6957: } rubidium@6950: return *this; rubidium@6950: } rubidium@6950: rubidium@6950: CommandCost CommandCost::MultiplyCost(int factor) rubidium@6950: { rubidium@6957: /* Overflow protection */ rubidium@6957: if (factor != 0 && (INT64_MAX / myabs(factor)) < myabs(this->cost)) { rubidium@6957: this->cost = (this->cost < 0 == factor < 0) ? INT64_MAX : INT64_MIN; rubidium@6957: } else { rubidium@6957: this->cost *= factor; rubidium@6957: } rubidium@6950: return *this; rubidium@6950: } rubidium@6950: rubidium@6953: Money CommandCost::GetCost() const rubidium@6950: { rubidium@6950: return this->cost; rubidium@6950: } rubidium@6950: rubidium@6950: void CommandCost::SetGlobalErrorMessage() const rubidium@6950: { rubidium@6950: extern StringID _error_message; rubidium@6950: if (this->message != INVALID_STRING_ID) _error_message = this->message; rubidium@6950: } rubidium@6950: rubidium@6950: bool CommandCost::Succeeded() const rubidium@6950: { rubidium@6950: return this->success; rubidium@6950: } rubidium@6950: rubidium@6950: bool CommandCost::Failed() const rubidium@6950: { rubidium@6950: return !this->success; rubidium@6950: }