/* $Id$ */ /** @file command.cpp Handling of commands. */ #include "stdafx.h" #include "openttd.h" #include "landscape.h" #include "tile_map.h" #include "gui.h" #include "command_func.h" #include "network/network.h" #include "variables.h" #include "genworld.h" #include "newgrf_storage.h" #include "strings_func.h" #include "gfx_func.h" #include "functions.h" #include "town.h" #include "date_func.h" #include "debug.h" #include "company_func.h" #include "company_base.h" #include "signal_func.h" #include "table/strings.h" StringID _error_message; /** * Helper macro to define the header of all command handler macros. * * This macro create the function header for a given command handler function, as * all command handler functions got the parameters from the #CommandProc callback * type. * * @param yyyy The desired function name of the new command handler function. */ #define DEF_COMMAND(yyyy) CommandCost yyyy(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, const char *text) DEF_COMMAND(CmdBuildRailroadTrack); DEF_COMMAND(CmdRemoveRailroadTrack); DEF_COMMAND(CmdBuildSingleRail); DEF_COMMAND(CmdRemoveSingleRail); DEF_COMMAND(CmdLandscapeClear); DEF_COMMAND(CmdBuildBridge); DEF_COMMAND(CmdBuildRailroadStation); DEF_COMMAND(CmdRemoveFromRailroadStation); DEF_COMMAND(CmdConvertRail); DEF_COMMAND(CmdBuildSingleSignal); DEF_COMMAND(CmdRemoveSingleSignal); DEF_COMMAND(CmdTerraformLand); DEF_COMMAND(CmdPurchaseLandArea); DEF_COMMAND(CmdSellLandArea); DEF_COMMAND(CmdBuildTunnel); DEF_COMMAND(CmdBuildTrainDepot); DEF_COMMAND(CmdBuildTrainWaypoint); DEF_COMMAND(CmdRenameWaypoint); DEF_COMMAND(CmdRemoveTrainWaypoint); DEF_COMMAND(CmdBuildRoadStop); DEF_COMMAND(CmdRemoveRoadStop); DEF_COMMAND(CmdBuildLongRoad); DEF_COMMAND(CmdRemoveLongRoad); DEF_COMMAND(CmdBuildRoad); DEF_COMMAND(CmdRemoveRoad); DEF_COMMAND(CmdBuildRoadDepot); DEF_COMMAND(CmdBuildAirport); DEF_COMMAND(CmdBuildDock); DEF_COMMAND(CmdBuildShipDepot); DEF_COMMAND(CmdBuildBuoy); DEF_COMMAND(CmdPlantTree); DEF_COMMAND(CmdBuildRailVehicle); DEF_COMMAND(CmdMoveRailVehicle); DEF_COMMAND(CmdSellRailWagon); DEF_COMMAND(CmdSendTrainToDepot); DEF_COMMAND(CmdForceTrainProceed); DEF_COMMAND(CmdReverseTrainDirection); DEF_COMMAND(CmdModifyOrder); DEF_COMMAND(CmdSkipToOrder); DEF_COMMAND(CmdDeleteOrder); DEF_COMMAND(CmdInsertOrder); DEF_COMMAND(CmdChangeServiceInt); DEF_COMMAND(CmdRestoreOrderIndex); DEF_COMMAND(CmdBuildIndustry); DEF_COMMAND(CmdBuildCompanyHQ); DEF_COMMAND(CmdSetCompanyManagerFace); DEF_COMMAND(CmdSetCompanyColor); DEF_COMMAND(CmdIncreaseLoan); DEF_COMMAND(CmdDecreaseLoan); DEF_COMMAND(CmdWantEnginePreview); DEF_COMMAND(CmdRenameVehicle); DEF_COMMAND(CmdRenameEngine); DEF_COMMAND(CmdRenameCompany); DEF_COMMAND(CmdRenamePresident); DEF_COMMAND(CmdRenameStation); DEF_COMMAND(CmdSellAircraft); DEF_COMMAND(CmdBuildAircraft); DEF_COMMAND(CmdSendAircraftToHangar); DEF_COMMAND(CmdRefitAircraft); DEF_COMMAND(CmdPlaceSign); DEF_COMMAND(CmdRenameSign); DEF_COMMAND(CmdBuildRoadVeh); DEF_COMMAND(CmdSellRoadVeh); DEF_COMMAND(CmdSendRoadVehToDepot); DEF_COMMAND(CmdTurnRoadVeh); DEF_COMMAND(CmdRefitRoadVeh); DEF_COMMAND(CmdPause); DEF_COMMAND(CmdBuyShareInCompany); DEF_COMMAND(CmdSellShareInCompany); DEF_COMMAND(CmdBuyCompany); DEF_COMMAND(CmdBuildTown); DEF_COMMAND(CmdRenameTown); DEF_COMMAND(CmdDoTownAction); DEF_COMMAND(CmdSetRoadDriveSide); DEF_COMMAND(CmdChangePatchSetting); DEF_COMMAND(CmdSellShip); DEF_COMMAND(CmdBuildShip); DEF_COMMAND(CmdSendShipToDepot); DEF_COMMAND(CmdRefitShip); DEF_COMMAND(CmdOrderRefit); DEF_COMMAND(CmdCloneOrder); DEF_COMMAND(CmdClearArea); DEF_COMMAND(CmdGiveMoney); DEF_COMMAND(CmdMoneyCheat); DEF_COMMAND(CmdBuildCanal); DEF_COMMAND(CmdBuildLock); DEF_COMMAND(CmdCompanyCtrl); DEF_COMMAND(CmdLevelLand); DEF_COMMAND(CmdRefitRailVehicle); DEF_COMMAND(CmdBuildSignalTrack); DEF_COMMAND(CmdRemoveSignalTrack); DEF_COMMAND(CmdSetAutoReplace); DEF_COMMAND(CmdCloneVehicle); DEF_COMMAND(CmdStartStopVehicle); DEF_COMMAND(CmdMassStartStopVehicle); DEF_COMMAND(CmdAutoreplaceVehicle); DEF_COMMAND(CmdDepotSellAllVehicles); DEF_COMMAND(CmdDepotMassAutoReplace); DEF_COMMAND(CmdCreateGroup); DEF_COMMAND(CmdRenameGroup); DEF_COMMAND(CmdDeleteGroup); DEF_COMMAND(CmdAddVehicleGroup); DEF_COMMAND(CmdAddSharedVehicleGroup); DEF_COMMAND(CmdRemoveAllVehiclesGroup); DEF_COMMAND(CmdSetGroupReplaceProtection); DEF_COMMAND(CmdMoveOrder); DEF_COMMAND(CmdChangeTimetable); DEF_COMMAND(CmdSetVehicleOnTime); DEF_COMMAND(CmdAutofillTimetable); #undef DEF_COMMAND /** * The master command table * * This table contains all possible CommandProc functions with * the flags which belongs to it. The indizes are the same * as the value from the CMD_* enums. */ static const Command _command_proc_table[] = { {CmdBuildRailroadTrack, CMD_NO_WATER | CMD_AUTO}, /* CMD_BUILD_RAILROAD_TRACK */ {CmdRemoveRailroadTrack, CMD_AUTO}, /* CMD_REMOVE_RAILROAD_TRACK */ {CmdBuildSingleRail, CMD_NO_WATER | CMD_AUTO}, /* CMD_BUILD_SINGLE_RAIL */ {CmdRemoveSingleRail, CMD_AUTO}, /* CMD_REMOVE_SINGLE_RAIL */ {CmdLandscapeClear, 0}, /* CMD_LANDSCAPE_CLEAR */ {CmdBuildBridge, CMD_AUTO}, /* CMD_BUILD_BRIDGE */ {CmdBuildRailroadStation, CMD_NO_WATER | CMD_AUTO}, /* CMD_BUILD_RAILROAD_STATION */ {CmdBuildTrainDepot, CMD_NO_WATER | CMD_AUTO}, /* CMD_BUILD_TRAIN_DEPOT */ {CmdBuildSingleSignal, CMD_AUTO}, /* CMD_BUILD_SIGNALS */ {CmdRemoveSingleSignal, CMD_AUTO}, /* CMD_REMOVE_SIGNALS */ {CmdTerraformLand, CMD_AUTO}, /* CMD_TERRAFORM_LAND */ {CmdPurchaseLandArea, CMD_NO_WATER | CMD_AUTO}, /* CMD_PURCHASE_LAND_AREA */ {CmdSellLandArea, 0}, /* CMD_SELL_LAND_AREA */ {CmdBuildTunnel, CMD_AUTO}, /* CMD_BUILD_TUNNEL */ {CmdRemoveFromRailroadStation, 0}, /* CMD_REMOVE_FROM_RAILROAD_STATION */ {CmdConvertRail, 0}, /* CMD_CONVERT_RAILD */ {CmdBuildTrainWaypoint, 0}, /* CMD_BUILD_TRAIN_WAYPOINT */ {CmdRenameWaypoint, 0}, /* CMD_RENAME_WAYPOINT */ {CmdRemoveTrainWaypoint, 0}, /* CMD_REMOVE_TRAIN_WAYPOINT */ {CmdBuildRoadStop, CMD_NO_WATER | CMD_AUTO}, /* CMD_BUILD_ROAD_STOP */ {CmdRemoveRoadStop, 0}, /* CMD_REMOVE_ROAD_STOP */ {CmdBuildLongRoad, CMD_NO_WATER | CMD_AUTO}, /* CMD_BUILD_LONG_ROAD */ {CmdRemoveLongRoad, CMD_NO_TEST | CMD_AUTO}, /* CMD_REMOVE_LONG_ROAD; towns may disallow removing road bits (as they are connected) in test, but in exec they're removed and thus removing is allowed. */ {CmdBuildRoad, 0}, /* CMD_BUILD_ROAD */ {CmdRemoveRoad, 0}, /* CMD_REMOVE_ROAD */ {CmdBuildRoadDepot, CMD_NO_WATER | CMD_AUTO}, /* CMD_BUILD_ROAD_DEPOT */ {CmdBuildAirport, CMD_NO_WATER | CMD_AUTO}, /* CMD_BUILD_AIRPORT */ {CmdBuildDock, CMD_AUTO}, /* CMD_BUILD_DOCK */ {CmdBuildShipDepot, CMD_AUTO}, /* CMD_BUILD_SHIP_DEPOT */ {CmdBuildBuoy, CMD_AUTO}, /* CMD_BUILD_BUOY */ {CmdPlantTree, CMD_AUTO}, /* CMD_PLANT_TREE */ {CmdBuildRailVehicle, 0}, /* CMD_BUILD_RAIL_VEHICLE */ {CmdMoveRailVehicle, 0}, /* CMD_MOVE_RAIL_VEHICLE */ {CmdSellRailWagon, 0}, /* CMD_SELL_RAIL_WAGON */ {CmdSendTrainToDepot, 0}, /* CMD_SEND_TRAIN_TO_DEPOT */ {CmdForceTrainProceed, 0}, /* CMD_FORCE_TRAIN_PROCEED */ {CmdReverseTrainDirection, 0}, /* CMD_REVERSE_TRAIN_DIRECTION */ {CmdModifyOrder, 0}, /* CMD_MODIFY_ORDER */ {CmdSkipToOrder, 0}, /* CMD_SKIP_TO_ORDER */ {CmdDeleteOrder, 0}, /* CMD_DELETE_ORDER */ {CmdInsertOrder, 0}, /* CMD_INSERT_ORDER */ {CmdChangeServiceInt, 0}, /* CMD_CHANGE_SERVICE_INT */ {CmdBuildIndustry, 0}, /* CMD_BUILD_INDUSTRY */ {CmdBuildCompanyHQ, CMD_NO_WATER | CMD_AUTO}, /* CMD_BUILD_COMPANY_HQ */ {CmdSetCompanyManagerFace, 0}, /* CMD_SET_COMPANY_MANAGER_FACE */ {CmdSetCompanyColor, 0}, /* CMD_SET_COMPANY_COLOR */ {CmdIncreaseLoan, 0}, /* CMD_INCREASE_LOAN */ {CmdDecreaseLoan, 0}, /* CMD_DECREASE_LOAN */ {CmdWantEnginePreview, 0}, /* CMD_WANT_ENGINE_PREVIEW */ {CmdRenameVehicle, 0}, /* CMD_RENAME_VEHICLE */ {CmdRenameEngine, 0}, /* CMD_RENAME_ENGINE */ {CmdRenameCompany, 0}, /* CMD_RENAME_COMPANY */ {CmdRenamePresident, 0}, /* CMD_RENAME_PRESIDENT */ {CmdRenameStation, 0}, /* CMD_RENAME_STATION */ {CmdSellAircraft, 0}, /* CMD_SELL_AIRCRAFT */ {CmdBuildAircraft, 0}, /* CMD_BUILD_AIRCRAFT */ {CmdSendAircraftToHangar, 0}, /* CMD_SEND_AIRCRAFT_TO_HANGAR */ {CmdRefitAircraft, 0}, /* CMD_REFIT_AIRCRAFT */ {CmdPlaceSign, 0}, /* CMD_PLACE_SIGN */ {CmdRenameSign, 0}, /* CMD_RENAME_SIGN */ {CmdBuildRoadVeh, 0}, /* CMD_BUILD_ROAD_VEH */ {CmdSellRoadVeh, 0}, /* CMD_SELL_ROAD_VEH */ {CmdSendRoadVehToDepot, 0}, /* CMD_SEND_ROADVEH_TO_DEPOT */ {CmdTurnRoadVeh, 0}, /* CMD_TURN_ROADVEH */ {CmdRefitRoadVeh, 0}, /* CMD_REFIT_ROAD_VEH */ {CmdPause, CMD_SERVER}, /* CMD_PAUSE */ {CmdBuyShareInCompany, 0}, /* CMD_BUY_SHARE_IN_COMPANY */ {CmdSellShareInCompany, 0}, /* CMD_SELL_SHARE_IN_COMPANY */ {CmdBuyCompany, 0}, /* CMD_BUY_COMANY */ {CmdBuildTown, CMD_OFFLINE}, /* CMD_BUILD_TOWN */ {CmdRenameTown, CMD_SERVER}, /* CMD_RENAME_TOWN */ {CmdDoTownAction, 0}, /* CMD_DO_TOWN_ACTION */ {CmdSetRoadDriveSide, CMD_SERVER}, /* CMD_SET_ROAD_DRIVE_SIDE */ {CmdSellShip, 0}, /* CMD_SELL_SHIP */ {CmdBuildShip, 0}, /* CMD_BUILD_SHIP */ {CmdSendShipToDepot, 0}, /* CMD_SEND_SHIP_TO_DEPOT */ {CmdRefitShip, 0}, /* CMD_REFIT_SHIP */ {CmdOrderRefit, 0}, /* CMD_ORDER_REFIT */ {CmdCloneOrder, 0}, /* CMD_CLONE_ORDER */ {CmdClearArea, CMD_NO_TEST}, /* CMD_CLEAR_AREA; destroying multi-tile houses makes town rating differ between test and execution */ {CmdMoneyCheat, CMD_OFFLINE}, /* CMD_MONEY_CHEAT */ {CmdBuildCanal, CMD_AUTO}, /* CMD_BUILD_CANAL */ {CmdCompanyCtrl, 0}, /* CMD_COMPANY_CTRL */ {CmdLevelLand, CMD_NO_TEST | CMD_AUTO}, /* CMD_LEVEL_LAND; test run might clear tiles multiple times, in execution that only happens once */ {CmdRefitRailVehicle, 0}, /* CMD_REFIT_RAIL_VEHICLE */ {CmdRestoreOrderIndex, 0}, /* CMD_RESTORE_ORDER_INDEX */ {CmdBuildLock, CMD_AUTO}, /* CMD_BUILD_LOCK */ {CmdBuildSignalTrack, CMD_AUTO}, /* CMD_BUILD_SIGNAL_TRACK */ {CmdRemoveSignalTrack, CMD_AUTO}, /* CMD_REMOVE_SIGNAL_TRACK */ {CmdGiveMoney, 0}, /* CMD_GIVE_MONEY */ {CmdChangePatchSetting, CMD_SERVER}, /* CMD_CHANGE_PATCH_SETTING */ {CmdSetAutoReplace, 0}, /* CMD_SET_AUTOREPLACE */ {CmdCloneVehicle, CMD_NO_TEST}, /* CMD_CLONE_VEHICLE; NewGRF callbacks influence building and refitting making it impossible to correctly estimate the cost */ {CmdStartStopVehicle, 0}, /* CMD_START_STOP_VEHICLE */ {CmdMassStartStopVehicle, 0}, /* CMD_MASS_START_STOP */ {CmdAutoreplaceVehicle, 0}, /* CMD_AUTOREPLACE_VEHICLE */ {CmdDepotSellAllVehicles, 0}, /* CMD_DEPOT_SELL_ALL_VEHICLES */ {CmdDepotMassAutoReplace, 0}, /* CMD_DEPOT_MASS_AUTOREPLACE */ {CmdCreateGroup, 0}, /* CMD_CREATE_GROUP */ {CmdDeleteGroup, 0}, /* CMD_DELETE_GROUP */ {CmdRenameGroup, 0}, /* CMD_RENAME_GROUP */ {CmdAddVehicleGroup, 0}, /* CMD_ADD_VEHICLE_GROUP */ {CmdAddSharedVehicleGroup, 0}, /* CMD_ADD_SHARE_VEHICLE_GROUP */ {CmdRemoveAllVehiclesGroup, 0}, /* CMD_REMOVE_ALL_VEHICLES_GROUP */ {CmdSetGroupReplaceProtection, 0}, /* CMD_SET_GROUP_REPLACE_PROTECTION */ {CmdMoveOrder, 0}, /* CMD_MOVE_ORDER */ {CmdChangeTimetable, 0}, /* CMD_CHANGE_TIMETABLE */ {CmdSetVehicleOnTime, 0}, /* CMD_SET_VEHICLE_ON_TIME */ {CmdAutofillTimetable, 0}, /* CMD_AUTOFILL_TIMETABLE */ }; /*! * This function range-checks a cmd, and checks if the cmd is not NULL * * @param cmd The integervalue of a command * @return true if the command is valid (and got a CommandProc function) */ bool IsValidCommand(uint32 cmd) { cmd &= CMD_ID_MASK; return cmd < lengthof(_command_proc_table) && _command_proc_table[cmd].proc != NULL; } /*! * This function mask the parameter with CMD_ID_MASK and returns * the flags which belongs to the given command. * * @param cmd The integer value of the command * @return The flags for this command */ byte GetCommandFlags(uint32 cmd) { assert(IsValidCommand(cmd)); return _command_proc_table[cmd & CMD_ID_MASK].flags; } static int _docommand_recursive = 0; /*! * This function executes a given command with the parameters from the #CommandProc parameter list. * Depending on the flags parameter it execute or test a command. * * @param tile The tile to apply the command on (for the #CommandProc) * @param p1 Additional data for the command (for the #CommandProc) * @param p2 Additional data for the command (for the #CommandProc) * @param flags Flags for the command and how to execute the command * @param cmd The command-id to execute (a value of the CMD_* enums) * @see CommandProc */ CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint32 cmd, const char *text) { CommandCost res; /* Do not even think about executing out-of-bounds tile-commands */ if (!IsValidTile(tile)) return CMD_ERROR; CommandProc *proc = _command_proc_table[cmd].proc; if (_docommand_recursive == 0) _error_message = INVALID_STRING_ID; _docommand_recursive++; /* only execute the test call if it's toplevel, or we're not execing. */ if (_docommand_recursive == 1 || !(flags & DC_EXEC) ) { SetTownRatingTestMode(true); res = proc(tile, flags & ~DC_EXEC, p1, p2, text); SetTownRatingTestMode(false); if (CmdFailed(res)) { res.SetGlobalErrorMessage(); goto error; } if (_docommand_recursive == 1 && !(flags & DC_QUERY_COST) && !(flags & DC_BANKRUPT) && res.GetCost() != 0 && !CheckCompanyHasMoney(res)) { goto error; } if (!(flags & DC_EXEC)) { _docommand_recursive--; return res; } } /* Execute the command here. All cost-relevant functions set the expenses type * themselves to the cost object at some point */ res = proc(tile, flags, p1, p2, text); if (CmdFailed(res)) { res.SetGlobalErrorMessage(); error: _docommand_recursive--; return CMD_ERROR; } /* if toplevel, subtract the money. */ if (--_docommand_recursive == 0 && !(flags & DC_BANKRUPT)) { SubtractMoneyFromCompany(res); /* XXX - Old AI hack which doesn't use DoCommandP; update last build coord of company */ if (tile != 0 && IsValidCompanyID(_current_company)) { GetCompany(_current_company)->last_build_coordinate = tile; } } return res; } /*! * This functions returns the money which can be used to execute a command. * This is either the money of the current company or INT64_MAX if there * is no such a company "at the moment" like the server itself. * * @return The available money of a company or INT64_MAX */ Money GetAvailableMoneyForCommand() { CompanyID company = _current_company; if (!IsValidCompanyID(company)) return INT64_MAX; return GetCompany(company)->money; } /*! * Toplevel network safe docommand function for the current company. Must not be called recursively. * The callback is called when the command succeeded or failed. The parameters * tile, p1 and p2 are from the #CommandProc function. The paramater cmd is the command to execute. * The parameter my_cmd is used to indicate if the command is from a company or the server. * * @param tile The tile to perform a command on (see #CommandProc) * @param p1 Additional data for the command (see #CommandProc) * @param p2 Additional data for the command (see #CommandProc) * @param cmd The command to execute (a CMD_* value) * @param callback A callback function to call after the command is finished * @param text The text to pass * @param my_cmd indicator if the command is from a company or server (to display error messages for a user) * @return true if the command succeeded, else false */ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, bool my_cmd) { assert(_docommand_recursive == 0); /* Do not even think about executing out-of-bounds tile-commands */ if (!IsValidTile(tile)) return false; CommandCost res, res2; int x = TileX(tile) * TILE_SIZE; int y = TileY(tile) * TILE_SIZE; _error_message = INVALID_STRING_ID; StringID error_part1 = GB(cmd, 16, 16); _additional_cash_required = 0; /** Spectator has no rights except for the (dedicated) server which * is/can be a spectator but as the server it can do anything */ if (_current_company == COMPANY_SPECTATOR && !_network_server) { if (my_cmd) ShowErrorMessage(_error_message, error_part1, x, y); return false; } /* get pointer to command handler */ byte cmd_id = cmd & CMD_ID_MASK; assert(cmd_id < lengthof(_command_proc_table)); CommandProc *proc = _command_proc_table[cmd_id].proc; if (proc == NULL) return false; /* Flags get send to the DoCommand */ uint32 flags = 0; /* Command flags are used internally */ uint cmd_flags = GetCommandFlags(cmd); if (cmd_flags & CMD_NO_WATER) flags |= DC_NO_WATER; if (cmd_flags & CMD_AUTO) flags |= DC_AUTO; bool notest = (cmd_flags & CMD_NO_TEST) != 0; _docommand_recursive = 1; /* cost estimation only? */ if (!IsGeneratingWorld() && _shift_pressed && IsLocalCompany() && !(cmd & (CMD_NETWORK_COMMAND | CMD_SHOW_NO_ERROR)) && cmd_id != CMD_PAUSE) { /* estimate the cost. */ SetTownRatingTestMode(true); res = proc(tile, flags, p1, p2, text); SetTownRatingTestMode(false); if (CmdFailed(res)) { res.SetGlobalErrorMessage(); ShowErrorMessage(_error_message, error_part1, x, y); } else { ShowEstimatedCostOrIncome(res.GetCost(), x, y); } _docommand_recursive = 0; ClearStorageChanges(false); return false; } if (!((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) { /* first test if the command can be executed. */ SetTownRatingTestMode(true); res = proc(tile, flags, p1, p2, text); SetTownRatingTestMode(false); if (CmdFailed(res)) { res.SetGlobalErrorMessage(); goto show_error; } /* no money? Only check if notest is off */ if (!notest && res.GetCost() != 0 && !CheckCompanyHasMoney(res)) goto show_error; } #ifdef ENABLE_NETWORK /** If we are in network, and the command is not from the network * send it to the command-queue and abort execution * If we are a dedicated server temporarily switch local company, otherwise * the other parties won't be able to execute our command and will desync. * We also need to do this if the server's company has gone bankrupt * @todo Rewrite (dedicated) server to something more than a dirty hack! */ if (_networking && !(cmd & CMD_NETWORK_COMMAND)) { CompanyID bck = _local_company; if (_network_dedicated || (_network_server && bck == COMPANY_SPECTATOR)) _local_company = COMPANY_FIRST; NetworkSend_Command(tile, p1, p2, cmd, callback, text); if (_network_dedicated || (_network_server && bck == COMPANY_SPECTATOR)) _local_company = bck; _docommand_recursive = 0; ClearStorageChanges(false); return true; } #endif /* ENABLE_NETWORK */ DEBUG(desync, 1, "cmd: %08x; %08x; %1x; %06x; %08x; %08x; %04x; %s\n", _date, _date_fract, (int)_current_company, tile, p1, p2, cmd & ~CMD_NETWORK_COMMAND, text); /* update last build coordinate of company. */ if (tile != 0 && IsValidCompanyID(_current_company)) { GetCompany(_current_company)->last_build_coordinate = tile; } /* Actually try and execute the command. If no cost-type is given * use the construction one */ res2 = proc(tile, flags | DC_EXEC, p1, p2, text); /* If notest is on, it means the result of the test can be different than * the real command.. so ignore the test */ if (!notest && !((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) { assert(res.GetCost() == res2.GetCost() && CmdFailed(res) == CmdFailed(res2)); // sanity check } else { if (CmdFailed(res2)) { res.SetGlobalErrorMessage(); goto show_error; } } SubtractMoneyFromCompany(res2); /* update signals if needed */ UpdateSignalsInBuffer(); if (IsLocalCompany() && _game_mode != GM_EDITOR) { if (res2.GetCost() != 0 && tile != 0) ShowCostOrIncomeAnimation(x, y, GetSlopeZ(x, y), res2.GetCost()); if (_additional_cash_required != 0) { SetDParam(0, _additional_cash_required); if (my_cmd) ShowErrorMessage(STR_0003_NOT_ENOUGH_CASH_REQUIRES, error_part1, x, y); if (res2.GetCost() == 0) goto callb_err; } } _docommand_recursive = 0; if (callback) callback(true, tile, p1, p2); ClearStorageChanges(true); return true; show_error: /* show error message if the command fails? */ if (IsLocalCompany() && error_part1 != 0 && my_cmd) { ShowErrorMessage(_error_message, error_part1, x, y); } callb_err: _docommand_recursive = 0; if (callback) callback(false, tile, p1, p2); ClearStorageChanges(false); return false; } CommandCost CommandCost::AddCost(CommandCost ret) { this->AddCost(ret.cost); if (this->success && !ret.success) { this->message = ret.message; this->success = false; } return *this; }