tron@2186: /* $Id$ */ tron@2186: rubidium@9111: /** @file command.cpp Handling of commands. */ belugas@6123: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" maedhros@6453: #include "landscape.h" rubidium@8108: #include "tile_map.h" truelight@0: #include "gui.h" rubidium@8116: #include "command_func.h" rubidium@5469: #include "network/network.h" tron@2159: #include "variables.h" truelight@4300: #include "genworld.h" rubidium@7609: #include "newgrf_storage.h" rubidium@8114: #include "strings_func.h" rubidium@8123: #include "gfx_func.h" rubidium@8131: #include "functions.h" glx@8232: #include "town.h" rubidium@8247: #include "date_func.h" rubidium@8247: #include "debug.h" rubidium@8254: #include "player_func.h" rubidium@8254: #include "player_base.h" smatz@8306: #include "signal_func.h" truelight@0: rubidium@8264: #include "table/strings.h" rubidium@8264: rubidium@7559: const char *_cmd_text = NULL; rubidium@8275: StringID _error_message; tron@1820: rubidium@7559: /** rubidium@7559: * Helper macro to define the header of all command handler macros. rubidium@7559: * rubidium@7559: * This macro create the function header for a given command handler function, as rubidium@7559: * all command handler functions got the parameters from the #CommandProc callback rubidium@7559: * type. rubidium@7559: * rubidium@7559: * @param yyyy The desired function name of the new command handler function. rubidium@7559: */ 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@7559: #undef DEF_COMMAND Darkvater@1804: rubidium@7559: /** rubidium@7559: * The master command table rubidium@7559: * rubidium@7559: * This table contains all possible CommandProc functions with rubidium@7559: * the flags which belongs to it. The indizes are the same rubidium@7559: * as the value from the CMD_* enums. rubidium@7559: */ rubidium@7559: static const Command _command_proc_table[] = { peter1138@8500: {CmdBuildRailroadTrack, CMD_AUTO}, /* CMD_BUILD_RAILROAD_TRACK */ peter1138@8500: {CmdRemoveRailroadTrack, CMD_AUTO}, /* CMD_REMOVE_RAILROAD_TRACK */ peter1138@8500: {CmdBuildSingleRail, CMD_AUTO}, /* CMD_BUILD_SINGLE_RAIL */ peter1138@8500: {CmdRemoveSingleRail, CMD_AUTO}, /* CMD_REMOVE_SINGLE_RAIL */ peter1138@8500: {CmdLandscapeClear, 0}, /* CMD_LANDSCAPE_CLEAR */ peter1138@8500: {CmdBuildBridge, CMD_AUTO}, /* CMD_BUILD_BRIDGE */ peter1138@8500: {CmdBuildRailroadStation, CMD_AUTO}, /* CMD_BUILD_RAILROAD_STATION */ peter1138@8500: {CmdBuildTrainDepot, CMD_AUTO}, /* CMD_BUILD_TRAIN_DEPOT */ peter1138@8500: {CmdBuildSingleSignal, CMD_AUTO}, /* CMD_BUILD_SIGNALS */ peter1138@8500: {CmdRemoveSingleSignal, CMD_AUTO}, /* CMD_REMOVE_SIGNALS */ peter1138@8500: {CmdTerraformLand, CMD_AUTO}, /* CMD_TERRAFORM_LAND */ peter1138@8500: {CmdPurchaseLandArea, CMD_AUTO}, /* CMD_PURCHASE_LAND_AREA */ peter1138@8500: {CmdSellLandArea, 0}, /* CMD_SELL_LAND_AREA */ peter1138@8500: {CmdBuildTunnel, CMD_AUTO}, /* CMD_BUILD_TUNNEL */ peter1138@8500: {CmdRemoveFromRailroadStation, 0}, /* CMD_REMOVE_FROM_RAILROAD_STATION */ peter1138@8500: {CmdConvertRail, 0}, /* CMD_CONVERT_RAILD */ peter1138@8500: {CmdBuildTrainWaypoint, 0}, /* CMD_BUILD_TRAIN_WAYPOINT */ peter1138@8500: {CmdRenameWaypoint, 0}, /* CMD_RENAME_WAYPOINT */ peter1138@8500: {CmdRemoveTrainWaypoint, 0}, /* CMD_REMOVE_TRAIN_WAYPOINT */ rubidium@7559: peter1138@8500: {CmdBuildRoadStop, CMD_AUTO}, /* CMD_BUILD_ROAD_STOP */ peter1138@8500: {CmdRemoveRoadStop, 0}, /* CMD_REMOVE_ROAD_STOP */ peter1138@8500: {CmdBuildLongRoad, CMD_AUTO}, /* CMD_BUILD_LONG_ROAD */ peter1138@8500: {CmdRemoveLongRoad, CMD_AUTO}, /* CMD_REMOVE_LONG_ROAD */ peter1138@8500: {CmdBuildRoad, 0}, /* CMD_BUILD_ROAD */ peter1138@8500: {CmdRemoveRoad, 0}, /* CMD_REMOVE_ROAD */ peter1138@8500: {CmdBuildRoadDepot, CMD_AUTO}, /* CMD_BUILD_ROAD_DEPOT */ rubidium@7559: peter1138@8500: {CmdBuildAirport, CMD_AUTO}, /* CMD_BUILD_AIRPORT */ peter1138@8500: {CmdBuildDock, CMD_AUTO}, /* CMD_BUILD_DOCK */ peter1138@8500: {CmdBuildShipDepot, CMD_AUTO}, /* CMD_BUILD_SHIP_DEPOT */ peter1138@8500: {CmdBuildBuoy, CMD_AUTO}, /* CMD_BUILD_BUOY */ peter1138@8500: {CmdPlantTree, CMD_AUTO}, /* CMD_PLANT_TREE */ peter1138@8500: {CmdBuildRailVehicle, 0}, /* CMD_BUILD_RAIL_VEHICLE */ peter1138@8500: {CmdMoveRailVehicle, 0}, /* CMD_MOVE_RAIL_VEHICLE */ peter1138@8500: {CmdStartStopTrain, 0}, /* CMD_START_STOP_TRAIN */ rubidium@7559: peter1138@8500: {CmdSellRailWagon, 0}, /* CMD_SELL_RAIL_WAGON */ peter1138@8500: {CmdSendTrainToDepot, 0}, /* CMD_SEND_TRAIN_TO_DEPOT */ peter1138@8500: {CmdForceTrainProceed, 0}, /* CMD_FORCE_TRAIN_PROCEED */ peter1138@8500: {CmdReverseTrainDirection, 0}, /* CMD_REVERSE_TRAIN_DIRECTION */ peter1138@8500: peter1138@8500: {CmdModifyOrder, 0}, /* CMD_MODIFY_ORDER */ peter1138@8500: {CmdSkipToOrder, 0}, /* CMD_SKIP_TO_ORDER */ peter1138@8500: {CmdDeleteOrder, 0}, /* CMD_DELETE_ORDER */ peter1138@8500: {CmdInsertOrder, 0}, /* CMD_INSERT_ORDER */ peter1138@8500: peter1138@8500: {CmdChangeServiceInt, 0}, /* CMD_CHANGE_SERVICE_INT */ peter1138@8500: peter1138@8500: {CmdBuildIndustry, 0}, /* CMD_BUILD_INDUSTRY */ peter1138@8500: {CmdBuildCompanyHQ, CMD_AUTO}, /* CMD_BUILD_COMPANY_HQ */ peter1138@8500: {CmdSetPlayerFace, 0}, /* CMD_SET_PLAYER_FACE */ peter1138@8500: {CmdSetPlayerColor, 0}, /* CMD_SET_PLAYER_COLOR */ peter1138@8500: peter1138@8500: {CmdIncreaseLoan, 0}, /* CMD_INCREASE_LOAN */ peter1138@8500: {CmdDecreaseLoan, 0}, /* CMD_DECREASE_LOAN */ peter1138@8500: peter1138@8500: {CmdWantEnginePreview, 0}, /* CMD_WANT_ENGINE_PREVIEW */ peter1138@8500: peter1138@8500: {CmdNameVehicle, 0}, /* CMD_NAME_VEHICLE */ peter1138@8500: {CmdRenameEngine, 0}, /* CMD_RENAME_ENGINE */ peter1138@8500: peter1138@8500: {CmdChangeCompanyName, 0}, /* CMD_CHANGE_COMPANY_NAME */ peter1138@8500: {CmdChangePresidentName, 0}, /* CMD_CHANGE_PRESIDENT_NAME */ peter1138@8500: peter1138@8500: {CmdRenameStation, 0}, /* CMD_RENAME_STATION */ peter1138@8500: peter1138@8500: {CmdSellAircraft, 0}, /* CMD_SELL_AIRCRAFT */ peter1138@8500: {CmdStartStopAircraft, 0}, /* CMD_START_STOP_AIRCRAFT */ peter1138@8500: peter1138@8500: {CmdBuildAircraft, 0}, /* CMD_BUILD_AIRCRAFT */ peter1138@8500: {CmdSendAircraftToHangar, 0}, /* CMD_SEND_AIRCRAFT_TO_HANGAR */ peter1138@8500: {CmdRefitAircraft, 0}, /* CMD_REFIT_AIRCRAFT */ peter1138@8500: peter1138@8500: {CmdPlaceSign, 0}, /* CMD_PLACE_SIGN */ peter1138@8500: {CmdRenameSign, 0}, /* CMD_RENAME_SIGN */ peter1138@8500: peter1138@8500: {CmdBuildRoadVeh, 0}, /* CMD_BUILD_ROAD_VEH */ peter1138@8500: {CmdStartStopRoadVeh, 0}, /* CMD_START_STOP_ROADVEH */ peter1138@8500: {CmdSellRoadVeh, 0}, /* CMD_SELL_ROAD_VEH */ peter1138@8500: {CmdSendRoadVehToDepot, 0}, /* CMD_SEND_ROADVEH_TO_DEPOT */ peter1138@8500: {CmdTurnRoadVeh, 0}, /* CMD_TURN_ROADVEH */ peter1138@8500: {CmdRefitRoadVeh, 0}, /* CMD_REFIT_ROAD_VEH */ peter1138@8500: peter1138@8500: {CmdPause, CMD_SERVER}, /* CMD_PAUSE */ peter1138@8500: peter1138@8500: {CmdBuyShareInCompany, 0}, /* CMD_BUY_SHARE_IN_COMPANY */ peter1138@8500: {CmdSellShareInCompany, 0}, /* CMD_SELL_SHARE_IN_COMPANY */ peter1138@8500: {CmdBuyCompany, 0}, /* CMD_BUY_COMANY */ peter1138@8500: peter1138@8500: {CmdBuildTown, CMD_OFFLINE}, /* CMD_BUILD_TOWN */ peter1138@8500: {CmdRenameTown, CMD_SERVER}, /* CMD_RENAME_TOWN */ peter1138@8500: {CmdDoTownAction, 0}, /* CMD_DO_TOWN_ACTION */ peter1138@8500: peter1138@8500: {CmdSetRoadDriveSide, CMD_SERVER}, /* CMD_SET_ROAD_DRIVE_SIDE */ peter1138@8500: {CmdChangeDifficultyLevel, CMD_SERVER}, /* CMD_CHANGE_DIFFICULTY_LEVEL */ peter1138@8500: peter1138@8500: {CmdStartStopShip, 0}, /* CMD_START_STOP_SHIP */ peter1138@8500: {CmdSellShip, 0}, /* CMD_SELL_SHIP */ peter1138@8500: {CmdBuildShip, 0}, /* CMD_BUILD_SHIP */ peter1138@8500: {CmdSendShipToDepot, 0}, /* CMD_SEND_SHIP_TO_DEPOT */ peter1138@8500: {CmdRefitShip, 0}, /* CMD_REFIT_SHIP */ peter1138@8500: peter1138@8500: {CmdOrderRefit, 0}, /* CMD_ORDER_REFIT */ peter1138@8500: {CmdCloneOrder, 0}, /* CMD_CLONE_ORDER */ peter1138@8500: peter1138@8500: {CmdClearArea, 0}, /* CMD_CLEAR_AREA */ peter1138@8500: peter1138@8500: {CmdMoneyCheat, CMD_OFFLINE}, /* CMD_MONEY_CHEAT */ peter1138@8500: {CmdBuildCanal, CMD_AUTO}, /* CMD_BUILD_CANAL */ peter1138@8500: {CmdPlayerCtrl, 0}, /* CMD_PLAYER_CTRL */ peter1138@8500: peter1138@8500: {CmdLevelLand, CMD_AUTO}, /* CMD_LEVEL_LAND */ peter1138@8500: peter1138@8500: {CmdRefitRailVehicle, 0}, /* CMD_REFIT_RAIL_VEHICLE */ peter1138@8500: {CmdRestoreOrderIndex, 0}, /* CMD_RESTORE_ORDER_INDEX */ peter1138@8500: {CmdBuildLock, CMD_AUTO}, /* CMD_BUILD_LOCK */ peter1138@8500: peter1138@8500: {CmdBuildSignalTrack, CMD_AUTO}, /* CMD_BUILD_SIGNAL_TRACK */ peter1138@8500: {CmdRemoveSignalTrack, CMD_AUTO}, /* CMD_REMOVE_SIGNAL_TRACK */ peter1138@8500: peter1138@8500: {CmdGiveMoney, 0}, /* CMD_GIVE_MONEY */ peter1138@8500: {CmdChangePatchSetting, CMD_SERVER}, /* CMD_CHANGE_PATCH_SETTING */ peter1138@8500: {CmdSetAutoReplace, 0}, /* CMD_SET_AUTOREPLACE */ peter1138@8500: {CmdCloneVehicle, 0}, /* CMD_CLONE_VEHICLE */ peter1138@8500: {CmdMassStartStopVehicle, 0}, /* CMD_MASS_START_STOP */ peter1138@8500: {CmdDepotSellAllVehicles, 0}, /* CMD_DEPOT_SELL_ALL_VEHICLES */ peter1138@8500: {CmdDepotMassAutoReplace, 0}, /* CMD_DEPOT_MASS_AUTOREPLACE */ peter1138@8500: {CmdCreateGroup, 0}, /* CMD_CREATE_GROUP */ peter1138@8500: {CmdDeleteGroup, 0}, /* CMD_DELETE_GROUP */ peter1138@8500: {CmdRenameGroup, 0}, /* CMD_RENAME_GROUP */ peter1138@8500: {CmdAddVehicleGroup, 0}, /* CMD_ADD_VEHICLE_GROUP */ peter1138@8500: {CmdAddSharedVehicleGroup, 0}, /* CMD_ADD_SHARE_VEHICLE_GROUP */ peter1138@8500: {CmdRemoveAllVehiclesGroup, 0}, /* CMD_REMOVE_ALL_VEHICLES_GROUP */ peter1138@8500: {CmdSetGroupReplaceProtection, 0}, /* CMD_SET_GROUP_REPLACE_PROTECTION */ peter1138@8500: {CmdMoveOrder, 0}, /* CMD_MOVE_ORDER */ peter1138@8500: {CmdChangeTimetable, 0}, /* CMD_CHANGE_TIMETABLE */ peter1138@8500: {CmdSetVehicleOnTime, 0}, /* CMD_SET_VEHICLE_ON_TIME */ peter1138@8500: {CmdAutofillTimetable, 0}, /* CMD_AUTOFILL_TIMETABLE */ truelight@0: }; truelight@0: rubidium@7559: /*! rubidium@7559: * This function range-checks a cmd, and checks if the cmd is not NULL rubidium@7559: * rubidium@7559: * @param cmd The integervalue of a command rubidium@7559: * @return true if the command is valid (and got a CommandProc function) rubidium@7559: */ 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: rubidium@7559: /*! rubidium@7559: * This function mask the parameter with 0xFF and returns rubidium@7559: * the flags which belongs to the given command. rubidium@7559: * rubidium@7559: * @param cmd The integer value of the command rubidium@7559: * @return The flags for this command rubidium@7559: * @bug integervalues which are less equals 0xFF and greater than the rubidium@7559: * size of _command_proc_table can result in an index out of bounce rubidium@7559: * error (which doesn't happend anyway). Check function #IsValidCommand(). (Progman) rubidium@7559: */ tron@4000: byte GetCommandFlags(uint cmd) tron@4000: { tron@4000: return _command_proc_table[cmd & 0xFF].flags; tron@4000: } Darkvater@1804: glx@8232: static int _docommand_recursive = 0; tron@2304: rubidium@7559: /*! rubidium@7559: * This function executes a given command with the parameters from the #CommandProc parameter list. rubidium@7559: * Depending on the flags parameter it execute or test a command. rubidium@7559: * rubidium@7559: * @param tile The tile to apply the command on (for the #CommandProc) rubidium@7559: * @param p1 Additional data for the command (for the #CommandProc) rubidium@7559: * @param p2 Additional data for the command (for the #CommandProc) rubidium@7559: * @param flags Flags for the command and how to execute the command rubidium@7559: * @param procc The command-id to execute (a value of the CMD_* enums) rubidium@7559: * @see CommandProc rubidium@7559: */ rubidium@8116: CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint32 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 */ glx@8652: if (!IsValidTile(tile)) { 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. */ smatz@8486: if (_docommand_recursive == 1 || !(flags & DC_EXEC) ) { glx@8232: SetTownRatingTestMode(true); tron@3491: res = proc(tile, flags & ~DC_EXEC, p1, p2); glx@8232: SetTownRatingTestMode(false); 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) && smatz@8519: !(flags & DC_BANKRUPT) && 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 rubidium@8230: * themselves to the cost object at some point */ 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. */ smatz@8519: if (--_docommand_recursive == 0 && !(flags & DC_BANKRUPT)) { 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@7559: /*! rubidium@7559: * This functions returns the money which can be used to execute a command. rubidium@7559: * This is either the money of the current player or INT64_MAX if there rubidium@7559: * is no such a player "at the moment" like the server itself. rubidium@7559: * rubidium@7559: * @return The available money of a player or INT64_MAX rubidium@7559: */ 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: rubidium@7559: /*! rubidium@7559: * Toplevel network safe docommand function for the current player. Must not be called recursively. rubidium@7559: * The callback is called when the command succeeded or failed. The parameters rubidium@7559: * tile, p1 and p2 are from the #CommandProc function. The paramater cmd is the command to execute. rubidium@7559: * The parameter my_cmd is used to indicate if the command is from a player or the server. rubidium@7559: * rubidium@7559: * @param tile The tile to perform a command on (see #CommandProc) rubidium@7559: * @param p1 Additional data for the command (see #CommandProc) rubidium@7559: * @param p2 Additional data for the command (see #CommandProc) rubidium@7559: * @param callback A callback function to call after the command is finished rubidium@7559: * @param cmd The command to execute (a CMD_* value) rubidium@7559: * @param my_cmd indicator if the command is from a player or server (to display error messages for a user) rubidium@7559: * @return true if the command succeeded, else false rubidium@7559: */ 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 */ glx@8652: if (!IsValidTile(tile)) { 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_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: rubidium@7521: if (GetCommandFlags(cmd) & CMD_AUTO) flags |= DC_AUTO; rubidium@7521: 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 glx@8232: * CMD_REMOVE_LONG_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 bjarni@9061: * estimate the cost of cloning a vehicle. */ truelight@193: notest = truelight@193: (cmd & 0xFF) == CMD_CLEAR_AREA || truelight@0: (cmd & 0xFF) == CMD_LEVEL_LAND || maedhros@6546: (cmd & 0xFF) == CMD_REMOVE_LONG_ROAD || bjarni@9061: (cmd & 0xFF) == CMD_CLONE_VEHICLE; glx@8187: 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. */ frosch@8442: SetTownRatingTestMode(true); tron@3491: res = proc(tile, flags, p1, p2); frosch@8442: SetTownRatingTestMode(false); 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; rubidium@7609: ClearStorageChanges(false); 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. */ glx@8232: SetTownRatingTestMode(true); tron@3491: res = proc(tile, flags, p1, p2); glx@8232: SetTownRatingTestMode(false); 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; rubidium@7609: ClearStorageChanges(false); truelight@543: return true; truelight@0: } truelight@543: #endif /* ENABLE_NETWORK */ rubidium@8247: DebugDumpCommands("ddc:cmd:%d;%d;%d;%d;%d;%d;%d;%s\n", _date, _date_fract, (int)_current_player, tile, p1, p2, cmd, _cmd_text); 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 */ 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: smatz@8306: /* update signals if needed */ smatz@8306: UpdateSignalsInBuffer(); smatz@8306: Darkvater@2425: if (IsLocalPlayer() && _game_mode != GM_EDITOR) { rubidium@7506: if (res2.GetCost() != 0 && tile != 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; rubidium@7609: ClearStorageChanges(true); 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; rubidium@7609: ClearStorageChanges(false); truelight@0: return false; truelight@0: } smatz@8854: smatz@8854: smatz@8854: CommandCost CommandCost::AddCost(CommandCost ret) smatz@8854: { smatz@8854: this->AddCost(ret.cost); smatz@8854: if (this->success && !ret.success) { smatz@8854: this->message = ret.message; smatz@8854: this->success = false; smatz@8854: } smatz@8854: return *this; smatz@8854: }