order_backup.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "command_func.h"
00014 #include "core/pool_func.hpp"
00015 #include "network/network.h"
00016 #include "network/network_func.h"
00017 #include "order_backup.h"
00018 #include "vehicle_base.h"
00019 #include "window_func.h"
00020 
00021 OrderBackupPool _order_backup_pool("BackupOrder");
00022 INSTANTIATE_POOL_METHODS(OrderBackup)
00023 
00024 
00025 OrderBackup::~OrderBackup()
00026 {
00027   free(this->name);
00028 
00029   if (CleaningPool()) return;
00030 
00031   Order *o = this->orders;
00032   while (o != NULL) {
00033     Order *next = o->next;
00034     delete o;
00035     o = next;
00036   }
00037 }
00038 
00044 OrderBackup::OrderBackup(const Vehicle *v, uint32 user)
00045 {
00046   this->user             = user;
00047   this->tile             = v->tile;
00048   this->orderindex       = v->cur_implicit_order_index;
00049   this->group            = v->group_id;
00050   this->service_interval = v->service_interval;
00051 
00052   if (v->name != NULL) this->name = strdup(v->name);
00053 
00054   /* If we have shared orders, store the vehicle we share the order with. */
00055   if (v->IsOrderListShared()) {
00056     this->clone = (v->FirstShared() == v) ? v->NextShared() : v->FirstShared();
00057   } else {
00058     /* Else copy the orders */
00059     Order **tail = &this->orders;
00060 
00061     /* Count the number of orders */
00062     const Order *order;
00063     FOR_VEHICLE_ORDERS(v, order) {
00064       Order *copy = new Order();
00065       copy->AssignOrder(*order);
00066       *tail = copy;
00067       tail = &copy->next;
00068     }
00069   }
00070 }
00071 
00076 void OrderBackup::DoRestore(Vehicle *v)
00077 {
00078   /* If we have a custom name, process that */
00079   v->name = this->name;
00080   this->name = NULL;
00081 
00082   /* If we had shared orders, recover that */
00083   if (this->clone != NULL) {
00084     DoCommand(0, v->index | CO_SHARE << 30, this->clone->index, DC_EXEC, CMD_CLONE_ORDER);
00085   } else if (this->orders != NULL && OrderList::CanAllocateItem()) {
00086     v->orders.list = new OrderList(this->orders, v);
00087     this->orders = NULL;
00088     /* Make sure buoys/oil rigs are updated in the station list. */
00089     InvalidateWindowClassesData(WC_STATION_LIST, 0);
00090   }
00091 
00092   uint num_orders = v->GetNumOrders();
00093   if (num_orders != 0) {
00094     v->cur_real_order_index = v->cur_implicit_order_index = this->orderindex % num_orders;
00095     v->UpdateRealOrderIndex();
00096   }
00097   v->service_interval = this->service_interval;
00098 
00099   /* Restore vehicle group */
00100   DoCommand(0, this->group, v->index, DC_EXEC, CMD_ADD_VEHICLE_GROUP);
00101 }
00102 
00109 /* static */ void OrderBackup::Backup(const Vehicle *v, uint32 user)
00110 {
00111   /* Don't use reset as that broadcasts over the network to reset the variable,
00112    * which is what we are doing at the moment. */
00113   OrderBackup *ob;
00114   FOR_ALL_ORDER_BACKUPS(ob) {
00115     if (ob->user == user) delete ob;
00116   }
00117   if (OrderBackup::CanAllocateItem()) {
00118     new OrderBackup(v, user);
00119   }
00120 }
00121 
00128 /* static */ void OrderBackup::Restore(Vehicle *v, uint32 user)
00129 {
00130   OrderBackup *ob;
00131   FOR_ALL_ORDER_BACKUPS(ob) {
00132     if (v->tile != ob->tile || ob->user != user) continue;
00133 
00134     ob->DoRestore(v);
00135     delete ob;
00136   }
00137 }
00138 
00145 /* static */ void OrderBackup::ResetOfUser(TileIndex tile, uint32 user)
00146 {
00147   OrderBackup *ob;
00148   FOR_ALL_ORDER_BACKUPS(ob) {
00149     if (ob->user == user && (ob->tile == tile || tile == INVALID_TILE)) delete ob;
00150   }
00151 }
00152 
00162 CommandCost CmdClearOrderBackup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00163 {
00164   /* No need to check anything. If the tile or user don't exist we just ignore it. */
00165   if (flags & DC_EXEC) OrderBackup::ResetOfUser(tile == 0 ? INVALID_TILE : tile, p2);
00166 
00167   return CommandCost();
00168 }
00169 
00176 /* static */ void OrderBackup::ResetUser(uint32 user)
00177 {
00178   assert(_network_server);
00179 
00180   OrderBackup *ob;
00181   FOR_ALL_ORDER_BACKUPS(ob) {
00182     /* If it's not an backup of us, so ignore it. */
00183     if (ob->user != user) continue;
00184 
00185     DoCommandP(0, 0, user, CMD_CLEAR_ORDER_BACKUP);
00186     return;
00187   }
00188 }
00189 
00196 /* static */ void OrderBackup::Reset(TileIndex t, bool from_gui)
00197 {
00198   /* The user has CLIENT_ID_SERVER as default when network play is not active,
00199    * but compiled it. A network client has its own variable for the unique
00200    * client/user identifier. Finally if networking isn't compiled in the
00201    * default is just plain and simple: 0. */
00202 #ifdef ENABLE_NETWORK
00203   uint32 user = _networking && !_network_server ? _network_own_client_id : CLIENT_ID_SERVER;
00204 #else
00205   uint32 user = 0;
00206 #endif
00207 
00208   OrderBackup *ob;
00209   FOR_ALL_ORDER_BACKUPS(ob) {
00210     /* If it's not an backup of us, so ignore it. */
00211     if (ob->user != user) continue;
00212     /* If it's not for our chosen tile either, ignore it. */
00213     if (t != INVALID_TILE && t != ob->tile) continue;
00214 
00215     if (from_gui) {
00216       /* We need to circumvent the "prevention" from this command being executed
00217        * while the game is paused, so use the internal method. Nor do we want
00218        * this command to get its cost estimated when shift is pressed. */
00219       DoCommandPInternal(ob->tile, 0, user, CMD_CLEAR_ORDER_BACKUP, NULL, NULL, true, false);
00220     } else {
00221       /* The command came from the game logic, i.e. the clearing of a tile.
00222        * In that case we have no need to actually sync this, just do it. */
00223       delete ob;
00224     }
00225   }
00226 }
00227 
00232 /* static */ void OrderBackup::ClearGroup(GroupID group)
00233 {
00234   OrderBackup *ob;
00235   FOR_ALL_ORDER_BACKUPS(ob) {
00236     if (ob->group == group) ob->group = DEFAULT_GROUP;
00237   }
00238 }
00239 
00247 /* static */ void OrderBackup::ClearVehicle(const Vehicle *v)
00248 {
00249   assert(v != NULL);
00250   OrderBackup *ob;
00251   FOR_ALL_ORDER_BACKUPS(ob) {
00252     if (ob->clone == v) {
00253       /* Get another item in the shared list. */
00254       ob->clone = (v->FirstShared() == v) ? v->NextShared() : v->FirstShared();
00255       /* But if that isn't there, remove it. */
00256       if (ob->clone == NULL) delete ob;
00257     }
00258   }
00259 }